1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Sterling Hughes <sterling@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "php.h"
24 #include "Zend/zend_exceptions.h"
25
26 #include <stdio.h>
27 #include <string.h>
28
29 #ifdef PHP_WIN32
30 #include <winsock2.h>
31 #include <sys/types.h>
32 #endif
33
34 #include <curl/curl.h>
35 #include <curl/easy.h>
36
37 /* As of curl 7.11.1 this is no longer defined inside curl.h */
38 #ifndef HttpPost
39 #define HttpPost curl_httppost
40 #endif
41
42 /* {{{ cruft for thread safe SSL crypto locks */
43 #if defined(ZTS) && defined(HAVE_CURL_OLD_OPENSSL)
44 # if defined(HAVE_OPENSSL_CRYPTO_H)
45 # define PHP_CURL_NEED_OPENSSL_TSL
46 # include <openssl/crypto.h>
47 # else
48 # warning \
49 "libcurl was compiled with OpenSSL support, but configure could not find " \
50 "openssl/crypto.h; thus no SSL crypto locking callbacks will be set, which may " \
51 "cause random crashes on SSL requests"
52 # endif
53 #endif /* ZTS && HAVE_CURL_OLD_OPENSSL */
54 /* }}} */
55
56 #define SMART_STR_PREALLOC 4096
57
58 #include "zend_smart_str.h"
59 #include "ext/standard/info.h"
60 #include "ext/standard/file.h"
61 #include "ext/standard/url.h"
62 #include "curl_private.h"
63
64 #ifdef __GNUC__
65 /* don't complain about deprecated CURLOPT_* we're exposing to PHP; we
66 need to keep using those to avoid breaking PHP API compatibiltiy */
67 # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
68 #endif
69
70 #include "curl_arginfo.h"
71
72 #ifdef PHP_CURL_NEED_OPENSSL_TSL /* {{{ */
73 static MUTEX_T *php_curl_openssl_tsl = NULL;
74
75 /* Locking callbacks are no longer used since OpenSSL 1.1. Mark the functions as unused to
76 * avoid warnings due to this. */
php_curl_ssl_lock(int mode,int n,const char * file,int line)77 static ZEND_ATTRIBUTE_UNUSED void php_curl_ssl_lock(int mode, int n, const char * file, int line)
78 {
79 if (mode & CRYPTO_LOCK) {
80 tsrm_mutex_lock(php_curl_openssl_tsl[n]);
81 } else {
82 tsrm_mutex_unlock(php_curl_openssl_tsl[n]);
83 }
84 }
85
php_curl_ssl_id(void)86 static ZEND_ATTRIBUTE_UNUSED unsigned long php_curl_ssl_id(void)
87 {
88 return (unsigned long) tsrm_thread_id();
89 }
90 #endif
91 /* }}} */
92
93 #define CAAL(s, v) add_assoc_long_ex(return_value, s, sizeof(s) - 1, (zend_long) v);
94 #define CAAD(s, v) add_assoc_double_ex(return_value, s, sizeof(s) - 1, (double) v);
95 #define CAAS(s, v) add_assoc_string_ex(return_value, s, sizeof(s) - 1, (char *) (v ? v : ""));
96 #define CAASTR(s, v) add_assoc_str_ex(return_value, s, sizeof(s) - 1, \
97 v ? zend_string_copy(v) : ZSTR_EMPTY_ALLOC());
98 #define CAAZ(s, v) add_assoc_zval_ex(return_value, s, sizeof(s) -1 , (zval *) v);
99
100 #if defined(PHP_WIN32) || defined(__GNUC__)
101 # define php_curl_ret(__ret) RETVAL_FALSE; return __ret;
102 #else
103 # define php_curl_ret(__ret) RETVAL_FALSE; return;
104 #endif
105
php_curl_option_str(php_curl * ch,zend_long option,const char * str,const size_t len)106 static zend_result php_curl_option_str(php_curl *ch, zend_long option, const char *str, const size_t len)
107 {
108 if (zend_char_has_nul_byte(str, len)) {
109 zend_value_error("%s(): cURL option must not contain any null bytes", get_active_function_name());
110 return FAILURE;
111 }
112
113 CURLcode error = curl_easy_setopt(ch->cp, option, str);
114 SAVE_CURL_ERROR(ch, error);
115
116 return error == CURLE_OK ? SUCCESS : FAILURE;
117 }
118
php_curl_option_url(php_curl * ch,const zend_string * url)119 static zend_result php_curl_option_url(php_curl *ch, const zend_string *url) /* {{{ */
120 {
121 /* Disable file:// if open_basedir are used */
122 if (PG(open_basedir) && *PG(open_basedir)) {
123 curl_easy_setopt(ch->cp, CURLOPT_PROTOCOLS, CURLPROTO_ALL & ~CURLPROTO_FILE);
124 }
125
126 #ifdef PHP_WIN32
127 if (
128 zend_string_starts_with_literal_ci(url, "file://")
129 && '/' != ZSTR_VAL(url)[sizeof("file://") - 1]
130 && ZSTR_LEN(url) < MAXPATHLEN - 2
131 ) {
132 char _tmp[MAXPATHLEN] = {0};
133
134 memmove(_tmp, "file:///", sizeof("file:///") - 1);
135 memmove(_tmp + sizeof("file:///") - 1, ZSTR_VAL(url) + sizeof("file://") - 1, ZSTR_LEN(url) - sizeof("file://") + 1);
136
137 return php_curl_option_str(ch, CURLOPT_URL, _tmp, ZSTR_LEN(url) + 1);
138 }
139 #endif
140
141 return php_curl_option_str(ch, CURLOPT_URL, ZSTR_VAL(url), ZSTR_LEN(url));
142 }
143 /* }}} */
144
_php_curl_verify_handlers(php_curl * ch,bool reporterror)145 void _php_curl_verify_handlers(php_curl *ch, bool reporterror) /* {{{ */
146 {
147 php_stream *stream;
148
149 ZEND_ASSERT(ch);
150
151 if (!Z_ISUNDEF(ch->handlers.std_err)) {
152 stream = (php_stream *)zend_fetch_resource2_ex(&ch->handlers.std_err, NULL, php_file_le_stream(), php_file_le_pstream());
153 if (stream == NULL) {
154 if (reporterror) {
155 php_error_docref(NULL, E_WARNING, "CURLOPT_STDERR resource has gone away, resetting to stderr");
156 }
157 zval_ptr_dtor(&ch->handlers.std_err);
158 ZVAL_UNDEF(&ch->handlers.std_err);
159
160 curl_easy_setopt(ch->cp, CURLOPT_STDERR, stderr);
161 }
162 }
163 if (ch->handlers.read && !Z_ISUNDEF(ch->handlers.read->stream)) {
164 stream = (php_stream *)zend_fetch_resource2_ex(&ch->handlers.read->stream, NULL, php_file_le_stream(), php_file_le_pstream());
165 if (stream == NULL) {
166 if (reporterror) {
167 php_error_docref(NULL, E_WARNING, "CURLOPT_INFILE resource has gone away, resetting to default");
168 }
169 zval_ptr_dtor(&ch->handlers.read->stream);
170 ZVAL_UNDEF(&ch->handlers.read->stream);
171 ch->handlers.read->res = NULL;
172 ch->handlers.read->fp = 0;
173
174 curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch);
175 }
176 }
177 if (ch->handlers.write_header && !Z_ISUNDEF(ch->handlers.write_header->stream)) {
178 stream = (php_stream *)zend_fetch_resource2_ex(&ch->handlers.write_header->stream, NULL, php_file_le_stream(), php_file_le_pstream());
179 if (stream == NULL) {
180 if (reporterror) {
181 php_error_docref(NULL, E_WARNING, "CURLOPT_WRITEHEADER resource has gone away, resetting to default");
182 }
183 zval_ptr_dtor(&ch->handlers.write_header->stream);
184 ZVAL_UNDEF(&ch->handlers.write_header->stream);
185 ch->handlers.write_header->fp = 0;
186
187 ch->handlers.write_header->method = PHP_CURL_IGNORE;
188 curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch);
189 }
190 }
191 if (ch->handlers.write && !Z_ISUNDEF(ch->handlers.write->stream)) {
192 stream = (php_stream *)zend_fetch_resource2_ex(&ch->handlers.write->stream, NULL, php_file_le_stream(), php_file_le_pstream());
193 if (stream == NULL) {
194 if (reporterror) {
195 php_error_docref(NULL, E_WARNING, "CURLOPT_FILE resource has gone away, resetting to default");
196 }
197 zval_ptr_dtor(&ch->handlers.write->stream);
198 ZVAL_UNDEF(&ch->handlers.write->stream);
199 ch->handlers.write->fp = 0;
200
201 ch->handlers.write->method = PHP_CURL_STDOUT;
202 curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch);
203 }
204 }
205 return;
206 }
207 /* }}} */
208
209 /* {{{ curl_module_entry */
210 zend_module_entry curl_module_entry = {
211 STANDARD_MODULE_HEADER,
212 "curl",
213 ext_functions,
214 PHP_MINIT(curl),
215 PHP_MSHUTDOWN(curl),
216 NULL,
217 NULL,
218 PHP_MINFO(curl),
219 PHP_CURL_VERSION,
220 STANDARD_MODULE_PROPERTIES
221 };
222 /* }}} */
223
224 #ifdef COMPILE_DL_CURL
225 ZEND_GET_MODULE (curl)
226 #endif
227
228 /* CurlHandle class */
229
230 zend_class_entry *curl_ce;
231 zend_class_entry *curl_share_ce;
232 static zend_object_handlers curl_object_handlers;
233
234 static zend_object *curl_create_object(zend_class_entry *class_type);
235 static void curl_free_obj(zend_object *object);
236 static HashTable *curl_get_gc(zend_object *object, zval **table, int *n);
237 static zend_function *curl_get_constructor(zend_object *object);
238 static zend_object *curl_clone_obj(zend_object *object);
239 php_curl *init_curl_handle_into_zval(zval *curl);
240 static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields);
241
242 /* {{{ PHP_INI_BEGIN */
243 PHP_INI_BEGIN()
244 PHP_INI_ENTRY("curl.cainfo", "", PHP_INI_SYSTEM, NULL)
PHP_INI_END()245 PHP_INI_END()
246 /* }}} */
247
248 /* {{{ PHP_MINFO_FUNCTION */
249 PHP_MINFO_FUNCTION(curl)
250 {
251 curl_version_info_data *d;
252 char **p;
253 char str[1024];
254 size_t n = 0;
255
256 d = curl_version_info(CURLVERSION_NOW);
257 php_info_print_table_start();
258 php_info_print_table_row(2, "cURL support", "enabled");
259 php_info_print_table_row(2, "cURL Information", d->version);
260 sprintf(str, "%d", d->age);
261 php_info_print_table_row(2, "Age", str);
262
263 /* To update on each new cURL release using src/main.c in cURL sources */
264 /* make sure to sync this list with curl_version as well */
265 if (d->features) {
266 struct feat {
267 const char *name;
268 int bitmask;
269 };
270
271 unsigned int i;
272
273 static const struct feat feats[] = {
274 {"AsynchDNS", CURL_VERSION_ASYNCHDNS},
275 {"CharConv", CURL_VERSION_CONV},
276 {"Debug", CURL_VERSION_DEBUG},
277 {"GSS-Negotiate", CURL_VERSION_GSSNEGOTIATE},
278 {"IDN", CURL_VERSION_IDN},
279 {"IPv6", CURL_VERSION_IPV6},
280 {"krb4", CURL_VERSION_KERBEROS4},
281 {"Largefile", CURL_VERSION_LARGEFILE},
282 {"libz", CURL_VERSION_LIBZ},
283 {"NTLM", CURL_VERSION_NTLM},
284 {"NTLMWB", CURL_VERSION_NTLM_WB},
285 {"SPNEGO", CURL_VERSION_SPNEGO},
286 {"SSL", CURL_VERSION_SSL},
287 {"SSPI", CURL_VERSION_SSPI},
288 {"TLS-SRP", CURL_VERSION_TLSAUTH_SRP},
289 {"HTTP2", CURL_VERSION_HTTP2},
290 {"GSSAPI", CURL_VERSION_GSSAPI},
291 {"KERBEROS5", CURL_VERSION_KERBEROS5},
292 {"UNIX_SOCKETS", CURL_VERSION_UNIX_SOCKETS},
293 {"PSL", CURL_VERSION_PSL},
294 {"HTTPS_PROXY", CURL_VERSION_HTTPS_PROXY},
295 {"MULTI_SSL", CURL_VERSION_MULTI_SSL},
296 {"BROTLI", CURL_VERSION_BROTLI},
297 #if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */
298 {"ALTSVC", CURL_VERSION_ALTSVC},
299 #endif
300 #if LIBCURL_VERSION_NUM >= 0x074200 /* Available since 7.66.0 */
301 {"HTTP3", CURL_VERSION_HTTP3},
302 #endif
303 #if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */
304 {"UNICODE", CURL_VERSION_UNICODE},
305 {"ZSTD", CURL_VERSION_ZSTD},
306 #endif
307 #if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */
308 {"HSTS", CURL_VERSION_HSTS},
309 #endif
310 #if LIBCURL_VERSION_NUM >= 0x074c00 /* Available since 7.76.0 */
311 {"GSASL", CURL_VERSION_GSASL},
312 #endif
313 {NULL, 0}
314 };
315
316 php_info_print_table_row(1, "Features");
317 for(i=0; i<sizeof(feats)/sizeof(feats[0]); i++) {
318 if (feats[i].name) {
319 php_info_print_table_row(2, feats[i].name, d->features & feats[i].bitmask ? "Yes" : "No");
320 }
321 }
322 }
323
324 n = 0;
325 p = (char **) d->protocols;
326 while (*p != NULL) {
327 n += sprintf(str + n, "%s%s", *p, *(p + 1) != NULL ? ", " : "");
328 p++;
329 }
330 php_info_print_table_row(2, "Protocols", str);
331
332 php_info_print_table_row(2, "Host", d->host);
333
334 if (d->ssl_version) {
335 php_info_print_table_row(2, "SSL Version", d->ssl_version);
336 }
337
338 if (d->libz_version) {
339 php_info_print_table_row(2, "ZLib Version", d->libz_version);
340 }
341
342 #if defined(CURLVERSION_SECOND) && CURLVERSION_NOW >= CURLVERSION_SECOND
343 if (d->ares) {
344 php_info_print_table_row(2, "ZLib Version", d->ares);
345 }
346 #endif
347
348 #if defined(CURLVERSION_THIRD) && CURLVERSION_NOW >= CURLVERSION_THIRD
349 if (d->libidn) {
350 php_info_print_table_row(2, "libIDN Version", d->libidn);
351 }
352 #endif
353
354 if (d->iconv_ver_num) {
355 php_info_print_table_row(2, "IconV Version", d->iconv_ver_num);
356 }
357
358 if (d->libssh_version) {
359 php_info_print_table_row(2, "libSSH Version", d->libssh_version);
360 }
361
362 php_info_print_table_end();
363
364 DISPLAY_INI_ENTRIES();
365 }
366 /* }}} */
367
368 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(curl)369 PHP_MINIT_FUNCTION(curl)
370 {
371 REGISTER_INI_ENTRIES();
372
373 register_curl_symbols(module_number);
374
375 #ifdef PHP_CURL_NEED_OPENSSL_TSL
376 if (!CRYPTO_get_id_callback()) {
377 int i, c = CRYPTO_num_locks();
378
379 php_curl_openssl_tsl = malloc(c * sizeof(MUTEX_T));
380 if (!php_curl_openssl_tsl) {
381 return FAILURE;
382 }
383
384 for (i = 0; i < c; ++i) {
385 php_curl_openssl_tsl[i] = tsrm_mutex_alloc();
386 }
387
388 CRYPTO_set_id_callback(php_curl_ssl_id);
389 CRYPTO_set_locking_callback(php_curl_ssl_lock);
390 }
391 #endif
392
393 if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) {
394 return FAILURE;
395 }
396
397 curl_ce = register_class_CurlHandle();
398 curl_ce->create_object = curl_create_object;
399 curl_ce->default_object_handlers = &curl_object_handlers;
400
401 memcpy(&curl_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
402 curl_object_handlers.offset = XtOffsetOf(php_curl, std);
403 curl_object_handlers.free_obj = curl_free_obj;
404 curl_object_handlers.get_gc = curl_get_gc;
405 curl_object_handlers.get_constructor = curl_get_constructor;
406 curl_object_handlers.clone_obj = curl_clone_obj;
407 curl_object_handlers.cast_object = curl_cast_object;
408 curl_object_handlers.compare = zend_objects_not_comparable;
409
410 curl_multi_ce = register_class_CurlMultiHandle();
411 curl_multi_register_handlers();
412
413 curl_share_ce = register_class_CurlShareHandle();
414 curl_share_register_handlers();
415 curlfile_register_class();
416
417 return SUCCESS;
418 }
419 /* }}} */
420
421 /* CurlHandle class */
422
curl_create_object(zend_class_entry * class_type)423 static zend_object *curl_create_object(zend_class_entry *class_type) {
424 php_curl *intern = zend_object_alloc(sizeof(php_curl), class_type);
425
426 zend_object_std_init(&intern->std, class_type);
427 object_properties_init(&intern->std, class_type);
428
429 return &intern->std;
430 }
431
curl_get_constructor(zend_object * object)432 static zend_function *curl_get_constructor(zend_object *object) {
433 zend_throw_error(NULL, "Cannot directly construct CurlHandle, use curl_init() instead");
434 return NULL;
435 }
436
curl_clone_obj(zend_object * object)437 static zend_object *curl_clone_obj(zend_object *object) {
438 php_curl *ch;
439 CURL *cp;
440 zval *postfields;
441 zend_object *clone_object;
442 php_curl *clone_ch;
443
444 clone_object = curl_create_object(curl_ce);
445 clone_ch = curl_from_obj(clone_object);
446 init_curl_handle(clone_ch);
447
448 ch = curl_from_obj(object);
449 cp = curl_easy_duphandle(ch->cp);
450 if (!cp) {
451 zend_throw_exception(NULL, "Failed to clone CurlHandle", 0);
452 return &clone_ch->std;
453 }
454
455 clone_ch->cp = cp;
456 _php_setup_easy_copy_handlers(clone_ch, ch);
457
458 postfields = &clone_ch->postfields;
459 if (Z_TYPE_P(postfields) != IS_UNDEF) {
460 if (build_mime_structure_from_hash(clone_ch, postfields) == FAILURE) {
461 zend_throw_exception(NULL, "Failed to clone CurlHandle", 0);
462 return &clone_ch->std;
463 }
464 }
465
466 return &clone_ch->std;
467 }
468
curl_get_gc(zend_object * object,zval ** table,int * n)469 static HashTable *curl_get_gc(zend_object *object, zval **table, int *n)
470 {
471 php_curl *curl = curl_from_obj(object);
472
473 zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
474
475 zend_get_gc_buffer_add_zval(gc_buffer, &curl->postfields);
476 if (curl->handlers.read) {
477 if (ZEND_FCC_INITIALIZED(curl->handlers.read->fcc)) {
478 zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.read->fcc);
479 }
480 zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.read->stream);
481 }
482
483 if (curl->handlers.write) {
484 if (ZEND_FCC_INITIALIZED(curl->handlers.write->fcc)) {
485 zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.write->fcc);
486 }
487 zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write->stream);
488 }
489
490 if (curl->handlers.write_header) {
491 if (ZEND_FCC_INITIALIZED(curl->handlers.write_header->fcc)) {
492 zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.write_header->fcc);
493 }
494 zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write_header->stream);
495 }
496
497 if (ZEND_FCC_INITIALIZED(curl->handlers.progress)) {
498 zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.progress);
499 }
500
501 if (ZEND_FCC_INITIALIZED(curl->handlers.xferinfo)) {
502 zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.xferinfo);
503 }
504
505 if (ZEND_FCC_INITIALIZED(curl->handlers.fnmatch)) {
506 zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.fnmatch);
507 }
508
509 #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
510 if (ZEND_FCC_INITIALIZED(curl->handlers.sshhostkey)) {
511 zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.sshhostkey);
512 }
513 #endif
514
515 zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.std_err);
516 zend_get_gc_buffer_add_zval(gc_buffer, &curl->private_data);
517
518 zend_get_gc_buffer_use(gc_buffer, table, n);
519
520 return zend_std_get_properties(object);
521 }
522
curl_cast_object(zend_object * obj,zval * result,int type)523 zend_result curl_cast_object(zend_object *obj, zval *result, int type)
524 {
525 if (type == IS_LONG) {
526 /* For better backward compatibility, make (int) $curl_handle return the object ID,
527 * similar to how it previously returned the resource ID. */
528 ZVAL_LONG(result, obj->handle);
529 return SUCCESS;
530 }
531
532 return zend_std_cast_object_tostring(obj, result, type);
533 }
534
535 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(curl)536 PHP_MSHUTDOWN_FUNCTION(curl)
537 {
538 curl_global_cleanup();
539 #ifdef PHP_CURL_NEED_OPENSSL_TSL
540 if (php_curl_openssl_tsl) {
541 int i, c = CRYPTO_num_locks();
542
543 CRYPTO_set_id_callback(NULL);
544 CRYPTO_set_locking_callback(NULL);
545
546 for (i = 0; i < c; ++i) {
547 tsrm_mutex_free(php_curl_openssl_tsl[i]);
548 }
549
550 free(php_curl_openssl_tsl);
551 php_curl_openssl_tsl = NULL;
552 }
553 #endif
554 UNREGISTER_INI_ENTRIES();
555 return SUCCESS;
556 }
557 /* }}} */
558
559 /* {{{ curl_write */
curl_write(char * data,size_t size,size_t nmemb,void * ctx)560 static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx)
561 {
562 php_curl *ch = (php_curl *) ctx;
563 php_curl_write *write_handler = ch->handlers.write;
564 size_t length = size * nmemb;
565
566 #if PHP_CURL_DEBUG
567 fprintf(stderr, "curl_write() called\n");
568 fprintf(stderr, "data = %s, size = %d, nmemb = %d, ctx = %x\n", data, size, nmemb, ctx);
569 #endif
570
571 switch (write_handler->method) {
572 case PHP_CURL_STDOUT:
573 PHPWRITE(data, length);
574 break;
575 case PHP_CURL_FILE:
576 return fwrite(data, size, nmemb, write_handler->fp);
577 case PHP_CURL_RETURN:
578 if (length > 0) {
579 smart_str_appendl(&write_handler->buf, data, (int) length);
580 }
581 break;
582 case PHP_CURL_USER: {
583 zval argv[2];
584 zval retval;
585
586 GC_ADDREF(&ch->std);
587 ZVAL_OBJ(&argv[0], &ch->std);
588 ZVAL_STRINGL(&argv[1], data, length);
589
590 ch->in_callback = true;
591 zend_call_known_fcc(&write_handler->fcc, &retval, /* param_count */ 2, argv, /* named_params */ NULL);
592 ch->in_callback = false;
593 if (!Z_ISUNDEF(retval)) {
594 _php_curl_verify_handlers(ch, /* reporterror */ true);
595 /* TODO Check callback returns an int or something castable to int */
596 length = zval_get_long(&retval);
597 }
598
599 zval_ptr_dtor(&argv[0]);
600 zval_ptr_dtor(&argv[1]);
601 break;
602 }
603 }
604
605 return length;
606 }
607 /* }}} */
608
609 /* {{{ curl_fnmatch */
curl_fnmatch(void * ctx,const char * pattern,const char * string)610 static int curl_fnmatch(void *ctx, const char *pattern, const char *string)
611 {
612 php_curl *ch = (php_curl *) ctx;
613 int rval = CURL_FNMATCHFUNC_FAIL;
614 zval argv[3];
615 zval retval;
616
617 GC_ADDREF(&ch->std);
618 ZVAL_OBJ(&argv[0], &ch->std);
619 ZVAL_STRING(&argv[1], pattern);
620 ZVAL_STRING(&argv[2], string);
621
622 ch->in_callback = true;
623 zend_call_known_fcc(&ch->handlers.fnmatch, &retval, /* param_count */ 3, argv, /* named_params */ NULL);
624 ch->in_callback = false;
625
626 if (!Z_ISUNDEF(retval)) {
627 _php_curl_verify_handlers(ch, /* reporterror */ true);
628 /* TODO Check callback returns an int or something castable to int */
629 rval = zval_get_long(&retval);
630 }
631 zval_ptr_dtor(&argv[0]);
632 zval_ptr_dtor(&argv[1]);
633 zval_ptr_dtor(&argv[2]);
634 return rval;
635 }
636 /* }}} */
637
638 /* {{{ curl_progress */
curl_progress(void * clientp,double dltotal,double dlnow,double ultotal,double ulnow)639 static size_t curl_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
640 {
641 php_curl *ch = (php_curl *)clientp;
642 size_t rval = 0;
643
644 #if PHP_CURL_DEBUG
645 fprintf(stderr, "curl_progress() called\n");
646 fprintf(stderr, "clientp = %x, dltotal = %f, dlnow = %f, ultotal = %f, ulnow = %f\n", clientp, dltotal, dlnow, ultotal, ulnow);
647 #endif
648
649 zval args[5];
650 zval retval;
651
652 GC_ADDREF(&ch->std);
653 ZVAL_OBJ(&args[0], &ch->std);
654 ZVAL_LONG(&args[1], (zend_long)dltotal);
655 ZVAL_LONG(&args[2], (zend_long)dlnow);
656 ZVAL_LONG(&args[3], (zend_long)ultotal);
657 ZVAL_LONG(&args[4], (zend_long)ulnow);
658
659 ch->in_callback = true;
660 zend_call_known_fcc(&ch->handlers.progress, &retval, /* param_count */ 5, args, /* named_params */ NULL);
661 ch->in_callback = false;
662
663 if (!Z_ISUNDEF(retval)) {
664 _php_curl_verify_handlers(ch, /* reporterror */ true);
665 /* TODO Check callback returns an int or something castable to int */
666 if (0 != zval_get_long(&retval)) {
667 rval = 1;
668 }
669 }
670
671 zval_ptr_dtor(&args[0]);
672 return rval;
673 }
674 /* }}} */
675
676 /* {{{ curl_xferinfo */
curl_xferinfo(void * clientp,curl_off_t dltotal,curl_off_t dlnow,curl_off_t ultotal,curl_off_t ulnow)677 static size_t curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
678 {
679 php_curl *ch = (php_curl *)clientp;
680 size_t rval = 0;
681
682 #if PHP_CURL_DEBUG
683 fprintf(stderr, "curl_xferinfo() called\n");
684 fprintf(stderr, "clientp = %x, dltotal = %ld, dlnow = %ld, ultotal = %ld, ulnow = %ld\n", clientp, dltotal, dlnow, ultotal, ulnow);
685 #endif
686
687 zval argv[5];
688 zval retval;
689
690 GC_ADDREF(&ch->std);
691 ZVAL_OBJ(&argv[0], &ch->std);
692 ZVAL_LONG(&argv[1], dltotal);
693 ZVAL_LONG(&argv[2], dlnow);
694 ZVAL_LONG(&argv[3], ultotal);
695 ZVAL_LONG(&argv[4], ulnow);
696
697 ch->in_callback = true;
698 zend_call_known_fcc(&ch->handlers.xferinfo, &retval, /* param_count */ 5, argv, /* named_params */ NULL);
699 ch->in_callback = false;
700
701 if (!Z_ISUNDEF(retval)) {
702 _php_curl_verify_handlers(ch, /* reporterror */ true);
703 /* TODO Check callback returns an int or something castable to int */
704 if (0 != zval_get_long(&retval)) {
705 rval = 1;
706 }
707 }
708
709 zval_ptr_dtor(&argv[0]);
710 return rval;
711 }
712 /* }}} */
713
714 #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
curl_ssh_hostkeyfunction(void * clientp,int keytype,const char * key,size_t keylen)715 static int curl_ssh_hostkeyfunction(void *clientp, int keytype, const char *key, size_t keylen)
716 {
717 php_curl *ch = (php_curl *)clientp;
718 int rval = CURLKHMATCH_MISMATCH; /* cancel connection in case of an exception */
719
720 #if PHP_CURL_DEBUG
721 fprintf(stderr, "curl_ssh_hostkeyfunction() called\n");
722 fprintf(stderr, "clientp = %x, keytype = %d, key = %s, keylen = %zu\n", clientp, keytype, key, keylen);
723 #endif
724
725 zval args[4];
726 zval retval;
727
728 GC_ADDREF(&ch->std);
729 ZVAL_OBJ(&args[0], &ch->std);
730 ZVAL_LONG(&args[1], keytype);
731 ZVAL_STRINGL(&args[2], key, keylen);
732 ZVAL_LONG(&args[3], keylen);
733
734 ch->in_callback = true;
735 zend_call_known_fcc(&ch->handlers.sshhostkey, &retval, /* param_count */ 4, args, /* named_params */ NULL);
736 ch->in_callback = false;
737
738 if (!Z_ISUNDEF(retval)) {
739 _php_curl_verify_handlers(ch, /* reporterror */ true);
740 if (Z_TYPE(retval) == IS_LONG) {
741 zend_long retval_long = Z_LVAL(retval);
742 if (retval_long == CURLKHMATCH_OK || retval_long == CURLKHMATCH_MISMATCH) {
743 rval = retval_long;
744 } else {
745 zend_throw_error(NULL, "The CURLOPT_SSH_HOSTKEYFUNCTION callback must return either CURLKHMATCH_OK or CURLKHMATCH_MISMATCH");
746 }
747 } else {
748 zend_throw_error(NULL, "The CURLOPT_SSH_HOSTKEYFUNCTION callback must return either CURLKHMATCH_OK or CURLKHMATCH_MISMATCH");
749 }
750 }
751
752 zval_ptr_dtor(&args[0]);
753 zval_ptr_dtor(&args[2]);
754 return rval;
755 }
756 #endif
757
758 /* {{{ curl_read */
curl_read(char * data,size_t size,size_t nmemb,void * ctx)759 static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx)
760 {
761 php_curl *ch = (php_curl *)ctx;
762 php_curl_read *read_handler = ch->handlers.read;
763 int length = 0;
764
765 switch (read_handler->method) {
766 case PHP_CURL_DIRECT:
767 if (read_handler->fp) {
768 length = fread(data, size, nmemb, read_handler->fp);
769 }
770 break;
771 case PHP_CURL_USER: {
772 zval argv[3];
773 zval retval;
774
775 GC_ADDREF(&ch->std);
776 ZVAL_OBJ(&argv[0], &ch->std);
777 if (read_handler->res) {
778 GC_ADDREF(read_handler->res);
779 ZVAL_RES(&argv[1], read_handler->res);
780 } else {
781 ZVAL_NULL(&argv[1]);
782 }
783 ZVAL_LONG(&argv[2], (int)size * nmemb);
784
785 ch->in_callback = true;
786 zend_call_known_fcc(&read_handler->fcc, &retval, /* param_count */ 3, argv, /* named_params */ NULL);
787 ch->in_callback = false;
788 if (!Z_ISUNDEF(retval)) {
789 _php_curl_verify_handlers(ch, /* reporterror */ true);
790 if (Z_TYPE(retval) == IS_STRING) {
791 length = MIN((int) (size * nmemb), Z_STRLEN(retval));
792 memcpy(data, Z_STRVAL(retval), length);
793 } else if (Z_TYPE(retval) == IS_LONG) {
794 length = Z_LVAL_P(&retval);
795 }
796 // TODO Do type error if invalid type?
797 zval_ptr_dtor(&retval);
798 }
799
800 zval_ptr_dtor(&argv[0]);
801 zval_ptr_dtor(&argv[1]);
802 break;
803 }
804 }
805
806 return length;
807 }
808 /* }}} */
809
810 /* {{{ curl_write_header */
curl_write_header(char * data,size_t size,size_t nmemb,void * ctx)811 static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx)
812 {
813 php_curl *ch = (php_curl *) ctx;
814 php_curl_write *write_handler = ch->handlers.write_header;
815 size_t length = size * nmemb;
816
817 switch (write_handler->method) {
818 case PHP_CURL_STDOUT:
819 /* Handle special case write when we're returning the entire transfer
820 */
821 if (ch->handlers.write->method == PHP_CURL_RETURN && length > 0) {
822 smart_str_appendl(&ch->handlers.write->buf, data, (int) length);
823 } else {
824 PHPWRITE(data, length);
825 }
826 break;
827 case PHP_CURL_FILE:
828 return fwrite(data, size, nmemb, write_handler->fp);
829 case PHP_CURL_USER: {
830 zval argv[2];
831 zval retval;
832
833 GC_ADDREF(&ch->std);
834 ZVAL_OBJ(&argv[0], &ch->std);
835 ZVAL_STRINGL(&argv[1], data, length);
836
837 ch->in_callback = true;
838 zend_call_known_fcc(&write_handler->fcc, &retval, /* param_count */ 2, argv, /* named_params */ NULL);
839 ch->in_callback = false;
840 if (!Z_ISUNDEF(retval)) {
841 // TODO: Check for valid int type for return value
842 _php_curl_verify_handlers(ch, /* reporterror */ true);
843 length = zval_get_long(&retval);
844 }
845 zval_ptr_dtor(&argv[0]);
846 zval_ptr_dtor(&argv[1]);
847 break;
848 }
849
850 case PHP_CURL_IGNORE:
851 return length;
852
853 default:
854 return -1;
855 }
856
857 return length;
858 }
859 /* }}} */
860
curl_debug(CURL * cp,curl_infotype type,char * buf,size_t buf_len,void * ctx)861 static int curl_debug(CURL *cp, curl_infotype type, char *buf, size_t buf_len, void *ctx) /* {{{ */
862 {
863 php_curl *ch = (php_curl *)ctx;
864
865 if (type == CURLINFO_HEADER_OUT) {
866 if (ch->header.str) {
867 zend_string_release_ex(ch->header.str, 0);
868 }
869 ch->header.str = zend_string_init(buf, buf_len, 0);
870 }
871
872 return 0;
873 }
874 /* }}} */
875
876 /* {{{ curl_free_post */
curl_free_post(void ** post)877 static void curl_free_post(void **post)
878 {
879 curl_mime_free((curl_mime *)*post);
880 }
881 /* }}} */
882
883 struct mime_data_cb_arg {
884 zend_string *filename;
885 php_stream *stream;
886 };
887
888 /* {{{ curl_free_cb_arg */
curl_free_cb_arg(void ** cb_arg_p)889 static void curl_free_cb_arg(void **cb_arg_p)
890 {
891 struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) *cb_arg_p;
892
893 ZEND_ASSERT(cb_arg->stream == NULL);
894 zend_string_release(cb_arg->filename);
895 efree(cb_arg);
896 }
897 /* }}} */
898
899 /* {{{ curl_free_slist */
curl_free_slist(zval * el)900 static void curl_free_slist(zval *el)
901 {
902 curl_slist_free_all(((struct curl_slist *)Z_PTR_P(el)));
903 }
904 /* }}} */
905
906 /* {{{ Return cURL version information. */
PHP_FUNCTION(curl_version)907 PHP_FUNCTION(curl_version)
908 {
909 curl_version_info_data *d;
910
911 ZEND_PARSE_PARAMETERS_NONE();
912
913 d = curl_version_info(CURLVERSION_NOW);
914 if (d == NULL) {
915 RETURN_FALSE;
916 }
917
918 array_init(return_value);
919
920 CAAL("version_number", d->version_num);
921 CAAL("age", d->age);
922 CAAL("features", d->features);
923 /* Add an array of features */
924 {
925 struct feat {
926 const char *name;
927 int bitmask;
928 };
929
930 unsigned int i;
931 zval feature_list;
932 array_init(&feature_list);
933
934 /* Sync this list with PHP_MINFO_FUNCTION(curl) as well */
935 static const struct feat feats[] = {
936 {"AsynchDNS", CURL_VERSION_ASYNCHDNS},
937 {"CharConv", CURL_VERSION_CONV},
938 {"Debug", CURL_VERSION_DEBUG},
939 {"GSS-Negotiate", CURL_VERSION_GSSNEGOTIATE},
940 {"IDN", CURL_VERSION_IDN},
941 {"IPv6", CURL_VERSION_IPV6},
942 {"krb4", CURL_VERSION_KERBEROS4},
943 {"Largefile", CURL_VERSION_LARGEFILE},
944 {"libz", CURL_VERSION_LIBZ},
945 {"NTLM", CURL_VERSION_NTLM},
946 {"NTLMWB", CURL_VERSION_NTLM_WB},
947 {"SPNEGO", CURL_VERSION_SPNEGO},
948 {"SSL", CURL_VERSION_SSL},
949 {"SSPI", CURL_VERSION_SSPI},
950 {"TLS-SRP", CURL_VERSION_TLSAUTH_SRP},
951 {"HTTP2", CURL_VERSION_HTTP2},
952 {"GSSAPI", CURL_VERSION_GSSAPI},
953 {"KERBEROS5", CURL_VERSION_KERBEROS5},
954 {"UNIX_SOCKETS", CURL_VERSION_UNIX_SOCKETS},
955 {"PSL", CURL_VERSION_PSL},
956 {"HTTPS_PROXY", CURL_VERSION_HTTPS_PROXY},
957 {"MULTI_SSL", CURL_VERSION_MULTI_SSL},
958 {"BROTLI", CURL_VERSION_BROTLI},
959 #if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */
960 {"ALTSVC", CURL_VERSION_ALTSVC},
961 #endif
962 #if LIBCURL_VERSION_NUM >= 0x074200 /* Available since 7.66.0 */
963 {"HTTP3", CURL_VERSION_HTTP3},
964 #endif
965 #if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */
966 {"UNICODE", CURL_VERSION_UNICODE},
967 {"ZSTD", CURL_VERSION_ZSTD},
968 #endif
969 #if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */
970 {"HSTS", CURL_VERSION_HSTS},
971 #endif
972 #if LIBCURL_VERSION_NUM >= 0x074c00 /* Available since 7.76.0 */
973 {"GSASL", CURL_VERSION_GSASL},
974 #endif
975 };
976
977 for(i = 0; i < sizeof(feats) / sizeof(feats[0]); i++) {
978 if (feats[i].name) {
979 add_assoc_bool(&feature_list, feats[i].name, d->features & feats[i].bitmask ? true : false);
980 }
981 }
982
983 CAAZ("feature_list", &feature_list);
984 }
985 CAAL("ssl_version_number", d->ssl_version_num);
986 CAAS("version", d->version);
987 CAAS("host", d->host);
988 CAAS("ssl_version", d->ssl_version);
989 CAAS("libz_version", d->libz_version);
990 /* Add an array of protocols */
991 {
992 char **p = (char **) d->protocols;
993 zval protocol_list;
994
995 array_init(&protocol_list);
996
997 while (*p != NULL) {
998 add_next_index_string(&protocol_list, *p);
999 p++;
1000 }
1001 CAAZ("protocols", &protocol_list);
1002 }
1003 if (d->age >= 1) {
1004 CAAS("ares", d->ares);
1005 CAAL("ares_num", d->ares_num);
1006 }
1007 if (d->age >= 2) {
1008 CAAS("libidn", d->libidn);
1009 }
1010 if (d->age >= 3) {
1011 CAAL("iconv_ver_num", d->iconv_ver_num);
1012 CAAS("libssh_version", d->libssh_version);
1013 }
1014 if (d->age >= 4) {
1015 CAAL("brotli_ver_num", d->brotli_ver_num);
1016 CAAS("brotli_version", d->brotli_version);
1017 }
1018 }
1019 /* }}} */
1020
init_curl_handle_into_zval(zval * curl)1021 php_curl *init_curl_handle_into_zval(zval *curl)
1022 {
1023 php_curl *ch;
1024
1025 object_init_ex(curl, curl_ce);
1026 ch = Z_CURL_P(curl);
1027
1028 init_curl_handle(ch);
1029
1030 return ch;
1031 }
1032
init_curl_handle(php_curl * ch)1033 void init_curl_handle(php_curl *ch)
1034 {
1035 ch->to_free = ecalloc(1, sizeof(struct _php_curl_free));
1036 ch->handlers.write = ecalloc(1, sizeof(php_curl_write));
1037 ch->handlers.write_header = ecalloc(1, sizeof(php_curl_write));
1038 ch->handlers.read = ecalloc(1, sizeof(php_curl_read));
1039 ch->handlers.progress = empty_fcall_info_cache;
1040 ch->handlers.xferinfo = empty_fcall_info_cache;
1041 ch->handlers.fnmatch = empty_fcall_info_cache;
1042 #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
1043 ch->handlers.sshhostkey = empty_fcall_info_cache;
1044 #endif
1045 ch->clone = emalloc(sizeof(uint32_t));
1046 *ch->clone = 1;
1047
1048 memset(&ch->err, 0, sizeof(struct _php_curl_error));
1049
1050 zend_llist_init(&ch->to_free->post, sizeof(struct HttpPost *), (llist_dtor_func_t)curl_free_post, 0);
1051 zend_llist_init(&ch->to_free->stream, sizeof(struct mime_data_cb_arg *), (llist_dtor_func_t)curl_free_cb_arg, 0);
1052
1053 ch->to_free->slist = emalloc(sizeof(HashTable));
1054 zend_hash_init(ch->to_free->slist, 4, NULL, curl_free_slist, 0);
1055 ZVAL_UNDEF(&ch->postfields);
1056 }
1057
1058 /* }}} */
1059
1060 /* {{{ create_certinfo */
create_certinfo(struct curl_certinfo * ci,zval * listcode)1061 static void create_certinfo(struct curl_certinfo *ci, zval *listcode)
1062 {
1063 int i;
1064
1065 if (ci) {
1066 zval certhash;
1067
1068 for (i=0; i<ci->num_of_certs; i++) {
1069 struct curl_slist *slist;
1070
1071 array_init(&certhash);
1072 for (slist = ci->certinfo[i]; slist; slist = slist->next) {
1073 int len;
1074 char s[64];
1075 char *tmp;
1076 strncpy(s, slist->data, sizeof(s));
1077 s[sizeof(s)-1] = '\0';
1078 tmp = memchr(s, ':', sizeof(s));
1079 if(tmp) {
1080 *tmp = '\0';
1081 len = strlen(s);
1082 add_assoc_string(&certhash, s, &slist->data[len+1]);
1083 } else {
1084 php_error_docref(NULL, E_WARNING, "Could not extract hash key from certificate info");
1085 }
1086 }
1087 add_next_index_zval(listcode, &certhash);
1088 }
1089 }
1090 }
1091 /* }}} */
1092
1093 /* {{{ _php_curl_set_default_options()
1094 Set default options for a handle */
_php_curl_set_default_options(php_curl * ch)1095 static void _php_curl_set_default_options(php_curl *ch)
1096 {
1097 char *cainfo;
1098
1099 curl_easy_setopt(ch->cp, CURLOPT_NOPROGRESS, 1);
1100 curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0);
1101 curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str);
1102 curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write);
1103 curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch);
1104 curl_easy_setopt(ch->cp, CURLOPT_READFUNCTION, curl_read);
1105 curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch);
1106 curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_header);
1107 curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch);
1108 #ifndef ZTS
1109 curl_easy_setopt(ch->cp, CURLOPT_DNS_USE_GLOBAL_CACHE, 1);
1110 #endif
1111 curl_easy_setopt(ch->cp, CURLOPT_DNS_CACHE_TIMEOUT, 120);
1112 curl_easy_setopt(ch->cp, CURLOPT_MAXREDIRS, 20); /* prevent infinite redirects */
1113
1114 cainfo = INI_STR("openssl.cafile");
1115 if (!(cainfo && cainfo[0] != '\0')) {
1116 cainfo = INI_STR("curl.cainfo");
1117 }
1118 if (cainfo && cainfo[0] != '\0') {
1119 curl_easy_setopt(ch->cp, CURLOPT_CAINFO, cainfo);
1120 }
1121
1122 #ifdef ZTS
1123 curl_easy_setopt(ch->cp, CURLOPT_NOSIGNAL, 1);
1124 #endif
1125 }
1126 /* }}} */
1127
1128 /* {{{ Initialize a cURL session */
PHP_FUNCTION(curl_init)1129 PHP_FUNCTION(curl_init)
1130 {
1131 php_curl *ch;
1132 CURL *cp;
1133 zend_string *url = NULL;
1134
1135 ZEND_PARSE_PARAMETERS_START(0,1)
1136 Z_PARAM_OPTIONAL
1137 Z_PARAM_STR_OR_NULL(url)
1138 ZEND_PARSE_PARAMETERS_END();
1139
1140 cp = curl_easy_init();
1141 if (!cp) {
1142 php_error_docref(NULL, E_WARNING, "Could not initialize a new cURL handle");
1143 RETURN_FALSE;
1144 }
1145
1146 ch = init_curl_handle_into_zval(return_value);
1147
1148 ch->cp = cp;
1149
1150 ch->handlers.write->method = PHP_CURL_STDOUT;
1151 ch->handlers.read->method = PHP_CURL_DIRECT;
1152 ch->handlers.write_header->method = PHP_CURL_IGNORE;
1153
1154 _php_curl_set_default_options(ch);
1155
1156 if (url) {
1157 if (php_curl_option_url(ch, url) == FAILURE) {
1158 zval_ptr_dtor(return_value);
1159 RETURN_FALSE;
1160 }
1161 }
1162 }
1163 /* }}} */
1164
php_curl_copy_fcc_with_option(php_curl * ch,CURLoption option,zend_fcall_info_cache * target_fcc,zend_fcall_info_cache * source_fcc)1165 static void php_curl_copy_fcc_with_option(php_curl *ch, CURLoption option, zend_fcall_info_cache *target_fcc, zend_fcall_info_cache *source_fcc)
1166 {
1167 if (ZEND_FCC_INITIALIZED(*source_fcc)) {
1168 zend_fcc_dup(target_fcc, source_fcc);
1169 curl_easy_setopt(ch->cp, option, (void *) ch);
1170 }
1171 }
1172
_php_setup_easy_copy_handlers(php_curl * ch,php_curl * source)1173 void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source)
1174 {
1175 if (!Z_ISUNDEF(source->handlers.write->stream)) {
1176 Z_ADDREF(source->handlers.write->stream);
1177 }
1178 ch->handlers.write->stream = source->handlers.write->stream;
1179 ch->handlers.write->method = source->handlers.write->method;
1180 if (!Z_ISUNDEF(source->handlers.read->stream)) {
1181 Z_ADDREF(source->handlers.read->stream);
1182 }
1183 ch->handlers.read->stream = source->handlers.read->stream;
1184 ch->handlers.read->method = source->handlers.read->method;
1185 ch->handlers.write_header->method = source->handlers.write_header->method;
1186 if (!Z_ISUNDEF(source->handlers.write_header->stream)) {
1187 Z_ADDREF(source->handlers.write_header->stream);
1188 }
1189 ch->handlers.write_header->stream = source->handlers.write_header->stream;
1190
1191 ch->handlers.write->fp = source->handlers.write->fp;
1192 ch->handlers.write_header->fp = source->handlers.write_header->fp;
1193 ch->handlers.read->fp = source->handlers.read->fp;
1194 ch->handlers.read->res = source->handlers.read->res;
1195
1196 if (ZEND_FCC_INITIALIZED(source->handlers.read->fcc)) {
1197 zend_fcc_dup(&source->handlers.read->fcc, &source->handlers.read->fcc);
1198 }
1199 if (ZEND_FCC_INITIALIZED(source->handlers.write->fcc)) {
1200 zend_fcc_dup(&source->handlers.write->fcc, &source->handlers.write->fcc);
1201 }
1202 if (ZEND_FCC_INITIALIZED(source->handlers.write_header->fcc)) {
1203 zend_fcc_dup(&source->handlers.write_header->fcc, &source->handlers.write_header->fcc);
1204 }
1205
1206 curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str);
1207 curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch);
1208 curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch);
1209 curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch);
1210 curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *) ch);
1211
1212 php_curl_copy_fcc_with_option(ch, CURLOPT_PROGRESSDATA, &ch->handlers.progress, &source->handlers.progress);
1213 php_curl_copy_fcc_with_option(ch, CURLOPT_XFERINFODATA, &ch->handlers.xferinfo, &source->handlers.xferinfo);
1214 php_curl_copy_fcc_with_option(ch, CURLOPT_FNMATCH_DATA, &ch->handlers.fnmatch, &source->handlers.fnmatch);
1215 #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
1216 php_curl_copy_fcc_with_option(ch, CURLOPT_SSH_HOSTKEYDATA, &ch->handlers.sshhostkey, &source->handlers.sshhostkey);
1217 #endif
1218
1219 ZVAL_COPY(&ch->private_data, &source->private_data);
1220
1221 efree(ch->to_free->slist);
1222 efree(ch->to_free);
1223 ch->to_free = source->to_free;
1224 efree(ch->clone);
1225 ch->clone = source->clone;
1226
1227 /* Keep track of cloned copies to avoid invoking curl destructors for every clone */
1228 (*source->clone)++;
1229 }
1230
read_cb(char * buffer,size_t size,size_t nitems,void * arg)1231 static size_t read_cb(char *buffer, size_t size, size_t nitems, void *arg) /* {{{ */
1232 {
1233 struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg;
1234 ssize_t numread;
1235
1236 if (cb_arg->stream == NULL) {
1237 if (!(cb_arg->stream = php_stream_open_wrapper(ZSTR_VAL(cb_arg->filename), "rb", IGNORE_PATH, NULL))) {
1238 return CURL_READFUNC_ABORT;
1239 }
1240 }
1241 numread = php_stream_read(cb_arg->stream, buffer, nitems * size);
1242 if (numread < 0) {
1243 php_stream_close(cb_arg->stream);
1244 cb_arg->stream = NULL;
1245 return CURL_READFUNC_ABORT;
1246 }
1247 return numread;
1248 }
1249 /* }}} */
1250
seek_cb(void * arg,curl_off_t offset,int origin)1251 static int seek_cb(void *arg, curl_off_t offset, int origin) /* {{{ */
1252 {
1253 struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg;
1254 int res;
1255
1256 if (cb_arg->stream == NULL) {
1257 return CURL_SEEKFUNC_CANTSEEK;
1258 }
1259 res = php_stream_seek(cb_arg->stream, offset, origin);
1260 return res == SUCCESS ? CURL_SEEKFUNC_OK : CURL_SEEKFUNC_CANTSEEK;
1261 }
1262 /* }}} */
1263
free_cb(void * arg)1264 static void free_cb(void *arg) /* {{{ */
1265 {
1266 struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg;
1267
1268 if (cb_arg->stream != NULL) {
1269 php_stream_close(cb_arg->stream);
1270 cb_arg->stream = NULL;
1271 }
1272 }
1273 /* }}} */
1274
add_simple_field(curl_mime * mime,zend_string * string_key,zval * current)1275 static inline CURLcode add_simple_field(curl_mime *mime, zend_string *string_key, zval *current)
1276 {
1277 CURLcode error = CURLE_OK;
1278 curl_mimepart *part;
1279 CURLcode form_error;
1280 zend_string *postval, *tmp_postval;
1281
1282 postval = zval_get_tmp_string(current, &tmp_postval);
1283
1284 part = curl_mime_addpart(mime);
1285 if (part == NULL) {
1286 zend_tmp_string_release(tmp_postval);
1287 zend_string_release_ex(string_key, 0);
1288 return CURLE_OUT_OF_MEMORY;
1289 }
1290 if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK
1291 || (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK) {
1292 error = form_error;
1293 }
1294
1295 zend_tmp_string_release(tmp_postval);
1296
1297 return error;
1298 }
1299
build_mime_structure_from_hash(php_curl * ch,zval * zpostfields)1300 static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields) /* {{{ */
1301 {
1302 HashTable *postfields = Z_ARRVAL_P(zpostfields);
1303 CURLcode error = CURLE_OK;
1304 zval *current;
1305 zend_string *string_key;
1306 zend_ulong num_key;
1307 curl_mime *mime = NULL;
1308 curl_mimepart *part;
1309 CURLcode form_error;
1310
1311 if (zend_hash_num_elements(postfields) > 0) {
1312 mime = curl_mime_init(ch->cp);
1313 if (mime == NULL) {
1314 return FAILURE;
1315 }
1316 }
1317
1318 ZEND_HASH_FOREACH_KEY_VAL(postfields, num_key, string_key, current) {
1319 zend_string *postval;
1320 /* Pretend we have a string_key here */
1321 if (!string_key) {
1322 string_key = zend_long_to_str(num_key);
1323 } else {
1324 zend_string_addref(string_key);
1325 }
1326
1327 ZVAL_DEREF(current);
1328 if (Z_TYPE_P(current) == IS_OBJECT &&
1329 instanceof_function(Z_OBJCE_P(current), curl_CURLFile_class)) {
1330 /* new-style file upload */
1331 zval *prop, rv;
1332 char *type = NULL, *filename = NULL;
1333 struct mime_data_cb_arg *cb_arg;
1334 php_stream *stream;
1335 php_stream_statbuf ssb;
1336 size_t filesize = -1;
1337 curl_seek_callback seekfunc = seek_cb;
1338
1339 prop = zend_read_property_ex(curl_CURLFile_class, Z_OBJ_P(current), ZSTR_KNOWN(ZEND_STR_NAME), /* silent */ false, &rv);
1340 ZVAL_DEREF(prop);
1341 if (Z_TYPE_P(prop) != IS_STRING) {
1342 php_error_docref(NULL, E_WARNING, "Invalid filename for key %s", ZSTR_VAL(string_key));
1343 } else {
1344 postval = Z_STR_P(prop);
1345
1346 if (php_check_open_basedir(ZSTR_VAL(postval))) {
1347 return FAILURE;
1348 }
1349
1350 prop = zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), "mime", sizeof("mime")-1, 0, &rv);
1351 ZVAL_DEREF(prop);
1352 if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) {
1353 type = Z_STRVAL_P(prop);
1354 }
1355 prop = zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), "postname", sizeof("postname")-1, 0, &rv);
1356 ZVAL_DEREF(prop);
1357 if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) {
1358 filename = Z_STRVAL_P(prop);
1359 }
1360
1361 zval_ptr_dtor(&ch->postfields);
1362 ZVAL_COPY(&ch->postfields, zpostfields);
1363
1364 if ((stream = php_stream_open_wrapper(ZSTR_VAL(postval), "rb", STREAM_MUST_SEEK, NULL))) {
1365 if (!stream->readfilters.head && !php_stream_stat(stream, &ssb)) {
1366 filesize = ssb.sb.st_size;
1367 }
1368 } else {
1369 seekfunc = NULL;
1370 }
1371
1372 cb_arg = emalloc(sizeof *cb_arg);
1373 cb_arg->filename = zend_string_copy(postval);
1374 cb_arg->stream = stream;
1375
1376 part = curl_mime_addpart(mime);
1377 if (part == NULL) {
1378 zend_string_release_ex(string_key, 0);
1379 return FAILURE;
1380 }
1381 if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK
1382 || (form_error = curl_mime_data_cb(part, filesize, read_cb, seekfunc, free_cb, cb_arg)) != CURLE_OK
1383 || (form_error = curl_mime_filename(part, filename ? filename : ZSTR_VAL(postval))) != CURLE_OK
1384 || (form_error = curl_mime_type(part, type ? type : "application/octet-stream")) != CURLE_OK) {
1385 error = form_error;
1386 }
1387 zend_llist_add_element(&ch->to_free->stream, &cb_arg);
1388 }
1389
1390 zend_string_release_ex(string_key, 0);
1391 continue;
1392 }
1393
1394 if (Z_TYPE_P(current) == IS_OBJECT && instanceof_function(Z_OBJCE_P(current), curl_CURLStringFile_class)) {
1395 /* new-style file upload from string */
1396 zval *prop, rv;
1397 char *type = NULL, *filename = NULL;
1398
1399 prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "postname", sizeof("postname")-1, 0, &rv);
1400 if (EG(exception)) {
1401 zend_string_release_ex(string_key, 0);
1402 return FAILURE;
1403 }
1404 ZVAL_DEREF(prop);
1405 ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);
1406
1407 filename = Z_STRVAL_P(prop);
1408
1409 prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "mime", sizeof("mime")-1, 0, &rv);
1410 if (EG(exception)) {
1411 zend_string_release_ex(string_key, 0);
1412 return FAILURE;
1413 }
1414 ZVAL_DEREF(prop);
1415 ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);
1416
1417 type = Z_STRVAL_P(prop);
1418
1419 prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "data", sizeof("data")-1, 0, &rv);
1420 if (EG(exception)) {
1421 zend_string_release_ex(string_key, 0);
1422 return FAILURE;
1423 }
1424 ZVAL_DEREF(prop);
1425 ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);
1426
1427 postval = Z_STR_P(prop);
1428
1429 zval_ptr_dtor(&ch->postfields);
1430 ZVAL_COPY(&ch->postfields, zpostfields);
1431
1432 part = curl_mime_addpart(mime);
1433 if (part == NULL) {
1434 zend_string_release_ex(string_key, 0);
1435 return FAILURE;
1436 }
1437 if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK
1438 || (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK
1439 || (form_error = curl_mime_filename(part, filename)) != CURLE_OK
1440 || (form_error = curl_mime_type(part, type)) != CURLE_OK) {
1441 error = form_error;
1442 }
1443
1444 zend_string_release_ex(string_key, 0);
1445 continue;
1446 }
1447
1448 if (Z_TYPE_P(current) == IS_ARRAY) {
1449 zval *current_element;
1450
1451 ZEND_HASH_FOREACH_VAL(HASH_OF(current), current_element) {
1452 add_simple_field(mime, string_key, current_element);
1453 } ZEND_HASH_FOREACH_END();
1454
1455 zend_string_release_ex(string_key, 0);
1456 continue;
1457 }
1458
1459 add_simple_field(mime, string_key, current);
1460
1461 zend_string_release_ex(string_key, 0);
1462 } ZEND_HASH_FOREACH_END();
1463
1464 SAVE_CURL_ERROR(ch, error);
1465 if (error != CURLE_OK) {
1466 return FAILURE;
1467 }
1468
1469 if ((*ch->clone) == 1) {
1470 zend_llist_clean(&ch->to_free->post);
1471 }
1472 zend_llist_add_element(&ch->to_free->post, &mime);
1473 error = curl_easy_setopt(ch->cp, CURLOPT_MIMEPOST, mime);
1474
1475 SAVE_CURL_ERROR(ch, error);
1476 return error == CURLE_OK ? SUCCESS : FAILURE;
1477 }
1478 /* }}} */
1479
1480 /* {{{ Copy a cURL handle along with all of it's preferences */
PHP_FUNCTION(curl_copy_handle)1481 PHP_FUNCTION(curl_copy_handle)
1482 {
1483 php_curl *ch;
1484 CURL *cp;
1485 zval *zid;
1486 php_curl *dupch;
1487 zval *postfields;
1488
1489 ZEND_PARSE_PARAMETERS_START(1,1)
1490 Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
1491 ZEND_PARSE_PARAMETERS_END();
1492
1493 ch = Z_CURL_P(zid);
1494
1495 cp = curl_easy_duphandle(ch->cp);
1496 if (!cp) {
1497 php_error_docref(NULL, E_WARNING, "Cannot duplicate cURL handle");
1498 RETURN_FALSE;
1499 }
1500
1501 dupch = init_curl_handle_into_zval(return_value);
1502 dupch->cp = cp;
1503
1504 _php_setup_easy_copy_handlers(dupch, ch);
1505
1506 postfields = &ch->postfields;
1507 if (Z_TYPE_P(postfields) != IS_UNDEF) {
1508 if (build_mime_structure_from_hash(dupch, postfields) == FAILURE) {
1509 zval_ptr_dtor(return_value);
1510 php_error_docref(NULL, E_WARNING, "Cannot rebuild mime structure");
1511 RETURN_FALSE;
1512 }
1513 }
1514 }
1515 /* }}} */
1516
php_curl_set_callable_handler(zend_fcall_info_cache * const handler_fcc,zval * callable,bool is_array_config,const char * option_name)1517 static bool php_curl_set_callable_handler(zend_fcall_info_cache *const handler_fcc, zval *callable, bool is_array_config, const char *option_name)
1518 {
1519 if (ZEND_FCC_INITIALIZED(*handler_fcc)) {
1520 zend_fcc_dtor(handler_fcc);
1521 }
1522
1523 if (Z_TYPE_P(callable) == IS_NULL) {
1524 return true;
1525 }
1526
1527 char *error = NULL;
1528 if (UNEXPECTED(!zend_is_callable_ex(callable, /* object */ NULL, /* check_flags */ 0, /* callable_name */ NULL, handler_fcc, /* error */ &error))) {
1529 if (!EG(exception)) {
1530 zend_argument_type_error(2 + !is_array_config, "must be a valid callback for option %s, %s", option_name, error);
1531 }
1532 efree(error);
1533 return false;
1534 }
1535 zend_fcc_addref(handler_fcc);
1536 return true;
1537 }
1538
1539
1540 #define HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(curl_ptr, constant_no_function, handler_type) \
1541 case constant_no_function##FUNCTION: { \
1542 bool result = php_curl_set_callable_handler(&curl_ptr->handlers.handler_type->fcc, zvalue, is_array_config, #constant_no_function "FUNCTION"); \
1543 if (!result) { \
1544 return FAILURE; \
1545 } \
1546 curl_ptr->handlers.handler_type->method = PHP_CURL_USER; \
1547 break; \
1548 }
1549
1550 #define HANDLE_CURL_OPTION_CALLABLE(curl_ptr, constant_no_function, handler_fcc, c_callback) \
1551 case constant_no_function##FUNCTION: { \
1552 bool result = php_curl_set_callable_handler(&curl_ptr->handler_fcc, zvalue, is_array_config, #constant_no_function "FUNCTION"); \
1553 if (!result) { \
1554 return FAILURE; \
1555 } \
1556 curl_easy_setopt(curl_ptr->cp, constant_no_function##FUNCTION, (c_callback)); \
1557 curl_easy_setopt(curl_ptr->cp, constant_no_function##DATA, curl_ptr); \
1558 break; \
1559 }
1560
_php_curl_setopt(php_curl * ch,zend_long option,zval * zvalue,bool is_array_config)1561 static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool is_array_config) /* {{{ */
1562 {
1563 CURLcode error = CURLE_OK;
1564 zend_long lval;
1565
1566 switch (option) {
1567 /* Callable options */
1568 HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_WRITE, write);
1569 HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_HEADER, write_header);
1570 HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_READ, read);
1571
1572 HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PROGRESS, handlers.progress, curl_progress);
1573 HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_XFERINFO, handlers.xferinfo, curl_xferinfo);
1574 HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_FNMATCH_, handlers.fnmatch, curl_fnmatch);
1575 #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
1576 HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_SSH_HOSTKEY, handlers.sshhostkey, curl_ssh_hostkeyfunction);
1577 #endif
1578
1579 /* Long options */
1580 case CURLOPT_SSL_VERIFYHOST:
1581 lval = zval_get_long(zvalue);
1582 if (lval == 1) {
1583 php_error_docref(NULL, E_NOTICE, "CURLOPT_SSL_VERIFYHOST no longer accepts the value 1, value 2 will be used instead");
1584 error = curl_easy_setopt(ch->cp, option, 2);
1585 break;
1586 }
1587 ZEND_FALLTHROUGH;
1588 case CURLOPT_AUTOREFERER:
1589 case CURLOPT_BUFFERSIZE:
1590 case CURLOPT_CONNECTTIMEOUT:
1591 case CURLOPT_COOKIESESSION:
1592 case CURLOPT_CRLF:
1593 case CURLOPT_DNS_CACHE_TIMEOUT:
1594 case CURLOPT_DNS_USE_GLOBAL_CACHE:
1595 case CURLOPT_FAILONERROR:
1596 case CURLOPT_FILETIME:
1597 case CURLOPT_FORBID_REUSE:
1598 case CURLOPT_FRESH_CONNECT:
1599 case CURLOPT_FTP_USE_EPRT:
1600 case CURLOPT_FTP_USE_EPSV:
1601 case CURLOPT_HEADER:
1602 case CURLOPT_HTTPGET:
1603 case CURLOPT_HTTPPROXYTUNNEL:
1604 case CURLOPT_HTTP_VERSION:
1605 case CURLOPT_INFILESIZE:
1606 case CURLOPT_LOW_SPEED_LIMIT:
1607 case CURLOPT_LOW_SPEED_TIME:
1608 case CURLOPT_MAXCONNECTS:
1609 case CURLOPT_MAXREDIRS:
1610 case CURLOPT_NETRC:
1611 case CURLOPT_NOBODY:
1612 case CURLOPT_NOPROGRESS:
1613 case CURLOPT_NOSIGNAL:
1614 case CURLOPT_PORT:
1615 case CURLOPT_POST:
1616 case CURLOPT_PROXYPORT:
1617 case CURLOPT_PROXYTYPE:
1618 case CURLOPT_PUT:
1619 case CURLOPT_RESUME_FROM:
1620 case CURLOPT_SSLVERSION:
1621 case CURLOPT_SSL_VERIFYPEER:
1622 case CURLOPT_TIMECONDITION:
1623 case CURLOPT_TIMEOUT:
1624 case CURLOPT_TIMEVALUE:
1625 case CURLOPT_TRANSFERTEXT:
1626 case CURLOPT_UNRESTRICTED_AUTH:
1627 case CURLOPT_UPLOAD:
1628 case CURLOPT_VERBOSE:
1629 case CURLOPT_HTTPAUTH:
1630 case CURLOPT_FTP_CREATE_MISSING_DIRS:
1631 case CURLOPT_PROXYAUTH:
1632 case CURLOPT_FTP_RESPONSE_TIMEOUT:
1633 case CURLOPT_IPRESOLVE:
1634 case CURLOPT_MAXFILESIZE:
1635 case CURLOPT_TCP_NODELAY:
1636 case CURLOPT_FTPSSLAUTH:
1637 case CURLOPT_IGNORE_CONTENT_LENGTH:
1638 case CURLOPT_FTP_SKIP_PASV_IP:
1639 case CURLOPT_FTP_FILEMETHOD:
1640 case CURLOPT_CONNECT_ONLY:
1641 case CURLOPT_LOCALPORT:
1642 case CURLOPT_LOCALPORTRANGE:
1643 case CURLOPT_SSL_SESSIONID_CACHE:
1644 case CURLOPT_FTP_SSL_CCC:
1645 case CURLOPT_SSH_AUTH_TYPES:
1646 case CURLOPT_CONNECTTIMEOUT_MS:
1647 case CURLOPT_HTTP_CONTENT_DECODING:
1648 case CURLOPT_HTTP_TRANSFER_DECODING:
1649 case CURLOPT_TIMEOUT_MS:
1650 case CURLOPT_NEW_DIRECTORY_PERMS:
1651 case CURLOPT_NEW_FILE_PERMS:
1652 case CURLOPT_USE_SSL:
1653 case CURLOPT_APPEND:
1654 case CURLOPT_DIRLISTONLY:
1655 case CURLOPT_PROXY_TRANSFER_MODE:
1656 case CURLOPT_ADDRESS_SCOPE:
1657 case CURLOPT_CERTINFO:
1658 case CURLOPT_PROTOCOLS:
1659 case CURLOPT_REDIR_PROTOCOLS:
1660 case CURLOPT_SOCKS5_GSSAPI_NEC:
1661 case CURLOPT_TFTP_BLKSIZE:
1662 case CURLOPT_FTP_USE_PRET:
1663 case CURLOPT_RTSP_CLIENT_CSEQ:
1664 case CURLOPT_RTSP_REQUEST:
1665 case CURLOPT_RTSP_SERVER_CSEQ:
1666 case CURLOPT_WILDCARDMATCH:
1667 case CURLOPT_GSSAPI_DELEGATION:
1668 case CURLOPT_ACCEPTTIMEOUT_MS:
1669 case CURLOPT_SSL_OPTIONS:
1670 case CURLOPT_TCP_KEEPALIVE:
1671 case CURLOPT_TCP_KEEPIDLE:
1672 case CURLOPT_TCP_KEEPINTVL:
1673 case CURLOPT_SASL_IR:
1674 case CURLOPT_EXPECT_100_TIMEOUT_MS:
1675 case CURLOPT_SSL_ENABLE_ALPN:
1676 case CURLOPT_SSL_ENABLE_NPN:
1677 case CURLOPT_HEADEROPT:
1678 case CURLOPT_SSL_VERIFYSTATUS:
1679 case CURLOPT_PATH_AS_IS:
1680 case CURLOPT_SSL_FALSESTART:
1681 case CURLOPT_PIPEWAIT:
1682 case CURLOPT_STREAM_WEIGHT:
1683 case CURLOPT_TFTP_NO_OPTIONS:
1684 case CURLOPT_TCP_FASTOPEN:
1685 case CURLOPT_KEEP_SENDING_ON_ERROR:
1686 case CURLOPT_PROXY_SSL_OPTIONS:
1687 case CURLOPT_PROXY_SSL_VERIFYHOST:
1688 case CURLOPT_PROXY_SSL_VERIFYPEER:
1689 case CURLOPT_PROXY_SSLVERSION:
1690 case CURLOPT_SUPPRESS_CONNECT_HEADERS:
1691 case CURLOPT_SOCKS5_AUTH:
1692 case CURLOPT_SSH_COMPRESSION:
1693 case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS:
1694 case CURLOPT_DNS_SHUFFLE_ADDRESSES:
1695 case CURLOPT_HAPROXYPROTOCOL:
1696 case CURLOPT_DISALLOW_USERNAME_IN_URL:
1697 #if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */
1698 case CURLOPT_UPKEEP_INTERVAL_MS:
1699 case CURLOPT_UPLOAD_BUFFERSIZE:
1700 #endif
1701 #if LIBCURL_VERSION_NUM >= 0x074000 /* Available since 7.64.0 */
1702 case CURLOPT_HTTP09_ALLOWED:
1703 #endif
1704 #if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */
1705 case CURLOPT_ALTSVC_CTRL:
1706 #endif
1707 #if LIBCURL_VERSION_NUM >= 0x074100 /* Available since 7.65.0 */
1708 case CURLOPT_MAXAGE_CONN:
1709 #endif
1710 #if LIBCURL_VERSION_NUM >= 0x074500 /* Available since 7.69.0 */
1711 case CURLOPT_MAIL_RCPT_ALLLOWFAILS:
1712 #endif
1713 #if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */
1714 case CURLOPT_HSTS_CTRL:
1715 #endif
1716 #if LIBCURL_VERSION_NUM >= 0x074c00 /* Available since 7.76.0 */
1717 case CURLOPT_DOH_SSL_VERIFYHOST:
1718 case CURLOPT_DOH_SSL_VERIFYPEER:
1719 case CURLOPT_DOH_SSL_VERIFYSTATUS:
1720 #endif
1721 #if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
1722 case CURLOPT_MAXLIFETIME_CONN:
1723 #endif
1724 #if LIBCURL_VERSION_NUM >= 0x075100 /* Available since 7.81.0 */
1725 case CURLOPT_MIME_OPTIONS:
1726 #endif
1727 #if LIBCURL_VERSION_NUM >= 0x075600 /* Available since 7.86.0 */
1728 case CURLOPT_WS_OPTIONS:
1729 #endif
1730 #if LIBCURL_VERSION_NUM >= 0x075700 /* Available since 7.87.0 */
1731 case CURLOPT_CA_CACHE_TIMEOUT:
1732 case CURLOPT_QUICK_EXIT:
1733 #endif
1734 lval = zval_get_long(zvalue);
1735 if ((option == CURLOPT_PROTOCOLS || option == CURLOPT_REDIR_PROTOCOLS) &&
1736 (PG(open_basedir) && *PG(open_basedir)) && (lval & CURLPROTO_FILE)) {
1737 php_error_docref(NULL, E_WARNING, "CURLPROTO_FILE cannot be activated when an open_basedir is set");
1738 return FAILURE;
1739 }
1740 # if defined(ZTS)
1741 if (option == CURLOPT_DNS_USE_GLOBAL_CACHE && lval) {
1742 php_error_docref(NULL, E_WARNING, "CURLOPT_DNS_USE_GLOBAL_CACHE cannot be activated when thread safety is enabled");
1743 return FAILURE;
1744 }
1745 # endif
1746 error = curl_easy_setopt(ch->cp, option, lval);
1747 break;
1748 case CURLOPT_SAFE_UPLOAD:
1749 if (!zend_is_true(zvalue)) {
1750 zend_value_error("%s(): Disabling safe uploads is no longer supported", get_active_function_name());
1751 return FAILURE;
1752 }
1753 break;
1754
1755 /* String options */
1756 case CURLOPT_CAINFO:
1757 case CURLOPT_CAPATH:
1758 case CURLOPT_COOKIE:
1759 case CURLOPT_EGDSOCKET:
1760 case CURLOPT_INTERFACE:
1761 case CURLOPT_PROXY:
1762 case CURLOPT_PROXYUSERPWD:
1763 case CURLOPT_REFERER:
1764 case CURLOPT_SSLCERTTYPE:
1765 case CURLOPT_SSLENGINE:
1766 case CURLOPT_SSLENGINE_DEFAULT:
1767 case CURLOPT_SSLKEY:
1768 case CURLOPT_SSLKEYPASSWD:
1769 case CURLOPT_SSLKEYTYPE:
1770 case CURLOPT_SSL_CIPHER_LIST:
1771 case CURLOPT_USERAGENT:
1772 case CURLOPT_USERPWD:
1773 case CURLOPT_COOKIELIST:
1774 case CURLOPT_FTP_ALTERNATIVE_TO_USER:
1775 case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:
1776 case CURLOPT_PASSWORD:
1777 case CURLOPT_PROXYPASSWORD:
1778 case CURLOPT_PROXYUSERNAME:
1779 case CURLOPT_USERNAME:
1780 case CURLOPT_NOPROXY:
1781 case CURLOPT_SOCKS5_GSSAPI_SERVICE:
1782 case CURLOPT_MAIL_FROM:
1783 case CURLOPT_RTSP_STREAM_URI:
1784 case CURLOPT_RTSP_TRANSPORT:
1785 case CURLOPT_TLSAUTH_TYPE:
1786 case CURLOPT_TLSAUTH_PASSWORD:
1787 case CURLOPT_TLSAUTH_USERNAME:
1788 case CURLOPT_TRANSFER_ENCODING:
1789 case CURLOPT_DNS_SERVERS:
1790 case CURLOPT_MAIL_AUTH:
1791 case CURLOPT_LOGIN_OPTIONS:
1792 case CURLOPT_PINNEDPUBLICKEY:
1793 case CURLOPT_PROXY_SERVICE_NAME:
1794 case CURLOPT_SERVICE_NAME:
1795 case CURLOPT_DEFAULT_PROTOCOL:
1796 case CURLOPT_PRE_PROXY:
1797 case CURLOPT_PROXY_CAINFO:
1798 case CURLOPT_PROXY_CAPATH:
1799 case CURLOPT_PROXY_CRLFILE:
1800 case CURLOPT_PROXY_KEYPASSWD:
1801 case CURLOPT_PROXY_PINNEDPUBLICKEY:
1802 case CURLOPT_PROXY_SSL_CIPHER_LIST:
1803 case CURLOPT_PROXY_SSLCERT:
1804 case CURLOPT_PROXY_SSLCERTTYPE:
1805 case CURLOPT_PROXY_SSLKEY:
1806 case CURLOPT_PROXY_SSLKEYTYPE:
1807 case CURLOPT_PROXY_TLSAUTH_PASSWORD:
1808 case CURLOPT_PROXY_TLSAUTH_TYPE:
1809 case CURLOPT_PROXY_TLSAUTH_USERNAME:
1810 case CURLOPT_ABSTRACT_UNIX_SOCKET:
1811 case CURLOPT_REQUEST_TARGET:
1812 case CURLOPT_PROXY_TLS13_CIPHERS:
1813 case CURLOPT_TLS13_CIPHERS:
1814 #if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */
1815 case CURLOPT_ALTSVC:
1816 #endif
1817 #if LIBCURL_VERSION_NUM >= 0x074200 /* Available since 7.66.0 */
1818 case CURLOPT_SASL_AUTHZID:
1819 #endif
1820 #if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */
1821 case CURLOPT_PROXY_ISSUERCERT:
1822 #endif
1823 #if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */
1824 case CURLOPT_SSL_EC_CURVES:
1825 #endif
1826 #if LIBCURL_VERSION_NUM >= 0x074b00 /* Available since 7.75.0 */
1827 case CURLOPT_AWS_SIGV4:
1828 #endif
1829 #if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
1830 case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256:
1831 #endif
1832 #if LIBCURL_VERSION_NUM >= 0x075500 /* Available since 7.85.0 */
1833 case CURLOPT_PROTOCOLS_STR:
1834 case CURLOPT_REDIR_PROTOCOLS_STR:
1835 #endif
1836 {
1837 zend_string *tmp_str;
1838 zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);
1839 #if LIBCURL_VERSION_NUM >= 0x075500 /* Available since 7.85.0 */
1840 if ((option == CURLOPT_PROTOCOLS_STR || option == CURLOPT_REDIR_PROTOCOLS_STR) &&
1841 (PG(open_basedir) && *PG(open_basedir)) && php_memnistr(ZSTR_VAL(str), "file", sizeof("file") - 1, ZSTR_VAL(str) + ZSTR_LEN(str)) != NULL) {
1842 php_error_docref(NULL, E_WARNING, "The FILE protocol cannot be activated when an open_basedir is set");
1843 return FAILURE;
1844 }
1845 #endif
1846 zend_result ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str));
1847 zend_tmp_string_release(tmp_str);
1848 return ret;
1849 }
1850
1851 /* Curl nullable string options */
1852 case CURLOPT_CUSTOMREQUEST:
1853 case CURLOPT_FTPPORT:
1854 case CURLOPT_RANGE:
1855 case CURLOPT_FTP_ACCOUNT:
1856 case CURLOPT_RTSP_SESSION_ID:
1857 case CURLOPT_ACCEPT_ENCODING:
1858 case CURLOPT_DNS_INTERFACE:
1859 case CURLOPT_DNS_LOCAL_IP4:
1860 case CURLOPT_DNS_LOCAL_IP6:
1861 case CURLOPT_XOAUTH2_BEARER:
1862 case CURLOPT_UNIX_SOCKET_PATH:
1863 #if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */
1864 case CURLOPT_DOH_URL:
1865 #endif
1866 #if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */
1867 case CURLOPT_HSTS:
1868 #endif
1869 case CURLOPT_KRBLEVEL:
1870 {
1871 if (Z_ISNULL_P(zvalue)) {
1872 error = curl_easy_setopt(ch->cp, option, NULL);
1873 } else {
1874 zend_string *tmp_str;
1875 zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);
1876 zend_result ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str));
1877 zend_tmp_string_release(tmp_str);
1878 return ret;
1879 }
1880 break;
1881 }
1882
1883 /* Curl private option */
1884 case CURLOPT_PRIVATE:
1885 {
1886 zval_ptr_dtor(&ch->private_data);
1887 ZVAL_COPY(&ch->private_data, zvalue);
1888 return SUCCESS;
1889 }
1890
1891 /* Curl url option */
1892 case CURLOPT_URL:
1893 {
1894 zend_string *tmp_str;
1895 zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);
1896 zend_result ret = php_curl_option_url(ch, str);
1897 zend_tmp_string_release(tmp_str);
1898 return ret;
1899 }
1900
1901 /* Curl file handle options */
1902 case CURLOPT_FILE:
1903 case CURLOPT_INFILE:
1904 case CURLOPT_STDERR:
1905 case CURLOPT_WRITEHEADER: {
1906 FILE *fp = NULL;
1907 php_stream *what = NULL;
1908
1909 if (Z_TYPE_P(zvalue) != IS_NULL) {
1910 what = (php_stream *)zend_fetch_resource2_ex(zvalue, "File-Handle", php_file_le_stream(), php_file_le_pstream());
1911 if (!what) {
1912 return FAILURE;
1913 }
1914
1915 if (FAILURE == php_stream_cast(what, PHP_STREAM_AS_STDIO, (void *) &fp, REPORT_ERRORS)) {
1916 return FAILURE;
1917 }
1918
1919 if (!fp) {
1920 return FAILURE;
1921 }
1922 }
1923
1924 error = CURLE_OK;
1925 switch (option) {
1926 case CURLOPT_FILE:
1927 if (!what) {
1928 if (!Z_ISUNDEF(ch->handlers.write->stream)) {
1929 zval_ptr_dtor(&ch->handlers.write->stream);
1930 ZVAL_UNDEF(&ch->handlers.write->stream);
1931 }
1932 ch->handlers.write->fp = NULL;
1933 ch->handlers.write->method = PHP_CURL_STDOUT;
1934 } else if (what->mode[0] != 'r' || what->mode[1] == '+') {
1935 zval_ptr_dtor(&ch->handlers.write->stream);
1936 ch->handlers.write->fp = fp;
1937 ch->handlers.write->method = PHP_CURL_FILE;
1938 ZVAL_COPY(&ch->handlers.write->stream, zvalue);
1939 } else {
1940 zend_value_error("%s(): The provided file handle must be writable", get_active_function_name());
1941 return FAILURE;
1942 }
1943 break;
1944 case CURLOPT_WRITEHEADER:
1945 if (!what) {
1946 if (!Z_ISUNDEF(ch->handlers.write_header->stream)) {
1947 zval_ptr_dtor(&ch->handlers.write_header->stream);
1948 ZVAL_UNDEF(&ch->handlers.write_header->stream);
1949 }
1950 ch->handlers.write_header->fp = NULL;
1951 ch->handlers.write_header->method = PHP_CURL_IGNORE;
1952 } else if (what->mode[0] != 'r' || what->mode[1] == '+') {
1953 zval_ptr_dtor(&ch->handlers.write_header->stream);
1954 ch->handlers.write_header->fp = fp;
1955 ch->handlers.write_header->method = PHP_CURL_FILE;
1956 ZVAL_COPY(&ch->handlers.write_header->stream, zvalue);
1957 } else {
1958 zend_value_error("%s(): The provided file handle must be writable", get_active_function_name());
1959 return FAILURE;
1960 }
1961 break;
1962 case CURLOPT_INFILE:
1963 if (!what) {
1964 if (!Z_ISUNDEF(ch->handlers.read->stream)) {
1965 zval_ptr_dtor(&ch->handlers.read->stream);
1966 ZVAL_UNDEF(&ch->handlers.read->stream);
1967 }
1968 ch->handlers.read->fp = NULL;
1969 ch->handlers.read->res = NULL;
1970 } else {
1971 zval_ptr_dtor(&ch->handlers.read->stream);
1972 ch->handlers.read->fp = fp;
1973 ch->handlers.read->res = Z_RES_P(zvalue);
1974 ZVAL_COPY(&ch->handlers.read->stream, zvalue);
1975 }
1976 break;
1977 case CURLOPT_STDERR:
1978 if (!what) {
1979 if (!Z_ISUNDEF(ch->handlers.std_err)) {
1980 zval_ptr_dtor(&ch->handlers.std_err);
1981 ZVAL_UNDEF(&ch->handlers.std_err);
1982 }
1983 } else if (what->mode[0] != 'r' || what->mode[1] == '+') {
1984 zval_ptr_dtor(&ch->handlers.std_err);
1985 ZVAL_COPY(&ch->handlers.std_err, zvalue);
1986 } else {
1987 zend_value_error("%s(): The provided file handle must be writable", get_active_function_name());
1988 return FAILURE;
1989 }
1990 ZEND_FALLTHROUGH;
1991 default:
1992 error = curl_easy_setopt(ch->cp, option, fp);
1993 break;
1994 }
1995 break;
1996 }
1997
1998 /* Curl linked list options */
1999 case CURLOPT_HTTP200ALIASES:
2000 case CURLOPT_HTTPHEADER:
2001 case CURLOPT_POSTQUOTE:
2002 case CURLOPT_PREQUOTE:
2003 case CURLOPT_QUOTE:
2004 case CURLOPT_TELNETOPTIONS:
2005 case CURLOPT_MAIL_RCPT:
2006 case CURLOPT_RESOLVE:
2007 case CURLOPT_PROXYHEADER:
2008 case CURLOPT_CONNECT_TO:
2009 {
2010 zval *current;
2011 HashTable *ph;
2012 zend_string *val, *tmp_val;
2013 struct curl_slist *slist = NULL;
2014
2015 if (Z_TYPE_P(zvalue) != IS_ARRAY) {
2016 const char *name = NULL;
2017 switch (option) {
2018 case CURLOPT_HTTPHEADER:
2019 name = "CURLOPT_HTTPHEADER";
2020 break;
2021 case CURLOPT_QUOTE:
2022 name = "CURLOPT_QUOTE";
2023 break;
2024 case CURLOPT_HTTP200ALIASES:
2025 name = "CURLOPT_HTTP200ALIASES";
2026 break;
2027 case CURLOPT_POSTQUOTE:
2028 name = "CURLOPT_POSTQUOTE";
2029 break;
2030 case CURLOPT_PREQUOTE:
2031 name = "CURLOPT_PREQUOTE";
2032 break;
2033 case CURLOPT_TELNETOPTIONS:
2034 name = "CURLOPT_TELNETOPTIONS";
2035 break;
2036 case CURLOPT_MAIL_RCPT:
2037 name = "CURLOPT_MAIL_RCPT";
2038 break;
2039 case CURLOPT_RESOLVE:
2040 name = "CURLOPT_RESOLVE";
2041 break;
2042 case CURLOPT_PROXYHEADER:
2043 name = "CURLOPT_PROXYHEADER";
2044 break;
2045 case CURLOPT_CONNECT_TO:
2046 name = "CURLOPT_CONNECT_TO";
2047 break;
2048 }
2049
2050 zend_type_error("%s(): The %s option must have an array value", get_active_function_name(), name);
2051 return FAILURE;
2052 }
2053
2054 ph = Z_ARRVAL_P(zvalue);
2055 ZEND_HASH_FOREACH_VAL(ph, current) {
2056 ZVAL_DEREF(current);
2057 val = zval_get_tmp_string(current, &tmp_val);
2058 slist = curl_slist_append(slist, ZSTR_VAL(val));
2059 zend_tmp_string_release(tmp_val);
2060 if (!slist) {
2061 php_error_docref(NULL, E_WARNING, "Could not build curl_slist");
2062 return FAILURE;
2063 }
2064 } ZEND_HASH_FOREACH_END();
2065
2066 if (slist) {
2067 if ((*ch->clone) == 1) {
2068 zend_hash_index_update_ptr(ch->to_free->slist, option, slist);
2069 } else {
2070 zend_hash_next_index_insert_ptr(ch->to_free->slist, slist);
2071 }
2072 }
2073
2074 error = curl_easy_setopt(ch->cp, option, slist);
2075
2076 break;
2077 }
2078
2079 case CURLOPT_BINARYTRANSFER:
2080 /* Do nothing, just backward compatibility */
2081 break;
2082
2083 case CURLOPT_FOLLOWLOCATION:
2084 lval = zend_is_true(zvalue);
2085 error = curl_easy_setopt(ch->cp, option, lval);
2086 break;
2087
2088 case CURLOPT_POSTFIELDS:
2089 if (Z_TYPE_P(zvalue) == IS_ARRAY) {
2090 if (zend_hash_num_elements(HASH_OF(zvalue)) == 0) {
2091 /* no need to build the mime structure for empty hashtables;
2092 also works around https://github.com/curl/curl/issues/6455 */
2093 curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDS, "");
2094 error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, 0);
2095 } else {
2096 return build_mime_structure_from_hash(ch, zvalue);
2097 }
2098 } else {
2099 zend_string *tmp_str;
2100 zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);
2101 /* with curl 7.17.0 and later, we can use COPYPOSTFIELDS, but we have to provide size before */
2102 error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, ZSTR_LEN(str));
2103 error = curl_easy_setopt(ch->cp, CURLOPT_COPYPOSTFIELDS, ZSTR_VAL(str));
2104 zend_tmp_string_release(tmp_str);
2105 }
2106 break;
2107
2108 case CURLOPT_RETURNTRANSFER:
2109 if (zend_is_true(zvalue)) {
2110 ch->handlers.write->method = PHP_CURL_RETURN;
2111 } else {
2112 ch->handlers.write->method = PHP_CURL_STDOUT;
2113 }
2114 break;
2115
2116 /* Curl off_t options */
2117 case CURLOPT_MAX_RECV_SPEED_LARGE:
2118 case CURLOPT_MAX_SEND_SPEED_LARGE:
2119 case CURLOPT_MAXFILESIZE_LARGE:
2120 case CURLOPT_TIMEVALUE_LARGE:
2121 lval = zval_get_long(zvalue);
2122 error = curl_easy_setopt(ch->cp, option, (curl_off_t)lval);
2123 break;
2124
2125 case CURLOPT_POSTREDIR:
2126 lval = zval_get_long(zvalue);
2127 error = curl_easy_setopt(ch->cp, CURLOPT_POSTREDIR, lval & CURL_REDIR_POST_ALL);
2128 break;
2129
2130 /* the following options deal with files, therefore the open_basedir check
2131 * is required.
2132 */
2133 case CURLOPT_COOKIEFILE:
2134 case CURLOPT_COOKIEJAR:
2135 case CURLOPT_RANDOM_FILE:
2136 case CURLOPT_SSLCERT:
2137 case CURLOPT_NETRC_FILE:
2138 case CURLOPT_SSH_PRIVATE_KEYFILE:
2139 case CURLOPT_SSH_PUBLIC_KEYFILE:
2140 case CURLOPT_CRLFILE:
2141 case CURLOPT_ISSUERCERT:
2142 case CURLOPT_SSH_KNOWNHOSTS:
2143 {
2144 zend_string *tmp_str;
2145 zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);
2146 zend_result ret;
2147
2148 if (ZSTR_LEN(str) && php_check_open_basedir(ZSTR_VAL(str))) {
2149 zend_tmp_string_release(tmp_str);
2150 return FAILURE;
2151 }
2152
2153 ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str));
2154 zend_tmp_string_release(tmp_str);
2155 return ret;
2156 }
2157
2158 case CURLINFO_HEADER_OUT:
2159 if (zend_is_true(zvalue)) {
2160 curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, curl_debug);
2161 curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *)ch);
2162 curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 1);
2163 } else {
2164 curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, NULL);
2165 curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, NULL);
2166 curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0);
2167 }
2168 break;
2169
2170 case CURLOPT_SHARE:
2171 {
2172 if (Z_TYPE_P(zvalue) == IS_OBJECT && Z_OBJCE_P(zvalue) == curl_share_ce) {
2173 php_curlsh *sh = Z_CURL_SHARE_P(zvalue);
2174 curl_easy_setopt(ch->cp, CURLOPT_SHARE, sh->share);
2175
2176 if (ch->share) {
2177 OBJ_RELEASE(&ch->share->std);
2178 }
2179 GC_ADDREF(&sh->std);
2180 ch->share = sh;
2181 }
2182 }
2183 break;
2184
2185 /* Curl blob options */
2186 #if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */
2187 case CURLOPT_ISSUERCERT_BLOB:
2188 case CURLOPT_PROXY_ISSUERCERT_BLOB:
2189 case CURLOPT_PROXY_SSLCERT_BLOB:
2190 case CURLOPT_PROXY_SSLKEY_BLOB:
2191 case CURLOPT_SSLCERT_BLOB:
2192 case CURLOPT_SSLKEY_BLOB:
2193 #if LIBCURL_VERSION_NUM >= 0x074d00 /* Available since 7.77.0 */
2194 case CURLOPT_CAINFO_BLOB:
2195 case CURLOPT_PROXY_CAINFO_BLOB:
2196 #endif
2197 {
2198 zend_string *tmp_str;
2199 zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);
2200
2201 struct curl_blob stblob;
2202 stblob.data = ZSTR_VAL(str);
2203 stblob.len = ZSTR_LEN(str);
2204 stblob.flags = CURL_BLOB_COPY;
2205 error = curl_easy_setopt(ch->cp, option, &stblob);
2206
2207 zend_tmp_string_release(tmp_str);
2208 }
2209 break;
2210 #endif
2211
2212 default:
2213 if (is_array_config) {
2214 zend_argument_value_error(2, "must contain only valid cURL options");
2215 } else {
2216 zend_argument_value_error(2, "is not a valid cURL option");
2217 }
2218 error = CURLE_UNKNOWN_OPTION;
2219 break;
2220 }
2221
2222 SAVE_CURL_ERROR(ch, error);
2223 if (error != CURLE_OK) {
2224 return FAILURE;
2225 } else {
2226 return SUCCESS;
2227 }
2228 }
2229 /* }}} */
2230
2231 /* {{{ Set an option for a cURL transfer */
PHP_FUNCTION(curl_setopt)2232 PHP_FUNCTION(curl_setopt)
2233 {
2234 zval *zid, *zvalue;
2235 zend_long options;
2236 php_curl *ch;
2237
2238 ZEND_PARSE_PARAMETERS_START(3, 3)
2239 Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
2240 Z_PARAM_LONG(options)
2241 Z_PARAM_ZVAL(zvalue)
2242 ZEND_PARSE_PARAMETERS_END();
2243
2244 ch = Z_CURL_P(zid);
2245
2246 if (_php_curl_setopt(ch, options, zvalue, 0) == SUCCESS) {
2247 RETURN_TRUE;
2248 } else {
2249 RETURN_FALSE;
2250 }
2251 }
2252 /* }}} */
2253
2254 /* {{{ Set an array of option for a cURL transfer */
PHP_FUNCTION(curl_setopt_array)2255 PHP_FUNCTION(curl_setopt_array)
2256 {
2257 zval *zid, *arr, *entry;
2258 php_curl *ch;
2259 zend_ulong option;
2260 zend_string *string_key;
2261
2262 ZEND_PARSE_PARAMETERS_START(2, 2)
2263 Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
2264 Z_PARAM_ARRAY(arr)
2265 ZEND_PARSE_PARAMETERS_END();
2266
2267 ch = Z_CURL_P(zid);
2268
2269 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(arr), option, string_key, entry) {
2270 if (string_key) {
2271 zend_argument_value_error(2, "contains an invalid cURL option");
2272 RETURN_THROWS();
2273 }
2274
2275 ZVAL_DEREF(entry);
2276 if (_php_curl_setopt(ch, (zend_long) option, entry, 1) == FAILURE) {
2277 RETURN_FALSE;
2278 }
2279 } ZEND_HASH_FOREACH_END();
2280
2281 RETURN_TRUE;
2282 }
2283 /* }}} */
2284
2285 /* {{{ _php_curl_cleanup_handle(ch)
2286 Cleanup an execution phase */
_php_curl_cleanup_handle(php_curl * ch)2287 void _php_curl_cleanup_handle(php_curl *ch)
2288 {
2289 smart_str_free(&ch->handlers.write->buf);
2290 if (ch->header.str) {
2291 zend_string_release_ex(ch->header.str, 0);
2292 ch->header.str = NULL;
2293 }
2294
2295 memset(ch->err.str, 0, CURL_ERROR_SIZE + 1);
2296 ch->err.no = 0;
2297 }
2298 /* }}} */
2299
2300 /* {{{ Perform a cURL session */
PHP_FUNCTION(curl_exec)2301 PHP_FUNCTION(curl_exec)
2302 {
2303 CURLcode error;
2304 zval *zid;
2305 php_curl *ch;
2306
2307 ZEND_PARSE_PARAMETERS_START(1, 1)
2308 Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
2309 ZEND_PARSE_PARAMETERS_END();
2310
2311 ch = Z_CURL_P(zid);
2312
2313 _php_curl_verify_handlers(ch, /* reporterror */ true);
2314
2315 _php_curl_cleanup_handle(ch);
2316
2317 error = curl_easy_perform(ch->cp);
2318 SAVE_CURL_ERROR(ch, error);
2319
2320 if (error != CURLE_OK) {
2321 smart_str_free(&ch->handlers.write->buf);
2322 RETURN_FALSE;
2323 }
2324
2325 if (!Z_ISUNDEF(ch->handlers.std_err)) {
2326 php_stream *stream;
2327 stream = (php_stream*)zend_fetch_resource2_ex(&ch->handlers.std_err, NULL, php_file_le_stream(), php_file_le_pstream());
2328 if (stream) {
2329 php_stream_flush(stream);
2330 }
2331 }
2332
2333 if (ch->handlers.write->method == PHP_CURL_RETURN && ch->handlers.write->buf.s) {
2334 smart_str_0(&ch->handlers.write->buf);
2335 RETURN_STR_COPY(ch->handlers.write->buf.s);
2336 }
2337
2338 /* flush the file handle, so any remaining data is synched to disk */
2339 if (ch->handlers.write->method == PHP_CURL_FILE && ch->handlers.write->fp) {
2340 fflush(ch->handlers.write->fp);
2341 }
2342 if (ch->handlers.write_header->method == PHP_CURL_FILE && ch->handlers.write_header->fp) {
2343 fflush(ch->handlers.write_header->fp);
2344 }
2345
2346 if (ch->handlers.write->method == PHP_CURL_RETURN) {
2347 RETURN_EMPTY_STRING();
2348 } else {
2349 RETURN_TRUE;
2350 }
2351 }
2352 /* }}} */
2353
2354 /* {{{ Get information regarding a specific transfer */
PHP_FUNCTION(curl_getinfo)2355 PHP_FUNCTION(curl_getinfo)
2356 {
2357 zval *zid;
2358 php_curl *ch;
2359 zend_long option;
2360 bool option_is_null = 1;
2361
2362 ZEND_PARSE_PARAMETERS_START(1, 2)
2363 Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
2364 Z_PARAM_OPTIONAL
2365 Z_PARAM_LONG_OR_NULL(option, option_is_null)
2366 ZEND_PARSE_PARAMETERS_END();
2367
2368 ch = Z_CURL_P(zid);
2369
2370 if (option_is_null) {
2371 char *s_code;
2372 /* libcurl expects long datatype. So far no cases are known where
2373 it would be an issue. Using zend_long would truncate a 64-bit
2374 var on Win64, so the exact long datatype fits everywhere, as
2375 long as there's no 32-bit int overflow. */
2376 long l_code;
2377 double d_code;
2378 struct curl_certinfo *ci = NULL;
2379 zval listcode;
2380 curl_off_t co;
2381
2382 array_init(return_value);
2383
2384 if (curl_easy_getinfo(ch->cp, CURLINFO_EFFECTIVE_URL, &s_code) == CURLE_OK) {
2385 CAAS("url", s_code);
2386 }
2387 if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_TYPE, &s_code) == CURLE_OK) {
2388 if (s_code != NULL) {
2389 CAAS("content_type", s_code);
2390 } else {
2391 zval retnull;
2392 ZVAL_NULL(&retnull);
2393 CAAZ("content_type", &retnull);
2394 }
2395 }
2396 if (curl_easy_getinfo(ch->cp, CURLINFO_HTTP_CODE, &l_code) == CURLE_OK) {
2397 CAAL("http_code", l_code);
2398 }
2399 if (curl_easy_getinfo(ch->cp, CURLINFO_HEADER_SIZE, &l_code) == CURLE_OK) {
2400 CAAL("header_size", l_code);
2401 }
2402 if (curl_easy_getinfo(ch->cp, CURLINFO_REQUEST_SIZE, &l_code) == CURLE_OK) {
2403 CAAL("request_size", l_code);
2404 }
2405 if (curl_easy_getinfo(ch->cp, CURLINFO_FILETIME, &l_code) == CURLE_OK) {
2406 CAAL("filetime", l_code);
2407 }
2408 if (curl_easy_getinfo(ch->cp, CURLINFO_SSL_VERIFYRESULT, &l_code) == CURLE_OK) {
2409 CAAL("ssl_verify_result", l_code);
2410 }
2411 if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_COUNT, &l_code) == CURLE_OK) {
2412 CAAL("redirect_count", l_code);
2413 }
2414 if (curl_easy_getinfo(ch->cp, CURLINFO_TOTAL_TIME, &d_code) == CURLE_OK) {
2415 CAAD("total_time", d_code);
2416 }
2417 if (curl_easy_getinfo(ch->cp, CURLINFO_NAMELOOKUP_TIME, &d_code) == CURLE_OK) {
2418 CAAD("namelookup_time", d_code);
2419 }
2420 if (curl_easy_getinfo(ch->cp, CURLINFO_CONNECT_TIME, &d_code) == CURLE_OK) {
2421 CAAD("connect_time", d_code);
2422 }
2423 if (curl_easy_getinfo(ch->cp, CURLINFO_PRETRANSFER_TIME, &d_code) == CURLE_OK) {
2424 CAAD("pretransfer_time", d_code);
2425 }
2426 if (curl_easy_getinfo(ch->cp, CURLINFO_SIZE_UPLOAD, &d_code) == CURLE_OK) {
2427 CAAD("size_upload", d_code);
2428 }
2429 if (curl_easy_getinfo(ch->cp, CURLINFO_SIZE_DOWNLOAD, &d_code) == CURLE_OK) {
2430 CAAD("size_download", d_code);
2431 }
2432 if (curl_easy_getinfo(ch->cp, CURLINFO_SPEED_DOWNLOAD, &d_code) == CURLE_OK) {
2433 CAAD("speed_download", d_code);
2434 }
2435 if (curl_easy_getinfo(ch->cp, CURLINFO_SPEED_UPLOAD, &d_code) == CURLE_OK) {
2436 CAAD("speed_upload", d_code);
2437 }
2438 if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d_code) == CURLE_OK) {
2439 CAAD("download_content_length", d_code);
2440 }
2441 if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_LENGTH_UPLOAD, &d_code) == CURLE_OK) {
2442 CAAD("upload_content_length", d_code);
2443 }
2444 if (curl_easy_getinfo(ch->cp, CURLINFO_STARTTRANSFER_TIME, &d_code) == CURLE_OK) {
2445 CAAD("starttransfer_time", d_code);
2446 }
2447 if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_TIME, &d_code) == CURLE_OK) {
2448 CAAD("redirect_time", d_code);
2449 }
2450 if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_URL, &s_code) == CURLE_OK) {
2451 CAAS("redirect_url", s_code);
2452 }
2453 if (curl_easy_getinfo(ch->cp, CURLINFO_PRIMARY_IP, &s_code) == CURLE_OK) {
2454 CAAS("primary_ip", s_code);
2455 }
2456 if (curl_easy_getinfo(ch->cp, CURLINFO_CERTINFO, &ci) == CURLE_OK) {
2457 array_init(&listcode);
2458 create_certinfo(ci, &listcode);
2459 CAAZ("certinfo", &listcode);
2460 }
2461 if (curl_easy_getinfo(ch->cp, CURLINFO_PRIMARY_PORT, &l_code) == CURLE_OK) {
2462 CAAL("primary_port", l_code);
2463 }
2464 if (curl_easy_getinfo(ch->cp, CURLINFO_LOCAL_IP, &s_code) == CURLE_OK) {
2465 CAAS("local_ip", s_code);
2466 }
2467 if (curl_easy_getinfo(ch->cp, CURLINFO_LOCAL_PORT, &l_code) == CURLE_OK) {
2468 CAAL("local_port", l_code);
2469 }
2470 if (curl_easy_getinfo(ch->cp, CURLINFO_HTTP_VERSION, &l_code) == CURLE_OK) {
2471 CAAL("http_version", l_code);
2472 }
2473 if (curl_easy_getinfo(ch->cp, CURLINFO_PROTOCOL, &l_code) == CURLE_OK) {
2474 CAAL("protocol", l_code);
2475 }
2476 if (curl_easy_getinfo(ch->cp, CURLINFO_PROXY_SSL_VERIFYRESULT, &l_code) == CURLE_OK) {
2477 CAAL("ssl_verifyresult", l_code);
2478 }
2479 if (curl_easy_getinfo(ch->cp, CURLINFO_SCHEME, &s_code) == CURLE_OK) {
2480 CAAS("scheme", s_code);
2481 }
2482 if (curl_easy_getinfo(ch->cp, CURLINFO_APPCONNECT_TIME_T, &co) == CURLE_OK) {
2483 CAAL("appconnect_time_us", co);
2484 }
2485 if (curl_easy_getinfo(ch->cp, CURLINFO_CONNECT_TIME_T, &co) == CURLE_OK) {
2486 CAAL("connect_time_us", co);
2487 }
2488 if (curl_easy_getinfo(ch->cp, CURLINFO_NAMELOOKUP_TIME_T, &co) == CURLE_OK) {
2489 CAAL("namelookup_time_us", co);
2490 }
2491 if (curl_easy_getinfo(ch->cp, CURLINFO_PRETRANSFER_TIME_T, &co) == CURLE_OK) {
2492 CAAL("pretransfer_time_us", co);
2493 }
2494 if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_TIME_T, &co) == CURLE_OK) {
2495 CAAL("redirect_time_us", co);
2496 }
2497 if (curl_easy_getinfo(ch->cp, CURLINFO_STARTTRANSFER_TIME_T, &co) == CURLE_OK) {
2498 CAAL("starttransfer_time_us", co);
2499 }
2500 if (curl_easy_getinfo(ch->cp, CURLINFO_TOTAL_TIME_T, &co) == CURLE_OK) {
2501 CAAL("total_time_us", co);
2502 }
2503 if (ch->header.str) {
2504 CAASTR("request_header", ch->header.str);
2505 }
2506 #if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */
2507 if (curl_easy_getinfo(ch->cp, CURLINFO_EFFECTIVE_METHOD, &s_code) == CURLE_OK) {
2508 CAAS("effective_method", s_code);
2509 }
2510 #endif
2511 #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
2512 if (curl_easy_getinfo(ch->cp, CURLINFO_CAPATH, &s_code) == CURLE_OK) {
2513 CAAS("capath", s_code);
2514 }
2515 if (curl_easy_getinfo(ch->cp, CURLINFO_CAINFO, &s_code) == CURLE_OK) {
2516 CAAS("cainfo", s_code);
2517 }
2518 #endif
2519 } else {
2520 switch (option) {
2521 case CURLINFO_HEADER_OUT:
2522 if (ch->header.str) {
2523 RETURN_STR_COPY(ch->header.str);
2524 } else {
2525 RETURN_FALSE;
2526 }
2527 case CURLINFO_CERTINFO: {
2528 struct curl_certinfo *ci = NULL;
2529
2530 array_init(return_value);
2531
2532 if (curl_easy_getinfo(ch->cp, CURLINFO_CERTINFO, &ci) == CURLE_OK) {
2533 create_certinfo(ci, return_value);
2534 } else {
2535 RETURN_FALSE;
2536 }
2537 break;
2538 }
2539 case CURLINFO_PRIVATE:
2540 if (!Z_ISUNDEF(ch->private_data)) {
2541 RETURN_COPY(&ch->private_data);
2542 } else {
2543 RETURN_FALSE;
2544 }
2545 break;
2546 default: {
2547 int type = CURLINFO_TYPEMASK & option;
2548 switch (type) {
2549 case CURLINFO_STRING:
2550 {
2551 char *s_code = NULL;
2552
2553 if (curl_easy_getinfo(ch->cp, option, &s_code) == CURLE_OK && s_code) {
2554 RETURN_STRING(s_code);
2555 } else {
2556 RETURN_FALSE;
2557 }
2558 break;
2559 }
2560 case CURLINFO_LONG:
2561 {
2562 zend_long code = 0;
2563
2564 if (curl_easy_getinfo(ch->cp, option, &code) == CURLE_OK) {
2565 RETURN_LONG(code);
2566 } else {
2567 RETURN_FALSE;
2568 }
2569 break;
2570 }
2571 case CURLINFO_DOUBLE:
2572 {
2573 double code = 0.0;
2574
2575 if (curl_easy_getinfo(ch->cp, option, &code) == CURLE_OK) {
2576 RETURN_DOUBLE(code);
2577 } else {
2578 RETURN_FALSE;
2579 }
2580 break;
2581 }
2582 case CURLINFO_SLIST:
2583 {
2584 struct curl_slist *slist;
2585 if (curl_easy_getinfo(ch->cp, option, &slist) == CURLE_OK) {
2586 struct curl_slist *current = slist;
2587 array_init(return_value);
2588 while (current) {
2589 add_next_index_string(return_value, current->data);
2590 current = current->next;
2591 }
2592 curl_slist_free_all(slist);
2593 } else {
2594 RETURN_FALSE;
2595 }
2596 break;
2597 }
2598 case CURLINFO_OFF_T:
2599 {
2600 curl_off_t c_off;
2601 if (curl_easy_getinfo(ch->cp, option, &c_off) == CURLE_OK) {
2602 RETURN_LONG((long) c_off);
2603 } else {
2604 RETURN_FALSE;
2605 }
2606 break;
2607 }
2608 default:
2609 RETURN_FALSE;
2610 }
2611 }
2612 }
2613 }
2614 }
2615 /* }}} */
2616
2617 /* {{{ Return a string contain the last error for the current session */
PHP_FUNCTION(curl_error)2618 PHP_FUNCTION(curl_error)
2619 {
2620 zval *zid;
2621 php_curl *ch;
2622
2623 ZEND_PARSE_PARAMETERS_START(1, 1)
2624 Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
2625 ZEND_PARSE_PARAMETERS_END();
2626
2627 ch = Z_CURL_P(zid);
2628
2629 if (ch->err.no) {
2630 ch->err.str[CURL_ERROR_SIZE] = 0;
2631 RETURN_STRING(ch->err.str);
2632 } else {
2633 RETURN_EMPTY_STRING();
2634 }
2635 }
2636 /* }}} */
2637
2638 /* {{{ Return an integer containing the last error number */
PHP_FUNCTION(curl_errno)2639 PHP_FUNCTION(curl_errno)
2640 {
2641 zval *zid;
2642 php_curl *ch;
2643
2644 ZEND_PARSE_PARAMETERS_START(1,1)
2645 Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
2646 ZEND_PARSE_PARAMETERS_END();
2647
2648 ch = Z_CURL_P(zid);
2649
2650 RETURN_LONG(ch->err.no);
2651 }
2652 /* }}} */
2653
2654 /* {{{ Close a cURL session */
PHP_FUNCTION(curl_close)2655 PHP_FUNCTION(curl_close)
2656 {
2657 zval *zid;
2658 php_curl *ch;
2659
2660 ZEND_PARSE_PARAMETERS_START(1, 1)
2661 Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
2662 ZEND_PARSE_PARAMETERS_END();
2663
2664 ch = Z_CURL_P(zid);
2665
2666 if (ch->in_callback) {
2667 zend_throw_error(NULL, "%s(): Attempt to close cURL handle from a callback", get_active_function_name());
2668 RETURN_THROWS();
2669 }
2670 }
2671 /* }}} */
2672
curl_free_obj(zend_object * object)2673 static void curl_free_obj(zend_object *object)
2674 {
2675 php_curl *ch = curl_from_obj(object);
2676
2677 #if PHP_CURL_DEBUG
2678 fprintf(stderr, "DTOR CALLED, ch = %x\n", ch);
2679 #endif
2680
2681 if (!ch->cp) {
2682 /* Can happen if constructor throws. */
2683 zend_object_std_dtor(&ch->std);
2684 return;
2685 }
2686
2687 _php_curl_verify_handlers(ch, /* reporterror */ false);
2688
2689 curl_easy_cleanup(ch->cp);
2690
2691 /* cURL destructors should be invoked only by last curl handle */
2692 if (--(*ch->clone) == 0) {
2693 zend_llist_clean(&ch->to_free->post);
2694 zend_llist_clean(&ch->to_free->stream);
2695
2696 zend_hash_destroy(ch->to_free->slist);
2697 efree(ch->to_free->slist);
2698 efree(ch->to_free);
2699 efree(ch->clone);
2700 }
2701
2702 smart_str_free(&ch->handlers.write->buf);
2703 if (ZEND_FCC_INITIALIZED(ch->handlers.write->fcc)) {
2704 zend_fcc_dtor(&ch->handlers.write->fcc);
2705 }
2706 if (ZEND_FCC_INITIALIZED(ch->handlers.write_header->fcc)) {
2707 zend_fcc_dtor(&ch->handlers.write_header->fcc);
2708 }
2709 if (ZEND_FCC_INITIALIZED(ch->handlers.read->fcc)) {
2710 zend_fcc_dtor(&ch->handlers.read->fcc);
2711 }
2712 zval_ptr_dtor(&ch->handlers.std_err);
2713 if (ch->header.str) {
2714 zend_string_release_ex(ch->header.str, 0);
2715 }
2716
2717 zval_ptr_dtor(&ch->handlers.write_header->stream);
2718 zval_ptr_dtor(&ch->handlers.write->stream);
2719 zval_ptr_dtor(&ch->handlers.read->stream);
2720
2721 efree(ch->handlers.write);
2722 efree(ch->handlers.write_header);
2723 efree(ch->handlers.read);
2724
2725 if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) {
2726 zend_fcc_dtor(&ch->handlers.progress);
2727 }
2728 if (ZEND_FCC_INITIALIZED(ch->handlers.xferinfo)) {
2729 zend_fcc_dtor(&ch->handlers.xferinfo);
2730 }
2731 if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) {
2732 zend_fcc_dtor(&ch->handlers.fnmatch);
2733 }
2734 #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
2735 if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) {
2736 zend_fcc_dtor(&ch->handlers.sshhostkey);
2737 }
2738 #endif
2739
2740 zval_ptr_dtor(&ch->postfields);
2741 zval_ptr_dtor(&ch->private_data);
2742
2743 if (ch->share) {
2744 OBJ_RELEASE(&ch->share->std);
2745 }
2746
2747 zend_object_std_dtor(&ch->std);
2748 }
2749 /* }}} */
2750
2751 /* {{{ return string describing error code */
PHP_FUNCTION(curl_strerror)2752 PHP_FUNCTION(curl_strerror)
2753 {
2754 zend_long code;
2755 const char *str;
2756
2757 ZEND_PARSE_PARAMETERS_START(1, 1)
2758 Z_PARAM_LONG(code)
2759 ZEND_PARSE_PARAMETERS_END();
2760
2761 str = curl_easy_strerror(code);
2762 if (str) {
2763 RETURN_STRING(str);
2764 } else {
2765 RETURN_NULL();
2766 }
2767 }
2768 /* }}} */
2769
2770 /* {{{ _php_curl_reset_handlers()
2771 Reset all handlers of a given php_curl */
_php_curl_reset_handlers(php_curl * ch)2772 static void _php_curl_reset_handlers(php_curl *ch)
2773 {
2774 if (!Z_ISUNDEF(ch->handlers.write->stream)) {
2775 zval_ptr_dtor(&ch->handlers.write->stream);
2776 ZVAL_UNDEF(&ch->handlers.write->stream);
2777 }
2778 ch->handlers.write->fp = NULL;
2779 ch->handlers.write->method = PHP_CURL_STDOUT;
2780
2781 if (!Z_ISUNDEF(ch->handlers.write_header->stream)) {
2782 zval_ptr_dtor(&ch->handlers.write_header->stream);
2783 ZVAL_UNDEF(&ch->handlers.write_header->stream);
2784 }
2785 ch->handlers.write_header->fp = NULL;
2786 ch->handlers.write_header->method = PHP_CURL_IGNORE;
2787
2788 if (!Z_ISUNDEF(ch->handlers.read->stream)) {
2789 zval_ptr_dtor(&ch->handlers.read->stream);
2790 ZVAL_UNDEF(&ch->handlers.read->stream);
2791 }
2792 ch->handlers.read->fp = NULL;
2793 ch->handlers.read->res = NULL;
2794 ch->handlers.read->method = PHP_CURL_DIRECT;
2795
2796 if (!Z_ISUNDEF(ch->handlers.std_err)) {
2797 zval_ptr_dtor(&ch->handlers.std_err);
2798 ZVAL_UNDEF(&ch->handlers.std_err);
2799 }
2800
2801 if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) {
2802 zend_fcc_dtor(&ch->handlers.progress);
2803 }
2804
2805 if (ZEND_FCC_INITIALIZED(ch->handlers.xferinfo)) {
2806 zend_fcc_dtor(&ch->handlers.xferinfo);
2807 }
2808
2809 if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) {
2810 zend_fcc_dtor(&ch->handlers.fnmatch);
2811 }
2812
2813 #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
2814 if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) {
2815 zend_fcc_dtor(&ch->handlers.sshhostkey);
2816 }
2817 #endif
2818 }
2819 /* }}} */
2820
2821 /* {{{ Reset all options of a libcurl session handle */
PHP_FUNCTION(curl_reset)2822 PHP_FUNCTION(curl_reset)
2823 {
2824 zval *zid;
2825 php_curl *ch;
2826
2827 ZEND_PARSE_PARAMETERS_START(1, 1)
2828 Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
2829 ZEND_PARSE_PARAMETERS_END();
2830
2831 ch = Z_CURL_P(zid);
2832
2833 if (ch->in_callback) {
2834 zend_throw_error(NULL, "%s(): Attempt to reset cURL handle from a callback", get_active_function_name());
2835 RETURN_THROWS();
2836 }
2837
2838 curl_easy_reset(ch->cp);
2839 _php_curl_reset_handlers(ch);
2840 _php_curl_set_default_options(ch);
2841 }
2842 /* }}} */
2843
2844 /* {{{ URL encodes the given string */
PHP_FUNCTION(curl_escape)2845 PHP_FUNCTION(curl_escape)
2846 {
2847 zend_string *str;
2848 char *res;
2849 zval *zid;
2850 php_curl *ch;
2851
2852 ZEND_PARSE_PARAMETERS_START(2,2)
2853 Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
2854 Z_PARAM_STR(str)
2855 ZEND_PARSE_PARAMETERS_END();
2856
2857 ch = Z_CURL_P(zid);
2858
2859 if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(str))) {
2860 RETURN_FALSE;
2861 }
2862
2863 if ((res = curl_easy_escape(ch->cp, ZSTR_VAL(str), ZSTR_LEN(str)))) {
2864 RETVAL_STRING(res);
2865 curl_free(res);
2866 } else {
2867 RETURN_FALSE;
2868 }
2869 }
2870 /* }}} */
2871
2872 /* {{{ URL decodes the given string */
PHP_FUNCTION(curl_unescape)2873 PHP_FUNCTION(curl_unescape)
2874 {
2875 char *out = NULL;
2876 int out_len;
2877 zval *zid;
2878 zend_string *str;
2879 php_curl *ch;
2880
2881 ZEND_PARSE_PARAMETERS_START(2,2)
2882 Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
2883 Z_PARAM_STR(str)
2884 ZEND_PARSE_PARAMETERS_END();
2885
2886 ch = Z_CURL_P(zid);
2887
2888 if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(str))) {
2889 RETURN_FALSE;
2890 }
2891
2892 if ((out = curl_easy_unescape(ch->cp, ZSTR_VAL(str), ZSTR_LEN(str), &out_len))) {
2893 RETVAL_STRINGL(out, out_len);
2894 curl_free(out);
2895 } else {
2896 RETURN_FALSE;
2897 }
2898 }
2899 /* }}} */
2900
2901 /* {{{ pause and unpause a connection */
PHP_FUNCTION(curl_pause)2902 PHP_FUNCTION(curl_pause)
2903 {
2904 zend_long bitmask;
2905 zval *zid;
2906 php_curl *ch;
2907
2908 ZEND_PARSE_PARAMETERS_START(2,2)
2909 Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
2910 Z_PARAM_LONG(bitmask)
2911 ZEND_PARSE_PARAMETERS_END();
2912
2913 ch = Z_CURL_P(zid);
2914
2915 RETURN_LONG(curl_easy_pause(ch->cp, bitmask));
2916 }
2917 /* }}} */
2918
2919 #if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */
2920 /* {{{ perform connection upkeep checks */
PHP_FUNCTION(curl_upkeep)2921 PHP_FUNCTION(curl_upkeep)
2922 {
2923 CURLcode error;
2924 zval *zid;
2925 php_curl *ch;
2926
2927 ZEND_PARSE_PARAMETERS_START(1, 1)
2928 Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
2929 ZEND_PARSE_PARAMETERS_END();
2930
2931 ch = Z_CURL_P(zid);
2932
2933 error = curl_easy_upkeep(ch->cp);
2934 SAVE_CURL_ERROR(ch, error);
2935
2936 RETURN_BOOL(error == CURLE_OK);
2937 }
2938 /*}}} */
2939 #endif
2940