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