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