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