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