Fixed bug #68353 (QsoSSL support removed in libcurl 7.39)
[m6w6/ext-http] / php_http_client_curl.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2014, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #include "php_http_api.h"
14 #include "php_http_client.h"
15
16 #if PHP_HTTP_HAVE_CURL
17
18 #if PHP_HTTP_HAVE_EVENT
19 # if !PHP_HTTP_HAVE_EVENT2 && /* just be really sure */ !(LIBEVENT_VERSION_NUMBER >= 0x02000000)
20 # include <event.h>
21 # define event_base_new event_init
22 # define event_assign(e, b, s, a, cb, d) do {\
23 event_set(e, s, a, cb, d); \
24 event_base_set(b, e); \
25 } while(0)
26 # else
27 # if PHP_HTTP_HAVE_EVENT2
28 # include <event2/event.h>
29 # include <event2/event_struct.h>
30 # else
31 # error "libevent presence is unknown"
32 # endif
33 # endif
34 # ifndef DBG_EVENTS
35 # define DBG_EVENTS 0
36 # endif
37 #endif
38
39 #ifdef PHP_HTTP_HAVE_OPENSSL
40 # include <openssl/ssl.h>
41 #endif
42 #ifdef PHP_HTTP_HAVE_GNUTLS
43 # include <gnutls.h>
44 #endif
45
46 typedef struct php_http_client_curl {
47 CURLM *handle;
48
49 int unfinished; /* int because of curl_multi_perform() */
50
51 #if PHP_HTTP_HAVE_EVENT
52 struct event_base *evbase;
53 struct event *timeout;
54 unsigned useevents:1;
55 #endif
56 } php_http_client_curl_t;
57
58 typedef struct php_http_client_curl_handler {
59 CURL *handle;
60 php_resource_factory_t *rf;
61 php_http_client_t *client;
62 php_http_client_progress_state_t progress;
63
64 php_http_client_enqueue_t queue;
65
66 struct {
67 php_http_message_parser_t *parser;
68 php_http_message_t *message;
69 php_http_buffer_t *buffer;
70 } request;
71
72 struct {
73 php_http_message_parser_t *parser;
74 php_http_message_t *message;
75 php_http_buffer_t *buffer;
76 } response;
77
78 struct {
79 HashTable cache;
80
81 struct curl_slist *headers;
82 struct curl_slist *resolve;
83 php_http_buffer_t cookies;
84 php_http_buffer_t ranges;
85
86 long redirects;
87 unsigned range_request:1;
88 unsigned encode_cookies:1;
89
90 struct {
91 uint count;
92 double delay;
93 } retry;
94
95 } options;
96
97 } php_http_client_curl_handler_t;
98
99 typedef struct php_http_curle_storage {
100 char *url;
101 char *cookiestore;
102 CURLcode errorcode;
103 char errorbuffer[0x100];
104 } php_http_curle_storage_t;
105
106 static inline php_http_curle_storage_t *php_http_curle_get_storage(CURL *ch) {
107 php_http_curle_storage_t *st = NULL;
108
109 curl_easy_getinfo(ch, CURLINFO_PRIVATE, &st);
110
111 if (!st) {
112 st = pecalloc(1, sizeof(*st), 1);
113 curl_easy_setopt(ch, CURLOPT_PRIVATE, st);
114 curl_easy_setopt(ch, CURLOPT_ERRORBUFFER, st->errorbuffer);
115 }
116
117 return st;
118 }
119
120 static void *php_http_curle_ctor(void *opaque, void *init_arg TSRMLS_DC)
121 {
122 void *ch;
123
124 if ((ch = curl_easy_init())) {
125 php_http_curle_get_storage(ch);
126 return ch;
127 }
128 return NULL;
129 }
130
131 static void *php_http_curle_copy(void *opaque, void *handle TSRMLS_DC)
132 {
133 void *ch;
134
135 if ((ch = curl_easy_duphandle(handle))) {
136 curl_easy_reset(ch);
137 php_http_curle_get_storage(ch);
138 return ch;
139 }
140 return NULL;
141 }
142
143 static void php_http_curle_dtor(void *opaque, void *handle TSRMLS_DC)
144 {
145 php_http_curle_storage_t *st = php_http_curle_get_storage(handle);
146
147 curl_easy_cleanup(handle);
148
149 if (st) {
150 if (st->url) {
151 pefree(st->url, 1);
152 }
153 if (st->cookiestore) {
154 pefree(st->cookiestore, 1);
155 }
156 pefree(st, 1);
157 }
158 }
159
160 static php_resource_factory_ops_t php_http_curle_resource_factory_ops = {
161 php_http_curle_ctor,
162 php_http_curle_copy,
163 php_http_curle_dtor
164 };
165
166 static void *php_http_curlm_ctor(void *opaque, void *init_arg TSRMLS_DC)
167 {
168 return curl_multi_init();
169 }
170
171 static void php_http_curlm_dtor(void *opaque, void *handle TSRMLS_DC)
172 {
173 curl_multi_cleanup(handle);
174 }
175
176 static php_resource_factory_ops_t php_http_curlm_resource_factory_ops = {
177 php_http_curlm_ctor,
178 NULL,
179 php_http_curlm_dtor
180 };
181
182 /* curl callbacks */
183
184 static size_t php_http_curle_read_callback(void *data, size_t len, size_t n, void *ctx)
185 {
186 php_http_message_body_t *body = ctx;
187
188 if (body && body->stream_id) {
189 php_stream *s = php_http_message_body_stream(body);
190
191 if (s) {
192 TSRMLS_FETCH_FROM_CTX(body->ts);
193 return php_stream_read(s, data, len * n);
194 } else abort();
195 }
196 return 0;
197 }
198
199 #if PHP_HTTP_CURL_VERSION(7,32,0)
200 static int php_http_curle_xferinfo_callback(void *ctx, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
201 #else
202 static int php_http_curle_progress_callback(void *ctx, double dltotal, double dlnow, double ultotal, double ulnow)
203 #endif
204 {
205 php_http_client_curl_handler_t *h = ctx;
206 zend_bool update = 0;
207
208 if (h->progress.dl.total != dltotal
209 || h->progress.dl.now != dlnow
210 || h->progress.ul.total != ultotal
211 || h->progress.ul.now != ulnow
212 ) {
213 update = 1;
214
215 h->progress.dl.total = dltotal;
216 h->progress.dl.now = dlnow;
217 h->progress.ul.total = ultotal;
218 h->progress.ul.now = ulnow;
219 }
220
221 if (update && h->client->callback.progress.func) {
222 h->client->callback.progress.func(h->client->callback.progress.arg, h->client, &h->queue, &h->progress);
223 }
224
225 return 0;
226 }
227
228 static curlioerr php_http_curle_ioctl_callback(CURL *ch, curliocmd cmd, void *ctx)
229 {
230 php_http_message_body_t *body = ctx;
231
232 if (cmd != CURLIOCMD_RESTARTREAD) {
233 return CURLIOE_UNKNOWNCMD;
234 }
235
236 if (body) {
237 TSRMLS_FETCH_FROM_CTX(body->ts);
238
239 if (SUCCESS == php_stream_rewind(php_http_message_body_stream(body))) {
240 return CURLIOE_OK;
241 }
242 }
243
244 return CURLIOE_FAILRESTART;
245 }
246
247 static int php_http_curle_raw_callback(CURL *ch, curl_infotype type, char *data, size_t length, void *ctx)
248 {
249 php_http_client_curl_handler_t *h = ctx;
250 unsigned flags = 0;
251
252 /* catch progress */
253 switch (type) {
254 case CURLINFO_TEXT:
255 if (data[0] == '-') {
256 } else if (php_memnstr(data, ZEND_STRL("Adding handle:"), data + length)) {
257 h->progress.info = "setup";
258 } else if (php_memnstr(data, ZEND_STRL("addHandle"), data + length)) {
259 h->progress.info = "setup";
260 } else if (php_memnstr(data, ZEND_STRL("About to connect"), data + length)) {
261 h->progress.info = "resolve";
262 } else if (php_memnstr(data, ZEND_STRL("Trying"), data + length)) {
263 h->progress.info = "connect";
264 } else if (php_memnstr(data, ZEND_STRL("Found bundle for host"), data + length)) {
265 h->progress.info = "connect";
266 } else if (php_memnstr(data, ZEND_STRL("Connected"), data + length)) {
267 h->progress.info = "connected";
268 } else if (php_memnstr(data, ZEND_STRL("Re-using existing connection!"), data + length)) {
269 h->progress.info = "connected";
270 } else if (php_memnstr(data, ZEND_STRL("blacklisted"), data + length)) {
271 h->progress.info = "blacklist check";
272 } else if (php_memnstr(data, ZEND_STRL("SSL"), data + length)) {
273 h->progress.info = "ssl negotiation";
274 } else if (php_memnstr(data, ZEND_STRL("upload"), data + length)) {
275 h->progress.info = "uploaded";
276 } else if (php_memnstr(data, ZEND_STRL("left intact"), data + length)) {
277 h->progress.info = "not disconnected";
278 } else if (php_memnstr(data, ZEND_STRL("closed"), data + length)) {
279 h->progress.info = "disconnected";
280 } else if (php_memnstr(data, ZEND_STRL("Issue another request"), data + length)) {
281 h->progress.info = "redirect";
282 } else if (php_memnstr(data, ZEND_STRL("Operation timed out"), data + length)) {
283 h->progress.info = "timeout";
284 } else {
285 #if PHP_DEBUG
286 h->progress.info = data;
287 data[length - 1] = '\0';
288 #endif
289 }
290 if (h->client->callback.progress.func) {
291 h->client->callback.progress.func(h->client->callback.progress.arg, h->client, &h->queue, &h->progress);
292 }
293 break;
294 case CURLINFO_HEADER_OUT:
295 case CURLINFO_DATA_OUT:
296 case CURLINFO_SSL_DATA_OUT:
297 h->progress.info = "send";
298 break;
299 case CURLINFO_HEADER_IN:
300 case CURLINFO_DATA_IN:
301 case CURLINFO_SSL_DATA_IN:
302 h->progress.info = "receive";
303 break;
304 default:
305 break;
306 }
307 /* process data */
308 switch (type) {
309 case CURLINFO_HEADER_IN:
310 case CURLINFO_DATA_IN:
311 php_http_buffer_append(h->response.buffer, data, length);
312
313 if (h->options.redirects) {
314 flags |= PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS;
315 }
316
317 if (PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE == php_http_message_parser_parse(h->response.parser, h->response.buffer, flags, &h->response.message)) {
318 return -1;
319 }
320 break;
321
322 case CURLINFO_HEADER_OUT:
323 case CURLINFO_DATA_OUT:
324 php_http_buffer_append(h->request.buffer, data, length);
325
326 if (PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE == php_http_message_parser_parse(h->request.parser, h->request.buffer, flags, &h->request.message)) {
327 return -1;
328 }
329 break;
330 default:
331 break;
332 }
333
334 #if 0
335 /* debug */
336 _dpf(type, data, length);
337 #endif
338
339 return 0;
340 }
341
342 static int php_http_curle_dummy_callback(char *data, size_t n, size_t l, void *s)
343 {
344 return n*l;
345 }
346
347 static STATUS php_http_curle_get_info(CURL *ch, HashTable *info)
348 {
349 char *c;
350 long l;
351 double d;
352 struct curl_slist *s, *p;
353 zval *subarray, array;
354 INIT_PZVAL_ARRAY(&array, info);
355
356 /* BEGIN::CURLINFO */
357 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_EFFECTIVE_URL, &c)) {
358 add_assoc_string_ex(&array, "effective_url", sizeof("effective_url"), c ? c : "", 1);
359 }
360 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &l)) {
361 add_assoc_long_ex(&array, "response_code", sizeof("response_code"), l);
362 }
363 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_TOTAL_TIME, &d)) {
364 add_assoc_double_ex(&array, "total_time", sizeof("total_time"), d);
365 }
366 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_NAMELOOKUP_TIME, &d)) {
367 add_assoc_double_ex(&array, "namelookup_time", sizeof("namelookup_time"), d);
368 }
369 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONNECT_TIME, &d)) {
370 add_assoc_double_ex(&array, "connect_time", sizeof("connect_time"), d);
371 }
372 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRETRANSFER_TIME, &d)) {
373 add_assoc_double_ex(&array, "pretransfer_time", sizeof("pretransfer_time"), d);
374 }
375 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SIZE_UPLOAD, &d)) {
376 add_assoc_double_ex(&array, "size_upload", sizeof("size_upload"), d);
377 }
378 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SIZE_DOWNLOAD, &d)) {
379 add_assoc_double_ex(&array, "size_download", sizeof("size_download"), d);
380 }
381 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SPEED_DOWNLOAD, &d)) {
382 add_assoc_double_ex(&array, "speed_download", sizeof("speed_download"), d);
383 }
384 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SPEED_UPLOAD, &d)) {
385 add_assoc_double_ex(&array, "speed_upload", sizeof("speed_upload"), d);
386 }
387 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HEADER_SIZE, &l)) {
388 add_assoc_long_ex(&array, "header_size", sizeof("header_size"), l);
389 }
390 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REQUEST_SIZE, &l)) {
391 add_assoc_long_ex(&array, "request_size", sizeof("request_size"), l);
392 }
393 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SSL_VERIFYRESULT, &l)) {
394 add_assoc_long_ex(&array, "ssl_verifyresult", sizeof("ssl_verifyresult"), l);
395 }
396 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_FILETIME, &l)) {
397 add_assoc_long_ex(&array, "filetime", sizeof("filetime"), l);
398 }
399 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d)) {
400 add_assoc_double_ex(&array, "content_length_download", sizeof("content_length_download"), d);
401 }
402 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_LENGTH_UPLOAD, &d)) {
403 add_assoc_double_ex(&array, "content_length_upload", sizeof("content_length_upload"), d);
404 }
405 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_STARTTRANSFER_TIME, &d)) {
406 add_assoc_double_ex(&array, "starttransfer_time", sizeof("starttransfer_time"), d);
407 }
408 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_TYPE, &c)) {
409 add_assoc_string_ex(&array, "content_type", sizeof("content_type"), c ? c : "", 1);
410 }
411 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_TIME, &d)) {
412 add_assoc_double_ex(&array, "redirect_time", sizeof("redirect_time"), d);
413 }
414 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_COUNT, &l)) {
415 add_assoc_long_ex(&array, "redirect_count", sizeof("redirect_count"), l);
416 }
417 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HTTP_CONNECTCODE, &l)) {
418 add_assoc_long_ex(&array, "connect_code", sizeof("connect_code"), l);
419 }
420 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HTTPAUTH_AVAIL, &l)) {
421 add_assoc_long_ex(&array, "httpauth_avail", sizeof("httpauth_avail"), l);
422 }
423 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PROXYAUTH_AVAIL, &l)) {
424 add_assoc_long_ex(&array, "proxyauth_avail", sizeof("proxyauth_avail"), l);
425 }
426 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_OS_ERRNO, &l)) {
427 add_assoc_long_ex(&array, "os_errno", sizeof("os_errno"), l);
428 }
429 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_NUM_CONNECTS, &l)) {
430 add_assoc_long_ex(&array, "num_connects", sizeof("num_connects"), l);
431 }
432 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SSL_ENGINES, &s)) {
433 MAKE_STD_ZVAL(subarray);
434 array_init(subarray);
435 for (p = s; p; p = p->next) {
436 if (p->data) {
437 add_next_index_string(subarray, p->data, 1);
438 }
439 }
440 add_assoc_zval_ex(&array, "ssl_engines", sizeof("ssl_engines"), subarray);
441 curl_slist_free_all(s);
442 }
443 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_COOKIELIST, &s)) {
444 MAKE_STD_ZVAL(subarray);
445 array_init(subarray);
446 for (p = s; p; p = p->next) {
447 if (p->data) {
448 add_next_index_string(subarray, p->data, 1);
449 }
450 }
451 add_assoc_zval_ex(&array, "cookies", sizeof("cookies"), subarray);
452 curl_slist_free_all(s);
453 }
454 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_URL, &c)) {
455 add_assoc_string_ex(&array, "redirect_url", sizeof("redirect_url"), c ? c : "", 1);
456 }
457 #if PHP_HTTP_CURL_VERSION(7,19,0)
458 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRIMARY_IP, &c)) {
459 add_assoc_string_ex(&array, "primary_ip", sizeof("primary_ip"), c ? c : "", 1);
460 }
461 #endif
462 #if PHP_HTTP_CURL_VERSION(7,19,0)
463 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_APPCONNECT_TIME, &d)) {
464 add_assoc_double_ex(&array, "appconnect_time", sizeof("appconnect_time"), d);
465 }
466 #endif
467 #if PHP_HTTP_CURL_VERSION(7,19,4)
468 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONDITION_UNMET, &l)) {
469 add_assoc_long_ex(&array, "condition_unmet", sizeof("condition_unmet"), l);
470 }
471 #endif
472 #if PHP_HTTP_CURL_VERSION(7,21,0)
473 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRIMARY_PORT, &l)) {
474 add_assoc_long_ex(&array, "primary_port", sizeof("primary_port"), l);
475 }
476 #endif
477 #if PHP_HTTP_CURL_VERSION(7,21,0)
478 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_LOCAL_IP, &c)) {
479 add_assoc_string_ex(&array, "local_ip", sizeof("local_ip"), c ? c : "", 1);
480 }
481 #endif
482 #if PHP_HTTP_CURL_VERSION(7,21,0)
483 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_LOCAL_PORT, &l)) {
484 add_assoc_long_ex(&array, "local_port", sizeof("local_port"), l);
485 }
486 #endif
487
488 /* END::CURLINFO */
489
490 #if PHP_HTTP_CURL_VERSION(7,34,0)
491 {
492 zval *ti_array;
493 struct curl_tlssessioninfo *ti;
494
495 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_TLS_SESSION, &ti)) {
496 const char *backend;
497
498 MAKE_STD_ZVAL(subarray);
499 ZVAL_NULL(subarray);
500 MAKE_STD_ZVAL(ti_array);
501 array_init(ti_array);
502
503 switch (ti->backend) {
504 case CURLSSLBACKEND_NONE:
505 backend = "none";
506 break;
507 case CURLSSLBACKEND_OPENSSL:
508 backend = "openssl";
509 #ifdef PHP_HTTP_HAVE_OPENSSL
510 {
511 SSL_CTX *ctx = ti->internals;
512
513 array_init(subarray);
514 add_assoc_long_ex(subarray, ZEND_STRS("number"), SSL_CTX_sess_number(ctx));
515 add_assoc_long_ex(subarray, ZEND_STRS("connect"), SSL_CTX_sess_connect(ctx));
516 add_assoc_long_ex(subarray, ZEND_STRS("connect_good"), SSL_CTX_sess_connect_good(ctx));
517 add_assoc_long_ex(subarray, ZEND_STRS("connect_renegotiate"), SSL_CTX_sess_connect_renegotiate(ctx));
518 add_assoc_long_ex(subarray, ZEND_STRS("hits"), SSL_CTX_sess_hits(ctx));
519 add_assoc_long_ex(subarray, ZEND_STRS("cache_full"), SSL_CTX_sess_cache_full(ctx));
520 }
521 #endif
522 break;
523 case CURLSSLBACKEND_GNUTLS:
524 backend = "gnutls";
525 #ifdef PHP_HTTP_HAVE_GNUTLS
526 {
527 gnutls_session_t sess = ti->internals;
528 char *desc;
529
530 array_init(subarray);
531 if ((desc = gnutls_session_get_desc(sess))) {
532 add_assoc_string_ex(subarray, ZEND_STRS("desc"), desc, 1);
533 gnutls_free(desc);
534 }
535 add_assoc_bool_ex(subarray, ZEND_STRS("resumed"), gnutls_session_is_resumed(sess));
536 }
537 #endif
538 break;
539 case CURLSSLBACKEND_NSS:
540 backend = "nss";
541 break;
542 #if !PHP_HTTP_CURL_VERSION(7,39,0)
543 case CURLSSLBACKEND_QSOSSL:
544 backend = "qsossl";
545 break;
546 #endif
547 case CURLSSLBACKEND_GSKIT:
548 backend = "gskit";
549 break;
550 case CURLSSLBACKEND_POLARSSL:
551 backend = "polarssl";
552 break;
553 case CURLSSLBACKEND_CYASSL:
554 backend = "cyassl";
555 break;
556 case CURLSSLBACKEND_SCHANNEL:
557 backend = "schannel";
558 break;
559 case CURLSSLBACKEND_DARWINSSL:
560 backend = "darwinssl";
561 break;
562 default:
563 backend = "unknown";
564 }
565 add_assoc_string_ex(ti_array, ZEND_STRS("backend"), estrdup(backend), 0);
566 add_assoc_zval_ex(ti_array, ZEND_STRS("internals"), subarray);
567 add_assoc_zval_ex(&array, "tls_session", sizeof("tls_session"), ti_array);
568 }
569 }
570 #endif
571
572 #if PHP_HTTP_CURL_VERSION(7,19,1) && defined(PHP_HTTP_HAVE_OPENSSL)
573 {
574 int i;
575 zval *ci_array;
576 struct curl_certinfo *ci;
577 char *colon, *keyname;
578
579 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CERTINFO, &ci)) {
580 MAKE_STD_ZVAL(ci_array);
581 array_init(ci_array);
582
583 for (i = 0; i < ci->num_of_certs; ++i) {
584 s = ci->certinfo[i];
585
586 MAKE_STD_ZVAL(subarray);
587 array_init(subarray);
588 for (p = s; p; p = p->next) {
589 if (p->data) {
590 if ((colon = strchr(p->data, ':'))) {
591 keyname = estrndup(p->data, colon - p->data);
592 add_assoc_string_ex(subarray, keyname, colon - p->data + 1, colon + 1, 1);
593 efree(keyname);
594 } else {
595 add_next_index_string(subarray, p->data, 1);
596 }
597 }
598 }
599 add_next_index_zval(ci_array, subarray);
600 }
601 add_assoc_zval_ex(&array, "certinfo", sizeof("certinfo"), ci_array);
602 }
603 }
604 #endif
605 {
606 php_http_curle_storage_t *st = php_http_curle_get_storage(ch);
607
608 add_assoc_long_ex(&array, "curlcode", sizeof("curlcode"), st->errorcode);
609 add_assoc_string_ex(&array, "error", sizeof("error"), st->errorbuffer, 1);
610 }
611
612 return SUCCESS;
613 }
614
615 static int compare_queue(php_http_client_enqueue_t *e, void *handle)
616 {
617 return handle == ((php_http_client_curl_handler_t *) e->opaque)->handle;
618 }
619
620 static void php_http_curlm_responsehandler(php_http_client_t *context)
621 {
622 int remaining = 0;
623 php_http_client_enqueue_t *enqueue;
624 php_http_client_curl_t *curl = context->ctx;
625 TSRMLS_FETCH_FROM_CTX(context->ts);
626
627 do {
628 CURLMsg *msg = curl_multi_info_read(curl->handle, &remaining);
629
630 if (msg && CURLMSG_DONE == msg->msg) {
631 if (CURLE_OK != msg->data.result) {
632 php_http_curle_storage_t *st = php_http_curle_get_storage(msg->easy_handle);
633 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s; %s (%s)", curl_easy_strerror(st->errorcode = msg->data.result), STR_PTR(st->errorbuffer), STR_PTR(st->url));
634 }
635
636 if ((enqueue = php_http_client_enqueued(context, msg->easy_handle, compare_queue))) {
637 php_http_client_curl_handler_t *handler = enqueue->opaque;
638
639 context->callback.response.func(context->callback.response.arg, context, &handler->queue, &handler->request.message, &handler->response.message);
640 }
641 }
642 } while (remaining);
643 }
644
645 #if PHP_HTTP_HAVE_EVENT
646
647 typedef struct php_http_curlm_event {
648 struct event evnt;
649 php_http_client_t *context;
650 } php_http_curlm_event_t;
651
652 static inline int etoca(short action) {
653 switch (action & (EV_READ|EV_WRITE)) {
654 case EV_READ:
655 return CURL_CSELECT_IN;
656 break;
657 case EV_WRITE:
658 return CURL_CSELECT_OUT;
659 break;
660 case EV_READ|EV_WRITE:
661 return CURL_CSELECT_IN|CURL_CSELECT_OUT;
662 break;
663 default:
664 return 0;
665 }
666 }
667
668 static void php_http_curlm_timeout_callback(int socket, short action, void *event_data)
669 {
670 php_http_client_t *context = event_data;
671 php_http_client_curl_t *curl = context->ctx;
672
673 #if DBG_EVENTS
674 fprintf(stderr, "T");
675 #endif
676 if (curl->useevents) {
677 CURLMcode rc;
678 TSRMLS_FETCH_FROM_CTX(context->ts);
679
680 /* ignore and use -1,0 on timeout */
681 (void) socket;
682 (void) action;
683
684 while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle, CURL_SOCKET_TIMEOUT, 0, &curl->unfinished)));
685
686 if (CURLM_OK != rc) {
687 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", curl_multi_strerror(rc));
688 }
689
690 php_http_curlm_responsehandler(context);
691 }
692 }
693
694 static void php_http_curlm_event_callback(int socket, short action, void *event_data)
695 {
696 php_http_client_t *context = event_data;
697 php_http_client_curl_t *curl = context->ctx;
698
699 #if DBG_EVENTS
700 fprintf(stderr, "E");
701 #endif
702 if (curl->useevents) {
703 CURLMcode rc = CURLM_OK;
704 TSRMLS_FETCH_FROM_CTX(context->ts);
705
706 while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle, socket, etoca(action), &curl->unfinished)));
707
708 if (CURLM_OK != rc) {
709 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", curl_multi_strerror(rc));
710 }
711
712 php_http_curlm_responsehandler(context);
713
714 /* remove timeout if there are no transfers left */
715 if (!curl->unfinished && event_initialized(curl->timeout) && event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
716 event_del(curl->timeout);
717 }
718 }
719 }
720
721 static int php_http_curlm_socket_callback(CURL *easy, curl_socket_t sock, int action, void *socket_data, void *assign_data)
722 {
723 php_http_client_t *context = socket_data;
724 php_http_client_curl_t *curl = context->ctx;
725
726 #if DBG_EVENTS
727 fprintf(stderr, "S");
728 #endif
729 if (curl->useevents) {
730 int events = EV_PERSIST;
731 php_http_curlm_event_t *ev = assign_data;
732 TSRMLS_FETCH_FROM_CTX(context->ts);
733
734 if (!ev) {
735 ev = ecalloc(1, sizeof(php_http_curlm_event_t));
736 ev->context = context;
737 curl_multi_assign(curl->handle, sock, ev);
738 } else {
739 event_del(&ev->evnt);
740 }
741
742 switch (action) {
743 case CURL_POLL_IN:
744 events |= EV_READ;
745 break;
746 case CURL_POLL_OUT:
747 events |= EV_WRITE;
748 break;
749 case CURL_POLL_INOUT:
750 events |= EV_READ|EV_WRITE;
751 break;
752
753 case CURL_POLL_REMOVE:
754 efree(ev);
755 /* no break */
756 case CURL_POLL_NONE:
757 return 0;
758
759 default:
760 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown socket action %d", action);
761 return -1;
762 }
763
764 event_assign(&ev->evnt, curl->evbase, sock, events, php_http_curlm_event_callback, context);
765 event_add(&ev->evnt, NULL);
766 }
767
768 return 0;
769 }
770
771 static void php_http_curlm_timer_callback(CURLM *multi, long timeout_ms, void *timer_data)
772 {
773 php_http_client_t *context = timer_data;
774 php_http_client_curl_t *curl = context->ctx;
775
776 #if DBG_EVENTS
777 fprintf(stderr, "\ntimer <- timeout_ms: %ld\n", timeout_ms);
778 #endif
779 if (curl->useevents) {
780
781 if (timeout_ms < 0) {
782 php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, context);
783 } else if (timeout_ms > 0 || !event_initialized(curl->timeout) || !event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
784 struct timeval timeout;
785
786 if (!event_initialized(curl->timeout)) {
787 event_assign(curl->timeout, curl->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, context);
788 } else if (event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
789 event_del(curl->timeout);
790 }
791
792 timeout.tv_sec = timeout_ms / 1000;
793 timeout.tv_usec = (timeout_ms % 1000) * 1000;
794
795 event_add(curl->timeout, &timeout);
796 }
797 }
798 }
799
800 #endif /* HAVE_EVENT */
801
802 /* curl options */
803
804 static php_http_options_t php_http_curle_options;
805
806 #define PHP_HTTP_CURLE_OPTION_CHECK_STRLEN 0x0001
807 #define PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR 0x0002
808 #define PHP_HTTP_CURLE_OPTION_TRANSFORM_MS 0x0004
809
810 static STATUS php_http_curle_option_set_ssl_verifyhost(php_http_option_t *opt, zval *val, void *userdata)
811 {
812 php_http_client_curl_handler_t *curl = userdata;
813 CURL *ch = curl->handle;
814
815 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_SSL_VERIFYHOST, Z_BVAL_P(val) ? 2 : 0)) {
816 return FAILURE;
817 }
818 return SUCCESS;
819 }
820
821 static STATUS php_http_curle_option_set_cookiestore(php_http_option_t *opt, zval *val, void *userdata)
822 {
823 php_http_client_curl_handler_t *curl = userdata;
824 CURL *ch = curl->handle;
825 php_http_curle_storage_t *storage = php_http_curle_get_storage(curl->handle);
826
827 if (storage->cookiestore) {
828 pefree(storage->cookiestore, 1);
829 }
830 if (val && Z_STRLEN_P(val)) {
831 storage->cookiestore = pestrndup(Z_STRVAL_P(val), Z_STRLEN_P(val), 1);
832 } else {
833 storage->cookiestore = NULL;
834 }
835 if ( CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIEFILE, storage->cookiestore)
836 || CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIEJAR, storage->cookiestore)
837 ) {
838 return FAILURE;
839 }
840 return SUCCESS;
841 }
842
843 static STATUS php_http_curle_option_set_cookies(php_http_option_t *opt, zval *val, void *userdata)
844 {
845 php_http_client_curl_handler_t *curl = userdata;
846 CURL *ch = curl->handle;
847 TSRMLS_FETCH_FROM_CTX(curl->client->ts);
848
849 if (val && Z_TYPE_P(val) != IS_NULL) {
850 if (curl->options.encode_cookies) {
851 if (SUCCESS == php_http_url_encode_hash_ex(HASH_OF(val), &curl->options.cookies, ZEND_STRL(";"), ZEND_STRL("="), NULL, 0 TSRMLS_CC)) {
852 php_http_buffer_fix(&curl->options.cookies);
853 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, curl->options.cookies.data)) {
854 return FAILURE;
855 }
856 } else {
857 return FAILURE;
858 }
859 } else {
860 HashPosition pos;
861 php_http_array_hashkey_t cookie_key = php_http_array_hashkey_init(0);
862 zval **cookie_val;
863
864 FOREACH_KEYVAL(pos, val, cookie_key, cookie_val) {
865 zval *zv = php_http_ztyp(IS_STRING, *cookie_val);
866
867 php_http_array_hashkey_stringify(&cookie_key);
868 php_http_buffer_appendf(&curl->options.cookies, "%s=%s; ", cookie_key.str, Z_STRVAL_P(zv));
869 php_http_array_hashkey_stringfree(&cookie_key);
870
871 zval_ptr_dtor(&zv);
872 }
873
874 php_http_buffer_fix(&curl->options.cookies);
875 if (curl->options.cookies.used) {
876 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, curl->options.cookies.data)) {
877 return FAILURE;
878 }
879 }
880 }
881 } else {
882 php_http_buffer_reset(&curl->options.cookies);
883 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, NULL)) {
884 return FAILURE;
885 }
886 }
887 return SUCCESS;
888 }
889
890 static STATUS php_http_curle_option_set_encodecookies(php_http_option_t *opt, zval *val, void *userdata)
891 {
892 php_http_client_curl_handler_t *curl = userdata;
893
894 curl->options.encode_cookies = Z_BVAL_P(val);
895 return SUCCESS;
896 }
897
898 static STATUS php_http_curle_option_set_lastmodified(php_http_option_t *opt, zval *val, void *userdata)
899 {
900 php_http_client_curl_handler_t *curl = userdata;
901 CURL *ch = curl->handle;
902 TSRMLS_FETCH_FROM_CTX(curl->client->ts);
903
904 if (Z_LVAL_P(val)) {
905 if (Z_LVAL_P(val) > 0) {
906 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, Z_LVAL_P(val))) {
907 return FAILURE;
908 }
909 } else {
910 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, (long) PHP_HTTP_G->env.request.time + Z_LVAL_P(val))) {
911 return FAILURE;
912 }
913 }
914 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMECONDITION, (long) (curl->options.range_request ? CURL_TIMECOND_IFUNMODSINCE : CURL_TIMECOND_IFMODSINCE))) {
915 return FAILURE;
916 }
917 } else {
918 if ( CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, 0)
919 || CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMECONDITION, 0)
920 ) {
921 return FAILURE;
922 }
923 }
924 return SUCCESS;
925 }
926
927 static STATUS php_http_curle_option_set_compress(php_http_option_t *opt, zval *val, void *userdata)
928 {
929 php_http_client_curl_handler_t *curl = userdata;
930
931 if (Z_BVAL_P(val)) {
932 curl->options.headers = curl_slist_append(curl->options.headers, "Accept-Encoding: gzip;q=1.0,deflate;q=0.5");
933 }
934 return SUCCESS;
935 }
936
937 static STATUS php_http_curle_option_set_etag(php_http_option_t *opt, zval *val, void *userdata)
938 {
939 php_http_client_curl_handler_t *curl = userdata;
940 php_http_buffer_t header;
941
942 if (Z_STRLEN_P(val)) {
943 zend_bool is_quoted = !((Z_STRVAL_P(val)[0] != '"') || (Z_STRVAL_P(val)[Z_STRLEN_P(val)-1] != '"'));
944 php_http_buffer_init(&header);
945 php_http_buffer_appendf(&header, is_quoted?"%s: %s":"%s: \"%s\"", curl->options.range_request?"If-Match":"If-None-Match", Z_STRVAL_P(val));
946 php_http_buffer_fix(&header);
947 curl->options.headers = curl_slist_append(curl->options.headers, header.data);
948 php_http_buffer_dtor(&header);
949 }
950 return SUCCESS;
951 }
952
953 static STATUS php_http_curle_option_set_range(php_http_option_t *opt, zval *val, void *userdata)
954 {
955 php_http_client_curl_handler_t *curl = userdata;
956 CURL *ch = curl->handle;
957 TSRMLS_FETCH_FROM_CTX(curl->client->ts);
958
959 php_http_buffer_reset(&curl->options.ranges);
960
961 if (val && Z_TYPE_P(val) != IS_NULL) {
962 HashPosition pos;
963 zval **rr, **rb, **re;
964
965 FOREACH_VAL(pos, val, rr) {
966 if (Z_TYPE_PP(rr) == IS_ARRAY) {
967 if (2 == php_http_array_list(Z_ARRVAL_PP(rr) TSRMLS_CC, 2, &rb, &re)) {
968 if ( ((Z_TYPE_PP(rb) == IS_LONG) || ((Z_TYPE_PP(rb) == IS_STRING) && is_numeric_string(Z_STRVAL_PP(rb), Z_STRLEN_PP(rb), NULL, NULL, 1))) &&
969 ((Z_TYPE_PP(re) == IS_LONG) || ((Z_TYPE_PP(re) == IS_STRING) && is_numeric_string(Z_STRVAL_PP(re), Z_STRLEN_PP(re), NULL, NULL, 1)))) {
970 zval *rbl = php_http_ztyp(IS_LONG, *rb);
971 zval *rel = php_http_ztyp(IS_LONG, *re);
972
973 if ((Z_LVAL_P(rbl) >= 0) && (Z_LVAL_P(rel) >= 0)) {
974 php_http_buffer_appendf(&curl->options.ranges, "%ld-%ld,", Z_LVAL_P(rbl), Z_LVAL_P(rel));
975 }
976 zval_ptr_dtor(&rbl);
977 zval_ptr_dtor(&rel);
978 }
979
980 }
981 }
982 }
983
984 if (curl->options.ranges.used) {
985 curl->options.range_request = 1;
986 /* ditch last comma */
987 curl->options.ranges.data[curl->options.ranges.used - 1] = '\0';
988 }
989 }
990
991 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RANGE, curl->options.ranges.data)) {
992 return FAILURE;
993 }
994 return SUCCESS;
995 }
996
997 static STATUS php_http_curle_option_set_resume(php_http_option_t *opt, zval *val, void *userdata)
998 {
999 php_http_client_curl_handler_t *curl = userdata;
1000 CURL *ch = curl->handle;
1001
1002 if (Z_LVAL_P(val) > 0) {
1003 curl->options.range_request = 1;
1004 }
1005 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESUME_FROM, Z_LVAL_P(val))) {
1006 return FAILURE;
1007 }
1008 return SUCCESS;
1009 }
1010
1011 static STATUS php_http_curle_option_set_retrydelay(php_http_option_t *opt, zval *val, void *userdata)
1012 {
1013 php_http_client_curl_handler_t *curl = userdata;
1014
1015 curl->options.retry.delay = Z_DVAL_P(val);
1016 return SUCCESS;
1017 }
1018
1019 static STATUS php_http_curle_option_set_retrycount(php_http_option_t *opt, zval *val, void *userdata)
1020 {
1021 php_http_client_curl_handler_t *curl = userdata;
1022
1023 curl->options.retry.count = Z_LVAL_P(val);
1024 return SUCCESS;
1025 }
1026
1027 static STATUS php_http_curle_option_set_redirect(php_http_option_t *opt, zval *val, void *userdata)
1028 {
1029 php_http_client_curl_handler_t *curl = userdata;
1030 CURL *ch = curl->handle;
1031
1032 if ( CURLE_OK != curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, Z_LVAL_P(val) ? 1L : 0L)
1033 || CURLE_OK != curl_easy_setopt(ch, CURLOPT_MAXREDIRS, curl->options.redirects = Z_LVAL_P(val))
1034 ) {
1035 return FAILURE;
1036 }
1037 return SUCCESS;
1038 }
1039
1040 static STATUS php_http_curle_option_set_portrange(php_http_option_t *opt, zval *val, void *userdata)
1041 {
1042 php_http_client_curl_handler_t *curl = userdata;
1043 CURL *ch = curl->handle;
1044 long localport = 0, localportrange = 0;
1045 TSRMLS_FETCH_FROM_CTX(curl->client->ts);
1046
1047 if (val && Z_TYPE_P(val) != IS_NULL) {
1048 zval **z_port_start, *zps_copy = NULL, **z_port_end, *zpe_copy = NULL;
1049
1050 switch (php_http_array_list(Z_ARRVAL_P(val) TSRMLS_CC, 2, &z_port_start, &z_port_end)) {
1051 case 2:
1052 zps_copy = php_http_ztyp(IS_LONG, *z_port_start);
1053 zpe_copy = php_http_ztyp(IS_LONG, *z_port_end);
1054 localportrange = labs(Z_LVAL_P(zps_copy)-Z_LVAL_P(zpe_copy))+1L;
1055 /* no break */
1056 case 1:
1057 if (!zps_copy) {
1058 zps_copy = php_http_ztyp(IS_LONG, *z_port_start);
1059 }
1060 localport = (zpe_copy && Z_LVAL_P(zpe_copy) > 0) ? MIN(Z_LVAL_P(zps_copy), Z_LVAL_P(zpe_copy)) : Z_LVAL_P(zps_copy);
1061 zval_ptr_dtor(&zps_copy);
1062 if (zpe_copy) {
1063 zval_ptr_dtor(&zpe_copy);
1064 }
1065 break;
1066 default:
1067 break;
1068 }
1069 }
1070 if ( CURLE_OK != curl_easy_setopt(ch, CURLOPT_LOCALPORT, localport)
1071 || CURLE_OK != curl_easy_setopt(ch, CURLOPT_LOCALPORTRANGE, localportrange)
1072 ) {
1073 return FAILURE;
1074 }
1075 return SUCCESS;
1076 }
1077
1078 #if PHP_HTTP_CURL_VERSION(7,21,3)
1079 static STATUS php_http_curle_option_set_resolve(php_http_option_t *opt, zval *val, void *userdata)
1080 {
1081 php_http_client_curl_handler_t *curl = userdata;
1082 CURL *ch = curl->handle;
1083 TSRMLS_FETCH_FROM_CTX(curl->client->ts);
1084
1085 if (val && Z_TYPE_P(val) != IS_NULL) {
1086 php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
1087 HashPosition pos;
1088 zval **data;
1089
1090 FOREACH_KEYVAL(pos, val, key, data) {
1091 zval *cpy = php_http_ztyp(IS_STRING, *data);
1092 curl->options.resolve = curl_slist_append(curl->options.resolve, Z_STRVAL_P(cpy));
1093 zval_ptr_dtor(&cpy);
1094 }
1095
1096 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESOLVE, curl->options.resolve)) {
1097 return FAILURE;
1098 }
1099 } else {
1100 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESOLVE, NULL)) {
1101 return FAILURE;
1102 }
1103 }
1104 return SUCCESS;
1105 }
1106 #endif
1107
1108 static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC)
1109 {
1110 php_http_option_t *opt;
1111
1112 /* proxy */
1113 if ((opt = php_http_option_register(registry, ZEND_STRL("proxyhost"), CURLOPT_PROXY, IS_STRING))) {
1114 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1115 }
1116 php_http_option_register(registry, ZEND_STRL("proxytype"), CURLOPT_PROXYTYPE, IS_LONG);
1117 php_http_option_register(registry, ZEND_STRL("proxyport"), CURLOPT_PROXYPORT, IS_LONG);
1118 if ((opt = php_http_option_register(registry, ZEND_STRL("proxyauth"), CURLOPT_PROXYUSERPWD, IS_STRING))) {
1119 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1120 }
1121 if ((opt = php_http_option_register(registry, ZEND_STRL("proxyauthtype"), CURLOPT_PROXYAUTH, IS_LONG))) {
1122 Z_LVAL(opt->defval) = CURLAUTH_ANYSAFE;
1123 }
1124 php_http_option_register(registry, ZEND_STRL("proxytunnel"), CURLOPT_HTTPPROXYTUNNEL, IS_BOOL);
1125 #if PHP_HTTP_CURL_VERSION(7,19,4)
1126 php_http_option_register(registry, ZEND_STRL("noproxy"), CURLOPT_NOPROXY, IS_STRING);
1127 #endif
1128
1129 /* dns */
1130 if ((opt = php_http_option_register(registry, ZEND_STRL("dns_cache_timeout"), CURLOPT_DNS_CACHE_TIMEOUT, IS_LONG))) {
1131 Z_LVAL(opt->defval) = 60;
1132 }
1133 php_http_option_register(registry, ZEND_STRL("ipresolve"), CURLOPT_IPRESOLVE, IS_LONG);
1134 #if PHP_HTTP_CURL_VERSION(7,21,3)
1135 if ((opt = php_http_option_register(registry, ZEND_STRL("resolve"), CURLOPT_RESOLVE, IS_ARRAY))) {
1136 opt->setter = php_http_curle_option_set_resolve;
1137 }
1138 #endif
1139 #if PHP_HTTP_HAVE_ARES
1140 # if PHP_HTTP_CURL_VERSION(7,24,0)
1141 if ((opt = php_http_option_register(registry, ZEND_STRL("dns_servers"), CURLOPT_DNS_SERVERS, IS_STRING))) {
1142 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1143 }
1144 # endif
1145 # if PHP_HTTP_CURL_VERSION(7,33,0)
1146 if ((opt = php_http_option_register(registry, ZEND_STRL("dns_interface"), CURLOPT_DNS_INTERFACE, IS_STRING))) {
1147 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1148 }
1149 if ((opt = php_http_option_register(registry, ZEND_STRL("dns_local_ip4"), CURLOPT_DNS_LOCAL_IP4, IS_STRING))) {
1150 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1151 }
1152 if ((opt = php_http_option_register(registry, ZEND_STRL("dns_local_ip6"), CURLOPT_DNS_LOCAL_IP6, IS_STRING))) {
1153 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1154 }
1155 # endif
1156 #endif
1157
1158 /* limits */
1159 php_http_option_register(registry, ZEND_STRL("low_speed_limit"), CURLOPT_LOW_SPEED_LIMIT, IS_LONG);
1160 php_http_option_register(registry, ZEND_STRL("low_speed_time"), CURLOPT_LOW_SPEED_TIME, IS_LONG);
1161
1162 /* LSF weirdance
1163 php_http_option_register(registry, ZEND_STRL("max_send_speed"), CURLOPT_MAX_SEND_SPEED_LARGE, IS_LONG);
1164 php_http_option_register(registry, ZEND_STRL("max_recv_speed"), CURLOPT_MAX_RECV_SPEED_LARGE, IS_LONG);
1165 */
1166
1167 /* connection handling */
1168 /* crashes
1169 if ((opt = php_http_option_register(registry, ZEND_STRL("maxconnects"), CURLOPT_MAXCONNECTS, IS_LONG))) {
1170 Z_LVAL(opt->defval) = 5;
1171 }
1172 */
1173 php_http_option_register(registry, ZEND_STRL("fresh_connect"), CURLOPT_FRESH_CONNECT, IS_BOOL);
1174 php_http_option_register(registry, ZEND_STRL("forbid_reuse"), CURLOPT_FORBID_REUSE, IS_BOOL);
1175
1176 /* outgoing interface */
1177 php_http_option_register(registry, ZEND_STRL("interface"), CURLOPT_INTERFACE, IS_STRING);
1178 if ((opt = php_http_option_register(registry, ZEND_STRL("portrange"), CURLOPT_LOCALPORT, IS_ARRAY))) {
1179 opt->setter = php_http_curle_option_set_portrange;
1180 }
1181
1182 /* another endpoint port */
1183 php_http_option_register(registry, ZEND_STRL("port"), CURLOPT_PORT, IS_LONG);
1184
1185 /* RFC4007 zone_id */
1186 #if PHP_HTTP_CURL_VERSION(7,19,0)
1187 php_http_option_register(registry, ZEND_STRL("address_scope"), CURLOPT_ADDRESS_SCOPE, IS_LONG);
1188 #endif
1189
1190 /* auth */
1191 if ((opt = php_http_option_register(registry, ZEND_STRL("httpauth"), CURLOPT_USERPWD, IS_STRING))) {
1192 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1193 }
1194 if ((opt = php_http_option_register(registry, ZEND_STRL("httpauthtype"), CURLOPT_HTTPAUTH, IS_LONG))) {
1195 Z_LVAL(opt->defval) = CURLAUTH_ANYSAFE;
1196 }
1197
1198 /* redirects */
1199 if ((opt = php_http_option_register(registry, ZEND_STRL("redirect"), CURLOPT_FOLLOWLOCATION, IS_LONG))) {
1200 opt->setter = php_http_curle_option_set_redirect;
1201 }
1202 php_http_option_register(registry, ZEND_STRL("unrestricted_auth"), CURLOPT_UNRESTRICTED_AUTH, IS_BOOL);
1203 #if PHP_HTTP_CURL_VERSION(7,19,1)
1204 php_http_option_register(registry, ZEND_STRL("postredir"), CURLOPT_POSTREDIR, IS_LONG);
1205 #endif
1206
1207 /* retries */
1208 if ((opt = php_http_option_register(registry, ZEND_STRL("retrycount"), 0, IS_LONG))) {
1209 opt->setter = php_http_curle_option_set_retrycount;
1210 }
1211 if ((opt = php_http_option_register(registry, ZEND_STRL("retrydelay"), 0, IS_DOUBLE))) {
1212 opt->setter = php_http_curle_option_set_retrydelay;
1213 }
1214
1215 /* referer */
1216 if ((opt = php_http_option_register(registry, ZEND_STRL("referer"), CURLOPT_REFERER, IS_STRING))) {
1217 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1218 }
1219 if ((opt = php_http_option_register(registry, ZEND_STRL("autoreferer"), CURLOPT_AUTOREFERER, IS_BOOL))) {
1220 ZVAL_BOOL(&opt->defval, 1);
1221 }
1222
1223 /* useragent */
1224 if ((opt = php_http_option_register(registry, ZEND_STRL("useragent"), CURLOPT_USERAGENT, IS_STRING))) {
1225 /* don't check strlen, to allow sending no useragent at all */
1226 ZVAL_STRING(&opt->defval,
1227 "PECL_HTTP/" PHP_PECL_HTTP_VERSION " "
1228 "PHP/" PHP_VERSION " "
1229 "libcurl/" LIBCURL_VERSION
1230 , 0);
1231 }
1232
1233 /* resume */
1234 if ((opt = php_http_option_register(registry, ZEND_STRL("resume"), CURLOPT_RESUME_FROM, IS_LONG))) {
1235 opt->setter = php_http_curle_option_set_resume;
1236 }
1237 /* ranges */
1238 if ((opt = php_http_option_register(registry, ZEND_STRL("range"), CURLOPT_RANGE, IS_ARRAY))) {
1239 opt->setter = php_http_curle_option_set_range;
1240 }
1241
1242 /* etag */
1243 if ((opt = php_http_option_register(registry, ZEND_STRL("etag"), 0, IS_STRING))) {
1244 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1245 opt->setter = php_http_curle_option_set_etag;
1246 }
1247
1248 /* compression */
1249 if ((opt = php_http_option_register(registry, ZEND_STRL("compress"), 0, IS_BOOL))) {
1250 opt->setter = php_http_curle_option_set_compress;
1251 }
1252
1253 /* lastmodified */
1254 if ((opt = php_http_option_register(registry, ZEND_STRL("lastmodified"), 0, IS_LONG))) {
1255 opt->setter = php_http_curle_option_set_lastmodified;
1256 }
1257
1258 /* cookies */
1259 if ((opt = php_http_option_register(registry, ZEND_STRL("encodecookies"), 0, IS_BOOL))) {
1260 opt->setter = php_http_curle_option_set_encodecookies;
1261 ZVAL_BOOL(&opt->defval, 1);
1262 }
1263 if ((opt = php_http_option_register(registry, ZEND_STRL("cookies"), 0, IS_ARRAY))) {
1264 opt->setter = php_http_curle_option_set_cookies;
1265 }
1266
1267 /* cookiesession, don't load session cookies from cookiestore */
1268 php_http_option_register(registry, ZEND_STRL("cookiesession"), CURLOPT_COOKIESESSION, IS_BOOL);
1269 /* cookiestore, read initial cookies from that file and store cookies back into that file */
1270 if ((opt = php_http_option_register(registry, ZEND_STRL("cookiestore"), 0, IS_STRING))) {
1271 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1272 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1273 opt->setter = php_http_curle_option_set_cookiestore;
1274 }
1275
1276 /* maxfilesize */
1277 php_http_option_register(registry, ZEND_STRL("maxfilesize"), CURLOPT_MAXFILESIZE, IS_LONG);
1278
1279 /* http protocol version */
1280 php_http_option_register(registry, ZEND_STRL("protocol"), CURLOPT_HTTP_VERSION, IS_LONG);
1281
1282 /* timeouts */
1283 if ((opt = php_http_option_register(registry, ZEND_STRL("timeout"), CURLOPT_TIMEOUT_MS, IS_DOUBLE))) {
1284 opt->flags |= PHP_HTTP_CURLE_OPTION_TRANSFORM_MS;
1285 }
1286 if ((opt = php_http_option_register(registry, ZEND_STRL("connecttimeout"), CURLOPT_CONNECTTIMEOUT_MS, IS_DOUBLE))) {
1287 opt->flags |= PHP_HTTP_CURLE_OPTION_TRANSFORM_MS;
1288 Z_DVAL(opt->defval) = 3;
1289 }
1290 #if PHP_HTTP_CURL_VERSION(7,36,0)
1291 if ((opt = php_http_option_register(registry, ZEND_STRL("expect_100_timeout"), CURLOPT_EXPECT_100_TIMEOUT_MS, IS_DOUBLE))) {
1292 opt->flags |= PHP_HTTP_CURLE_OPTION_TRANSFORM_MS;
1293 Z_DVAL(opt->defval) = 1;
1294 }
1295 #endif
1296
1297 /* tcp */
1298 php_http_option_register(registry, ZEND_STRL("tcp_nodelay"), CURLOPT_TCP_NODELAY, IS_BOOL);
1299 #if PHP_HTTP_CURL_VERSION(7,25,0)
1300 php_http_option_register(registry, ZEND_STRL("tcp_keepalive"), CURLOPT_TCP_KEEPALIVE, IS_BOOL);
1301 if ((opt = php_http_option_register(registry, ZEND_STRL("tcp_keepidle"), CURLOPT_TCP_KEEPIDLE, IS_LONG))) {
1302 Z_LVAL(opt->defval) = 60;
1303 }
1304 if ((opt = php_http_option_register(registry, ZEND_STRL("tcp_keepintvl"), CURLOPT_TCP_KEEPINTVL, IS_LONG))) {
1305 Z_LVAL(opt->defval) = 60;
1306 }
1307 #endif
1308
1309 /* ssl */
1310 if ((opt = php_http_option_register(registry, ZEND_STRL("ssl"), 0, IS_ARRAY))) {
1311 registry = &opt->suboptions;
1312
1313 if ((opt = php_http_option_register(registry, ZEND_STRL("cert"), CURLOPT_SSLCERT, IS_STRING))) {
1314 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1315 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1316 }
1317 if ((opt = php_http_option_register(registry, ZEND_STRL("certtype"), CURLOPT_SSLCERTTYPE, IS_STRING))) {
1318 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1319 ZVAL_STRING(&opt->defval, "PEM", 0);
1320 }
1321 if ((opt = php_http_option_register(registry, ZEND_STRL("key"), CURLOPT_SSLKEY, IS_STRING))) {
1322 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1323 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1324 }
1325 if ((opt = php_http_option_register(registry, ZEND_STRL("keytype"), CURLOPT_SSLKEYTYPE, IS_STRING))) {
1326 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1327 ZVAL_STRING(&opt->defval, "PEM", 0);
1328 }
1329 if ((opt = php_http_option_register(registry, ZEND_STRL("keypasswd"), CURLOPT_SSLKEYPASSWD, IS_STRING))) {
1330 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1331 }
1332 php_http_option_register(registry, ZEND_STRL("engine"), CURLOPT_SSLENGINE, IS_STRING);
1333 php_http_option_register(registry, ZEND_STRL("version"), CURLOPT_SSLVERSION, IS_LONG);
1334 if ((opt = php_http_option_register(registry, ZEND_STRL("verifypeer"), CURLOPT_SSL_VERIFYPEER, IS_BOOL))) {
1335 ZVAL_BOOL(&opt->defval, 1);
1336 }
1337 if ((opt = php_http_option_register(registry, ZEND_STRL("verifyhost"), CURLOPT_SSL_VERIFYHOST, IS_BOOL))) {
1338 ZVAL_BOOL(&opt->defval, 1);
1339 opt->setter = php_http_curle_option_set_ssl_verifyhost;
1340 }
1341 php_http_option_register(registry, ZEND_STRL("cipher_list"), CURLOPT_SSL_CIPHER_LIST, IS_STRING);
1342 if ((opt = php_http_option_register(registry, ZEND_STRL("cainfo"), CURLOPT_CAINFO, IS_STRING))) {
1343 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1344 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1345 #ifdef PHP_HTTP_CURL_CAINFO
1346 ZVAL_STRING(&opt->defval, PHP_HTTP_CURL_CAINFO, 0);
1347 #endif
1348 }
1349 if ((opt = php_http_option_register(registry, ZEND_STRL("capath"), CURLOPT_CAPATH, IS_STRING))) {
1350 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1351 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1352 }
1353 if ((opt = php_http_option_register(registry, ZEND_STRL("random_file"), CURLOPT_RANDOM_FILE, IS_STRING))) {
1354 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1355 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1356 }
1357 if ((opt = php_http_option_register(registry, ZEND_STRL("egdsocket"), CURLOPT_EGDSOCKET, IS_STRING))) {
1358 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1359 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1360 }
1361 #if PHP_HTTP_CURL_VERSION(7,19,0)
1362 if ((opt = php_http_option_register(registry, ZEND_STRL("issuercert"), CURLOPT_ISSUERCERT, IS_STRING))) {
1363 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1364 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1365 }
1366 # ifdef PHP_HTTP_HAVE_OPENSSL
1367 if ((opt = php_http_option_register(registry, ZEND_STRL("crlfile"), CURLOPT_CRLFILE, IS_STRING))) {
1368 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1369 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1370 }
1371 # endif
1372 #endif
1373 #if PHP_HTTP_CURL_VERSION(7,19,1) && defined(PHP_HTTP_HAVE_OPENSSL)
1374 php_http_option_register(registry, ZEND_STRL("certinfo"), CURLOPT_CERTINFO, IS_BOOL);
1375 #endif
1376 #if PHP_HTTP_CURL_VERSION(7,36,0)
1377 if ((opt = php_http_option_register(registry, ZEND_STRL("enable_npn"), CURLOPT_SSL_ENABLE_NPN, IS_BOOL))) {
1378 ZVAL_BOOL(&opt->defval, 1);
1379 }
1380 if ((opt = php_http_option_register(registry, ZEND_STRL("enable_alpn"), CURLOPT_SSL_ENABLE_ALPN, IS_BOOL))) {
1381 ZVAL_BOOL(&opt->defval, 1);
1382 }
1383 #endif
1384 }
1385 }
1386
1387 static zval *php_http_curle_get_option(php_http_option_t *opt, HashTable *options, void *userdata)
1388 {
1389 php_http_client_curl_handler_t *curl = userdata;
1390 zval *option;
1391
1392 if ((option = php_http_option_get(opt, options, NULL))) {
1393 option = php_http_ztyp(opt->type, option);
1394 zend_hash_quick_update(&curl->options.cache, opt->name.s, opt->name.l, opt->name.h, &option, sizeof(zval *), NULL);
1395 }
1396 return option;
1397 }
1398
1399 static STATUS php_http_curle_set_option(php_http_option_t *opt, zval *val, void *userdata)
1400 {
1401 php_http_client_curl_handler_t *curl = userdata;
1402 CURL *ch = curl->handle;
1403 zval tmp;
1404 CURLcode rc = CURLE_OK;
1405 STATUS rv = SUCCESS;
1406 TSRMLS_FETCH_FROM_CTX(curl->client->ts);
1407
1408 if (!val) {
1409 val = &opt->defval;
1410 }
1411
1412 switch (opt->type) {
1413 case IS_BOOL:
1414 if (opt->setter) {
1415 rv = opt->setter(opt, val, curl);
1416 } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, (long) Z_BVAL_P(val))) {
1417 rv = FAILURE;
1418 }
1419 break;
1420
1421 case IS_LONG:
1422 if (opt->setter) {
1423 rv = opt->setter(opt, val, curl);
1424 } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, Z_LVAL_P(val))) {
1425 rv = FAILURE;
1426 }
1427 break;
1428
1429 case IS_STRING:
1430 if (opt->setter) {
1431 rv = opt->setter(opt, val, curl);
1432 } else if ((opt->flags & PHP_HTTP_CURLE_OPTION_CHECK_STRLEN) && !Z_STRLEN_P(val)) {
1433 if (CURLE_OK != (rc = curl_easy_setopt(ch, opt->option, NULL))) {
1434 rv = FAILURE;
1435 }
1436 } else if ((opt->flags & PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR) && Z_STRVAL_P(val) && SUCCESS != php_check_open_basedir(Z_STRVAL_P(val) TSRMLS_CC)) {
1437 if (CURLE_OK != (rc = curl_easy_setopt(ch, opt->option, NULL))) {
1438 rv = FAILURE;
1439 }
1440 } else if (CURLE_OK != (rc = curl_easy_setopt(ch, opt->option, Z_STRVAL_P(val)))) {
1441 rv = FAILURE;
1442 }
1443 break;
1444
1445 case IS_DOUBLE:
1446 if (opt->flags & PHP_HTTP_CURLE_OPTION_TRANSFORM_MS) {
1447 tmp = *val;
1448 Z_DVAL(tmp) *= 1000;
1449 val = &tmp;
1450 }
1451 if (opt->setter) {
1452 rv = opt->setter(opt, val, curl);
1453 } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, (long) Z_DVAL_P(val))) {
1454 rv = FAILURE;
1455 }
1456 break;
1457
1458 case IS_ARRAY:
1459 if (opt->setter) {
1460 rv = opt->setter(opt, val, curl);
1461 } else if (Z_TYPE_P(val) != IS_NULL) {
1462 rv = php_http_options_apply(&opt->suboptions, Z_ARRVAL_P(val), curl);
1463 }
1464 break;
1465
1466 default:
1467 if (opt->setter) {
1468 rv = opt->setter(opt, val, curl);
1469 } else {
1470 rv = FAILURE;
1471 }
1472 break;
1473 }
1474 if (rv != SUCCESS) {
1475 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, curl_easy_strerror(rc));
1476 }
1477 return rv;
1478 }
1479
1480
1481 /* client ops */
1482
1483 static STATUS php_http_client_curl_handler_reset(php_http_client_curl_handler_t *curl)
1484 {
1485 CURL *ch = curl->handle;
1486 php_http_curle_storage_t *st;
1487
1488 if ((st = php_http_curle_get_storage(ch))) {
1489 if (st->url) {
1490 pefree(st->url, 1);
1491 st->url = NULL;
1492 }
1493 if (st->cookiestore) {
1494 pefree(st->cookiestore, 1);
1495 st->cookiestore = NULL;
1496 }
1497 st->errorbuffer[0] = '\0';
1498 }
1499
1500 curl_easy_setopt(ch, CURLOPT_URL, NULL);
1501 curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, NULL);
1502 curl_easy_setopt(ch, CURLOPT_HTTPGET, 1L);
1503 curl_easy_setopt(ch, CURLOPT_NOBODY, 0L);
1504 /* libcurl < 7.19.6 does not clear auth info with USERPWD set to NULL */
1505 #if PHP_HTTP_CURL_VERSION(7,19,1)
1506 curl_easy_setopt(ch, CURLOPT_PROXYUSERNAME, NULL);
1507 curl_easy_setopt(ch, CURLOPT_PROXYPASSWORD, NULL);
1508 curl_easy_setopt(ch, CURLOPT_USERNAME, NULL);
1509 curl_easy_setopt(ch, CURLOPT_PASSWORD, NULL);
1510 #endif
1511
1512 #if PHP_HTTP_CURL_VERSION(7,21,3)
1513 if (curl->options.resolve) {
1514 curl_slist_free_all(curl->options.resolve);
1515 curl->options.resolve = NULL;
1516 }
1517 #endif
1518 curl->options.retry.count = 0;
1519 curl->options.retry.delay = 0;
1520 curl->options.redirects = 0;
1521 curl->options.encode_cookies = 1;
1522
1523 if (curl->options.headers) {
1524 curl_slist_free_all(curl->options.headers);
1525 curl->options.headers = NULL;
1526 }
1527
1528 php_http_buffer_reset(&curl->options.cookies);
1529 php_http_buffer_reset(&curl->options.ranges);
1530
1531 return SUCCESS;
1532 }
1533
1534 static php_http_client_curl_handler_t *php_http_client_curl_handler_init(php_http_client_t *h, php_resource_factory_t *rf)
1535 {
1536 void *handle;
1537 php_http_client_curl_handler_t *handler;
1538 TSRMLS_FETCH_FROM_CTX(h->ts);
1539
1540 if (!(handle = php_resource_factory_handle_ctor(rf, NULL TSRMLS_CC))) {
1541 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to initialize curl handle");
1542 return NULL;
1543 }
1544
1545 handler = ecalloc(1, sizeof(*handler));
1546 handler->rf = rf;
1547 handler->client = h;
1548 handler->handle = handle;
1549 handler->request.buffer = php_http_buffer_init(NULL);
1550 handler->request.parser = php_http_message_parser_init(NULL TSRMLS_CC);
1551 handler->request.message = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
1552 handler->response.buffer = php_http_buffer_init(NULL);
1553 handler->response.parser = php_http_message_parser_init(NULL TSRMLS_CC);
1554 handler->response.message = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
1555 php_http_buffer_init(&handler->options.cookies);
1556 php_http_buffer_init(&handler->options.ranges);
1557 zend_hash_init(&handler->options.cache, 0, NULL, ZVAL_PTR_DTOR, 0);
1558
1559 #if defined(ZTS)
1560 curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1L);
1561 #endif
1562 curl_easy_setopt(handle, CURLOPT_HEADER, 0L);
1563 curl_easy_setopt(handle, CURLOPT_FILETIME, 1L);
1564 curl_easy_setopt(handle, CURLOPT_AUTOREFERER, 1L);
1565 curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L);
1566 curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0L);
1567 curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, NULL);
1568 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, php_http_curle_dummy_callback);
1569 curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, php_http_curle_raw_callback);
1570 curl_easy_setopt(handle, CURLOPT_READFUNCTION, php_http_curle_read_callback);
1571 curl_easy_setopt(handle, CURLOPT_IOCTLFUNCTION, php_http_curle_ioctl_callback);
1572 #if PHP_HTTP_CURL_VERSION(7,32,0)
1573 curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, php_http_curle_xferinfo_callback);
1574 curl_easy_setopt(handle, CURLOPT_XFERINFODATA, handler);
1575 #else
1576 curl_easy_setopt(handle, CURLOPT_PROGRESSFUNCTION, php_http_curle_progress_callback);
1577 curl_easy_setopt(handle, CURLOPT_PROGRESSDATA, handler);
1578 #endif
1579 curl_easy_setopt(handle, CURLOPT_DEBUGDATA, handler);
1580
1581 php_http_client_curl_handler_reset(handler);
1582
1583 return handler;
1584 }
1585
1586
1587 static STATUS php_http_client_curl_handler_prepare(php_http_client_curl_handler_t *curl, php_http_client_enqueue_t *enqueue)
1588 {
1589 size_t body_size;
1590 php_http_message_t *msg = enqueue->request;
1591 php_http_curle_storage_t *storage = php_http_curle_get_storage(curl->handle);
1592 TSRMLS_FETCH_FROM_CTX(curl->client->ts);
1593
1594 /* request url */
1595 if (!PHP_HTTP_INFO(msg).request.url) {
1596 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot request empty URL");
1597 return FAILURE;
1598 }
1599 storage->errorbuffer[0] = '\0';
1600 if (storage->url) {
1601 pefree(storage->url, 1);
1602 }
1603 storage->url = pestrdup(PHP_HTTP_INFO(msg).request.url, 1);
1604 curl_easy_setopt(curl->handle, CURLOPT_URL, storage->url);
1605
1606 /* request method */
1607 switch (php_http_select_str(PHP_HTTP_INFO(msg).request.method, 4, "GET", "HEAD", "POST", "PUT")) {
1608 case 0:
1609 curl_easy_setopt(curl->handle, CURLOPT_HTTPGET, 1L);
1610 break;
1611
1612 case 1:
1613 curl_easy_setopt(curl->handle, CURLOPT_NOBODY, 1L);
1614 break;
1615
1616 case 2:
1617 curl_easy_setopt(curl->handle, CURLOPT_POST, 1L);
1618 break;
1619
1620 case 3:
1621 curl_easy_setopt(curl->handle, CURLOPT_UPLOAD, 1L);
1622 break;
1623
1624 default: {
1625 if (PHP_HTTP_INFO(msg).request.method) {
1626 curl_easy_setopt(curl->handle, CURLOPT_CUSTOMREQUEST, PHP_HTTP_INFO(msg).request.method);
1627 } else {
1628 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot use empty request method");
1629 return FAILURE;
1630 }
1631 break;
1632 }
1633 }
1634
1635 /* request headers */
1636 php_http_message_update_headers(msg);
1637 if (zend_hash_num_elements(&msg->hdrs)) {
1638 php_http_array_hashkey_t header_key = php_http_array_hashkey_init(0);
1639 zval **header_val;
1640 HashPosition pos;
1641 php_http_buffer_t header;
1642
1643 php_http_buffer_init(&header);
1644 FOREACH_HASH_KEYVAL(pos, &msg->hdrs, header_key, header_val) {
1645 if (header_key.type == HASH_KEY_IS_STRING) {
1646 zval *header_cpy = php_http_ztyp(IS_STRING, *header_val);
1647
1648 php_http_buffer_appendf(&header, "%s: %s", header_key.str, Z_STRVAL_P(header_cpy));
1649 php_http_buffer_fix(&header);
1650 curl->options.headers = curl_slist_append(curl->options.headers, header.data);
1651 php_http_buffer_reset(&header);
1652
1653 zval_ptr_dtor(&header_cpy);
1654 }
1655 }
1656 php_http_buffer_dtor(&header);
1657 }
1658 curl_easy_setopt(curl->handle, CURLOPT_HTTPHEADER, curl->options.headers);
1659
1660 /* attach request body */
1661 if ((body_size = php_http_message_body_size(msg->body))) {
1662 /* RFC2616, section 4.3 (para. 4) states that »a message-body MUST NOT be included in a request if the
1663 * specification of the request method (section 5.1.1) does not allow sending an entity-body in request.«
1664 * Following the clause in section 5.1.1 (para. 2) that request methods »MUST be implemented with the
1665 * same semantics as those specified in section 9« reveal that not any single defined HTTP/1.1 method
1666 * does not allow a request body.
1667 */
1668 php_stream_rewind(php_http_message_body_stream(msg->body));
1669 curl_easy_setopt(curl->handle, CURLOPT_IOCTLDATA, msg->body);
1670 curl_easy_setopt(curl->handle, CURLOPT_READDATA, msg->body);
1671 curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE, body_size);
1672 curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, body_size);
1673 } else {
1674 curl_easy_setopt(curl->handle, CURLOPT_IOCTLDATA, NULL);
1675 curl_easy_setopt(curl->handle, CURLOPT_READDATA, NULL);
1676 curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE, 0L);
1677 curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, 0L);
1678 }
1679
1680 php_http_options_apply(&php_http_curle_options, enqueue->options, curl);
1681
1682 return SUCCESS;
1683 }
1684
1685 static void php_http_client_curl_handler_clear(php_http_client_curl_handler_t *handler)
1686 {
1687 curl_easy_setopt(handler->handle, CURLOPT_NOPROGRESS, 1L);
1688 #if PHP_HTTP_CURL_VERSION(7,32,0)
1689 curl_easy_setopt(handler->handle, CURLOPT_XFERINFOFUNCTION, NULL);
1690 #else
1691 curl_easy_setopt(handler->handle, CURLOPT_PROGRESSFUNCTION, NULL);
1692 #endif
1693 curl_easy_setopt(handler->handle, CURLOPT_VERBOSE, 0L);
1694 curl_easy_setopt(handler->handle, CURLOPT_DEBUGFUNCTION, NULL);
1695 }
1696
1697 static void php_http_client_curl_handler_dtor(php_http_client_curl_handler_t *handler)
1698 {
1699 TSRMLS_FETCH_FROM_CTX(handler->client->ts);
1700
1701 php_http_client_curl_handler_clear(handler);
1702
1703 php_resource_factory_handle_dtor(handler->rf, handler->handle TSRMLS_CC);
1704 php_resource_factory_free(&handler->rf);
1705
1706 php_http_message_parser_free(&handler->request.parser);
1707 php_http_message_free(&handler->request.message);
1708 php_http_buffer_free(&handler->request.buffer);
1709 php_http_message_parser_free(&handler->response.parser);
1710 php_http_message_free(&handler->response.message);
1711 php_http_buffer_free(&handler->response.buffer);
1712 php_http_buffer_dtor(&handler->options.ranges);
1713 php_http_buffer_dtor(&handler->options.cookies);
1714 zend_hash_destroy(&handler->options.cache);
1715
1716 if (handler->options.headers) {
1717 curl_slist_free_all(handler->options.headers);
1718 handler->options.headers = NULL;
1719 }
1720
1721 efree(handler);
1722 }
1723
1724 static php_http_client_t *php_http_client_curl_init(php_http_client_t *h, void *handle)
1725 {
1726 php_http_client_curl_t *curl;
1727 TSRMLS_FETCH_FROM_CTX(h->ts);
1728
1729 if (!handle && !(handle = php_resource_factory_handle_ctor(h->rf, NULL TSRMLS_CC))) {
1730 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to initialize curl handle");
1731 return NULL;
1732 }
1733
1734 curl = ecalloc(1, sizeof(*curl));
1735 curl->handle = handle;
1736 curl->unfinished = 0;
1737 h->ctx = curl;
1738
1739 return h;
1740 }
1741
1742 static void php_http_client_curl_dtor(php_http_client_t *h)
1743 {
1744 php_http_client_curl_t *curl = h->ctx;
1745 TSRMLS_FETCH_FROM_CTX(h->ts);
1746
1747 #if PHP_HTTP_HAVE_EVENT
1748 if (curl->timeout) {
1749 if (event_initialized(curl->timeout) && event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
1750 event_del(curl->timeout);
1751 }
1752 efree(curl->timeout);
1753 curl->timeout = NULL;
1754 }
1755 if (curl->evbase) {
1756 event_base_free(curl->evbase);
1757 curl->evbase = NULL;
1758 }
1759 #endif
1760 curl->unfinished = 0;
1761
1762 php_resource_factory_handle_dtor(h->rf, curl->handle TSRMLS_CC);
1763
1764 efree(curl);
1765 h->ctx = NULL;
1766 }
1767
1768 static void queue_dtor(php_http_client_enqueue_t *e)
1769 {
1770 php_http_client_curl_handler_t *handler = e->opaque;
1771
1772 if (handler->queue.dtor) {
1773 e->opaque = handler->queue.opaque;
1774 handler->queue.dtor(e);
1775 }
1776 php_http_client_curl_handler_dtor(handler);
1777 }
1778
1779 static php_resource_factory_t *create_rf(const char *url TSRMLS_DC)
1780 {
1781 php_url *purl;
1782 php_resource_factory_t *rf = NULL;
1783
1784 if (!url || !*url) {
1785 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot request empty URL");
1786 return NULL;
1787 }
1788
1789 purl = php_url_parse(url);
1790
1791 if (!purl) {
1792 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not parse URL '%s'", url);
1793 return NULL;
1794 } else {
1795 char *id_str = NULL;
1796 size_t id_len = spprintf(&id_str, 0, "%s:%d", STR_PTR(purl->host), purl->port ? purl->port : 80);
1797 php_persistent_handle_factory_t *pf = php_persistent_handle_concede(NULL, ZEND_STRL("http\\Client\\Curl\\Request"), id_str, id_len, NULL, NULL TSRMLS_CC);
1798
1799 if (pf) {
1800 rf = php_resource_factory_init(NULL, php_persistent_handle_get_resource_factory_ops(), pf, (void (*)(void*)) php_persistent_handle_abandon);
1801 }
1802
1803 php_url_free(purl);
1804 efree(id_str);
1805 }
1806
1807 if (!rf) {
1808 rf = php_resource_factory_init(NULL, &php_http_curle_resource_factory_ops, NULL, NULL);
1809 }
1810
1811 return rf;
1812 }
1813
1814 static STATUS php_http_client_curl_enqueue(php_http_client_t *h, php_http_client_enqueue_t *enqueue)
1815 {
1816 CURLMcode rs;
1817 php_http_client_curl_t *curl = h->ctx;
1818 php_http_client_curl_handler_t *handler;
1819 php_http_client_progress_state_t *progress;
1820 php_resource_factory_t *rf;
1821 TSRMLS_FETCH_FROM_CTX(h->ts);
1822
1823 rf = create_rf(enqueue->request->http.info.request.url TSRMLS_CC);
1824 if (!rf) {
1825 return FAILURE;
1826 }
1827
1828 handler = php_http_client_curl_handler_init(h, rf);
1829 if (!handler) {
1830 return FAILURE;
1831 }
1832
1833 if (SUCCESS != php_http_client_curl_handler_prepare(handler, enqueue)) {
1834 php_http_client_curl_handler_dtor(handler);
1835 return FAILURE;
1836 }
1837
1838 handler->queue = *enqueue;
1839 enqueue->opaque = handler;
1840 enqueue->dtor = queue_dtor;
1841
1842 if (CURLM_OK == (rs = curl_multi_add_handle(curl->handle, handler->handle))) {
1843 zend_llist_add_element(&h->requests, enqueue);
1844 ++curl->unfinished;
1845
1846 if (h->callback.progress.func && SUCCESS == php_http_client_getopt(h, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, enqueue->request, &progress)) {
1847 progress->info = "start";
1848 h->callback.progress.func(h->callback.progress.arg, h, &handler->queue, progress);
1849 progress->started = 1;
1850 }
1851
1852 return SUCCESS;
1853 } else {
1854 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not enqueue request: %s", curl_multi_strerror(rs));
1855 return FAILURE;
1856 }
1857 }
1858
1859 static STATUS php_http_client_curl_dequeue(php_http_client_t *h, php_http_client_enqueue_t *enqueue)
1860 {
1861 CURLMcode rs;
1862 php_http_client_curl_t *curl = h->ctx;
1863 php_http_client_curl_handler_t *handler = enqueue->opaque;
1864 TSRMLS_FETCH_FROM_CTX(h->ts);
1865
1866 php_http_client_curl_handler_clear(handler);
1867 if (CURLM_OK == (rs = curl_multi_remove_handle(curl->handle, handler->handle))) {
1868 zend_llist_del_element(&h->requests, handler->handle, (int (*)(void *, void *)) compare_queue);
1869 return SUCCESS;
1870 } else {
1871 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not dequeue request: %s", curl_multi_strerror(rs));
1872 }
1873
1874 return FAILURE;
1875 }
1876
1877 static void php_http_client_curl_reset(php_http_client_t *h)
1878 {
1879 zend_llist_element *next_el, *this_el;
1880
1881 for (this_el = h->requests.head; this_el; this_el = next_el) {
1882 next_el = this_el->next;
1883 php_http_client_curl_dequeue(h, (void *) this_el->data);
1884 }
1885 }
1886
1887 #ifdef PHP_WIN32
1888 # define SELECT_ERROR SOCKET_ERROR
1889 #else
1890 # define SELECT_ERROR -1
1891 #endif
1892
1893 static STATUS php_http_client_curl_wait(php_http_client_t *h, struct timeval *custom_timeout)
1894 {
1895 int MAX;
1896 fd_set R, W, E;
1897 struct timeval timeout;
1898 php_http_client_curl_t *curl = h->ctx;
1899
1900 #if PHP_HTTP_HAVE_EVENT
1901 if (curl->useevents) {
1902 TSRMLS_FETCH_FROM_CTX(h->ts);
1903
1904 php_error_docref(NULL TSRMLS_CC, E_WARNING, "not implemented");
1905 return FAILURE;
1906 }
1907 #endif
1908
1909 FD_ZERO(&R);
1910 FD_ZERO(&W);
1911 FD_ZERO(&E);
1912
1913 if (CURLM_OK == curl_multi_fdset(curl->handle, &R, &W, &E, &MAX)) {
1914 if (custom_timeout && timerisset(custom_timeout)) {
1915 timeout = *custom_timeout;
1916 } else {
1917 long max_tout = 1000;
1918
1919 if ((CURLM_OK == curl_multi_timeout(curl->handle, &max_tout)) && (max_tout > 0)) {
1920 timeout.tv_sec = max_tout / 1000;
1921 timeout.tv_usec = (max_tout % 1000) * 1000;
1922 } else {
1923 timeout.tv_sec = 0;
1924 timeout.tv_usec = 1000;
1925 }
1926 }
1927
1928 if (MAX == -1) {
1929 php_http_sleep((double) timeout.tv_sec + (double) (timeout.tv_usec / PHP_HTTP_MCROSEC));
1930 return SUCCESS;
1931 } else if (SELECT_ERROR != select(MAX + 1, &R, &W, &E, &timeout)) {
1932 return SUCCESS;
1933 }
1934 }
1935 return FAILURE;
1936 }
1937
1938 static int php_http_client_curl_once(php_http_client_t *h)
1939 {
1940 php_http_client_curl_t *curl = h->ctx;
1941
1942 #if PHP_HTTP_HAVE_EVENT
1943 if (curl->useevents) {
1944 TSRMLS_FETCH_FROM_CTX(h->ts);
1945 php_error_docref(NULL TSRMLS_CC, E_WARNING, "not implemented");
1946 return FAILURE;
1947 }
1948 #endif
1949
1950 while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curl->handle, &curl->unfinished));
1951
1952 php_http_curlm_responsehandler(h);
1953
1954 return curl->unfinished;
1955
1956 }
1957
1958 static STATUS php_http_client_curl_exec(php_http_client_t *h)
1959 {
1960 #if PHP_HTTP_HAVE_EVENT
1961 php_http_client_curl_t *curl = h->ctx;
1962 #endif
1963 TSRMLS_FETCH_FROM_CTX(h->ts);
1964
1965 #if PHP_HTTP_HAVE_EVENT
1966 if (curl->useevents) {
1967 php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, h);
1968 do {
1969 int ev_rc = event_base_dispatch(curl->evbase);
1970
1971 #if DBG_EVENTS
1972 fprintf(stderr, "%c", "X.0"[ev_rc+1]);
1973 #endif
1974
1975 if (ev_rc < 0) {
1976 php_error_docref(NULL TSRMLS_CC, E_ERROR, "Error in event_base_dispatch()");
1977 return FAILURE;
1978 }
1979 } while (curl->unfinished);
1980 } else
1981 #endif
1982 {
1983 while (php_http_client_curl_once(h)) {
1984 if (SUCCESS != php_http_client_curl_wait(h, NULL)) {
1985 #ifdef PHP_WIN32
1986 /* see http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */
1987 php_error_docref(NULL TSRMLS_CC, E_WARNING, "WinSock error: %d", WSAGetLastError());
1988 #else
1989 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno));
1990 #endif
1991 return FAILURE;
1992 }
1993 }
1994 }
1995
1996 return SUCCESS;
1997 }
1998
1999 static STATUS php_http_client_curl_setopt(php_http_client_t *h, php_http_client_setopt_opt_t opt, void *arg)
2000 {
2001 php_http_client_curl_t *curl = h->ctx;
2002
2003 switch (opt) {
2004 case PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING:
2005 if (CURLM_OK != curl_multi_setopt(curl->handle, CURLMOPT_PIPELINING, (long) *((zend_bool *) arg))) {
2006 return FAILURE;
2007 }
2008 break;
2009
2010 case PHP_HTTP_CLIENT_OPT_USE_EVENTS:
2011 #if PHP_HTTP_HAVE_EVENT
2012 if ((curl->useevents = *((zend_bool *) arg))) {
2013 if (!curl->evbase) {
2014 curl->evbase = event_base_new();
2015 }
2016 if (!curl->timeout) {
2017 curl->timeout = ecalloc(1, sizeof(struct event));
2018 }
2019 curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, h);
2020 curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, php_http_curlm_socket_callback);
2021 curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, h);
2022 curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, php_http_curlm_timer_callback);
2023 } else {
2024 curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, NULL);
2025 curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, NULL);
2026 curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, NULL);
2027 curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, NULL);
2028 }
2029 break;
2030 #endif
2031
2032 default:
2033 return FAILURE;
2034 }
2035 return SUCCESS;
2036 }
2037
2038 static STATUS php_http_client_curl_getopt(php_http_client_t *h, php_http_client_getopt_opt_t opt, void *arg, void **res)
2039 {
2040 php_http_client_enqueue_t *enqueue;
2041
2042 switch (opt) {
2043 case PHP_HTTP_CLIENT_OPT_PROGRESS_INFO:
2044 if ((enqueue = php_http_client_enqueued(h, arg, NULL))) {
2045 php_http_client_curl_handler_t *handler = enqueue->opaque;
2046
2047 *((php_http_client_progress_state_t **) res) = &handler->progress;
2048 return SUCCESS;
2049 }
2050 break;
2051
2052 case PHP_HTTP_CLIENT_OPT_TRANSFER_INFO:
2053 if ((enqueue = php_http_client_enqueued(h, arg, NULL))) {
2054 php_http_client_curl_handler_t *handler = enqueue->opaque;
2055
2056 php_http_curle_get_info(handler->handle, *(HashTable **) res);
2057 return SUCCESS;
2058 }
2059 break;
2060
2061 default:
2062 break;
2063 }
2064
2065 return FAILURE;
2066 }
2067
2068 static php_http_client_ops_t php_http_client_curl_ops = {
2069 &php_http_curlm_resource_factory_ops,
2070 php_http_client_curl_init,
2071 NULL /* copy */,
2072 php_http_client_curl_dtor,
2073 php_http_client_curl_reset,
2074 php_http_client_curl_exec,
2075 php_http_client_curl_wait,
2076 php_http_client_curl_once,
2077 php_http_client_curl_enqueue,
2078 php_http_client_curl_dequeue,
2079 php_http_client_curl_setopt,
2080 php_http_client_curl_getopt
2081 };
2082
2083 php_http_client_ops_t *php_http_client_curl_get_ops(void)
2084 {
2085 return &php_http_client_curl_ops;
2086 }
2087
2088 PHP_MINIT_FUNCTION(http_client_curl)
2089 {
2090 php_http_options_t *options;
2091 php_http_client_driver_t driver = {
2092 ZEND_STRL("curl"),
2093 &php_http_client_curl_ops
2094 };
2095
2096 if (SUCCESS != php_http_client_driver_add(&driver)) {
2097 return FAILURE;
2098 }
2099
2100 if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl"), &php_http_curlm_resource_factory_ops, NULL, NULL TSRMLS_CC)) {
2101 return FAILURE;
2102 }
2103 if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl\\Request"), &php_http_curle_resource_factory_ops, NULL, NULL TSRMLS_CC)) {
2104 return FAILURE;
2105 }
2106
2107 if ((options = php_http_options_init(&php_http_curle_options, 1))) {
2108 options->getter = php_http_curle_get_option;
2109 options->setter = php_http_curle_set_option;
2110
2111 php_http_curle_options_init(options TSRMLS_CC);
2112 }
2113
2114 /*
2115 * HTTP Protocol Version Constants
2116 */
2117 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_1_0", CURL_HTTP_VERSION_1_0, CONST_CS|CONST_PERSISTENT);
2118 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_1_1", CURL_HTTP_VERSION_1_1, CONST_CS|CONST_PERSISTENT);
2119 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_ANY", CURL_HTTP_VERSION_NONE, CONST_CS|CONST_PERSISTENT);
2120
2121 /*
2122 * SSL Version Constants
2123 */
2124 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1", CURL_SSLVERSION_TLSv1, CONST_CS|CONST_PERSISTENT);
2125 #if PHP_HTTP_CURL_VERSION(7,34,0)
2126 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_0", CURL_SSLVERSION_TLSv1_0, CONST_CS|CONST_PERSISTENT);
2127 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_1", CURL_SSLVERSION_TLSv1_1, CONST_CS|CONST_PERSISTENT);
2128 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_2", CURL_SSLVERSION_TLSv1_2, CONST_CS|CONST_PERSISTENT);
2129 #endif
2130 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_SSLv2", CURL_SSLVERSION_SSLv2, CONST_CS|CONST_PERSISTENT);
2131 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_SSLv3", CURL_SSLVERSION_SSLv3, CONST_CS|CONST_PERSISTENT);
2132 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_ANY", CURL_SSLVERSION_DEFAULT, CONST_CS|CONST_PERSISTENT);
2133
2134 /*
2135 * DNS IPvX resolving
2136 */
2137 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_V4", CURL_IPRESOLVE_V4, CONST_CS|CONST_PERSISTENT);
2138 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_V6", CURL_IPRESOLVE_V6, CONST_CS|CONST_PERSISTENT);
2139 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_ANY", CURL_IPRESOLVE_WHATEVER, CONST_CS|CONST_PERSISTENT);
2140
2141 /*
2142 * Auth Constants
2143 */
2144 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_BASIC", CURLAUTH_BASIC, CONST_CS|CONST_PERSISTENT);
2145 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_DIGEST", CURLAUTH_DIGEST, CONST_CS|CONST_PERSISTENT);
2146 #if PHP_HTTP_CURL_VERSION(7,19,3)
2147 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_DIGEST_IE", CURLAUTH_DIGEST_IE, CONST_CS|CONST_PERSISTENT);
2148 #endif
2149 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_NTLM", CURLAUTH_NTLM, CONST_CS|CONST_PERSISTENT);
2150 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_GSSNEG", CURLAUTH_GSSNEGOTIATE, CONST_CS|CONST_PERSISTENT);
2151 #if PHP_HTTP_CURL_VERSION(7,38,0)
2152 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_SPNEGO", CURLAUTH_NEGOTIATE, CONST_CS|CONST_PERSISTENT);
2153 #endif
2154 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_ANY", CURLAUTH_ANY, CONST_CS|CONST_PERSISTENT);
2155
2156 /*
2157 * Proxy Type Constants
2158 */
2159 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS4", CURLPROXY_SOCKS4, CONST_CS|CONST_PERSISTENT);
2160 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS4A", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT);
2161 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS5_HOSTNAME", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT);
2162 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS5", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT);
2163 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_HTTP", CURLPROXY_HTTP, CONST_CS|CONST_PERSISTENT);
2164 #if PHP_HTTP_CURL_VERSION(7,19,4)
2165 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_HTTP_1_0", CURLPROXY_HTTP_1_0, CONST_CS|CONST_PERSISTENT);
2166 #endif
2167
2168 /*
2169 * Post Redirection Constants
2170 */
2171 #if PHP_HTTP_CURL_VERSION(7,19,1)
2172 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_301", CURL_REDIR_POST_301, CONST_CS|CONST_PERSISTENT);
2173 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_302", CURL_REDIR_POST_302, CONST_CS|CONST_PERSISTENT);
2174 #if PHP_HTTP_CURL_VERSION(7,26,0)
2175 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_303", CURL_REDIR_POST_303, CONST_CS|CONST_PERSISTENT);
2176 #endif
2177 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_ALL", CURL_REDIR_POST_ALL, CONST_CS|CONST_PERSISTENT);
2178 #endif
2179
2180 return SUCCESS;
2181 }
2182
2183 PHP_MSHUTDOWN_FUNCTION(http_client_curl)
2184 {
2185 php_persistent_handle_cleanup(ZEND_STRL("http\\Client\\Curl"), NULL, 0 TSRMLS_CC);
2186 php_persistent_handle_cleanup(ZEND_STRL("http\\Client\\Curl\\Request"), NULL, 0 TSRMLS_CC);
2187
2188 php_http_options_dtor(&php_http_curle_options);
2189
2190 return SUCCESS;
2191 }
2192
2193 #endif /* PHP_HTTP_HAVE_CURL */
2194
2195 /*
2196 * Local variables:
2197 * tab-width: 4
2198 * c-basic-offset: 4
2199 * End:
2200 * vim600: noet sw=4 ts=4 fdm=marker
2201 * vim<600: noet sw=4 ts=4
2202 */