* attempt to make http_build_query available for PHP4
[m6w6/ext-http] / http_api.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 <ctype.h>
25
26 #include "php.h"
27 #include "php_version.h"
28 #include "php_streams.h"
29 #include "snprintf.h"
30 #include "ext/standard/md5.h"
31 #include "ext/standard/url.h"
32 #include "ext/standard/base64.h"
33 #include "ext/standard/php_string.h"
34 #include "ext/standard/php_smart_str.h"
35 #include "ext/standard/php_lcg.h"
36
37 #include "SAPI.h"
38
39 #ifdef ZEND_ENGINE_2
40 #include "ext/standard/php_http.h"
41 #else
42 #include "php_http_build_query.c"
43 #endif
44
45 #include "php_http.h"
46 #include "php_http_api.h"
47
48 #ifdef HTTP_HAVE_CURL
49
50 #ifdef PHP_WIN32
51 #include <winsock2.h>
52 #include <sys/types.h>
53 #endif
54
55 #include <curl/curl.h>
56 #include <curl/easy.h>
57 #endif
58
59
60 ZEND_DECLARE_MODULE_GLOBALS(http)
61
62 /* {{{ day/month names */
63 static const char *days[] = {
64 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
65 };
66 static const char *wkdays[] = {
67 "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
68 };
69 static const char *weekdays[] = {
70 "Monday", "Tuesday", "Wednesday",
71 "Thursday", "Friday", "Saturday", "Sunday"
72 };
73 static const char *months[] = {
74 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
75 "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"
76 };
77 enum assume_next {
78 DATE_MDAY,
79 DATE_YEAR,
80 DATE_TIME
81 };
82 static const struct time_zone {
83 const char *name;
84 const int offset;
85 } time_zones[] = {
86 {"GMT", 0}, /* Greenwich Mean */
87 {"UTC", 0}, /* Universal (Coordinated) */
88 {"WET", 0}, /* Western European */
89 {"BST", 0}, /* British Summer */
90 {"WAT", 60}, /* West Africa */
91 {"AST", 240}, /* Atlantic Standard */
92 {"ADT", 240}, /* Atlantic Daylight */
93 {"EST", 300}, /* Eastern Standard */
94 {"EDT", 300}, /* Eastern Daylight */
95 {"CST", 360}, /* Central Standard */
96 {"CDT", 360}, /* Central Daylight */
97 {"MST", 420}, /* Mountain Standard */
98 {"MDT", 420}, /* Mountain Daylight */
99 {"PST", 480}, /* Pacific Standard */
100 {"PDT", 480}, /* Pacific Daylight */
101 {"YST", 540}, /* Yukon Standard */
102 {"YDT", 540}, /* Yukon Daylight */
103 {"HST", 600}, /* Hawaii Standard */
104 {"HDT", 600}, /* Hawaii Daylight */
105 {"CAT", 600}, /* Central Alaska */
106 {"AHST", 600}, /* Alaska-Hawaii Standard */
107 {"NT", 660}, /* Nome */
108 {"IDLW", 720}, /* International Date Line West */
109 {"CET", -60}, /* Central European */
110 {"MET", -60}, /* Middle European */
111 {"MEWT", -60}, /* Middle European Winter */
112 {"MEST", -120}, /* Middle European Summer */
113 {"CEST", -120}, /* Central European Summer */
114 {"MESZ", -60}, /* Middle European Summer */
115 {"FWT", -60}, /* French Winter */
116 {"FST", -60}, /* French Summer */
117 {"EET", -120}, /* Eastern Europe, USSR Zone 1 */
118 {"WAST", -420}, /* West Australian Standard */
119 {"WADT", -420}, /* West Australian Daylight */
120 {"CCT", -480}, /* China Coast, USSR Zone 7 */
121 {"JST", -540}, /* Japan Standard, USSR Zone 8 */
122 {"EAST", -600}, /* Eastern Australian Standard */
123 {"EADT", -600}, /* Eastern Australian Daylight */
124 {"GST", -600}, /* Guam Standard, USSR Zone 9 */
125 {"NZT", -720}, /* New Zealand */
126 {"NZST", -720}, /* New Zealand Standard */
127 {"NZDT", -720}, /* New Zealand Daylight */
128 {"IDLE", -720}, /* International Date Line East */
129 };
130 /* }}} */
131
132 /* {{{ internals */
133
134 static int http_sort_q(const void *a, const void *b TSRMLS_DC);
135 #define http_etag(e, p, l, m) _http_etag((e), (p), (l), (m) TSRMLS_CC)
136 static inline char *_http_etag(char **new_etag, const void *data_ptr, const size_t data_len, const http_send_mode data_mode TSRMLS_DC);
137 #define http_is_range_request() _http_is_range_request(TSRMLS_C)
138 static inline int _http_is_range_request(TSRMLS_D);
139 #define http_send_chunk(d, b, e, m) _http_send_chunk((d), (b), (e), (m) TSRMLS_CC)
140 static STATUS _http_send_chunk(const void *data, const size_t begin, const size_t end, const http_send_mode mode TSRMLS_DC);
141
142 static int check_day(char *day, size_t len);
143 static int check_month(char *month);
144 static int check_tzone(char *tzone);
145
146 /* {{{ HAVE_CURL */
147 #ifdef HTTP_HAVE_CURL
148 #define http_curl_initbuf(m) _http_curl_initbuf((m) TSRMLS_CC)
149 static inline void _http_curl_initbuf(http_curlbuf_member member TSRMLS_DC);
150 #define http_curl_freebuf(m) _http_curl_freebuf((m) TSRMLS_CC)
151 static inline void _http_curl_freebuf(http_curlbuf_member member TSRMLS_DC);
152 #define http_curl_sizebuf(m, l) _http_curl_sizebuf((m), (l) TSRMLS_CC)
153 static inline void _http_curl_sizebuf(http_curlbuf_member member, size_t len TSRMLS_DC);
154 #define http_curl_movebuf(m, d, l) _http_curl_movebuf((m), (d), (l) TSRMLS_CC)
155 static inline void _http_curl_movebuf(http_curlbuf_member member, char **data, size_t *data_len TSRMLS_DC);
156 #define http_curl_copybuf(m, d, l) _http_curl_copybuf((m), (d), (l) TSRMLS_CC)
157 static inline void _http_curl_copybuf(http_curlbuf_member member, char **data, size_t *data_len TSRMLS_DC);
158 #define http_curl_setopts(c, u, o) _http_curl_setopts((c), (u), (o) TSRMLS_CC)
159 static inline void _http_curl_setopts(CURL *ch, const char *url, HashTable *options TSRMLS_DC);
160
161 #define http_curl_getopt(o, k) _http_curl_getopt((o), (k) TSRMLS_CC, 0)
162 #define http_curl_getopt1(o, k, t1) _http_curl_getopt((o), (k) TSRMLS_CC, 1, (t1))
163 #define http_curl_getopt2(o, k, t1, t2) _http_curl_getopt((o), (k) TSRMLS_CC, 2, (t1), (t2))
164 static inline zval *_http_curl_getopt(HashTable *options, char *key TSRMLS_DC, int checks, ...);
165
166 static size_t http_curl_body_callback(char *, size_t, size_t, void *);
167 static size_t http_curl_hdrs_callback(char *, size_t, size_t, void *);
168
169 #define http_curl_getinfo(c, h) _http_curl_getinfo((c), (h) TSRMLS_CC)
170 static inline void _http_curl_getinfo(CURL *ch, HashTable *info TSRMLS_DC);
171 #define http_curl_getinfo_ex(c, i, a) _http_curl_getinfo_ex((c), (i), (a) TSRMLS_CC)
172 static inline void _http_curl_getinfo_ex(CURL *ch, CURLINFO i, zval *array TSRMLS_DC);
173 #define http_curl_getinfoname(i) _http_curl_getinfoname((i) TSRMLS_CC)
174 static inline char *_http_curl_getinfoname(CURLINFO i TSRMLS_DC);
175
176 #endif
177 /* }}} HAVE_CURL */
178
179 /* {{{ static int http_sort_q(const void *, const void *) */
180 static int http_sort_q(const void *a, const void *b TSRMLS_DC)
181 {
182 Bucket *f, *s;
183 zval result, *first, *second;
184
185 f = *((Bucket **) a);
186 s = *((Bucket **) b);
187
188 first = *((zval **) f->pData);
189 second= *((zval **) s->pData);
190
191 if (numeric_compare_function(&result, first, second TSRMLS_CC) != SUCCESS) {
192 return 0;
193 }
194 return (Z_LVAL(result) > 0 ? -1 : (Z_LVAL(result) < 0 ? 1 : 0));
195 }
196 /* }}} */
197
198 /* {{{ static inline char *http_etag(char **, void *, size_t, http_send_mode) */
199 static inline char *_http_etag(char **new_etag, const void *data_ptr,
200 const size_t data_len, const http_send_mode data_mode TSRMLS_DC)
201 {
202 char ssb_buf[127], digest[16];
203 PHP_MD5_CTX ctx;
204
205 PHP_MD5Init(&ctx);
206
207 switch (data_mode)
208 {
209 case SEND_DATA:
210 PHP_MD5Update(&ctx, data_ptr, data_len);
211 break;
212
213 case SEND_RSRC:
214 snprintf(ssb_buf, 127, "%l=%l=%l",
215 HTTP_G(ssb).sb.st_mtime,
216 HTTP_G(ssb).sb.st_ino,
217 HTTP_G(ssb).sb.st_size
218 );
219 PHP_MD5Update(&ctx, ssb_buf, strlen(ssb_buf));
220 break;
221
222 default:
223 return NULL;
224 break;
225 }
226
227 PHP_MD5Final(digest, &ctx);
228 make_digest(*new_etag, digest);
229
230 return *new_etag;
231 }
232 /* }}} */
233
234 /* {{{ static inline int http_is_range_request(void) */
235 static inline int _http_is_range_request(TSRMLS_D)
236 {
237 return zend_hash_exists(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]),
238 "HTTP_RANGE", strlen("HTTP_RANGE") + 1);
239 }
240 /* }}} */
241
242 /* {{{ static STATUS http_send_chunk(const void *, size_t, size_t,
243 http_send_mode) */
244 static STATUS _http_send_chunk(const void *data, const size_t begin,
245 const size_t end, const http_send_mode mode TSRMLS_DC)
246 {
247 char *buf;
248 size_t read = 0;
249 long len = end - begin;
250 php_stream *s;
251
252 switch (mode)
253 {
254 case SEND_RSRC:
255 s = (php_stream *) data;
256 if (php_stream_seek(s, begin, SEEK_SET)) {
257 return FAILURE;
258 }
259 buf = (char *) ecalloc(1, HTTP_BUF_SIZE);
260 /* read into buf and write out */
261 while ((len -= HTTP_BUF_SIZE) >= 0) {
262 if (!(read = php_stream_read(s, buf, HTTP_BUF_SIZE))) {
263 efree(buf);
264 return FAILURE;
265 }
266 if (read - php_body_write(buf, read TSRMLS_CC)) {
267 efree(buf);
268 return FAILURE;
269 }
270 }
271
272 /* read & write left over */
273 if (len) {
274 if (read = php_stream_read(s, buf, HTTP_BUF_SIZE + len)) {
275 if (read - php_body_write(buf, read TSRMLS_CC)) {
276 efree(buf);
277 return FAILURE;
278 }
279 } else {
280 efree(buf);
281 return FAILURE;
282 }
283 }
284 efree(buf);
285 return SUCCESS;
286 break;
287
288 case SEND_DATA:
289 return len == php_body_write(
290 Z_STRVAL_P((zval *) data) + begin, len TSRMLS_CC)
291 ? SUCCESS : FAILURE;
292 break;
293
294 default:
295 return FAILURE;
296 break;
297 }
298 }
299 /* }}} */
300
301 /* {{{ HAVE_CURL */
302 #ifdef HTTP_HAVE_CURL
303
304 /* {{{ static inline void http_curl_initbuf(http_curlbuf_member) */
305 static inline void _http_curl_initbuf(http_curlbuf_member member TSRMLS_DC)
306 {
307 http_curl_freebuf(member);
308
309 if (member & CURLBUF_HDRS) {
310 HTTP_G(curlbuf).hdrs.data = emalloc(HTTP_CURLBUF_HDRSSIZE);
311 HTTP_G(curlbuf).hdrs.free = HTTP_CURLBUF_HDRSSIZE;
312 }
313 if (member & CURLBUF_BODY) {
314 HTTP_G(curlbuf).body.data = emalloc(HTTP_CURLBUF_BODYSIZE);
315 HTTP_G(curlbuf).body.free = HTTP_CURLBUF_BODYSIZE;
316 }
317 }
318 /* }}} */
319
320 /* {{{ static inline void http_curl_freebuf(http_curlbuf_member) */
321 static inline void _http_curl_freebuf(http_curlbuf_member member TSRMLS_DC)
322 {
323 if (member & CURLBUF_HDRS) {
324 if (HTTP_G(curlbuf).hdrs.data) {
325 efree(HTTP_G(curlbuf).hdrs.data);
326 HTTP_G(curlbuf).hdrs.data = NULL;
327 }
328 HTTP_G(curlbuf).hdrs.used = 0;
329 HTTP_G(curlbuf).hdrs.free = 0;
330 }
331 if (member & CURLBUF_BODY) {
332 if (HTTP_G(curlbuf).body.data) {
333 efree(HTTP_G(curlbuf).body.data);
334 HTTP_G(curlbuf).body.data = NULL;
335 }
336 HTTP_G(curlbuf).body.used = 0;
337 HTTP_G(curlbuf).body.free = 0;
338 }
339 }
340 /* }}} */
341
342 /* {{{ static inline void http_curl_copybuf(http_curlbuf_member, char **,
343 size_t *) */
344 static inline void _http_curl_copybuf(http_curlbuf_member member, char **data,
345 size_t *data_len TSRMLS_DC)
346 {
347 *data = NULL;
348 *data_len = 0;
349
350 if ((member & CURLBUF_HDRS) && HTTP_G(curlbuf).hdrs.used) {
351 if ((member & CURLBUF_BODY) && HTTP_G(curlbuf).body.used) {
352 *data = emalloc(HTTP_G(curlbuf).hdrs.used + HTTP_G(curlbuf).body.used + 1);
353 } else {
354 *data = emalloc(HTTP_G(curlbuf).hdrs.used + 1);
355 }
356 memcpy(*data, HTTP_G(curlbuf).hdrs.data, HTTP_G(curlbuf).hdrs.used);
357 *data_len = HTTP_G(curlbuf).hdrs.used;
358 }
359
360 if ((member & CURLBUF_BODY) && HTTP_G(curlbuf).body.used) {
361 if (*data) {
362 memcpy((*data) + HTTP_G(curlbuf).hdrs.used,
363 HTTP_G(curlbuf).body.data, HTTP_G(curlbuf).body.used);
364 *data_len = HTTP_G(curlbuf).hdrs.used + HTTP_G(curlbuf).body.used;
365 } else {
366 emalloc(HTTP_G(curlbuf).body.used + 1);
367 memcpy(*data, HTTP_G(curlbuf).body.data, HTTP_G(curlbuf).body.used);
368 *data_len = HTTP_G(curlbuf).body.used;
369 }
370 }
371 if (*data) {
372 (*data)[*data_len] = 0;
373 } else {
374 *data = "";
375 }
376 }
377 /* }}} */
378
379 /* {{{ static inline void http_curl_movebuf(http_curlbuf_member, char **,
380 size_t *) */
381 static inline void _http_curl_movebuf(http_curlbuf_member member, char **data,
382 size_t *data_len TSRMLS_DC)
383 {
384 http_curl_copybuf(member, data, data_len);
385 http_curl_freebuf(member);
386 }
387 /* }}} */
388
389 /* {{{ static size_t http_curl_body_callback(char *, size_t, size_t, void *) */
390 static size_t http_curl_body_callback(char *buf, size_t len, size_t n, void *s)
391 {
392 TSRMLS_FETCH();
393
394 if ((len *= n) > HTTP_G(curlbuf).body.free) {
395 size_t bsize = HTTP_CURLBUF_BODYSIZE;
396 while (bsize < len) {
397 bsize *= 2;
398 }
399 HTTP_G(curlbuf).body.data = erealloc(HTTP_G(curlbuf).body.data,
400 HTTP_G(curlbuf).body.used + bsize);
401 HTTP_G(curlbuf).body.free += bsize;
402 }
403
404 memcpy(HTTP_G(curlbuf).body.data + HTTP_G(curlbuf).body.used, buf, len);
405 HTTP_G(curlbuf).body.free -= len;
406 HTTP_G(curlbuf).body.used += len;
407
408 return len;
409 }
410 /* }}} */
411
412 /* {{{ static size_t http_curl_hdrs_callback(char*, size_t, size_t, void *) */
413 static size_t http_curl_hdrs_callback(char *buf, size_t len, size_t n, void *s)
414 {
415 TSRMLS_FETCH();
416
417 /* discard previous headers */
418 if ((HTTP_G(curlbuf).hdrs.used) && (!strncmp(buf, "HTTP/1.", strlen("HTTP/1.")))) {
419 http_curl_initbuf(CURLBUF_HDRS);
420 }
421
422 if ((len *= n) > HTTP_G(curlbuf).hdrs.free) {
423 size_t bsize = HTTP_CURLBUF_HDRSSIZE;
424 while (bsize < len) {
425 bsize *= 2;
426 }
427 HTTP_G(curlbuf).hdrs.data = erealloc(HTTP_G(curlbuf).hdrs.data,
428 HTTP_G(curlbuf).hdrs.used + bsize);
429 HTTP_G(curlbuf).hdrs.free += bsize;
430 }
431
432 memcpy(HTTP_G(curlbuf).hdrs.data + HTTP_G(curlbuf).hdrs.used, buf, len);
433 HTTP_G(curlbuf).hdrs.free -= len;
434 HTTP_G(curlbuf).hdrs.used += len;
435
436 return len;
437 }
438 /* }}} */
439
440 /* {{{ static inline zval *http_curl_getopt(HashTable *, char *, int, ...) */
441 static inline zval *_http_curl_getopt(HashTable *options, char *key TSRMLS_DC, int checks, ...)
442 {
443 zval **zoption;
444 va_list types;
445 int i;
446
447 if (SUCCESS != zend_hash_find(options, key, strlen(key) + 1, (void **) &zoption)) {
448 return NULL;
449 }
450 if (checks < 1) {
451 return *zoption;
452 }
453
454 va_start(types, checks);
455 for (i = 0; i < checks; ++i) {
456 if ((va_arg(types, int)) == (Z_TYPE_PP(zoption))) {
457 va_end(types);
458 return *zoption;
459 }
460 }
461 va_end(types);
462 return NULL;
463 }
464 /* }}} */
465
466 /* {{{ static inline void http_curl_setopts(CURL *, char *, HashTable *) */
467 static inline void _http_curl_setopts(CURL *ch, const char *url, HashTable *options TSRMLS_DC)
468 {
469 zval *zoption;
470
471 /* standard options */
472 curl_easy_setopt(ch, CURLOPT_URL, url);
473 curl_easy_setopt(ch, CURLOPT_HEADER, 0);
474 curl_easy_setopt(ch, CURLOPT_NOPROGRESS, 1);
475 curl_easy_setopt(ch, CURLOPT_AUTOREFERER, 1);
476 curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, http_curl_body_callback);
477 curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, http_curl_hdrs_callback);
478 #ifdef ZTS
479 curl_easy_setopt(ch, CURLOPT_NOSIGNAL, 1);
480 #endif
481
482 if ((!options) || (1 > zend_hash_num_elements(options))) {
483 return;
484 }
485
486 /* redirects, defaults to 0 */
487 if (zoption = http_curl_getopt1(options, "redirect", IS_LONG)) {
488 curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, Z_LVAL_P(zoption) ? 1 : 0);
489 curl_easy_setopt(ch, CURLOPT_MAXREDIRS, Z_LVAL_P(zoption));
490 if (zoption = http_curl_getopt2(options, "unrestrictedauth", IS_LONG, IS_BOOL)) {
491 curl_easy_setopt(ch, CURLOPT_UNRESTRICTED_AUTH, Z_LVAL_P(zoption));
492 }
493 } else {
494 curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, 0);
495 }
496
497 /* proxy */
498 if (zoption = http_curl_getopt1(options, "proxyhost", IS_STRING)) {
499 curl_easy_setopt(ch, CURLOPT_PROXY, Z_STRVAL_P(zoption));
500 /* port */
501 if (zoption = http_curl_getopt1(options, "proxyport", IS_LONG)) {
502 curl_easy_setopt(ch, CURLOPT_PROXYPORT, Z_LVAL_P(zoption));
503 }
504 /* user:pass */
505 if (zoption = http_curl_getopt1(options, "proxyauth", IS_STRING)) {
506 curl_easy_setopt(ch, CURLOPT_PROXYUSERPWD, Z_STRVAL_P(zoption));
507 }
508 /* auth method */
509 if (zoption = http_curl_getopt1(options, "proxyauthtype", IS_LONG)) {
510 curl_easy_setopt(ch, CURLOPT_PROXYAUTH, Z_LVAL_P(zoption));
511 }
512 }
513
514 /* auth */
515 if (zoption = http_curl_getopt1(options, "httpauth", IS_STRING)) {
516 curl_easy_setopt(ch, CURLOPT_USERPWD, Z_STRVAL_P(zoption));
517 }
518 if (zoption = http_curl_getopt1(options, "httpauthtype", IS_LONG)) {
519 curl_easy_setopt(ch, CURLOPT_HTTPAUTH, Z_LVAL_P(zoption));
520 }
521
522 /* compress, enabled by default (empty string enables deflate and gzip) */
523 if (zoption = http_curl_getopt2(options, "compress", IS_LONG, IS_BOOL)) {
524 if (Z_LVAL_P(zoption)) {
525 curl_easy_setopt(ch, CURLOPT_ENCODING, "");
526 }
527 } else {
528 curl_easy_setopt(ch, CURLOPT_ENCODING, "");
529 }
530
531 /* another port */
532 if (zoption = http_curl_getopt1(options, "port", IS_LONG)) {
533 curl_easy_setopt(ch, CURLOPT_PORT, Z_LVAL_P(zoption));
534 }
535
536 /* referer */
537 if (zoption = http_curl_getopt1(options, "referer", IS_STRING)) {
538 curl_easy_setopt(ch, CURLOPT_REFERER, Z_STRVAL_P(zoption));
539 }
540
541 /* useragent, default "PECL::HTTP/version (PHP/version)" */
542 if (zoption = http_curl_getopt1(options, "useragent", IS_STRING)) {
543 curl_easy_setopt(ch, CURLOPT_USERAGENT, Z_STRVAL_P(zoption));
544 } else {
545 curl_easy_setopt(ch, CURLOPT_USERAGENT,
546 "PECL::HTTP/" PHP_EXT_HTTP_VERSION " (PHP/" PHP_VERSION ")");
547 }
548
549 /* cookies, array('name' => 'value') */
550 if (zoption = http_curl_getopt1(options, "cookies", IS_ARRAY)) {
551 char *cookie_key;
552 zval **cookie_val;
553 int key_type;
554 smart_str qstr = {0};
555
556 zend_hash_internal_pointer_reset(Z_ARRVAL_P(zoption));
557 while (HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_type(Z_ARRVAL_P(zoption)))) {
558 if (key_type == HASH_KEY_IS_STRING) {
559 zend_hash_get_current_key(Z_ARRVAL_P(zoption), &cookie_key, NULL, 0);
560 zend_hash_get_current_data(Z_ARRVAL_P(zoption), (void **) &cookie_val);
561 smart_str_appends(&qstr, cookie_key);
562 smart_str_appendl(&qstr, "=", 1);
563 smart_str_appendl(&qstr, Z_STRVAL_PP(cookie_val), Z_STRLEN_PP(cookie_val));
564 smart_str_appendl(&qstr, "; ", 2);
565 zend_hash_move_forward(Z_ARRVAL_P(zoption));
566 }
567 }
568 smart_str_0(&qstr);
569
570 if (qstr.c) {
571 curl_easy_setopt(ch, CURLOPT_COOKIE, qstr.c);
572 }
573 }
574
575 /* cookiestore */
576 if (zoption = http_curl_getopt1(options, "cookiestore", IS_STRING)) {
577 curl_easy_setopt(ch, CURLOPT_COOKIEFILE, Z_STRVAL_P(zoption));
578 curl_easy_setopt(ch, CURLOPT_COOKIEJAR, Z_STRVAL_P(zoption));
579 }
580
581 /* additional headers, array('name' => 'value') */
582 if (zoption = http_curl_getopt1(options, "headers", IS_ARRAY)) {
583 int key_type;
584 char *header_key, header[1024] = {0};
585 zval **header_val;
586 struct curl_slist *headers = NULL;
587
588 zend_hash_internal_pointer_reset(Z_ARRVAL_P(zoption));
589 while (HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_type(Z_ARRVAL_P(zoption)))) {
590 if (key_type == HASH_KEY_IS_STRING) {
591 zend_hash_get_current_key(Z_ARRVAL_P(zoption), &header_key, NULL, 0);
592 zend_hash_get_current_data(Z_ARRVAL_P(zoption), (void **) &header_val);
593 snprintf(header, 1024, "%s: %s", header_key, Z_STRVAL_PP(header_val));
594 headers = curl_slist_append(headers, header);
595 zend_hash_move_forward(Z_ARRVAL_P(zoption));
596 }
597 }
598 if (headers) {
599 curl_easy_setopt(ch, CURLOPT_HTTPHEADER, headers);
600 }
601 }
602 }
603 /* }}} */
604
605 /* {{{ static inline char *http_curl_getinfoname(CURLINFO) */
606 static inline char *_http_curl_getinfoname(CURLINFO i TSRMLS_DC)
607 {
608 #define CASE(I) case CURLINFO_ ##I : return #I
609 switch (i)
610 {
611 /* CURLINFO_EFFECTIVE_URL = CURLINFO_STRING +1, */
612 CASE(EFFECTIVE_URL);
613 /* CURLINFO_RESPONSE_CODE = CURLINFO_LONG +2, */
614 CASE(RESPONSE_CODE);
615 /* CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE +3, */
616 CASE(TOTAL_TIME);
617 /* CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE +4, */
618 CASE(NAMELOOKUP_TIME);
619 /* CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE +5, */
620 CASE(CONNECT_TIME);
621 /* CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE +6, */
622 CASE(PRETRANSFER_TIME);
623 /* CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE +7, */
624 CASE(SIZE_UPLOAD);
625 /* CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE +8, */
626 CASE(SIZE_DOWNLOAD);
627 /* CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE +9, */
628 CASE(SPEED_DOWNLOAD);
629 /* CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE +10, */
630 CASE(SPEED_UPLOAD);
631 /* CURLINFO_HEADER_SIZE = CURLINFO_LONG +11, */
632 CASE(HEADER_SIZE);
633 /* CURLINFO_REQUEST_SIZE = CURLINFO_LONG +12, */
634 CASE(REQUEST_SIZE);
635 /* CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG +13, */
636 CASE(SSL_VERIFYRESULT);
637 /* CURLINFO_FILETIME = CURLINFO_LONG +14, */
638 CASE(FILETIME);
639 /* CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE +15, */
640 CASE(CONTENT_LENGTH_DOWNLOAD);
641 /* CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE +16, */
642 CASE(CONTENT_LENGTH_UPLOAD);
643 /* CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE +17, */
644 CASE(STARTTRANSFER_TIME);
645 /* CURLINFO_CONTENT_TYPE = CURLINFO_STRING +18, */
646 CASE(CONTENT_TYPE);
647 /* CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE +19, */
648 CASE(REDIRECT_TIME);
649 /* CURLINFO_REDIRECT_COUNT = CURLINFO_LONG +20, */
650 CASE(REDIRECT_COUNT);
651 /* CURLINFO_PRIVATE = CURLINFO_STRING +21, */
652 CASE(PRIVATE);
653 /* CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG +22, */
654 CASE(HTTP_CONNECTCODE);
655 /* CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG +23, */
656 CASE(HTTPAUTH_AVAIL);
657 /* CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG +24, */
658 CASE(PROXYAUTH_AVAIL);
659 }
660 #undef CASE
661 return NULL;
662 }
663 /* }}} */
664
665 /* {{{ static inline void http_curl_getinfo_ex(CURL, CURLINFO, zval *) */
666 static inline void _http_curl_getinfo_ex(CURL *ch, CURLINFO i, zval *array TSRMLS_DC)
667 {
668 char *key;
669 if (key = http_curl_getinfoname(i)) {
670 switch (i & ~CURLINFO_MASK)
671 {
672 case CURLINFO_STRING:
673 {
674 char *c;
675 if (CURLE_OK == curl_easy_getinfo(ch, i, &c)) {
676 add_assoc_string(array, key, c ? c : "", 1);
677 }
678 }
679 break;
680
681 case CURLINFO_DOUBLE:
682 {
683 double d;
684 if (CURLE_OK == curl_easy_getinfo(ch, i, &d)) {
685 add_assoc_double(array, key, (double) d);
686 }
687 }
688 break;
689
690 case CURLINFO_LONG:
691 {
692 long l;
693 if (CURLE_OK == curl_easy_getinfo(ch, i, &l)) {
694 add_assoc_long(array, key, (long) l);
695 }
696 }
697 break;
698 }
699 }
700 }
701 /* }}} */
702
703 /* {{{ static inline http_curl_getinfo(CURL, HashTable *) */
704 static inline void _http_curl_getinfo(CURL *ch, HashTable *info TSRMLS_DC)
705 {
706 zval *array;
707
708 MAKE_STD_ZVAL(array);
709 Z_ARRVAL_P(array) = info;
710
711 #define INFO(I) http_curl_getinfo_ex(ch, CURLINFO_ ##I , array)
712 /* CURLINFO_EFFECTIVE_URL = CURLINFO_STRING +1, */
713 INFO(EFFECTIVE_URL);
714 /* CURLINFO_RESPONSE_CODE = CURLINFO_LONG +2, */
715 INFO(RESPONSE_CODE);
716 /* CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE +3, */
717 INFO(TOTAL_TIME);
718 /* CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE +4, */
719 INFO(NAMELOOKUP_TIME);
720 /* CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE +5, */
721 INFO(CONNECT_TIME);
722 /* CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE +6, */
723 INFO(PRETRANSFER_TIME);
724 /* CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE +7, */
725 INFO(SIZE_UPLOAD);
726 /* CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE +8, */
727 INFO(SIZE_DOWNLOAD);
728 /* CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE +9, */
729 INFO(SPEED_DOWNLOAD);
730 /* CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE +10, */
731 INFO(SPEED_UPLOAD);
732 /* CURLINFO_HEADER_SIZE = CURLINFO_LONG +11, */
733 INFO(HEADER_SIZE);
734 /* CURLINFO_REQUEST_SIZE = CURLINFO_LONG +12, */
735 INFO(REQUEST_SIZE);
736 /* CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG +13, */
737 INFO(SSL_VERIFYRESULT);
738 /* CURLINFO_FILETIME = CURLINFO_LONG +14, */
739 INFO(FILETIME);
740 /* CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE +15, */
741 INFO(CONTENT_LENGTH_DOWNLOAD);
742 /* CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE +16, */
743 INFO(CONTENT_LENGTH_UPLOAD);
744 /* CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE +17, */
745 INFO(STARTTRANSFER_TIME);
746 /* CURLINFO_CONTENT_TYPE = CURLINFO_STRING +18, */
747 INFO(CONTENT_TYPE);
748 /* CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE +19, */
749 INFO(REDIRECT_TIME);
750 /* CURLINFO_REDIRECT_COUNT = CURLINFO_LONG +20, */
751 INFO(REDIRECT_COUNT);
752 /* CURLINFO_PRIVATE = CURLINFO_STRING +21, */
753 INFO(PRIVATE);
754 /* CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG +22, */
755 INFO(HTTP_CONNECTCODE);
756 /* CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG +23, */
757 INFO(HTTPAUTH_AVAIL);
758 /* CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG +24, */
759 INFO(PROXYAUTH_AVAIL);
760 #undef INFO
761 efree(array);
762 }
763 /* }}} */
764
765 /* {{{ Day/Month/TZ checks for http_parse_date()
766 Originally by libcurl, Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. */
767 static int check_day(char *day, size_t len)
768 {
769 int i;
770 const char * const *check = (len > 3) ? &weekdays[0] : &wkdays[0];
771 for (i = 0; i < 7; i++) {
772 if (!strcmp(day, check[0])) {
773 return i;
774 }
775 check++;
776 }
777 return -1;
778 }
779
780 static int check_month(char *month)
781 {
782 int i;
783 const char * const *check = &months[0];
784 for (i = 0; i < 12; i++) {
785 if (!strcmp(month, check[0])) {
786 return i;
787 }
788 check++;
789 }
790 return -1;
791 }
792
793 /* return the time zone offset between GMT and the input one, in number
794 of seconds or -1 if the timezone wasn't found/legal */
795
796 static int check_tzone(char *tzone)
797 {
798 int i;
799 const struct time_zone *check = time_zones;
800 for (i = 0; i < sizeof(time_zones) / sizeof(time_zones[0]); i++) {
801 if (!strcmp(tzone, check->name)) {
802 return check->offset * 60;
803 }
804 check++;
805 }
806 return -1;
807 }
808 /* }}} */
809
810 #endif
811 /* }}} HAVE_CURL */
812
813 /* }}} internals */
814
815 /* {{{ public API */
816
817 /* {{{ char *http_date(time_t) */
818 PHP_HTTP_API char *_http_date(time_t t TSRMLS_DC)
819 {
820 struct tm *gmtime, tmbuf;
821 char *date = ecalloc(1, 30);
822
823 gmtime = php_gmtime_r(&t, &tmbuf);
824 snprintf(date, 30,
825 "%s, %02d %s %04d %02d:%02d:%02d GMT",
826 days[gmtime->tm_wday], gmtime->tm_mday,
827 months[gmtime->tm_mon], gmtime->tm_year + 1900,
828 gmtime->tm_hour, gmtime->tm_min, gmtime->tm_sec
829 );
830 return date;
831 }
832 /* }}} */
833
834 /* {{{ time_t http_parse_date(char *)
835 Originally by libcurl, Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. */
836 PHP_HTTP_API time_t _http_parse_date(const char *date)
837 {
838 time_t t = 0;
839 int tz_offset = -1, year = -1, month = -1, monthday = -1, weekday = -1,
840 hours = -1, minutes = -1, seconds = -1;
841 struct tm tm;
842 enum assume_next dignext = DATE_MDAY;
843 const char *indate = date;
844
845 int found = 0, part = 0; /* max 6 parts */
846
847 while (*date && (part < 6)) {
848 int found = 0;
849
850 while (*date && !isalnum(*date)) {
851 date++;
852 }
853
854 if (isalpha(*date)) {
855 /* a name coming up */
856 char buf[32] = "";
857 size_t len;
858 sscanf(date, "%31[A-Za-z]", buf);
859 len = strlen(buf);
860
861 if (weekday == -1) {
862 weekday = check_day(buf, len);
863 if (weekday != -1) {
864 found = 1;
865 }
866 }
867
868 if (!found && (month == -1)) {
869 month = check_month(buf);
870 if (month != -1) {
871 found = 1;
872 }
873 }
874
875 if (!found && (tz_offset == -1)) {
876 /* this just must be a time zone string */
877 tz_offset = check_tzone(buf);
878 if (tz_offset != -1) {
879 found = 1;
880 }
881 }
882
883 if (!found) {
884 return -1; /* bad string */
885 }
886 date += len;
887 }
888 else if (isdigit(*date)) {
889 /* a digit */
890 int val;
891 char *end;
892 if((seconds == -1) && (3 == sscanf(date, "%02d:%02d:%02d", &hours, &minutes, &seconds))) {
893 /* time stamp! */
894 date += 8;
895 found = 1;
896 }
897 else {
898 val = (int) strtol(date, &end, 10);
899
900 if ((tz_offset == -1) && ((end - date) == 4) && (val < 1300) && (indate < date) && ((date[-1] == '+' || date[-1] == '-'))) {
901 /* four digits and a value less than 1300 and it is preceeded with
902 a plus or minus. This is a time zone indication. */
903 found = 1;
904 tz_offset = (val / 100 * 60 + val % 100) * 60;
905
906 /* the + and - prefix indicates the local time compared to GMT,
907 this we need ther reversed math to get what we want */
908 tz_offset = date[-1] == '+' ? -tz_offset : tz_offset;
909 }
910
911 if (((end - date) == 8) && (year == -1) && (month == -1) && (monthday == -1)) {
912 /* 8 digits, no year, month or day yet. This is YYYYMMDD */
913 found = 1;
914 year = val / 10000;
915 month = (val % 10000) / 100 - 1; /* month is 0 - 11 */
916 monthday = val % 100;
917 }
918
919 if (!found && (dignext == DATE_MDAY) && (monthday == -1)) {
920 if ((val > 0) && (val < 32)) {
921 monthday = val;
922 found = 1;
923 }
924 dignext = DATE_YEAR;
925 }
926
927 if (!found && (dignext == DATE_YEAR) && (year == -1)) {
928 year = val;
929 found = 1;
930 if (year < 1900) {
931 year += year > 70 ? 1900 : 2000;
932 }
933 if(monthday == -1) {
934 dignext = DATE_MDAY;
935 }
936 }
937
938 if (!found) {
939 return -1;
940 }
941
942 date = end;
943 }
944 }
945
946 part++;
947 }
948
949 if (-1 == seconds) {
950 seconds = minutes = hours = 0; /* no time, make it zero */
951 }
952
953 if ((-1 == monthday) || (-1 == month) || (-1 == year)) {
954 /* lacks vital info, fail */
955 return -1;
956 }
957
958 if (sizeof(time_t) < 5) {
959 /* 32 bit time_t can only hold dates to the beginning of 2038 */
960 if (year > 2037) {
961 return 0x7fffffff;
962 }
963 }
964
965 tm.tm_sec = seconds;
966 tm.tm_min = minutes;
967 tm.tm_hour = hours;
968 tm.tm_mday = monthday;
969 tm.tm_mon = month;
970 tm.tm_year = year - 1900;
971 tm.tm_wday = 0;
972 tm.tm_yday = 0;
973 tm.tm_isdst = 0;
974
975 t = mktime(&tm);
976
977 /* time zone adjust */
978 {
979 struct tm *gmt, keeptime2;
980 long delta;
981 time_t t2;
982
983 if(!(gmt = php_gmtime_r(&t, &keeptime2))) {
984 return -1; /* illegal date/time */
985 }
986
987 t2 = mktime(gmt);
988
989 /* Add the time zone diff (between the given timezone and GMT) and the
990 diff between the local time zone and GMT. */
991 delta = (tz_offset != -1 ? tz_offset : 0) + (t - t2);
992
993 if((delta > 0) && (t + delta < t)) {
994 return -1; /* time_t overflow */
995 }
996
997 t += delta;
998 }
999
1000 return t;
1001 }
1002 /* }}} */
1003
1004 /* {{{ inline STATUS http_send_status(int) */
1005 PHP_HTTP_API inline STATUS _http_send_status(const int status TSRMLS_DC)
1006 {
1007 int s = status;
1008 return sapi_header_op(SAPI_HEADER_SET_STATUS, (void *) s TSRMLS_CC);
1009 }
1010 /* }}} */
1011
1012 /* {{{ inline STATUS http_send_header(char *) */
1013 PHP_HTTP_API inline STATUS _http_send_header(const char *header TSRMLS_DC)
1014 {
1015 return http_send_status_header(0, header);
1016 }
1017 /* }}} */
1018
1019 /* {{{ inline STATUS http_send_status_header(int, char *) */
1020 PHP_HTTP_API inline STATUS _http_send_status_header(const int status, const char *header TSRMLS_DC)
1021 {
1022 sapi_header_line h = {(char *) header, strlen(header), status};
1023 return sapi_header_op(SAPI_HEADER_REPLACE, &h TSRMLS_CC);
1024 }
1025 /* }}} */
1026
1027 /* {{{ inline zval *http_get_server_var(char *) */
1028 PHP_HTTP_API inline zval *_http_get_server_var(const char *key TSRMLS_DC)
1029 {
1030 zval **var;
1031 if (SUCCESS == zend_hash_find(
1032 HTTP_SERVER_VARS,
1033 (char *) key, strlen(key) + 1, (void **) &var)) {
1034 return *var;
1035 }
1036 return NULL;
1037 }
1038 /* }}} */
1039
1040 /* {{{ void http_ob_etaghandler(char *, uint, char **, uint *, int) */
1041 PHP_HTTP_API void _http_ob_etaghandler(char *output, uint output_len,
1042 char **handled_output, uint *handled_output_len, int mode TSRMLS_DC)
1043 {
1044 char etag[33] = { 0 };
1045 unsigned char digest[16];
1046
1047 if (mode & PHP_OUTPUT_HANDLER_START) {
1048 PHP_MD5Init(&HTTP_G(etag_md5));
1049 }
1050
1051 PHP_MD5Update(&HTTP_G(etag_md5), output, output_len);
1052
1053 if (mode & PHP_OUTPUT_HANDLER_END) {
1054 PHP_MD5Final(digest, &HTTP_G(etag_md5));
1055
1056 /* just do that if desired */
1057 if (HTTP_G(etag_started)) {
1058 make_digest(etag, digest);
1059
1060 if (http_etag_match("HTTP_IF_NONE_MATCH", etag)) {
1061 http_send_status(304);
1062 } else {
1063 http_send_etag(etag, 32);
1064 }
1065 }
1066 }
1067
1068 *handled_output_len = output_len;
1069 *handled_output = estrndup(output, output_len);
1070 }
1071 /* }}} */
1072
1073 /* {{{ int http_modified_match(char *, int) */
1074 PHP_HTTP_API int _http_modified_match(const char *entry, const time_t t TSRMLS_DC)
1075 {
1076 int retval;
1077 zval *zmodified;
1078 char *modified, *chr_ptr;
1079
1080 HTTP_GSC(zmodified, entry, 0);
1081
1082 modified = estrndup(Z_STRVAL_P(zmodified), Z_STRLEN_P(zmodified));
1083 if (chr_ptr = strrchr(modified, ';')) {
1084 chr_ptr = 0;
1085 }
1086 retval = (t <= http_parse_date(modified));
1087 efree(modified);
1088 return retval;
1089 }
1090 /* }}} */
1091
1092 /* {{{ int http_etag_match(char *, char *) */
1093 PHP_HTTP_API int _http_etag_match(const char *entry, const char *etag TSRMLS_DC)
1094 {
1095 zval *zetag;
1096 char *quoted_etag;
1097 STATUS result;
1098
1099 HTTP_GSC(zetag, entry, 0);
1100
1101 if (NULL != strchr(Z_STRVAL_P(zetag), '*')) {
1102 return 1;
1103 }
1104
1105 quoted_etag = (char *) emalloc(strlen(etag) + 3);
1106 sprintf(quoted_etag, "\"%s\"", etag);
1107
1108 if (!strchr(Z_STRVAL_P(zetag), ',')) {
1109 result = !strcmp(Z_STRVAL_P(zetag), quoted_etag);
1110 } else {
1111 result = (NULL != strstr(Z_STRVAL_P(zetag), quoted_etag));
1112 }
1113 efree(quoted_etag);
1114 return result;
1115 }
1116 /* }}} */
1117
1118 /* {{{ STATUS http_send_last_modified(int) */
1119 PHP_HTTP_API STATUS _http_send_last_modified(const time_t t TSRMLS_DC)
1120 {
1121 char modified[96] = "Last-Modified: ", *date;
1122 date = http_date(t);
1123 strcat(modified, date);
1124 efree(date);
1125
1126 /* remember */
1127 HTTP_G(lmod) = t;
1128
1129 return http_send_header(modified);
1130 }
1131 /* }}} */
1132
1133 /* {{{ static STATUS http_send_etag(char *, int) */
1134 PHP_HTTP_API STATUS _http_send_etag(const char *etag,
1135 const int etag_len TSRMLS_DC)
1136 {
1137 STATUS ret;
1138 int header_len;
1139 char *etag_header;
1140
1141 header_len = strlen("ETag: \"\"") + etag_len + 1;
1142 etag_header = (char *) emalloc(header_len);
1143 snprintf(etag_header, header_len, "ETag: \"%s\"", etag);
1144 ret = http_send_header(etag_header);
1145 efree(etag_header);
1146
1147 /* remember */
1148 if (HTTP_G(etag)) {
1149 efree(HTTP_G(etag));
1150 }
1151 HTTP_G(etag) = estrdup(etag);
1152
1153 return ret;
1154 }
1155 /* }}} */
1156
1157 /* {{{ char *http_absolute_uri(char *, char *) */
1158 PHP_HTTP_API char *_http_absolute_uri(const char *url,
1159 const char *proto TSRMLS_DC)
1160 {
1161 char URI[HTTP_URI_MAXLEN + 1], *PTR, *proto_ptr, *host, *path;
1162 zval *zhost;
1163
1164 if (!url || !strlen(url)) {
1165 if (!SG(request_info).request_uri) {
1166 return NULL;
1167 }
1168 url = SG(request_info).request_uri;
1169 }
1170 /* Mess around with already absolute URIs */
1171 else if (proto_ptr = strstr(url, "://")) {
1172 if (!proto || !strncmp(url, proto, strlen(proto))) {
1173 return estrdup(url);
1174 } else {
1175 snprintf(URI, HTTP_URI_MAXLEN, "%s%s", proto, proto_ptr + 3);
1176 return estrdup(URI);
1177 }
1178 }
1179
1180 /* protocol defaults to http */
1181 if (!proto || !strlen(proto)) {
1182 proto = "http";
1183 }
1184
1185 /* get host name */
1186 if ( (zhost = http_get_server_var("HTTP_HOST")) ||
1187 (zhost = http_get_server_var("SERVER_NAME"))) {
1188 host = Z_STRVAL_P(zhost);
1189 } else {
1190 host = "localhost";
1191 }
1192
1193
1194 /* glue together */
1195 if (url[0] == '/') {
1196 snprintf(URI, HTTP_URI_MAXLEN, "%s://%s%s", proto, host, url);
1197 } else if (SG(request_info).request_uri) {
1198 path = estrdup(SG(request_info).request_uri);
1199 php_dirname(path, strlen(path));
1200 snprintf(URI, HTTP_URI_MAXLEN, "%s://%s%s/%s", proto, host, path, url);
1201 efree(path);
1202 } else {
1203 snprintf(URI, HTTP_URI_MAXLEN, "%s://%s/%s", proto, host, url);
1204 }
1205
1206 /* strip everything after a new line */
1207 PTR = URI;
1208 while (*PTR != 0) {
1209 if (*PTR == '\n' || *PTR == '\r') {
1210 *PTR = 0;
1211 break;
1212 }
1213 PTR++;
1214 }
1215
1216 return estrdup(URI);
1217 }
1218 /* }}} */
1219
1220 /* {{{ char *http_negotiate_q(char *, zval *, char *, hash_entry_type) */
1221 PHP_HTTP_API char *_http_negotiate_q(const char *entry, const zval *supported,
1222 const char *def TSRMLS_DC)
1223 {
1224 zval *zaccept, *zarray, *zdelim, **zentry, *zentries, **zsupp;
1225 char *q_ptr, *result;
1226 int i, c;
1227 double qual;
1228
1229 HTTP_GSC(zaccept, entry, estrdup(def));
1230
1231 MAKE_STD_ZVAL(zarray);
1232 array_init(zarray);
1233
1234 MAKE_STD_ZVAL(zdelim);
1235 ZVAL_STRING(zdelim, ",", 0);
1236 php_explode(zdelim, zaccept, zarray, -1);
1237 efree(zdelim);
1238
1239 MAKE_STD_ZVAL(zentries);
1240 array_init(zentries);
1241
1242 c = zend_hash_num_elements(Z_ARRVAL_P(zarray));
1243 for (i = 0; i < c; i++, zend_hash_move_forward(Z_ARRVAL_P(zarray))) {
1244
1245 if (SUCCESS != zend_hash_get_current_data(
1246 Z_ARRVAL_P(zarray), (void **) &zentry)) {
1247 php_error_docref(NULL TSRMLS_CC, E_WARNING,
1248 "Cannot parse %s header: %s", entry, Z_STRVAL_P(zaccept));
1249 break;
1250 }
1251
1252 /* check for qualifier */
1253 if (NULL != (q_ptr = strrchr(Z_STRVAL_PP(zentry), ';'))) {
1254 qual = strtod(q_ptr + 3, NULL);
1255 } else {
1256 qual = 1000.0 - i;
1257 }
1258
1259 /* walk through the supported array */
1260 for ( zend_hash_internal_pointer_reset(Z_ARRVAL_P(supported));
1261 SUCCESS == zend_hash_get_current_data(
1262 Z_ARRVAL_P(supported), (void **) &zsupp);
1263 zend_hash_move_forward(Z_ARRVAL_P(supported))) {
1264 if (!strcasecmp(Z_STRVAL_PP(zsupp), Z_STRVAL_PP(zentry))) {
1265 add_assoc_double(zentries, Z_STRVAL_PP(zsupp), qual);
1266 break;
1267 }
1268 }
1269 }
1270
1271 zval_dtor(zarray);
1272 efree(zarray);
1273
1274 zend_hash_internal_pointer_reset(Z_ARRVAL_P(zentries));
1275
1276 if ( (SUCCESS != zend_hash_sort(Z_ARRVAL_P(zentries), zend_qsort,
1277 http_sort_q, 0 TSRMLS_CC)) ||
1278 (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key(
1279 Z_ARRVAL_P(zentries), &result, 0, 1))) {
1280 result = estrdup(def);
1281 }
1282
1283 zval_dtor(zentries);
1284 efree(zentries);
1285
1286 return result;
1287 }
1288 /* }}} */
1289
1290 /* {{{ http_range_status http_get_request_ranges(zval *zranges, size_t) */
1291 PHP_HTTP_API http_range_status _http_get_request_ranges(zval *zranges,
1292 const size_t length TSRMLS_DC)
1293 {
1294 zval *zrange;
1295 char *range, c;
1296 long begin = -1, end = -1, *ptr;
1297
1298 HTTP_GSC(zrange, "HTTP_RANGE", RANGE_NO);
1299 range = Z_STRVAL_P(zrange);
1300
1301 if (strncmp(range, "bytes=", strlen("bytes="))) {
1302 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Range header misses bytes=");
1303 return RANGE_ERR;
1304 }
1305
1306 ptr = &begin;
1307 range += strlen("bytes=");
1308
1309 do {
1310 switch (c = *(range++))
1311 {
1312 case '0':
1313 *ptr *= 10;
1314 break;
1315
1316 case '1': case '2': case '3':
1317 case '4': case '5': case '6':
1318 case '7': case '8': case '9':
1319 /*
1320 * If the value of the pointer is already set (non-negative)
1321 * then multiply its value by ten and add the current value,
1322 * else initialise the pointers value with the current value
1323 * --
1324 * This let us recognize empty fields when validating the
1325 * ranges, i.e. a "-10" for begin and "12345" for the end
1326 * was the following range request: "Range: bytes=0-12345";
1327 * While a "-1" for begin and "12345" for the end would
1328 * have been: "Range: bytes=-12345".
1329 */
1330 if (*ptr > 0) {
1331 *ptr *= 10;
1332 *ptr += c - '0';
1333 } else {
1334 *ptr = c - '0';
1335 }
1336 break;
1337
1338 case '-':
1339 ptr = &end;
1340 break;
1341
1342 case ' ':
1343 /* IE - ignore for now */
1344 break;
1345
1346 case 0:
1347 case ',':
1348
1349 if (length) {
1350 /* validate ranges */
1351 switch (begin)
1352 {
1353 /* "0-12345" */
1354 case -10:
1355 if ((length - end) < 1) {
1356 return RANGE_ERR;
1357 }
1358 begin = 0;
1359 break;
1360
1361 /* "-12345" */
1362 case -1:
1363 if ((length - end) < 1) {
1364 return RANGE_ERR;
1365 }
1366 begin = length - end;
1367 end = length;
1368 break;
1369
1370 /* "12345-(xxx)" */
1371 default:
1372 switch (end)
1373 {
1374 /* "12345-" */
1375 case -1:
1376 if ((length - begin) < 1) {
1377 return RANGE_ERR;
1378 }
1379 end = length - 1;
1380 break;
1381
1382 /* "12345-67890" */
1383 default:
1384 if ( ((length - begin) < 1) ||
1385 ((length - end) < 1) ||
1386 ((begin - end) >= 0)) {
1387 return RANGE_ERR;
1388 }
1389 break;
1390 }
1391 break;
1392 }
1393 }
1394 {
1395 zval *zentry;
1396 MAKE_STD_ZVAL(zentry);
1397 array_init(zentry);
1398 add_index_long(zentry, 0, begin);
1399 add_index_long(zentry, 1, end);
1400 add_next_index_zval(zranges, zentry);
1401
1402 begin = -1;
1403 end = -1;
1404 ptr = &begin;
1405 }
1406 break;
1407
1408 default:
1409 return RANGE_ERR;
1410 break;
1411 }
1412 } while (c != 0);
1413
1414 return RANGE_OK;
1415 }
1416 /* }}} */
1417
1418 /* {{{ STATUS http_send_ranges(zval *, void *, size_t, http_send_mode) */
1419 PHP_HTTP_API STATUS _http_send_ranges(zval *zranges, const void *data, const size_t size, const http_send_mode mode TSRMLS_DC)
1420 {
1421 zval **zrange;
1422 long **begin, **end;
1423 char range_header[255], multi_header[68] = "Content-Type: multipart/byteranges; boundary=", bound[23], preface[1024];
1424 int i, c;
1425
1426 /* Send HTTP 206 Partial Content */
1427 http_send_status(206);
1428
1429 /* single range */
1430 if ((c = zend_hash_num_elements(Z_ARRVAL_P(zranges))) == 1) {
1431 zend_hash_index_find(Z_ARRVAL_P(zranges), 0, (void **) &zrange);
1432 zend_hash_index_find(Z_ARRVAL_PP(zrange), 0, (void **) &begin);
1433 zend_hash_index_find(Z_ARRVAL_PP(zrange), 1, (void **) &end);
1434
1435 /* send content range header */
1436 snprintf(range_header, 255, "Content-Range: bytes %d-%d/%d", **begin, **end, size);
1437 http_send_header(range_header);
1438
1439 /* send requested chunk */
1440 return http_send_chunk(data, **begin, **end + 1, mode);
1441 }
1442
1443 /* multi range */
1444
1445 snprintf(bound, 23, "--%d%0.9f", time(NULL), php_combined_lcg(TSRMLS_C));
1446 strncat(multi_header, bound + 2, 21);
1447 http_send_header(multi_header);
1448
1449 /* send each requested chunk */
1450 for ( i = 0, zend_hash_internal_pointer_reset(Z_ARRVAL_P(zranges));
1451 i < c;
1452 i++, zend_hash_move_forward(Z_ARRVAL_P(zranges))) {
1453 if ( HASH_KEY_NON_EXISTANT == zend_hash_get_current_data(
1454 Z_ARRVAL_P(zranges), (void **) &zrange) ||
1455 SUCCESS != zend_hash_index_find(
1456 Z_ARRVAL_PP(zrange), 0, (void **) &begin) ||
1457 SUCCESS != zend_hash_index_find(
1458 Z_ARRVAL_PP(zrange), 1, (void **) &end)) {
1459 break;
1460 }
1461
1462 snprintf(preface, 1024,
1463 "\r\n%s\r\nContent-Type: %s\r\nContent-Range: bytes %d-%d/%d\r\n\r\n", bound,
1464 HTTP_G(ctype) ? HTTP_G(ctype) : "application/x-octetstream",
1465 **begin, **end, size);
1466 php_body_write(preface, strlen(preface) TSRMLS_CC);
1467 http_send_chunk(data, **begin, **end + 1, mode);
1468 }
1469
1470 /* write boundary once more */
1471 php_body_write("\r\n", 1 TSRMLS_CC);
1472 php_body_write(bound, strlen(bound) TSRMLS_CC);
1473
1474 return SUCCESS;
1475 }
1476 /* }}} */
1477
1478 /* {{{ STATUS http_send(void *, sizezo_t, http_send_mode) */
1479 PHP_HTTP_API STATUS _http_send(const void *data_ptr, const size_t data_size,
1480 const http_send_mode data_mode TSRMLS_DC)
1481 {
1482 char *new_etag = NULL;
1483 int is_range_request = http_is_range_request();
1484
1485 if (!data_ptr) {
1486 return FAILURE;
1487 }
1488
1489 /* etag handling */
1490 if (HTTP_G(etag_started)) {
1491 new_etag = (char *) emalloc(33);
1492
1493 /* never ever use the output to compute the ETag if http_send() is used */
1494 HTTP_G(etag_started) = 0;
1495 php_end_ob_buffer(0, 0 TSRMLS_CC);
1496 if (NULL == http_etag(&new_etag, data_ptr, data_size, data_mode)) {
1497 efree(new_etag);
1498 return FAILURE;
1499 }
1500
1501 /* send 304 Not Modified if etag matches */
1502 if ((!is_range_request) && http_etag_match("HTTP_IF_NONE_MATCH", new_etag)) {
1503 efree(new_etag);
1504 return http_send_status(304);
1505 }
1506
1507 http_send_etag(new_etag, 32);
1508 efree(new_etag);
1509 }
1510
1511 /* send 304 Not Modified if last-modified matches*/
1512 if ((!is_range_request) && http_modified_match("HTTP_IF_MODIFIED_SINCE", HTTP_G(lmod))) {
1513 return http_send_status(304);
1514 }
1515
1516 if (is_range_request) {
1517
1518 /* only send ranges if entity hasn't changed */
1519 if (
1520 ((!zend_hash_exists(HTTP_SERVER_VARS, "HTTP_IF_MATCH", 13)) ||
1521 http_etag_match("HTTP_IF_MATCH", HTTP_G(etag)))
1522 &&
1523 ((!zend_hash_exists(HTTP_SERVER_VARS, "HTTP_IF_UNMODIFIED_SINCE", 25)) ||
1524 http_modified_match("HTTP_IF_UNMODIFIED_SINCE", HTTP_G(lmod)))
1525 ) {
1526
1527 STATUS result = FAILURE;
1528 zval *zranges = NULL;
1529 MAKE_STD_ZVAL(zranges);
1530 array_init(zranges);
1531
1532 switch (http_get_request_ranges(zranges, data_size))
1533 {
1534 case RANGE_NO:
1535 zval_dtor(zranges);
1536 efree(zranges);
1537 /* go ahead and send all */
1538 break;
1539
1540 case RANGE_OK:
1541 result = http_send_ranges(zranges, data_ptr, data_size, data_mode);
1542 zval_dtor(zranges);
1543 efree(zranges);
1544 return result;
1545 break;
1546
1547 case RANGE_ERR:
1548 zval_dtor(zranges);
1549 efree(zranges);
1550 http_send_status(416);
1551 return FAILURE;
1552 break;
1553
1554 default:
1555 return FAILURE;
1556 break;
1557 }
1558 }
1559 }
1560 /* send all */
1561 return http_send_chunk(data_ptr, 0, data_size, data_mode);
1562 }
1563 /* }}} */
1564
1565 /* {{{ STATUS http_send_data(zval *) */
1566 PHP_HTTP_API STATUS _http_send_data(const zval *zdata TSRMLS_DC)
1567 {
1568 if (!Z_STRLEN_P(zdata)) {
1569 return SUCCESS;
1570 }
1571 if (!Z_STRVAL_P(zdata)) {
1572 return FAILURE;
1573 }
1574
1575 return http_send(zdata, Z_STRLEN_P(zdata), SEND_DATA);
1576 }
1577 /* }}} */
1578
1579 /* {{{ STATUS http_send_stream(php_stream *) */
1580 PHP_HTTP_API STATUS _http_send_stream(const php_stream *file TSRMLS_DC)
1581 {
1582 if (php_stream_stat((php_stream *) file, &HTTP_G(ssb))) {
1583 return FAILURE;
1584 }
1585
1586 return http_send(file, HTTP_G(ssb).sb.st_size, SEND_RSRC);
1587 }
1588 /* }}} */
1589
1590 /* {{{ STATUS http_send_file(zval *) */
1591 PHP_HTTP_API STATUS _http_send_file(const zval *zfile TSRMLS_DC)
1592 {
1593 php_stream *file;
1594 STATUS ret;
1595
1596 if (!Z_STRLEN_P(zfile)) {
1597 return FAILURE;
1598 }
1599
1600 if (!(file = php_stream_open_wrapper(Z_STRVAL_P(zfile), "rb",
1601 REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL))) {
1602 return FAILURE;
1603 }
1604
1605 ret = http_send_stream(file);
1606 php_stream_close(file);
1607 return ret;
1608 }
1609 /* }}} */
1610
1611 /* {{{ proto STATUS http_chunked_decode(char *, size_t, char **, size_t *) */
1612 PHP_HTTP_API STATUS _http_chunked_decode(const char *encoded,
1613 const size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC)
1614 {
1615 const char *e_ptr;
1616 char *d_ptr;
1617
1618 *decoded_len = 0;
1619 *decoded = (char *) ecalloc(1, encoded_len);
1620 d_ptr = *decoded;
1621 e_ptr = encoded;
1622
1623 while (((e_ptr - encoded) - encoded_len) > 0) {
1624 char hex_len[9] = {0};
1625 size_t chunk_len = 0;
1626 int i = 0;
1627
1628 /* read in chunk size */
1629 while (isxdigit(*e_ptr)) {
1630 if (i == 9) {
1631 php_error_docref(NULL TSRMLS_CC, E_WARNING,
1632 "Chunk size is too long: 0x%s...", hex_len);
1633 efree(*decoded);
1634 return FAILURE;
1635 }
1636 hex_len[i++] = *e_ptr++;
1637 }
1638
1639 /* hex to long */
1640 if (strcmp(hex_len, "0")) {
1641 char *error = NULL;
1642 chunk_len = strtol(hex_len, &error, 16);
1643 if (error == hex_len) {
1644 php_error_docref(NULL TSRMLS_CC, E_WARNING,
1645 "Invalid chunk size string: '%s'", hex_len);
1646 efree(*decoded);
1647 return FAILURE;
1648 }
1649 } else {
1650 break;
1651 }
1652
1653 /* new line */
1654 if (*e_ptr++ != '\r' || *e_ptr++ != '\n') {
1655 php_error_docref(NULL TSRMLS_CC, E_WARNING,
1656 "Invalid character (expected 0x0A, 0x0D; got: %x)",
1657 *(e_ptr - 1));
1658 efree(*decoded);
1659 return FAILURE;
1660 }
1661
1662 memcpy(d_ptr, e_ptr, chunk_len);
1663 d_ptr += chunk_len;
1664 e_ptr += chunk_len + 2;
1665 *decoded_len += chunk_len;
1666 }
1667
1668 return SUCCESS;
1669 }
1670 /* }}} */
1671
1672 /* {{{ proto void http_split_response(zval *, zval *, zval *) */
1673 PHP_HTTP_API void _http_split_response(const zval *zresponse, zval *zheaders,
1674 zval *zbody TSRMLS_DC)
1675 {
1676 char *header, *response, *body = NULL;
1677 long response_len = Z_STRLEN_P(zresponse);
1678 header = response = Z_STRVAL_P(zresponse);
1679
1680 while ((response - Z_STRVAL_P(zresponse) + 3) < response_len) {
1681 if ( (*response++ == '\r') &&
1682 (*response++ == '\n') &&
1683 (*response++ == '\r') &&
1684 (*response++ == '\n')) {
1685 body = response;
1686 break;
1687 }
1688 }
1689
1690 if (body && (response_len - (body - header))) {
1691 ZVAL_STRINGL(zbody, body, response_len - (body - header) - 1, 1);
1692 } else {
1693 Z_TYPE_P(zbody) = IS_NULL;
1694 }
1695
1696 /* check for HTTP status - FIXXME: strchr() */
1697 if (!strncmp(header, "HTTP/1.", 7)) {
1698 char *end = strchr(header, '\r');
1699 add_assoc_stringl(zheaders, "Status",
1700 header + strlen("HTTP/1.x "),
1701 end - (header + strlen("HTTP/1.x ")), 1);
1702 header = end + 2;
1703 }
1704 /* split headers */
1705 {
1706 char *colon = NULL, *line = header;
1707
1708 while ( (line - Z_STRVAL_P(zresponse) + 3) <
1709 (body - Z_STRVAL_P(zresponse))) {
1710 switch (*line++)
1711 {
1712 case '\r':
1713 if (colon && (*line == '\n')) {
1714 char *key = estrndup(header, colon - header);
1715 add_assoc_stringl(zheaders, key,
1716 colon + 2, line - colon - 3, 1);
1717 efree(key);
1718
1719 colon = NULL;
1720 header += line - header + 1;
1721 }
1722 break;
1723
1724 case ':':
1725 if (!colon) {
1726 colon = line - 1;
1727 }
1728 break;
1729 }
1730 }
1731 }
1732 }
1733 /* }}} */
1734
1735 /* {{{ HAVE_CURL */
1736 #ifdef HTTP_HAVE_CURL
1737
1738 /* {{{ STATUS http_get(char *, HashTable *, HashTable *, char **, size_t *) */
1739 PHP_HTTP_API STATUS _http_get(const char *URL, HashTable *options,
1740 HashTable *info, char **data, size_t *data_len TSRMLS_DC)
1741 {
1742 CURL *ch = curl_easy_init();
1743
1744 if (!ch) {
1745 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize curl");
1746 return FAILURE;
1747 }
1748
1749 http_curl_initbuf(CURLBUF_EVRY);
1750 http_curl_setopts(ch, URL, options);
1751
1752 if (CURLE_OK != curl_easy_perform(ch)) {
1753 curl_easy_cleanup(ch);
1754 http_curl_freebuf(CURLBUF_EVRY);
1755 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not perform request");
1756 return FAILURE;
1757 }
1758 if (info) {
1759 http_curl_getinfo(ch, info);
1760 }
1761 curl_easy_cleanup(ch);
1762
1763 http_curl_movebuf(CURLBUF_EVRY, data, data_len);
1764
1765 return SUCCESS;
1766 }
1767 /* }}} */
1768
1769 /* {{{ STATUS http_head(char *, HashTable *, HashTable *, char **data, size_t *) */
1770 PHP_HTTP_API STATUS _http_head(const char *URL, HashTable *options,
1771 HashTable *info, char **data, size_t *data_len TSRMLS_DC)
1772 {
1773 CURL *ch = curl_easy_init();
1774
1775 if (!ch) {
1776 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize curl");
1777 return FAILURE;
1778 }
1779
1780 http_curl_initbuf(CURLBUF_HDRS);
1781 http_curl_setopts(ch, URL, options);
1782 curl_easy_setopt(ch, CURLOPT_NOBODY, 1);
1783
1784 if (CURLE_OK != curl_easy_perform(ch)) {
1785 curl_easy_cleanup(ch);
1786 http_curl_freebuf(CURLBUF_HDRS);
1787 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not perform request");
1788 return FAILURE;
1789 }
1790 if (info) {
1791 http_curl_getinfo(ch, info);
1792 }
1793 curl_easy_cleanup(ch);
1794
1795 http_curl_movebuf(CURLBUF_HDRS, data, data_len);
1796
1797 return SUCCESS;
1798 }
1799 /* }}} */
1800
1801 /* {{{ STATUS http_post_data(char *, char *, size_t, HashTable *, HashTable *, char **, size_t *) */
1802 PHP_HTTP_API STATUS _http_post_data(const char *URL, char *postdata,
1803 size_t postdata_len, HashTable *options, HashTable *info, char **data,
1804 size_t *data_len TSRMLS_DC)
1805 {
1806 CURL *ch = curl_easy_init();
1807
1808 if (!ch) {
1809 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize curl");
1810 return FAILURE;
1811 }
1812
1813 http_curl_initbuf(CURLBUF_EVRY);
1814 http_curl_setopts(ch, URL, options);
1815 curl_easy_setopt(ch, CURLOPT_POST, 1);
1816 curl_easy_setopt(ch, CURLOPT_POSTFIELDS, postdata);
1817 curl_easy_setopt(ch, CURLOPT_POSTFIELDSIZE, postdata_len);
1818
1819 if (CURLE_OK != curl_easy_perform(ch)) {
1820 curl_easy_cleanup(ch);
1821 http_curl_freebuf(CURLBUF_EVRY);
1822 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not perform request");
1823 return FAILURE;
1824 }
1825 if (info) {
1826 http_curl_getinfo(ch, info);
1827 }
1828 curl_easy_cleanup(ch);
1829
1830 http_curl_movebuf(CURLBUF_EVRY, data, data_len);
1831
1832 return SUCCESS;
1833 }
1834 /* }}} */
1835
1836 /* {{{ STATUS http_post_array(char *, HashTable *, HashTable *, HashTable *, char **, size_t *) */
1837 PHP_HTTP_API STATUS _http_post_array(const char *URL, HashTable *postarray,
1838 HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC)
1839 {
1840 smart_str qstr = {0};
1841 STATUS status;
1842
1843 if (php_url_encode_hash_ex(postarray, &qstr, NULL,0,NULL,0,NULL,0,NULL TSRMLS_CC) != SUCCESS) {
1844 if (qstr.c) {
1845 efree(qstr.c);
1846 }
1847 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not encode post data");
1848 return FAILURE;
1849 }
1850 smart_str_0(&qstr);
1851
1852 status = http_post_data(URL, qstr.c, qstr.len, options, info, data, data_len);
1853 if (qstr.c) {
1854 efree(qstr.c);
1855 }
1856 return status;
1857 }
1858 /* }}} */
1859
1860 #endif
1861 /* }}} HAVE_CURL */
1862
1863 /* {{{ void http_auth_header(char *, char*) */
1864 PHP_HTTP_API void _http_auth_header(const char *type, const char *realm TSRMLS_DC)
1865 {
1866 char realm_header[1024];
1867 snprintf(realm_header, 1024, "WWW-Authenticate: %s realm=\"%s\"", type, realm);
1868 http_send_status_header(401, realm_header);
1869 }
1870 /* }}} */
1871
1872 /* {{{ STATUS http_auth_credentials(char **, char **) */
1873 PHP_HTTP_API STATUS _http_auth_credentials(char **user, char **pass TSRMLS_DC)
1874 {
1875 if (strncmp(sapi_module.name, "isapi", 5)) {
1876 zval *zuser, *zpass;
1877
1878 HTTP_GSC(zuser, "PHP_AUTH_USER", FAILURE);
1879 HTTP_GSC(zpass, "PHP_AUTH_PW", FAILURE);
1880
1881 *user = estrndup(Z_STRVAL_P(zuser), Z_STRLEN_P(zuser));
1882 *pass = estrndup(Z_STRVAL_P(zpass), Z_STRLEN_P(zpass));
1883
1884 return SUCCESS;
1885 } else {
1886 zval *zauth = NULL;
1887 HTTP_GSC(zauth, "HTTP_AUTHORIZATION", FAILURE);
1888 {
1889 char *decoded, *colon;
1890 int decoded_len;
1891 decoded = php_base64_decode(Z_STRVAL_P(zauth), Z_STRLEN_P(zauth),
1892 &decoded_len);
1893
1894 if (colon = strchr(decoded + 6, ':')) {
1895 *user = estrndup(decoded + 6, colon - decoded - 6);
1896 *pass = estrndup(colon + 1, decoded + decoded_len - colon - 6 - 1);
1897
1898 return SUCCESS;
1899 } else {
1900 return FAILURE;
1901 }
1902 }
1903 }
1904 }
1905 /* }}} */
1906
1907 /* }}} public API */
1908
1909 /*
1910 * Local variables:
1911 * tab-width: 4
1912 * c-basic-offset: 4
1913 * End:
1914 * vim600: noet sw=4 ts=4 fdm=marker
1915 * vim<600: noet sw=4 ts=4
1916 */