*build files
[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 void HTTPi_Request::unsetOptions()
632 *
633 * Unset all options/headers/cookies.
634 */
635 PHP_METHOD(HTTPi_Request, unsetOptions)
636 {
637 getObject(httpi_request_object, obj);
638
639 NO_ARGS;
640
641 FREE_PARR(obj, options);
642 INIT_PARR(obj, options);
643 }
644 /* }}} */
645
646 /* {{{ proto bool HTTPi_Request::addHeader(array header)
647 *
648 * Add (a) request header name/value pair(s).
649 */
650 PHP_METHOD(HTTPi_Request, addHeader)
651 {
652 zval *opts, **headers, *new_headers;
653 getObject(httpi_request_object, obj);
654
655 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &new_headers)) {
656 RETURN_FALSE;
657 }
658
659 opts = GET_PROP(obj, options);
660
661 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(opts), "headers", sizeof("headers"), (void **) &headers)) {
662 array_merge(new_headers, *headers);
663 } else {
664 zval_add_ref(&new_headers);
665 add_assoc_zval(opts, "headers", new_headers);
666 }
667
668 RETURN_TRUE;
669 }
670 /* }}} */
671
672 /* {{{ proto bool HTTPi_Request::addCookie(array cookie)
673 *
674 * Add (a) cookie(s).
675 */
676 PHP_METHOD(HTTPi_Request, addCookie)
677 {
678 zval *opts, **cookies, *new_cookies;
679 getObject(httpi_request_object, obj);
680
681 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &new_cookies)) {
682 RETURN_FALSE;
683 }
684
685 opts = GET_PROP(obj, options);
686
687 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(opts), "cookies", sizeof("cookies"), (void **) &cookies)) {
688 array_merge(new_cookies, *cookies);
689 } else {
690 zval_add_ref(&new_cookies);
691 add_assoc_zval(opts, "cookies", new_cookies);
692 }
693
694 RETURN_TRUE;
695 }
696 /* }}} */
697
698 /* {{{ proto bool HTTPi_Request::setURL(string url)
699 *
700 * Set the request URL.
701 */
702 PHP_METHOD(HTTPi_Request, setURL)
703 {
704 char *URL = NULL;
705 int URL_len;
706 getObject(httpi_request_object, obj);
707
708 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &URL, &URL_len)) {
709 RETURN_FALSE;
710 }
711
712 UPD_PROP(obj, string, url, URL);
713 RETURN_TRUE;
714 }
715 /* }}} */
716
717 /* {{{ proto string HTTPi_Request::getUrl()
718 *
719 * Get the previously set request URL.
720 */
721 PHP_METHOD(HTTPi_Request, getURL)
722 {
723 zval *URL;
724 getObject(httpi_request_object, obj);
725
726 NO_ARGS;
727
728 URL = GET_PROP(obj, url);
729 RETURN_STRINGL(Z_STRVAL_P(URL), Z_STRLEN_P(URL), 1);
730 }
731 /* }}} */
732
733 /* {{{ proto bool HTTPi_Request::setMethod(long request_method)
734 *
735 * Set the request methods; one of the <tt>HTTP_HEAD</tt>, <tt>HTTP_GET</tt> or
736 * <tt>HTTP_POST</tt> constants.
737 */
738 PHP_METHOD(HTTPi_Request, setMethod)
739 {
740 long meth;
741 getObject(httpi_request_object, obj);
742
743 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &meth)) {
744 RETURN_FALSE;
745 }
746
747 UPD_PROP(obj, long, method, meth);
748 RETURN_TRUE;
749 }
750 /* }}} */
751
752 /* {{{ proto long HTTPi_Request::getMethod()
753 *
754 * Get the previously set request method.
755 */
756 PHP_METHOD(HTTPi_Request, getMethod)
757 {
758 zval *meth;
759 getObject(httpi_request_object, obj);
760
761 NO_ARGS;
762
763 meth = GET_PROP(obj, method);
764 RETURN_LONG(Z_LVAL_P(meth));
765 }
766 /* }}} */
767
768 /* {{{ proto bool HTTPi_Request::setContentType(string content_type)
769 *
770 * Set the content type the post request should have.
771 * Use this only if you know what you're doing.
772 */
773 PHP_METHOD(HTTPi_Request, setContentType)
774 {
775 char *ctype;
776 int ct_len;
777 getObject(httpi_request_object, obj);
778
779 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ctype, &ct_len)) {
780 RETURN_FALSE;
781 }
782
783 if (!strchr(ctype, '/')) {
784 php_error_docref(NULL TSRMLS_CC, E_WARNING,
785 "Content-Type '%s' doesn't seem to contain a primary and a secondary part",
786 ctype);
787 RETURN_FALSE;
788 }
789
790 UPD_PROP(obj, string, contentType, ctype);
791 RETURN_TRUE;
792 }
793 /* }}} */
794
795 /* {{{ proto string HTTPi_Request::getContentType()
796 *
797 * Get the previously content type.
798 */
799 PHP_METHOD(HTTPi_Request, getContentType)
800 {
801 zval *ctype;
802 getObject(httpi_request_object, obj);
803
804 NO_ARGS;
805
806 ctype = GET_PROP(obj, contentType);
807 RETURN_STRINGL(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1);
808 }
809 /* }}} */
810
811 /* {{{ proto bool HTTPi_Request::setQueryData(mixed query_data)
812 *
813 * Set the URL query parameters to use.
814 * Overwrites previously set query parameters.
815 * Affects any request types.
816 */
817 PHP_METHOD(HTTPi_Request, setQueryData)
818 {
819 zval *qdata;
820 char *query_data = NULL;
821 getObject(httpi_request_object, obj);
822
823 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &qdata)) {
824 RETURN_FALSE;
825 }
826
827 if ((Z_TYPE_P(qdata) == IS_ARRAY) || (Z_TYPE_P(qdata) == IS_OBJECT)) {
828 if (SUCCESS != http_urlencode_hash(HASH_OF(qdata), &query_data)) {
829 RETURN_FALSE;
830 }
831 UPD_PROP(obj, string, queryData, query_data);
832 efree(query_data);
833 RETURN_TRUE;
834 }
835
836 convert_to_string(qdata);
837 UPD_PROP(obj, string, queryData, Z_STRVAL_P(qdata));
838 RETURN_TRUE;
839 }
840 /* }}} */
841
842 /* {{{ proto string HTTPi_Request::getQueryData()
843 *
844 * Get the current query data in form of an urlencoded query string.
845 */
846 PHP_METHOD(HTTPi_Request, getQueryData)
847 {
848 zval *qdata;
849 getObject(httpi_request_object, obj);
850
851 NO_ARGS;
852
853 qdata = GET_PROP(obj, queryData);
854 RETURN_STRINGL(Z_STRVAL_P(qdata), Z_STRLEN_P(qdata), 1);
855 }
856 /* }}} */
857
858 /* {{{ proto bool HTTPi_Request::addQueryData(array query_params)
859 *
860 * Add parameters to the query parameter list.
861 * Affects any request type.
862 */
863 PHP_METHOD(HTTPi_Request, addQueryData)
864 {
865 zval *qdata, *old_qdata;
866 char *query_data = NULL;
867 getObject(httpi_request_object, obj);
868
869 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &qdata)) {
870 RETURN_FALSE;
871 }
872
873 old_qdata = GET_PROP(obj, queryData);
874
875 if (SUCCESS != http_urlencode_hash_ex(HASH_OF(qdata), 1, Z_STRVAL_P(old_qdata), Z_STRLEN_P(old_qdata), &query_data, NULL)) {
876 RETURN_FALSE;
877 }
878
879 UPD_PROP(obj, string, queryData, query_data);
880 efree(query_data);
881
882 RETURN_TRUE;
883 }
884 /* }}} */
885
886 /* {{{ proto void HTTPi_Request::unsetQueryData()
887 *
888 * Clean the query parameters.
889 * Affects any request type.
890 */
891 PHP_METHOD(HTTPi_Request, unsetQueryData)
892 {
893 getObject(httpi_request_object, obj);
894
895 NO_ARGS;
896
897 UPD_PROP(obj, string, queryData, "");
898 }
899 /* }}} */
900
901 /* {{{ proto bool HTTPi_Request::addPostData(array post_data)
902 *
903 * Adds POST data entries.
904 * Affects only POST requests.
905 */
906 PHP_METHOD(HTTPi_Request, addPostData)
907 {
908 zval *post, *post_data;
909 getObject(httpi_request_object, obj);
910
911 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &post_data)) {
912 RETURN_FALSE;
913 }
914
915 post = GET_PROP(obj, postData);
916 array_merge(post_data, post);
917
918 RETURN_TRUE;
919 }
920 /* }}} */
921
922 /* {{{ proto bool HTTPi_Request::setPostData(array post_data)
923 *
924 * Set the POST data entries.
925 * Overwrites previously set POST data.
926 * Affects only POST requests.
927 */
928 PHP_METHOD(HTTPi_Request, setPostData)
929 {
930 zval *post, *post_data;
931 getObject(httpi_request_object, obj);
932
933 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &post_data)) {
934 RETURN_FALSE;
935 }
936
937 post = GET_PROP(obj, postData);
938 zend_hash_clean(Z_ARRVAL_P(post));
939 array_copy(post_data, post);
940
941 RETURN_TRUE;
942 }
943 /* }}}*/
944
945 /* {{{ proto array HTTPi_Request::getPostData()
946 *
947 * Get previously set POST data.
948 */
949 PHP_METHOD(HTTPi_Request, getPostData)
950 {
951 zval *post_data;
952 getObject(httpi_request_object, obj);
953
954 NO_ARGS;
955
956 post_data = GET_PROP(obj, postData);
957 array_init(return_value);
958 array_copy(post_data, return_value);
959 }
960 /* }}} */
961
962 /* {{{ proto void HTTPi_Request::unsetPostData()
963 *
964 * Clean POST data entires.
965 * Affects only POST requests.
966 */
967 PHP_METHOD(HTTPi_Request, unsetPostData)
968 {
969 zval *post_data;
970 getObject(httpi_request_object, obj);
971
972 NO_ARGS;
973
974 post_data = GET_PROP(obj, postData);
975 zend_hash_clean(Z_ARRVAL_P(post_data));
976 }
977 /* }}} */
978
979 /* {{{ proto bool HTTPi_Request::addPostFile(string name, string file[, string content_type = "application/x-octetstream"])
980 *
981 * Add a file to the POST request.
982 * Affects only POST requests.
983 */
984 PHP_METHOD(HTTPi_Request, addPostFile)
985 {
986 zval *files, *entry;
987 char *name, *file, *type = NULL;
988 int name_len, file_len, type_len = 0;
989 getObject(httpi_request_object, obj);
990
991 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|s", &name, &name_len, &file, &file_len, &type, &type_len)) {
992 RETURN_FALSE;
993 }
994
995 if (type_len) {
996 if (!strchr(type, '/')) {
997 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Content-Type '%s' doesn't seem to contain a primary and a secondary part", type);
998 RETURN_FALSE;
999 }
1000 } else {
1001 type = "application/x-octetstream";
1002 type_len = sizeof("application/x-octetstream") - 1;
1003 }
1004
1005 MAKE_STD_ZVAL(entry);
1006 array_init(entry);
1007
1008 add_assoc_stringl(entry, "name", name, name_len, 1);
1009 add_assoc_stringl(entry, "type", type, type_len, 1);
1010 add_assoc_stringl(entry, "file", file, file_len, 1);
1011
1012 files = GET_PROP(obj, postFiles);
1013 add_next_index_zval(files, entry);
1014
1015 RETURN_TRUE;
1016 }
1017 /* }}} */
1018
1019 /* {{{ proto array HTTPi_Request::getPostFiles()
1020 *
1021 * Get all previously added POST files.
1022 */
1023 PHP_METHOD(HTTPi_Request, getPostFiles)
1024 {
1025 zval *files;
1026 getObject(httpi_request_object, obj);
1027
1028 NO_ARGS;
1029
1030 files = GET_PROP(obj, postFiles);
1031
1032 array_init(return_value);
1033 array_copy(files, return_value);
1034 }
1035 /* }}} */
1036
1037 /* {{{ proto void HTTPi_Request::unsetPostFiles()
1038 *
1039 * Unset the POST files list.
1040 * Affects only POST requests.
1041 */
1042 PHP_METHOD(HTTPi_Request, unsetPostFiles)
1043 {
1044 zval *files;
1045 getObject(httpi_request_object, obj);
1046
1047 NO_ARGS;
1048
1049 files = GET_PROP(obj, postFiles);
1050 zend_hash_clean(Z_ARRVAL_P(files));
1051 }
1052 /* }}} */
1053
1054 /* {{{ proto array HTTPi_Request::getResponseData()
1055 *
1056 * Get all response data after the request has been sent.
1057 */
1058 PHP_METHOD(HTTPi_Request, getResponseData)
1059 {
1060 zval *data;
1061 getObject(httpi_request_object, obj);
1062
1063 NO_ARGS;
1064
1065 data = GET_PROP(obj, responseData);
1066 array_init(return_value);
1067 array_copy(data, return_value);
1068 }
1069 /* }}} */
1070
1071 /* {{{ proto string HTTPi_Request::getResponseHeader([string name])
1072 *
1073 * Get response header(s) after the request has been sent.
1074 */
1075 PHP_METHOD(HTTPi_Request, getResponseHeader)
1076 {
1077 zval *data, **headers, **header;
1078 char *header_name = NULL;
1079 int header_len = 0;
1080 getObject(httpi_response_object, obj);
1081
1082 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &header_name, &header_len)) {
1083 RETURN_FALSE;
1084 }
1085
1086 data = GET_PROP(obj, responseData);
1087 if (SUCCESS != zend_hash_find(Z_ARRVAL_P(data), "headers", sizeof("headers"), (void **) &headers)) {
1088 RETURN_FALSE;
1089 }
1090
1091 if (!header_len || !header_name) {
1092 array_init(return_value);
1093 array_copy(*headers, return_value);
1094 } else if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(headers), pretty_key(header_name, header_len, 1, 1), header_len + 1, (void **) &header)) {
1095 RETURN_STRINGL(Z_STRVAL_PP(header), Z_STRLEN_PP(header), 1);
1096 } else {
1097 RETURN_FALSE;
1098 }
1099 }
1100
1101 /* {{{ proto string HTTPi_Request::getResponseBody()
1102 *
1103 * Get the response body after the request has been sent.
1104 */
1105 PHP_METHOD(HTTPi_Request, getResponseBody)
1106 {
1107 zval *data, **body;
1108 getObject(httpi_request_object, obj);
1109
1110 NO_ARGS;
1111
1112 data = GET_PROP(obj, responseData);
1113 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(data), "body", sizeof("body"), (void **) &body)) {
1114 RETURN_STRINGL(Z_STRVAL_PP(body), Z_STRLEN_PP(body), 1);
1115 } else {
1116 RETURN_FALSE;
1117 }
1118 }
1119 /* }}} */
1120
1121 /* {{{ proto int HTTPi_Request::getResponseCode()
1122 *
1123 * Get the response code after the request has been sent.
1124 */
1125 PHP_METHOD(HTTPi_Request, getResponseCode)
1126 {
1127 zval **code, **hdrs, *data;
1128 getObject(httpi_request_object, obj);
1129
1130 NO_ARGS;
1131
1132 data = GET_PROP(obj, responseData);
1133 if ( (SUCCESS == zend_hash_find(Z_ARRVAL_P(data), "headers", sizeof("headers"), (void **) &hdrs)) &&
1134 (SUCCESS == zend_hash_find(Z_ARRVAL_PP(hdrs), "Status", sizeof("Status"), (void **) &code))) {
1135 RETVAL_STRINGL(Z_STRVAL_PP(code), Z_STRLEN_PP(code), 1);
1136 convert_to_long(return_value);
1137 } else {
1138 RETURN_FALSE;
1139 }
1140 }
1141 /* }}} */
1142
1143 /* {{{ proto array HTTPi_Request::getResponseInfo([string name])
1144 *
1145 * Get response info after the request has been sent.
1146 * See http_get() for a full list of returned info.
1147 */
1148 PHP_METHOD(HTTPi_Request, getResponseInfo)
1149 {
1150 zval *info, **infop;
1151 char *info_name = NULL;
1152 int info_len = 0;
1153 getObject(httpi_request_object, obj);
1154
1155 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &info_name, &info_len)) {
1156 RETURN_FALSE;
1157 }
1158
1159 info = GET_PROP(obj, responseInfo);
1160
1161 if (info_len && info_name) {
1162 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(info), pretty_key(info_name, info_len, 0, 0), info_len + 1, (void **) &infop)) {
1163 RETURN_ZVAL(*infop, 1, ZVAL_PTR_DTOR);
1164 } else {
1165 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not find response info named %s", info_name);
1166 RETURN_FALSE;
1167 }
1168 } else {
1169 array_init(return_value);
1170 array_copy(info, return_value);
1171 }
1172 }
1173 /* }}}*/
1174
1175 /* {{{ proto bool HTTPi_Request::send()
1176 *
1177 * Send the HTTP request.
1178 *
1179 * GET example:
1180 * <pre>
1181 * <?php
1182 * $r = new HTTPi_Request('http://example.com/feed.rss', HTTP_GET);
1183 * $r->setOptions(array('lastmodified' => filemtime('local.rss')));
1184 * $r->addQueryData(array('category' => 3));
1185 * if ($r->send() && $r->getResponseCode() == 200) {
1186 * file_put_contents('local.rss', $r->getResponseBody());
1187 * }
1188 * ?>
1189 * </pre>
1190 *
1191 * POST example:
1192 * <pre>
1193 * <?php
1194 * $r = new HTTPi_Request('http://example.com/form.php', HTTP_POST);
1195 * $r->setOptions(array('cookies' => array('lang' => 'de')));
1196 * $r->addPostData(array('user' => 'mike', 'pass' => 's3c|r3t'));
1197 * $r->addPostFile('image', 'profile.jpg', 'image/jpeg');
1198 * if ($r->send()) {
1199 * echo $r->getResponseBody();
1200 * }
1201 * ?>
1202 * </pre>
1203 */
1204 PHP_METHOD(HTTPi_Request, send)
1205 {
1206 STATUS status = FAILURE;
1207 zval *meth, *URL, *qdata, *opts, *info, *resp;
1208 char *response_data, *request_uri;
1209 size_t response_len;
1210 getObject(httpi_request_object, obj);
1211
1212 NO_ARGS;
1213
1214 if ((!obj->ch) && (!(obj->ch = curl_easy_init()))) {
1215 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initilaize cURL");
1216 RETURN_FALSE;
1217 }
1218
1219 meth = GET_PROP(obj, method);
1220 URL = GET_PROP(obj, url);
1221 qdata = GET_PROP(obj, queryData);
1222 opts = GET_PROP(obj, options);
1223 info = GET_PROP(obj, responseInfo);
1224 resp = GET_PROP(obj, responseData);
1225
1226 // HTTP_URI_MAXLEN+1 long char *
1227 request_uri = http_absolute_uri_ex(Z_STRVAL_P(URL), Z_STRLEN_P(URL), NULL, 0, NULL, 0, 0);
1228
1229 if (Z_STRLEN_P(qdata) && (strlen(request_uri) < HTTP_URI_MAXLEN)) {
1230 if (!strchr(request_uri, '?')) {
1231 strcat(request_uri, "?");
1232 } else {
1233 strcat(request_uri, "&");
1234 }
1235 strncat(request_uri, Z_STRVAL_P(qdata), HTTP_URI_MAXLEN - strlen(request_uri));
1236 }
1237
1238 switch (Z_LVAL_P(meth))
1239 {
1240 case HTTP_GET:
1241 status = http_get_ex(obj->ch, request_uri, Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len);
1242 break;
1243
1244 case HTTP_HEAD:
1245 status = http_head_ex(obj->ch, request_uri, Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len);
1246 break;
1247
1248 case HTTP_POST:
1249 {
1250 zval *post_files, *post_data;
1251
1252 post_files = GET_PROP(obj, postFiles);
1253 post_data = GET_PROP(obj, postData);
1254
1255 if (!zend_hash_num_elements(Z_ARRVAL_P(post_files))) {
1256
1257 /* urlencoded post */
1258 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);
1259
1260 } else {
1261
1262 /*
1263 * multipart post
1264 */
1265 char *key = NULL;
1266 long idx;
1267 zval **data;
1268 struct curl_httppost *http_post_data[2] = {NULL, NULL};
1269
1270 /* normal data */
1271 FOREACH_KEYVAL(post_data, key, idx, data) {
1272 if (key) {
1273 convert_to_string_ex(data);
1274 curl_formadd(&http_post_data[0], &http_post_data[1],
1275 CURLFORM_COPYNAME, key,
1276 CURLFORM_COPYCONTENTS, Z_STRVAL_PP(data),
1277 CURLFORM_CONTENTSLENGTH, Z_STRLEN_PP(data),
1278 CURLFORM_END
1279 );
1280
1281 /* reset */
1282 key = NULL;
1283 }
1284 }
1285
1286 /* file data */
1287 FOREACH_VAL(post_files, data) {
1288 zval **file, **type, **name;
1289
1290 if ( SUCCESS == zend_hash_find(Z_ARRVAL_PP(data), "name", sizeof("name"), (void **) &name) &&
1291 SUCCESS == zend_hash_find(Z_ARRVAL_PP(data), "type", sizeof("type"), (void **) &type) &&
1292 SUCCESS == zend_hash_find(Z_ARRVAL_PP(data), "file", sizeof("file"), (void **) &file)) {
1293
1294 curl_formadd(&http_post_data[0], &http_post_data[1],
1295 CURLFORM_COPYNAME, Z_STRVAL_PP(name),
1296 CURLFORM_FILE, Z_STRVAL_PP(file),
1297 CURLFORM_CONTENTTYPE, Z_STRVAL_PP(type),
1298 CURLFORM_END
1299 );
1300 }
1301 }
1302
1303 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);
1304 curl_formfree(http_post_data[0]);
1305 }
1306 }
1307 break;
1308
1309 default:
1310 break;
1311 }
1312
1313 efree(request_uri);
1314
1315 /* final data handling */
1316 if (status != SUCCESS) {
1317 RETURN_FALSE;
1318 } else {
1319 char *body = NULL;
1320 size_t body_len = 0;
1321 zval *zheaders;
1322
1323 MAKE_STD_ZVAL(zheaders)
1324 array_init(zheaders);
1325
1326 if (SUCCESS != http_split_response_ex(response_data, response_len, Z_ARRVAL_P(zheaders), &body, &body_len)) {
1327 zval_dtor(zheaders);
1328 efree(zheaders),
1329 efree(response_data);
1330 RETURN_FALSE;
1331 }
1332
1333 add_assoc_zval(resp, "headers", zheaders);
1334 add_assoc_stringl(resp, "body", body, body_len, 0);
1335
1336 efree(response_data);
1337
1338 RETURN_TRUE;
1339 }
1340 /* */
1341 }
1342 /* }}} */
1343 /* }}} */
1344 #endif /* HTTP_HAVE_CURL */
1345
1346 #endif /* ZEND_ENGINE_2 */
1347
1348 /*
1349 * Local variables:
1350 * tab-width: 4
1351 * c-basic-offset: 4
1352 * End:
1353 * vim600: noet sw=4 ts=4 fdm=marker
1354 * vim<600: noet sw=4 ts=4
1355 */
1356