From 726db27d1cb9023ec6f474d9bc5a7250a6e2386b Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Wed, 8 Jan 2020 17:47:05 +0100 Subject: [PATCH 1/4] Fix #79019: Copied cURL handles upload empty file To cater to `curl_copy_handle()` of cURL handles with attached `CURLFile`s, we must not attach the opened stream, because the stream may not be seekable, so that we could rewind, when the same stream is going to be uploaded multiple times. Instead, we're opening the stream lazily in the read or seek callback. Since `curl_multi_perfom()` processes easy handles asynchronously, we have no control of the operation sequence. Since duplicated cURL handles may be used with multi handles, we cannot use a single arg structure, but actually have to rebuild the whole mime structure on handle duplication and attach this to the new handle. This requires to store the zval that has been originally provided for `CURLOPT_POSTFIELDS`. Ideally, we would store it in an additional member of `php_curl`, but since that would constitute a BC break, we work around by actually storing a union in `to_free->stream`, and we retrieve the appropriate zval by sequentially searching the list. In order to better test this behavior, we extend the test responder to print the size of the upload, and patch the existing tests accordingly. --- ext/curl/interface.c | 181 +++++++++++++----- ext/curl/tests/bug27023.phpt | 8 +- ext/curl/tests/bug77711.phpt | 2 +- .../tests/curl_copy_handle_variation3.phpt | 4 +- .../tests/curl_copy_handle_variation4.phpt | 46 +++++ .../tests/curl_copy_handle_variation5.phpt | 52 +++++ ext/curl/tests/curl_file_upload.phpt | 12 +- ext/curl/tests/curl_file_upload_stream.phpt | 2 +- ext/curl/tests/responder/get.inc | 2 +- 9 files changed, 244 insertions(+), 65 deletions(-) create mode 100644 ext/curl/tests/curl_copy_handle_variation4.phpt create mode 100644 ext/curl/tests/curl_copy_handle_variation5.phpt diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 9286777f5b28a..1b30c50351b76 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -1791,11 +1791,30 @@ static void curl_free_post(void **post) } /* }}} */ -/* {{{ curl_free_stream +struct mime_data_cb_arg { + php_curl *ch; + union { + zval postfields; + struct { + zend_string *filename; + php_stream *stream; + }; + }; +}; + +/* {{{ curl_free_cb_arg */ -static void curl_free_stream(void **post) +static void curl_free_cb_arg(void **cb_arg_p) { - php_stream_close((php_stream *)*post); + struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) *cb_arg_p; + + if (cb_arg->ch) { + Z_DELREF(cb_arg->postfields); + } else { + ZEND_ASSERT(cb_arg->stream == NULL); + zend_string_release(cb_arg->filename); + } + efree(cb_arg); } /* }}} */ @@ -1896,7 +1915,7 @@ php_curl *alloc_curl_handle() zend_llist_init(&ch->to_free->str, sizeof(char *), (llist_dtor_func_t)curl_free_string, 0); zend_llist_init(&ch->to_free->post, sizeof(struct HttpPost *), (llist_dtor_func_t)curl_free_post, 0); - zend_llist_init(&ch->to_free->stream, sizeof(php_stream *), (llist_dtor_func_t)curl_free_stream, 0); + zend_llist_init(&ch->to_free->stream, sizeof(struct mime_data_cb_arg *), (llist_dtor_func_t)curl_free_cb_arg, 0); ch->to_free->slist = emalloc(sizeof(HashTable)); zend_hash_init(ch->to_free->slist, 4, NULL, curl_free_slist, 0); @@ -2090,62 +2109,71 @@ void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source) (*source->clone)++; } -/* {{{ proto resource curl_copy_handle(resource ch) - Copy a cURL handle along with all of it's preferences */ -PHP_FUNCTION(curl_copy_handle) +#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ +static size_t read_cb(char *buffer, size_t size, size_t nitems, void *arg) /* {{{ */ { - CURL *cp; - zval *zid; - php_curl *ch, *dupch; + struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg; + ssize_t numread; - ZEND_PARSE_PARAMETERS_START(1,1) - Z_PARAM_RESOURCE(zid) - ZEND_PARSE_PARAMETERS_END(); + ZEND_ASSERT(!cb_arg->ch); - if ((ch = (php_curl*)zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) { - RETURN_FALSE; + if (cb_arg->stream == NULL) { + if (!(cb_arg->stream = php_stream_open_wrapper(ZSTR_VAL(cb_arg->filename), "rb", IGNORE_PATH, NULL))) { + return CURL_READFUNC_ABORT; + } } - - cp = curl_easy_duphandle(ch->cp); - if (!cp) { - php_error_docref(NULL, E_WARNING, "Cannot duplicate cURL handle"); - RETURN_FALSE; + numread = php_stream_read(cb_arg->stream, buffer, nitems * size); + if (numread < 0) { + php_stream_close(cb_arg->stream); + cb_arg->stream = NULL; } + return (numread >= 0) ? numread : CURL_READFUNC_ABORT; +} +/* }}} */ - dupch = alloc_curl_handle(); - dupch->cp = cp; - - _php_setup_easy_copy_handlers(dupch, ch); +static int seek_cb(void *arg, curl_off_t offset, int origin) /* {{{ */ +{ + struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg; + int res; - Z_ADDREF_P(zid); + ZEND_ASSERT(!cb_arg->ch); - ZVAL_RES(return_value, zend_register_resource(dupch, le_curl)); - dupch->res = Z_RES_P(return_value); + if (cb_arg->stream == NULL) { + if (!(cb_arg->stream = php_stream_open_wrapper(ZSTR_VAL(cb_arg->filename), "rb", IGNORE_PATH, NULL))) { + return CURL_SEEKFUNC_CANTSEEK; + } + } + res = php_stream_seek(cb_arg->stream, offset, origin); + return res == SUCCESS ? CURL_SEEKFUNC_OK : CURL_SEEKFUNC_CANTSEEK; } /* }}} */ -#if LIBCURL_VERSION_NUM >= 0x073800 -static size_t read_cb(char *buffer, size_t size, size_t nitems, void *arg) /* {{{ */ +static void free_cb(void *arg) /* {{{ */ { - php_stream *stream = (php_stream *) arg; - ssize_t numread = php_stream_read(stream, buffer, nitems * size); + struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg; - if (numread < 0) { - return CURL_READFUNC_ABORT; + ZEND_ASSERT(!cb_arg->ch); + + if (cb_arg->stream != NULL) { + php_stream_close(cb_arg->stream); + cb_arg->stream = NULL; } - return numread; } /* }}} */ -static int seek_cb(void *arg, curl_off_t offset, int origin) /* {{{ */ +static zval *get_postfields_of_handle(php_curl *ch) /* {{{ */ { - php_stream *stream = (php_stream *) arg; - int res = php_stream_seek(stream, offset, origin); + struct mime_data_cb_arg *cb_arg, **cb_arg_p; - if (res) { - return CURL_SEEKFUNC_CANTSEEK; + cb_arg_p = zend_llist_get_last(&ch->to_free->stream); + while (cb_arg_p) { + cb_arg = *cb_arg_p; + if (cb_arg->ch == ch) { + return &cb_arg->postfields; + } + cb_arg_p = zend_llist_get_prev(&ch->to_free->stream); } - return CURL_SEEKFUNC_OK; + return NULL; } /* }}} */ #endif @@ -2156,7 +2184,7 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields zval *current; HashTable *postfields; zend_string *string_key; - zend_ulong num_key; + zend_ulong num_key; #if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ curl_mime *mime = NULL; curl_mimepart *part; @@ -2198,7 +2226,7 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields zval *prop, rv; char *type = NULL, *filename = NULL; #if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ - php_stream *stream; + struct mime_data_cb_arg *cb_arg; #endif prop = zend_read_property(curl_CURLFile_class, current, "name", sizeof("name")-1, 0, &rv); @@ -2221,24 +2249,28 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields } #if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ - if (!(stream = php_stream_open_wrapper(ZSTR_VAL(postval), "rb", IGNORE_PATH, NULL))) { - zend_string_release_ex(string_key, 0); - return FAILURE; - } + cb_arg = emalloc(sizeof *cb_arg); + cb_arg->ch = ch; + ZVAL_COPY(&cb_arg->postfields, zpostfields); + zend_llist_add_element(&ch->to_free->stream, &cb_arg); + + cb_arg = emalloc(sizeof *cb_arg); + cb_arg->ch = NULL; + cb_arg->filename = zend_string_copy(postval); + cb_arg->stream = NULL; + part = curl_mime_addpart(mime); if (part == NULL) { - php_stream_close(stream); zend_string_release_ex(string_key, 0); return FAILURE; } if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK - || (form_error = curl_mime_data_cb(part, -1, read_cb, seek_cb, NULL, stream)) != CURLE_OK + || (form_error = curl_mime_data_cb(part, -1, read_cb, seek_cb, free_cb, cb_arg)) != CURLE_OK || (form_error = curl_mime_filename(part, filename ? filename : ZSTR_VAL(postval))) != CURLE_OK || (form_error = curl_mime_type(part, type ? type : "application/octet-stream")) != CURLE_OK) { - php_stream_close(stream); error = form_error; } - zend_llist_add_element(&ch->to_free->stream, &stream); + zend_llist_add_element(&ch->to_free->stream, &cb_arg); #else form_error = curl_formadd(&first, &last, CURLFORM_COPYNAME, ZSTR_VAL(string_key), @@ -2306,11 +2338,60 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields zend_llist_add_element(&ch->to_free->post, &first); error = curl_easy_setopt(ch->cp, CURLOPT_HTTPPOST, first); #endif + SAVE_CURL_ERROR(ch, error); return error == CURLE_OK ? SUCCESS : FAILURE; } /* }}} */ +/* {{{ proto resource curl_copy_handle(resource ch) + Copy a cURL handle along with all of it's preferences */ +PHP_FUNCTION(curl_copy_handle) +{ + CURL *cp; + zval *zid; + php_curl *ch, *dupch; +#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ + zval *postfields; +#endif + + ZEND_PARSE_PARAMETERS_START(1,1) + Z_PARAM_RESOURCE(zid) + ZEND_PARSE_PARAMETERS_END(); + + if ((ch = (php_curl*)zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) { + RETURN_FALSE; + } + + cp = curl_easy_duphandle(ch->cp); + if (!cp) { + php_error_docref(NULL, E_WARNING, "Cannot duplicate cURL handle"); + RETURN_FALSE; + } + + dupch = alloc_curl_handle(); + dupch->cp = cp; + + _php_setup_easy_copy_handlers(dupch, ch); + +#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ + postfields = get_postfields_of_handle(ch); + if (postfields) { + if (build_mime_structure_from_hash(dupch, postfields) != SUCCESS) { + _php_curl_close_ex(dupch); + php_error_docref(NULL, E_WARNING, "Cannot rebuild mime structure"); + RETURN_FALSE; + } + } +#endif + + Z_ADDREF_P(zid); + + ZVAL_RES(return_value, zend_register_resource(dupch, le_curl)); + dupch->res = Z_RES_P(return_value); +} +/* }}} */ + static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ */ { CURLcode error = CURLE_OK; diff --git a/ext/curl/tests/bug27023.phpt b/ext/curl/tests/bug27023.phpt index 3d649b3f738df..f985c192b84e3 100644 --- a/ext/curl/tests/bug27023.phpt +++ b/ext/curl/tests/bug27023.phpt @@ -38,7 +38,7 @@ var_dump(curl_exec($ch)); curl_close($ch); ?> --EXPECTF-- -string(%d) "curl_testdata1.txt|application/octet-stream" -string(%d) "curl_testdata1.txt|text/plain" -string(%d) "foo.txt|application/octet-stream" -string(%d) "foo.txt|text/plain" +string(%d) "curl_testdata1.txt|application/octet-stream|6" +string(%d) "curl_testdata1.txt|text/plain|6" +string(%d) "foo.txt|application/octet-stream|6" +string(%d) "foo.txt|text/plain|6" diff --git a/ext/curl/tests/bug77711.phpt b/ext/curl/tests/bug77711.phpt index 148c26322a64b..8ef5e48891943 100644 --- a/ext/curl/tests/bug77711.phpt +++ b/ext/curl/tests/bug77711.phpt @@ -24,7 +24,7 @@ curl_close($ch); ===DONE=== --EXPECTF-- bool(true) -string(%d) "АБВ.txt|application/octet-stream" +string(%d) "АБВ.txt|application/octet-stream|5" ===DONE=== --CLEAN-- +--FILE-- + $file); +var_dump(curl_setopt($ch1, CURLOPT_POSTFIELDS, $params)); + +$ch2 = curl_copy_handle($ch1); +$ch3 = curl_copy_handle($ch1); + +$mh = curl_multi_init(); +curl_multi_add_handle($mh, $ch1); +curl_multi_add_handle($mh, $ch2); +do { + $status = curl_multi_exec($mh, $active); + if ($active) { + curl_multi_select($mh); + } +} while ($active && $status == CURLM_OK); + +curl_multi_remove_handle($mh, $ch1); +curl_multi_remove_handle($mh, $ch2); +curl_multi_remove_handle($mh, $ch3); +curl_multi_close($mh); +?> +===DONE=== +--EXPECTF-- +bool(true) +АБВ.txt|application/octet-stream|5АБВ.txt|application/octet-stream|5===DONE=== +--CLEAN-- + diff --git a/ext/curl/tests/curl_copy_handle_variation5.phpt b/ext/curl/tests/curl_copy_handle_variation5.phpt new file mode 100644 index 0000000000000..019704e6c89f2 --- /dev/null +++ b/ext/curl/tests/curl_copy_handle_variation5.phpt @@ -0,0 +1,52 @@ +--TEST-- +curl_copy_handle() allows to post CURLFile multiple times if postfields change +--SKIPIF-- + +--FILE-- + $file); +var_dump(curl_setopt($ch1, CURLOPT_POSTFIELDS, $params)); + +$ch2 = curl_copy_handle($ch1); + +$filename = __DIR__ . '/def.txt'; +file_put_contents($filename, "Other test."); +$file = curl_file_create($filename); +$params = array('file' => $file); +var_dump(curl_setopt($ch2, CURLOPT_POSTFIELDS, $params)); + +$ch3 = curl_copy_handle($ch2); + +var_dump(curl_exec($ch1)); +curl_close($ch1); + +var_dump(curl_exec($ch2)); +curl_close($ch2); + +var_dump(curl_exec($ch3)); +curl_close($ch3); +?> +===DONE=== +--EXPECTF-- +bool(true) +bool(true) +string(%d) "abc.txt|application/octet-stream|5" +string(%d) "def.txt|application/octet-stream|11" +string(%d) "def.txt|application/octet-stream|11" +===DONE=== +--CLEAN-- + diff --git a/ext/curl/tests/curl_file_upload.phpt b/ext/curl/tests/curl_file_upload.phpt index 73a2f363fbbe6..1626f8117c394 100644 --- a/ext/curl/tests/curl_file_upload.phpt +++ b/ext/curl/tests/curl_file_upload.phpt @@ -60,15 +60,15 @@ var_dump(curl_exec($ch)); curl_close($ch); ?> --EXPECTF-- -string(%d) "curl_testdata1.txt|application/octet-stream" -string(%d) "curl_testdata1.txt|text/plain" -string(%d) "foo.txt|application/octet-stream" -string(%d) "foo.txt|text/plain" +string(%d) "curl_testdata1.txt|application/octet-stream|6" +string(%d) "curl_testdata1.txt|text/plain|6" +string(%d) "foo.txt|application/octet-stream|6" +string(%d) "foo.txt|text/plain|6" string(%d) "text/plain" string(%d) "%s/curl_testdata1.txt" -string(%d) "curl_testdata1.txt|text/plain" +string(%d) "curl_testdata1.txt|text/plain|6" string(%d) "foo.txt" -string(%d) "foo.txt|application/octet-stream" +string(%d) "foo.txt|application/octet-stream|6" Warning: curl_setopt(): Disabling safe uploads is no longer supported in %s on line %d string(0) "" diff --git a/ext/curl/tests/curl_file_upload_stream.phpt b/ext/curl/tests/curl_file_upload_stream.phpt index 03c85b4b82be9..949cccbc3120a 100644 --- a/ext/curl/tests/curl_file_upload_stream.phpt +++ b/ext/curl/tests/curl_file_upload_stream.phpt @@ -24,5 +24,5 @@ curl_close($ch); ===DONE=== --EXPECT-- bool(true) -string(21) "i-love-php|text/plain" +string(24) "i-love-php|text/plain|11" ===DONE=== diff --git a/ext/curl/tests/responder/get.inc b/ext/curl/tests/responder/get.inc index f9269745f6111..64ab267d50365 100644 --- a/ext/curl/tests/responder/get.inc +++ b/ext/curl/tests/responder/get.inc @@ -28,7 +28,7 @@ break; case 'file': if (isset($_FILES['file'])) { - echo $_FILES['file']['name'] . '|' . $_FILES['file']['type']; + echo $_FILES['file']['name'] . '|' . $_FILES['file']['type'] . '|' . $_FILES['file']['size']; } break; case 'method': From c158a91cad4417c3134725d324524de40fb3f0ee Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 2 Feb 2020 15:17:11 +0100 Subject: [PATCH 2/4] Properly free the postfields --- ext/curl/interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 1b30c50351b76..9875813a2b0a1 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -1809,7 +1809,7 @@ static void curl_free_cb_arg(void **cb_arg_p) struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) *cb_arg_p; if (cb_arg->ch) { - Z_DELREF(cb_arg->postfields); + zval_ptr_dtor(&cb_arg->postfields); } else { ZEND_ASSERT(cb_arg->stream == NULL); zend_string_release(cb_arg->filename); From 672a8c690f586f9205a627ba550af7bec608fa6c Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 2 Feb 2020 15:33:43 +0100 Subject: [PATCH 3/4] Don't try to open stream in seek_cb() According to the `curl_mime_data_cb()` man page[1], the seek callback is only called when resending due to redirection. In this case the stream would already have been opened, so there is no need to try to open the stream in `seek_cb()`. [1] --- ext/curl/interface.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 9875813a2b0a1..67c382788d31b 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -2139,9 +2139,7 @@ static int seek_cb(void *arg, curl_off_t offset, int origin) /* {{{ */ ZEND_ASSERT(!cb_arg->ch); if (cb_arg->stream == NULL) { - if (!(cb_arg->stream = php_stream_open_wrapper(ZSTR_VAL(cb_arg->filename), "rb", IGNORE_PATH, NULL))) { - return CURL_SEEKFUNC_CANTSEEK; - } + return CURL_SEEKFUNC_CANTSEEK; } res = php_stream_seek(cb_arg->stream, offset, origin); return res == SUCCESS ? CURL_SEEKFUNC_OK : CURL_SEEKFUNC_CANTSEEK; From b626a3a15dba75be294a904a8a56dca7506fc8fe Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Tue, 4 Feb 2020 09:30:57 +0100 Subject: [PATCH 4/4] Use additional member of php_curl struct instead of hack --- ext/curl/interface.c | 58 +++++++++++--------------------------------- ext/curl/php_curl.h | 3 +++ 2 files changed, 17 insertions(+), 44 deletions(-) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 67c382788d31b..31cac9c86c155 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -1792,14 +1792,8 @@ static void curl_free_post(void **post) /* }}} */ struct mime_data_cb_arg { - php_curl *ch; - union { - zval postfields; - struct { - zend_string *filename; - php_stream *stream; - }; - }; + zend_string *filename; + php_stream *stream; }; /* {{{ curl_free_cb_arg @@ -1808,12 +1802,8 @@ static void curl_free_cb_arg(void **cb_arg_p) { struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) *cb_arg_p; - if (cb_arg->ch) { - zval_ptr_dtor(&cb_arg->postfields); - } else { - ZEND_ASSERT(cb_arg->stream == NULL); - zend_string_release(cb_arg->filename); - } + ZEND_ASSERT(cb_arg->stream == NULL); + zend_string_release(cb_arg->filename); efree(cb_arg); } /* }}} */ @@ -1919,7 +1909,9 @@ php_curl *alloc_curl_handle() ch->to_free->slist = emalloc(sizeof(HashTable)); zend_hash_init(ch->to_free->slist, 4, NULL, curl_free_slist, 0); - +#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ + ZVAL_UNDEF(&ch->postfields); +#endif return ch; } /* }}} */ @@ -2115,8 +2107,6 @@ static size_t read_cb(char *buffer, size_t size, size_t nitems, void *arg) /* {{ struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg; ssize_t numread; - ZEND_ASSERT(!cb_arg->ch); - if (cb_arg->stream == NULL) { if (!(cb_arg->stream = php_stream_open_wrapper(ZSTR_VAL(cb_arg->filename), "rb", IGNORE_PATH, NULL))) { return CURL_READFUNC_ABORT; @@ -2136,8 +2126,6 @@ static int seek_cb(void *arg, curl_off_t offset, int origin) /* {{{ */ struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg; int res; - ZEND_ASSERT(!cb_arg->ch); - if (cb_arg->stream == NULL) { return CURL_SEEKFUNC_CANTSEEK; } @@ -2150,30 +2138,12 @@ static void free_cb(void *arg) /* {{{ */ { struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg; - ZEND_ASSERT(!cb_arg->ch); - if (cb_arg->stream != NULL) { php_stream_close(cb_arg->stream); cb_arg->stream = NULL; } } /* }}} */ - -static zval *get_postfields_of_handle(php_curl *ch) /* {{{ */ -{ - struct mime_data_cb_arg *cb_arg, **cb_arg_p; - - cb_arg_p = zend_llist_get_last(&ch->to_free->stream); - while (cb_arg_p) { - cb_arg = *cb_arg_p; - if (cb_arg->ch == ch) { - return &cb_arg->postfields; - } - cb_arg_p = zend_llist_get_prev(&ch->to_free->stream); - } - return NULL; -} -/* }}} */ #endif static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields) /* {{{ */ @@ -2247,13 +2217,10 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields } #if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ - cb_arg = emalloc(sizeof *cb_arg); - cb_arg->ch = ch; - ZVAL_COPY(&cb_arg->postfields, zpostfields); - zend_llist_add_element(&ch->to_free->stream, &cb_arg); + zval_ptr_dtor(&ch->postfields); + ZVAL_COPY(&ch->postfields, zpostfields); cb_arg = emalloc(sizeof *cb_arg); - cb_arg->ch = NULL; cb_arg->filename = zend_string_copy(postval); cb_arg->stream = NULL; @@ -2373,8 +2340,8 @@ PHP_FUNCTION(curl_copy_handle) _php_setup_easy_copy_handlers(dupch, ch); #if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ - postfields = get_postfields_of_handle(ch); - if (postfields) { + postfields = &ch->postfields; + if (Z_TYPE_P(postfields) != IS_UNDEF) { if (build_mime_structure_from_hash(dupch, postfields) != SUCCESS) { _php_curl_close_ex(dupch); php_error_docref(NULL, E_WARNING, "Cannot rebuild mime structure"); @@ -3685,6 +3652,9 @@ static void _php_curl_close_ex(php_curl *ch) #endif efree(ch->handlers); +#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ + zval_ptr_dtor(&ch->postfields); +#endif efree(ch); } /* }}} */ diff --git a/ext/curl/php_curl.h b/ext/curl/php_curl.h index 5d93f16c03929..f4f722e13427d 100644 --- a/ext/curl/php_curl.h +++ b/ext/curl/php_curl.h @@ -183,6 +183,9 @@ typedef struct { struct _php_curl_error err; zend_bool in_callback; uint32_t* clone; +#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ + zval postfields; +#endif } php_curl; #define CURLOPT_SAFE_UPLOAD -1