- fix possible crash with http_redirect() and http_build_url() called without parameters
[m6w6/ext-http] / http_functions.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2006, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #define HTTP_WANT_SAPI
16 #define HTTP_WANT_CURL
17 #define HTTP_WANT_ZLIB
18 #include "php_http.h"
19
20 #include "php_ini.h"
21 #include "ext/standard/php_string.h"
22 #include "zend_operators.h"
23
24 #ifdef HAVE_PHP_SESSION
25 # include "ext/session/php_session.h"
26 #endif
27
28 #include "php_http_api.h"
29 #include "php_http_cache_api.h"
30 #include "php_http_cookie_api.h"
31 #include "php_http_date_api.h"
32 #include "php_http_encoding_api.h"
33 #include "php_http_headers_api.h"
34 #include "php_http_message_api.h"
35 #include "php_http_request_api.h"
36 #include "php_http_request_method_api.h"
37 #include "php_http_send_api.h"
38 #include "php_http_url_api.h"
39
40 /* {{{ proto string http_date([int timestamp])
41 *
42 * Compose a valid HTTP date regarding RFC 1123
43 * looking like: "Wed, 22 Dec 2004 11:34:47 GMT"
44 *
45 * Accepts an optional unix timestamp as parameter.
46 *
47 * Returns the HTTP date as string.
48 */
49 PHP_FUNCTION(http_date)
50 {
51 long t = -1;
52
53 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) {
54 RETURN_FALSE;
55 }
56
57 if (t == -1) {
58 t = (long) HTTP_GET_REQUEST_TIME();
59 }
60
61 RETURN_STRING(http_date(t), 0);
62 }
63 /* }}} */
64
65 /* {{{ proto string http_build_url([mixed url[, mixed parts[, int flags = HTTP_URL_REPLACE[, array &new_url]]]])
66 *
67 * Build an URL.
68 *
69 * Expexts (part(s) of) an URL as first parameter in form of a string or assoziative array
70 * like parse_url() returns. Accepts an optional second parameter in the same way as the
71 * first argument. Accepts an optional third integer parameter, which is a bitmask of
72 * binary or'ed HTTP_URL_* constants. The optional fourth parameter will be filled
73 * with the results as associative array like parse_url() would return.
74 *
75 * The parts of the second URL will be merged into the first according to the flags argument.
76 * The following flags are recognized:
77 * <pre>
78 * - HTTP_URL_REPLACE: (default) set parts of the second url will replace the parts in the first
79 * - HTTP_URL_JOIN_PATH: the path of the second url will be merged into the one of the first
80 * - HTTP_URL_JOIN_QUERY: the two querystrings will be merged naivly; no replacements are done
81 * - HTTP_URL_STRIP_USER: the user part will not appear in the result
82 * - HTTP_URL_STRIP_PASS: the password part will not appear in the result
83 * - HTTP_URL_STRIP_AUTH: neither the user nor the password part will appear in the result
84 * - HTTP_URL_STRIP_PORT: no explicit port will be set in the result
85 * - HTTP_URL_STRIP_PATH: the path part will not appear in the result
86 * - HTTP_URL_STRIP_QUERY: no query string will be present in the result
87 * - HTTP_URL_STRIP_FRAGMENT: no fragment will be present in the result
88 * </pre>
89 *
90 * Example:
91 * <pre>
92 * <?php
93 * // ftp://ftp.example.com/pub/files/current/?a=b&a=c
94 * echo http_build_url("http://user@www.example.com/pub/index.php?a=b#files",
95 * array(
96 * "scheme" => "ftp",
97 * "host" => "ftp.example.com",
98 * "path" => "files/current/",
99 * "query" => "a=c"
100 * ),
101 * HTTP_URL_STRIP_AUTH | HTTP_URL_JOIN_PATH | HTTP_URL_JOIN_QUERY | HTTP_URL_STRIP_FRAGMENT
102 * );
103 * ?>
104 * </pre>
105 * Returns the new URL as string on success or FALSE on failure.
106 */
107 PHP_FUNCTION(http_build_url)
108 {
109 char *url_str = NULL;
110 size_t url_len = 0;
111 long flags = HTTP_URL_REPLACE;
112 zval *z_old_url = NULL, *z_new_url = NULL, *z_composed_url = NULL;
113 php_url *old_url = NULL, *new_url = NULL, *composed_url = NULL;
114
115 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/z/lz", &z_old_url, &z_new_url, &flags, &z_composed_url) != SUCCESS) {
116 RETURN_FALSE;
117 }
118
119 if (z_new_url) {
120 if (Z_TYPE_P(z_new_url) == IS_ARRAY || Z_TYPE_P(z_new_url) == IS_OBJECT) {
121 new_url = array2url(HASH_OF(z_new_url));
122 } else {
123 convert_to_string(z_new_url);
124 if (!(new_url = php_url_parse_ex(Z_STRVAL_P(z_new_url), Z_STRLEN_P(z_new_url)))) {
125 http_error_ex(HE_WARNING, HTTP_E_URL, "Could not parse URL (%s)", Z_STRVAL_P(z_new_url));
126 RETURN_FALSE;
127 }
128 }
129 }
130
131 if (z_old_url) {
132 if (Z_TYPE_P(z_old_url) == IS_ARRAY || Z_TYPE_P(z_old_url) == IS_OBJECT) {
133 old_url = array2url(HASH_OF(z_old_url));
134 } else {
135 convert_to_string(z_old_url);
136 if (!(old_url = php_url_parse_ex(Z_STRVAL_P(z_old_url), Z_STRLEN_P(z_old_url)))) {
137 if (new_url) {
138 php_url_free(new_url);
139 }
140 http_error_ex(HE_WARNING, HTTP_E_URL, "Could not parse URL (%s)", Z_STRVAL_P(z_old_url));
141 RETURN_FALSE;
142 }
143 }
144 }
145
146 if (z_composed_url) {
147 http_build_url(flags, old_url, new_url, &composed_url, &url_str, &url_len);
148
149 zval_dtor(z_composed_url);
150 array_init(z_composed_url);
151 if (composed_url->scheme) {
152 add_assoc_string(z_composed_url, "scheme", composed_url->scheme, 1);
153 }
154 if (composed_url->user) {
155 add_assoc_string(z_composed_url, "user", composed_url->user, 1);
156 }
157 if (composed_url->pass) {
158 add_assoc_string(z_composed_url, "pass", composed_url->pass, 1);
159 }
160 if (composed_url->host) {
161 add_assoc_string(z_composed_url, "host", composed_url->host, 1);
162 }
163 if (composed_url->port) {
164 add_assoc_long(z_composed_url, "port", composed_url->port);
165 }
166 if (composed_url->path) {
167 add_assoc_string(z_composed_url, "path", composed_url->path, 1);
168 }
169 if (composed_url->query) {
170 add_assoc_string(z_composed_url, "query", composed_url->query, 1);
171 }
172 if (composed_url->fragment) {
173 add_assoc_string(z_composed_url, "fragment", composed_url->fragment, 1);
174 }
175 php_url_free(composed_url);
176 } else {
177 http_build_url(flags, old_url, new_url, NULL, &url_str, &url_len);
178 }
179
180 if (new_url) {
181 php_url_free(new_url);
182 }
183 php_url_free(old_url);
184
185 RETURN_STRINGL(url_str, url_len, 0);
186 }
187 /* }}} */
188
189 /* {{{ proto string http_build_str(array query [, string prefix[, string arg_separator]])
190 *
191 * Opponent to parse_str().
192 *
193 * Expects an array as first argument which represents the parts of the query string to build.
194 * Accepts a string as optional second parameter containing a top-level prefix to use.
195 * The optional third parameter should specify an argument separator to use (by default the
196 * INI setting arg_separator.output will be used, or "&" if neither is set).
197 *
198 * Returns the built query as string on success or FALSE on failure.
199 */
200 PHP_FUNCTION(http_build_str)
201 {
202 zval *formdata;
203 char *prefix = NULL, *arg_sep = INI_STR("arg_separator.output");
204 int prefix_len = 0, arg_sep_len = strlen(arg_sep);
205 phpstr formstr;
206
207 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|ss", &formdata, &prefix, &prefix_len, &arg_sep, &arg_sep_len) != SUCCESS) {
208 RETURN_FALSE;
209 }
210
211 if (!arg_sep_len) {
212 arg_sep = HTTP_URL_ARGSEP;
213 arg_sep_len = lenof(HTTP_URL_ARGSEP);
214 }
215
216 phpstr_init(&formstr);
217 if (SUCCESS != http_urlencode_hash_recursive(HASH_OF(formdata), &formstr, arg_sep, arg_sep_len, prefix, prefix_len)) {
218 RETURN_FALSE;
219 }
220
221 if (!formstr.used) {
222 phpstr_dtor(&formstr);
223 RETURN_NULL();
224 }
225
226 RETURN_PHPSTR_VAL(&formstr);
227 }
228 /* }}} */
229
230 #define HTTP_DO_NEGOTIATE(type, supported, rs_array) \
231 { \
232 HashTable *result; \
233 if ((result = http_negotiate_ ##type(supported))) { \
234 char *key; \
235 uint key_len; \
236 ulong idx; \
237 \
238 if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(result, &key, &key_len, &idx, 1, NULL)) { \
239 RETVAL_STRINGL(key, key_len-1, 0); \
240 } else { \
241 RETVAL_NULL(); \
242 } \
243 \
244 if (rs_array) { \
245 zend_hash_copy(Z_ARRVAL_P(rs_array), result, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); \
246 } \
247 \
248 zend_hash_destroy(result); \
249 FREE_HASHTABLE(result); \
250 \
251 } else { \
252 zval **value; \
253 \
254 zend_hash_internal_pointer_reset(Z_ARRVAL_P(supported)); \
255 if (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(supported), (void *) &value)) { \
256 RETVAL_ZVAL(*value, 1, 0); \
257 } else { \
258 RETVAL_NULL(); \
259 } \
260 \
261 if (rs_array) { \
262 HashPosition pos; \
263 zval **value; \
264 \
265 FOREACH_VAL(pos, supported, value) { \
266 convert_to_string_ex(value); \
267 add_assoc_double(rs_array, Z_STRVAL_PP(value), 1.0); \
268 } \
269 } \
270 } \
271 }
272
273
274 /* {{{ proto string http_negotiate_language(array supported[, array &result])
275 *
276 * This function negotiates the clients preferred language based on its
277 * Accept-Language HTTP header. The qualifier is recognized and languages
278 * without qualifier are rated highest. The qualifier will be decreased by
279 * 10% for partial matches (i.e. matching primary language).
280 *
281 * Expects an array as parameter containing the supported languages as values.
282 * If the optional second parameter is supplied, it will be filled with an
283 * array containing the negotiation results.
284 *
285 * Returns the negotiated language or the default language (i.e. first array entry)
286 * if none match.
287 *
288 * Example:
289 * <pre>
290 * <?php
291 * $langs = array(
292 * 'en-US',// default
293 * 'fr',
294 * 'fr-FR',
295 * 'de',
296 * 'de-DE',
297 * 'de-AT',
298 * 'de-CH',
299 * );
300 *
301 * include './langs/'. http_negotiate_language($langs, $result) .'.php';
302 *
303 * print_r($result);
304 * ?>
305 * </pre>
306 */
307 PHP_FUNCTION(http_negotiate_language)
308 {
309 zval *supported, *rs_array = NULL;
310
311 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|z", &supported, &rs_array) != SUCCESS) {
312 RETURN_FALSE;
313 }
314
315 if (rs_array) {
316 zval_dtor(rs_array);
317 array_init(rs_array);
318 }
319
320 HTTP_DO_NEGOTIATE(language, supported, rs_array);
321 }
322 /* }}} */
323
324 /* {{{ proto string http_negotiate_charset(array supported[, array &result])
325 *
326 * This function negotiates the clients preferred charset based on its
327 * Accept-Charset HTTP header. The qualifier is recognized and charsets
328 * without qualifier are rated highest.
329 *
330 * Expects an array as parameter containing the supported charsets as values.
331 * If the optional second parameter is supplied, it will be filled with an
332 * array containing the negotiation results.
333 *
334 * Returns the negotiated charset or the default charset (i.e. first array entry)
335 * if none match.
336 *
337 * Example:
338 * <pre>
339 * <?php
340 * $charsets = array(
341 * 'iso-8859-1', // default
342 * 'iso-8859-2',
343 * 'iso-8859-15',
344 * 'utf-8'
345 * );
346 *
347 * $pref = http_negotiate_charset($charsets, $result);
348 *
349 * if (strcmp($pref, 'iso-8859-1')) {
350 * iconv_set_encoding('internal_encoding', 'iso-8859-1');
351 * iconv_set_encoding('output_encoding', $pref);
352 * ob_start('ob_iconv_handler');
353 * }
354 *
355 * print_r($result);
356 * ?>
357 * </pre>
358 */
359 PHP_FUNCTION(http_negotiate_charset)
360 {
361 zval *supported, *rs_array = NULL;
362
363 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|z", &supported, &rs_array) != SUCCESS) {
364 RETURN_FALSE;
365 }
366
367 if (rs_array) {
368 zval_dtor(rs_array);
369 array_init(rs_array);
370 }
371
372 HTTP_DO_NEGOTIATE(charset, supported, rs_array);
373 }
374 /* }}} */
375
376 /* {{{ proto string http_negotiate_ctype(array supported[, array &result])
377 *
378 * This function negotiates the clients preferred content type based on its
379 * Accept HTTP header. The qualifier is recognized and content types
380 * without qualifier are rated highest.
381 *
382 * Expects an array as parameter containing the supported content types as values.
383 * If the optional second parameter is supplied, it will be filled with an
384 * array containing the negotiation results.
385 *
386 * Returns the negotiated content type or the default content type
387 * (i.e. first array entry) if none match.
388 *
389 * Example:
390 * <pre>
391 * <?php
392 * $ctypes = array('application/xhtml+xml', 'text/html');
393 * http_send_content_type(http_negotiate_content_type($ctypes));
394 * ?>
395 * </pre>
396 */
397 PHP_FUNCTION(http_negotiate_content_type)
398 {
399 zval *supported, *rs_array = NULL;
400
401 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|z", &supported, &rs_array)) {
402 RETURN_FALSE;
403 }
404
405 if (rs_array) {
406 zval_dtor(rs_array);
407 array_init(rs_array);
408 }
409
410 HTTP_DO_NEGOTIATE(content_type, supported, rs_array);
411 }
412 /* }}} */
413
414 /* {{{ proto bool http_send_status(int status)
415 *
416 * Send HTTP status code.
417 *
418 * Expects an HTTP status code as parameter.
419 *
420 * Returns TRUE on success or FALSE on failure.
421 */
422 PHP_FUNCTION(http_send_status)
423 {
424 int status = 0;
425
426 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &status) != SUCCESS) {
427 RETURN_FALSE;
428 }
429 if (status < 100 || status > 510) {
430 http_error_ex(HE_WARNING, HTTP_E_HEADER, "Invalid HTTP status code (100-510): %d", status);
431 RETURN_FALSE;
432 }
433
434 RETURN_SUCCESS(http_send_status(status));
435 }
436 /* }}} */
437
438 /* {{{ proto bool http_send_last_modified([int timestamp])
439 *
440 * Send a "Last-Modified" header with a valid HTTP date.
441 *
442 * Accepts a unix timestamp, converts it to a valid HTTP date and
443 * sends it as "Last-Modified" HTTP header. If timestamp is
444 * omitted, the current time will be sent.
445 *
446 * Returns TRUE on success or FALSE on failure.
447 */
448 PHP_FUNCTION(http_send_last_modified)
449 {
450 long t = -1;
451
452 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) {
453 RETURN_FALSE;
454 }
455
456 if (t == -1) {
457 t = (long) HTTP_GET_REQUEST_TIME();
458 }
459
460 RETURN_SUCCESS(http_send_last_modified(t));
461 }
462 /* }}} */
463
464 /* {{{ proto bool http_send_content_type([string content_type = 'application/x-octetstream'])
465 *
466 * Send the Content-Type of the sent entity. This is particularly important
467 * if you use the http_send() API.
468 *
469 * Accepts an optional string parameter containing the desired content type
470 * (primary/secondary).
471 *
472 * Returns TRUE on success or FALSE on failure.
473 */
474 PHP_FUNCTION(http_send_content_type)
475 {
476 char *ct = "application/x-octetstream";
477 int ct_len = lenof("application/x-octetstream");
478
479 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &ct, &ct_len) != SUCCESS) {
480 RETURN_FALSE;
481 }
482
483 RETURN_SUCCESS(http_send_content_type(ct, ct_len));
484 }
485 /* }}} */
486
487 /* {{{ proto bool http_send_content_disposition(string filename[, bool inline = false])
488 *
489 * Send the Content-Disposition. The Content-Disposition header is very useful
490 * if the data actually sent came from a file or something similar, that should
491 * be "saved" by the client/user (i.e. by browsers "Save as..." popup window).
492 *
493 * Expects a string parameter specifying the file name the "Save as..." dialog
494 * should display. Optionally accepts a bool parameter, which, if set to true
495 * and the user agent knows how to handle the content type, will probably not
496 * cause the popup window to be shown.
497 *
498 * Returns TRUE on success or FALSE on failure.
499 */
500 PHP_FUNCTION(http_send_content_disposition)
501 {
502 char *filename;
503 int f_len;
504 zend_bool send_inline = 0;
505
506 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &filename, &f_len, &send_inline) != SUCCESS) {
507 RETURN_FALSE;
508 }
509 RETURN_SUCCESS(http_send_content_disposition(filename, f_len, send_inline));
510 }
511 /* }}} */
512
513 /* {{{ proto bool http_match_modified([int timestamp[, bool for_range = false]])
514 *
515 * Matches the given unix timestamp against the clients "If-Modified-Since"
516 * resp. "If-Unmodified-Since" HTTP headers.
517 *
518 * Accepts a unix timestamp which should be matched. Optionally accepts an
519 * additional bool parameter, which if set to true will check the header
520 * usually used to validate HTTP ranges. If timestamp is omitted, the
521 * current time will be used.
522 *
523 * Returns TRUE if timestamp represents an earlier date than the header,
524 * else FALSE.
525 */
526 PHP_FUNCTION(http_match_modified)
527 {
528 long t = -1;
529 zend_bool for_range = 0;
530
531 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lb", &t, &for_range) != SUCCESS) {
532 RETURN_FALSE;
533 }
534
535 // current time if not supplied (senseless though)
536 if (t == -1) {
537 t = (long) HTTP_GET_REQUEST_TIME();
538 }
539
540 if (for_range) {
541 RETURN_BOOL(http_match_last_modified("HTTP_IF_UNMODIFIED_SINCE", t));
542 }
543 RETURN_BOOL(http_match_last_modified("HTTP_IF_MODIFIED_SINCE", t));
544 }
545 /* }}} */
546
547 /* {{{ proto bool http_match_etag(string etag[, bool for_range = false])
548 *
549 * Matches the given ETag against the clients "If-Match" resp.
550 * "If-None-Match" HTTP headers.
551 *
552 * Expects a string parameter containing the ETag to compare. Optionally
553 * accepts a bool parameter, which, if set to true, will check the header
554 * usually used to validate HTTP ranges.
555 *
556 * Returns TRUE if ETag matches or the header contained the asterisk ("*"),
557 * else FALSE.
558 */
559 PHP_FUNCTION(http_match_etag)
560 {
561 int etag_len;
562 char *etag;
563 zend_bool for_range = 0;
564
565 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &etag, &etag_len, &for_range) != SUCCESS) {
566 RETURN_FALSE;
567 }
568
569 if (for_range) {
570 RETURN_BOOL(http_match_etag("HTTP_IF_MATCH", etag));
571 }
572 RETURN_BOOL(http_match_etag("HTTP_IF_NONE_MATCH", etag));
573 }
574 /* }}} */
575
576 /* {{{ proto bool http_cache_last_modified([int timestamp_or_expires]])
577 *
578 * Attempts to cache the sent entity by its last modification date.
579 *
580 * Accepts a unix timestamp as parameter which is handled as follows:
581 *
582 * If timestamp_or_expires is greater than 0, it is handled as timestamp
583 * and will be sent as date of last modification. If it is 0 or omitted,
584 * the current time will be sent as Last-Modified date. If it's negative,
585 * it is handled as expiration time in seconds, which means that if the
586 * requested last modification date is not between the calculated timespan,
587 * the Last-Modified header is updated and the actual body will be sent.
588 *
589 * Returns FALSE on failure, or *exits* with "304 Not Modified" if the entity is cached.
590 *
591 * A log entry will be written to the cache log if the INI entry
592 * http.cache_log is set and the cache attempt was successful.
593 */
594 PHP_FUNCTION(http_cache_last_modified)
595 {
596 long last_modified = 0, send_modified = 0, t;
597 zval *zlm;
598
599 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &last_modified) != SUCCESS) {
600 RETURN_FALSE;
601 }
602
603 HTTP_CHECK_HEADERS_SENT(RETURN_FALSE);
604
605 t = (long) HTTP_GET_REQUEST_TIME();
606
607 /* 0 or omitted */
608 if (!last_modified) {
609 /* does the client have? (att: caching "forever") */
610 if ((zlm = http_get_server_var("HTTP_IF_MODIFIED_SINCE"))) {
611 last_modified = send_modified = http_parse_date(Z_STRVAL_P(zlm));
612 /* send current time */
613 } else {
614 send_modified = t;
615 }
616 /* negative value is supposed to be expiration time */
617 } else if (last_modified < 0) {
618 last_modified += t;
619 send_modified = t;
620 /* send supplied time explicitly */
621 } else {
622 send_modified = last_modified;
623 }
624
625 RETURN_SUCCESS(http_cache_last_modified(last_modified, send_modified, HTTP_DEFAULT_CACHECONTROL, lenof(HTTP_DEFAULT_CACHECONTROL)));
626 }
627 /* }}} */
628
629 /* {{{ proto bool http_cache_etag([string etag])
630 *
631 * Attempts to cache the sent entity by its ETag, either supplied or generated
632 * by the hash algorithm specified by the INI setting "http.etag_mode".
633 *
634 * If the clients "If-None-Match" header matches the supplied/calculated
635 * ETag, the body is considered cached on the clients side and
636 * a "304 Not Modified" status code is issued.
637 *
638 * Returns FALSE on failure, or *exits* with "304 Not Modified" if the entity is cached.
639 *
640 * A log entry is written to the cache log if the INI entry
641 * "http.cache_log" is set and the cache attempt was successful.
642 */
643 PHP_FUNCTION(http_cache_etag)
644 {
645 char *etag = NULL;
646 int etag_len = 0;
647
648 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &etag, &etag_len) != SUCCESS) {
649 RETURN_FALSE;
650 }
651
652 HTTP_CHECK_HEADERS_SENT(RETURN_FALSE);
653
654 RETURN_SUCCESS(http_cache_etag(etag, etag_len, HTTP_DEFAULT_CACHECONTROL, lenof(HTTP_DEFAULT_CACHECONTROL)));
655 }
656 /* }}} */
657
658 /* {{{ proto string ob_etaghandler(string data, int mode)
659 *
660 * For use with ob_start(). Output buffer handler generating an ETag with
661 * the hash algorithm specified with the INI setting "http.etag_mode".
662 */
663 PHP_FUNCTION(ob_etaghandler)
664 {
665 char *data;
666 int data_len;
667 long mode;
668
669 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &data, &data_len, &mode)) {
670 RETURN_FALSE;
671 }
672
673 Z_TYPE_P(return_value) = IS_STRING;
674 http_ob_etaghandler(data, data_len, &Z_STRVAL_P(return_value), (uint *) &Z_STRLEN_P(return_value), mode);
675 }
676 /* }}} */
677
678 /* {{{ proto void http_throttle(double sec[, int bytes = 40960])
679 *
680 * Sets the throttle delay and send buffer size for use with http_send() API.
681 * Provides a basic throttling mechanism, which will yield the current process
682 * resp. thread until the entity has been completely sent, though.
683 *
684 * Expects a double parameter specifying the seconds too sleep() after
685 * each chunk sent. Additionally accepts an optional int parameter
686 * representing the chunk size in bytes.
687 *
688 * Example:
689 * <pre>
690 * <?php
691 * // ~ 20 kbyte/s
692 * # http_throttle(1, 20000);
693 * # http_throttle(0.5, 10000);
694 * # http_throttle(0.1, 2000);
695 * http_send_file('document.pdf');
696 * ?>
697 * </pre>
698 */
699 PHP_FUNCTION(http_throttle)
700 {
701 long chunk_size = HTTP_SENDBUF_SIZE;
702 double interval;
703
704 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|l", &interval, &chunk_size)) {
705 return;
706 }
707
708 HTTP_G->send.throttle_delay = interval;
709 HTTP_G->send.buffer_size = chunk_size;
710 }
711 /* }}} */
712
713 /* {{{ proto void http_redirect([string url[, array params[, bool session = false[, int status = 302]]]])
714 *
715 * Redirect to the given url.
716 *
717 * The supplied url will be expanded with http_build_url(), the params array will
718 * be treated with http_build_query() and the session identification will be appended
719 * if session is true.
720 *
721 * The HTTP response code will be set according to status.
722 * You can use one of the following constants for convenience:
723 * - HTTP_REDIRECT 302 Found for GET/HEAD, else 303 See Other
724 * - HTTP_REDIRECT_PERM 301 Moved Permanently
725 * - HTTP_REDIRECT_FOUND 302 Found
726 * - HTTP_REDIRECT_POST 303 See Other
727 * - HTTP_REDIRECT_PROXY 305 Use Proxy
728 * - HTTP_REDIRECT_TEMP 307 Temporary Redirect
729 *
730 * Please see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3
731 * for which redirect response code to use in which situation.
732 *
733 * To be RFC compliant, "Redirecting to <a>URL</a>." will be displayed,
734 * if the client doesn't redirect immediately, and the request method was
735 * another one than HEAD.
736 *
737 * Returns FALSE on failure, or *exits* on success.
738 *
739 * A log entry will be written to the redirect log, if the INI entry
740 * "http.redirect_log" is set and the redirect attempt was successful.
741 */
742 PHP_FUNCTION(http_redirect)
743 {
744 int url_len = 0;
745 size_t query_len = 0;
746 zend_bool session = 0, free_params = 0;
747 zval *params = NULL;
748 long status = HTTP_REDIRECT;
749 char *query = NULL, *url = NULL, *URI, *LOC, *RED = NULL;
750
751 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sa!/bl", &url, &url_len, &params, &session, &status) != SUCCESS) {
752 RETURN_FALSE;
753 }
754
755 #ifdef HAVE_PHP_SESSION
756 /* append session info */
757 if (session) {
758 if (!params) {
759 free_params = 1;
760 MAKE_STD_ZVAL(params);
761 array_init(params);
762 }
763 if (PS(session_status) == php_session_active) {
764 if (add_assoc_string(params, PS(session_name), PS(id), 1) != SUCCESS) {
765 http_error(HE_WARNING, HTTP_E_RUNTIME, "Could not append session information");
766 }
767 }
768 }
769 #endif
770
771 /* treat params array with http_build_query() */
772 if (params) {
773 if (SUCCESS != http_urlencode_hash_ex(Z_ARRVAL_P(params), 0, NULL, 0, &query, &query_len)) {
774 if (free_params) {
775 zval_dtor(params);
776 FREE_ZVAL(params);
777 }
778 if (query) {
779 efree(query);
780 }
781 RETURN_FALSE;
782 }
783 }
784
785 URI = http_absolute_url(url);
786
787 if (query_len) {
788 spprintf(&LOC, 0, "Location: %s?%s", URI, query);
789 if (status != 300) {
790 spprintf(&RED, 0, "Redirecting to <a href=\"%s?%s\">%s?%s</a>.\n", URI, query, URI, query);
791 }
792 } else {
793 spprintf(&LOC, 0, "Location: %s", URI);
794 if (status != 300) {
795 spprintf(&RED, 0, "Redirecting to <a href=\"%s\">%s</a>.\n", URI, URI);
796 }
797 }
798
799 efree(URI);
800 if (query) {
801 efree(query);
802 }
803 if (free_params) {
804 zval_dtor(params);
805 FREE_ZVAL(params);
806 }
807
808 switch (status)
809 {
810 case 300:
811 RETVAL_SUCCESS(http_send_status_header(status, LOC));
812 efree(LOC);
813 return;
814 break;
815
816 case HTTP_REDIRECT_PERM:
817 case HTTP_REDIRECT_FOUND:
818 case HTTP_REDIRECT_POST:
819 case HTTP_REDIRECT_PROXY:
820 case HTTP_REDIRECT_TEMP:
821 break;
822
823 case 306:
824 default:
825 http_error_ex(HE_NOTICE, HTTP_E_RUNTIME, "Unsupported redirection status code: %ld", status);
826 case HTTP_REDIRECT:
827 if ( SG(request_info).request_method &&
828 strcasecmp(SG(request_info).request_method, "HEAD") &&
829 strcasecmp(SG(request_info).request_method, "GET")) {
830 status = HTTP_REDIRECT_POST;
831 } else {
832 status = HTTP_REDIRECT_FOUND;
833 }
834 break;
835 }
836
837 RETURN_SUCCESS(http_exit_ex(status, LOC, RED, 1));
838 }
839 /* }}} */
840
841 /* {{{ proto bool http_send_data(string data)
842 *
843 * Sends raw data with support for (multiple) range requests.
844 *
845 * Returns TRUE on success, or FALSE on failure.
846 */
847 PHP_FUNCTION(http_send_data)
848 {
849 zval *zdata;
850
851 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zdata) != SUCCESS) {
852 RETURN_FALSE;
853 }
854
855 convert_to_string_ex(&zdata);
856 RETURN_SUCCESS(http_send_data(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata)));
857 }
858 /* }}} */
859
860 /* {{{ proto bool http_send_file(string file)
861 *
862 * Sends a file with support for (multiple) range requests.
863 *
864 * Expects a string parameter referencing the file to send.
865 *
866 * Returns TRUE on success, or FALSE on failure.
867 */
868 PHP_FUNCTION(http_send_file)
869 {
870 char *file;
871 int flen = 0;
872
873 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &file, &flen) != SUCCESS) {
874 RETURN_FALSE;
875 }
876 if (!flen) {
877 RETURN_FALSE;
878 }
879
880 RETURN_SUCCESS(http_send_file(file));
881 }
882 /* }}} */
883
884 /* {{{ proto bool http_send_stream(resource stream)
885 *
886 * Sends an already opened stream with support for (multiple) range requests.
887 *
888 * Expects a resource parameter referencing the stream to read from.
889 *
890 * Returns TRUE on success, or FALSE on failure.
891 */
892 PHP_FUNCTION(http_send_stream)
893 {
894 zval *zstream;
895 php_stream *file;
896
897 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zstream) != SUCCESS) {
898 RETURN_FALSE;
899 }
900
901 php_stream_from_zval(file, &zstream);
902 RETURN_SUCCESS(http_send_stream(file));
903 }
904 /* }}} */
905
906 /* {{{ proto string http_chunked_decode(string encoded)
907 *
908 * Decodes a string that was HTTP-chunked encoded.
909 *
910 * Expects a chunked encoded string as parameter.
911 *
912 * Returns the decoded string on success or FALSE on failure.
913 */
914 PHP_FUNCTION(http_chunked_decode)
915 {
916 char *encoded = NULL, *decoded = NULL;
917 size_t decoded_len = 0;
918 int encoded_len = 0;
919
920 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &encoded, &encoded_len) != SUCCESS) {
921 RETURN_FALSE;
922 }
923
924 if (NULL != http_encoding_dechunk(encoded, encoded_len, &decoded, &decoded_len)) {
925 RETURN_STRINGL(decoded, (int) decoded_len, 0);
926 } else {
927 RETURN_FALSE;
928 }
929 }
930 /* }}} */
931
932 /* {{{ proto object http_parse_message(string message)
933 *
934 * Parses (a) http_message(s) into a simple recursive object structure.
935 *
936 * Expects a string parameter containing a single HTTP message or
937 * several consecutive HTTP messages.
938 *
939 * Returns an hierarchical object structure of the parsed messages.
940 *
941 * Example:
942 * <pre>
943 * <?php
944 * print_r(http_parse_message(http_get(URL, array('redirect' => 3)));
945 *
946 * stdClass object
947 * (
948 * [type] => 2
949 * [httpVersion] => 1.1
950 * [responseCode] => 200
951 * [headers] => Array
952 * (
953 * [Content-Length] => 3
954 * [Server] => Apache
955 * )
956 * [body] => Hi!
957 * [parentMessage] => stdClass object
958 * (
959 * [type] => 2
960 * [httpVersion] => 1.1
961 * [responseCode] => 302
962 * [headers] => Array
963 * (
964 * [Content-Length] => 0
965 * [Location] => ...
966 * )
967 * [body] =>
968 * [parentMessage] => ...
969 * )
970 * )
971 * ?>
972 * </pre>
973 */
974 PHP_FUNCTION(http_parse_message)
975 {
976 char *message;
977 int message_len;
978 http_message *msg = NULL;
979
980 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &message, &message_len)) {
981 RETURN_NULL();
982 }
983
984 if ((msg = http_message_parse(message, message_len))) {
985 object_init(return_value);
986 http_message_tostruct_recursive(msg, return_value);
987 http_message_free(&msg);
988 } else {
989 RETURN_NULL();
990 }
991 }
992 /* }}} */
993
994 /* {{{ proto array http_parse_headers(string header)
995 *
996 * Parses HTTP headers into an associative array.
997 *
998 * Expects a string parameter containing HTTP headers.
999 *
1000 * Returns an array on success, or FALSE on failure.
1001 *
1002 * Example:
1003 * <pre>
1004 * <?php
1005 * $headers = "content-type: text/html; charset=UTF-8\r\n".
1006 * "Server: Funky/1.0\r\n".
1007 * "Set-Cookie: foo=bar\r\n".
1008 * "Set-Cookie: baz=quux\r\n".
1009 * "Folded: works\r\n\ttoo\r\n";
1010 * print_r(http_parse_headers($headers));
1011 *
1012 * Array
1013 * (
1014 * [Content-Type] => text/html; chatset=UTF-8
1015 * [Server] => Funky/1.0
1016 * [Set-Cookie] => Array
1017 * (
1018 * [0] => foo=bar
1019 * [1] => baz=quux
1020 * )
1021 * [Folded] => works
1022 * too
1023 * )
1024 * ?>
1025 * </pre>
1026 */
1027 PHP_FUNCTION(http_parse_headers)
1028 {
1029 char *header;
1030 int header_len;
1031
1032 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &header, &header_len)) {
1033 RETURN_FALSE;
1034 }
1035
1036 array_init(return_value);
1037 if (SUCCESS != http_parse_headers(header, return_value)) {
1038 zval_dtor(return_value);
1039 http_error(HE_WARNING, HTTP_E_MALFORMED_HEADERS, "Failed to parse headers");
1040 RETURN_FALSE;
1041 }
1042 }
1043 /* }}}*/
1044
1045 /* {{{ proto object http_parse_cookie(string cookie[, int flags[, array allowed_extras]])
1046 *
1047 * Parses HTTP cookies like sent in a response into a struct.
1048 *
1049 * Expects a string as parameter containing the value of a Set-Cookie response header.
1050 *
1051 * Returns an stdClass olike shown in the example on success or FALSE on failure.
1052 *
1053 * Example:
1054 * <pre>
1055 * <?php
1056 * print_r(http_parse_cookie("foo=bar; bar=baz; path=/; domain=example.com; comment=; secure", 0, array("comment")));
1057 *
1058 * stdClass Object
1059 * (
1060 * [cookies] => Array
1061 * (
1062 * [foo] => bar
1063 * [bar] => baz
1064 * )
1065 *
1066 * [extras] => Array
1067 * (
1068 * [comment] =>
1069 * )
1070 *
1071 * [flags] => 16
1072 * [expires] => 0
1073 * [path] => /
1074 * [domain] => example.com
1075 * )
1076 * ?>
1077 * </pre>
1078 */
1079 PHP_FUNCTION(http_parse_cookie)
1080 {
1081 char *cookie, **allowed_extras = NULL;
1082 int i = 0, cookie_len;
1083 long flags = 0;
1084 zval *allowed_extras_array = NULL, **entry = NULL;
1085 HashPosition pos;
1086 http_cookie_list list;
1087
1088 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|la", &cookie, &cookie_len, &flags, &allowed_extras_array)) {
1089 RETURN_FALSE;
1090 }
1091
1092 if (allowed_extras_array) {
1093 allowed_extras = ecalloc(zend_hash_num_elements(Z_ARRVAL_P(allowed_extras_array)) + 1, sizeof(char *));
1094 FOREACH_VAL(pos, allowed_extras_array, entry) {
1095 ZVAL_ADDREF(*entry);
1096 convert_to_string_ex(entry);
1097 allowed_extras[i] = estrndup(Z_STRVAL_PP(entry), Z_STRLEN_PP(entry));
1098 zval_ptr_dtor(entry);
1099 }
1100 }
1101
1102 if (http_parse_cookie_ex(&list, cookie, flags, allowed_extras)) {
1103 object_init(return_value);
1104 http_cookie_list_tostruct(&list, return_value);
1105 http_cookie_list_dtor(&list);
1106 } else {
1107 RETVAL_FALSE;
1108 }
1109
1110 if (allowed_extras) {
1111 for (i = 0; allowed_extras[i]; ++i) {
1112 efree(allowed_extras[i]);
1113 }
1114 efree(allowed_extras);
1115 }
1116 }
1117
1118 /* {{{ proto array http_get_request_headers(void)
1119 *
1120 * Get a list of incoming HTTP headers.
1121 *
1122 * Returns an associative array of incoming request headers.
1123 */
1124 PHP_FUNCTION(http_get_request_headers)
1125 {
1126 NO_ARGS;
1127
1128 array_init(return_value);
1129 http_get_request_headers(return_value);
1130 }
1131 /* }}} */
1132
1133 /* {{{ proto string http_get_request_body(void)
1134 *
1135 * Get the raw request body (e.g. POST or PUT data).
1136 *
1137 * This function can not be used after http_get_request_body_stream()
1138 * if the request method was another than POST.
1139 *
1140 * Returns the raw request body as string on success or NULL on failure.
1141 */
1142 PHP_FUNCTION(http_get_request_body)
1143 {
1144 char *body;
1145 size_t length;
1146
1147 NO_ARGS;
1148
1149 if (SUCCESS == http_get_request_body(&body, &length)) {
1150 RETURN_STRINGL(body, (int) length, 0);
1151 } else {
1152 RETURN_NULL();
1153 }
1154 }
1155 /* }}} */
1156
1157 /* {{{ proto resource http_get_request_body_stream(void)
1158 *
1159 * Create a stream to read the raw request body (e.g. POST or PUT data).
1160 *
1161 * This function can only be used once if the request method was another than POST.
1162 *
1163 * Returns the raw request body as stream on success or NULL on failure.
1164 */
1165 PHP_FUNCTION(http_get_request_body_stream)
1166 {
1167 php_stream *s;
1168
1169 NO_ARGS;
1170
1171 if ((s = http_get_request_body_stream())) {
1172 php_stream_to_zval(s, return_value);
1173 } else {
1174 http_error(HE_WARNING, HTTP_E_RUNTIME, "Failed to create request body stream");
1175 RETURN_NULL();
1176 }
1177 }
1178 /* }}} */
1179
1180 /* {{{ proto bool http_match_request_header(string header, string value[, bool match_case = false])
1181 *
1182 * Match an incoming HTTP header.
1183 *
1184 * Expects two string parameters representing the header name (case-insensitive)
1185 * and the header value that should be compared. The case sensitivity of the
1186 * header value depends on the additional optional bool parameter accepted.
1187 *
1188 * Returns TRUE if header value matches, else FALSE.
1189 */
1190 PHP_FUNCTION(http_match_request_header)
1191 {
1192 char *header, *value;
1193 int header_len, value_len;
1194 zend_bool match_case = 0;
1195
1196 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", &header, &header_len, &value, &value_len, &match_case)) {
1197 RETURN_FALSE;
1198 }
1199
1200 RETURN_BOOL(http_match_request_header_ex(header, value, match_case));
1201 }
1202 /* }}} */
1203
1204 /* {{{ HAVE_CURL */
1205 #ifdef HTTP_HAVE_CURL
1206
1207 #define RETVAL_RESPONSE_OR_BODY(request) \
1208 { \
1209 zval **bodyonly; \
1210 \
1211 /* check if only the body should be returned */ \
1212 if (options && (SUCCESS == zend_hash_find(Z_ARRVAL_P(options), "bodyonly", sizeof("bodyonly"), (void *) &bodyonly)) && zval_is_true(*bodyonly)) { \
1213 http_message *msg = http_message_parse(PHPSTR_VAL(&request.conv.response), PHPSTR_LEN(&request.conv.response)); \
1214 \
1215 if (msg) { \
1216 RETVAL_STRINGL(PHPSTR_VAL(&msg->body), PHPSTR_LEN(&msg->body), 1); \
1217 http_message_free(&msg); \
1218 } \
1219 } else { \
1220 RETVAL_STRINGL(request.conv.response.data, request.conv.response.used, 1); \
1221 } \
1222 }
1223
1224 /* {{{ proto string http_get(string url[, array options[, array &info]])
1225 *
1226 * Performs an HTTP GET request on the supplied url.
1227 *
1228 * The second parameter, if set, is expected to be an associative
1229 * array where the following keys will be recognized:
1230 * <pre>
1231 * - redirect: int, whether and how many redirects to follow
1232 * - unrestrictedauth: bool, whether to continue sending credentials on
1233 * redirects to a different host
1234 * - proxyhost: string, proxy host in "host[:port]" format
1235 * - proxyport: int, use another proxy port as specified in proxyhost
1236 * - proxytype: int, HTTP_PROXY_HTTP, SOCKS4 or SOCKS5
1237 * - proxyauth: string, proxy credentials in "user:pass" format
1238 * - proxyauthtype: int, HTTP_AUTH_BASIC and/or HTTP_AUTH_NTLM
1239 * - httpauth: string, http credentials in "user:pass" format
1240 * - httpauthtype: int, HTTP_AUTH_BASIC, DIGEST and/or NTLM
1241 * - compress: bool, whether to allow gzip/deflate content encoding
1242 * - port: int, use another port as specified in the url
1243 * - referer: string, the referer to send
1244 * - useragent: string, the user agent to send
1245 * (defaults to PECL::HTTP/version (PHP/version)))
1246 * - headers: array, list of custom headers as associative array
1247 * like array("header" => "value")
1248 * - cookies: array, list of cookies as associative array
1249 * like array("cookie" => "value")
1250 * - encodecookies: bool, whether to urlencode the cookies (default: true)
1251 * - resetcookies: bool, wheter to reset the cookies
1252 * - cookiestore: string, path to a file where cookies are/will be stored
1253 * - cookiesession: bool, accept (true) or reset (false) sessioncookies
1254 * - resume: int, byte offset to start the download from;
1255 * if the server supports ranges
1256 * - range: array, array of arrays, each containing two integers,
1257 * specifying the ranges to download if server support is
1258 * given; only recognized if the resume option is empty
1259 * - maxfilesize: int, maximum file size that should be downloaded;
1260 * has no effect, if the size of the requested entity is not known
1261 * - lastmodified: int, timestamp for If-(Un)Modified-Since header
1262 * - etag: string, quoted etag for If-(None-)Match header
1263 * - timeout: int, seconds the request may take
1264 * - connecttimeout: int, seconds the connect may take
1265 * - onprogress: mixed, progress callback
1266 * - interface: string, outgoing network interface (ifname, ip or hostname)
1267 * - portrange: array, 2 integers specifying outgoing portrange to try
1268 * - ssl: array, with the following options:
1269 * cert: string, path to certificate
1270 * certtype: string, type of certificate
1271 * certpasswd: string, password for certificate
1272 * key: string, path to key
1273 * keytype: string, type of key
1274 * keypasswd: string, pasword for key
1275 * engine: string, ssl engine to use
1276 * version: int, ssl version to use
1277 * verifypeer: bool, whether to verify the peer
1278 * verifyhost: bool whether to verify the host
1279 * cipher_list: string, list of allowed ciphers
1280 * cainfo: string
1281 * capath: string
1282 * random_file: string
1283 * egdsocket: string
1284 * </pre>
1285 *
1286 * The optional third parameter will be filled with some additional information
1287 * in form of an associative array, if supplied, like the following example:
1288 * <pre>
1289 * <?php
1290 * array (
1291 * 'effective_url' => 'http://www.example.com/',
1292 * 'response_code' => 302,
1293 * 'connect_code' => 0,
1294 * 'filetime' => -1,
1295 * 'total_time' => 0.212348,
1296 * 'namelookup_time' => 0.038296,
1297 * 'connect_time' => 0.104144,
1298 * 'pretransfer_time' => 0.104307,
1299 * 'starttransfer_time' => 0.212077,
1300 * 'redirect_time' => 0,
1301 * 'redirect_count' => 0,
1302 * 'size_upload' => 0,
1303 * 'size_download' => 218,
1304 * 'speed_download' => 1026,
1305 * 'speed_upload' => 0,
1306 * 'header_size' => 307,
1307 * 'request_size' => 103,
1308 * 'ssl_verifyresult' => 0,
1309 * 'ssl_engines' =>
1310 * array (
1311 * 0 => 'dynamic',
1312 * 1 => 'cswift',
1313 * 2 => 'chil',
1314 * 3 => 'atalla',
1315 * 4 => 'nuron',
1316 * 5 => 'ubsec',
1317 * 6 => 'aep',
1318 * 7 => 'sureware',
1319 * 8 => '4758cca',
1320 * ),
1321 * 'content_length_download' => 218,
1322 * 'content_length_upload' => 0,
1323 * 'content_type' => 'text/html',
1324 * 'httpauth_avail' => 0,
1325 * 'proxyauth_avail' => 0,
1326 * 'num_connects' => 1,
1327 * 'os_errno' => 0,
1328 * 'error' => '',
1329 * )
1330 * ?>
1331 * </pre>
1332 *
1333 * Returns the HTTP response(s) as string on success, or FALSE on failure.
1334 */
1335 PHP_FUNCTION(http_get)
1336 {
1337 zval *options = NULL, *info = NULL;
1338 char *URL;
1339 int URL_len;
1340 http_request request;
1341
1342 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a/!z", &URL, &URL_len, &options, &info) != SUCCESS) {
1343 RETURN_FALSE;
1344 }
1345
1346 if (info) {
1347 zval_dtor(info);
1348 array_init(info);
1349 }
1350
1351 RETVAL_FALSE;
1352
1353 http_request_init_ex(&request, NULL, HTTP_GET, URL);
1354 if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) {
1355 http_request_exec(&request);
1356 if (info) {
1357 http_request_info(&request, Z_ARRVAL_P(info));
1358 }
1359 RETVAL_RESPONSE_OR_BODY(request);
1360 }
1361 http_request_dtor(&request);
1362 }
1363 /* }}} */
1364
1365 /* {{{ proto string http_head(string url[, array options[, array &info]])
1366 *
1367 * Performs an HTTP HEAD request on the supplied url.
1368 *
1369 * See http_get() for a full list of available parameters and options.
1370 *
1371 * Returns the HTTP response as string on success, or FALSE on failure.
1372 */
1373 PHP_FUNCTION(http_head)
1374 {
1375 zval *options = NULL, *info = NULL;
1376 char *URL;
1377 int URL_len;
1378 http_request request;
1379
1380 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a/!z", &URL, &URL_len, &options, &info) != SUCCESS) {
1381 RETURN_FALSE;
1382 }
1383
1384 if (info) {
1385 zval_dtor(info);
1386 array_init(info);
1387 }
1388
1389 RETVAL_FALSE;
1390
1391 http_request_init_ex(&request, NULL, HTTP_HEAD, URL);
1392 if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) {
1393 http_request_exec(&request);
1394 if (info) {
1395 http_request_info(&request, Z_ARRVAL_P(info));
1396 }
1397 RETVAL_RESPONSE_OR_BODY(request);
1398 }
1399 http_request_dtor(&request);
1400 }
1401 /* }}} */
1402
1403 /* {{{ proto string http_post_data(string url, string data[, array options[, array &info]])
1404 *
1405 * Performs an HTTP POST request on the supplied url.
1406 *
1407 * Expects a string as second parameter containing the pre-encoded post data.
1408 * See http_get() for a full list of available parameters and options.
1409 *
1410 * Returns the HTTP response(s) as string on success, or FALSE on failure.
1411 */
1412 PHP_FUNCTION(http_post_data)
1413 {
1414 zval *options = NULL, *info = NULL;
1415 char *URL, *postdata;
1416 int postdata_len, URL_len;
1417 http_request_body body;
1418 http_request request;
1419
1420 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!z", &URL, &URL_len, &postdata, &postdata_len, &options, &info) != SUCCESS) {
1421 RETURN_FALSE;
1422 }
1423
1424 if (info) {
1425 zval_dtor(info);
1426 array_init(info);
1427 }
1428
1429 RETVAL_FALSE;
1430
1431 http_request_init_ex(&request, NULL, HTTP_POST, URL);
1432 request.body = http_request_body_init_ex(&body, HTTP_REQUEST_BODY_CSTRING, postdata, postdata_len, 0);
1433 if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) {
1434 http_request_exec(&request);
1435 if (info) {
1436 http_request_info(&request, Z_ARRVAL_P(info));
1437 }
1438 RETVAL_RESPONSE_OR_BODY(request);
1439 }
1440 http_request_dtor(&request);
1441 }
1442 /* }}} */
1443
1444 /* {{{ proto string http_post_fields(string url, array data[, array files[, array options[, array &info]]])
1445 *
1446 * Performs an HTTP POST request on the supplied url.
1447 *
1448 * Expects an associative array as second parameter, which will be
1449 * www-form-urlencoded. See http_get() for a full list of available options.
1450 *
1451 * Returns the HTTP response(s) as string on success, or FALSE on failure.
1452 */
1453 PHP_FUNCTION(http_post_fields)
1454 {
1455 zval *options = NULL, *info = NULL, *fields, *files = NULL;
1456 char *URL;
1457 int URL_len;
1458 http_request_body body;
1459 http_request request;
1460
1461 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|aa/!z", &URL, &URL_len, &fields, &files, &options, &info) != SUCCESS) {
1462 RETURN_FALSE;
1463 }
1464
1465 if (!http_request_body_fill(&body, Z_ARRVAL_P(fields), files ? Z_ARRVAL_P(files) : NULL)) {
1466 RETURN_FALSE;
1467 }
1468
1469 if (info) {
1470 zval_dtor(info);
1471 array_init(info);
1472 }
1473
1474 RETVAL_FALSE;
1475
1476 http_request_init_ex(&request, NULL, HTTP_POST, URL);
1477 request.body = &body;
1478 if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) {
1479 http_request_exec(&request);
1480 if (info) {
1481 http_request_info(&request, Z_ARRVAL_P(info));
1482 }
1483 RETVAL_RESPONSE_OR_BODY(request);
1484 }
1485 http_request_dtor(&request);
1486 }
1487 /* }}} */
1488
1489 /* {{{ proto string http_put_file(string url, string file[, array options[, array &info]])
1490 *
1491 * Performs an HTTP PUT request on the supplied url.
1492 *
1493 * Expects the second parameter to be a string referencing the file to upload.
1494 * See http_get() for a full list of available options.
1495 *
1496 * Returns the HTTP response(s) as string on success, or FALSE on failure.
1497 */
1498 PHP_FUNCTION(http_put_file)
1499 {
1500 char *URL, *file;
1501 int URL_len, f_len;
1502 zval *options = NULL, *info = NULL;
1503 php_stream *stream;
1504 php_stream_statbuf ssb;
1505 http_request_body body;
1506 http_request request;
1507
1508 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!z", &URL, &URL_len, &file, &f_len, &options, &info)) {
1509 RETURN_FALSE;
1510 }
1511
1512 if (!(stream = php_stream_open_wrapper_ex(file, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL, HTTP_DEFAULT_STREAM_CONTEXT))) {
1513 RETURN_FALSE;
1514 }
1515 if (php_stream_stat(stream, &ssb)) {
1516 php_stream_close(stream);
1517 RETURN_FALSE;
1518 }
1519
1520 if (info) {
1521 zval_dtor(info);
1522 array_init(info);
1523 }
1524
1525 RETVAL_FALSE;
1526
1527 http_request_init_ex(&request, NULL, HTTP_PUT, URL);
1528 request.body = http_request_body_init_ex(&body, HTTP_REQUEST_BODY_UPLOADFILE, stream, ssb.sb.st_size, 1);
1529 if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) {
1530 http_request_exec(&request);
1531 if (info) {
1532 http_request_info(&request, Z_ARRVAL_P(info));
1533 }
1534 RETVAL_RESPONSE_OR_BODY(request);
1535 }
1536 http_request_dtor(&request);
1537 }
1538 /* }}} */
1539
1540 /* {{{ proto string http_put_stream(string url, resource stream[, array options[, array &info]])
1541 *
1542 * Performs an HTTP PUT request on the supplied url.
1543 *
1544 * Expects the second parameter to be a resource referencing an already
1545 * opened stream, from which the data to upload should be read.
1546 * See http_get() for a full list of available options.
1547 *
1548 * Returns the HTTP response(s) as string on success, or FALSE on failure.
1549 */
1550 PHP_FUNCTION(http_put_stream)
1551 {
1552 zval *resource, *options = NULL, *info = NULL;
1553 char *URL;
1554 int URL_len;
1555 php_stream *stream;
1556 php_stream_statbuf ssb;
1557 http_request_body body;
1558 http_request request;
1559
1560 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sr|a/!z", &URL, &URL_len, &resource, &options, &info)) {
1561 RETURN_FALSE;
1562 }
1563
1564 php_stream_from_zval(stream, &resource);
1565 if (php_stream_stat(stream, &ssb)) {
1566 RETURN_FALSE;
1567 }
1568
1569 if (info) {
1570 zval_dtor(info);
1571 array_init(info);
1572 }
1573
1574 RETVAL_FALSE;
1575
1576 http_request_init_ex(&request, NULL, HTTP_PUT, URL);
1577 request.body = http_request_body_init_ex(&body, HTTP_REQUEST_BODY_UPLOADFILE, stream, ssb.sb.st_size, 0);
1578 if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) {
1579 http_request_exec(&request);
1580 if (info) {
1581 http_request_info(&request, Z_ARRVAL_P(info));
1582 }
1583 RETVAL_RESPONSE_OR_BODY(request);
1584 }
1585 http_request_dtor(&request);
1586 }
1587 /* }}} */
1588
1589 /* {{{ proto string http_put_data(string url, string data[, array options[, array &info]])
1590 *
1591 * Performs an HTTP PUT request on the supplied url.
1592 *
1593 * Expects the second parameter to be a string containing the data to upload.
1594 * See http_get() for a full list of available options.
1595 *
1596 * Returns the HTTP response(s) as string on success, or FALSE on failure.
1597 */
1598 PHP_FUNCTION(http_put_data)
1599 {
1600 char *URL, *data;
1601 int URL_len, data_len;
1602 zval *options = NULL, *info = NULL;
1603 http_request_body body;
1604 http_request request;
1605
1606 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!z", &URL, &URL_len, &data, &data_len, &options, &info)) {
1607 RETURN_FALSE;
1608 }
1609
1610 if (info) {
1611 zval_dtor(info);
1612 array_init(info);
1613 }
1614
1615 RETVAL_FALSE;
1616
1617 http_request_init_ex(&request, NULL, HTTP_PUT, URL);
1618 request.body = http_request_body_init_ex(&body, HTTP_REQUEST_BODY_CSTRING, data, data_len, 0);
1619 if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) {
1620 http_request_exec(&request);
1621 if (info) {
1622 http_request_info(&request, Z_ARRVAL_P(info));
1623 }
1624 RETVAL_RESPONSE_OR_BODY(request);
1625 }
1626 http_request_dtor(&request);
1627 }
1628 /* }}} */
1629
1630 /* {{{ proto string http_request(int method, string url[, string body[, array options[, array &info]]])
1631 *
1632 * Performs a custom HTTP request on the supplied url.
1633 *
1634 * Expects the first parameter to be an integer specifying the request method to use.
1635 * Accepts an optional third string parameter containing the raw request body.
1636 * See http_get() for a full list of available options.
1637 *
1638 * Returns the HTTP response(s) as string on success, or FALSE on failure.
1639 */
1640 PHP_FUNCTION(http_request)
1641 {
1642 long meth;
1643 char *URL, *data = NULL;
1644 int URL_len, data_len = 0;
1645 zval *options = NULL, *info = NULL;
1646 http_request_body body;
1647 http_request request;
1648
1649 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls|sa/!z", &meth, &URL, &URL_len, &data, &data_len, &options, &info)) {
1650 RETURN_FALSE;
1651 }
1652
1653 if (info) {
1654 zval_dtor(info);
1655 array_init(info);
1656 }
1657
1658 RETVAL_FALSE;
1659
1660 http_request_init_ex(&request, NULL, meth, URL);
1661 request.body = http_request_body_init_ex(&body, HTTP_REQUEST_BODY_CSTRING, data, data_len, 0);
1662 if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) {
1663 http_request_exec(&request);
1664 if (info) {
1665 http_request_info(&request, Z_ARRVAL_P(info));
1666 }
1667 RETVAL_RESPONSE_OR_BODY(request);
1668 }
1669 http_request_dtor(&request);
1670 }
1671 /* }}} */
1672
1673 static char *file_get_contents(char *file, size_t *len TSRMLS_DC)
1674 {
1675 php_stream *s = NULL;
1676 char *buf = NULL;
1677
1678 if ((s = php_stream_open_wrapper_ex(file, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL, HTTP_DEFAULT_STREAM_CONTEXT))) {
1679 *len = php_stream_copy_to_mem(s, &buf, (size_t) -1, 0);
1680 php_stream_close(s);
1681 } else {
1682 *len = 0;
1683 }
1684 return buf;
1685 }
1686 struct FormData {
1687 struct FormData *next;
1688 int type;
1689 char *line;
1690 size_t length;
1691 };
1692 CURLcode Curl_getFormData(struct FormData **, struct curl_httppost *post, curl_off_t *size);
1693
1694 /* {{{ proto string http_request_body_encode(array fields, array files)
1695 *
1696 * Generate x-www-form-urlencoded resp. form-data encoded request body.
1697 *
1698 * Returns encoded string on success, or FALSE on failure.
1699 */
1700 PHP_FUNCTION(http_request_body_encode)
1701 {
1702 zval *fields = NULL, *files = NULL;
1703 HashTable *fields_ht, *files_ht;
1704 http_request_body body;
1705 phpstr rbuf;
1706 struct FormData *data, *ptr;
1707 curl_off_t size;
1708 char *fdata = NULL;
1709 size_t fsize = 0;
1710 CURLcode rc;
1711 int fgc_error = 0;
1712
1713 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a!a!", &fields, &files)) {
1714 RETURN_FALSE;
1715 }
1716
1717 fields_ht = (fields && Z_TYPE_P(fields) == IS_ARRAY) ? Z_ARRVAL_P(fields) : NULL;
1718 files_ht = (files && Z_TYPE_P(files) == IS_ARRAY) ? Z_ARRVAL_P(files) : NULL;
1719 if (!http_request_body_fill(&body, fields_ht, files_ht)) {
1720 RETURN_FALSE;
1721 }
1722
1723 switch (body.type)
1724 {
1725 case HTTP_REQUEST_BODY_CURLPOST:
1726 if (CURLE_OK != (rc = Curl_getFormData(&data, body.data, &size))) {
1727 http_error_ex(HE_WARNING, HTTP_E_RUNTIME, "Could not encode request body: %s", curl_easy_strerror(rc));
1728 RETVAL_FALSE;
1729 } else {
1730 phpstr_init_ex(&rbuf, (size_t) size, PHPSTR_INIT_PREALLOC);
1731 for (ptr = data; ptr; ptr = ptr->next) {
1732 if (!fgc_error) {
1733 if (ptr->type) {
1734 if ((fdata = file_get_contents(ptr->line, &fsize TSRMLS_CC))) {
1735 phpstr_append(&rbuf, fdata, fsize);
1736 efree(fdata);
1737 } else {
1738 fgc_error = 1;
1739 }
1740 } else {
1741 phpstr_append(&rbuf, ptr->line, ptr->length);
1742 }
1743 }
1744 curl_free(ptr->line);
1745 }
1746 curl_free(data);
1747 if (fgc_error) {
1748 phpstr_dtor(&rbuf);
1749 RETVAL_FALSE;
1750 } else {
1751 RETVAL_PHPSTR_VAL(&rbuf);
1752 }
1753 }
1754 http_request_body_dtor(&body);
1755 break;
1756
1757 case HTTP_REQUEST_BODY_CSTRING:
1758 RETVAL_STRINGL(body.data, body.size, 0);
1759 break;
1760
1761 default:
1762 http_request_body_dtor(&body);
1763 RETVAL_FALSE;
1764 break;
1765 }
1766 }
1767 #endif /* HTTP_HAVE_CURL */
1768 /* }}} HAVE_CURL */
1769
1770 /* {{{ proto int http_request_method_register(string method)
1771 *
1772 * Register a custom request method.
1773 *
1774 * Expects a string parameter containing the request method name to register.
1775 *
1776 * Returns the ID of the request method on success, or FALSE on failure.
1777 */
1778 PHP_FUNCTION(http_request_method_register)
1779 {
1780 char *method;
1781 int method_len;
1782 ulong existing;
1783
1784 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len)) {
1785 RETURN_FALSE;
1786 }
1787 if ((existing = http_request_method_exists(1, 0, method))) {
1788 RETURN_LONG((long) existing);
1789 }
1790
1791 RETVAL_LONG((long) http_request_method_register(method, method_len));
1792 }
1793 /* }}} */
1794
1795 /* {{{ proto bool http_request_method_unregister(mixed method)
1796 *
1797 * Unregister a previously registered custom request method.
1798 *
1799 * Expects either the request method name or ID.
1800 *
1801 * Returns TRUE on success, or FALSE on failure.
1802 */
1803 PHP_FUNCTION(http_request_method_unregister)
1804 {
1805 zval *method;
1806
1807 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &method)) {
1808 RETURN_FALSE;
1809 }
1810
1811 switch (Z_TYPE_P(method))
1812 {
1813 case IS_OBJECT:
1814 convert_to_string(method);
1815 case IS_STRING:
1816 if (is_numeric_string(Z_STRVAL_P(method), Z_STRLEN_P(method), NULL, NULL, 1)) {
1817 convert_to_long(method);
1818 } else {
1819 int mn;
1820 if (!(mn = http_request_method_exists(1, 0, Z_STRVAL_P(method)))) {
1821 RETURN_FALSE;
1822 }
1823 zval_dtor(method);
1824 ZVAL_LONG(method, (long)mn);
1825 }
1826 case IS_LONG:
1827 RETURN_SUCCESS(http_request_method_unregister(Z_LVAL_P(method)));
1828 default:
1829 RETURN_FALSE;
1830 }
1831 }
1832 /* }}} */
1833
1834 /* {{{ proto int http_request_method_exists(mixed method)
1835 *
1836 * Check if a request method is registered (or available by default).
1837 *
1838 * Expects either the request method name or ID as parameter.
1839 *
1840 * Returns TRUE if the request method is known, else FALSE.
1841 */
1842 PHP_FUNCTION(http_request_method_exists)
1843 {
1844 IF_RETVAL_USED {
1845 zval *method;
1846
1847 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &method)) {
1848 RETURN_FALSE;
1849 }
1850
1851 switch (Z_TYPE_P(method))
1852 {
1853 case IS_OBJECT:
1854 convert_to_string(method);
1855 case IS_STRING:
1856 if (is_numeric_string(Z_STRVAL_P(method), Z_STRLEN_P(method), NULL, NULL, 1)) {
1857 convert_to_long(method);
1858 } else {
1859 RETURN_LONG((long) http_request_method_exists(1, 0, Z_STRVAL_P(method)));
1860 }
1861 case IS_LONG:
1862 RETURN_LONG((long) http_request_method_exists(0, (int) Z_LVAL_P(method), NULL));
1863 default:
1864 RETURN_FALSE;
1865 }
1866 }
1867 }
1868 /* }}} */
1869
1870 /* {{{ proto string http_request_method_name(int method)
1871 *
1872 * Get the literal string representation of a standard or registered request method.
1873 *
1874 * Expects the request method ID as parameter.
1875 *
1876 * Returns the request method name as string on success, or FALSE on failure.
1877 */
1878 PHP_FUNCTION(http_request_method_name)
1879 {
1880 IF_RETVAL_USED {
1881 long method;
1882
1883 if ((SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &method)) || (method < 0)) {
1884 RETURN_FALSE;
1885 }
1886
1887 RETURN_STRING(estrdup(http_request_method_name((int) method)), 0);
1888 }
1889 }
1890 /* }}} */
1891
1892 /* {{{ */
1893 #ifdef HTTP_HAVE_ZLIB
1894
1895 /* {{{ proto string http_deflate(string data[, int flags = 0])
1896 *
1897 * Compress data with gzip, zlib AKA deflate or raw deflate encoding.
1898 *
1899 * Expects the first parameter to be a string containing the data that should
1900 * be encoded.
1901 *
1902 * Returns the encoded string on success, or NULL on failure.
1903 */
1904 PHP_FUNCTION(http_deflate)
1905 {
1906 char *data;
1907 int data_len;
1908 long flags = 0;
1909
1910 RETVAL_NULL();
1911
1912 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &data, &data_len, &flags)) {
1913 char *encoded;
1914 size_t encoded_len;
1915
1916 if (SUCCESS == http_encoding_deflate(flags, data, data_len, &encoded, &encoded_len)) {
1917 RETURN_STRINGL(encoded, (int) encoded_len, 0);
1918 }
1919 }
1920 }
1921 /* }}} */
1922
1923 /* {{{ proto string http_inflate(string data)
1924 *
1925 * Decompress data compressed with either gzip, deflate AKA zlib or raw
1926 * deflate encoding.
1927 *
1928 * Expects a string as parameter containing the compressed data.
1929 *
1930 * Returns the decoded string on success, or NULL on failure.
1931 */
1932 PHP_FUNCTION(http_inflate)
1933 {
1934 char *data;
1935 int data_len;
1936
1937 RETVAL_NULL();
1938
1939 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_len)) {
1940 char *decoded;
1941 size_t decoded_len;
1942
1943 if (SUCCESS == http_encoding_inflate(data, data_len, &decoded, &decoded_len)) {
1944 RETURN_STRINGL(decoded, (int) decoded_len, 0);
1945 }
1946 }
1947 }
1948 /* }}} */
1949
1950 /* {{{ proto string ob_deflatehandler(string data, int mode)
1951 *
1952 * For use with ob_start(). The deflate output buffer handler can only be used once.
1953 * It conflicts with ob_gzhandler and zlib.output_compression as well and should
1954 * not be used after ext/mbstrings mb_output_handler and ext/sessions URL-Rewriter (AKA
1955 * session.use_trans_sid).
1956 */
1957 PHP_FUNCTION(ob_deflatehandler)
1958 {
1959 char *data;
1960 int data_len;
1961 long mode;
1962
1963 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &data, &data_len, &mode)) {
1964 RETURN_FALSE;
1965 }
1966
1967 http_ob_deflatehandler(data, data_len, &Z_STRVAL_P(return_value), (uint *) &Z_STRLEN_P(return_value), mode);
1968 Z_TYPE_P(return_value) = Z_STRVAL_P(return_value) ? IS_STRING : IS_NULL;
1969 }
1970 /* }}} */
1971
1972 /* {{{ proto string ob_inflatehandler(string data, int mode)
1973 *
1974 * For use with ob_start(). Same restrictions as with ob_deflatehandler apply.
1975 */
1976 PHP_FUNCTION(ob_inflatehandler)
1977 {
1978 char *data;
1979 int data_len;
1980 long mode;
1981
1982 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &data, &data_len, &mode)) {
1983 RETURN_FALSE;
1984 }
1985
1986 http_ob_inflatehandler(data, data_len, &Z_STRVAL_P(return_value), (uint *) &Z_STRLEN_P(return_value), mode);
1987 Z_TYPE_P(return_value) = Z_STRVAL_P(return_value) ? IS_STRING : IS_NULL;
1988 }
1989 /* }}} */
1990
1991 #endif /* HTTP_HAVE_ZLIB */
1992 /* }}} */
1993
1994 /* {{{ proto int http_support([int feature = 0])
1995 *
1996 * Check for feature that require external libraries.
1997 *
1998 * Accepts an optional in parameter specifying which feature to probe for.
1999 * If the parameter is 0 or omitted, the return value contains a bitmask of
2000 * all supported features that depend on external libraries.
2001 *
2002 * Available features to probe for are:
2003 * <ul>
2004 * <li> HTTP_SUPPORT: always set
2005 * <li> HTTP_SUPPORT_REQUESTS: whether ext/http was linked against libcurl,
2006 * and HTTP requests can be issued
2007 * <li> HTTP_SUPPORT_SSLREQUESTS: whether libcurl was linked against openssl,
2008 * and SSL requests can be issued
2009 * <li> HTTP_SUPPORT_ENCODINGS: whether ext/http was linked against zlib,
2010 * and compressed HTTP responses can be decoded
2011 * <li> HTTP_SUPPORT_MAGICMIME: whether ext/http was linked against libmagic,
2012 * and the HttpResponse::guessContentType() method is usable
2013 * </ul>
2014 *
2015 * Returns int, whether requested feature is supported, or a bitmask with
2016 * all supported features.
2017 */
2018 PHP_FUNCTION(http_support)
2019 {
2020 long feature = 0;
2021
2022 RETVAL_LONG(0L);
2023
2024 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &feature)) {
2025 RETVAL_LONG(http_support(feature));
2026 }
2027 }
2028 /* }}} */
2029
2030 PHP_FUNCTION(http_test)
2031 {
2032 }
2033
2034 /*
2035 * Local variables:
2036 * tab-width: 4
2037 * c-basic-offset: 4
2038 * End:
2039 * vim600: noet sw=4 ts=4 fdm=marker
2040 * vim<600: noet sw=4 ts=4
2041 */
2042