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