wrap more options behind feature checks
[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 void php_http_curlm_options_init(php_http_options_t *registry TSRMLS_DC)
1764 {
1765 php_http_option_t *opt;
1766
1767 /* set size of connection cache */
1768 if ((opt = php_http_option_register(registry, ZEND_STRL("maxconnects"), CURLMOPT_MAXCONNECTS, IS_LONG))) {
1769 /* -1 == default, 0 == unlimited */
1770 ZVAL_LONG(&opt->defval, -1);
1771 }
1772 /* set max number of connections to a single host */
1773 #if PHP_HTTP_CURL_VERSION(7,30,0)
1774 php_http_option_register(registry, ZEND_STRL("max_host_connections"), CURLMOPT_MAX_HOST_CONNECTIONS, IS_LONG);
1775 #endif
1776 /* maximum number of requests in a pipeline */
1777 #if PHP_HTTP_CURL_VERSION(7,30,0)
1778 if ((opt = php_http_option_register(registry, ZEND_STRL("max_pipeline_length"), CURLMOPT_MAX_PIPELINE_LENGTH, IS_LONG))) {
1779 ZVAL_LONG(&opt->defval, 5);
1780 }
1781 #endif
1782 /* max simultaneously open connections */
1783 #if PHP_HTTP_CURL_VERSION(7,30,0)
1784 php_http_option_register(registry, ZEND_STRL("max_total_connections"), CURLMOPT_MAX_TOTAL_CONNECTIONS, IS_LONG);
1785 #endif
1786 /* enable/disable HTTP pipelining */
1787 php_http_option_register(registry, ZEND_STRL("pipelining"), CURLMOPT_PIPELINING, IS_BOOL);
1788 /* chunk length threshold for pipelining */
1789 #if PHP_HTTP_CURL_VERSION(7,30,0)
1790 php_http_option_register(registry, ZEND_STRL("chunk_length_penalty_size"), CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, IS_LONG);
1791 #endif
1792 /* size threshold for pipelining penalty */
1793 #if PHP_HTTP_CURL_VERSION(7,30,0)
1794 php_http_option_register(registry, ZEND_STRL("content_length_penalty_size"), CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, IS_LONG);
1795 #endif
1796 /* pipelining server blacklist */
1797 #if PHP_HTTP_CURL_VERSION(7,30,0)
1798 if ((opt = php_http_option_register(registry, ZEND_STRL("pipelining_server_bl"), CURLMOPT_PIPELINING_SERVER_BL, IS_ARRAY))) {
1799 opt->setter = php_http_curlm_option_set_pipelining_bl;
1800 }
1801 #endif
1802 /* pipelining host blacklist */
1803 #if PHP_HTTP_CURL_VERSION(7,30,0)
1804 if ((opt = php_http_option_register(registry, ZEND_STRL("pipelining_site_bl"), CURLMOPT_PIPELINING_SITE_BL, IS_ARRAY))) {
1805 opt->setter = php_http_curlm_option_set_pipelining_bl;
1806 }
1807 #endif
1808 /* events */
1809 #if PHP_HTTP_HAVE_EVENT
1810 if ((opt = php_http_option_register(registry, ZEND_STRL("use_eventloop"), 0, IS_BOOL))) {
1811 opt->setter = php_http_curlm_option_set_use_eventloop;
1812 }
1813 #endif
1814 }
1815
1816 static ZEND_RESULT_CODE php_http_curlm_set_option(php_http_option_t *opt, zval *val, void *userdata)
1817 {
1818 php_http_client_t *client = userdata;
1819 php_http_client_curl_t *curl = client->ctx;
1820 CURLM *ch = curl->handle->multi;
1821 zval *orig = val;
1822 CURLMcode rc = CURLM_UNKNOWN_OPTION;
1823 ZEND_RESULT_CODE rv = SUCCESS;
1824 TSRMLS_FETCH_FROM_CTX(client->ts);
1825
1826 if (!val) {
1827 val = &opt->defval;
1828 } else if (opt->type && Z_TYPE_P(val) != opt->type && !(Z_TYPE_P(val) == IS_NULL && opt->type == IS_ARRAY)) {
1829 val = php_http_ztyp(opt->type, val);
1830 }
1831
1832 if (opt->setter) {
1833 rv = opt->setter(opt, val, client);
1834 } else {
1835 switch (opt->type) {
1836 case IS_BOOL:
1837 if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, (long) Z_BVAL_P(val)))) {
1838 rv = FAILURE;
1839 }
1840 break;
1841 case IS_LONG:
1842 if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, Z_LVAL_P(val)))) {
1843 rv = FAILURE;
1844 }
1845 break;
1846 default:
1847 rv = FAILURE;
1848 break;
1849 }
1850 }
1851
1852 if (val && val != orig && val != &opt->defval) {
1853 zval_ptr_dtor(&val);
1854 }
1855
1856 if (rv != SUCCESS) {
1857 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, curl_easy_strerror(rc));
1858 }
1859 return rv;
1860 }
1861
1862 /* client ops */
1863
1864 static ZEND_RESULT_CODE php_http_client_curl_handler_reset(php_http_client_curl_handler_t *curl)
1865 {
1866 CURL *ch = curl->handle;
1867 php_http_curle_storage_t *st;
1868
1869 if ((st = php_http_curle_get_storage(ch))) {
1870 if (st->url) {
1871 pefree(st->url, 1);
1872 st->url = NULL;
1873 }
1874 if (st->cookiestore) {
1875 pefree(st->cookiestore, 1);
1876 st->cookiestore = NULL;
1877 }
1878 st->errorbuffer[0] = '\0';
1879 st->errorcode = 0;
1880 }
1881
1882 curl_easy_setopt(ch, CURLOPT_URL, NULL);
1883 curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, NULL);
1884 curl_easy_setopt(ch, CURLOPT_HTTPGET, 1L);
1885 curl_easy_setopt(ch, CURLOPT_NOBODY, 0L);
1886 /* libcurl < 7.19.6 does not clear auth info with USERPWD set to NULL */
1887 #if PHP_HTTP_CURL_VERSION(7,19,1)
1888 curl_easy_setopt(ch, CURLOPT_PROXYUSERNAME, NULL);
1889 curl_easy_setopt(ch, CURLOPT_PROXYPASSWORD, NULL);
1890 curl_easy_setopt(ch, CURLOPT_USERNAME, NULL);
1891 curl_easy_setopt(ch, CURLOPT_PASSWORD, NULL);
1892 #endif
1893
1894 #if PHP_HTTP_CURL_VERSION(7,21,3)
1895 if (curl->options.resolve) {
1896 curl_slist_free_all(curl->options.resolve);
1897 curl->options.resolve = NULL;
1898 }
1899 #endif
1900 curl->options.retry.count = 0;
1901 curl->options.retry.delay = 0;
1902 curl->options.redirects = 0;
1903 curl->options.encode_cookies = 1;
1904
1905 if (curl->options.headers) {
1906 curl_slist_free_all(curl->options.headers);
1907 curl->options.headers = NULL;
1908 }
1909 if (curl->options.proxyheaders) {
1910 curl_slist_free_all(curl->options.proxyheaders);
1911 curl->options.proxyheaders = NULL;
1912 }
1913
1914 php_http_buffer_reset(&curl->options.cookies);
1915 php_http_buffer_reset(&curl->options.ranges);
1916
1917 return SUCCESS;
1918 }
1919
1920 static php_http_client_curl_handler_t *php_http_client_curl_handler_init(php_http_client_t *h, php_resource_factory_t *rf)
1921 {
1922 void *handle;
1923 php_http_client_curl_t *curl = h->ctx;
1924 php_http_client_curl_handler_t *handler;
1925 TSRMLS_FETCH_FROM_CTX(h->ts);
1926
1927 if (!(handle = php_resource_factory_handle_ctor(rf, NULL TSRMLS_CC))) {
1928 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to initialize curl handle->multi");
1929 return NULL;
1930 }
1931
1932 handler = ecalloc(1, sizeof(*handler));
1933 handler->rf = rf;
1934 handler->client = h;
1935 handler->handle = handle;
1936 handler->response.body = php_http_message_body_init(NULL, NULL TSRMLS_CC);
1937 php_http_buffer_init(&handler->response.headers);
1938 php_http_buffer_init(&handler->options.cookies);
1939 php_http_buffer_init(&handler->options.ranges);
1940 zend_hash_init(&handler->options.cache, 0, NULL, ZVAL_PTR_DTOR, 0);
1941
1942 #if defined(ZTS)
1943 curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1L);
1944 #endif
1945 curl_easy_setopt(handle, CURLOPT_HEADER, 0L);
1946 curl_easy_setopt(handle, CURLOPT_FILETIME, 1L);
1947 curl_easy_setopt(handle, CURLOPT_AUTOREFERER, 1L);
1948 curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L);
1949 curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0L);
1950 curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, php_http_curle_header_callback);
1951 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, php_http_curle_body_callback);
1952 curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, php_http_curle_raw_callback);
1953 curl_easy_setopt(handle, CURLOPT_READFUNCTION, php_http_curle_read_callback);
1954 curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, php_http_curle_seek_callback);
1955 #if PHP_HTTP_CURL_VERSION(7,32,0)
1956 curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, php_http_curle_xferinfo_callback);
1957 curl_easy_setopt(handle, CURLOPT_XFERINFODATA, handler);
1958 #else
1959 curl_easy_setopt(handle, CURLOPT_PROGRESSFUNCTION, php_http_curle_progress_callback);
1960 curl_easy_setopt(handle, CURLOPT_PROGRESSDATA, handler);
1961 #endif
1962 curl_easy_setopt(handle, CURLOPT_DEBUGDATA, handler);
1963 curl_easy_setopt(handle, CURLOPT_WRITEDATA, handler);
1964 curl_easy_setopt(handle, CURLOPT_HEADERDATA, handler);
1965 curl_easy_setopt(handle, CURLOPT_SHARE, curl->handle->share);
1966
1967 php_http_client_curl_handler_reset(handler);
1968
1969 return handler;
1970 }
1971
1972
1973 static ZEND_RESULT_CODE php_http_client_curl_handler_prepare(php_http_client_curl_handler_t *curl, php_http_client_enqueue_t *enqueue)
1974 {
1975 size_t body_size;
1976 php_http_message_t *msg = enqueue->request;
1977 php_http_curle_storage_t *storage = php_http_curle_get_storage(curl->handle);
1978 TSRMLS_FETCH_FROM_CTX(curl->client->ts);
1979
1980 /* request url */
1981 if (!PHP_HTTP_INFO(msg).request.url) {
1982 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot request empty URL");
1983 return FAILURE;
1984 }
1985 storage->errorbuffer[0] = '\0';
1986 if (storage->url) {
1987 pefree(storage->url, 1);
1988 }
1989 php_http_url_to_string(PHP_HTTP_INFO(msg).request.url, &storage->url, NULL, 1);
1990 curl_easy_setopt(curl->handle, CURLOPT_URL, storage->url);
1991
1992 /* apply options */
1993 php_http_options_apply(&php_http_curle_options, enqueue->options, curl);
1994
1995 /* request headers */
1996 php_http_message_update_headers(msg);
1997 if (zend_hash_num_elements(&msg->hdrs)) {
1998 php_http_array_hashkey_t header_key = php_http_array_hashkey_init(0);
1999 zval **header_val, *header_cpy;
2000 HashPosition pos;
2001 php_http_buffer_t header;
2002 #if !PHP_HTTP_CURL_VERSION(7,23,0)
2003 zval **ct = NULL;
2004
2005 zend_hash_find(&msg->hdrs, ZEND_STRS("Content-Length"), (void *) &ct);
2006 #endif
2007
2008 php_http_buffer_init(&header);
2009 FOREACH_HASH_KEYVAL(pos, &msg->hdrs, header_key, header_val) {
2010 if (header_key.type == HASH_KEY_IS_STRING) {
2011 #if !PHP_HTTP_CURL_VERSION(7,23,0)
2012 /* avoid duplicate content-length header */
2013 if (ct && *ct == *header_val) {
2014 continue;
2015 }
2016 #endif
2017 header_cpy = php_http_ztyp(IS_STRING, *header_val);
2018 php_http_buffer_appendf(&header, "%s: %s", header_key.str, Z_STRVAL_P(header_cpy));
2019 php_http_buffer_fix(&header);
2020 curl->options.headers = curl_slist_append(curl->options.headers, header.data);
2021 php_http_buffer_reset(&header);
2022
2023 zval_ptr_dtor(&header_cpy);
2024 }
2025 }
2026 php_http_buffer_dtor(&header);
2027 }
2028 curl_easy_setopt(curl->handle, CURLOPT_HTTPHEADER, curl->options.headers);
2029
2030 /* attach request body */
2031 if ((body_size = php_http_message_body_size(msg->body))) {
2032 /* RFC2616, section 4.3 (para. 4) states that »a message-body MUST NOT be included in a request if the
2033 * specification of the request method (section 5.1.1) does not allow sending an entity-body in request.«
2034 * Following the clause in section 5.1.1 (para. 2) that request methods »MUST be implemented with the
2035 * same semantics as those specified in section 9« reveal that not any single defined HTTP/1.1 method
2036 * does not allow a request body.
2037 */
2038 php_stream_rewind(php_http_message_body_stream(msg->body));
2039 curl_easy_setopt(curl->handle, CURLOPT_SEEKDATA, msg->body);
2040 curl_easy_setopt(curl->handle, CURLOPT_READDATA, msg->body);
2041 curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE, body_size);
2042 curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, body_size);
2043 curl_easy_setopt(curl->handle, CURLOPT_POST, 1L);
2044 } else {
2045 curl_easy_setopt(curl->handle, CURLOPT_SEEKDATA, NULL);
2046 curl_easy_setopt(curl->handle, CURLOPT_READDATA, NULL);
2047 curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE, 0L);
2048 curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, 0L);
2049 }
2050
2051 /*
2052 * Always use CUSTOMREQUEST, else curl won't send any request body for GET etc.
2053 * See e.g. bug #69313.
2054 *
2055 * Here's what curl does:
2056 * - CURLOPT_HTTPGET: ignore request body
2057 * - CURLOPT_UPLOAD: set "Expect: 100-continue" header
2058 * - CURLOPT_POST: set "Content-Type: application/x-www-form-urlencoded" header
2059 * Now select the least bad.
2060 *
2061 * See also https://tools.ietf.org/html/rfc7231#section-5.1.1
2062 */
2063 if (PHP_HTTP_INFO(msg).request.method) {
2064 switch(php_http_select_str(PHP_HTTP_INFO(msg).request.method, 2, "HEAD", "PUT")) {
2065 case 0:
2066 curl_easy_setopt(curl->handle, CURLOPT_NOBODY, 1L);
2067 break;
2068 case 1:
2069 curl_easy_setopt(curl->handle, CURLOPT_UPLOAD, 1L);
2070 break;
2071 default:
2072 curl_easy_setopt(curl->handle, CURLOPT_CUSTOMREQUEST, PHP_HTTP_INFO(msg).request.method);
2073 }
2074 } else {
2075 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot use empty request method");
2076 return FAILURE;
2077 }
2078
2079 return SUCCESS;
2080 }
2081
2082 static void php_http_client_curl_handler_clear(php_http_client_curl_handler_t *handler)
2083 {
2084 curl_easy_setopt(handler->handle, CURLOPT_NOPROGRESS, 1L);
2085 #if PHP_HTTP_CURL_VERSION(7,32,0)
2086 curl_easy_setopt(handler->handle, CURLOPT_XFERINFOFUNCTION, NULL);
2087 #else
2088 curl_easy_setopt(handler->handle, CURLOPT_PROGRESSFUNCTION, NULL);
2089 #endif
2090 curl_easy_setopt(handler->handle, CURLOPT_VERBOSE, 0L);
2091 curl_easy_setopt(handler->handle, CURLOPT_DEBUGFUNCTION, NULL);
2092 curl_easy_setopt(handler->handle, CURLOPT_COOKIELIST, "FLUSH");
2093 curl_easy_setopt(handler->handle, CURLOPT_SHARE, NULL);
2094 }
2095
2096 static void php_http_client_curl_handler_dtor(php_http_client_curl_handler_t *handler)
2097 {
2098 TSRMLS_FETCH_FROM_CTX(handler->client->ts);
2099
2100 php_http_client_curl_handler_clear(handler);
2101
2102 php_resource_factory_handle_dtor(handler->rf, handler->handle TSRMLS_CC);
2103 php_resource_factory_free(&handler->rf);
2104
2105 php_http_message_body_free(&handler->response.body);
2106 php_http_buffer_dtor(&handler->response.headers);
2107 php_http_buffer_dtor(&handler->options.ranges);
2108 php_http_buffer_dtor(&handler->options.cookies);
2109 zend_hash_destroy(&handler->options.cache);
2110
2111 if (handler->options.headers) {
2112 curl_slist_free_all(handler->options.headers);
2113 handler->options.headers = NULL;
2114 }
2115
2116 efree(handler);
2117 }
2118
2119 static php_http_client_t *php_http_client_curl_init(php_http_client_t *h, void *handle)
2120 {
2121 php_http_client_curl_t *curl;
2122 TSRMLS_FETCH_FROM_CTX(h->ts);
2123
2124 if (!handle && !(handle = php_resource_factory_handle_ctor(h->rf, NULL TSRMLS_CC))) {
2125 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to initialize curl handle->multi");
2126 return NULL;
2127 }
2128
2129 curl = ecalloc(1, sizeof(*curl));
2130 curl->handle = handle;
2131 curl->unfinished = 0;
2132 h->ctx = curl;
2133
2134 return h;
2135 }
2136
2137 static void php_http_client_curl_dtor(php_http_client_t *h)
2138 {
2139 php_http_client_curl_t *curl = h->ctx;
2140 TSRMLS_FETCH_FROM_CTX(h->ts);
2141
2142 #if PHP_HTTP_HAVE_EVENT
2143 if (curl->timeout) {
2144 if (event_initialized(curl->timeout) && event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
2145 event_del(curl->timeout);
2146 }
2147 efree(curl->timeout);
2148 curl->timeout = NULL;
2149 }
2150 if (curl->evbase) {
2151 event_base_free(curl->evbase);
2152 curl->evbase = NULL;
2153 }
2154 #endif
2155 curl->unfinished = 0;
2156
2157 php_resource_factory_handle_dtor(h->rf, curl->handle TSRMLS_CC);
2158
2159 efree(curl);
2160 h->ctx = NULL;
2161 }
2162
2163 static void queue_dtor(php_http_client_enqueue_t *e)
2164 {
2165 php_http_client_curl_handler_t *handler = e->opaque;
2166
2167 if (handler->queue.dtor) {
2168 e->opaque = handler->queue.opaque;
2169 handler->queue.dtor(e);
2170 }
2171 php_http_client_curl_handler_dtor(handler);
2172 }
2173
2174 static php_resource_factory_t *create_rf(php_http_client_t *h, php_http_client_enqueue_t *enqueue TSRMLS_DC)
2175 {
2176 php_persistent_handle_factory_t *pf = NULL;
2177 php_resource_factory_t *rf = NULL;
2178 php_http_url_t *url = enqueue->request->http.info.request.url;
2179
2180 if (!url || (!url->host && !url->path)) {
2181 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot request empty URL");
2182 return NULL;
2183 }
2184
2185 /* only if the client itself is setup for persistence */
2186 if (php_resource_factory_is_persistent(h->rf)) {
2187 char *id_str = NULL;
2188 size_t id_len;
2189 int port = url->port ? url->port : 80;
2190 zval **zport;
2191 php_persistent_handle_factory_t *phf = h->rf->data;
2192
2193 if (SUCCESS == zend_hash_find(enqueue->options, ZEND_STRS("port"), (void *) &zport)) {
2194 zval *zcpy = php_http_ztyp(IS_LONG, *zport);
2195
2196 if (Z_LVAL_P(zcpy)) {
2197 port = Z_LVAL_P(zcpy);
2198 }
2199 zval_ptr_dtor(&zcpy);
2200 }
2201
2202 id_len = spprintf(&id_str, 0, "%.*s:%s:%d", (int) phf->ident.len, phf->ident.str, STR_PTR(url->host), port);
2203 pf = php_persistent_handle_concede(NULL, ZEND_STRL("http\\Client\\Curl\\Request"), id_str, id_len, NULL, NULL TSRMLS_CC);
2204 efree(id_str);
2205 }
2206
2207 if (pf) {
2208 rf = php_persistent_handle_resource_factory_init(NULL, pf);
2209 } else {
2210 rf = php_resource_factory_init(NULL, &php_http_curle_resource_factory_ops, NULL, NULL);
2211 }
2212
2213 return rf;
2214 }
2215
2216 static ZEND_RESULT_CODE php_http_client_curl_enqueue(php_http_client_t *h, php_http_client_enqueue_t *enqueue)
2217 {
2218 CURLMcode rs;
2219 php_http_client_curl_t *curl = h->ctx;
2220 php_http_client_curl_handler_t *handler;
2221 php_http_client_progress_state_t *progress;
2222 php_resource_factory_t *rf;
2223 TSRMLS_FETCH_FROM_CTX(h->ts);
2224
2225 rf = create_rf(h, enqueue TSRMLS_CC);
2226 if (!rf) {
2227 return FAILURE;
2228 }
2229
2230 handler = php_http_client_curl_handler_init(h, rf);
2231 if (!handler) {
2232 return FAILURE;
2233 }
2234
2235 if (SUCCESS != php_http_client_curl_handler_prepare(handler, enqueue)) {
2236 php_http_client_curl_handler_dtor(handler);
2237 return FAILURE;
2238 }
2239
2240 handler->queue = *enqueue;
2241 enqueue->opaque = handler;
2242 enqueue->dtor = queue_dtor;
2243
2244 if (CURLM_OK != (rs = curl_multi_add_handle(curl->handle->multi, handler->handle))) {
2245 php_http_client_curl_handler_dtor(handler);
2246 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not enqueue request: %s", curl_multi_strerror(rs));
2247 return FAILURE;
2248 }
2249
2250 zend_llist_add_element(&h->requests, enqueue);
2251 ++curl->unfinished;
2252
2253 if (h->callback.progress.func && SUCCESS == php_http_client_getopt(h, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, enqueue->request, &progress)) {
2254 progress->info = "start";
2255 h->callback.progress.func(h->callback.progress.arg, h, &handler->queue, progress);
2256 progress->started = 1;
2257 }
2258
2259 return SUCCESS;
2260 }
2261
2262 static ZEND_RESULT_CODE php_http_client_curl_dequeue(php_http_client_t *h, php_http_client_enqueue_t *enqueue)
2263 {
2264 CURLMcode rs;
2265 php_http_client_curl_t *curl = h->ctx;
2266 php_http_client_curl_handler_t *handler = enqueue->opaque;
2267 TSRMLS_FETCH_FROM_CTX(h->ts);
2268
2269 php_http_client_curl_handler_clear(handler);
2270 if (CURLM_OK == (rs = curl_multi_remove_handle(curl->handle->multi, handler->handle))) {
2271 zend_llist_del_element(&h->requests, handler->handle, (int (*)(void *, void *)) compare_queue);
2272 return SUCCESS;
2273 } else {
2274 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not dequeue request: %s", curl_multi_strerror(rs));
2275 }
2276
2277 return FAILURE;
2278 }
2279
2280 static void php_http_client_curl_reset(php_http_client_t *h)
2281 {
2282 zend_llist_element *next_el, *this_el;
2283
2284 for (this_el = h->requests.head; this_el; this_el = next_el) {
2285 next_el = this_el->next;
2286 php_http_client_curl_dequeue(h, (void *) this_el->data);
2287 }
2288 }
2289
2290 static inline void php_http_client_curl_get_timeout(php_http_client_curl_t *curl, long max_tout, struct timeval *timeout)
2291 {
2292 if ((CURLM_OK == curl_multi_timeout(curl->handle->multi, &max_tout)) && (max_tout > 0)) {
2293 timeout->tv_sec = max_tout / 1000;
2294 timeout->tv_usec = (max_tout % 1000) * 1000;
2295 } else {
2296 timeout->tv_sec = 0;
2297 timeout->tv_usec = 1000;
2298 }
2299 }
2300
2301 #ifdef PHP_WIN32
2302 # define SELECT_ERROR SOCKET_ERROR
2303 #else
2304 # define SELECT_ERROR -1
2305 #endif
2306
2307 static ZEND_RESULT_CODE php_http_client_curl_wait(php_http_client_t *h, struct timeval *custom_timeout)
2308 {
2309 int MAX;
2310 fd_set R, W, E;
2311 struct timeval timeout;
2312 php_http_client_curl_t *curl = h->ctx;
2313
2314 #if PHP_HTTP_HAVE_EVENT
2315 if (curl->useevents) {
2316 if (!event_initialized(curl->timeout)) {
2317 event_assign(curl->timeout, curl->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, h);
2318 } else if (custom_timeout && timerisset(custom_timeout)) {
2319 event_add(curl->timeout, custom_timeout);
2320 } else if (!event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
2321 php_http_client_curl_get_timeout(curl, 1000, &timeout);
2322 event_add(curl->timeout, &timeout);
2323 }
2324
2325 event_base_loop(curl->evbase, EVLOOP_ONCE);
2326
2327 return SUCCESS;
2328 }
2329 #endif
2330
2331 FD_ZERO(&R);
2332 FD_ZERO(&W);
2333 FD_ZERO(&E);
2334
2335 if (CURLM_OK == curl_multi_fdset(curl->handle->multi, &R, &W, &E, &MAX)) {
2336 if (custom_timeout && timerisset(custom_timeout)) {
2337 timeout = *custom_timeout;
2338 } else {
2339 php_http_client_curl_get_timeout(curl, 1000, &timeout);
2340 }
2341
2342 if (MAX == -1) {
2343 php_http_sleep((double) timeout.tv_sec + (double) (timeout.tv_usec / PHP_HTTP_MCROSEC));
2344 return SUCCESS;
2345 } else if (SELECT_ERROR != select(MAX + 1, &R, &W, &E, &timeout)) {
2346 return SUCCESS;
2347 }
2348 }
2349 return FAILURE;
2350 }
2351
2352 static int php_http_client_curl_once(php_http_client_t *h)
2353 {
2354 php_http_client_curl_t *curl = h->ctx;
2355
2356 #if PHP_HTTP_HAVE_EVENT
2357 if (curl->useevents) {
2358 event_base_loop(curl->evbase, EVLOOP_NONBLOCK);
2359 } else
2360 #endif
2361 while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curl->handle->multi, &curl->unfinished));
2362
2363 php_http_curlm_responsehandler(h);
2364
2365 return curl->unfinished;
2366
2367 }
2368
2369 static ZEND_RESULT_CODE php_http_client_curl_exec(php_http_client_t *h)
2370 {
2371 #if PHP_HTTP_HAVE_EVENT
2372 php_http_client_curl_t *curl = h->ctx;
2373 #endif
2374 TSRMLS_FETCH_FROM_CTX(h->ts);
2375
2376 #if PHP_HTTP_HAVE_EVENT
2377 if (curl->useevents) {
2378 php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, h);
2379 do {
2380 int ev_rc = event_base_dispatch(curl->evbase);
2381
2382 #if DBG_EVENTS
2383 fprintf(stderr, "%c", "X.0"[ev_rc+1]);
2384 #endif
2385
2386 if (ev_rc < 0) {
2387 php_error_docref(NULL TSRMLS_CC, E_ERROR, "Error in event_base_dispatch()");
2388 return FAILURE;
2389 }
2390 } while (curl->unfinished && !EG(exception));
2391 } else
2392 #endif
2393 {
2394 while (php_http_client_curl_once(h) && !EG(exception)) {
2395 if (SUCCESS != php_http_client_curl_wait(h, NULL)) {
2396 #ifdef PHP_WIN32
2397 /* see http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */
2398 php_error_docref(NULL TSRMLS_CC, E_WARNING, "WinSock error: %d", WSAGetLastError());
2399 #else
2400 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno));
2401 #endif
2402 return FAILURE;
2403 }
2404 }
2405 }
2406
2407 return SUCCESS;
2408 }
2409
2410 static ZEND_RESULT_CODE php_http_client_curl_setopt(php_http_client_t *h, php_http_client_setopt_opt_t opt, void *arg)
2411 {
2412 php_http_client_curl_t *curl = h->ctx;
2413
2414 switch (opt) {
2415 case PHP_HTTP_CLIENT_OPT_CONFIGURATION:
2416 return php_http_options_apply(&php_http_curlm_options, (HashTable *) arg, h);
2417 break;
2418
2419 case PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING:
2420 if (CURLM_OK != curl_multi_setopt(curl->handle->multi, CURLMOPT_PIPELINING, (long) *((zend_bool *) arg))) {
2421 return FAILURE;
2422 }
2423 break;
2424
2425 case PHP_HTTP_CLIENT_OPT_USE_EVENTS:
2426 #if PHP_HTTP_HAVE_EVENT
2427 return php_http_curlm_use_eventloop(h, *(zend_bool *) arg);
2428 break;
2429 #endif
2430
2431 default:
2432 return FAILURE;
2433 }
2434 return SUCCESS;
2435 }
2436
2437 static int apply_available_options(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
2438 {
2439 php_http_option_t *opt = pDest;
2440 HashTable *ht;
2441 zval *entry;
2442 int c;
2443
2444 ht = va_arg(args, HashTable*);
2445
2446 MAKE_STD_ZVAL(entry);
2447
2448 if ((c = zend_hash_num_elements(&opt->suboptions.options))) {
2449 array_init_size(entry, c);
2450 zend_hash_apply_with_arguments(&opt->suboptions.options TSRMLS_CC, apply_available_options, 1, Z_ARRVAL_P(entry));
2451 } else {
2452 /* catch deliberate NULL options */
2453 if (Z_TYPE(opt->defval) == IS_STRING && !Z_STRVAL(opt->defval)) {
2454 ZVAL_NULL(entry);
2455 } else {
2456 ZVAL_COPY_VALUE(entry, &opt->defval);
2457 zval_copy_ctor(entry);
2458 }
2459 }
2460
2461 if (hash_key->nKeyLength) {
2462 zend_hash_quick_update(ht, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &entry, sizeof(zval *), NULL);
2463 } else {
2464 zend_hash_index_update(ht, hash_key->h, (void *) &entry, sizeof(zval *), NULL);
2465 }
2466
2467 return ZEND_HASH_APPLY_KEEP;
2468 }
2469
2470 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)
2471 {
2472 php_http_client_enqueue_t *enqueue;
2473 TSRMLS_FETCH_FROM_CTX(h->ts);
2474
2475 switch (opt) {
2476 case PHP_HTTP_CLIENT_OPT_PROGRESS_INFO:
2477 if ((enqueue = php_http_client_enqueued(h, arg, NULL))) {
2478 php_http_client_curl_handler_t *handler = enqueue->opaque;
2479
2480 *((php_http_client_progress_state_t **) res) = &handler->progress;
2481 return SUCCESS;
2482 }
2483 break;
2484
2485 case PHP_HTTP_CLIENT_OPT_TRANSFER_INFO:
2486 if ((enqueue = php_http_client_enqueued(h, arg, NULL))) {
2487 php_http_client_curl_handler_t *handler = enqueue->opaque;
2488
2489 php_http_curle_get_info(handler->handle, *(HashTable **) res);
2490 return SUCCESS;
2491 }
2492 break;
2493
2494 case PHP_HTTP_CLIENT_OPT_AVAILABLE_OPTIONS:
2495 zend_hash_apply_with_arguments(&php_http_curle_options.options TSRMLS_CC, apply_available_options, 1, *(HashTable **) res);
2496 break;
2497
2498 case PHP_HTTP_CLIENT_OPT_AVAILABLE_CONFIGURATION:
2499 zend_hash_apply_with_arguments(&php_http_curlm_options.options TSRMLS_CC, apply_available_options, 1, *(HashTable **) res);
2500 break;
2501
2502 default:
2503 break;
2504 }
2505
2506 return FAILURE;
2507 }
2508
2509 static php_http_client_ops_t php_http_client_curl_ops = {
2510 &php_http_curlm_resource_factory_ops,
2511 php_http_client_curl_init,
2512 NULL /* copy */,
2513 php_http_client_curl_dtor,
2514 php_http_client_curl_reset,
2515 php_http_client_curl_exec,
2516 php_http_client_curl_wait,
2517 php_http_client_curl_once,
2518 php_http_client_curl_enqueue,
2519 php_http_client_curl_dequeue,
2520 php_http_client_curl_setopt,
2521 php_http_client_curl_getopt
2522 };
2523
2524 php_http_client_ops_t *php_http_client_curl_get_ops(void)
2525 {
2526 return &php_http_client_curl_ops;
2527 }
2528
2529 PHP_MINIT_FUNCTION(http_client_curl)
2530 {
2531 curl_version_info_data *info;
2532 php_http_options_t *options;
2533 php_http_client_driver_t driver = {
2534 ZEND_STRL("curl"),
2535 &php_http_client_curl_ops
2536 };
2537
2538 if (SUCCESS != php_http_client_driver_add(&driver)) {
2539 return FAILURE;
2540 }
2541
2542 if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl"), &php_http_curlm_resource_factory_ops, NULL, NULL TSRMLS_CC)) {
2543 return FAILURE;
2544 }
2545 if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl\\Request"), &php_http_curle_resource_factory_ops, NULL, NULL TSRMLS_CC)) {
2546 return FAILURE;
2547 }
2548
2549 if ((options = php_http_options_init(&php_http_curle_options, 1))) {
2550 options->getter = php_http_curle_get_option;
2551 options->setter = php_http_curle_set_option;
2552
2553 php_http_curle_options_init(options TSRMLS_CC);
2554 }
2555 if ((options = php_http_options_init(&php_http_curlm_options, 1))) {
2556 options->getter = php_http_option_get;
2557 options->setter = php_http_curlm_set_option;
2558
2559 php_http_curlm_options_init(options TSRMLS_CC);
2560 }
2561
2562 if ((info = curl_version_info(CURLVERSION_NOW))) {
2563 /*
2564 * Feature constants
2565 */
2566 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "FEATURES", info->features, CONST_CS|CONST_PERSISTENT);
2567
2568 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "IPV6", CURL_VERSION_IPV6, CONST_CS|CONST_PERSISTENT);
2569 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "KERBEROS4", CURL_VERSION_KERBEROS4, CONST_CS|CONST_PERSISTENT);
2570 #if PHP_HTTP_CURL_VERSION(7,40,0)
2571 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "KERBEROS5", CURL_VERSION_KERBEROS5, CONST_CS|CONST_PERSISTENT);
2572 #endif
2573 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "SSL", CURL_VERSION_SSL, CONST_CS|CONST_PERSISTENT);
2574 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "LIBZ", CURL_VERSION_LIBZ, CONST_CS|CONST_PERSISTENT);
2575 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "NTLM", CURL_VERSION_NTLM, CONST_CS|CONST_PERSISTENT);
2576 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "GSSNEGOTIATE", CURL_VERSION_GSSNEGOTIATE, CONST_CS|CONST_PERSISTENT);
2577 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "ASYNCHDNS", CURL_VERSION_ASYNCHDNS, CONST_CS|CONST_PERSISTENT);
2578 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "SPNEGO", CURL_VERSION_SPNEGO, CONST_CS|CONST_PERSISTENT);
2579 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "LARGEFILE", CURL_VERSION_LARGEFILE, CONST_CS|CONST_PERSISTENT);
2580 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "IDN", CURL_VERSION_IDN, CONST_CS|CONST_PERSISTENT);
2581 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "SSPI", CURL_VERSION_SSPI, CONST_CS|CONST_PERSISTENT);
2582 #if PHP_HTTP_CURL_VERSION(7,38,0)
2583 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "GSSAPI", CURL_VERSION_GSSAPI, CONST_CS|CONST_PERSISTENT);
2584 #endif
2585 #if PHP_HTTP_CURL_VERSION(7,21,4)
2586 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "TLSAUTH_SRP", CURL_VERSION_TLSAUTH_SRP, CONST_CS|CONST_PERSISTENT);
2587 #endif
2588 #if PHP_HTTP_CURL_VERSION(7,22,0)
2589 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "NTLM_WB", CURL_VERSION_NTLM_WB, CONST_CS|CONST_PERSISTENT);
2590 #endif
2591 #if PHP_HTTP_CURL_VERSION(7,33,0)
2592 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "HTTP2", CURL_VERSION_HTTP2, CONST_CS|CONST_PERSISTENT);
2593 #endif
2594 #if PHP_HTTP_CURL_VERSION(7,40,0)
2595 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "UNIX_SOCKETS", CURL_VERSION_UNIX_SOCKETS, CONST_CS|CONST_PERSISTENT);
2596 #endif
2597 #if PHP_HTTP_CURL_VERSION(7,47,0)
2598 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "PSL", CURL_VERSION_PSL, CONST_CS|CONST_PERSISTENT);
2599 #endif
2600
2601 /*
2602 * Version constants
2603 */
2604 REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl", "VERSIONS", curl_version(), CONST_CS|CONST_PERSISTENT);
2605 #if CURLVERSION_NOW >= 0
2606 REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "CURL", (char *) info->version, CONST_CS|CONST_PERSISTENT);
2607 REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "SSL", (char *) info->ssl_version, CONST_CS|CONST_PERSISTENT);
2608 REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "LIBZ", (char *) info->libz_version, CONST_CS|CONST_PERSISTENT);
2609 # if CURLVERSION_NOW >= 1
2610 REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "ARES", (char *) info->ares, CONST_CS|CONST_PERSISTENT);
2611 # if CURLVERSION_NOW >= 2
2612 REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "IDN", (char *) info->libidn, CONST_CS|CONST_PERSISTENT);
2613 # endif
2614 # endif
2615 #endif
2616 }
2617
2618 /*
2619 * HTTP Protocol Version Constants
2620 */
2621 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_1_0", CURL_HTTP_VERSION_1_0, CONST_CS|CONST_PERSISTENT);
2622 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_1_1", CURL_HTTP_VERSION_1_1, CONST_CS|CONST_PERSISTENT);
2623 #if PHP_HTTP_CURL_VERSION(7,33,0)
2624 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_2_0", CURL_HTTP_VERSION_2_0, CONST_CS|CONST_PERSISTENT);
2625 #endif
2626 #if PHP_HTTP_CURL_VERSION(7,47,0)
2627 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_2TLS", CURL_HTTP_VERSION_2TLS, CONST_CS|CONST_PERSISTENT);
2628 #endif
2629 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_ANY", CURL_HTTP_VERSION_NONE, CONST_CS|CONST_PERSISTENT);
2630
2631 /*
2632 * SSL Version Constants
2633 */
2634 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1", CURL_SSLVERSION_TLSv1, CONST_CS|CONST_PERSISTENT);
2635 #if PHP_HTTP_CURL_VERSION(7,34,0)
2636 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_0", CURL_SSLVERSION_TLSv1_0, CONST_CS|CONST_PERSISTENT);
2637 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_1", CURL_SSLVERSION_TLSv1_1, CONST_CS|CONST_PERSISTENT);
2638 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_2", CURL_SSLVERSION_TLSv1_2, CONST_CS|CONST_PERSISTENT);
2639 #endif
2640 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_SSLv2", CURL_SSLVERSION_SSLv2, CONST_CS|CONST_PERSISTENT);
2641 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_SSLv3", CURL_SSLVERSION_SSLv3, CONST_CS|CONST_PERSISTENT);
2642 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_ANY", CURL_SSLVERSION_DEFAULT, CONST_CS|CONST_PERSISTENT);
2643 #if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
2644 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "TLSAUTH_SRP", CURL_TLSAUTH_SRP, CONST_CS|CONST_PERSISTENT);
2645 #endif
2646
2647 /*
2648 * DNS IPvX resolving
2649 */
2650 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_V4", CURL_IPRESOLVE_V4, CONST_CS|CONST_PERSISTENT);
2651 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_V6", CURL_IPRESOLVE_V6, CONST_CS|CONST_PERSISTENT);
2652 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_ANY", CURL_IPRESOLVE_WHATEVER, CONST_CS|CONST_PERSISTENT);
2653
2654 /*
2655 * Auth Constants
2656 */
2657 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_BASIC", CURLAUTH_BASIC, CONST_CS|CONST_PERSISTENT);
2658 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_DIGEST", CURLAUTH_DIGEST, CONST_CS|CONST_PERSISTENT);
2659 #if PHP_HTTP_CURL_VERSION(7,19,3)
2660 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_DIGEST_IE", CURLAUTH_DIGEST_IE, CONST_CS|CONST_PERSISTENT);
2661 #endif
2662 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_NTLM", CURLAUTH_NTLM, CONST_CS|CONST_PERSISTENT);
2663 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_GSSNEG", CURLAUTH_GSSNEGOTIATE, CONST_CS|CONST_PERSISTENT);
2664 #if PHP_HTTP_CURL_VERSION(7,38,0)
2665 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_SPNEGO", CURLAUTH_NEGOTIATE, CONST_CS|CONST_PERSISTENT);
2666 #endif
2667 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_ANY", CURLAUTH_ANY, CONST_CS|CONST_PERSISTENT);
2668
2669 /*
2670 * Proxy Type Constants
2671 */
2672 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS4", CURLPROXY_SOCKS4, CONST_CS|CONST_PERSISTENT);
2673 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS4A", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT);
2674 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS5_HOSTNAME", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT);
2675 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS5", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT);
2676 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_HTTP", CURLPROXY_HTTP, CONST_CS|CONST_PERSISTENT);
2677 #if PHP_HTTP_CURL_VERSION(7,19,4)
2678 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_HTTP_1_0", CURLPROXY_HTTP_1_0, CONST_CS|CONST_PERSISTENT);
2679 #endif
2680
2681 /*
2682 * Post Redirection Constants
2683 */
2684 #if PHP_HTTP_CURL_VERSION(7,19,1)
2685 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_301", CURL_REDIR_POST_301, CONST_CS|CONST_PERSISTENT);
2686 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_302", CURL_REDIR_POST_302, CONST_CS|CONST_PERSISTENT);
2687 #if PHP_HTTP_CURL_VERSION(7,26,0)
2688 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_303", CURL_REDIR_POST_303, CONST_CS|CONST_PERSISTENT);
2689 #endif
2690 REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_ALL", CURL_REDIR_POST_ALL, CONST_CS|CONST_PERSISTENT);
2691 #endif
2692
2693 return SUCCESS;
2694 }
2695
2696 PHP_MSHUTDOWN_FUNCTION(http_client_curl)
2697 {
2698 php_persistent_handle_cleanup(ZEND_STRL("http\\Client\\Curl"), NULL, 0 TSRMLS_CC);
2699 php_persistent_handle_cleanup(ZEND_STRL("http\\Client\\Curl\\Request"), NULL, 0 TSRMLS_CC);
2700
2701 php_http_options_dtor(&php_http_curle_options);
2702 php_http_options_dtor(&php_http_curlm_options);
2703
2704 return SUCCESS;
2705 }
2706
2707 #endif /* PHP_HTTP_HAVE_CURL */
2708
2709 /*
2710 * Local variables:
2711 * tab-width: 4
2712 * c-basic-offset: 4
2713 * End:
2714 * vim600: noet sw=4 ts=4 fdm=marker
2715 * vim<600: noet sw=4 ts=4
2716 */