* nicer MINFO
[m6w6/ext-http] / http_methods.c
1 /*
2 +----------------------------------------------------------------------+
3 | PECL :: http |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.0 of the PHP license, that |
6 | is bundled with this package in the file LICENSE, and is available |
7 | through the world-wide-web at http://www.php.net/license/3_0.txt. |
8 | If you did not receive a copy of the PHP license and are unable to |
9 | obtain it through the world-wide-web, please send a note to |
10 | license@php.net so we can mail you a copy immediately. |
11 +----------------------------------------------------------------------+
12 | Copyright (c) 2004-2005 Michael Wallner <mike@php.net> |
13 +----------------------------------------------------------------------+
14 */
15
16 /* $Id$ */
17
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
21
22 #include "php.h"
23 #include "php_http.h"
24 #include "php_http_api.h"
25 #include "php_http_curl_api.h"
26 #include "php_http_std_defs.h"
27
28 #include "ext/standard/php_smart_str.h"
29
30 #ifdef ZEND_ENGINE_2
31
32 /* {{{ HTTPi_Response */
33
34 /* {{{ proto void HTTPi_Response::__construct(bool cache, bool gzip)
35 *
36 * Instantiates a new HTTPi_Response object, which can be used to send
37 * any data/resource/file to an HTTP client with caching and multiple
38 * ranges/resuming support.
39 *
40 * NOTE: GZIPping is not implemented yet.
41 */
42 PHP_METHOD(HTTPi_Response, __construct)
43 {
44 zend_bool do_cache = 0, do_gzip = 0;
45 getObject(httpi_response_object, obj);
46
47 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|bb", &do_cache, &do_gzip)) {
48 // throw exception
49 return;
50 }
51
52 UPD_PROP(obj, long, cache, do_cache);
53 UPD_PROP(obj, long, gzip, do_gzip);
54 }
55 /* }}} */
56
57 /* {{{ proto bool HTTPi_Response::setCache(bool cache)
58 *
59 * Whether it sould be attempted to cache the entitity.
60 * This will result in necessary caching headers and checks of clients
61 * "If-Modified-Since" and "If-None-Match" headers. If one of those headers
62 * matches a "304 Not Modified" status code will be issued.
63 *
64 * NOTE: If you're using sessions, be shure that you set session.cache_limiter
65 * to something more appropriate than "no-cache"!
66 */
67 PHP_METHOD(HTTPi_Response, setCache)
68 {
69 zend_bool do_cache = 0;
70 getObject(httpi_response_object, obj);
71
72 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_cache)) {
73 RETURN_FALSE;
74 }
75
76 UPD_PROP(obj, long, cache, do_cache);
77 RETURN_TRUE;
78 }
79 /* }}} */
80
81 /* {{{ proto bool HTTPi_Response::getCache()
82 *
83 * Get current caching setting.
84 */
85 PHP_METHOD(HTTPi_Response, getCache)
86 {
87 zval *do_cache = NULL;
88 getObject(httpi_response_object, obj);
89
90 NO_ARGS;
91
92 do_cache = GET_PROP(obj, cache);
93 RETURN_BOOL(Z_LVAL_P(do_cache));
94 }
95 /* }}}*/
96
97 /* {{{ proto bool HTTPi_Response::setGzip(bool gzip)
98 *
99 * Enable on-thy-fly gzipping of the sent entity. NOT IMPLEMENTED YET.
100 */
101 PHP_METHOD(HTTPi_Response, setGzip)
102 {
103 zend_bool do_gzip = 0;
104 getObject(httpi_response_object, obj);
105
106 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_gzip)) {
107 RETURN_FALSE;
108 }
109
110 UPD_PROP(obj, long, gzip, do_gzip);
111 RETURN_TRUE;
112 }
113 /* }}} */
114
115 /* {{{ proto bool HTTPi_Response::getGzip()
116 *
117 * Get current gzipping setting.
118 */
119 PHP_METHOD(HTTPi_Response, getGzip)
120 {
121 zval *do_gzip = NULL;
122 getObject(httpi_response_object, obj);
123
124 NO_ARGS;
125
126 do_gzip = GET_PROP(obj, gzip);
127 RETURN_BOOL(Z_LVAL_P(do_gzip));
128 }
129 /* }}} */
130
131 /* {{{ proto bool HTTPi_Response::setCacheControl(string control[, bool raw = false])
132 *
133 * Set a custom cache-control header, usually being "private" or "public"; if
134 * $raw is set to true the header will be sent as-is.
135 */
136 PHP_METHOD(HTTPi_Response, setCacheControl)
137 {
138 char *ccontrol;
139 int cc_len;
140 zend_bool raw = 0;
141 getObject(httpi_response_object, obj);
142
143 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &ccontrol, &cc_len, &raw)) {
144 RETURN_FALSE;
145 }
146
147 if ((!raw) && (strcmp(ccontrol, "public") && strcmp(ccontrol, "private") && strcmp(ccontrol, "no-cache"))) {
148 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache-Control '%s' doesn't match public, private or no-cache", ccontrol);
149 RETURN_FALSE;
150 }
151
152 UPD_PROP(obj, long, raw_cache_header, raw);
153 UPD_PROP(obj, string, cacheControl, ccontrol);
154 RETURN_TRUE;
155 }
156 /* }}} */
157
158 /* {{{ proto string HTTPi_Response::getCacheControl()
159 *
160 * Get current Cache-Control header setting.
161 */
162 PHP_METHOD(HTTPi_Response, getCacheControl)
163 {
164 zval *ccontrol;
165 getObject(httpi_response_object, obj);
166
167 NO_ARGS;
168
169 ccontrol = GET_PROP(obj, cacheControl);
170 RETURN_STRINGL(Z_STRVAL_P(ccontrol), Z_STRLEN_P(ccontrol), 1);
171 }
172 /* }}} */
173
174 /* {{{ proto bool HTTPi_Response::setContentType(string content_type)
175 *
176 * Set the content-type of the sent entity.
177 */
178 PHP_METHOD(HTTPi_Response, setContentType)
179 {
180 char *ctype;
181 int ctype_len;
182 getObject(httpi_response_object, obj);
183
184 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ctype, &ctype_len)) {
185 RETURN_FALSE;
186 }
187
188 if (!strchr(ctype, '/')) {
189 php_error_docref(NULL TSRMLS_CC, E_WARNING,
190 "Content type '%s' doesn't seem to contain a primary and a secondary part", ctype);
191 RETURN_FALSE;
192 }
193
194 UPD_PROP(obj, string, contentType, ctype);
195
196 RETURN_TRUE;
197 }
198 /* }}} */
199
200 /* {{{ proto string HTTPi_Response::getContentType()
201 *
202 * Get current Content-Type header setting.
203 */
204 PHP_METHOD(HTTPi_Response, getContentType)
205 {
206 zval *ctype;
207 getObject(httpi_response_object, obj);
208
209 NO_ARGS;
210
211 ctype = GET_PROP(obj, contentType);
212 RETURN_STRINGL(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1);
213 }
214 /* }}} */
215
216 /* {{{ proto bool HTTPi_Response::setContentDisposition(string filename[, bool inline = false])
217 *
218 * Set the Content-Disposition of the sent entity. This setting aims to suggest
219 * the receiveing user agent how to handle the sent entity; usually the client
220 * will show the user a "Save As..." popup.
221 */
222 PHP_METHOD(HTTPi_Response, setContentDisposition)
223 {
224 char *file;
225 int file_len;
226 zend_bool is_inline = 0;
227 getObject(httpi_response_object, obj);
228
229 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &file, &file_len, &is_inline)) {
230 RETURN_FALSE;
231 }
232
233 UPD_PROP(obj, string, dispoFile, file);
234 UPD_PROP(obj, long, dispoInline, is_inline);
235 RETURN_TRUE;
236 }
237 /* }}} */
238
239 /* {{{ proto array HTTPi_Response::getContentDisposition()
240 *
241 * Get current Content-Disposition setting.
242 * Will return an associative array like:
243 * <pre>
244 * array(
245 * 'filename' => 'foo.bar',
246 * 'inline' => false
247 * )
248 * </pre>
249 */
250 PHP_METHOD(HTTPi_Response, getContentDisposition)
251 {
252 zval *file;
253 zval *is_inline;
254 getObject(httpi_response_object, obj);
255
256 if (ZEND_NUM_ARGS()) {
257 WRONG_PARAM_COUNT;
258 }
259
260 file = GET_PROP(obj, dispoFile);
261 is_inline = GET_PROP(obj, dispoInline);
262
263 array_init(return_value);
264 add_assoc_stringl(return_value, "filename", Z_STRVAL_P(file), Z_STRLEN_P(file), 1);
265 add_assoc_bool(return_value, "inline", Z_LVAL_P(is_inline));
266 }
267 /* }}} */
268
269 /* {{{ proto bool HTTPi_Response::setETag(string etag)
270 *
271 * Set a custom ETag. Use this only if you know what you're doing.
272 */
273 PHP_METHOD(HTTPi_Response, setETag)
274 {
275 char *etag;
276 int etag_len;
277 getObject(httpi_response_object, obj);
278
279 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &etag, &etag_len)) {
280 RETURN_FALSE;
281 }
282
283 UPD_PROP(obj, string, eTag, etag);
284 RETURN_TRUE;
285 }
286 /* }}} */
287
288 /* {{{ proto string HTTPi_Response::getETag()
289 *
290 * Get the previously set custom ETag.
291 */
292 PHP_METHOD(HTTPi_Response, getETag)
293 {
294 zval *etag;
295 getObject(httpi_response_object, obj);
296
297 NO_ARGS;
298
299 etag = GET_PROP(obj, eTag);
300 RETURN_STRINGL(Z_STRVAL_P(etag), Z_STRLEN_P(etag), 1);
301 }
302 /* }}} */
303
304 /* {{{ proto bool HTTPi_Response::setData(string data)
305 *
306 * Set the data to be sent.
307 */
308 PHP_METHOD(HTTPi_Response, setData)
309 {
310 zval *the_data;
311 getObject(httpi_response_object, obj);
312
313 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &the_data)) {
314 RETURN_FALSE;
315 }
316
317 convert_to_string_ex(&the_data);
318 SET_PROP(obj, data, the_data);
319 UPD_PROP(obj, long, lastModified, http_lmod(the_data, SEND_DATA));
320 UPD_PROP(obj, long, send_mode, SEND_DATA);
321 RETURN_TRUE;
322 }
323 /* }}} */
324
325 /* {{{ proto string HTTPi_Response::getData()
326 *
327 * Get the previously set data to be sent.
328 */
329 PHP_METHOD(HTTPi_Response, getData)
330 {
331 zval *the_data;
332 getObject(httpi_response_object, obj);
333
334 NO_ARGS;
335
336 the_data = GET_PROP(obj, data);
337 RETURN_STRINGL(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), 1);
338 }
339 /* }}} */
340
341 /* {{{ proto bool HTTPi_Response::setStream(resource stream)
342 *
343 * Set the resource to be sent.
344 */
345 PHP_METHOD(HTTPi_Response, setStream)
346 {
347 zval *the_stream;
348 php_stream *the_real_stream;
349 getObject(httpi_response_object, obj);
350
351 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &the_stream)) {
352 RETURN_FALSE;
353 }
354
355 php_stream_from_zval(the_real_stream, &the_stream);
356
357 SET_PROP(obj, stream, the_stream);
358 UPD_PROP(obj, long, lastModified, http_lmod(the_real_stream, SEND_RSRC));
359 UPD_PROP(obj, long, send_mode, SEND_RSRC);
360 RETURN_TRUE;
361 }
362 /* }}} */
363
364 /* {{{ proto resource HTTPi_Response::getStream()
365 *
366 * Get the previously set resource to be sent.
367 */
368 PHP_METHOD(HTTPi_Response, getStream)
369 {
370 zval *the_stream;
371 getObject(httpi_response_object, obj);
372
373 NO_ARGS;
374
375 the_stream = GET_PROP(obj, stream);
376 RETURN_RESOURCE(Z_LVAL_P(the_stream));
377 }
378 /* }}} */
379
380 /* {{{ proto bool HTTPi_Response::setFile(string file)
381 *
382 * Set the file to be sent.
383 */
384 PHP_METHOD(HTTPi_Response, setFile)
385 {
386 zval *the_file;
387 getObject(httpi_response_object, obj);
388
389 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &the_file)) {
390 RETURN_FALSE;
391 }
392
393 convert_to_string_ex(&the_file);
394
395 UPD_PROP(obj, string, file, Z_STRVAL_P(the_file));
396 UPD_PROP(obj, long, lastModified, http_lmod(the_file, -1));
397 UPD_PROP(obj, long, send_mode, -1);
398 RETURN_TRUE;
399 }
400 /* }}} */
401
402 /* {{{ proto string HTTPi_Response::getFile()
403 *
404 * Get the previously set file to be sent.
405 */
406 PHP_METHOD(HTTPi_Response, getFile)
407 {
408 zval *the_file;
409 getObject(httpi_response_object, obj);
410
411 NO_ARGS;
412
413 the_file = GET_PROP(obj, file);
414 RETURN_STRINGL(Z_STRVAL_P(the_file), Z_STRLEN_P(the_file), 1);
415 }
416 /* }}} */
417
418 /* {{{ proto bool HTTPi_Response::send()
419 *
420 * Finally send the entity.
421 *
422 * Example:
423 * <pre>
424 * <?php
425 * $r = new HTTPi_Response(true);
426 * $r->setFile('../hidden/contract.pdf');
427 * $r->setContentType('application/pdf');
428 * $r->send();
429 * ?>
430 * </pre>
431 *
432 */
433 PHP_METHOD(HTTPi_Response, send)
434 {
435 zval *do_cache, *do_gzip;
436 getObject(httpi_response_object, obj);
437
438 NO_ARGS;
439
440 do_cache = GET_PROP(obj, cache);
441 do_gzip = GET_PROP(obj, gzip);
442
443 /* caching */
444 if (Z_LVAL_P(do_cache)) {
445 zval *cctrl, *etag, *lmod, *ccraw;
446
447 etag = GET_PROP(obj, eTag);
448 lmod = GET_PROP(obj, lastModified);
449 cctrl = GET_PROP(obj, cacheControl);
450 ccraw = GET_PROP(obj, raw_cache_header);
451
452 if (Z_LVAL_P(ccraw)) {
453 http_cache_etag(Z_STRVAL_P(etag), Z_STRLEN_P(etag), Z_STRVAL_P(cctrl), Z_STRLEN_P(cctrl));
454 http_cache_last_modified(Z_LVAL_P(lmod), Z_LVAL_P(lmod) ? Z_LVAL_P(lmod) : time(NULL), Z_STRVAL_P(cctrl), Z_STRLEN_P(cctrl));
455 } else {
456 char cc_header[42] = {0};
457 sprintf(cc_header, "%s, must-revalidate, max-age=0", Z_STRVAL_P(cctrl));
458 http_cache_etag(Z_STRVAL_P(etag), Z_STRLEN_P(etag), cc_header, strlen(cc_header));
459 http_cache_last_modified(Z_LVAL_P(lmod), Z_LVAL_P(lmod) ? Z_LVAL_P(lmod) : time(NULL), cc_header, strlen(cc_header));
460 }
461 }
462
463 /* gzip */
464 if (Z_LVAL_P(do_gzip)) {
465 /* ... */
466 }
467
468 /* content type */
469 {
470 zval *ctype = GET_PROP(obj, contentType);
471 if (Z_STRLEN_P(ctype)) {
472 http_send_content_type(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype));
473 } else {
474 http_send_content_type("application/x-octetstream", sizeof("application/x-octetstream") - 1);
475 }
476 }
477
478 /* content disposition */
479 {
480 zval *dispo_file = GET_PROP(obj, dispoFile);
481 if (Z_STRLEN_P(dispo_file)) {
482 zval *dispo_inline = GET_PROP(obj, dispoInline);
483 http_send_content_disposition(Z_STRVAL_P(dispo_file), Z_STRLEN_P(dispo_file), Z_LVAL_P(dispo_inline));
484 }
485 }
486
487 /* send */
488 {
489 zval *send_mode = GET_PROP(obj, send_mode);
490 switch (Z_LVAL_P(send_mode))
491 {
492 case SEND_DATA:
493 {
494 zval *zdata = GET_PROP(obj, data);
495 RETURN_SUCCESS(http_send_data(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata)));
496 }
497
498 case SEND_RSRC:
499 {
500 php_stream *the_real_stream;
501 zval *the_stream = GET_PROP(obj, stream);
502 php_stream_from_zval(the_real_stream, &the_stream);
503 RETURN_SUCCESS(http_send_stream(the_real_stream));
504 }
505
506 default:
507 {
508 zval *zfile = GET_PROP(obj, file);
509 RETURN_SUCCESS(http_send_file(Z_STRVAL_P(zfile)));
510 }
511 }
512 }
513 }
514 /* }}} */
515 /* }}} */
516
517 #ifdef HTTP_HAVE_CURL
518 /* {{{ HTTPi_Request */
519
520 /* {{{ proto void HTTPi_Request::__construct([string url[, long request_method = HTTP_GET]])
521 *
522 * Instantiate a new HTTPi_Request object which can be used to issue HEAD, GET
523 * and POST (including posting files) HTTP requests.
524 */
525 PHP_METHOD(HTTPi_Request, __construct)
526 {
527 char *URL = NULL;
528 int URL_len;
529 long meth = -1;
530 getObject(httpi_request_object, obj);
531
532 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &URL, &URL_len, &meth)) {
533 return;
534 }
535
536 INIT_PARR(obj, options);
537 INIT_PARR(obj, responseInfo);
538 INIT_PARR(obj, responseData);
539 INIT_PARR(obj, postData);
540 INIT_PARR(obj, postFiles);
541
542 if (URL) {
543 UPD_PROP(obj, string, url, URL);
544 }
545 if (meth > -1) {
546 UPD_PROP(obj, long, method, meth);
547 }
548 }
549 /* }}} */
550
551 /* {{{ proto void HTTPi_Request::__destruct()
552 *
553 * Destroys the HTTPi_Request object.
554 */
555 PHP_METHOD(HTTPi_Request, __destruct)
556 {
557 getObject(httpi_request_object, obj);
558
559 NO_ARGS;
560
561 FREE_PARR(obj, options);
562 FREE_PARR(obj, responseInfo);
563 FREE_PARR(obj, responseData);
564 FREE_PARR(obj, postData);
565 FREE_PARR(obj, postFiles);
566 }
567 /* }}} */
568
569 /* {{{ proto bool HTTPi_Request::setOptions(array options)
570 *
571 * Set the request options to use. See http_get() for a full list of available options.
572 */
573 PHP_METHOD(HTTPi_Request, setOptions)
574 {
575 char *key = NULL;
576 long idx = 0;
577 zval *opts, *old_opts, **opt;
578 getObject(httpi_request_object, obj);
579
580 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &opts)) {
581 RETURN_FALSE;
582 }
583
584 old_opts = GET_PROP(obj, options);
585
586 /* headers and cookies need extra attention -- thus cannot use array_merge() directly */
587 FOREACH_KEYVAL(opts, key, idx, opt) {
588 if (key) {
589 if (!strcmp(key, "headers")) {
590 zval **headers;
591 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(old_opts), "headers", sizeof("headers"), (void **) &headers)) {
592 array_merge(*opt, *headers);
593 continue;
594 }
595 } else if (!strcmp(key, "cookies")) {
596 zval **cookies;
597 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(old_opts), "cookies", sizeof("cookies"), (void **) &cookies)) {
598 array_merge(*opt, *cookies);
599 continue;
600 }
601 }
602 zval_add_ref(opt);
603 add_assoc_zval(old_opts, key, *opt);
604
605 /* reset */
606 key = NULL;
607 }
608 }
609
610 RETURN_TRUE;
611 }
612 /* }}} */
613
614 /* {{{ proto array HTTPi_Request::getOptions()
615 *
616 * Get current set options.
617 */
618 PHP_METHOD(HTTPi_Request, getOptions)
619 {
620 zval *opts;
621 getObject(httpi_request_object, obj);
622
623 NO_ARGS;
624
625 opts = GET_PROP(obj, options);
626 array_init(return_value);
627 array_copy(opts, return_value);
628 }
629 /* }}} */
630
631 /* {{{ proto bool HTTPi_Request::setURL(string url)
632 *
633 * Set the request URL.
634 */
635 PHP_METHOD(HTTPi_Request, setURL)
636 {
637 char *URL = NULL;
638 int URL_len;
639 getObject(httpi_request_object, obj);
640
641 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &URL, &URL_len)) {
642 RETURN_FALSE;
643 }
644
645 UPD_PROP(obj, string, url, URL);
646 RETURN_TRUE;
647 }
648 /* }}} */
649
650 /* {{{ proto string HTTPi_Request::getUrl()
651 *
652 * Get the previously set request URL.
653 */
654 PHP_METHOD(HTTPi_Request, getURL)
655 {
656 zval *URL;
657 getObject(httpi_request_object, obj);
658
659 NO_ARGS;
660
661 URL = GET_PROP(obj, url);
662 RETURN_STRINGL(Z_STRVAL_P(URL), Z_STRLEN_P(URL), 1);
663 }
664 /* }}} */
665
666 /* {{{ proto bool HTTPi_Request::setMethod(long request_method)
667 *
668 * Set the request methods; one of the <tt>HTTP_HEAD</tt>, <tt>HTTP_GET</tt> or
669 * <tt>HTTP_POST</tt> constants.
670 */
671 PHP_METHOD(HTTPi_Request, setMethod)
672 {
673 long meth;
674 getObject(httpi_request_object, obj);
675
676 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &meth)) {
677 RETURN_FALSE;
678 }
679
680 UPD_PROP(obj, long, method, meth);
681 RETURN_TRUE;
682 }
683 /* }}} */
684
685 /* {{{ proto long HTTPi_Request::getMethod()
686 *
687 * Get the previously set request method.
688 */
689 PHP_METHOD(HTTPi_Request, getMethod)
690 {
691 zval *meth;
692 getObject(httpi_request_object, obj);
693
694 NO_ARGS;
695
696 meth = GET_PROP(obj, method);
697 RETURN_LONG(Z_LVAL_P(meth));
698 }
699 /* }}} */
700
701 /* {{{ proto bool HTTPi_Request::setContentType(string content_type)
702 *
703 * Set the content type the post request should have.
704 * Use this only if you know what you're doing.
705 */
706 PHP_METHOD(HTTPi_Request, setContentType)
707 {
708 char *ctype;
709 int ct_len;
710 getObject(httpi_request_object, obj);
711
712 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ctype, &ct_len)) {
713 RETURN_FALSE;
714 }
715
716 if (!strchr(ctype, '/')) {
717 php_error_docref(NULL TSRMLS_CC, E_WARNING,
718 "Content-Type '%s' doesn't seem to contain a primary and a secondary part",
719 ctype);
720 RETURN_FALSE;
721 }
722
723 UPD_PROP(obj, string, contentType, ctype);
724 RETURN_TRUE;
725 }
726 /* }}} */
727
728 /* {{{ proto string HTTPi_Request::getContentType()
729 *
730 * Get the previously content type.
731 */
732 PHP_METHOD(HTTPi_Request, getContentType)
733 {
734 zval *ctype;
735 getObject(httpi_request_object, obj);
736
737 NO_ARGS;
738
739 ctype = GET_PROP(obj, contentType);
740 RETURN_STRINGL(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1);
741 }
742 /* }}} */
743
744 /* {{{ proto bool HTTPi_Request::setQueryData(mixed query_data)
745 *
746 * Set the URL query parameters to use.
747 * Overwrites previously set query parameters.
748 * Affects any request types.
749 */
750 PHP_METHOD(HTTPi_Request, setQueryData)
751 {
752 zval *qdata;
753 char *query_data = NULL;
754 getObject(httpi_request_object, obj);
755
756 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &qdata)) {
757 RETURN_FALSE;
758 }
759
760 if ((Z_TYPE_P(qdata) == IS_ARRAY) || (Z_TYPE_P(qdata) == IS_OBJECT)) {
761 if (SUCCESS != http_urlencode_hash(HASH_OF(qdata), &query_data)) {
762 RETURN_FALSE;
763 }
764 UPD_PROP(obj, string, queryData, query_data);
765 efree(query_data);
766 RETURN_TRUE;
767 }
768
769 convert_to_string(qdata);
770 UPD_PROP(obj, string, queryData, Z_STRVAL_P(qdata));
771 RETURN_TRUE;
772 }
773 /* }}} */
774
775 /* {{{ proto string HTTPi_Request::getQueryData()
776 *
777 * Get the current query data in form of an urlencoded query string.
778 */
779 PHP_METHOD(HTTPi_Request, getQueryData)
780 {
781 zval *qdata;
782 getObject(httpi_request_object, obj);
783
784 NO_ARGS;
785
786 qdata = GET_PROP(obj, queryData);
787 RETURN_STRINGL(Z_STRVAL_P(qdata), Z_STRLEN_P(qdata), 1);
788 }
789 /* }}} */
790
791 /* {{{ proto bool HTTPi_Request::addQueryData(array query_params)
792 *
793 * Add parameters to the query parameter list.
794 * Affects any request type.
795 */
796 PHP_METHOD(HTTPi_Request, addQueryData)
797 {
798 zval *qdata, *old_qdata;
799 char *query_data = NULL;
800 getObject(httpi_request_object, obj);
801
802 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &qdata)) {
803 RETURN_FALSE;
804 }
805
806 old_qdata = GET_PROP(obj, queryData);
807
808 if (SUCCESS != http_urlencode_hash_ex(HASH_OF(qdata), 1, Z_STRVAL_P(old_qdata), Z_STRLEN_P(old_qdata), &query_data, NULL)) {
809 RETURN_FALSE;
810 }
811
812 UPD_PROP(obj, string, queryData, query_data);
813 efree(query_data);
814
815 RETURN_TRUE;
816 }
817 /* }}} */
818
819 /* {{{ proto void HTTPi_Request::unsetQueryData()
820 *
821 * Clean the query parameters.
822 * Affects any request type.
823 */
824 PHP_METHOD(HTTPi_Request, unsetQueryData)
825 {
826 getObject(httpi_request_object, obj);
827
828 NO_ARGS;
829
830 UPD_PROP(obj, string, queryData, "");
831 }
832 /* }}} */
833
834 /* {{{ proto bool HTTPi_Request::addPostData(array post_data)
835 *
836 * Adds POST data entries.
837 * Affects only POST requests.
838 */
839 PHP_METHOD(HTTPi_Request, addPostData)
840 {
841 zval *post, *post_data;
842 getObject(httpi_request_object, obj);
843
844 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &post_data)) {
845 RETURN_FALSE;
846 }
847
848 post = GET_PROP(obj, postData);
849 array_merge(post_data, post);
850
851 RETURN_TRUE;
852 }
853 /* }}} */
854
855 /* {{{ proto bool HTTPi_Request::setPostData(array post_data)
856 *
857 * Set the POST data entries.
858 * Overwrites previously set POST data.
859 * Affects only POST requests.
860 */
861 PHP_METHOD(HTTPi_Request, setPostData)
862 {
863 zval *post, *post_data;
864 getObject(httpi_request_object, obj);
865
866 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &post_data)) {
867 RETURN_FALSE;
868 }
869
870 post = GET_PROP(obj, postData);
871 zend_hash_clean(Z_ARRVAL_P(post));
872 array_copy(post_data, post);
873
874 RETURN_TRUE;
875 }
876 /* }}}*/
877
878 /* {{{ proto array HTTPi_Request::getPostData()
879 *
880 * Get previously set POST data.
881 */
882 PHP_METHOD(HTTPi_Request, getPostData)
883 {
884 zval *post_data;
885 getObject(httpi_request_object, obj);
886
887 NO_ARGS;
888
889 post_data = GET_PROP(obj, postData);
890 array_init(return_value);
891 array_copy(post_data, return_value);
892 }
893 /* }}} */
894
895 /* {{{ proto void HTTPi_Request::unsetPostData()
896 *
897 * Clean POST data entires.
898 * Affects only POST requests.
899 */
900 PHP_METHOD(HTTPi_Request, unsetPostData)
901 {
902 zval *post_data;
903 getObject(httpi_request_object, obj);
904
905 NO_ARGS;
906
907 post_data = GET_PROP(obj, postData);
908 zend_hash_clean(Z_ARRVAL_P(post_data));
909 }
910 /* }}} */
911
912 /* {{{ proto bool HTTPi_Request::addPostFile(string name, string file[, string content_type = "application/x-octetstream"])
913 *
914 * Add a file to the POST request.
915 * Affects only POST requests.
916 */
917 PHP_METHOD(HTTPi_Request, addPostFile)
918 {
919 zval *files, *entry;
920 char *name, *file, *type = NULL;
921 int name_len, file_len, type_len = 0;
922 getObject(httpi_request_object, obj);
923
924 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|s", &name, &name_len, &file, &file_len, &type, &type_len)) {
925 RETURN_FALSE;
926 }
927
928 if (type_len) {
929 if (!strchr(type, '/')) {
930 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Content-Type '%s' doesn't seem to contain a primary and a secondary part", type);
931 RETURN_FALSE;
932 }
933 } else {
934 type = "application/x-octetstream";
935 type_len = sizeof("application/x-octetstream") - 1;
936 }
937
938 MAKE_STD_ZVAL(entry);
939 array_init(entry);
940
941 add_assoc_stringl(entry, "name", name, name_len, 1);
942 add_assoc_stringl(entry, "type", type, type_len, 1);
943 add_assoc_stringl(entry, "file", file, file_len, 1);
944
945 files = GET_PROP(obj, postFiles);
946 add_next_index_zval(files, entry);
947
948 RETURN_TRUE;
949 }
950 /* }}} */
951
952 /* {{{ proto array HTTPi_Request::getPostFiles()
953 *
954 * Get all previously added POST files.
955 */
956 PHP_METHOD(HTTPi_Request, getPostFiles)
957 {
958 zval *files;
959 getObject(httpi_request_object, obj);
960
961 NO_ARGS;
962
963 files = GET_PROP(obj, postFiles);
964
965 array_init(return_value);
966 array_copy(files, return_value);
967 }
968 /* }}} */
969
970 /* {{{ proto void HTTPi_Request::unsetPostFiles()
971 *
972 * Unset the POST files list.
973 * Affects only POST requests.
974 */
975 PHP_METHOD(HTTPi_Request, unsetPostFiles)
976 {
977 zval *files;
978 getObject(httpi_request_object, obj);
979
980 NO_ARGS;
981
982 files = GET_PROP(obj, postFiles);
983 zend_hash_clean(Z_ARRVAL_P(files));
984 }
985 /* }}} */
986
987 /* {{{ proto array HTTPi_Request::getResponseData()
988 *
989 * Get all response data after the request has been sent.
990 */
991 PHP_METHOD(HTTPi_Request, getResponseData)
992 {
993 zval *data;
994 getObject(httpi_request_object, obj);
995
996 NO_ARGS;
997
998 data = GET_PROP(obj, responseData);
999 array_init(return_value);
1000 array_copy(data, return_value);
1001 }
1002 /* }}} */
1003
1004 /* {{{ proto string HTTPi_Request::getResponseHeader([string name])
1005 *
1006 * Get response header(s) after the request has been sent.
1007 */
1008 PHP_METHOD(HTTPi_Request, getResponseHeader)
1009 {
1010 zval *data, **headers, **header;
1011 char *header_name = NULL;
1012 int header_len = 0;
1013 getObject(httpi_response_object, obj);
1014
1015 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &header_name, &header_len)) {
1016 RETURN_FALSE;
1017 }
1018
1019 data = GET_PROP(obj, responseData);
1020 if (SUCCESS != zend_hash_find(Z_ARRVAL_P(data), "headers", sizeof("headers"), (void **) &headers)) {
1021 RETURN_FALSE;
1022 }
1023
1024 if (!header_len || !header_name) {
1025 array_init(return_value);
1026 array_copy(*headers, return_value);
1027 } else if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(headers), pretty_key(header_name, header_len, 1, 1), header_len + 1, (void **) &header)) {
1028 RETURN_STRINGL(Z_STRVAL_PP(header), Z_STRLEN_PP(header), 1);
1029 } else {
1030 RETURN_FALSE;
1031 }
1032 }
1033
1034 /* {{{ proto string HTTPi_Request::getResponseBody()
1035 *
1036 * Get the response body after the request has been sent.
1037 */
1038 PHP_METHOD(HTTPi_Request, getResponseBody)
1039 {
1040 zval *data, **body;
1041 getObject(httpi_request_object, obj);
1042
1043 NO_ARGS;
1044
1045 data = GET_PROP(obj, responseData);
1046 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(data), "body", sizeof("body"), (void **) &body)) {
1047 RETURN_STRINGL(Z_STRVAL_PP(body), Z_STRLEN_PP(body), 1);
1048 } else {
1049 RETURN_FALSE;
1050 }
1051 }
1052 /* }}} */
1053
1054 /* {{{ proto int HTTPi_Request::getResponseCode()
1055 *
1056 * Get the response code after the request has been sent.
1057 */
1058 PHP_METHOD(HTTPi_Request, getResponseCode)
1059 {
1060 zval **code, **hdrs, *data;
1061 getObject(httpi_request_object, obj);
1062
1063 NO_ARGS;
1064
1065 data = GET_PROP(obj, responseData);
1066 if ( (SUCCESS == zend_hash_find(Z_ARRVAL_P(data), "headers", sizeof("headers"), (void **) &hdrs)) &&
1067 (SUCCESS == zend_hash_find(Z_ARRVAL_PP(hdrs), "Status", sizeof("Status"), (void **) &code))) {
1068 RETVAL_STRINGL(Z_STRVAL_PP(code), Z_STRLEN_PP(code), 1);
1069 convert_to_long(return_value);
1070 } else {
1071 RETURN_FALSE;
1072 }
1073 }
1074 /* }}} */
1075
1076 /* {{{ proto array HTTPi_Request::getResponseInfo([string name])
1077 *
1078 * Get response info after the request has been sent.
1079 * See http_get() for a full list of returned info.
1080 */
1081 PHP_METHOD(HTTPi_Request, getResponseInfo)
1082 {
1083 zval *info, **infop;
1084 char *info_name = NULL;
1085 int info_len = 0;
1086 getObject(httpi_request_object, obj);
1087
1088 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &info_name, &info_len)) {
1089 RETURN_FALSE;
1090 }
1091
1092 info = GET_PROP(obj, responseInfo);
1093
1094 if (info_len && info_name) {
1095 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(info), pretty_key(info_name, info_len, 0, 0), info_len + 1, (void **) &infop)) {
1096 RETURN_ZVAL(*infop, 1, ZVAL_PTR_DTOR);
1097 } else {
1098 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not find response info named %s", info_name);
1099 RETURN_FALSE;
1100 }
1101 } else {
1102 array_init(return_value);
1103 array_copy(info, return_value);
1104 }
1105 }
1106 /* }}}*/
1107
1108 /* {{{ proto bool HTTPi_Request::send()
1109 *
1110 * Send the HTTP request.
1111 *
1112 * GET example:
1113 * <pre>
1114 * <?php
1115 * $r = new HTTPi_Request('http://example.com/feed.rss', HTTP_GET);
1116 * $r->setOptions(array('lastmodified' => filemtime('local.rss')));
1117 * $r->addQueryData(array('category' => 3));
1118 * if ($r->send() && $r->getResponseCode() == 200) {
1119 * file_put_contents('local.rss', $r->getResponseBody());
1120 * }
1121 * ?>
1122 * </pre>
1123 *
1124 * POST example:
1125 * <pre>
1126 * <?php
1127 * $r = new HTTPi_Request('http://example.com/form.php', HTTP_POST);
1128 * $r->setOptions(array('cookies' => array('lang' => 'de')));
1129 * $r->addPostData(array('user' => 'mike', 'pass' => 's3c|r3t'));
1130 * $r->addPostFile('image', 'profile.jpg', 'image/jpeg');
1131 * if ($r->send()) {
1132 * echo $r->getResponseBody();
1133 * }
1134 * ?>
1135 * </pre>
1136 */
1137 PHP_METHOD(HTTPi_Request, send)
1138 {
1139 STATUS status = FAILURE;
1140 zval *meth, *URL, *qdata, *opts, *info, *resp;
1141 char *response_data, *request_uri;
1142 size_t response_len;
1143 getObject(httpi_request_object, obj);
1144
1145 NO_ARGS;
1146
1147 if ((!obj->ch) && (!(obj->ch = curl_easy_init()))) {
1148 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initilaize cURL");
1149 RETURN_FALSE;
1150 }
1151
1152 meth = GET_PROP(obj, method);
1153 URL = GET_PROP(obj, url);
1154 qdata = GET_PROP(obj, queryData);
1155 opts = GET_PROP(obj, options);
1156 info = GET_PROP(obj, responseInfo);
1157 resp = GET_PROP(obj, responseData);
1158
1159 // HTTP_URI_MAXLEN+1 long char *
1160 request_uri = http_absolute_uri_ex(Z_STRVAL_P(URL), Z_STRLEN_P(URL), NULL, 0, NULL, 0, 0);
1161
1162 if (Z_STRLEN_P(qdata) && (strlen(request_uri) < HTTP_URI_MAXLEN)) {
1163 if (!strchr(request_uri, '?')) {
1164 strcat(request_uri, "?");
1165 } else {
1166 strcat(request_uri, "&");
1167 }
1168 strncat(request_uri, Z_STRVAL_P(qdata), HTTP_URI_MAXLEN - strlen(request_uri));
1169 }
1170
1171 switch (Z_LVAL_P(meth))
1172 {
1173 case HTTP_GET:
1174 status = http_get_ex(obj->ch, request_uri, Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len);
1175 break;
1176
1177 case HTTP_HEAD:
1178 status = http_head_ex(obj->ch, request_uri, Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len);
1179 break;
1180
1181 case HTTP_POST:
1182 {
1183 zval *post_files, *post_data;
1184
1185 post_files = GET_PROP(obj, postFiles);
1186 post_data = GET_PROP(obj, postData);
1187
1188 if (!zend_hash_num_elements(Z_ARRVAL_P(post_files))) {
1189
1190 /* urlencoded post */
1191 status = http_post_array_ex(obj->ch, request_uri, Z_ARRVAL_P(post_data), Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len);
1192
1193 } else {
1194
1195 /*
1196 * multipart post
1197 */
1198 char *key = NULL;
1199 long idx;
1200 zval **data;
1201 struct curl_httppost *http_post_data[2] = {NULL, NULL};
1202
1203 /* normal data */
1204 FOREACH_KEYVAL(post_data, key, idx, data) {
1205 if (key) {
1206 convert_to_string_ex(data);
1207 curl_formadd(&http_post_data[0], &http_post_data[1],
1208 CURLFORM_COPYNAME, key,
1209 CURLFORM_COPYCONTENTS, Z_STRVAL_PP(data),
1210 CURLFORM_CONTENTSLENGTH, Z_STRLEN_PP(data),
1211 CURLFORM_END
1212 );
1213
1214 /* reset */
1215 key = NULL;
1216 }
1217 }
1218
1219 /* file data */
1220 FOREACH_VAL(post_files, data) {
1221 zval **file, **type, **name;
1222
1223 if ( SUCCESS == zend_hash_find(Z_ARRVAL_PP(data), "name", sizeof("name"), (void **) &name) &&
1224 SUCCESS == zend_hash_find(Z_ARRVAL_PP(data), "type", sizeof("type"), (void **) &type) &&
1225 SUCCESS == zend_hash_find(Z_ARRVAL_PP(data), "file", sizeof("file"), (void **) &file)) {
1226
1227 curl_formadd(&http_post_data[0], &http_post_data[1],
1228 CURLFORM_COPYNAME, Z_STRVAL_PP(name),
1229 CURLFORM_FILE, Z_STRVAL_PP(file),
1230 CURLFORM_CONTENTTYPE, Z_STRVAL_PP(type),
1231 CURLFORM_END
1232 );
1233 }
1234 }
1235
1236 status = http_post_curldata_ex(obj->ch, request_uri, http_post_data[0], Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len);
1237 curl_formfree(http_post_data[0]);
1238 }
1239 }
1240 break;
1241
1242 default:
1243 break;
1244 }
1245
1246 efree(request_uri);
1247
1248 /* final data handling */
1249 if (status != SUCCESS) {
1250 RETURN_FALSE;
1251 } else {
1252 char *body = NULL;
1253 size_t body_len = 0;
1254 zval *zheaders;
1255
1256 MAKE_STD_ZVAL(zheaders)
1257 array_init(zheaders);
1258
1259 if (SUCCESS != http_split_response_ex(response_data, response_len, Z_ARRVAL_P(zheaders), &body, &body_len)) {
1260 zval_dtor(zheaders);
1261 efree(zheaders),
1262 efree(response_data);
1263 RETURN_FALSE;
1264 }
1265
1266 add_assoc_zval(resp, "headers", zheaders);
1267 add_assoc_stringl(resp, "body", body, body_len, 0);
1268
1269 efree(response_data);
1270
1271 RETURN_TRUE;
1272 }
1273 /* */
1274 }
1275 /* }}} */
1276 /* }}} */
1277 #endif /* HTTP_HAVE_CURL */
1278
1279 #endif /* ZEND_ENGINE_2 */
1280
1281 /*
1282 * Local variables:
1283 * tab-width: 4
1284 * c-basic-offset: 4
1285 * End:
1286 * vim600: noet sw=4 ts=4 fdm=marker
1287 * vim<600: noet sw=4 ts=4
1288 */
1289