fix error messages of curlm_set_option
[m6w6/ext-http] / src / 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_handle {
47 CURLM *multi;
48 CURLSH *share;
49 } php_http_client_curl_handle_t;
50
51 typedef struct php_http_client_curl {
52 php_http_client_curl_handle_t *handle;
53
54 int unfinished; /* int because of curl_multi_perform() */
55
56 #if PHP_HTTP_HAVE_EVENT
57 struct event_base *evbase;
58 struct event *timeout;
59 unsigned useevents:1;
60 #endif
61 } php_http_client_curl_t;
62
63 typedef struct php_http_client_curl_handler {
64 CURL *handle;
65 php_resource_factory_t *rf;
66 php_http_client_t *client;
67 php_http_client_progress_state_t progress;
68 php_http_client_enqueue_t queue;
69
70 struct {
71 php_http_buffer_t headers;
72 php_http_message_body_t *body;
73 } response;
74
75 struct {
76 HashTable cache;
77
78 struct curl_slist *proxyheaders;
79 struct curl_slist *headers;
80 struct curl_slist *resolve;
81 php_http_buffer_t cookies;
82 php_http_buffer_t ranges;
83
84 long redirects;
85 unsigned range_request:1;
86 unsigned encode_cookies:1;
87
88 struct {
89 uint count;
90 double delay;
91 } retry;
92
93 } options;
94
95 } php_http_client_curl_handler_t;
96
97 typedef struct php_http_curle_storage {
98 char *url;
99 char *cookiestore;
100 CURLcode errorcode;
101 char errorbuffer[0x100];
102 } php_http_curle_storage_t;
103
104 static inline php_http_curle_storage_t *php_http_curle_get_storage(CURL *ch) {
105 php_http_curle_storage_t *st = NULL;
106
107 curl_easy_getinfo(ch, CURLINFO_PRIVATE, &st);
108
109 if (!st) {
110 st = pecalloc(1, sizeof(*st), 1);
111 curl_easy_setopt(ch, CURLOPT_PRIVATE, st);
112 curl_easy_setopt(ch, CURLOPT_ERRORBUFFER, st->errorbuffer);
113 }
114
115 return st;
116 }
117
118 static void *php_http_curle_ctor(void *opaque, void *init_arg TSRMLS_DC)
119 {
120 void *ch;
121
122 if ((ch = curl_easy_init())) {
123 php_http_curle_get_storage(ch);
124 return ch;
125 }
126 return NULL;
127 }
128
129 static void *php_http_curle_copy(void *opaque, void *handle TSRMLS_DC)
130 {
131 void *ch;
132
133 if ((ch = curl_easy_duphandle(handle))) {
134 curl_easy_reset(ch);
135 php_http_curle_get_storage(ch);
136 return ch;
137 }
138 return NULL;
139 }
140
141 static void php_http_curle_dtor(void *opaque, void *handle TSRMLS_DC)
142 {
143 php_http_curle_storage_t *st = php_http_curle_get_storage(handle);
144
145 curl_easy_cleanup(handle);
146
147 if (st) {
148 if (st->url) {
149 pefree(st->url, 1);
150 }
151 if (st->cookiestore) {
152 pefree(st->cookiestore, 1);
153 }
154 pefree(st, 1);
155 }
156 }
157
158 static php_resource_factory_ops_t php_http_curle_resource_factory_ops = {
159 php_http_curle_ctor,
160 php_http_curle_copy,
161 php_http_curle_dtor
162 };
163
164 static void *php_http_curlm_ctor(void *opaque, void *init_arg TSRMLS_DC)
165 {
166 php_http_client_curl_handle_t *curl = calloc(1, sizeof(*curl));
167
168 if (!(curl->multi = curl_multi_init())) {
169 free(curl);
170 return NULL;
171 }
172 if (!(curl->share = curl_share_init())) {
173 curl_multi_cleanup(curl->multi);
174 free(curl);
175 return NULL;
176 }
177 curl_share_setopt(curl->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
178 curl_share_setopt(curl->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
179 return curl;
180 }
181
182 static void php_http_curlm_dtor(void *opaque, void *handle TSRMLS_DC)
183 {
184 php_http_client_curl_handle_t *curl = handle;
185
186 curl_share_cleanup(curl->share);
187 curl_multi_cleanup(curl->multi);
188 free(handle);
189 }
190
191 static php_resource_factory_ops_t php_http_curlm_resource_factory_ops = {
192 php_http_curlm_ctor,
193 NULL,
194 php_http_curlm_dtor
195 };
196
197 /* curl callbacks */
198
199 static size_t php_http_curle_read_callback(void *data, size_t len, size_t n, void *ctx)
200 {
201 php_http_message_body_t *body = ctx;
202
203 if (body && body->stream_id) {
204 php_stream *s = php_http_message_body_stream(body);
205
206 if (s) {
207 TSRMLS_FETCH_FROM_CTX(body->ts);
208 return php_stream_read(s, data, len * n);
209 } else abort();
210 }
211 return 0;
212 }
213
214 #if PHP_HTTP_CURL_VERSION(7,32,0)
215 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)
216 #else
217 static int php_http_curle_progress_callback(void *ctx, double dltotal, double dlnow, double ultotal, double ulnow)
218 #endif
219 {
220 php_http_client_curl_handler_t *h = ctx;
221 zend_bool update = 0;
222
223 if (h->progress.dl.total != dltotal
224 || h->progress.dl.now != dlnow
225 || h->progress.ul.total != ultotal
226 || h->progress.ul.now != ulnow
227 ) {
228 update = 1;
229
230 h->progress.dl.total = dltotal;
231 h->progress.dl.now = dlnow;
232 h->progress.ul.total = ultotal;
233 h->progress.ul.now = ulnow;
234 }
235
236 if (update && h->client->callback.progress.func) {
237 h->client->callback.progress.func(h->client->callback.progress.arg, h->client, &h->queue, &h->progress);
238 }
239
240 return 0;
241 }
242
243 static int php_http_curle_seek_callback(void *userdata, curl_off_t offset, int origin)
244 {
245 php_http_message_body_t *body = userdata;
246 TSRMLS_FETCH_FROM_CTX(body->ts);
247
248 if (!body) {
249 return 1;
250 }
251 if (0 == php_stream_seek(php_http_message_body_stream(body), offset, origin)) {
252 return 0;
253 }
254 return 2;
255 }
256
257 static int php_http_curle_raw_callback(CURL *ch, curl_infotype type, char *data, size_t length, void *ctx)
258 {
259 php_http_client_curl_handler_t *h = ctx;
260
261 /* catch progress */
262 switch (type) {
263 case CURLINFO_TEXT:
264 if (data[0] == '-') {
265 } else if (php_memnstr(data, ZEND_STRL("Adding handle:"), data + length)) {
266 h->progress.info = "setup";
267 } else if (php_memnstr(data, ZEND_STRL("addHandle"), data + length)) {
268 h->progress.info = "setup";
269 } else if (php_memnstr(data, ZEND_STRL("About to connect"), data + length)) {
270 h->progress.info = "resolve";
271 } else if (php_memnstr(data, ZEND_STRL("Trying"), data + length)) {
272 h->progress.info = "connect";
273 } else if (php_memnstr(data, ZEND_STRL("Found bundle for host"), data + length)) {
274 h->progress.info = "connect";
275 } else if (php_memnstr(data, ZEND_STRL("Connected"), data + length)) {
276 h->progress.info = "connected";
277 } else if (php_memnstr(data, ZEND_STRL("Re-using existing connection!"), data + length)) {
278 h->progress.info = "connected";
279 } else if (php_memnstr(data, ZEND_STRL("blacklisted"), data + length)) {
280 h->progress.info = "blacklist check";
281 } else if (php_memnstr(data, ZEND_STRL("SSL"), data + length)) {
282 h->progress.info = "ssl negotiation";
283 } else if (php_memnstr(data, ZEND_STRL("upload"), data + length)) {
284 h->progress.info = "uploaded";
285 } else if (php_memnstr(data, ZEND_STRL("left intact"), data + length)) {
286 h->progress.info = "not disconnected";
287 } else if (php_memnstr(data, ZEND_STRL("closed"), data + length)) {
288 h->progress.info = "disconnected";
289 } else if (php_memnstr(data, ZEND_STRL("Issue another request"), data + length)) {
290 h->progress.info = "redirect";
291 } else if (php_memnstr(data, ZEND_STRL("Operation timed out"), data + length)) {
292 h->progress.info = "timeout";
293 } else {
294 #if 0
295 h->progress.info = data;
296 data[length - 1] = '\0';
297 #endif
298 }
299 if (h->client->callback.progress.func) {
300 h->client->callback.progress.func(h->client->callback.progress.arg, h->client, &h->queue, &h->progress);
301 }
302 break;
303 case CURLINFO_HEADER_OUT:
304 case CURLINFO_DATA_OUT:
305 case CURLINFO_SSL_DATA_OUT:
306 h->progress.info = "send";
307 break;
308 case CURLINFO_HEADER_IN:
309 case CURLINFO_DATA_IN:
310 case CURLINFO_SSL_DATA_IN:
311 h->progress.info = "receive";
312 break;
313 default:
314 break;
315 }
316
317 #if 0
318 /* debug */
319 _dpf(type, data, length);
320 #endif
321
322 return 0;
323 }
324
325 static int php_http_curle_header_callback(char *data, size_t n, size_t l, void *arg)
326 {
327 php_http_client_curl_handler_t *h = arg;
328
329 return php_http_buffer_append(&h->response.headers, data, n * l);
330 }
331
332 static int php_http_curle_body_callback(char *data, size_t n, size_t l, void *arg)
333 {
334 php_http_client_curl_handler_t *h = arg;
335
336 return php_http_message_body_append(h->response.body, data, n*l);
337 }
338
339 static ZEND_RESULT_CODE php_http_curle_get_info(CURL *ch, HashTable *info)
340 {
341 char *c;
342 long l;
343 double d;
344 struct curl_slist *s, *p;
345 zval *subarray, array;
346 INIT_PZVAL_ARRAY(&array, info);
347
348 /* BEGIN::CURLINFO */
349 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_EFFECTIVE_URL, &c)) {
350 add_assoc_string_ex(&array, "effective_url", sizeof("effective_url"), c ? c : "", 1);
351 }
352 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &l)) {
353 add_assoc_long_ex(&array, "response_code", sizeof("response_code"), l);
354 }
355 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_TOTAL_TIME, &d)) {
356 add_assoc_double_ex(&array, "total_time", sizeof("total_time"), d);
357 }
358 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_NAMELOOKUP_TIME, &d)) {
359 add_assoc_double_ex(&array, "namelookup_time", sizeof("namelookup_time"), d);
360 }
361 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONNECT_TIME, &d)) {
362 add_assoc_double_ex(&array, "connect_time", sizeof("connect_time"), d);
363 }
364 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRETRANSFER_TIME, &d)) {
365 add_assoc_double_ex(&array, "pretransfer_time", sizeof("pretransfer_time"), d);
366 }
367 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SIZE_UPLOAD, &d)) {
368 add_assoc_double_ex(&array, "size_upload", sizeof("size_upload"), d);
369 }
370 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SIZE_DOWNLOAD, &d)) {
371 add_assoc_double_ex(&array, "size_download", sizeof("size_download"), d);
372 }
373 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SPEED_DOWNLOAD, &d)) {
374 add_assoc_double_ex(&array, "speed_download", sizeof("speed_download"), d);
375 }
376 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SPEED_UPLOAD, &d)) {
377 add_assoc_double_ex(&array, "speed_upload", sizeof("speed_upload"), d);
378 }
379 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HEADER_SIZE, &l)) {
380 add_assoc_long_ex(&array, "header_size", sizeof("header_size"), l);
381 }
382 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REQUEST_SIZE, &l)) {
383 add_assoc_long_ex(&array, "request_size", sizeof("request_size"), l);
384 }
385 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SSL_VERIFYRESULT, &l)) {
386 add_assoc_long_ex(&array, "ssl_verifyresult", sizeof("ssl_verifyresult"), l);
387 }
388 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_FILETIME, &l)) {
389 add_assoc_long_ex(&array, "filetime", sizeof("filetime"), l);
390 }
391 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d)) {
392 add_assoc_double_ex(&array, "content_length_download", sizeof("content_length_download"), d);
393 }
394 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_LENGTH_UPLOAD, &d)) {
395 add_assoc_double_ex(&array, "content_length_upload", sizeof("content_length_upload"), d);
396 }
397 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_STARTTRANSFER_TIME, &d)) {
398 add_assoc_double_ex(&array, "starttransfer_time", sizeof("starttransfer_time"), d);
399 }
400 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_TYPE, &c)) {
401 add_assoc_string_ex(&array, "content_type", sizeof("content_type"), c ? c : "", 1);
402 }
403 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_TIME, &d)) {
404 add_assoc_double_ex(&array, "redirect_time", sizeof("redirect_time"), d);
405 }
406 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_COUNT, &l)) {
407 add_assoc_long_ex(&array, "redirect_count", sizeof("redirect_count"), l);
408 }
409 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HTTP_CONNECTCODE, &l)) {
410 add_assoc_long_ex(&array, "connect_code", sizeof("connect_code"), l);
411 }
412 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HTTPAUTH_AVAIL, &l)) {
413 add_assoc_long_ex(&array, "httpauth_avail", sizeof("httpauth_avail"), l);
414 }
415 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PROXYAUTH_AVAIL, &l)) {
416 add_assoc_long_ex(&array, "proxyauth_avail", sizeof("proxyauth_avail"), l);
417 }
418 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_OS_ERRNO, &l)) {
419 add_assoc_long_ex(&array, "os_errno", sizeof("os_errno"), l);
420 }
421 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_NUM_CONNECTS, &l)) {
422 add_assoc_long_ex(&array, "num_connects", sizeof("num_connects"), l);
423 }
424 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SSL_ENGINES, &s)) {
425 MAKE_STD_ZVAL(subarray);
426 array_init(subarray);
427 for (p = s; p; p = p->next) {
428 if (p->data) {
429 add_next_index_string(subarray, p->data, 1);
430 }
431 }
432 add_assoc_zval_ex(&array, "ssl_engines", sizeof("ssl_engines"), subarray);
433 curl_slist_free_all(s);
434 }
435 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_URL, &c)) {
436 add_assoc_string_ex(&array, "redirect_url", sizeof("redirect_url"), c ? c : "", 1);
437 }
438 #if PHP_HTTP_CURL_VERSION(7,19,0)
439 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRIMARY_IP, &c)) {
440 add_assoc_string_ex(&array, "primary_ip", sizeof("primary_ip"), c ? c : "", 1);
441 }
442 #endif
443 #if PHP_HTTP_CURL_VERSION(7,19,0)
444 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_APPCONNECT_TIME, &d)) {
445 add_assoc_double_ex(&array, "appconnect_time", sizeof("appconnect_time"), d);
446 }
447 #endif
448 #if PHP_HTTP_CURL_VERSION(7,19,4)
449 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONDITION_UNMET, &l)) {
450 add_assoc_long_ex(&array, "condition_unmet", sizeof("condition_unmet"), l);
451 }
452 #endif
453 #if PHP_HTTP_CURL_VERSION(7,21,0)
454 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRIMARY_PORT, &l)) {
455 add_assoc_long_ex(&array, "primary_port", sizeof("primary_port"), l);
456 }
457 #endif
458 #if PHP_HTTP_CURL_VERSION(7,21,0)
459 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_LOCAL_IP, &c)) {
460 add_assoc_string_ex(&array, "local_ip", sizeof("local_ip"), c ? c : "", 1);
461 }
462 #endif
463 #if PHP_HTTP_CURL_VERSION(7,21,0)
464 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_LOCAL_PORT, &l)) {
465 add_assoc_long_ex(&array, "local_port", sizeof("local_port"), l);
466 }
467 #endif
468
469 /* END::CURLINFO */
470
471 #if PHP_HTTP_CURL_VERSION(7,34,0)
472 {
473 zval *ti_array;
474 struct curl_tlssessioninfo *ti;
475
476 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_TLS_SESSION, &ti)) {
477 const char *backend;
478
479 MAKE_STD_ZVAL(subarray);
480 ZVAL_NULL(subarray);
481 MAKE_STD_ZVAL(ti_array);
482 array_init(ti_array);
483
484 switch (ti->backend) {
485 case CURLSSLBACKEND_NONE:
486 backend = "none";
487 break;
488 case CURLSSLBACKEND_OPENSSL:
489 backend = "openssl";
490 #ifdef PHP_HTTP_HAVE_OPENSSL
491 {
492 SSL_CTX *ctx = ti->internals;
493
494 array_init(subarray);
495 add_assoc_long_ex(subarray, ZEND_STRS("number"), SSL_CTX_sess_number(ctx));
496 add_assoc_long_ex(subarray, ZEND_STRS("connect"), SSL_CTX_sess_connect(ctx));
497 add_assoc_long_ex(subarray, ZEND_STRS("connect_good"), SSL_CTX_sess_connect_good(ctx));
498 add_assoc_long_ex(subarray, ZEND_STRS("connect_renegotiate"), SSL_CTX_sess_connect_renegotiate(ctx));
499 add_assoc_long_ex(subarray, ZEND_STRS("hits"), SSL_CTX_sess_hits(ctx));
500 add_assoc_long_ex(subarray, ZEND_STRS("cache_full"), SSL_CTX_sess_cache_full(ctx));
501 }
502 #endif
503 break;
504 case CURLSSLBACKEND_GNUTLS:
505 backend = "gnutls";
506 #ifdef PHP_HTTP_HAVE_GNUTLS
507 {
508 gnutls_session_t sess = ti->internals;
509 char *desc;
510
511 array_init(subarray);
512 if ((desc = gnutls_session_get_desc(sess))) {
513 add_assoc_string_ex(subarray, ZEND_STRS("desc"), desc, 1);
514 gnutls_free(desc);
515 }
516 add_assoc_bool_ex(subarray, ZEND_STRS("resumed"), gnutls_session_is_resumed(sess));
517 }
518 #endif
519 break;
520 case CURLSSLBACKEND_NSS:
521 backend = "nss";
522 break;
523 #if !PHP_HTTP_CURL_VERSION(7,39,0)
524 case CURLSSLBACKEND_QSOSSL:
525 backend = "qsossl";
526 break;
527 #else
528 case CURLSSLBACKEND_GSKIT:
529 backend = "gskit";
530 break;
531 #endif
532 case CURLSSLBACKEND_POLARSSL:
533 backend = "polarssl";
534 break;
535 case CURLSSLBACKEND_CYASSL:
536 backend = "cyassl";
537 break;
538 case CURLSSLBACKEND_SCHANNEL:
539 backend = "schannel";
540 break;
541 case CURLSSLBACKEND_DARWINSSL:
542 backend = "darwinssl";
543 break;
544 default:
545 backend = "unknown";
546 }
547 add_assoc_string_ex(ti_array, ZEND_STRS("backend"), estrdup(backend), 0);
548 add_assoc_zval_ex(ti_array, ZEND_STRS("internals"), subarray);
549 add_assoc_zval_ex(&array, "tls_session", sizeof("tls_session"), ti_array);
550 }
551 }
552 #endif
553
554 #if (PHP_HTTP_CURL_VERSION(7,19,1) && defined(PHP_HTTP_HAVE_OPENSSL)) || (PHP_HTTP_CURL_VERSION(7,34,0) && defined(PHP_HTTP_HAVE_NSS)) || (PHP_HTTP_CURL_VERSION(7,42,0) && defined(PHP_HTTP_HAVE_GNUTLS)) || (PHP_HTTP_CURL_VERSION(7,39,0) && defined(PHP_HTTP_HAVE_GSKIT))
555 {
556 int i;
557 zval *ci_array;
558 struct curl_certinfo *ci;
559 char *colon, *keyname;
560
561 if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CERTINFO, &ci)) {
562 MAKE_STD_ZVAL(ci_array);
563 array_init(ci_array);
564
565 for (i = 0; i < ci->num_of_certs; ++i) {
566 s = ci->certinfo[i];
567
568 MAKE_STD_ZVAL(subarray);
569 array_init(subarray);
570 for (p = s; p; p = p->next) {
571 if (p->data) {
572 if ((colon = strchr(p->data, ':'))) {
573 keyname = estrndup(p->data, colon - p->data);
574 add_assoc_string_ex(subarray, keyname, colon - p->data + 1, colon + 1, 1);
575 efree(keyname);
576 } else {
577 add_next_index_string(subarray, p->data, 1);
578 }
579 }
580 }
581 add_next_index_zval(ci_array, subarray);
582 }
583 add_assoc_zval_ex(&array, "certinfo", sizeof("certinfo"), ci_array);
584 }
585 }
586 #endif
587 {
588 php_http_curle_storage_t *st = php_http_curle_get_storage(ch);
589
590 add_assoc_long_ex(&array, "curlcode", sizeof("curlcode"), st->errorcode);
591 add_assoc_string_ex(&array, "error", sizeof("error"), st->errorbuffer, 1);
592 }
593
594 return SUCCESS;
595 }
596
597 static int compare_queue(php_http_client_enqueue_t *e, void *handle)
598 {
599 return handle == ((php_http_client_curl_handler_t *) e->opaque)->handle;
600 }
601
602 static php_http_message_t *php_http_curlm_responseparser(php_http_client_curl_handler_t *h TSRMLS_DC)
603 {
604 php_http_message_t *response;
605 php_http_header_parser_t parser;
606 zval *zh;
607
608 response = php_http_message_init(NULL, 0, h->response.body TSRMLS_CC);
609 php_http_header_parser_init(&parser TSRMLS_CC);
610 while (h->response.headers.used) {
611 php_http_header_parser_state_t st = php_http_header_parser_parse(&parser,
612 &h->response.headers, PHP_HTTP_HEADER_PARSER_CLEANUP, &response->hdrs,
613 (php_http_info_callback_t) php_http_message_info_callback, (void *) &response);
614 if (PHP_HTTP_HEADER_PARSER_STATE_FAILURE == st) {
615 break;
616 }
617 }
618 php_http_header_parser_dtor(&parser);
619
620 /* move body to right message */
621 if (response->body != h->response.body) {
622 php_http_message_t *ptr = response;
623
624 while (ptr->parent) {
625 ptr = ptr->parent;
626 }
627 php_http_message_body_free(&response->body);
628 response->body = ptr->body;
629 ptr->body = NULL;
630 }
631 php_http_message_body_addref(h->response.body);
632
633 /* let's update the response headers */
634 if ((zh = php_http_message_header(response, ZEND_STRL("Content-Length"), 1))) {
635 zend_hash_update(&response->hdrs, "X-Original-Content-Length", sizeof("X-Original-Content-Length"), &zh, sizeof(zval *), NULL);
636 }
637 if ((zh = php_http_message_header(response, ZEND_STRL("Transfer-Encoding"), 0))) {
638 zend_hash_update(&response->hdrs, "X-Original-Transfer-Encoding", sizeof("X-Original-Transfer-Encoding"), (void *) &zh, sizeof(zval *), NULL);
639 zend_hash_del(&response->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding"));
640 }
641 if ((zh = php_http_message_header(response, ZEND_STRL("Content-Range"), 0))) {
642 zend_hash_update(&response->hdrs, "X-Original-Content-Range", sizeof("X-Original-Content-Range"), &zh, sizeof(zval *), NULL);
643 zend_hash_del(&response->hdrs, "Content-Range", sizeof("Content-Range"));
644 }
645 if ((zh = php_http_message_header(response, ZEND_STRL("Content-Encoding"), 0))) {
646 zend_hash_update(&response->hdrs, "X-Original-Content-Encoding", sizeof("X-Original-Content-Encoding"), &zh, sizeof(zval *), NULL);
647 zend_hash_del(&response->hdrs, "Content-Encoding", sizeof("Content-Encoding"));
648 }
649 php_http_message_update_headers(response);
650
651 return response;
652 }
653
654 static void php_http_curlm_responsehandler(php_http_client_t *context)
655 {
656 int err_count = 0, remaining = 0;
657 php_http_curle_storage_t *st, *err = NULL;
658 php_http_client_enqueue_t *enqueue;
659 php_http_client_curl_t *curl = context->ctx;
660 TSRMLS_FETCH_FROM_CTX(context->ts);
661
662 do {
663 CURLMsg *msg = curl_multi_info_read(curl->handle->multi, &remaining);
664
665 if (msg && CURLMSG_DONE == msg->msg) {
666 if (CURLE_OK != msg->data.result) {
667 st = php_http_curle_get_storage(msg->easy_handle);
668 st->errorcode = msg->data.result;
669
670 /* defer the warnings/exceptions, so the callback is still called for this request */
671 if (!err) {
672 err = ecalloc(remaining + 1, sizeof(*err));
673 }
674 memcpy(&err[err_count], st, sizeof(*st));
675 if (st->url) {
676 err[err_count].url = estrdup(st->url);
677 }
678 err_count++;
679 }
680
681 if ((enqueue = php_http_client_enqueued(context, msg->easy_handle, compare_queue))) {
682 php_http_client_curl_handler_t *handler = enqueue->opaque;
683 php_http_message_t *response = php_http_curlm_responseparser(handler TSRMLS_CC);
684
685 if (response) {
686 context->callback.response.func(context->callback.response.arg, context, &handler->queue, &response);
687 php_http_message_free(&response);
688 }
689 }
690 }
691 } while (remaining);
692
693 if (err_count) {
694 int i = 0;
695
696 do {
697 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s; %s (%s)", curl_easy_strerror(err[i].errorcode), err[i].errorbuffer, STR_PTR(err[i].url));
698 if (err[i].url) {
699 efree(err[i].url);
700 }
701 } while (++i < err_count);
702
703 efree(err);
704 }
705 }
706
707 #if PHP_HTTP_HAVE_EVENT
708
709 typedef struct php_http_curlm_event {
710 struct event evnt;
711 php_http_client_t *context;
712 } php_http_curlm_event_t;
713
714 static inline int etoca(short action) {
715 switch (action & (EV_READ|EV_WRITE)) {
716 case EV_READ:
717 return CURL_CSELECT_IN;
718 break;
719 case EV_WRITE:
720 return CURL_CSELECT_OUT;
721 break;
722 case EV_READ|EV_WRITE:
723 return CURL_CSELECT_IN|CURL_CSELECT_OUT;
724 break;
725 default:
726 return 0;
727 }
728 }
729
730 static void php_http_curlm_timeout_callback(int socket, short action, void *event_data)
731 {
732 php_http_client_t *context = event_data;
733 php_http_client_curl_t *curl = context->ctx;
734
735 #if DBG_EVENTS
736 fprintf(stderr, "T");
737 #endif
738 if (curl->useevents) {
739 CURLMcode rc;
740 TSRMLS_FETCH_FROM_CTX(context->ts);
741
742 /* ignore and use -1,0 on timeout */
743 (void) socket;
744 (void) action;
745
746 while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle->multi, CURL_SOCKET_TIMEOUT, 0, &curl->unfinished)));
747
748 if (CURLM_OK != rc) {
749 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", curl_multi_strerror(rc));
750 }
751
752 php_http_curlm_responsehandler(context);
753 }
754 }
755
756 static void php_http_curlm_event_callback(int socket, short action, void *event_data)
757 {
758 php_http_client_t *context = event_data;
759 php_http_client_curl_t *curl = context->ctx;
760
761 #if DBG_EVENTS
762 fprintf(stderr, "E");
763 #endif
764 if (curl->useevents) {
765 CURLMcode rc = CURLM_OK;
766 TSRMLS_FETCH_FROM_CTX(context->ts);
767
768 while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle->multi, socket, etoca(action), &curl->unfinished)));
769
770 if (CURLM_OK != rc) {
771 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", curl_multi_strerror(rc));
772 }
773
774 php_http_curlm_responsehandler(context);
775
776 /* remove timeout if there are no transfers left */
777 if (!curl->unfinished && event_initialized(curl->timeout) && event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
778 event_del(curl->timeout);
779 }
780 }
781 }
782
783 static int php_http_curlm_socket_callback(CURL *easy, curl_socket_t sock, int action, void *socket_data, void *assign_data)
784 {
785 php_http_client_t *context = socket_data;
786 php_http_client_curl_t *curl = context->ctx;
787
788 #if DBG_EVENTS
789 fprintf(stderr, "S");
790 #endif
791 if (curl->useevents) {
792 int events = EV_PERSIST;
793 php_http_curlm_event_t *ev = assign_data;
794 TSRMLS_FETCH_FROM_CTX(context->ts);
795
796 if (!ev) {
797 ev = ecalloc(1, sizeof(php_http_curlm_event_t));
798 ev->context = context;
799 curl_multi_assign(curl->handle->multi, sock, ev);
800 } else {
801 event_del(&ev->evnt);
802 }
803
804 switch (action) {
805 case CURL_POLL_IN:
806 events |= EV_READ;
807 break;
808 case CURL_POLL_OUT:
809 events |= EV_WRITE;
810 break;
811 case CURL_POLL_INOUT:
812 events |= EV_READ|EV_WRITE;
813 break;
814
815 case CURL_POLL_REMOVE:
816 efree(ev);
817 /* no break */
818 case CURL_POLL_NONE:
819 return 0;
820
821 default:
822 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown socket action %d", action);
823 return -1;
824 }
825
826 event_assign(&ev->evnt, curl->evbase, sock, events, php_http_curlm_event_callback, context);
827 event_add(&ev->evnt, NULL);
828 }
829
830 return 0;
831 }
832
833 static void php_http_curlm_timer_callback(CURLM *multi, long timeout_ms, void *timer_data)
834 {
835 php_http_client_t *context = timer_data;
836 php_http_client_curl_t *curl = context->ctx;
837
838 #if DBG_EVENTS
839 fprintf(stderr, "\ntimer <- timeout_ms: %ld\n", timeout_ms);
840 #endif
841 if (curl->useevents) {
842
843 if (timeout_ms < 0) {
844 php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, context);
845 } else if (timeout_ms > 0 || !event_initialized(curl->timeout) || !event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
846 struct timeval timeout;
847
848 if (!event_initialized(curl->timeout)) {
849 event_assign(curl->timeout, curl->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, context);
850 }
851
852 timeout.tv_sec = timeout_ms / 1000;
853 timeout.tv_usec = (timeout_ms % 1000) * 1000;
854
855 event_add(curl->timeout, &timeout);
856 }
857 }
858 }
859
860 #endif /* HAVE_EVENT */
861
862 /* curl options */
863
864 static php_http_options_t php_http_curle_options, php_http_curlm_options;
865
866 #define PHP_HTTP_CURLE_OPTION_CHECK_STRLEN 0x0001
867 #define PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR 0x0002
868 #define PHP_HTTP_CURLE_OPTION_TRANSFORM_MS 0x0004
869
870 static ZEND_RESULT_CODE php_http_curle_option_set_ssl_verifyhost(php_http_option_t *opt, zval *val, void *userdata)
871 {
872 php_http_client_curl_handler_t *curl = userdata;
873 CURL *ch = curl->handle;
874
875 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_SSL_VERIFYHOST, Z_BVAL_P(val) ? 2 : 0)) {
876 return FAILURE;
877 }
878 return SUCCESS;
879 }
880
881 static ZEND_RESULT_CODE php_http_curle_option_set_cookiesession(php_http_option_t *opt, zval *val, void *userdata)
882 {
883 php_http_client_curl_handler_t *curl = userdata;
884 CURL *ch = curl->handle;
885
886 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIESESSION, (long) Z_BVAL_P(val))) {
887 return FAILURE;
888 }
889 if (Z_BVAL_P(val)) {
890 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIELIST, "SESS")) {
891 return FAILURE;
892 }
893 }
894
895 return SUCCESS;
896 }
897
898 static ZEND_RESULT_CODE php_http_curle_option_set_cookiestore(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 php_http_curle_storage_t *storage = php_http_curle_get_storage(curl->handle);
903
904 if (storage->cookiestore) {
905 pefree(storage->cookiestore, 1);
906 }
907 if (val && Z_STRLEN_P(val)) {
908 storage->cookiestore = pestrndup(Z_STRVAL_P(val), Z_STRLEN_P(val), 1);
909 } else {
910 storage->cookiestore = NULL;
911 }
912 if ( CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIEFILE, storage->cookiestore)
913 || CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIEJAR, storage->cookiestore)
914 ) {
915 return FAILURE;
916 }
917
918 return SUCCESS;
919 }
920
921 static ZEND_RESULT_CODE php_http_curle_option_set_cookies(php_http_option_t *opt, zval *val, void *userdata)
922 {
923 php_http_client_curl_handler_t *curl = userdata;
924 CURL *ch = curl->handle;
925 TSRMLS_FETCH_FROM_CTX(curl->client->ts);
926
927 if (val && Z_TYPE_P(val) != IS_NULL) {
928 if (curl->options.encode_cookies) {
929 if (SUCCESS == php_http_url_encode_hash_ex(HASH_OF(val), &curl->options.cookies, ZEND_STRL(";"), ZEND_STRL("="), NULL, 0 TSRMLS_CC)) {
930 php_http_buffer_fix(&curl->options.cookies);
931 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, curl->options.cookies.data)) {
932 return FAILURE;
933 }
934 } else {
935 return FAILURE;
936 }
937 } else {
938 HashPosition pos;
939 php_http_array_hashkey_t cookie_key = php_http_array_hashkey_init(0);
940 zval **cookie_val;
941
942 FOREACH_KEYVAL(pos, val, cookie_key, cookie_val) {
943 zval *zv = php_http_ztyp(IS_STRING, *cookie_val);
944
945 php_http_array_hashkey_stringify(&cookie_key);
946 php_http_buffer_appendf(&curl->options.cookies, "%s=%s; ", cookie_key.str, Z_STRVAL_P(zv));
947 php_http_array_hashkey_stringfree(&cookie_key);
948
949 zval_ptr_dtor(&zv);
950 }
951
952 php_http_buffer_fix(&curl->options.cookies);
953 if (curl->options.cookies.used) {
954 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, curl->options.cookies.data)) {
955 return FAILURE;
956 }
957 }
958 }
959 } else {
960 php_http_buffer_reset(&curl->options.cookies);
961 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, NULL)) {
962 return FAILURE;
963 }
964 }
965 return SUCCESS;
966 }
967
968 static ZEND_RESULT_CODE php_http_curle_option_set_encodecookies(php_http_option_t *opt, zval *val, void *userdata)
969 {
970 php_http_client_curl_handler_t *curl = userdata;
971
972 curl->options.encode_cookies = Z_BVAL_P(val);
973 return SUCCESS;
974 }
975
976 static ZEND_RESULT_CODE php_http_curle_option_set_lastmodified(php_http_option_t *opt, zval *val, void *userdata)
977 {
978 php_http_client_curl_handler_t *curl = userdata;
979 CURL *ch = curl->handle;
980 TSRMLS_FETCH_FROM_CTX(curl->client->ts);
981
982 if (Z_LVAL_P(val)) {
983 if (Z_LVAL_P(val) > 0) {
984 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, Z_LVAL_P(val))) {
985 return FAILURE;
986 }
987 } else {
988 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, (long) sapi_get_request_time(TSRMLS_C) + Z_LVAL_P(val))) {
989 return FAILURE;
990 }
991 }
992 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMECONDITION, (long) (curl->options.range_request ? CURL_TIMECOND_IFUNMODSINCE : CURL_TIMECOND_IFMODSINCE))) {
993 return FAILURE;
994 }
995 } else {
996 if ( CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, 0)
997 || CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMECONDITION, 0)
998 ) {
999 return FAILURE;
1000 }
1001 }
1002 return SUCCESS;
1003 }
1004
1005 static ZEND_RESULT_CODE php_http_curle_option_set_compress(php_http_option_t *opt, zval *val, void *userdata)
1006 {
1007 php_http_client_curl_handler_t *curl = userdata;
1008 CURL *ch = curl->handle;
1009
1010 #if !PHP_HTTP_CURL_VERSION(7,21,6)
1011 # define CURLOPT_ACCEPT_ENCODING CURLOPT_ENCODING
1012 #endif
1013 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_ACCEPT_ENCODING, Z_BVAL_P(val) ? "" : NULL)) {
1014 return FAILURE;
1015 }
1016 return SUCCESS;
1017 }
1018
1019 static ZEND_RESULT_CODE php_http_curle_option_set_etag(php_http_option_t *opt, zval *val, void *userdata)
1020 {
1021 php_http_client_curl_handler_t *curl = userdata;
1022 php_http_buffer_t header;
1023
1024 if (Z_STRLEN_P(val)) {
1025 zend_bool is_quoted = !((Z_STRVAL_P(val)[0] != '"') || (Z_STRVAL_P(val)[Z_STRLEN_P(val)-1] != '"'));
1026 php_http_buffer_init(&header);
1027 php_http_buffer_appendf(&header, is_quoted?"%s: %s":"%s: \"%s\"", curl->options.range_request?"If-Match":"If-None-Match", Z_STRVAL_P(val));
1028 php_http_buffer_fix(&header);
1029 curl->options.headers = curl_slist_append(curl->options.headers, header.data);
1030 php_http_buffer_dtor(&header);
1031 }
1032 return SUCCESS;
1033 }
1034
1035 static ZEND_RESULT_CODE php_http_curle_option_set_range(php_http_option_t *opt, zval *val, void *userdata)
1036 {
1037 php_http_client_curl_handler_t *curl = userdata;
1038 CURL *ch = curl->handle;
1039 TSRMLS_FETCH_FROM_CTX(curl->client->ts);
1040
1041 php_http_buffer_reset(&curl->options.ranges);
1042
1043 if (val && Z_TYPE_P(val) != IS_NULL) {
1044 HashPosition pos;
1045 zval **rr, **rb, **re;
1046
1047 FOREACH_VAL(pos, val, rr) {
1048 if (Z_TYPE_PP(rr) == IS_ARRAY) {
1049 if (2 == php_http_array_list(Z_ARRVAL_PP(rr) TSRMLS_CC, 2, &rb, &re)) {
1050 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))) &&
1051 ((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)))) {
1052 zval *rbl = php_http_ztyp(IS_LONG, *rb);
1053 zval *rel = php_http_ztyp(IS_LONG, *re);
1054
1055 if ((Z_LVAL_P(rbl) >= 0) && (Z_LVAL_P(rel) >= 0)) {
1056 php_http_buffer_appendf(&curl->options.ranges, "%ld-%ld,", Z_LVAL_P(rbl), Z_LVAL_P(rel));
1057 }
1058 zval_ptr_dtor(&rbl);
1059 zval_ptr_dtor(&rel);
1060 }
1061
1062 }
1063 }
1064 }
1065
1066 if (curl->options.ranges.used) {
1067 curl->options.range_request = 1;
1068 /* ditch last comma */
1069 curl->options.ranges.data[curl->options.ranges.used - 1] = '\0';
1070 }
1071 }
1072
1073 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RANGE, curl->options.ranges.data)) {
1074 return FAILURE;
1075 }
1076 return SUCCESS;
1077 }
1078
1079 static ZEND_RESULT_CODE php_http_curle_option_set_resume(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
1084 if (Z_LVAL_P(val) > 0) {
1085 curl->options.range_request = 1;
1086 }
1087 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESUME_FROM, Z_LVAL_P(val))) {
1088 return FAILURE;
1089 }
1090 return SUCCESS;
1091 }
1092
1093 static ZEND_RESULT_CODE php_http_curle_option_set_retrydelay(php_http_option_t *opt, zval *val, void *userdata)
1094 {
1095 php_http_client_curl_handler_t *curl = userdata;
1096
1097 curl->options.retry.delay = Z_DVAL_P(val);
1098 return SUCCESS;
1099 }
1100
1101 static ZEND_RESULT_CODE php_http_curle_option_set_retrycount(php_http_option_t *opt, zval *val, void *userdata)
1102 {
1103 php_http_client_curl_handler_t *curl = userdata;
1104
1105 curl->options.retry.count = Z_LVAL_P(val);
1106 return SUCCESS;
1107 }
1108
1109 static ZEND_RESULT_CODE php_http_curle_option_set_redirect(php_http_option_t *opt, zval *val, void *userdata)
1110 {
1111 php_http_client_curl_handler_t *curl = userdata;
1112 CURL *ch = curl->handle;
1113
1114 if ( CURLE_OK != curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, Z_LVAL_P(val) ? 1L : 0L)
1115 || CURLE_OK != curl_easy_setopt(ch, CURLOPT_MAXREDIRS, curl->options.redirects = Z_LVAL_P(val))
1116 ) {
1117 return FAILURE;
1118 }
1119 return SUCCESS;
1120 }
1121
1122 static ZEND_RESULT_CODE php_http_curle_option_set_portrange(php_http_option_t *opt, zval *val, void *userdata)
1123 {
1124 php_http_client_curl_handler_t *curl = userdata;
1125 CURL *ch = curl->handle;
1126 long localport = 0, localportrange = 0;
1127 TSRMLS_FETCH_FROM_CTX(curl->client->ts);
1128
1129 if (val && Z_TYPE_P(val) != IS_NULL) {
1130 zval **z_port_start, *zps_copy = NULL, **z_port_end, *zpe_copy = NULL;
1131
1132 switch (php_http_array_list(Z_ARRVAL_P(val) TSRMLS_CC, 2, &z_port_start, &z_port_end)) {
1133 case 2:
1134 zps_copy = php_http_ztyp(IS_LONG, *z_port_start);
1135 zpe_copy = php_http_ztyp(IS_LONG, *z_port_end);
1136 localportrange = labs(Z_LVAL_P(zps_copy)-Z_LVAL_P(zpe_copy))+1L;
1137 /* no break */
1138 case 1:
1139 if (!zps_copy) {
1140 zps_copy = php_http_ztyp(IS_LONG, *z_port_start);
1141 }
1142 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);
1143 zval_ptr_dtor(&zps_copy);
1144 if (zpe_copy) {
1145 zval_ptr_dtor(&zpe_copy);
1146 }
1147 break;
1148 default:
1149 break;
1150 }
1151 }
1152 if ( CURLE_OK != curl_easy_setopt(ch, CURLOPT_LOCALPORT, localport)
1153 || CURLE_OK != curl_easy_setopt(ch, CURLOPT_LOCALPORTRANGE, localportrange)
1154 ) {
1155 return FAILURE;
1156 }
1157 return SUCCESS;
1158 }
1159
1160 #if PHP_HTTP_CURL_VERSION(7,37,0)
1161 static ZEND_RESULT_CODE php_http_curle_option_set_proxyheader(php_http_option_t *opt, zval *val, void *userdata)
1162 {
1163 php_http_client_curl_handler_t *curl = userdata;
1164 TSRMLS_FETCH_FROM_CTX(curl->client->ts);
1165
1166 if (val && Z_TYPE_P(val) != IS_NULL) {
1167 php_http_array_hashkey_t header_key = php_http_array_hashkey_init(0);
1168 zval **header_val, *header_cpy;
1169 HashPosition pos;
1170 php_http_buffer_t header;
1171
1172 php_http_buffer_init(&header);
1173 FOREACH_KEYVAL(pos, val, header_key, header_val) {
1174 if (header_key.type == HASH_KEY_IS_STRING) {
1175 header_cpy = php_http_ztyp(IS_STRING, *header_val);
1176 php_http_buffer_appendf(&header, "%s: %s", header_key.str, Z_STRVAL_P(header_cpy));
1177 php_http_buffer_fix(&header);
1178 curl->options.proxyheaders = curl_slist_append(curl->options.proxyheaders, header.data);
1179 php_http_buffer_reset(&header);
1180
1181 zval_ptr_dtor(&header_cpy);
1182 }
1183 }
1184 php_http_buffer_dtor(&header);
1185 }
1186 if (CURLE_OK != curl_easy_setopt(curl->handle, CURLOPT_PROXYHEADER, curl->options.proxyheaders)) {
1187 return FAILURE;
1188 }
1189 if (CURLE_OK != curl_easy_setopt(curl->handle, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE)) {
1190 curl_easy_setopt(curl->handle, CURLOPT_PROXYHEADER, NULL);
1191 return FAILURE;
1192 }
1193 return SUCCESS;
1194 }
1195 #endif
1196
1197 #if PHP_HTTP_CURL_VERSION(7,21,3)
1198 static ZEND_RESULT_CODE php_http_curle_option_set_resolve(php_http_option_t *opt, zval *val, void *userdata)
1199 {
1200 php_http_client_curl_handler_t *curl = userdata;
1201 CURL *ch = curl->handle;
1202 TSRMLS_FETCH_FROM_CTX(curl->client->ts);
1203
1204 if (val && Z_TYPE_P(val) != IS_NULL) {
1205 php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
1206 HashPosition pos;
1207 zval **data;
1208
1209 FOREACH_KEYVAL(pos, val, key, data) {
1210 zval *cpy = php_http_ztyp(IS_STRING, *data);
1211 curl->options.resolve = curl_slist_append(curl->options.resolve, Z_STRVAL_P(cpy));
1212 zval_ptr_dtor(&cpy);
1213 }
1214
1215 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESOLVE, curl->options.resolve)) {
1216 return FAILURE;
1217 }
1218 } else {
1219 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESOLVE, NULL)) {
1220 return FAILURE;
1221 }
1222 }
1223 return SUCCESS;
1224 }
1225 #endif
1226
1227 #if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
1228 static ZEND_RESULT_CODE php_http_curle_option_set_ssl_tlsauthtype(php_http_option_t *opt, zval *val, void *userdata)
1229 {
1230 php_http_client_curl_handler_t *curl = userdata;
1231 CURL *ch = curl->handle;
1232
1233 if (val && Z_LVAL_P(val)) {
1234 switch (Z_LVAL_P(val)) {
1235 case CURL_TLSAUTH_SRP:
1236 if (CURLE_OK == curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_SRP)) {
1237 return SUCCESS;
1238 }
1239 /* no break */
1240 default:
1241 return FAILURE;
1242 }
1243 }
1244 if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_DEF)) {
1245 return FAILURE;
1246 }
1247 return SUCCESS;
1248 }
1249 #endif
1250
1251 static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC)
1252 {
1253 php_http_option_t *opt;
1254
1255 /* url options */
1256 #if PHP_HTTP_CURL_VERSION(7,42,0)
1257 php_http_option_register(registry, ZEND_STRL("path_as_is"), CURLOPT_PATH_AS_IS, IS_BOOL);
1258 #endif
1259
1260 /* proxy */
1261 php_http_option_register(registry, ZEND_STRL("proxyhost"), CURLOPT_PROXY, IS_STRING);
1262 php_http_option_register(registry, ZEND_STRL("proxytype"), CURLOPT_PROXYTYPE, IS_LONG);
1263 php_http_option_register(registry, ZEND_STRL("proxyport"), CURLOPT_PROXYPORT, IS_LONG);
1264 if ((opt = php_http_option_register(registry, ZEND_STRL("proxyauth"), CURLOPT_PROXYUSERPWD, IS_STRING))) {
1265 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1266 }
1267 if ((opt = php_http_option_register(registry, ZEND_STRL("proxyauthtype"), CURLOPT_PROXYAUTH, IS_LONG))) {
1268 Z_LVAL(opt->defval) = CURLAUTH_ANYSAFE;
1269 }
1270 php_http_option_register(registry, ZEND_STRL("proxytunnel"), CURLOPT_HTTPPROXYTUNNEL, IS_BOOL);
1271 #if PHP_HTTP_CURL_VERSION(7,19,4)
1272 php_http_option_register(registry, ZEND_STRL("noproxy"), CURLOPT_NOPROXY, IS_STRING);
1273 #endif
1274
1275 #if PHP_HTTP_CURL_VERSION(7,37,0)
1276 if ((opt = php_http_option_register(registry, ZEND_STRL("proxyheader"), CURLOPT_PROXYHEADER, IS_ARRAY))) {
1277 opt->setter = php_http_curle_option_set_proxyheader;
1278 }
1279 #endif
1280 #if PHP_HTTP_CURL_VERSION(7,43,0)
1281 if (PHP_HTTP_CURL_FEATURE(CURL_VERSION_GSSAPI)
1282 && (opt = php_http_option_register(registry, ZEND_STRL("proxy_service_name"), CURLOPT_PROXY_SERVICE_NAME, IS_STRING))
1283 ) {
1284 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1285 }
1286 #endif
1287
1288 #if PHP_HTTP_CURL_VERSION(7,40,0)
1289 if (PHP_HTTP_CURL_FEATURE(CURL_VERSION_UNIX_SOCKETS)) {
1290 if ((opt = php_http_option_register(registry, ZEND_STRL("unix_socket_path"), CURLOPT_UNIX_SOCKET_PATH, IS_STRING))) {
1291 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1292 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1293 }
1294 }
1295 #endif
1296
1297 /* dns */
1298 if ((opt = php_http_option_register(registry, ZEND_STRL("dns_cache_timeout"), CURLOPT_DNS_CACHE_TIMEOUT, IS_LONG))) {
1299 Z_LVAL(opt->defval) = 60;
1300 }
1301 php_http_option_register(registry, ZEND_STRL("ipresolve"), CURLOPT_IPRESOLVE, IS_LONG);
1302 #if PHP_HTTP_CURL_VERSION(7,21,3)
1303 if ((opt = php_http_option_register(registry, ZEND_STRL("resolve"), CURLOPT_RESOLVE, IS_ARRAY))) {
1304 opt->setter = php_http_curle_option_set_resolve;
1305 }
1306 #endif
1307 #if PHP_HTTP_HAVE_ARES
1308 # if PHP_HTTP_CURL_VERSION(7,24,0)
1309 if ((opt = php_http_option_register(registry, ZEND_STRL("dns_servers"), CURLOPT_DNS_SERVERS, IS_STRING))) {
1310 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1311 }
1312 # endif
1313 # if PHP_HTTP_CURL_VERSION(7,33,0)
1314 if ((opt = php_http_option_register(registry, ZEND_STRL("dns_interface"), CURLOPT_DNS_INTERFACE, IS_STRING))) {
1315 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1316 }
1317 if ((opt = php_http_option_register(registry, ZEND_STRL("dns_local_ip4"), CURLOPT_DNS_LOCAL_IP4, IS_STRING))) {
1318 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1319 }
1320 if ((opt = php_http_option_register(registry, ZEND_STRL("dns_local_ip6"), CURLOPT_DNS_LOCAL_IP6, IS_STRING))) {
1321 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1322 }
1323 # endif
1324 #endif
1325
1326 /* limits */
1327 php_http_option_register(registry, ZEND_STRL("low_speed_limit"), CURLOPT_LOW_SPEED_LIMIT, IS_LONG);
1328 php_http_option_register(registry, ZEND_STRL("low_speed_time"), CURLOPT_LOW_SPEED_TIME, IS_LONG);
1329
1330 /* LSF weirdance
1331 php_http_option_register(registry, ZEND_STRL("max_send_speed"), CURLOPT_MAX_SEND_SPEED_LARGE, IS_LONG);
1332 php_http_option_register(registry, ZEND_STRL("max_recv_speed"), CURLOPT_MAX_RECV_SPEED_LARGE, IS_LONG);
1333 */
1334
1335 /* connection handling */
1336 /* crashes
1337 if ((opt = php_http_option_register(registry, ZEND_STRL("maxconnects"), CURLOPT_MAXCONNECTS, IS_LONG))) {
1338 Z_LVAL(opt->defval) = 5;
1339 }
1340 */
1341 php_http_option_register(registry, ZEND_STRL("fresh_connect"), CURLOPT_FRESH_CONNECT, IS_BOOL);
1342 php_http_option_register(registry, ZEND_STRL("forbid_reuse"), CURLOPT_FORBID_REUSE, IS_BOOL);
1343
1344 /* outgoing interface */
1345 php_http_option_register(registry, ZEND_STRL("interface"), CURLOPT_INTERFACE, IS_STRING);
1346 if ((opt = php_http_option_register(registry, ZEND_STRL("portrange"), CURLOPT_LOCALPORT, IS_ARRAY))) {
1347 opt->setter = php_http_curle_option_set_portrange;
1348 }
1349
1350 /* another endpoint port */
1351 php_http_option_register(registry, ZEND_STRL("port"), CURLOPT_PORT, IS_LONG);
1352
1353 /* RFC4007 zone_id */
1354 #if PHP_HTTP_CURL_VERSION(7,19,0)
1355 php_http_option_register(registry, ZEND_STRL("address_scope"), CURLOPT_ADDRESS_SCOPE, IS_LONG);
1356 #endif
1357
1358 /* auth */
1359 if ((opt = php_http_option_register(registry, ZEND_STRL("httpauth"), CURLOPT_USERPWD, IS_STRING))) {
1360 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1361 }
1362 if ((opt = php_http_option_register(registry, ZEND_STRL("httpauthtype"), CURLOPT_HTTPAUTH, IS_LONG))) {
1363 Z_LVAL(opt->defval) = CURLAUTH_ANYSAFE;
1364 }
1365 #if PHP_HTTP_CURL_VERSION(7,43,0)
1366 if (PHP_HTTP_CURL_FEATURE(CURL_VERSION_SSPI) || PHP_HTTP_CURL_FEATURE(CURL_VERSION_GSSAPI))
1367 if ((opt = php_http_option_register(registry, ZEND_STRL("service_name"), CURLOPT_SERVICE_NAME, IS_STRING))) {
1368 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1369 }
1370 #endif
1371
1372 /* redirects */
1373 if ((opt = php_http_option_register(registry, ZEND_STRL("redirect"), CURLOPT_FOLLOWLOCATION, IS_LONG))) {
1374 opt->setter = php_http_curle_option_set_redirect;
1375 }
1376 php_http_option_register(registry, ZEND_STRL("unrestricted_auth"), CURLOPT_UNRESTRICTED_AUTH, IS_BOOL);
1377 #if PHP_HTTP_CURL_VERSION(7,19,1)
1378 php_http_option_register(registry, ZEND_STRL("postredir"), CURLOPT_POSTREDIR, IS_LONG);
1379 #endif
1380
1381 /* retries */
1382 if ((opt = php_http_option_register(registry, ZEND_STRL("retrycount"), 0, IS_LONG))) {
1383 opt->setter = php_http_curle_option_set_retrycount;
1384 }
1385 if ((opt = php_http_option_register(registry, ZEND_STRL("retrydelay"), 0, IS_DOUBLE))) {
1386 opt->setter = php_http_curle_option_set_retrydelay;
1387 }
1388
1389 /* referer */
1390 if ((opt = php_http_option_register(registry, ZEND_STRL("referer"), CURLOPT_REFERER, IS_STRING))) {
1391 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1392 }
1393 if ((opt = php_http_option_register(registry, ZEND_STRL("autoreferer"), CURLOPT_AUTOREFERER, IS_BOOL))) {
1394 ZVAL_BOOL(&opt->defval, 1);
1395 }
1396
1397 /* useragent */
1398 if ((opt = php_http_option_register(registry, ZEND_STRL("useragent"), CURLOPT_USERAGENT, IS_STRING))) {
1399 /* don't check strlen, to allow sending no useragent at all */
1400 ZVAL_STRING(&opt->defval,
1401 "PECL_HTTP/" PHP_PECL_HTTP_VERSION " "
1402 "PHP/" PHP_VERSION " "
1403 "libcurl/" LIBCURL_VERSION
1404 , 0);
1405 }
1406
1407 /* resume */
1408 if ((opt = php_http_option_register(registry, ZEND_STRL("resume"), CURLOPT_RESUME_FROM, IS_LONG))) {
1409 opt->setter = php_http_curle_option_set_resume;
1410 }
1411 /* ranges */
1412 if ((opt = php_http_option_register(registry, ZEND_STRL("range"), CURLOPT_RANGE, IS_ARRAY))) {
1413 opt->setter = php_http_curle_option_set_range;
1414 }
1415
1416 /* etag */
1417 if ((opt = php_http_option_register(registry, ZEND_STRL("etag"), 0, IS_STRING))) {
1418 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1419 opt->setter = php_http_curle_option_set_etag;
1420 }
1421
1422 /* compression */
1423 if ((opt = php_http_option_register(registry, ZEND_STRL("compress"), 0, IS_BOOL))) {
1424 opt->setter = php_http_curle_option_set_compress;
1425 }
1426
1427 /* lastmodified */
1428 if ((opt = php_http_option_register(registry, ZEND_STRL("lastmodified"), 0, IS_LONG))) {
1429 opt->setter = php_http_curle_option_set_lastmodified;
1430 }
1431
1432 /* cookies */
1433 if ((opt = php_http_option_register(registry, ZEND_STRL("encodecookies"), 0, IS_BOOL))) {
1434 opt->setter = php_http_curle_option_set_encodecookies;
1435 ZVAL_BOOL(&opt->defval, 1);
1436 }
1437 if ((opt = php_http_option_register(registry, ZEND_STRL("cookies"), 0, IS_ARRAY))) {
1438 opt->setter = php_http_curle_option_set_cookies;
1439 }
1440
1441 /* cookiesession, don't load session cookies from cookiestore */
1442 if ((opt = php_http_option_register(registry, ZEND_STRL("cookiesession"), CURLOPT_COOKIESESSION, IS_BOOL))) {
1443 opt->setter = php_http_curle_option_set_cookiesession;
1444 }
1445 /* cookiestore, read initial cookies from that file and store cookies back into that file */
1446 if ((opt = php_http_option_register(registry, ZEND_STRL("cookiestore"), 0, IS_STRING))) {
1447 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1448 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1449 opt->setter = php_http_curle_option_set_cookiestore;
1450 }
1451
1452 /* maxfilesize */
1453 php_http_option_register(registry, ZEND_STRL("maxfilesize"), CURLOPT_MAXFILESIZE, IS_LONG);
1454
1455 /* http protocol version */
1456 php_http_option_register(registry, ZEND_STRL("protocol"), CURLOPT_HTTP_VERSION, IS_LONG);
1457
1458 /* timeouts */
1459 if ((opt = php_http_option_register(registry, ZEND_STRL("timeout"), CURLOPT_TIMEOUT_MS, IS_DOUBLE))) {
1460 opt->flags |= PHP_HTTP_CURLE_OPTION_TRANSFORM_MS;
1461 }
1462 if ((opt = php_http_option_register(registry, ZEND_STRL("connecttimeout"), CURLOPT_CONNECTTIMEOUT_MS, IS_DOUBLE))) {
1463 opt->flags |= PHP_HTTP_CURLE_OPTION_TRANSFORM_MS;
1464 Z_DVAL(opt->defval) = 3;
1465 }
1466 #if PHP_HTTP_CURL_VERSION(7,36,0)
1467 if ((opt = php_http_option_register(registry, ZEND_STRL("expect_100_timeout"), CURLOPT_EXPECT_100_TIMEOUT_MS, IS_DOUBLE))) {
1468 opt->flags |= PHP_HTTP_CURLE_OPTION_TRANSFORM_MS;
1469 Z_DVAL(opt->defval) = 1;
1470 }
1471 #endif
1472
1473 /* tcp */
1474 php_http_option_register(registry, ZEND_STRL("tcp_nodelay"), CURLOPT_TCP_NODELAY, IS_BOOL);
1475 #if PHP_HTTP_CURL_VERSION(7,25,0)
1476 php_http_option_register(registry, ZEND_STRL("tcp_keepalive"), CURLOPT_TCP_KEEPALIVE, IS_BOOL);
1477 if ((opt = php_http_option_register(registry, ZEND_STRL("tcp_keepidle"), CURLOPT_TCP_KEEPIDLE, IS_LONG))) {
1478 Z_LVAL(opt->defval) = 60;
1479 }
1480 if ((opt = php_http_option_register(registry, ZEND_STRL("tcp_keepintvl"), CURLOPT_TCP_KEEPINTVL, IS_LONG))) {
1481 Z_LVAL(opt->defval) = 60;
1482 }
1483 #endif
1484
1485 /* ssl */
1486 if (PHP_HTTP_CURL_FEATURE(CURL_VERSION_SSL)) {
1487 if ((opt = php_http_option_register(registry, ZEND_STRL("ssl"), 0, IS_ARRAY))) {
1488 registry = &opt->suboptions;
1489
1490 if ((opt = php_http_option_register(registry, ZEND_STRL("cert"), CURLOPT_SSLCERT, IS_STRING))) {
1491 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1492 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1493 }
1494 if ((opt = php_http_option_register(registry, ZEND_STRL("certtype"), CURLOPT_SSLCERTTYPE, IS_STRING))) {
1495 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1496 ZVAL_STRING(&opt->defval, "PEM", 0);
1497 }
1498 if ((opt = php_http_option_register(registry, ZEND_STRL("key"), CURLOPT_SSLKEY, IS_STRING))) {
1499 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1500 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1501 }
1502 if ((opt = php_http_option_register(registry, ZEND_STRL("keytype"), CURLOPT_SSLKEYTYPE, IS_STRING))) {
1503 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1504 ZVAL_STRING(&opt->defval, "PEM", 0);
1505 }
1506 if ((opt = php_http_option_register(registry, ZEND_STRL("keypasswd"), CURLOPT_SSLKEYPASSWD, IS_STRING))) {
1507 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1508 }
1509 php_http_option_register(registry, ZEND_STRL("engine"), CURLOPT_SSLENGINE, IS_STRING);
1510 php_http_option_register(registry, ZEND_STRL("version"), CURLOPT_SSLVERSION, IS_LONG);
1511 if ((opt = php_http_option_register(registry, ZEND_STRL("verifypeer"), CURLOPT_SSL_VERIFYPEER, IS_BOOL))) {
1512 ZVAL_BOOL(&opt->defval, 1);
1513 }
1514 if ((opt = php_http_option_register(registry, ZEND_STRL("verifyhost"), CURLOPT_SSL_VERIFYHOST, IS_BOOL))) {
1515 ZVAL_BOOL(&opt->defval, 1);
1516 opt->setter = php_http_curle_option_set_ssl_verifyhost;
1517 }
1518 #if PHP_HTTP_CURL_VERSION(7,41,0) && (defined(PHP_HTTP_HAVE_OPENSSL) || defined(PHP_HTTP_HAVE_NSS) || defined(PHP_HTTP_HAVE_GNUTLS))
1519 php_http_option_register(registry, ZEND_STRL("verifystatus"), CURLOPT_SSL_VERIFYSTATUS, IS_BOOL);
1520 #endif
1521 php_http_option_register(registry, ZEND_STRL("cipher_list"), CURLOPT_SSL_CIPHER_LIST, IS_STRING);
1522 if ((opt = php_http_option_register(registry, ZEND_STRL("cainfo"), CURLOPT_CAINFO, IS_STRING))) {
1523 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1524 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1525 #ifdef PHP_HTTP_CURL_CAINFO
1526 ZVAL_STRING(&opt->defval, PHP_HTTP_CURL_CAINFO, 0);
1527 #endif
1528 }
1529 if ((opt = php_http_option_register(registry, ZEND_STRL("capath"), CURLOPT_CAPATH, IS_STRING))) {
1530 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1531 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1532 #ifdef PHP_HTTP_CURL_CAPATH
1533 ZVAL_STRING(&opt->defval, PHP_HTTP_CURL_CAPATH, 0);
1534 #endif
1535 }
1536 if ((opt = php_http_option_register(registry, ZEND_STRL("random_file"), CURLOPT_RANDOM_FILE, IS_STRING))) {
1537 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1538 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1539 }
1540 if ((opt = php_http_option_register(registry, ZEND_STRL("egdsocket"), CURLOPT_EGDSOCKET, IS_STRING))) {
1541 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1542 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1543 }
1544 #if PHP_HTTP_CURL_VERSION(7,19,0)
1545 if ((opt = php_http_option_register(registry, ZEND_STRL("issuercert"), CURLOPT_ISSUERCERT, IS_STRING))) {
1546 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1547 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1548 }
1549 # ifdef PHP_HTTP_HAVE_OPENSSL
1550 if ((opt = php_http_option_register(registry, ZEND_STRL("crlfile"), CURLOPT_CRLFILE, IS_STRING))) {
1551 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1552 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1553 }
1554 # endif
1555 #endif
1556 #if (PHP_HTTP_CURL_VERSION(7,19,1) && defined(PHP_HTTP_HAVE_OPENSSL)) || (PHP_HTTP_CURL_VERSION(7,34,0) && defined(PHP_HTTP_HAVE_NSS)) || (PHP_HTTP_CURL_VERSION(7,42,0) && defined(PHP_HTTP_HAVE_GNUTLS)) || (PHP_HTTP_CURL_VERSION(7,39,0) && defined(PHP_HTTP_HAVE_GSKIT))
1557 php_http_option_register(registry, ZEND_STRL("certinfo"), CURLOPT_CERTINFO, IS_BOOL);
1558 #endif
1559 #if PHP_HTTP_CURL_VERSION(7,36,0)
1560 if ((opt = php_http_option_register(registry, ZEND_STRL("enable_npn"), CURLOPT_SSL_ENABLE_NPN, IS_BOOL))) {
1561 ZVAL_BOOL(&opt->defval, 1);
1562 }
1563 if ((opt = php_http_option_register(registry, ZEND_STRL("enable_alpn"), CURLOPT_SSL_ENABLE_ALPN, IS_BOOL))) {
1564 ZVAL_BOOL(&opt->defval, 1);
1565 }
1566 #endif
1567 #if PHP_HTTP_CURL_VERSION(7,39,0)
1568 /* FIXME: see http://curl.haxx.se/libcurl/c/CURLOPT_PINNEDPUBLICKEY.html#AVAILABILITY */
1569 if ((opt = php_http_option_register(registry, ZEND_STRL("pinned_publickey"), CURLOPT_PINNEDPUBLICKEY, IS_STRING))) {
1570 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1571 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
1572 }
1573 #endif
1574 #if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
1575 if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthtype"), CURLOPT_TLSAUTH_TYPE, IS_LONG))) {
1576 opt->setter = php_http_curle_option_set_ssl_tlsauthtype;
1577 }
1578 if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthuser"), CURLOPT_TLSAUTH_USERNAME, IS_STRING))) {
1579 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1580 }
1581 if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthpass"), CURLOPT_TLSAUTH_PASSWORD, IS_STRING))) {
1582 opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
1583 }
1584 #endif
1585 #if PHP_HTTP_CURL_VERSION(7,42,0) && (defined(PHP_HTTP_HAVE_NSS) || defined(PHP_HTTP_HAVE_DARWINSSL))
1586 php_http_option_register(registry, ZEND_STRL("falsestart"), CURLOPT_SSL_FALSESTART, IS_BOOL);
1587 #endif
1588 }
1589 }
1590 }
1591
1592 static zval *php_http_curle_get_option(php_http_option_t *opt, HashTable *options, void *userdata)
1593 {
1594 php_http_client_curl_handler_t *curl = userdata;
1595 zval *option;
1596
1597 if ((option = php_http_option_get(opt, options, NULL))) {
1598 option = php_http_ztyp(opt->type, option);
1599 zend_hash_quick_update(&curl->options.cache, opt->name.s, opt->name.l, opt->name.h, &option, sizeof(zval *), NULL);
1600 }
1601 return option;
1602 }
1603
1604 static ZEND_RESULT_CODE php_http_curle_set_option(php_http_option_t *opt, zval *val, void *userdata)
1605 {
1606 php_http_client_curl_handler_t *curl = userdata;
1607 CURL *ch = curl->handle;
1608 zval tmp;
1609 CURLcode rc = CURLE_UNKNOWN_OPTION;
1610 ZEND_RESULT_CODE rv = SUCCESS;
1611 TSRMLS_FETCH_FROM_CTX(curl->client->ts);
1612
1613 if (!val) {
1614 val = &opt->defval;
1615 }
1616
1617 switch (opt->type) {
1618 case IS_BOOL:
1619 if (opt->setter) {
1620 rv = opt->setter(opt, val, curl);
1621 } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, (long) Z_BVAL_P(val))) {
1622 rv = FAILURE;
1623 }
1624 break;
1625
1626 case IS_LONG:
1627 if (opt->setter) {
1628 rv = opt->setter(opt, val, curl);
1629 } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, Z_LVAL_P(val))) {
1630 rv = FAILURE;
1631 }
1632 break;
1633
1634 case IS_STRING:
1635 if (opt->setter) {
1636 rv = opt->setter(opt, val, curl);
1637 } else if ((opt->flags & PHP_HTTP_CURLE_OPTION_CHECK_STRLEN) && !Z_STRLEN_P(val)) {
1638 if (CURLE_OK != (rc = curl_easy_setopt(ch, opt->option, NULL))) {
1639 rv = FAILURE;
1640 }
1641 } 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)) {
1642 if (CURLE_OK != (rc = curl_easy_setopt(ch, opt->option, NULL))) {
1643 rv = FAILURE;
1644 }
1645 } else if (CURLE_OK != (rc = curl_easy_setopt(ch, opt->option, Z_STRVAL_P(val)))) {
1646 rv = FAILURE;
1647 }
1648 break;
1649
1650 case IS_DOUBLE:
1651 if (opt->flags & PHP_HTTP_CURLE_OPTION_TRANSFORM_MS) {
1652 tmp = *val;
1653 Z_DVAL(tmp) *= 1000;
1654 val = &tmp;
1655 }
1656 if (opt->setter) {
1657 rv = opt->setter(opt, val, curl);
1658 } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, (long) Z_DVAL_P(val))) {
1659 rv = FAILURE;
1660 }
1661 break;
1662
1663 case IS_ARRAY:
1664 if (opt->setter) {
1665 rv = opt->setter(opt, val, curl);
1666 } else if (Z_TYPE_P(val) != IS_NULL) {
1667 rv = php_http_options_apply(&opt->suboptions, Z_ARRVAL_P(val), curl);
1668 }
1669 break;
1670
1671 default:
1672 if (opt->setter) {
1673 rv = opt->setter(opt, val, curl);
1674 } else {
1675 rv = FAILURE;
1676 }
1677 break;
1678 }
1679 if (rv != SUCCESS) {
1680 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, curl_easy_strerror(rc));
1681 }
1682 return rv;
1683 }
1684
1685 #if PHP_HTTP_CURL_VERSION(7,30,0)
1686 static ZEND_RESULT_CODE php_http_curlm_option_set_pipelining_bl(php_http_option_t *opt, zval *value, void *userdata)
1687 {
1688 php_http_client_t *client = userdata;
1689 php_http_client_curl_t *curl = client->ctx;
1690 CURLM *ch = curl->handle->multi;
1691 HashTable tmp_ht;
1692 char **bl = NULL;
1693 TSRMLS_FETCH_FROM_CTX(client->ts);
1694
1695 /* array of char *, ending with a NULL */
1696 if (value && Z_TYPE_P(value) != IS_NULL) {
1697 zval **entry;
1698 HashPosition pos;
1699 HashTable *ht = HASH_OF(value);
1700 int c = zend_hash_num_elements(ht);
1701 char **ptr = ecalloc(c + 1, sizeof(char *));
1702
1703 bl = ptr;
1704
1705 zend_hash_init(&tmp_ht, c, NULL, ZVAL_PTR_DTOR, 0);
1706 array_join(ht, &tmp_ht, 0, ARRAY_JOIN_STRINGIFY);
1707
1708 FOREACH_HASH_VAL(pos, &tmp_ht, entry) {
1709 *ptr++ = Z_STRVAL_PP(entry);
1710 }
1711 }
1712
1713 if (CURLM_OK != curl_multi_setopt(ch, opt->option, bl)) {
1714 if (bl) {
1715 efree(bl);
1716 zend_hash_destroy(&tmp_ht);
1717 }
1718 return FAILURE;
1719 }
1720
1721 if (bl) {
1722 efree(bl);
1723 zend_hash_destroy(&tmp_ht);
1724 }
1725 return SUCCESS;
1726 }
1727 #endif
1728
1729 #if PHP_HTTP_HAVE_EVENT
1730 static inline ZEND_RESULT_CODE php_http_curlm_use_eventloop(php_http_client_t *h, zend_bool enable)
1731 {
1732 php_http_client_curl_t *curl = h->ctx;
1733
1734 if ((curl->useevents = enable)) {
1735 if (!curl->evbase) {
1736 curl->evbase = event_base_new();
1737 }
1738 if (!curl->timeout) {
1739 curl->timeout = ecalloc(1, sizeof(struct event));
1740 }
1741 curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, h);
1742 curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, php_http_curlm_socket_callback);
1743 curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, h);
1744 curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, php_http_curlm_timer_callback);
1745 } else {
1746 curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, NULL);
1747 curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, NULL);
1748 curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, NULL);
1749 curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, NULL);
1750 }
1751
1752 return SUCCESS;
1753 }
1754
1755 static ZEND_RESULT_CODE php_http_curlm_option_set_use_eventloop(php_http_option_t *opt, zval *value, void *userdata)
1756 {
1757 php_http_client_t *client = userdata;
1758
1759 return php_http_curlm_use_eventloop(client, value && Z_BVAL_P(value));
1760 }
1761 #endif
1762
1763 static ZEND_RESULT_CODE php_http_curlm_option_set_share_cookies(php_http_option_t *opt, zval *value, void *userdata)
1764 {
1765 php_http_client_t *client = userdata;
1766 php_http_client_curl_t *curl = client->ctx;
1767 CURLSHcode rc;
1768
1769 if (Z_BVAL_P(value)) {
1770 rc = curl_share_setopt(curl->handle->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
1771 } else {
1772 rc = curl_share_setopt(curl->handle->share, CURLSHOPT_UNSHARE, CURL_LOCK_DATA_COOKIE);
1773 }
1774
1775 if (CURLSHE_OK != rc) {
1776 TSRMLS_FETCH_FROM_CTX(client->ts);
1777 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, curl_share_strerror(rc));
1778 return FAILURE;
1779 }
1780 return SUCCESS;
1781 }
1782
1783 static ZEND_RESULT_CODE php_http_curlm_option_set_share_ssl(php_http_option_t *opt, zval *value, void *userdata)
1784 {
1785 php_http_client_t *client = userdata;
1786 php_http_client_curl_t *curl = client->ctx;
1787 CURLSHcode rc;
1788
1789 if (Z_BVAL_P(value)) {
1790 rc = curl_share_setopt(curl->handle->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
1791 } else {
1792 rc = curl_share_setopt(curl->handle->share, CURLSHOPT_UNSHARE, CURL_LOCK_DATA_SSL_SESSION);
1793 }
1794
1795 if (CURLSHE_OK != rc) {
1796 TSRMLS_FETCH_FROM_CTX(client->ts);
1797 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, curl_share_strerror(rc));
1798 return FAILURE;
1799 }
1800 return SUCCESS;
1801 }
1802
1803 static void php_http_curlm_options_init(php_http_options_t *registry TSRMLS_DC)
1804 {
1805 php_http_option_t *opt;
1806
1807 /* set size of connection cache */
1808 if ((opt = php_http_option_register(registry, ZEND_STRL("maxconnects"), CURLMOPT_MAXCONNECTS, IS_LONG))) {
1809 /* -1 == default, 0 == unlimited */
1810 ZVAL_LONG(&opt->defval, -1);
1811 }
1812 /* set max number of connections to a single host */
1813 #if PHP_HTTP_CURL_VERSION(7,30,0)
1814 php_http_option_register(registry, ZEND_STRL("max_host_connections"), CURLMOPT_MAX_HOST_CONNECTIONS, IS_LONG);
1815 #endif
1816 /* maximum number of requests in a pipeline */
1817 #if PHP_HTTP_CURL_VERSION(7,30,0)
1818 if ((opt = php_http_option_register(registry, ZEND_STRL("max_pipeline_length"), CURLMOPT_MAX_PIPELINE_LENGTH, IS_LONG))) {
1819 ZVAL_LONG(&opt->defval, 5);
1820 }
1821 #endif
1822 /* max simultaneously open connections */
1823 #if PHP_HTTP_CURL_VERSION(7,30,0)
1824 php_http_option_register(registry, ZEND_STRL("max_total_connections"), CURLMOPT_MAX_TOTAL_CONNECTIONS, IS_LONG);
1825 #endif
1826 /* enable/disable HTTP pipelining */
1827 php_http_option_register(registry, ZEND_STRL("pipelining"), CURLMOPT_PIPELINING, IS_BOOL);
1828 /* chunk length threshold for pipelining */
1829 #if PHP_HTTP_CURL_VERSION(7,30,0)
1830 php_http_option_register(registry, ZEND_STRL("chunk_length_penalty_size"), CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, IS_LONG);
1831 #endif
1832 /* size threshold for pipelining penalty */
1833 #if PHP_HTTP_CURL_VERSION(7,30,0)
1834 php_http_option_register(registry, ZEND_STRL("content_length_penalty_size"), CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, IS_LONG);
1835 #endif
1836 /* pipelining server blacklist */
1837 #if PHP_HTTP_CURL_VERSION(7,30,0)
1838 if ((opt = php_http_option_register(registry, ZEND_STRL("pipelining_server_bl"), CURLMOPT_PIPELINING_SERVER_BL, IS_ARRAY))) {
1839 opt->setter = php_http_curlm_option_set_pipelining_bl;
1840 }
1841 #endif
1842 /* pipelining host blacklist */
1843 #if PHP_HTTP_CURL_VERSION(7,30,0)
1844 if ((opt = php_http_option_register(registry, ZEND_STRL("pipelining_site_bl"), CURLMOPT_PIPELINING_SITE_BL, IS_ARRAY))) {
1845 opt->setter = php_http_curlm_option_set_pipelining_bl;
1846 }
1847 #endif
1848 /* events */
1849 #if PHP_HTTP_HAVE_EVENT
1850 if ((opt = php_http_option_register(registry, ZEND_STRL("use_eventloop"), 0, IS_BOOL))) {
1851 opt->setter = php_http_curlm_option_set_use_eventloop;
1852 }
1853 #endif
1854 /* share */
1855 if ((opt = php_http_option_register(registry, ZEND_STRL("share_cookies"), 0, IS_BOOL))) {
1856 opt->setter = php_http_curlm_option_set_share_cookies;
1857 ZVAL_BOOL(&opt->defval, 1);
1858 }
1859 if ((opt = php_http_option_register(registry, ZEND_STRL("share_ssl"), 0, IS_BOOL))) {
1860 opt->setter = php_http_curlm_option_set_share_ssl;
1861 ZVAL_BOOL(&opt->defval, 1);
1862 }
1863 }
1864
1865 static ZEND_RESULT_CODE php_http_curlm_set_option(php_http_option_t *opt, zval *val, void *userdata)
1866 {
1867 php_http_client_t *client = userdata;
1868 php_http_client_curl_t *curl = client->ctx;
1869 CURLM *ch = curl->handle->multi;
1870 zval *orig = val;
1871 CURLMcode rc = CURLM_UNKNOWN_OPTION;
1872 ZEND_RESULT_CODE rv = SUCCESS;
1873 TSRMLS_FETCH_FROM_CTX(client->ts);
1874
1875 if (!val) {
1876 val = &opt->defval;
1877 } else if (opt->type && Z_TYPE_P(val) != opt->type && !(Z_TYPE_P(val) == IS_NULL && opt->type == IS_ARRAY)) {
1878 val = php_http_ztyp(opt->type, val);
1879 }
1880
1881 if (opt->setter) {
1882 rv = opt->setter(opt, val, client);
1883 } else {
1884 switch (opt->type) {
1885 case IS_BOOL:
1886 if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, (long) Z_BVAL_P(val)))) {
1887 rv = FAILURE;
1888 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, curl_multi_strerror(rc));
1889 }
1890 break;
1891 case IS_LONG:
1892 if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, Z_LVAL_P(val)))) {
1893 rv = FAILURE;
1894 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, curl_multi_strerror(rc));
1895 }
1896 break;
1897 default:
1898 rv = FAILURE;
1899 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s", opt->name.s);
1900 break;
1901 }
1902 }
1903
1904 if (val && val != orig && val != &opt->defval) {
1905 zval_ptr_dtor(&val);
1906 }
1907
1908 return rv;
1909 }
1910
1911 /* client ops */
1912
1913 static ZEND_RESULT_CODE php_http_client_curl_handler_reset(php_http_client_curl_handler_t *curl)
1914 {
1915 CURL *ch = curl->handle;
1916 php_http_curle_storage_t *st;
1917
1918 if ((st = php_http_curle_get_storage(ch))) {
1919 if (st->url) {
1920 pefree(st->url, 1);
1921 st->url = NULL;
1922 }
1923 if (st->cookiestore) {
1924 pefree(st->cookiestore, 1);
1925 st->cookiestore = NULL;
1926 }
1927 st->errorbuffer[0] = '\0';
1928 st->errorcode = 0;
1929 }
1930
1931 curl_easy_setopt(ch, CURLOPT_URL, NULL);
1932 curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, NULL);
1933 curl_easy_setopt(ch, CURLOPT_HTTPGET, 1L);
1934 curl_easy_setopt(ch, CURLOPT_NOBODY, 0L);
1935 /* libcurl < 7.19.6 does not clear auth info with USERPWD set to NULL */
1936 #if PHP_HTTP_CURL_VERSION(7,19,1)
1937 curl_easy_setopt(ch, CURLOPT_PROXYUSERNAME, NULL);
1938 curl_easy_setopt(ch, CURLOPT_PROXYPASSWORD, NULL);
1939 curl_easy_setopt(ch, CURLOPT_USERNAME, NULL);
1940 curl_easy_setopt(ch, CURLOPT_PASSWORD, NULL);
1941 #endif
1942
1943 #if PHP_HTTP_CURL_VERSION(7,21,3)
1944 if (curl->options.resolve) {
1945 curl_slist_free_all(curl->options.resolve);
1946 curl->options.resolve = NULL;
1947 }
1948 #endif
1949 curl->options.retry.count = 0;
1950 curl->options.retry.delay = 0;
1951 curl->options.redirects = 0;
1952 curl->options.encode_cookies = 1;
1953
1954 if (curl->options.headers) {
1955 curl_slist_free_all(curl->options.headers);
1956 curl->options.headers = NULL;
1957 }
1958 if (curl->options.proxyheaders) {
1959 curl_slist_free_all(curl->options.proxyheaders);
1960 curl->options.proxyheaders = NULL;
1961 }
1962
1963 php_http_buffer_reset(&curl->options.cookies);
1964 php_http_buffer_reset(&curl->options.ranges);
1965
1966 return SUCCESS;
1967 }
1968
1969 static php_http_client_curl_handler_t *php_http_client_curl_handler_init(php_http_client_t *h, php_resource_factory_t *rf)
1970 {
1971 void *handle;
1972 php_http_client_curl_t *curl = h->ctx;
1973 php_http_client_curl_handler_t *handler;
1974 TSRMLS_FETCH_FROM_CTX(h->ts);
1975
1976 if (!(handle = php_resource_factory_handle_ctor(rf, NULL TSRMLS_CC))) {
1977 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to initialize curl handle->multi");
1978 return NULL;
1979 }
1980
1981 handler = ecalloc(1, sizeof(*handler));
1982 handler->rf = rf;
1983 handler->client = h;
1984 handler->handle = handle;
1985 handler->response.body = php_http_message_body_init(NULL, NULL TSRMLS_CC);
1986 php_http_buffer_init(&handler->response.headers);
1987 php_http_buffer_init(&handler->options.cookies);
1988 php_http_buffer_init(&handler->options.ranges);
1989 zend_hash_init(&handler->options.cache, 0, NULL, ZVAL_PTR_DTOR, 0);
1990
1991 #if defined(ZTS)
1992 curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1L);
1993 #endif
1994 curl_easy_setopt(handle, CURLOPT_HEADER, 0L);
1995 curl_easy_setopt(handle, CURLOPT_FILETIME, 1L);
1996 curl_easy_setopt(handle, CURLOPT_AUTOREFERER, 1L);
1997 curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L);
1998 curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0L);
1999 curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, php_http_curle_header_callback);
2000 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, php_http_curle_body_callback);
2001 curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, php_http_curle_raw_callback);
2002 curl_easy_setopt(handle, CURLOPT_READFUNCTION, php_http_curle_read_callback);
2003 curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, php_http_curle_seek_callback);
2004 #if PHP_HTTP_CURL_VERSION(7,32,0)
2005 curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, php_http_curle_xferinfo_callback);
2006 curl_easy_setopt(handle, CURLOPT_XFERINFODATA, handler);
2007 #else
2008 curl_easy_setopt(handle, CURLOPT_PROGRESSFUNCTION, php_http_curle_progress_callback);
2009 curl_easy_setopt(handle, CURLOPT_PROGRESSDATA, handler);
2010 #endif
2011 curl_easy_setopt(handle, CURLOPT_DEBUGDATA, handler);
2012 curl_easy_setopt(handle, CURLOPT_WRITEDATA, handler);
2013 curl_easy_setopt(handle, CURLOPT_HEADERDATA, handler);
2014 curl_easy_setopt(handle, CURLOPT_SHARE, curl->handle->share);
2015
2016 php_http_client_curl_handler_reset(handler);
2017
2018 return handler;
2019 }
2020
2021
2022 static ZEND_RESULT_CODE php_http_client_curl_handler_prepare(php_http_client_curl_handler_t *curl, php_http_client_enqueue_t *enqueue)
2023 {
2024 size_t body_size;
2025 php_http_message_t *msg = enqueue->request;
2026 php_http_curle_storage_t *storage = php_http_curle_get_storage(curl->handle);
2027 TSRMLS_FETCH_FROM_CTX(curl->client->ts);
2028
2029 /* request url */
2030 if (!PHP_HTTP_INFO(msg).request.url) {
2031 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot request empty URL");
2032 return FAILURE;
2033 }
2034 storage->errorbuffer[0] = '\0';
2035 if (storage->url) {
2036 pefree(storage->url, 1);
2037 }
2038 php_http_url_to_string(PHP_HTTP_INFO(msg).request.url, &storage->url, NULL, 1);
2039 curl_easy_setopt(curl->handle, CURLOPT_URL, storage->url);
2040
2041 /* apply options */
2042 php_http_options_apply(&php_http_curle_options, enqueue->options, curl);
2043
2044 /* request headers */
2045 php_http_message_update_headers(msg);
2046 if (zend_hash_num_elements(&msg->hdrs)) {
2047 php_http_array_hashkey_t header_key = php_http_array_hashkey_init(0);
2048 zval **header_val, *header_cpy;
2049 HashPosition pos;
2050 php_http_buffer_t header;
2051 #if !PHP_HTTP_CURL_VERSION(7,23,0)
2052 zval **ct = NULL;
2053
2054 zend_hash_find(&msg->hdrs, ZEND_STRS("Content-Length"), (void *) &ct);
2055 #endif
2056
2057 php_http_buffer_init(&header);
2058 FOREACH_HASH_KEYVAL(pos, &msg->hdrs, header_key, header_val) {
2059 if (header_key.type == HASH_KEY_IS_STRING) {
2060 #if !PHP_HTTP_CURL_VERSION(7,23,0)
2061 /* avoid duplicate content-length header */
2062 if (ct && *ct == *header_val) {
2063 continue;
2064 }
2065 #endif
2066 header_cpy = php_http_ztyp(IS_STRING, *header_val);
2067 php_http_buffer_appendf(&header, "%s: %s", header_key.str, Z_STRVAL_P(header_cpy));
2068 php_http_buffer_fix(&header);
2069 curl->options.headers = curl_slist_append(curl->options.headers, header.data);
2070 php_http_buffer_reset(&header);
2071
2072 zval_ptr_dtor(&header_cpy);
2073 }
2074 }
2075 php_http_buffer_dtor(&header);
2076 }
2077 curl_easy_setopt(curl->handle, CURLOPT_HTTPHEADER, curl->options.headers);
2078
2079 /* attach request body */
2080 if ((body_size = php_http_message_body_size(msg->body))) {
2081 /* RFC2616, section 4.3 (para. 4) states that »a message-body MUST NOT be included in a request if the
2082 * specification of the request method (section 5.1.1) does not allow sending an entity-body in request.«
2083 * Following the clause in section 5.1.1 (para. 2) that request methods »MUST be implemented with the
2084 * same semantics as those specified in section 9« reveal that not any single defined HTTP/1.1 method
2085 * does not allow a request body.
2086 */
2087 php_stream_rewind(php_http_message_body_stream(msg->body));
2088 curl_easy_setopt(curl->handle, CURLOPT_SEEKDATA, msg->body);
2089 curl_easy_setopt(curl->handle, CURLOPT_READDATA, msg->body);
2090 curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE, body_size);
2091 curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, body_size);
2092 curl_easy_setopt(curl->handle, CURLOPT_POST, 1L);
2093 } else {
2094 curl_easy_setopt(curl->handle, CURLOPT_SEEKDATA, NULL);
2095 curl_easy_setopt(curl->handle, CURLOPT_READDATA, NULL);
2096 curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE, 0L);
2097 curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, 0L);
2098 }
2099
2100 /*
2101 * Always use CUSTOMREQUEST, else curl won't send any request body for GET etc.
2102 * See e.g. bug #69313.
2103 *
2104 * Here's what curl does:
2105 * - CURLOPT_HTTPGET: ignore request body
2106 * - CURLOPT_UPLOAD: set "Expect: 100-continue" header
2107 * - CURLOPT_POST: set "Content-Type: application/x-www-form-urlencoded" header
2108 * Now select the least bad.
2109 *
2110 * See also https://tools.ietf.org/html/rfc7231#section-5.1.1
2111 */
2112 if (PHP_HTTP_INFO(msg).request.method) {
2113 switch(php_http_select_str(PHP_HTTP_INFO(msg).request.method, 2, "HEAD", "PUT")) {
2114 case 0:
2115 curl_easy_setopt(curl->handle, CURLOPT_NOBODY, 1L);
2116 break;
2117 case 1:
2118 curl_easy_setopt(curl->handle, CURLOPT_UPLOAD, 1L);
2119 break;
2120 default:
2121 curl_easy_setopt(curl->handle, CURLOPT_CUSTOMREQUEST, PHP_HTTP_INFO(msg).request.method);
2122 }
2123 } else {
2124 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot use empty request method");
2125 return FAILURE;
2126 }
2127
2128 return SUCCESS;
2129 }
2130
2131 static void php_http_client_curl_handler_clear(php_http_client_curl_handler_t *handler)
2132 {
2133 curl_easy_setopt(handler->handle, CURLOPT_NOPROGRESS, 1L);
2134 #if PHP_HTTP_CURL_VERSION(7,32,0)
2135 curl_easy_setopt(handler->handle, CURLOPT_XFERINFOFUNCTION, NULL);
2136 #else
2137 curl_easy_setopt(handler->handle, CURLOPT_PROGRESSFUNCTION, NULL);
2138 #endif
2139 curl_easy_setopt(handler->handle, CURLOPT_VERBOSE, 0L);
2140 curl_easy_setopt(handler->handle, CURLOPT_DEBUGFUNCTION, NULL);
2141 curl_easy_setopt(handler->handle, CURLOPT_COOKIELIST, "FLUSH");
2142 curl_easy_setopt(handler->handle, CURLOPT_SHARE, NULL);
2143 }
2144
2145 static void php_http_client_curl_handler_dtor(php_http_client_curl_handler_t *handler)
2146 {
2147 TSRMLS_FETCH_FROM_CTX(handler->client->ts);
2148
2149 php_http_client_curl_handler_clear(handler);
2150
2151 php_resource_factory_handle_dtor(handler->rf, handler->handle TSRMLS_CC);
2152 php_resource_factory_free(&handler->rf);
2153
2154 php_http_message_body_free(&handler->response.body);
2155 php_http_buffer_dtor(&handler->response.headers);
2156 php_http_buffer_dtor(&handler->options.ranges);
2157 php_http_buffer_dtor(&handler->options.cookies);
2158 zend_hash_destroy(&handler->options.cache);
2159
2160 if (handler->options.headers) {
2161 curl_slist_free_all(handler->options.headers);
2162 handler->options.headers = NULL;
2163 }
2164
2165 efree(handler);
2166 }
2167
2168 static php_http_client_t *php_http_client_curl_init(php_http_client_t *h, void *handle)
2169 {
2170 php_http_client_curl_t *curl;
2171 TSRMLS_FETCH_FROM_CTX(h->ts);
2172
2173 if (!handle && !(handle = php_resource_factory_handle_ctor(h->rf, NULL TSRMLS_CC))) {
2174 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to initialize curl handle->multi");
2175 return NULL;
2176 }
2177
2178 curl = ecalloc(1, sizeof(*curl));
2179 curl->handle = handle;
2180 curl->unfinished = 0;
2181 h->ctx = curl;
2182
2183 return h;
2184 }
2185
2186 static void php_http_client_curl_dtor(php_http_client_t *h)
2187 {
2188 php_http_client_curl_t *curl = h->ctx;
2189 TSRMLS_FETCH_FROM_CTX(h->ts);
2190
2191 #if PHP_HTTP_HAVE_EVENT
2192 if (curl->timeout) {
2193 if (event_initialized(curl->timeout) && event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
2194 event_del(curl->timeout);
2195 }
2196 efree(curl->timeout);
2197 curl->timeout = NULL;
2198 }
2199 if (curl->evbase) {
2200 event_base_free(curl->evbase);
2201 curl->evbase = NULL;
2202 }
2203 #endif
2204 curl->unfinished = 0;
2205
2206 php_resource_factory_handle_dtor(h->rf, curl->handle TSRMLS_CC);
2207
2208 efree(curl);
2209 h->ctx = NULL;
2210 }
2211
2212 static void queue_dtor(php_http_client_enqueue_t *e)
2213 {
2214 php_http_client_curl_handler_t *handler = e->opaque;
2215
2216 if (handler->queue.dtor) {
2217 e->opaque = handler->queue.opaque;
2218 handler->queue.dtor(e);
2219 }
2220 php_http_client_curl_handler_dtor(handler);
2221 }
2222
2223 static php_resource_factory_t *create_rf(php_http_client_t *h, php_http_client_enqueue_t *enqueue TSRMLS_DC)
2224 {
2225 php_persistent_handle_factory_t *pf = NULL;
2226 php_resource_factory_t *rf = NULL;
2227 php_http_url_t *url = enqueue->request->http.info.request.url;
2228
2229 if (!url || (!url->host && !url->path)) {
2230 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot request empty URL");
2231 return NULL;
2232 }
2233
2234 /* only if the client itself is setup for persistence */
2235 if (php_resource_factory_is_persistent(h->rf)) {
2236 char *id_str = NULL;
2237 size_t id_len;
2238 int port = url->port ? url->port : 80;
2239 zval **zport;
2240 php_persistent_handle_factory_t *phf = h->rf->data;
2241
2242 if (SUCCESS == zend_hash_find(enqueue->options, ZEND_STRS("port"), (void *) &zport)) {
2243 zval *zcpy = php_http_ztyp(IS_LONG, *zport);
2244
2245 if (Z_LVAL_P(zcpy)) {
2246 port = Z_LVAL_P(zcpy);
2247 }
2248 zval_ptr_dtor(&zcpy);
2249 }
2250
2251 id_len = spprintf(&id_str, 0, "%.*s:%s:%d", (int) phf->ident.len, phf->ident.str, STR_PTR(url->host), port);
2252 pf = php_persistent_handle_concede(NULL, ZEND_STRL("http\\Client\\Curl\\Request"), id_str, id_len, NULL, NULL TSRMLS_CC);
2253 efree(id_str);
2254 }
2255
2256 if (pf) {
2257 rf = php_persistent_handle_resource_factory_init(NULL, pf);
2258 } else {
2259 rf = php_resource_factory_init(NULL, &php_http_curle_resource_factory_ops, NULL, NULL);
2260 }
2261
2262 return rf;
2263 }
2264
2265 static ZEND_RESULT_CODE php_http_client_curl_enqueue(php_http_client_t *h, php_http_client_enqueue_t *enqueue)
2266 {
2267 CURLMcode rs;
2268 php_http_client_curl_t *curl = h->ctx;
2269 php_http_client_curl_handler_t *handler;
2270 php_http_client_progress_state_t *progress;
2271 php_resource_factory_t *rf;
2272 TSRMLS_FETCH_FROM_CTX(h->ts);
2273
2274 rf = create_rf(h, enqueue TSRMLS_CC);
2275 if (!rf) {
2276 return FAILURE;
2277 }
2278
2279 handler = php_http_client_curl_handler_init(h, rf);
2280 if (!handler) {
2281 return FAILURE;
2282 }
2283
2284 if (SUCCESS != php_http_client_curl_handler_prepare(handler, enqueue)) {
2285 php_http_client_curl_handler_dtor(handler);
2286 return FAILURE;
2287 }
2288
2289 handler->queue = *enqueue;
2290 enqueue->opaque = handler;
2291 enqueue->dtor = queue_dtor;
2292
2293 if (CURLM_OK != (rs = curl_multi_add_handle(curl->handle->multi, handler->handle))) {
2294 php_http_client_curl_handler_dtor(handler);
2295 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not enqueue request: %s", curl_multi_strerror(rs));
2296 return FAILURE;
2297 }
2298
2299 zend_llist_add_element(&h->requests, enqueue);
2300 ++curl->unfinished;
2301
2302 if (h->callback.progress.func && SUCCESS == php_http_client_getopt(h, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, enqueue->request, &progress)) {
2303 progress->info = "start";
2304 h->callback.progress.func(h->callback.progress.arg, h, &handler->queue, progress);
2305 progress->started = 1;
2306 }
2307
2308 return SUCCESS;
2309 }
2310
2311 static ZEND_RESULT_CODE php_http_client_curl_dequeue(php_http_client_t *h, php_http_client_enqueue_t *enqueue)
2312 {
2313 CURLMcode rs;
2314 php_http_client_curl_t *curl = h->ctx;
2315 php_http_client_curl_handler_t *handler = enqueue->opaque;
2316 TSRMLS_FETCH_FROM_CTX(h->ts);
2317
2318 php_http_client_curl_handler_clear(handler);
2319 if (CURLM_OK == (rs = curl_multi_remove_handle(curl->handle->multi, handler->handle))) {
2320 zend_llist_del_element(&h->requests, handler->handle, (int (*)(void *, void *)) compare_queue);
2321 return SUCCESS;
2322 } else {
2323 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not dequeue request: %s", curl_multi_strerror(rs));
2324 }
2325
2326 return FAILURE;
2327 }
2328
2329 static void php_http_client_curl_reset(php_http_client_t *h)
2330 {
2331 zend_llist_element *next_el, *this_el;
2332
2333 for (this_el = h->requests.head; this_el; this_el = next_el) {
2334 next_el = this_el->next;
2335 php_http_client_curl_dequeue(h, (void *) this_el->data);
2336 }
2337 }
2338
2339 static inline void php_http_client_curl_get_timeout(php_http_client_curl_t *curl, long max_tout, struct timeval *timeout)
2340 {
2341 if ((CURLM_OK == curl_multi_timeout(curl->handle->multi, &max_tout)) && (max_tout > 0)) {
2342 timeout->tv_sec = max_tout / 1000;
2343 timeout->tv_usec = (max_tout % 1000) * 1000;
2344 } else {
2345 timeout->tv_sec = 0;
2346 timeout->tv_usec = 1000;
2347 }
2348 }
2349
2350 #ifdef PHP_WIN32
2351 # define SELECT_ERROR SOCKET_ERROR
2352 #else
2353 # define SELECT_ERROR -1
2354 #endif
2355
2356 static ZEND_RESULT_CODE php_http_client_curl_wait(php_http_client_t *h, struct timeval *custom_timeout)
2357 {
2358 int MAX;
2359 fd_set R, W, E;
2360 struct timeval timeout;
2361 php_http_client_curl_t *curl = h->ctx;
2362
2363 #if PHP_HTTP_HAVE_EVENT
2364 if (curl->useevents) {
2365 if (!event_initialized(curl->timeout)) {
2366 event_assign(curl->timeout, curl->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, h);
2367 } else if (custom_timeout && timerisset(custom_timeout)) {
2368 event_add(curl->timeout, custom_timeout);
2369 } else if (!event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
2370 php_http_client_curl_get_timeout(curl, 1000, &timeout);
2371 event_add(curl->timeout, &timeout);
2372 }
2373
2374 event_base_loop(curl->evbase, EVLOOP_ONCE);
2375
2376 return SUCCESS;
2377 }
2378 #endif
2379
2380 FD_ZERO(&R);
2381 FD_ZERO(&W);
2382 FD_ZERO(&E);
2383
2384 if (CURLM_OK == curl_multi_fdset(curl->handle->multi, &R, &W, &E, &MAX)) {
2385 if (custom_timeout && timerisset(custom_timeout)) {
2386 timeout = *custom_timeout;
2387 } else {
2388 php_http_client_curl_get_timeout(curl, 1000, &timeout);
2389 }
2390
2391 if (MAX == -1) {
2392 php_http_sleep((double) timeout.tv_sec + (double) (timeout.tv_usec / PHP_HTTP_MCROSEC));
2393 return SUCCESS;
2394 } else if (SELECT_ERROR != select(MAX + 1, &R, &W, &E, &timeout)) {
2395 return SUCCESS;
2396 }
2397 }
2398 return FAILURE;
2399 }
2400
2401 static int php_http_client_curl_once(php_http_client_t *h)
2402 {
2403 php_http_client_curl_t *curl = h->ctx;
2404
2405 #if PHP_HTTP_HAVE_EVENT
2406 if (curl->useevents) {
2407 event_base_loop(curl->evbase, EVLOOP_NONBLOCK);
2408 } else
2409 #endif
2410 while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curl->handle->multi, &curl->unfinished));
2411
2412 php_http_curlm_responsehandler(h);
2413
2414 return curl->unfinished;
2415
2416 }
2417
2418 static ZEND_RESULT_CODE php_http_client_curl_exec(php_http_client_t *h)
2419 {
2420 #if PHP_HTTP_HAVE_EVENT
2421 php_http_client_curl_t *curl = h->ctx;
2422 #endif
2423 TSRMLS_FETCH_FROM_CTX(h->ts);
2424
2425 #if PHP_HTTP_HAVE_EVENT
2426 if (curl->useevents) {
2427 php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, h);
2428 do {
2429 int ev_rc = event_base_dispatch(curl->evbase);
2430
2431 #if DBG_EVENTS
2432 fprintf(stderr, "%c", "X.0"[ev_rc+1]);
2433 #endif
2434
2435 if (ev_rc < 0) {
2436 php_error_docref(NULL TSRMLS_CC, E_ERROR, "Error in event_base_dispatch()");
2437 return FAILURE;
2438 }
2439 } while (curl->unfinished && !EG(exception));
2440 } else
2441 #endif
2442 {
2443 while (php_http_client_curl_once(h) && !EG(exception)) {
2444 if (SUCCESS != php_http_client_curl_wait(h, NULL)) {
2445 #ifdef PHP_WIN32
2446 /* see http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */
2447 php_error_docref(NULL TSRMLS_CC, E_WARNING, "WinSock error: %d", WSAGetLastError());
2448 #else
2449 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno));
2450 #endif
2451 return FAILURE;
2452 }
2453 }
2454 }
2455
2456 return SUCCESS;
2457 }
2458
2459 static ZEND_RESULT_CODE php_http_client_curl_setopt(php_http_client_t *h, php_http_client_setopt_opt_t opt, void *arg)
2460 {
2461 php_http_client_curl_t *curl = h->ctx;
2462
2463 switch (opt) {
2464 case PHP_HTTP_CLIENT_OPT_CONFIGURATION:
2465 return php_http_options_apply(&php_http_curlm_options, (HashTable *) arg, h);
2466 break;
2467
2468 case PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING:
2469 if (CURLM_OK != curl_multi_setopt(curl->handle->multi, CURLMOPT_PIPELINING, (long) *((zend_bool *) arg))) {
2470 return FAILURE;
2471 }
2472 break;
2473
2474 case PHP_HTTP_CLIENT_OPT_USE_EVENTS:
2475 #if PHP_HTTP_HAVE_EVENT
2476 return php_http_curlm_use_eventloop(h, *(zend_bool *) arg);
2477 break;
2478 #endif
2479
2480 default:
2481 return FAILURE;
2482 }
2483 return SUCCESS;
2484 }
2485
2486 static int apply_available_options(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
2487 {
2488 php_http_option_t *opt = pDest;
2489 HashTable *ht;
2490 zval *entry;
2491 int c;
2492
2493 ht = va_arg(args, HashTable*);
2494
2495 MAKE_STD_ZVAL(entry);
2496
2497 if ((c = zend_hash_num_elements(&opt->suboptions.options))) {
2498 array_init_size(entry, c);
2499 zend_hash_apply_with_arguments(&opt->suboptions.options TSRMLS_CC, apply_available_options, 1, Z_ARRVAL_P(entry));
2500 } else {
2501 /* catch deliberate NULL options */
2502 if (Z_TYPE(opt->defval) == IS_STRING && !Z_STRVAL(opt->defval)) {
2503 ZVAL_NULL(entry);
2504 } else {
2505 ZVAL_COPY_VALUE(entry, &opt->defval);
2506 zval_copy_ctor(entry);
2507 }
2508 }
2509
2510 if (hash_key->nKeyLength) {
2511 zend_hash_quick_update(ht, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &entry, sizeof(zval *), NULL);
2512 } else {
2513 zend_hash_index_update(ht, hash_key->h, (void *) &entry, sizeof(zval *), NULL);
2514 }
2515
2516 return ZEND_HASH_APPLY_KEEP;
2517 }
2518
2519 static ZEND_RESULT_CODE php_http_client_curl_getopt(php_http_client_t *h, php_http_client_getopt_opt_t opt, void *arg, void **res)
2520 {
2521 php_http_client_enqueue_t *enqueue;
2522 TSRMLS_FETCH_FROM_CTX(h->ts);
2523
2524 switch (opt) {
2525 case PHP_HTTP_CLIENT_OPT_PROGRESS_INFO:
2526 if ((enqueue = php_http_client_enqueued(h, arg, NULL))) {
2527 php_http_client_curl_handler_t *handler = enqueue->opaque;
2528
2529 *((php_http_client_progress_state_t **) res) = &handler->progress;
2530 return SUCCESS;
2531 }
2532 break;
2533
2534 case PHP_HTTP_CLIENT_OPT_TRANSFER_INFO:
2535 if ((enqueue = php_http_client_enqueued(h, arg, NULL))) {
2536 php_http_client_curl_handler_t *handler = enqueue->opaque;
2537
2538 php_http_curle_get_info(handler->handle, *(HashTable **) res);
2539 return SUCCESS;
2540 }
2541 break;
2542
2543 case PHP_HTTP_CLIENT_OPT_AVAILABLE_OPTIONS:
2544 zend_hash_apply_with_arguments(&php_http_curle_options.options TSRMLS_CC, apply_available_options, 1, *(HashTable **) res);
2545 break;
2546
2547 case PHP_HTTP_CLIENT_OPT_AVAILABLE_CONFIGURATION:
2548 zend_hash_apply_with_arguments(&php_http_curlm_options.options TSRMLS_CC, apply_available_options, 1, *(HashTable **) res);
2549 break;
2550
2551 default:
2552 break;
2553 }
2554
2555 return FAILURE;
2556 }
2557
2558 static php_http_client_ops_t php_http_client_curl_ops = {
2559 &php_http_curlm_resource_factory_ops,
2560 php_http_client_curl_init,
2561 NULL /* copy */,
2562 php_http_client_curl_dtor,
2563 php_http_client_curl_reset,
2564 php_http_client_curl_exec,
2565 php_http_client_curl_wait,
2566 php_http_client_curl_once,
2567 php_http_client_curl_enqueue,
2568 php_http_client_curl_dequeue,
2569 php_http_client_curl_setopt,
2570 php_http_client_curl_getopt
2571 };
2572
2573 php_http_client_ops_t *php_http_client_curl_get_ops(void)
2574 {
2575 return &php_http_client_curl_ops;
2576 }
2577
2578 PHP_MINIT_FUNCTION(http_client_curl)
2579 {
2580 curl_version_info_data *info;
2581 php_http_options_t *options;
2582 php_http_client_driver_t driver = {
2583 ZEND_STRL("curl"),
2584 &php_http_client_curl_ops
2585 };
2586
2587 if (SUCCESS != php_http_client_driver_add(&driver)) {
2588 return FAILURE;
2589 }
2590
2591 if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl"), &php_http_curlm_resource_factory_ops, NULL, NULL TSRMLS_CC)) {
2592 return FAILURE;
2593 }
2594 if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl\\Request"), &php_http_curle_resource_factory_ops, NULL, NULL TSRMLS_CC)) {
2595 return FAILURE;
2596 }
2597
2598 if ((options = php_http_options_init(&php_http_curle_options, 1))) {
2599 options->getter = php_http_curle_get_option;
2600 options->setter = php_http_curle_set_option;
2601
2602 php_http_curle_options_init(options TSRMLS_CC);
2603 }
2604 if ((options = php_http_options_init(&php_http_curlm_options, 1))) {
2605 options->getter = php_http_option_get;
2606 options->setter = php_http_curlm_set_option;
2607
2608 php_http_curlm_options_init(options TSRMLS_CC);
2609 }
2610
2611 if ((info = curl_version_info(CURLVERSION_NOW))) {
2612 /*
2613 * Feature constants
2614 */
2615 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "FEATURES", info->features, CONST_CS|CONST_PERSISTENT);
2616
2617 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "IPV6", CURL_VERSION_IPV6, CONST_CS|CONST_PERSISTENT);
2618 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "KERBEROS4", CURL_VERSION_KERBEROS4, CONST_CS|CONST_PERSISTENT);
2619 #if PHP_HTTP_CURL_VERSION(7,40,0)
2620 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "KERBEROS5", CURL_VERSION_KERBEROS5, CONST_CS|CONST_PERSISTENT);
2621 #endif
2622 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "SSL", CURL_VERSION_SSL, CONST_CS|CONST_PERSISTENT);
2623 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "LIBZ", CURL_VERSION_LIBZ, CONST_CS|CONST_PERSISTENT);
2624 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "NTLM", CURL_VERSION_NTLM, CONST_CS|CONST_PERSISTENT);
2625 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "GSSNEGOTIATE", CURL_VERSION_GSSNEGOTIATE, CONST_CS|CONST_PERSISTENT);
2626 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "ASYNCHDNS", CURL_VERSION_ASYNCHDNS, CONST_CS|CONST_PERSISTENT);
2627 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "SPNEGO", CURL_VERSION_SPNEGO, CONST_CS|CONST_PERSISTENT);
2628 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "LARGEFILE", CURL_VERSION_LARGEFILE, CONST_CS|CONST_PERSISTENT);
2629 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "IDN", CURL_VERSION_IDN, CONST_CS|CONST_PERSISTENT);
2630 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "SSPI", CURL_VERSION_SSPI, CONST_CS|CONST_PERSISTENT);
2631 #if PHP_HTTP_CURL_VERSION(7,38,0)
2632 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "GSSAPI", CURL_VERSION_GSSAPI, CONST_CS|CONST_PERSISTENT);
2633 #endif
2634 #if PHP_HTTP_CURL_VERSION(7,21,4)
2635 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "TLSAUTH_SRP", CURL_VERSION_TLSAUTH_SRP, CONST_CS|CONST_PERSISTENT);
2636 #endif
2637 #if PHP_HTTP_CURL_VERSION(7,22,0)
2638 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "NTLM_WB", CURL_VERSION_NTLM_WB, CONST_CS|CONST_PERSISTENT);
2639 #endif
2640 #if PHP_HTTP_CURL_VERSION(7,33,0)
2641 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "HTTP2", CURL_VERSION_HTTP2, CONST_CS|CONST_PERSISTENT);
2642 #endif
2643 #if PHP_HTTP_CURL_VERSION(7,40,0)
2644 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "UNIX_SOCKETS", CURL_VERSION_UNIX_SOCKETS, CONST_CS|CONST_PERSISTENT);
2645 #endif
2646 #if PHP_HTTP_CURL_VERSION(7,47,0)
2647 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "PSL", CURL_VERSION_PSL, CONST_CS|CONST_PERSISTENT);
2648 #endif
2649
2650 /*
2651 * Version constants
2652 */
2653 REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl", "VERSIONS", curl_version(), CONST_CS|CONST_PERSISTENT);
2654 #if CURLVERSION_NOW >= 0
2655 REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "CURL", (char *) info->version, CONST_CS|CONST_PERSISTENT);
2656 REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "SSL", (char *) info->ssl_version, CONST_CS|CONST_PERSISTENT);
2657 REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "LIBZ", (char *) info->libz_version, CONST_CS|CONST_PERSISTENT);
2658 # if CURLVERSION_NOW >= 1
2659 REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "ARES", (char *) info->ares, CONST_CS|CONST_PERSISTENT);
2660 # if CURLVERSION_NOW >= 2
2661 REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "IDN", (char *) info->libidn, CONST_CS|CONST_PERSISTENT);
2662 # endif
2663 # endif
2664 #endif
2665 }
2666
2667 /*
2668 * HTTP Protocol Version Constants
2669 */
2670 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_1_0", CURL_HTTP_VERSION_1_0, CONST_CS|CONST_PERSISTENT);
2671 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_1_1", CURL_HTTP_VERSION_1_1, CONST_CS|CONST_PERSISTENT);
2672 #if PHP_HTTP_CURL_VERSION(7,33,0)
2673 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_2_0", CURL_HTTP_VERSION_2_0, CONST_CS|CONST_PERSISTENT);
2674 #endif
2675 #if PHP_HTTP_CURL_VERSION(7,47,0)
2676 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_2TLS", CURL_HTTP_VERSION_2TLS, CONST_CS|CONST_PERSISTENT);
2677 #endif
2678 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_ANY", CURL_HTTP_VERSION_NONE, CONST_CS|CONST_PERSISTENT);
2679
2680 /*
2681 * SSL Version Constants
2682 */
2683 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1", CURL_SSLVERSION_TLSv1, CONST_CS|CONST_PERSISTENT);
2684 #if PHP_HTTP_CURL_VERSION(7,34,0)
2685 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_0", CURL_SSLVERSION_TLSv1_0, CONST_CS|CONST_PERSISTENT);
2686 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_1", CURL_SSLVERSION_TLSv1_1, CONST_CS|CONST_PERSISTENT);
2687 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_2", CURL_SSLVERSION_TLSv1_2, CONST_CS|CONST_PERSISTENT);
2688 #endif
2689 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_SSLv2", CURL_SSLVERSION_SSLv2, CONST_CS|CONST_PERSISTENT);
2690 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_SSLv3", CURL_SSLVERSION_SSLv3, CONST_CS|CONST_PERSISTENT);
2691 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_ANY", CURL_SSLVERSION_DEFAULT, CONST_CS|CONST_PERSISTENT);
2692 #if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
2693 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "TLSAUTH_SRP", CURL_TLSAUTH_SRP, CONST_CS|CONST_PERSISTENT);
2694 #endif
2695
2696 /*
2697 * DNS IPvX resolving
2698 */
2699 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_V4", CURL_IPRESOLVE_V4, CONST_CS|CONST_PERSISTENT);
2700 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_V6", CURL_IPRESOLVE_V6, CONST_CS|CONST_PERSISTENT);
2701 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_ANY", CURL_IPRESOLVE_WHATEVER, CONST_CS|CONST_PERSISTENT);
2702
2703 /*
2704 * Auth Constants
2705 */
2706 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_BASIC", CURLAUTH_BASIC, CONST_CS|CONST_PERSISTENT);
2707 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_DIGEST", CURLAUTH_DIGEST, CONST_CS|CONST_PERSISTENT);
2708 #if PHP_HTTP_CURL_VERSION(7,19,3)
2709 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_DIGEST_IE", CURLAUTH_DIGEST_IE, CONST_CS|CONST_PERSISTENT);
2710 #endif
2711 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_NTLM", CURLAUTH_NTLM, CONST_CS|CONST_PERSISTENT);
2712 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_GSSNEG", CURLAUTH_GSSNEGOTIATE, CONST_CS|CONST_PERSISTENT);
2713 #if PHP_HTTP_CURL_VERSION(7,38,0)
2714 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_SPNEGO", CURLAUTH_NEGOTIATE, CONST_CS|CONST_PERSISTENT);
2715 #endif
2716 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_ANY", CURLAUTH_ANY, CONST_CS|CONST_PERSISTENT);
2717
2718 /*
2719 * Proxy Type Constants
2720 */
2721 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS4", CURLPROXY_SOCKS4, CONST_CS|CONST_PERSISTENT);
2722 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS4A", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT);
2723 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS5_HOSTNAME", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT);
2724 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS5", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT);
2725 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_HTTP", CURLPROXY_HTTP, CONST_CS|CONST_PERSISTENT);
2726 #if PHP_HTTP_CURL_VERSION(7,19,4)
2727 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_HTTP_1_0", CURLPROXY_HTTP_1_0, CONST_CS|CONST_PERSISTENT);
2728 #endif
2729
2730 /*
2731 * Post Redirection Constants
2732 */
2733 #if PHP_HTTP_CURL_VERSION(7,19,1)
2734 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_301", CURL_REDIR_POST_301, CONST_CS|CONST_PERSISTENT);
2735 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_302", CURL_REDIR_POST_302, CONST_CS|CONST_PERSISTENT);
2736 #if PHP_HTTP_CURL_VERSION(7,26,0)
2737 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_303", CURL_REDIR_POST_303, CONST_CS|CONST_PERSISTENT);
2738 #endif
2739 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_ALL", CURL_REDIR_POST_ALL, CONST_CS|CONST_PERSISTENT);
2740 #endif
2741
2742 return SUCCESS;
2743 }
2744
2745 PHP_MSHUTDOWN_FUNCTION(http_client_curl)
2746 {
2747 php_persistent_handle_cleanup(ZEND_STRL("http\\Client\\Curl"), NULL, 0 TSRMLS_CC);
2748 php_persistent_handle_cleanup(ZEND_STRL("http\\Client\\Curl\\Request"), NULL, 0 TSRMLS_CC);
2749
2750 php_http_options_dtor(&php_http_curle_options);
2751 php_http_options_dtor(&php_http_curlm_options);
2752
2753 return SUCCESS;
2754 }
2755
2756 #endif /* PHP_HTTP_HAVE_CURL */
2757
2758 /*
2759 * Local variables:
2760 * tab-width: 4
2761 * c-basic-offset: 4
2762 * End:
2763 * vim600: noet sw=4 ts=4 fdm=marker
2764 * vim<600: noet sw=4 ts=4
2765 */