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