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