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