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