Skip to content

Commit 553dd5d

Browse files
committed
Merge branch 'bug51634-curl-postfields-multi-value'
2 parents 4117063 + 8093ed6 commit 553dd5d

File tree

2 files changed

+121
-27
lines changed

2 files changed

+121
-27
lines changed

ext/curl/interface.c

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1991,6 +1991,54 @@ static void free_cb(void *arg) /* {{{ */
19911991
/* }}} */
19921992
#endif
19931993

1994+
static inline CURLcode add_simple_field(curl_mime *mime, zend_string *string_key, zval *current)
1995+
{
1996+
CURLcode error = CURLE_OK;
1997+
#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
1998+
curl_mimepart *part;
1999+
CURLcode form_error;
2000+
#else
2001+
struct HttpPost *first = NULL;
2002+
struct HttpPost *last = NULL;
2003+
CURLFORMcode form_error;
2004+
#endif
2005+
zend_string *postval, *tmp_postval;
2006+
2007+
postval = zval_get_tmp_string(current, &tmp_postval);
2008+
2009+
#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
2010+
part = curl_mime_addpart(mime);
2011+
if (part == NULL) {
2012+
zend_tmp_string_release(tmp_postval);
2013+
zend_string_release_ex(string_key, 0);
2014+
return CURLE_OUT_OF_MEMORY;
2015+
}
2016+
if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK
2017+
|| (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK) {
2018+
error = form_error;
2019+
}
2020+
#else
2021+
/* The arguments after _NAMELENGTH and _CONTENTSLENGTH
2022+
* must be explicitly cast to long in curl_formadd
2023+
* use since curl needs a long not an int. */
2024+
form_error = curl_formadd(&first, &last,
2025+
CURLFORM_COPYNAME, ZSTR_VAL(string_key),
2026+
CURLFORM_NAMELENGTH, ZSTR_LEN(string_key),
2027+
CURLFORM_COPYCONTENTS, ZSTR_VAL(postval),
2028+
CURLFORM_CONTENTSLENGTH, ZSTR_LEN(postval),
2029+
CURLFORM_END
2030+
);
2031+
2032+
if (form_error != CURL_FORMADD_OK) {
2033+
/* Not nice to convert between enums but we only have place for one error type */
2034+
error = (CURLcode)form_error;
2035+
}
2036+
#endif
2037+
zend_tmp_string_release(tmp_postval);
2038+
2039+
return error;
2040+
}
2041+
19942042
static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields) /* {{{ */
19952043
{
19962044
HashTable *postfields = Z_ARRVAL_P(zpostfields);
@@ -2018,7 +2066,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo
20182066
#endif
20192067

20202068
ZEND_HASH_FOREACH_KEY_VAL(postfields, num_key, string_key, current) {
2021-
zend_string *postval, *tmp_postval;
2069+
zend_string *postval;
20222070
/* Pretend we have a string_key here */
20232071
if (!string_key) {
20242072
string_key = zend_long_to_str(num_key);
@@ -2181,36 +2229,19 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo
21812229
continue;
21822230
}
21832231

2184-
postval = zval_get_tmp_string(current, &tmp_postval);
2232+
if (Z_TYPE_P(current) == IS_ARRAY) {
2233+
zval *current_element;
2234+
2235+
ZEND_HASH_FOREACH_VAL(HASH_OF(current), current_element) {
2236+
add_simple_field(mime, string_key, current_element);
2237+
} ZEND_HASH_FOREACH_END();
21852238

2186-
#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
2187-
part = curl_mime_addpart(mime);
2188-
if (part == NULL) {
2189-
zend_tmp_string_release(tmp_postval);
21902239
zend_string_release_ex(string_key, 0);
2191-
return FAILURE;
2192-
}
2193-
if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK
2194-
|| (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK) {
2195-
error = form_error;
2240+
continue;
21962241
}
2197-
#else
2198-
/* The arguments after _NAMELENGTH and _CONTENTSLENGTH
2199-
* must be explicitly cast to long in curl_formadd
2200-
* use since curl needs a long not an int. */
2201-
form_error = curl_formadd(&first, &last,
2202-
CURLFORM_COPYNAME, ZSTR_VAL(string_key),
2203-
CURLFORM_NAMELENGTH, ZSTR_LEN(string_key),
2204-
CURLFORM_COPYCONTENTS, ZSTR_VAL(postval),
2205-
CURLFORM_CONTENTSLENGTH, ZSTR_LEN(postval),
2206-
CURLFORM_END);
22072242

2208-
if (form_error != CURL_FORMADD_OK) {
2209-
/* Not nice to convert between enums but we only have place for one error type */
2210-
error = (CURLcode)form_error;
2211-
}
2212-
#endif
2213-
zend_tmp_string_release(tmp_postval);
2243+
add_simple_field(mime, string_key, current);
2244+
22142245
zend_string_release_ex(string_key, 0);
22152246
} ZEND_HASH_FOREACH_END();
22162247

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
--TEST--
2+
CURLOPT_POSTFIELDS with multi-value fields
3+
--EXTENSIONS--
4+
sockets
5+
--FILE--
6+
<?php
7+
$socket = stream_socket_server("tcp://0.0.0.0:29999", $errno, $errstr);
8+
9+
if (!$socket) {
10+
echo "$errstr ($errno)<br />\n";
11+
return;
12+
}
13+
14+
$url = "http://127.0.0.1:29999/get.inc?test=raw";
15+
16+
$fields = [
17+
'single' => 'SingleValue',
18+
'multi' => [
19+
'Multi1',
20+
'Multi2',
21+
]
22+
];
23+
24+
$options = [
25+
CURLOPT_POST => 1,
26+
CURLOPT_HEADER => 0,
27+
CURLOPT_URL => $url,
28+
CURLOPT_FRESH_CONNECT => 1,
29+
CURLOPT_RETURNTRANSFER => 1,
30+
CURLOPT_FORBID_REUSE => 1,
31+
CURLOPT_TIMEOUT => 1,
32+
CURLOPT_POSTFIELDS => $fields,
33+
];
34+
35+
$ch = curl_init();
36+
curl_setopt_array($ch, $options);
37+
38+
$curl_content = curl_exec($ch);
39+
curl_close($ch);
40+
41+
$conn = stream_socket_accept($socket);
42+
echo stream_get_contents($conn);
43+
?>
44+
--EXPECTF--
45+
POST /get.inc?test=raw HTTP/1.1
46+
Host: %s
47+
Accept: */*
48+
Content-Length: %d
49+
Content-Type: multipart/form-data; boundary=------------------------%s
50+
51+
--------------------------%s
52+
Content-Disposition: form-data; name="single"
53+
54+
SingleValue
55+
--------------------------%s
56+
Content-Disposition: form-data; name="multi"
57+
58+
Multi1
59+
--------------------------%s
60+
Content-Disposition: form-data; name="multi"
61+
62+
Multi2
63+
--------------------------%s--

0 commit comments

Comments
 (0)