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