* attempt to fix snap-build
[m6w6/ext-http] / http.c
1 /*
2 +----------------------------------------------------------------------+
3 | PECL :: http |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.0 of the PHP license, that |
6 | is bundled with this package in the file LICENSE, and is available |
7 | through the world-wide-web at http://www.php.net/license/3_0.txt. |
8 | If you did not receive a copy of the PHP license and are unable to |
9 | obtain it through the world-wide-web, please send a note to |
10 | license@php.net so we can mail you a copy immediately. |
11 +----------------------------------------------------------------------+
12 | Copyright (c) 2004-2005 Michael Wallner <mike@php.net> |
13 +----------------------------------------------------------------------+
14 */
15
16 /* $Id$ */
17
18 #define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "php.h"
25 #include "snprintf.h"
26 #include "ext/standard/info.h"
27 #include "ext/session/php_session.h"
28 #include "ext/standard/php_string.h"
29 #include "ext/standard/php_smart_str.h"
30
31 #include "php_http.h"
32 #include "php_http_api.h"
33
34 #ifdef HTTP_HAVE_CURL
35
36 #ifdef PHP_WIN32
37 #include <winsock2.h>
38 #include <sys/types.h>
39 #endif
40
41 #include <curl/curl.h>
42 #endif
43
44 ZEND_DECLARE_MODULE_GLOBALS(http)
45
46 #ifdef COMPILE_DL_HTTP
47 ZEND_GET_MODULE(http)
48 #endif
49
50 /* {{{ http_functions[] */
51 function_entry http_functions[] = {
52 PHP_FE(http_date, NULL)
53 PHP_FE(http_absolute_uri, NULL)
54 PHP_FE(http_negotiate_language, NULL)
55 PHP_FE(http_negotiate_charset, NULL)
56 PHP_FE(http_redirect, NULL)
57 PHP_FE(http_send_status, NULL)
58 PHP_FE(http_send_last_modified, NULL)
59 PHP_FE(http_match_modified, NULL)
60 PHP_FE(http_match_etag, NULL)
61 PHP_FE(http_cache_last_modified, NULL)
62 PHP_FE(http_cache_etag, NULL)
63 PHP_FE(http_content_type, NULL)
64 PHP_FE(http_content_disposition, NULL)
65 PHP_FE(http_send_data, NULL)
66 PHP_FE(http_send_file, NULL)
67 PHP_FE(http_send_stream, NULL)
68 PHP_FE(http_chunked_decode, NULL)
69 PHP_FE(http_split_response, NULL)
70 #ifdef HTTP_HAVE_CURL
71 PHP_FE(http_get, NULL)
72 PHP_FE(http_head, NULL)
73 PHP_FE(http_post_data, NULL)
74 PHP_FE(http_post_array, NULL)
75 #endif
76 PHP_FE(http_auth_basic, NULL)
77 PHP_FE(http_auth_basic_cb, NULL)
78 {NULL, NULL, NULL}
79 };
80 /* }}} */
81
82 /* {{{ http_module_entry */
83 zend_module_entry http_module_entry = {
84 #if ZEND_MODULE_API_NO >= 20010901
85 STANDARD_MODULE_HEADER,
86 #endif
87 "http",
88 http_functions,
89 PHP_MINIT(http),
90 NULL,
91 NULL,
92 PHP_RSHUTDOWN(http),
93 PHP_MINFO(http),
94 #if ZEND_MODULE_API_NO >= 20010901
95 PHP_EXT_HTTP_VERSION,
96 #endif
97 STANDARD_MODULE_PROPERTIES
98 };
99 /* }}} */
100
101 #define RETURN_SUCCESS(v) RETURN_BOOL(SUCCESS == (v))
102 #define HASH_ORNULL(z) ((z) ? Z_ARRVAL_P(z) : NULL)
103
104 /* {{{ proto string http_date([int timestamp])
105 *
106 * This function returns a valid HTTP date regarding RFC 822/1123
107 * looking like: "Wed, 22 Dec 2004 11:34:47 GMT"
108 *
109 */
110 PHP_FUNCTION(http_date)
111 {
112 long t = -1;
113
114 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) {
115 RETURN_FALSE;
116 }
117
118 if (t == -1) {
119 t = (long) time(NULL);
120 }
121
122 RETURN_STRING(http_date(t), 0);
123 }
124 /* }}} */
125
126 /* {{{ proto string http_absolute_uri(string url[, string proto])
127 *
128 * This function returns an absolute URI constructed from url.
129 * If the url is already abolute but a different proto was supplied,
130 * only the proto part of the URI will be updated. If url has no
131 * path specified, the path of the current REQUEST_URI will be taken.
132 * The host will be taken either from the Host HTTP header of the client
133 * the SERVER_NAME or just localhost if prior are not available.
134 *
135 * Some examples:
136 * <pre>
137 * url = "page.php" => http://www.example.com/current/path/page.php
138 * url = "/page.php" => http://www.example.com/page.php
139 * url = "/page.php", proto = "https" => https://www.example.com/page.php
140 * </pre>
141 *
142 */
143 PHP_FUNCTION(http_absolute_uri)
144 {
145 char *url = NULL, *proto = NULL;
146 int url_len = 0, proto_len = 0;
147
148 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &url, &url_len, &proto, &proto_len) != SUCCESS) {
149 RETURN_FALSE;
150 }
151
152 RETURN_STRING(http_absolute_uri(url, proto), 0);
153 }
154 /* }}} */
155
156 /* {{{ proto string http_negotiate_language(array supported[, string default = 'en-US'])
157 *
158 * This function negotiates the clients preferred language based on its
159 * Accept-Language HTTP header. It returns the negotiated language or
160 * the default language if none match.
161 *
162 * The qualifier is recognized and languages without qualifier are rated highest.
163 *
164 * The supported parameter is expected to be an array having
165 * the supported languages as array values.
166 *
167 * Example:
168 * <pre>
169 * <?php
170 * $langs = array(
171 * 'en-US',// default
172 * 'fr',
173 * 'fr-FR',
174 * 'de',
175 * 'de-DE',
176 * 'de-AT',
177 * 'de-CH',
178 * );
179 * include './langs/'. http_negotiate_language($langs) .'.php';
180 * ?>
181 * </pre>
182 *
183 */
184 PHP_FUNCTION(http_negotiate_language)
185 {
186 zval *supported;
187 char *def = NULL;
188 int def_len = 0;
189
190 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|s", &supported, &def, &def_len) != SUCCESS) {
191 RETURN_FALSE;
192 }
193
194 if (!def) {
195 def = "en-US";
196 }
197
198 RETURN_STRING(http_negotiate_language(supported, def), 0);
199 }
200 /* }}} */
201
202 /* {{{ proto string http_negotiate_charset(array supported[, string default = 'iso-8859-1'])
203 *
204 * This function negotiates the clients preferred charset based on its
205 * Accept-Charset HTTP header. It returns the negotiated charset or
206 * the default charset if none match.
207 *
208 * The qualifier is recognized and charset without qualifier are rated highest.
209 *
210 * The supported parameter is expected to be an array having
211 * the supported charsets as array values.
212 *
213 * Example:
214 * <pre>
215 * <?php
216 * $charsets = array(
217 * 'iso-8859-1', // default
218 * 'iso-8859-2',
219 * 'iso-8859-15',
220 * 'utf-8'
221 * );
222 * $pref = http_negotiate_charset($charsets);
223 * if (!strcmp($pref, 'iso-8859-1')) {
224 * iconv_set_encoding('internal_encoding', 'iso-8859-1');
225 * iconv_set_encoding('output_encoding', $pref);
226 * ob_start('ob_iconv_handler');
227 * }
228 * ?>
229 * </pre>
230 */
231 PHP_FUNCTION(http_negotiate_charset)
232 {
233 zval *supported;
234 char *def = NULL;
235 int def_len = 0;
236
237 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|s", &supported, &def, &def_len) != SUCCESS) {
238 RETURN_FALSE;
239 }
240
241 if (!def) {
242 def = "iso-8859-1";
243 }
244
245 RETURN_STRING(http_negotiate_charset(supported, def), 0);
246 }
247 /* }}} */
248
249 /* {{{ proto bool http_send_status(int status)
250 *
251 * Send HTTP status code.
252 *
253 */
254 PHP_FUNCTION(http_send_status)
255 {
256 int status = 0;
257
258 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &status) != SUCCESS) {
259 RETURN_FALSE;
260 }
261 if (status < 100 || status > 510) {
262 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid HTTP status code (100-510): %d", status);
263 RETURN_FALSE;
264 }
265
266 RETURN_SUCCESS(http_send_status(status));
267 }
268 /* }}} */
269
270 /* {{{ proto bool http_send_last_modified([int timestamp])
271 *
272 * This converts the given timestamp to a valid HTTP date and
273 * sends it as "Last-Modified" HTTP header. If timestamp is
274 * omitted, current time is sent.
275 *
276 */
277 PHP_FUNCTION(http_send_last_modified)
278 {
279 long t = -1;
280
281 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) {
282 RETURN_FALSE;
283 }
284
285 if (t == -1) {
286 t = (long) time(NULL);
287 }
288
289 RETURN_SUCCESS(http_send_last_modified(t));
290 }
291 /* }}} */
292
293 /* {{{ proto bool http_match_modified([int timestamp])
294 *
295 * Matches the given timestamp against the clients "If-Modified-Since" resp.
296 * "If-Unmodified-Since" HTTP headers.
297 *
298 */
299 PHP_FUNCTION(http_match_modified)
300 {
301 long t = -1;
302
303 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) {
304 RETURN_FALSE;
305 }
306
307 // current time if not supplied (senseless though)
308 if (t == -1) {
309 t = (long) time(NULL);
310 }
311
312 RETURN_BOOL(http_modified_match("HTTP_IF_MODIFIED_SINCE", t) || http_modified_match("HTTP_IF_UNMODIFIED_SINCE", t));
313 }
314 /* }}} */
315
316 /* {{{ proto bool http_match_etag(string etag)
317 *
318 * This matches the given ETag against the clients
319 * "If-Match" resp. "If-None-Match" HTTP headers.
320 *
321 */
322 PHP_FUNCTION(http_match_etag)
323 {
324 int etag_len;
325 char *etag;
326
327 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &etag, &etag_len) != SUCCESS) {
328 RETURN_FALSE;
329 }
330
331 RETURN_BOOL(http_etag_match("HTTP_IF_NONE_MATCH", etag) || http_etag_match("HTTP_IF_MATCH", etag));
332 }
333 /* }}} */
334
335 /* {{{ proto bool http_cache_last_modified([int timestamp_or_expires]])
336 *
337 * If timestamp_or_exires is greater than 0, it is handled as timestamp
338 * and will be sent as date of last modification. If it is 0 or omitted,
339 * the current time will be sent as Last-Modified date. If it's negative,
340 * it is handled as expiration time in seconds, which means that if the
341 * requested last modification date is not between the calculated timespan,
342 * the Last-Modified header is updated and the actual body will be sent.
343 *
344 */
345 PHP_FUNCTION(http_cache_last_modified)
346 {
347 long last_modified = 0, send_modified = 0, t;
348 zval *zlm;
349
350 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &last_modified) != SUCCESS) {
351 RETURN_FALSE;
352 }
353
354 t = (long) time(NULL);
355
356 /* 0 or omitted */
357 if (!last_modified) {
358 /* does the client have? (att: caching "forever") */
359 if (zlm = http_get_server_var("HTTP_IF_MODIFIED_SINCE")) {
360 last_modified = send_modified = http_parse_date(Z_STRVAL_P(zlm));
361 /* send current time */
362 } else {
363 send_modified = t;
364 }
365 /* negative value is supposed to be expiration time */
366 } else if (last_modified < 0) {
367 last_modified += t;
368 send_modified = t;
369 /* send supplied time explicitly */
370 } else {
371 send_modified = last_modified;
372 }
373
374 http_send_header("Cache-Control: private, must-revalidate, max-age=0");
375
376 if (http_modified_match("HTTP_IF_MODIFIED_SINCE", last_modified)) {
377 if (SUCCESS == http_send_status(304)) {
378 zend_bailout();
379 } else {
380 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not send 304 Not Modified");
381 RETURN_FALSE;
382 }
383 }
384 RETURN_SUCCESS(http_send_last_modified(send_modified));
385 }
386 /* }}} */
387
388 /* {{{ proto bool http_cache_etag([string etag])
389 *
390 * This function attempts to cache the HTTP body based on an ETag,
391 * either supplied or generated through calculation of the MD5
392 * checksum of the output (uses output buffering).
393 *
394 * If clients "If-None-Match" header matches the supplied/calculated
395 * ETag, the body is considered cached on the clients side and
396 * a "304 Not Modified" status code is issued.
397 *
398 */
399 PHP_FUNCTION(http_cache_etag)
400 {
401 char *etag;
402 int etag_len = 0;
403
404 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &etag, &etag_len) != SUCCESS) {
405 RETURN_FALSE;
406 }
407
408 php_end_ob_buffers(0 TSRMLS_CC);
409 http_send_header("Cache-Control: private, must-revalidate, max-age=0");
410
411 /* if no etag is given and we didn't already
412 * start ob_etaghandler -- start it
413 */
414 if (!HTTP_G(etag_started) && !etag_len) {
415 php_ob_set_internal_handler(_http_ob_etaghandler, (uint) 4096, "etag output handler", 0 TSRMLS_CC);
416 HTTP_G(etag_started) = 1;
417 RETURN_BOOL(php_start_ob_buffer_named("etag output handler", (uint) 4096, 0 TSRMLS_CC));
418 }
419
420 if (http_etag_match("HTTP_IF_NONE_MATCH", etag)) {
421 if (SUCCESS == http_send_status(304)) {
422 zend_bailout();
423 } else {
424 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not send 304 Not Modified");
425 RETURN_FALSE;
426 }
427 }
428
429 RETURN_SUCCESS(http_send_etag(etag, etag_len));
430 }
431 /* }}} */
432
433 /* {{{ proto void http_redirect([string url[, array params[, bool session,[ bool permanent]]]])
434 *
435 * Redirect to a given url.
436 * The supplied url will be expanded with http_absolute_uri(), the params array will
437 * be treated with http_build_query() and the session identification will be appended
438 * if session is true.
439 *
440 * Depending on permanent the redirection will be issued with a permanent
441 * ("301 Moved Permanently") or a temporary ("302 Found") redirection
442 * status code.
443 *
444 * To be RFC compliant, "Redirecting to <a>URI</a>." will be displayed,
445 * if the client doesn't redirect immediatly.
446 */
447 PHP_FUNCTION(http_redirect)
448 {
449 int url_len;
450 zend_bool session = 0, permanent = 0;
451 zval *params = NULL;
452 smart_str qstr = {0};
453 char *url, *URI, LOC[HTTP_URI_MAXLEN + 9], RED[HTTP_URI_MAXLEN * 2 + 34];
454
455 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sa!/bb", &url, &url_len, &params, &session, &permanent) != SUCCESS) {
456 RETURN_FALSE;
457 }
458
459 /* append session info */
460 if (session && (PS(session_status) == php_session_active)) {
461 if (!params) {
462 MAKE_STD_ZVAL(params);
463 array_init(params);
464 }
465 if (add_assoc_string(params, PS(session_name), PS(id), 1) != SUCCESS) {
466 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not append session information");
467 }
468 }
469
470 /* treat params array with http_build_query() */
471 if (params) {
472 if (php_url_encode_hash_ex(Z_ARRVAL_P(params), &qstr, NULL,0,NULL,0,NULL,0,NULL TSRMLS_CC) != SUCCESS) {
473 if (qstr.c) {
474 efree(qstr.c);
475 }
476 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not encode query parameters");
477 RETURN_FALSE;
478 }
479 smart_str_0(&qstr);
480 }
481
482 URI = http_absolute_uri(url, NULL);
483 if (qstr.c) {
484 snprintf(LOC, HTTP_URI_MAXLEN + strlen("Location: "), "Location: %s?%s", URI, qstr.c);
485 sprintf(RED, "Redirecting to <a href=\"%s?%s\">%s?%s</a>.\n", URI, qstr.c, URI, qstr.c);
486 efree(qstr.c);
487 } else {
488 snprintf(LOC, HTTP_URI_MAXLEN + strlen("Location: "), "Location: %s", URI);
489 sprintf(RED, "Redirecting to <a href=\"%s\">%s</a>.\n", URI, URI);
490 }
491 efree(URI);
492
493 if ((SUCCESS == http_send_header(LOC)) && (SUCCESS == http_send_status((permanent ? 301 : 302)))) {
494 php_body_write(RED, strlen(RED) TSRMLS_CC);
495 RETURN_TRUE;
496 }
497 RETURN_FALSE;
498 }
499 /* }}} */
500
501 /* {{{ proto bool http_send_data(string data)
502 *
503 * Sends raw data with support for (multiple) range requests.
504 *
505 */
506 PHP_FUNCTION(http_send_data)
507 {
508 zval *zdata;
509
510 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zdata) != SUCCESS) {
511 RETURN_FALSE;
512 }
513
514 convert_to_string_ex(&zdata);
515 http_send_header("Accept-Ranges: bytes");
516 RETURN_SUCCESS(http_send_data(zdata));
517 }
518 /* }}} */
519
520 /* {{{ proto bool http_send_file(string file)
521 *
522 * Sends a file with support for (multiple) range requests.
523 *
524 */
525 PHP_FUNCTION(http_send_file)
526 {
527 zval *zfile;
528
529 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zfile) != SUCCESS) {
530 RETURN_FALSE;
531 }
532
533 convert_to_string_ex(&zfile);
534 http_send_header("Accept-Ranges: bytes");
535 RETURN_SUCCESS(http_send_file(zfile));
536 }
537 /* }}} */
538
539 /* {{{ proto bool http_send_stream(resource stream)
540 *
541 * Sends an already opened stream with support for (multiple) range requests.
542 *
543 */
544 PHP_FUNCTION(http_send_stream)
545 {
546 zval *zstream;
547 php_stream *file;
548
549 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zstream) != SUCCESS) {
550 RETURN_FALSE;
551 }
552
553 php_stream_from_zval(file, &zstream);
554 http_send_header("Accept-Ranges: bytes");
555 RETURN_SUCCESS(http_send_stream(file));
556 }
557 /* }}} */
558
559 /* {{{ proto bool http_content_type([string content_type = 'application/x-octetstream'])
560 *
561 * Sets the content type.
562 *
563 */
564 PHP_FUNCTION(http_content_type)
565 {
566 char *ct, *content_type;
567 int ct_len = 0;
568
569 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &ct, &ct_len) != SUCCESS) {
570 RETURN_FALSE;
571 }
572
573 if (!ct_len) {
574 RETURN_SUCCESS(http_send_header("Content-Type: application/x-octetstream"));
575 }
576
577 /* remember for multiple ranges */
578 if (HTTP_G(ctype)) {
579 efree(HTTP_G(ctype));
580 }
581 HTTP_G(ctype) = estrndup(ct, ct_len);
582
583 content_type = (char *) emalloc(strlen("Content-Type: ") + ct_len + 1);
584 sprintf(content_type, "Content-Type: %s", ct);
585
586 RETVAL_BOOL(SUCCESS == http_send_header(content_type));
587 efree(content_type);
588 }
589 /* }}} */
590
591 /* {{{ proto bool http_content_disposition(string filename[, bool inline = false])
592 *
593 * Set the Content Disposition. The Content-Disposition header is very useful
594 * if the data actually sent came from a file or something similar, that should
595 * be "saved" by the client/user (i.e. by browsers "Save as..." popup window).
596 *
597 */
598 PHP_FUNCTION(http_content_disposition)
599 {
600 char *filename, *header;
601 int f_len;
602 zend_bool send_inline = 0;
603
604 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &filename, &f_len, &send_inline) != SUCCESS) {
605 RETURN_FALSE;
606 }
607
608 if (send_inline) {
609 header = (char *) emalloc(strlen("Content-Disposition: inline; filename=\"\"") + f_len + 1);
610 sprintf(header, "Content-Disposition: inline; filename=\"%s\"", filename);
611 } else {
612 header = (char *) emalloc(strlen("Content-Disposition: attachment; filename=\"\"") + f_len + 1);
613 sprintf(header, "Content-Disposition: attachment; filename=\"%s\"", filename);
614 }
615
616 RETVAL_BOOL(SUCCESS == http_send_header(header));
617 efree(header);
618 }
619 /* }}} */
620
621 /* {{{ proto string http_chunked_decode(string encoded)
622 *
623 * This function decodes a string that was HTTP-chunked encoded.
624 * Returns false on failure.
625 */
626 PHP_FUNCTION(http_chunked_decode)
627 {
628 char *encoded = NULL, *decoded = NULL;
629 int encoded_len = 0, decoded_len = 0;
630
631 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &encoded, &encoded_len) != SUCCESS) {
632 RETURN_FALSE;
633 }
634
635 if (SUCCESS == http_chunked_decode(encoded, encoded_len, &decoded, &decoded_len)) {
636 RETURN_STRINGL(decoded, decoded_len, 0);
637 } else {
638 RETURN_FALSE;
639 }
640 }
641 /* }}} */
642
643 /* {{{ proto array http_split_response(string http_response)
644 *
645 * This function splits an HTTP response into an array with headers and the
646 * content body. The returned array may look simliar to the following example:
647 *
648 * <pre>
649 * array(
650 * 0 => array(
651 * 'Status' => '200 Ok',
652 * 'Content-Type' => 'text/plain',
653 * 'Content-Language' => 'en-US'
654 * ),
655 * 1 => "Hello World!"
656 * );
657 * </pre>
658 */
659 PHP_FUNCTION(http_split_response)
660 {
661 zval *zresponse, *zbody, *zheaders;
662
663 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zresponse) != SUCCESS) {
664 RETURN_FALSE;
665 }
666
667 convert_to_string_ex(&zresponse);
668
669 MAKE_STD_ZVAL(zbody);
670 MAKE_STD_ZVAL(zheaders);
671 array_init(zheaders);
672
673 http_split_response(zresponse, zheaders, zbody);
674
675 array_init(return_value);
676 add_index_zval(return_value, 0, zheaders);
677 add_index_zval(return_value, 1, zbody);
678 }
679 /* }}} */
680
681 /* {{{ HAVE_CURL */
682 #ifdef HTTP_HAVE_CURL
683
684 /* {{{ proto string http_get(string url[, array options[, array &info]])
685 *
686 * Performs an HTTP GET request on the supplied url.
687 *
688 * The second parameter is expected to be an associative
689 * array where the following keys will be recognized:
690 * <pre>
691 * - redirect: int, whether and how many redirects to follow
692 * - unrestrictedauth: bool, whether to continue sending credentials on
693 * redirects to a different host
694 * - proxyhost: string, proxy host in "host[:port]" format
695 * - proxyport: int, use another proxy port as specified in proxyhost
696 * - proxyauth: string, proxy credentials in "user:pass" format
697 * - proxyauthtype: int, HTTP_AUTH_BASIC and/or HTTP_AUTH_NTLM
698 * - httpauth: string, http credentials in "user:pass" format
699 * - httpauthtype: int, HTTP_AUTH_BASIC, DIGEST and/or NTLM
700 * - compress: bool, whether to allow gzip/deflate content encoding
701 * (defaults to true)
702 * - port: int, use another port as specified in the url
703 * - referer: string, the referer to sends
704 * - useragent: string, the user agent to send
705 * (defaults to PECL::HTTP/version (PHP/version)))
706 * - headers: array, list of custom headers as associative array
707 * like array("header" => "value")
708 * - cookies: array, list of cookies as associative array
709 * like array("cookie" => "value")
710 * - cookiestore: string, path to a file where cookies are/will be stored
711 * </pre>
712 *
713 * The optional third parameter will be filled with some additional information
714 * in form af an associative array, if supplied (don't forget to initialize it
715 * with NULL or array()).
716 */
717 PHP_FUNCTION(http_get)
718 {
719 char *URL, *data = NULL;
720 size_t data_len = 0;
721 int URL_len;
722 zval *options = NULL, *info = NULL;
723
724 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a/!z", &URL, &URL_len, &options, &info) != SUCCESS) {
725 RETURN_FALSE;
726 }
727
728 if (info) {
729 zval_dtor(info);
730 array_init(info);
731 }
732
733 if (SUCCESS == http_get(URL, HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) {
734 RETURN_STRINGL(data, data_len, 0);
735 } else {
736 RETURN_FALSE;
737 }
738 }
739 /* }}} */
740
741 /* {{{ proto string http_head(string url[, array options[, array &info]])
742 *
743 * Performs an HTTP HEAD request on the suppied url.
744 * Returns the HTTP response as string.
745 * See http_get() for a full list of available options.
746 */
747 PHP_FUNCTION(http_head)
748 {
749 char *URL, *data = NULL;
750 size_t data_len = 0;
751 int URL_len;
752 zval *options = NULL, *info = NULL;
753
754 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a/!z", &URL, &URL_len, &options, &info) != SUCCESS) {
755 RETURN_FALSE;
756 }
757
758 if (info) {
759 zval_dtor(info);
760 array_init(info);
761 }
762
763 if (SUCCESS == http_head(URL, HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) {
764 RETURN_STRINGL(data, data_len, 0);
765 } else {
766 RETURN_FALSE;
767 }
768 }
769 /* }}} */
770
771 /* {{{ proto string http_post_data(string url, string data[, array options[, &info]])
772 *
773 * Performs an HTTP POST request, posting data.
774 * Returns the HTTP response as string.
775 * See http_get() for a full list of available options.
776 */
777 PHP_FUNCTION(http_post_data)
778 {
779 char *URL, *postdata, *data = NULL;
780 size_t data_len = 0;
781 int postdata_len, URL_len;
782 zval *options = NULL, *info = NULL;
783
784 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!z", &URL, &URL_len, &postdata, &postdata_len, &options, &info) != SUCCESS) {
785 RETURN_FALSE;
786 }
787
788 if (info) {
789 zval_dtor(info);
790 array_init(info);
791 }
792
793 if (SUCCESS == http_post_data(URL, postdata, (size_t) postdata_len, HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) {
794 RETURN_STRINGL(data, data_len, 0);
795 } else {
796 RETURN_FALSE;
797 }
798 }
799 /* }}} */
800
801 /* {{{ proto string http_post_array(string url, array data[, array options[, array &info]])
802 *
803 * Performs an HTTP POST request, posting www-form-urlencoded array data.
804 * Returns the HTTP response as string.
805 * See http_get() for a full list of available options.
806 */
807 PHP_FUNCTION(http_post_array)
808 {
809 char *URL, *data = NULL;
810 size_t data_len = 0;
811 int URL_len;
812 zval *options = NULL, *info = NULL, *postdata;
813
814 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|a/!z", &URL, &URL_len, &postdata, &options, &info) != SUCCESS) {
815 RETURN_FALSE;
816 }
817
818 if (info) {
819 zval_dtor(info);
820 array_init(info);
821 }
822
823 if (SUCCESS == http_post_array(URL, Z_ARRVAL_P(postdata), HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) {
824 RETURN_STRINGL(data, data_len, 0);
825 } else {
826 RETURN_FALSE;
827 }
828 }
829 /* }}} */
830
831 #endif
832 /* }}} HAVE_CURL */
833
834
835 /* {{{ proto bool http_auth_basic(string user, string pass[, string realm = "Restricted"])
836 *
837 * Example:
838 * <pre>
839 * <?php
840 * if (!http_auth_basic('mike', 's3c|r3t')) {
841 * die('<h1>Authorization failed!</h1>');
842 * }
843 * ?>
844 * </pre>
845 */
846 PHP_FUNCTION(http_auth_basic)
847 {
848 char *realm = NULL, *user, *pass, *suser, *spass;
849 int r_len, u_len, p_len;
850
851 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|s", &user, &u_len, &pass, &p_len, &realm, &r_len) != SUCCESS) {
852 RETURN_FALSE;
853 }
854
855 if (!realm) {
856 realm = "Restricted";
857 }
858
859 if (SUCCESS != http_auth_credentials(&suser, &spass)) {
860 http_auth_header("Basic", realm);
861 RETURN_FALSE;
862 }
863
864 if (strcasecmp(suser, user)) {
865 http_auth_header("Basic", realm);
866 RETURN_FALSE;
867 }
868
869 if (strcmp(spass, pass)) {
870 http_auth_header("Basic", realm);
871 RETURN_FALSE;
872 }
873
874 RETURN_TRUE;
875 }
876 /* }}} */
877
878 /* {{{ proto bool http_auth_basic_cb(mixed callback[, string realm = "Restricted"])
879 *
880 * Example:
881 * <pre>
882 * <?php
883 * function auth_cb($user, $pass)
884 * {
885 * global $db;
886 * $query = 'SELECT pass FROM users WHERE user='. $db->quoteSmart($user);
887 * if (strlen($realpass = $db->getOne($query)) {
888 * return $pass === $realpass;
889 * }
890 * return false;
891 * }
892 *
893 * if (!http_auth_basic_cb('auth_cb')) {
894 * die('<h1>Authorization failed</h1>');
895 * }
896 * ?>
897 * </pre>
898 */
899 PHP_FUNCTION(http_auth_basic_cb)
900 {
901 zval *cb;
902 char *realm = NULL, *user, *pass;
903 int r_len;
904
905 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s", &cb, &realm, &r_len) != SUCCESS) {
906 RETURN_FALSE;
907 }
908
909 if (!realm) {
910 realm = "Restricted";
911 }
912
913 if (SUCCESS != http_auth_credentials(&user, &pass)) {
914 http_auth_header("Basic", realm);
915 RETURN_FALSE;
916 }
917 {
918 zval *zparams[2] = {NULL, NULL}, retval;
919 int result = 0;
920
921 MAKE_STD_ZVAL(zparams[0]);
922 MAKE_STD_ZVAL(zparams[1]);
923 ZVAL_STRING(zparams[0], user, 0);
924 ZVAL_STRING(zparams[1], pass, 0);
925
926 if (SUCCESS == call_user_function(EG(function_table), NULL, cb,
927 &retval, 2, zparams TSRMLS_CC)) {
928 result = Z_LVAL(retval);
929 }
930
931 efree(user);
932 efree(pass);
933 efree(zparams[0]);
934 efree(zparams[1]);
935
936 if (!result) {
937 http_auth_header("Basic", realm);
938 }
939
940 RETURN_BOOL(result);
941 }
942 }
943 /* }}}*/
944
945
946 /* {{{ php_http_init_globals(zend_http_globals *) */
947 static void php_http_init_globals(zend_http_globals *http_globals)
948 {
949 http_globals->etag_started = 0;
950 http_globals->ctype = NULL;
951 http_globals->etag = NULL;
952 http_globals->lmod = 0;
953 #ifdef HTTP_HAVE_CURL
954 http_globals->curlbuf.body.data = NULL;
955 http_globals->curlbuf.body.used = 0;
956 http_globals->curlbuf.body.free = 0;
957 http_globals->curlbuf.hdrs.data = NULL;
958 http_globals->curlbuf.hdrs.used = 0;
959 http_globals->curlbuf.hdrs.free = 0;
960 #endif
961 }
962 /* }}} */
963
964 /* {{{ PHP_MINIT_FUNCTION */
965 PHP_MINIT_FUNCTION(http)
966 {
967 ZEND_INIT_MODULE_GLOBALS(http, php_http_init_globals, NULL);
968 #ifdef HTTP_HAVE_CURL
969 REGISTER_LONG_CONSTANT("HTTP_AUTH_BASIC", CURLAUTH_BASIC, CONST_CS | CONST_PERSISTENT);
970 REGISTER_LONG_CONSTANT("HTTP_AUTH_DIGEST", CURLAUTH_DIGEST, CONST_CS | CONST_PERSISTENT);
971 REGISTER_LONG_CONSTANT("HTTP_AUTH_NTLM", CURLAUTH_NTLM, CONST_CS | CONST_PERSISTENT);
972 #endif
973 return SUCCESS;
974 }
975 /* }}} */
976
977 /* {{{ PHP_RSHUTDOWN_FUNCTION */
978 PHP_RSHUTDOWN_FUNCTION(http)
979 {
980 if (HTTP_G(ctype)) {
981 efree(HTTP_G(ctype));
982 }
983 if (HTTP_G(etag)) {
984 efree(HTTP_G(etag));
985 }
986 #ifdef HTTP_HAVE_CURL
987 if (HTTP_G(curlbuf).body.data) {
988 efree(HTTP_G(curlbuf).body.data);
989 }
990 if (HTTP_G(curlbuf).hdrs.data) {
991 efree(HTTP_G(curlbuf).hdrs.data);
992 }
993 #endif
994 return SUCCESS;
995 }
996 /* }}} */
997
998 /* {{{ PHP_MINFO_FUNCTION */
999 PHP_MINFO_FUNCTION(http)
1000 {
1001 php_info_print_table_start();
1002 php_info_print_table_header(2, "Extended HTTP support", "enabled");
1003 php_info_print_table_row(2, "Version:", PHP_EXT_HTTP_VERSION);
1004 php_info_print_table_row(2, "cURL convenience functions:",
1005 #ifdef HTTP_HAVE_CURL
1006 "enabled"
1007 #else
1008 "disabled"
1009 #endif
1010 );
1011 php_info_print_table_end();
1012 }
1013 /* }}} */
1014
1015 /*
1016 * Local variables:
1017 * tab-width: 4
1018 * c-basic-offset: 4
1019 * End:
1020 * vim600: noet sw=4 ts=4 fdm=marker
1021 * vim<600: noet sw=4 ts=4
1022 */
1023