- eliminate warinings in encoding api
[m6w6/ext-http] / http_request_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 #include "php.h"
22
23 #ifdef HTTP_HAVE_CURL
24
25 #include "php_http.h"
26 #include "php_http_std_defs.h"
27 #include "php_http_api.h"
28 #include "php_http_request_api.h"
29 #include "php_http_request_method_api.h"
30 #include "php_http_url_api.h"
31 #ifdef ZEND_ENGINE_2
32 # include "php_http_request_object.h"
33 #endif
34
35 #include "phpstr/phpstr.h"
36
37 #ifdef PHP_WIN32
38 # include <winsock2.h>
39 #endif
40
41 #include <curl/curl.h>
42
43 /*
44 * cruft for thread safe SSL crypto locks
45 */
46 #if defined(ZTS) && defined(HTTP_HAVE_SSL)
47 # ifdef PHP_WIN32
48 # define HTTP_NEED_SSL_TSL
49 # define HTTP_NEED_OPENSSL_TSL
50 # include <openssl/crypto.h>
51 # else /* !PHP_WIN32 */
52 # define HTTP_NEED_SSL_TSL
53 # if defined(HTTP_HAVE_OPENSSL)
54 # if defined(HAVE_OPENSSL_CRYPTO_H)
55 # define HTTP_NEED_OPENSSL_TSL
56 # include <openssl/crypto.h>
57 # else
58 # warning \
59 "libcurl was compiled with OpenSSL support, but configure could not find " \
60 "openssl/crypto.h; thus no SSL crypto locking callbacks will be set, which may " \
61 "cause random crashes on SSL requests"
62 # endif
63 # elif defined(HTTP_HAVE_GNUTLS)
64 # if defined(HAVE_GCRYPT_H)
65 # define HTTP_NEED_GNUTLS_TSL
66 # include <gcrypt.h>
67 # else
68 # warning \
69 "libcurl was compiled with GnuTLS support, but configure could not find " \
70 "gcrypt.h; thus no SSL crypto locking callbacks will be set, which may " \
71 "cause random crashes on SSL requests"
72 # endif
73 # else
74 # warning \
75 "libcurl was compiled with SSL support, but configure could not determine which" \
76 "library was used; thus no SSL crypto locking callbacks will be set, which may " \
77 "cause random crashes on SSL requests"
78 # endif /* HTTP_HAVE_OPENSSL || HTTP_HAVE_GNUTLS */
79 # endif /* PHP_WIN32 */
80 #endif /* ZTS && HTTP_HAVE_SSL */
81
82 ZEND_EXTERN_MODULE_GLOBALS(http);
83
84 #ifdef HTTP_NEED_SSL_TSL
85 static inline void http_ssl_init(void);
86 static inline void http_ssl_cleanup(void);
87 #endif
88
89 PHP_MINIT_FUNCTION(http_request)
90 {
91 #ifdef HTTP_NEED_SSL_TSL
92 http_ssl_init();
93 #endif
94
95 if (CURLE_OK != curl_global_init(CURL_GLOBAL_ALL)) {
96 return FAILURE;
97 }
98
99 #if LIBCURL_VERSION_NUM >= 0x070a05
100 HTTP_LONG_CONSTANT("HTTP_AUTH_BASIC", CURLAUTH_BASIC);
101 HTTP_LONG_CONSTANT("HTTP_AUTH_DIGEST", CURLAUTH_DIGEST);
102 HTTP_LONG_CONSTANT("HTTP_AUTH_NTLM", CURLAUTH_NTLM);
103 HTTP_LONG_CONSTANT("HTTP_AUTH_ANY", CURLAUTH_ANY);
104 #endif /* LIBCURL_VERSION_NUM */
105
106 return SUCCESS;
107 }
108
109 PHP_MSHUTDOWN_FUNCTION(http_request)
110 {
111 curl_global_cleanup();
112 #ifdef HTTP_NEED_SSL_TSL
113 http_ssl_cleanup();
114 #endif
115 return SUCCESS;
116 }
117
118 #ifndef HAVE_CURL_EASY_STRERROR
119 # define curl_easy_strerror(code) HTTP_G(request).error
120 #endif
121
122 #define HTTP_CURL_INFO(I) HTTP_CURL_INFO_EX(I, I)
123 #define HTTP_CURL_INFO_EX(I, X) \
124 switch (CURLINFO_ ##I & ~CURLINFO_MASK) \
125 { \
126 case CURLINFO_STRING: \
127 { \
128 char *c; \
129 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_ ##I, &c)) { \
130 add_assoc_string(&array, pretty_key(http_request_data_copy(COPY_STRING, #X), sizeof(#X)-1, 0, 0), c ? c : "", 1); \
131 } \
132 } \
133 break; \
134 \
135 case CURLINFO_DOUBLE: \
136 { \
137 double d; \
138 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_ ##I, &d)) { \
139 add_assoc_double(&array, pretty_key(http_request_data_copy(COPY_STRING, #X), sizeof(#X)-1, 0, 0), d); \
140 } \
141 } \
142 break; \
143 \
144 case CURLINFO_LONG: \
145 { \
146 long l; \
147 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_ ##I, &l)) { \
148 add_assoc_long(&array, pretty_key(http_request_data_copy(COPY_STRING, #X), sizeof(#X)-1, 0, 0), l); \
149 } \
150 } \
151 break; \
152 }
153
154 #define HTTP_CURL_OPT(OPTION, p) curl_easy_setopt(ch, CURLOPT_##OPTION, (p))
155 #define HTTP_CURL_OPT_STRING(keyname) HTTP_CURL_OPT_STRING_EX(keyname, keyname)
156 #define HTTP_CURL_OPT_SSL_STRING(keyname) HTTP_CURL_OPT_STRING_EX(keyname, SSL##keyname)
157 #define HTTP_CURL_OPT_SSL_STRING_(keyname) HTTP_CURL_OPT_STRING_EX(keyname, SSL_##keyname)
158 #define HTTP_CURL_OPT_STRING_EX(keyname, optname) \
159 if (!strcasecmp(key, #keyname)) { \
160 convert_to_string_ex(param); \
161 HTTP_CURL_OPT(optname, http_request_data_copy(COPY_STRING, Z_STRVAL_PP(param))); \
162 key = NULL; \
163 continue; \
164 }
165 #define HTTP_CURL_OPT_LONG(keyname) HTTP_OPT_SSL_LONG_EX(keyname, keyname)
166 #define HTTP_CURL_OPT_SSL_LONG(keyname) HTTP_CURL_OPT_LONG_EX(keyname, SSL##keyname)
167 #define HTTP_CURL_OPT_SSL_LONG_(keyname) HTTP_CURL_OPT_LONG_EX(keyname, SSL_##keyname)
168 #define HTTP_CURL_OPT_LONG_EX(keyname, optname) \
169 if (!strcasecmp(key, #keyname)) { \
170 convert_to_long_ex(param); \
171 HTTP_CURL_OPT(optname, Z_LVAL_PP(param)); \
172 key = NULL; \
173 continue; \
174 }
175
176 #define http_curl_getopt(o, k, t) _http_curl_getopt_ex((o), (k), sizeof(k), (t) TSRMLS_CC)
177 #define http_curl_getopt_ex(o, k, l, t) _http_curl_getopt_ex((o), (k), (l), (t) TSRMLS_CC)
178 static inline zval *_http_curl_getopt_ex(HashTable *options, char *key, size_t keylen, int type TSRMLS_DC);
179 #define http_curl_defaults(ch) _http_curl_defaults((ch))
180 static inline void _http_curl_defaults(CURL *ch);
181 static size_t http_curl_read_callback(void *, size_t, size_t, void *);
182 static int http_curl_progress_callback(void *, double, double, double, double);
183 static int http_curl_raw_callback(CURL *, curl_infotype, char *, size_t, void *);
184 static int http_curl_dummy_callback(char *data, size_t n, size_t l, void *s) { return n*l; }
185
186 /* {{{ http_request_callback_ctx http_request_callback_data(void *) */
187 http_request_callback_ctx *_http_request_callback_data_ex(void *data, zend_bool cpy TSRMLS_DC)
188 {
189 http_request_callback_ctx *ctx = emalloc(sizeof(http_request_callback_ctx));
190
191 TSRMLS_SET_CTX(ctx->tsrm_ctx);
192 ctx->data = data;
193
194 if (cpy) {
195 return http_request_data_copy(COPY_CONTEXT, ctx);
196 } else {
197 return ctx;
198 }
199 }
200 /* }}} */
201
202 /* {{{ void *http_request_data_copy(int, void *) */
203 void *_http_request_data_copy(int type, void *data TSRMLS_DC)
204 {
205 switch (type)
206 {
207 case COPY_STRING:
208 {
209 char *new_str = estrdup(data);
210 zend_llist_add_element(&HTTP_G(request).copies.strings, &new_str);
211 return new_str;
212 }
213
214 case COPY_SLIST:
215 {
216 zend_llist_add_element(&HTTP_G(request).copies.slists, &data);
217 return data;
218 }
219
220 case COPY_CONTEXT:
221 {
222 zend_llist_add_element(&HTTP_G(request).copies.contexts, &data);
223 return data;
224 }
225
226 case COPY_CONV:
227 {
228 zend_llist_add_element(&HTTP_G(request).copies.convs, &data);
229 return data;
230 }
231
232 default:
233 {
234 return data;
235 }
236 }
237 }
238 /* }}} */
239
240 /* {{{ void http_request_data_free_string(char **) */
241 void _http_request_data_free_string(void *string)
242 {
243 efree(*((char **)string));
244 }
245 /* }}} */
246
247 /* {{{ void http_request_data_free_slist(struct curl_slist **) */
248 void _http_request_data_free_slist(void *list)
249 {
250 curl_slist_free_all(*((struct curl_slist **) list));
251 }
252 /* }}} */
253
254 /* {{{ _http_request_data_free_context(http_request_callback_ctx **) */
255 void _http_request_data_free_context(void *context)
256 {
257 efree(*((http_request_callback_ctx **) context));
258 }
259 /* }}} */
260
261 /* {{{ _http_request_data_free_conv(http_request_conv **) */
262 void _http_request_data_free_conv(void *conv)
263 {
264 efree(*((http_request_conv **) conv));
265 }
266 /* }}} */
267
268 /* {{{ http_request_body *http_request_body_new() */
269 PHP_HTTP_API http_request_body *_http_request_body_new(TSRMLS_D)
270 {
271 http_request_body *body = ecalloc(1, sizeof(http_request_body));
272 return body;
273 }
274 /* }}} */
275
276 /* {{{ STATUS http_request_body_fill(http_request_body *body, HashTable *, HashTable *) */
277 PHP_HTTP_API STATUS _http_request_body_fill(http_request_body *body, HashTable *fields, HashTable *files TSRMLS_DC)
278 {
279 if (files && (zend_hash_num_elements(files) > 0)) {
280 char *key = NULL;
281 ulong idx;
282 zval **data;
283 struct curl_httppost *http_post_data[2] = {NULL, NULL};
284
285 /* normal data */
286 FOREACH_HASH_KEYVAL(fields, key, idx, data) {
287 CURLcode err;
288 if (key) {
289 convert_to_string_ex(data);
290 err = curl_formadd(&http_post_data[0], &http_post_data[1],
291 CURLFORM_COPYNAME, key,
292 CURLFORM_COPYCONTENTS, Z_STRVAL_PP(data),
293 CURLFORM_CONTENTSLENGTH, (long) Z_STRLEN_PP(data),
294 CURLFORM_END
295 );
296 if (CURLE_OK != err) {
297 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not encode post fields: %s", curl_easy_strerror(err));
298 curl_formfree(http_post_data[0]);
299 return FAILURE;
300 }
301
302 /* reset */
303 key = NULL;
304 }
305 }
306
307 /* file data */
308 FOREACH_HASH_VAL(files, data) {
309 zval **file, **type, **name;
310
311 if ( SUCCESS != zend_hash_find(Z_ARRVAL_PP(data), "name", sizeof("name"), (void **) &name) ||
312 SUCCESS != zend_hash_find(Z_ARRVAL_PP(data), "type", sizeof("type"), (void **) &type) ||
313 SUCCESS != zend_hash_find(Z_ARRVAL_PP(data), "file", sizeof("file"), (void **) &file)) {
314 http_error(HE_NOTICE, HTTP_E_INVALID_PARAM, "Post file array entry misses either 'name', 'type' or 'file' entry");
315 } else {
316 CURLcode err = curl_formadd(&http_post_data[0], &http_post_data[1],
317 CURLFORM_COPYNAME, Z_STRVAL_PP(name),
318 CURLFORM_FILE, Z_STRVAL_PP(file),
319 CURLFORM_CONTENTTYPE, Z_STRVAL_PP(type),
320 CURLFORM_END
321 );
322 if (CURLE_OK != err) {
323 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not encode post files: %s", curl_easy_strerror(err));
324 curl_formfree(http_post_data[0]);
325 return FAILURE;
326 }
327 }
328 }
329
330 body->type = HTTP_REQUEST_BODY_CURLPOST;
331 body->data = http_post_data[0];
332 body->size = 0;
333
334 } else {
335 char *encoded;
336 size_t encoded_len;
337
338 if (SUCCESS != http_urlencode_hash_ex(fields, 1, NULL, 0, &encoded, &encoded_len)) {
339 http_error(HE_WARNING, HTTP_E_ENCODING, "Could not encode post data");
340 return FAILURE;
341 }
342
343 body->type = HTTP_REQUEST_BODY_CSTRING;
344 body->data = encoded;
345 body->size = encoded_len;
346 }
347
348 return SUCCESS;
349 }
350 /* }}} */
351
352 /* {{{ void http_request_body_dtor(http_request_body *) */
353 PHP_HTTP_API void _http_request_body_dtor(http_request_body *body TSRMLS_DC)
354 {
355 if (body) {
356 switch (body->type)
357 {
358 case HTTP_REQUEST_BODY_CSTRING:
359 if (body->data) {
360 efree(body->data);
361 }
362 break;
363
364 case HTTP_REQUEST_BODY_CURLPOST:
365 curl_formfree(body->data);
366 break;
367
368 case HTTP_REQUEST_BODY_UPLOADFILE:
369 php_stream_close(body->data);
370 break;
371 }
372 }
373 }
374 /* }}} */
375
376 /* {{{ void http_request_body_free(http_request_body *) */
377 PHP_HTTP_API void _http_request_body_free(http_request_body *body TSRMLS_DC)
378 {
379 if (body) {
380 http_request_body_dtor(body);
381 efree(body);
382 }
383 }
384 /* }}} */
385
386 /* {{{ STATUS http_request_init(CURL *, http_request_method, char *, http_request_body *, HashTable *) */
387 PHP_HTTP_API STATUS _http_request_init(CURL *ch, http_request_method meth, char *url, http_request_body *body, HashTable *options TSRMLS_DC)
388 {
389 zval *zoption;
390 zend_bool range_req = 0;
391
392 /* reset CURL handle */
393 #ifdef HAVE_CURL_EASY_RESET
394 curl_easy_reset(ch);
395 #endif
396 http_curl_defaults(ch);
397
398 /* set options */
399 if (url) {
400 HTTP_CURL_OPT(URL, http_request_data_copy(COPY_STRING, url));
401 }
402
403 HTTP_CURL_OPT(HEADER, 0);
404 HTTP_CURL_OPT(FILETIME, 1);
405 HTTP_CURL_OPT(AUTOREFERER, 1);
406 HTTP_CURL_OPT(READFUNCTION, http_curl_read_callback);
407 /* we'll get all data through the debug function */
408 HTTP_CURL_OPT(WRITEFUNCTION, http_curl_dummy_callback);
409 HTTP_CURL_OPT(HEADERFUNCTION, NULL);
410
411 HTTP_CURL_OPT(VERBOSE, 1);
412 HTTP_CURL_OPT(DEBUGFUNCTION, http_curl_raw_callback);
413
414 #if defined(ZTS) && (LIBCURL_VERSION_NUM >= 0x070a00)
415 HTTP_CURL_OPT(NOSIGNAL, 1);
416 #endif
417 #if LIBCURL_VERSION_NUM < 0x070c00
418 HTTP_CURL_OPT(ERRORBUFFER, HTTP_G(request).error);
419 #endif
420
421 /* progress callback */
422 if (zoption = http_curl_getopt(options, "onprogress", 0)) {
423 HTTP_CURL_OPT(NOPROGRESS, 0);
424 HTTP_CURL_OPT(PROGRESSFUNCTION, http_curl_progress_callback);
425 HTTP_CURL_OPT(PROGRESSDATA, http_request_callback_data(zoption));
426 }
427
428 /* proxy */
429 if (zoption = http_curl_getopt(options, "proxyhost", IS_STRING)) {
430 HTTP_CURL_OPT(PROXY, http_request_data_copy(COPY_STRING, Z_STRVAL_P(zoption)));
431 /* port */
432 if (zoption = http_curl_getopt(options, "proxyport", IS_LONG)) {
433 HTTP_CURL_OPT(PROXYPORT, Z_LVAL_P(zoption));
434 }
435 /* user:pass */
436 if (zoption = http_curl_getopt(options, "proxyauth", IS_STRING)) {
437 HTTP_CURL_OPT(PROXYUSERPWD, http_request_data_copy(COPY_STRING, Z_STRVAL_P(zoption)));
438 }
439 #if LIBCURL_VERSION_NUM >= 0x070a07
440 /* auth method */
441 if (zoption = http_curl_getopt(options, "proxyauthtype", IS_LONG)) {
442 HTTP_CURL_OPT(PROXYAUTH, Z_LVAL_P(zoption));
443 }
444 #endif
445 }
446
447 /* outgoing interface */
448 if (zoption = http_curl_getopt(options, "interface", IS_STRING)) {
449 HTTP_CURL_OPT(INTERFACE, http_request_data_copy(COPY_STRING, Z_STRVAL_P(zoption)));
450 }
451
452 /* another port */
453 if (zoption = http_curl_getopt(options, "port", IS_LONG)) {
454 HTTP_CURL_OPT(PORT, Z_LVAL_P(zoption));
455 }
456
457 /* auth */
458 if (zoption = http_curl_getopt(options, "httpauth", IS_STRING)) {
459 HTTP_CURL_OPT(USERPWD, http_request_data_copy(COPY_STRING, Z_STRVAL_P(zoption)));
460 }
461 #if LIBCURL_VERSION_NUM >= 0x070a06
462 if (zoption = http_curl_getopt(options, "httpauthtype", IS_LONG)) {
463 HTTP_CURL_OPT(HTTPAUTH, Z_LVAL_P(zoption));
464 }
465 #endif
466
467 /* compress, empty string enables all supported if libcurl was build with zlib support */
468 if ((zoption = http_curl_getopt(options, "compress", IS_BOOL)) && Z_LVAL_P(zoption)) {
469 #if defined(HTTP_HAVE_ZLIB) || defined(HAVE_ZLIB)
470 HTTP_CURL_OPT(ENCODING, "gzip;q=1.0, deflate;q=0.5, *;q=0");
471 #else
472 HTTP_CURL_OPT(ENCODING, "");
473 #endif
474 }
475
476 /* redirects, defaults to 0 */
477 if (zoption = http_curl_getopt(options, "redirect", IS_LONG)) {
478 HTTP_CURL_OPT(FOLLOWLOCATION, Z_LVAL_P(zoption) ? 1 : 0);
479 HTTP_CURL_OPT(MAXREDIRS, Z_LVAL_P(zoption));
480 if (zoption = http_curl_getopt(options, "unrestrictedauth", IS_BOOL)) {
481 HTTP_CURL_OPT(UNRESTRICTED_AUTH, Z_LVAL_P(zoption));
482 }
483 }
484
485 /* referer */
486 if (zoption = http_curl_getopt(options, "referer", IS_STRING)) {
487 HTTP_CURL_OPT(REFERER, http_request_data_copy(COPY_STRING, Z_STRVAL_P(zoption)));
488 }
489
490 /* useragent, default "PECL::HTTP/version (PHP/version)" */
491 if (zoption = http_curl_getopt(options, "useragent", IS_STRING)) {
492 HTTP_CURL_OPT(USERAGENT, http_request_data_copy(COPY_STRING, Z_STRVAL_P(zoption)));
493 }
494
495 /* additional headers, array('name' => 'value') */
496 if (zoption = http_curl_getopt(options, "headers", IS_ARRAY)) {
497 char *header_key;
498 ulong header_idx;
499 struct curl_slist *headers = NULL;
500
501 FOREACH_KEY(zoption, header_key, header_idx) {
502 if (header_key) {
503 zval **header_val;
504 if (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(zoption), (void **) &header_val)) {
505 char header[1024] = {0};
506 snprintf(header, 1023, "%s: %s", header_key, Z_STRVAL_PP(header_val));
507 headers = curl_slist_append(headers, http_request_data_copy(COPY_STRING, header));
508 }
509
510 /* reset */
511 header_key = NULL;
512 }
513 }
514
515 if (headers) {
516 HTTP_CURL_OPT(HTTPHEADER, http_request_data_copy(COPY_SLIST, headers));
517 }
518 }
519
520 /* cookies, array('name' => 'value') */
521 if (zoption = http_curl_getopt(options, "cookies", IS_ARRAY)) {
522 char *cookie_key = NULL;
523 ulong cookie_idx = 0;
524 phpstr *qstr = phpstr_new();
525
526 FOREACH_KEY(zoption, cookie_key, cookie_idx) {
527 if (cookie_key) {
528 zval **cookie_val;
529 if (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(zoption), (void **) &cookie_val)) {
530 phpstr_appendf(qstr, "%s=%s; ", cookie_key, Z_STRVAL_PP(cookie_val));
531 }
532
533 /* reset */
534 cookie_key = NULL;
535 }
536 }
537
538 if (qstr->used) {
539 phpstr_fix(qstr);
540 HTTP_CURL_OPT(COOKIE, http_request_data_copy(COPY_STRING, qstr->data));
541 }
542 phpstr_free(&qstr);
543 }
544
545 /* session cookies */
546 if (zoption = http_curl_getopt(options, "cookiesession", IS_BOOL)) {
547 if (Z_LVAL_P(zoption)) {
548 /* accept cookies for this session */
549 HTTP_CURL_OPT(COOKIEFILE, "");
550 } else {
551 /* reset session cookies */
552 HTTP_CURL_OPT(COOKIESESSION, 1);
553 }
554 }
555
556 /* cookiestore, read initial cookies from that file and store cookies back into that file */
557 if ((zoption = http_curl_getopt(options, "cookiestore", IS_STRING)) && Z_STRLEN_P(zoption)) {
558 HTTP_CURL_OPT(COOKIEFILE, http_request_data_copy(COPY_STRING, Z_STRVAL_P(zoption)));
559 HTTP_CURL_OPT(COOKIEJAR, http_request_data_copy(COPY_STRING, Z_STRVAL_P(zoption)));
560 }
561
562 /* resume */
563 if ((zoption = http_curl_getopt(options, "resume", IS_LONG)) && (Z_LVAL_P(zoption) != 0)) {
564 range_req = 1;
565 HTTP_CURL_OPT(RESUME_FROM, Z_LVAL_P(zoption));
566 }
567
568 /* maxfilesize */
569 if (zoption = http_curl_getopt(options, "maxfilesize", IS_LONG)) {
570 HTTP_CURL_OPT(MAXFILESIZE, Z_LVAL_P(zoption));
571 }
572
573 /* lastmodified */
574 if (zoption = http_curl_getopt(options, "lastmodified", IS_LONG)) {
575 if (Z_LVAL_P(zoption)) {
576 if (Z_LVAL_P(zoption) > 0) {
577 HTTP_CURL_OPT(TIMEVALUE, Z_LVAL_P(zoption));
578 } else {
579 HTTP_CURL_OPT(TIMEVALUE, time(NULL) + Z_LVAL_P(zoption));
580 }
581 HTTP_CURL_OPT(TIMECONDITION, range_req ? CURL_TIMECOND_IFUNMODSINCE : CURL_TIMECOND_IFMODSINCE);
582 } else {
583 HTTP_CURL_OPT(TIMECONDITION, CURL_TIMECOND_NONE);
584 }
585 }
586
587 /* timeout, defaults to 0 */
588 if (zoption = http_curl_getopt(options, "timeout", IS_LONG)) {
589 HTTP_CURL_OPT(TIMEOUT, Z_LVAL_P(zoption));
590 }
591
592 /* connecttimeout, defaults to 3 */
593 if (zoption = http_curl_getopt(options, "connecttimeout", IS_LONG)) {
594 HTTP_CURL_OPT(CONNECTTIMEOUT, Z_LVAL_P(zoption));
595 }
596
597 /* ssl */
598 if (zoption = http_curl_getopt(options, "ssl", IS_ARRAY)) {
599 ulong idx;
600 char *key = NULL;
601 zval **param;
602
603 FOREACH_KEYVAL(zoption, key, idx, param) {
604 if (key) {
605 HTTP_CURL_OPT_SSL_STRING(CERT);
606 #if LIBCURL_VERSION_NUM >= 0x070903
607 HTTP_CURL_OPT_SSL_STRING(CERTTYPE);
608 #endif
609 HTTP_CURL_OPT_SSL_STRING(CERTPASSWD);
610
611 HTTP_CURL_OPT_SSL_STRING(KEY);
612 HTTP_CURL_OPT_SSL_STRING(KEYTYPE);
613 HTTP_CURL_OPT_SSL_STRING(KEYPASSWD);
614
615 HTTP_CURL_OPT_SSL_STRING(ENGINE);
616 HTTP_CURL_OPT_SSL_LONG(VERSION);
617
618 HTTP_CURL_OPT_SSL_LONG_(VERIFYPEER);
619 HTTP_CURL_OPT_SSL_LONG_(VERIFYHOST);
620 HTTP_CURL_OPT_SSL_STRING_(CIPHER_LIST);
621
622
623 HTTP_CURL_OPT_STRING(CAINFO);
624 #if LIBCURL_VERSION_NUM >= 0x070908
625 HTTP_CURL_OPT_STRING(CAPATH);
626 #endif
627 HTTP_CURL_OPT_STRING(RANDOM_FILE);
628 HTTP_CURL_OPT_STRING(EGDSOCKET);
629
630 /* reset key */
631 key = NULL;
632 }
633 }
634 }
635
636 /* request method */
637 switch (meth)
638 {
639 case HTTP_GET:
640 curl_easy_setopt(ch, CURLOPT_HTTPGET, 1);
641 break;
642
643 case HTTP_HEAD:
644 curl_easy_setopt(ch, CURLOPT_NOBODY, 1);
645 break;
646
647 case HTTP_POST:
648 curl_easy_setopt(ch, CURLOPT_POST, 1);
649 break;
650
651 case HTTP_PUT:
652 curl_easy_setopt(ch, CURLOPT_UPLOAD, 1);
653 break;
654
655 default:
656 if (http_request_method_exists(0, meth, NULL)) {
657 curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, http_request_method_name(meth));
658 } else {
659 http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Unsupported request method: %d", meth);
660 return FAILURE;
661 }
662 break;
663 }
664
665 /* attach request body */
666 if (body && (meth != HTTP_GET) && (meth != HTTP_HEAD)) {
667 switch (body->type)
668 {
669 case HTTP_REQUEST_BODY_CSTRING:
670 curl_easy_setopt(ch, CURLOPT_POSTFIELDS, body->data);
671 curl_easy_setopt(ch, CURLOPT_POSTFIELDSIZE, body->size);
672 break;
673
674 case HTTP_REQUEST_BODY_CURLPOST:
675 curl_easy_setopt(ch, CURLOPT_HTTPPOST, (struct curl_httppost *) body->data);
676 break;
677
678 case HTTP_REQUEST_BODY_UPLOADFILE:
679 curl_easy_setopt(ch, CURLOPT_READDATA, http_request_callback_data(body));
680 curl_easy_setopt(ch, CURLOPT_INFILESIZE, body->size);
681 break;
682
683 default:
684 /* shouldn't ever happen */
685 http_error_ex(HE_ERROR, 0, "Unknown request body type: %d", body->type);
686 return FAILURE;
687 break;
688 }
689 }
690
691 return SUCCESS;
692 }
693 /* }}} */
694
695 /* {{{ void http_request_conv(CURL *, phpstr *, phpstr *) */
696 void _http_request_conv(CURL *ch, phpstr* response, phpstr *request TSRMLS_DC)
697 {
698 http_request_conv *conv = emalloc(sizeof(http_request_conv));
699 conv->response = response;
700 conv->request = request;
701 conv->last_info = -1;
702 HTTP_CURL_OPT(DEBUGDATA, http_request_callback_data(http_request_data_copy(COPY_CONV, conv)));
703 }
704 /* }}} */
705
706 /* {{{ STATUS http_request_exec(CURL *, HashTable *) */
707 PHP_HTTP_API STATUS _http_request_exec(CURL *ch, HashTable *info, phpstr *response, phpstr *request TSRMLS_DC)
708 {
709 CURLcode result;
710
711 http_request_conv(ch, response, request);
712
713 /* perform request */
714 if (CURLE_OK != (result = curl_easy_perform(ch))) {
715 http_error_ex(HE_WARNING, HTTP_E_REQUEST, "Could not perform request: %s", curl_easy_strerror(result));
716 return FAILURE;
717 } else {
718 /* get curl info */
719 if (info) {
720 http_request_info(ch, info);
721 }
722 return SUCCESS;
723 }
724 }
725 /* }}} */
726
727 /* {{{ void http_request_info(CURL *, HashTable *) */
728 PHP_HTTP_API void _http_request_info(CURL *ch, HashTable *info TSRMLS_DC)
729 {
730 zval array;
731 INIT_ZARR(array, info);
732
733 HTTP_CURL_INFO(EFFECTIVE_URL);
734 #if LIBCURL_VERSION_NUM >= 0x070a07
735 HTTP_CURL_INFO(RESPONSE_CODE);
736 #else
737 HTTP_CURL_INFO_EX(HTTP_CODE, RESPONSE_CODE);
738 #endif
739 HTTP_CURL_INFO(HTTP_CONNECTCODE);
740 #if LIBCURL_VERSION_NUM >= 0x070500
741 HTTP_CURL_INFO(FILETIME);
742 #endif
743 HTTP_CURL_INFO(TOTAL_TIME);
744 HTTP_CURL_INFO(NAMELOOKUP_TIME);
745 HTTP_CURL_INFO(CONNECT_TIME);
746 HTTP_CURL_INFO(PRETRANSFER_TIME);
747 HTTP_CURL_INFO(STARTTRANSFER_TIME);
748 #if LIBCURL_VERSION_NUM >= 0x070907
749 HTTP_CURL_INFO(REDIRECT_TIME);
750 HTTP_CURL_INFO(REDIRECT_COUNT);
751 #endif
752 HTTP_CURL_INFO(SIZE_UPLOAD);
753 HTTP_CURL_INFO(SIZE_DOWNLOAD);
754 HTTP_CURL_INFO(SPEED_DOWNLOAD);
755 HTTP_CURL_INFO(SPEED_UPLOAD);
756 HTTP_CURL_INFO(HEADER_SIZE);
757 HTTP_CURL_INFO(REQUEST_SIZE);
758 HTTP_CURL_INFO(SSL_VERIFYRESULT);
759 #if LIBCURL_VERSION_NUM >= 0x070c03
760 /*HTTP_CURL_INFO(SSL_ENGINES); todo: CURLINFO_SLIST */
761 #endif
762 HTTP_CURL_INFO(CONTENT_LENGTH_DOWNLOAD);
763 HTTP_CURL_INFO(CONTENT_LENGTH_UPLOAD);
764 HTTP_CURL_INFO(CONTENT_TYPE);
765 #if LIBCURL_VERSION_NUM >= 0x070a03
766 /*HTTP_CURL_INFO(PRIVATE);*/
767 #endif
768 #if LIBCURL_VERSION_NUM >= 0x070a08
769 HTTP_CURL_INFO(HTTPAUTH_AVAIL);
770 HTTP_CURL_INFO(PROXYAUTH_AVAIL);
771 #endif
772 #if LIBCURL_VERSION_NUM >= 0x070c02
773 /*HTTP_CURL_INFO(OS_ERRNO);*/
774 #endif
775 #if LIBCURL_VERSION_NUM >= 0x070c03
776 HTTP_CURL_INFO(NUM_CONNECTS);
777 #endif
778 }
779 /* }}} */
780
781 /* {{{ STATUS http_request_ex(CURL *, http_request_method, char *, http_request_body, HashTable, HashTable, phpstr *) */
782 PHP_HTTP_API STATUS _http_request_ex(CURL *ch, http_request_method meth, char *url, http_request_body *body, HashTable *options, HashTable *info, phpstr *response TSRMLS_DC)
783 {
784 STATUS status;
785 zend_bool clean_curl = !ch;
786
787 HTTP_CHECK_CURL_INIT(ch, curl_easy_init(), return FAILURE);
788
789 status = ((SUCCESS == http_request_init(ch, meth, url, body, options)) &&
790 (SUCCESS == http_request_exec(ch, info, response, NULL))) ? SUCCESS : FAILURE;
791
792 if (clean_curl) {
793 curl_easy_cleanup(ch);
794 }
795 return status;
796 }
797 /* }}} */
798
799 /* {{{ static size_t http_curl_read_callback(void *, size_t, size_t, void *) */
800 static size_t http_curl_read_callback(void *data, size_t len, size_t n, void *s)
801 {
802 HTTP_REQUEST_CALLBACK_DATA(s, http_request_body *, body);
803
804 if (body->type != HTTP_REQUEST_BODY_UPLOADFILE) {
805 return 0;
806 }
807 return php_stream_read((php_stream *) body->data, data, len * n);
808 }
809 /* }}} */
810
811 /* {{{ static int http_curl_progress_callback(void *, double, double, double, double) */
812 static int http_curl_progress_callback(void *data, double dltotal, double dlnow, double ultotal, double ulnow)
813 {
814 zval *params_pass[4], params_local[4], retval;
815 HTTP_REQUEST_CALLBACK_DATA(data, zval *, func);
816
817 params_pass[0] = &params_local[0];
818 params_pass[1] = &params_local[1];
819 params_pass[2] = &params_local[2];
820 params_pass[3] = &params_local[3];
821
822 INIT_PZVAL(params_pass[0]);
823 INIT_PZVAL(params_pass[1]);
824 INIT_PZVAL(params_pass[2]);
825 INIT_PZVAL(params_pass[3]);
826 ZVAL_DOUBLE(params_pass[0], dltotal);
827 ZVAL_DOUBLE(params_pass[1], dlnow);
828 ZVAL_DOUBLE(params_pass[2], ultotal);
829 ZVAL_DOUBLE(params_pass[3], ulnow);
830
831 return call_user_function(EG(function_table), NULL, func, &retval, 4, params_pass TSRMLS_CC);
832 }
833 /* }}} */
834
835 /* {{{ static int http_curl_raw_callback(CURL *, curl_infotype, char *, size_t, void *) */
836 static int http_curl_raw_callback(CURL *ch, curl_infotype type, char *data, size_t length, void *ctx)
837 {
838 HTTP_REQUEST_CALLBACK_DATA(ctx, http_request_conv *, conv);
839
840 #if 0
841 fprintf(stderr, "DEBUG: %s\n", data);
842 #endif
843
844 switch (type)
845 {
846 case CURLINFO_DATA_IN:
847 if (conv->response && conv->last_info == CURLINFO_HEADER_IN) {
848 phpstr_appends(conv->response, HTTP_CRLF);
849 }
850 case CURLINFO_HEADER_IN:
851 if (conv->response) {
852 phpstr_append(conv->response, data, length);
853 }
854 break;
855 case CURLINFO_DATA_OUT:
856 if (conv->request && conv->last_info == CURLINFO_HEADER_OUT) {
857 phpstr_appends(conv->request, HTTP_CRLF);
858 }
859 case CURLINFO_HEADER_OUT:
860 if (conv->request) {
861 phpstr_append(conv->request, data, length);
862 }
863 break;
864 }
865
866 if (type) {
867 conv->last_info = type;
868 }
869 return 0;
870 }
871 /* }}} */
872
873 /* {{{ static inline zval *http_curl_getopt(HashTable *, char *, size_t, int) */
874 static inline zval *_http_curl_getopt_ex(HashTable *options, char *key, size_t keylen, int type TSRMLS_DC)
875 {
876 zval **zoption;
877
878 if (!options || (SUCCESS != zend_hash_find(options, key, keylen, (void **) &zoption))) {
879 return NULL;
880 }
881
882 if (Z_TYPE_PP(zoption) != type) {
883 switch (type)
884 {
885 case IS_BOOL: convert_to_boolean_ex(zoption); break;
886 case IS_LONG: convert_to_long_ex(zoption); break;
887 case IS_DOUBLE: convert_to_double_ex(zoption); break;
888 case IS_STRING: convert_to_string_ex(zoption); break;
889 case IS_ARRAY: convert_to_array_ex(zoption); break;
890 case IS_OBJECT: convert_to_object_ex(zoption); break;
891 default:
892 break;
893 }
894 }
895
896 return *zoption;
897 }
898 /* }}} */
899
900 #ifdef HTTP_NEED_OPENSSL_TSL
901
902 static MUTEX_T *http_ssl_mutex = NULL;
903
904 static void http_ssl_lock(int mode, int n, const char * file, int line)
905 {
906 if (mode & CRYPTO_LOCK) {
907 tsrm_mutex_lock(http_ssl_mutex[n]);
908 } else {
909 tsrm_mutex_unlock(http_ssl_mutex[n]);
910 }
911 }
912
913 static unsigned long http_ssl_id(void)
914 {
915 return (unsigned long) tsrm_thread_id();
916 }
917
918 static inline void http_ssl_init(void)
919 {
920 int i, c = CRYPTO_num_locks();
921
922 http_ssl_mutex = malloc(c * sizeof(MUTEX_T));
923
924 for (i = 0; i < c; ++i) {
925 http_ssl_mutex[i] = tsrm_mutex_alloc();
926 }
927
928 CRYPTO_set_id_callback(http_ssl_id);
929 CRYPTO_set_locking_callback(http_ssl_lock);
930 }
931
932 static inline void http_ssl_cleanup(void)
933 {
934 if (http_ssl_mutex) {
935 int i, c = CRYPTO_num_locks();
936
937 CRYPTO_set_id_callback(NULL);
938 CRYPTO_set_locking_callback(NULL);
939
940 for (i = 0; i < c; ++i) {
941 tsrm_mutex_free(http_ssl_mutex[i]);
942 }
943
944 free(http_ssl_mutex);
945 http_ssl_mutex = NULL;
946 }
947 }
948 #endif /* HTTP_NEED_OPENSSL_TSL */
949
950 #ifdef HTTP_NEED_GNUTLS_TSL
951
952 static int http_ssl_mutex_create(void **m)
953 {
954 if (*((MUTEX_T **) m) = tsrm_mutex_alloc()) {
955 return SUCCESS;
956 } else {
957 return FAILURE;
958 }
959 }
960
961 static int http_ssl_mutex_destroy(void **m)
962 {
963 tsrm_mutex_free(*((MUTEX_T **) m));
964 return SUCCESS;
965 }
966
967 static int http_ssl_mutex_lock(void **m)
968 {
969 return tsrm_mutex_lock(*((MUTEX_T **) m));
970 }
971
972 static int http_ssl_mutex_unlock(void **m)
973 {
974 return tsrm_mutex_unlock(*((MUTEX_T **) m));
975 }
976
977 static struct gcry_thread_cbs http_ssl_callbacks = {
978 GCRY_THREAD_OPTIONS_USER,
979 NULL,
980 http_ssl_mutex_create,
981 http_ssl_mutex_destroy,
982 http_ssl_mutex_lock,
983 http_ssl_mutex_unlock
984 };
985
986 static inline void http_ssl_init(void)
987 {
988 gcry_control(GCRYCTL_SET_THREAD_CBS, &http_ssl_callbacks);
989 }
990
991 static inline void http_ssl_cleanup(void)
992 {
993 return;
994 }
995
996 #endif /* HTTP_NEED_GNUTLS_TSL */
997
998 static inline void _http_curl_defaults(CURL *ch)
999 {
1000 HTTP_CURL_OPT(URL, NULL);
1001 HTTP_CURL_OPT(NOPROGRESS, 1);
1002 HTTP_CURL_OPT(PROXY, NULL);
1003 HTTP_CURL_OPT(PROXYPORT, 0);
1004 HTTP_CURL_OPT(PROXYUSERPWD, NULL);
1005 #if LIBCURL_VERSION_NUM >= 0x070a07
1006 HTTP_CURL_OPT(PROXYAUTH, 0);
1007 #endif
1008 HTTP_CURL_OPT(INTERFACE, NULL);
1009 HTTP_CURL_OPT(PORT, 0);
1010 HTTP_CURL_OPT(USERPWD, NULL);
1011 #if LIBCURL_VERSION_NUM >= 0x070a06
1012 HTTP_CURL_OPT(HTTPAUTH, 0);
1013 #endif
1014 HTTP_CURL_OPT(ENCODING, 0);
1015 HTTP_CURL_OPT(FOLLOWLOCATION, 0);
1016 HTTP_CURL_OPT(UNRESTRICTED_AUTH, 0);
1017 HTTP_CURL_OPT(REFERER, NULL);
1018 HTTP_CURL_OPT(USERAGENT, "PECL::HTTP/" HTTP_PEXT_VERSION " (PHP/" PHP_VERSION ")");
1019 HTTP_CURL_OPT(HTTPHEADER, NULL);
1020 HTTP_CURL_OPT(COOKIE, NULL);
1021 HTTP_CURL_OPT(COOKIEFILE, NULL);
1022 HTTP_CURL_OPT(COOKIEJAR, NULL);
1023 HTTP_CURL_OPT(RESUME_FROM, 0);
1024 HTTP_CURL_OPT(MAXFILESIZE, 0);
1025 HTTP_CURL_OPT(TIMECONDITION, 0);
1026 HTTP_CURL_OPT(TIMEVALUE, 0);
1027 HTTP_CURL_OPT(TIMEOUT, 0);
1028 HTTP_CURL_OPT(CONNECTTIMEOUT, 3);
1029 HTTP_CURL_OPT(SSLCERT, NULL);
1030 #if LIBCURL_VERSION_NUM >= 0x070903
1031 HTTP_CURL_OPT(SSLCERTTYPE, NULL);
1032 #endif
1033 HTTP_CURL_OPT(SSLCERTPASSWD, NULL);
1034 HTTP_CURL_OPT(SSLKEY, NULL);
1035 HTTP_CURL_OPT(SSLKEYTYPE, NULL);
1036 HTTP_CURL_OPT(SSLKEYPASSWD, NULL);
1037 HTTP_CURL_OPT(SSLENGINE, NULL);
1038 HTTP_CURL_OPT(SSLVERSION, 0);
1039 HTTP_CURL_OPT(SSL_VERIFYPEER, 0);
1040 HTTP_CURL_OPT(SSL_VERIFYHOST, 0);
1041 HTTP_CURL_OPT(SSL_CIPHER_LIST, NULL);
1042 HTTP_CURL_OPT(CAINFO, NULL);
1043 #if LIBCURL_VERSION_NUM >= 0x070908
1044 HTTP_CURL_OPT(CAPATH, NULL);
1045 #endif
1046 HTTP_CURL_OPT(RANDOM_FILE, NULL);
1047 HTTP_CURL_OPT(EGDSOCKET, NULL);
1048 HTTP_CURL_OPT(POSTFIELDS, NULL);
1049 HTTP_CURL_OPT(POSTFIELDSIZE, 0);
1050 HTTP_CURL_OPT(HTTPPOST, NULL);
1051 HTTP_CURL_OPT(READDATA, NULL);
1052 HTTP_CURL_OPT(INFILESIZE, 0);
1053 }
1054
1055 #endif /* HTTP_HAVE_CURL */
1056
1057 /*
1058 * Local variables:
1059 * tab-width: 4
1060 * c-basic-offset: 4
1061 * End:
1062 * vim600: noet sw=4 ts=4 fdm=marker
1063 * vim<600: noet sw=4 ts=4
1064 */
1065