Skip to content

Commit 022fc17

Browse files
committed
Post multiple similarly-named files with curl
php#15859
1 parent 29cec7f commit 022fc17

2 files changed

Lines changed: 210 additions & 122 deletions

File tree

ext/curl/interface.c

Lines changed: 144 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,165 +1361,189 @@ static inline CURLcode add_simple_field(curl_mime *mime, zend_string *string_key
13611361
return error;
13621362
}
13631363

1364-
static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields) /* {{{ */
1365-
{
1366-
HashTable *postfields = Z_ARRVAL_P(zpostfields);
1364+
static inline CURLcode add_form_field(php_curl *ch, curl_mime *mime, zend_string *string_key, zval *current, zval *zpostfields) {
1365+
zend_string *postval;
13671366
CURLcode error = CURLE_OK;
1368-
zval *current;
1369-
zend_string *string_key;
1370-
zend_ulong num_key;
1371-
curl_mime *mime = NULL;
1372-
curl_mimepart *part;
13731367
CURLcode form_error;
1368+
curl_mimepart *part;
13741369

1375-
if (zend_hash_num_elements(postfields) > 0) {
1376-
mime = curl_mime_init(ch->cp);
1377-
if (mime == NULL) {
1378-
return FAILURE;
1379-
}
1380-
}
1381-
1382-
ZEND_HASH_FOREACH_KEY_VAL(postfields, num_key, string_key, current) {
1383-
zend_string *postval;
1384-
/* Pretend we have a string_key here */
1385-
if (!string_key) {
1386-
string_key = zend_long_to_str(num_key);
1370+
ZVAL_DEREF(current);
1371+
if (Z_TYPE_P(current) == IS_OBJECT &&
1372+
instanceof_function(Z_OBJCE_P(current), curl_CURLFile_class)) {
1373+
/* new-style file upload */
1374+
zval *prop, rv;
1375+
char *type = NULL, *filename = NULL;
1376+
struct mime_data_cb_arg *cb_arg;
1377+
php_stream_statbuf ssb;
1378+
size_t filesize = -1;
1379+
curl_seek_callback seekfunc = seek_cb;
1380+
1381+
prop = zend_read_property_ex(curl_CURLFile_class, Z_OBJ_P(current), ZSTR_KNOWN(ZEND_STR_NAME), /* silent */ false, &rv);
1382+
ZVAL_DEREF(prop);
1383+
if (Z_TYPE_P(prop) != IS_STRING) {
1384+
php_error_docref(NULL, E_WARNING, "Invalid filename for key %s", ZSTR_VAL(string_key));
13871385
} else {
1388-
zend_string_addref(string_key);
1389-
}
1386+
postval = Z_STR_P(prop);
13901387

1391-
ZVAL_DEREF(current);
1392-
if (Z_TYPE_P(current) == IS_OBJECT &&
1393-
instanceof_function(Z_OBJCE_P(current), curl_CURLFile_class)) {
1394-
/* new-style file upload */
1395-
zval *prop, rv;
1396-
char *type = NULL, *filename = NULL;
1397-
struct mime_data_cb_arg *cb_arg;
1398-
php_stream_statbuf ssb;
1399-
size_t filesize = -1;
1400-
curl_seek_callback seekfunc = seek_cb;
1401-
1402-
prop = zend_read_property_ex(curl_CURLFile_class, Z_OBJ_P(current), ZSTR_KNOWN(ZEND_STR_NAME), /* silent */ false, &rv);
1388+
if (php_check_open_basedir(ZSTR_VAL(postval))) {
1389+
goto out_string;
1390+
}
1391+
1392+
prop = zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), "mime", sizeof("mime")-1, 0, &rv);
14031393
ZVAL_DEREF(prop);
1404-
if (Z_TYPE_P(prop) != IS_STRING) {
1405-
php_error_docref(NULL, E_WARNING, "Invalid filename for key %s", ZSTR_VAL(string_key));
1406-
} else {
1407-
postval = Z_STR_P(prop);
1394+
if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) {
1395+
type = Z_STRVAL_P(prop);
1396+
}
1397+
prop = zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), "postname", sizeof("postname")-1, 0, &rv);
1398+
ZVAL_DEREF(prop);
1399+
if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) {
1400+
filename = Z_STRVAL_P(prop);
1401+
}
14081402

1409-
if (php_check_open_basedir(ZSTR_VAL(postval))) {
1410-
goto out_string;
1411-
}
1403+
zval_ptr_dtor(&ch->postfields);
1404+
ZVAL_COPY(&ch->postfields, zpostfields);
14121405

1413-
prop = zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), "mime", sizeof("mime")-1, 0, &rv);
1414-
ZVAL_DEREF(prop);
1415-
if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) {
1416-
type = Z_STRVAL_P(prop);
1406+
php_stream *stream;
1407+
if ((stream = php_stream_open_wrapper(ZSTR_VAL(postval), "rb", STREAM_MUST_SEEK, NULL))) {
1408+
if (!stream->readfilters.head && !php_stream_stat(stream, &ssb)) {
1409+
filesize = ssb.sb.st_size;
14171410
}
1418-
prop = zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), "postname", sizeof("postname")-1, 0, &rv);
1419-
ZVAL_DEREF(prop);
1420-
if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) {
1421-
filename = Z_STRVAL_P(prop);
1411+
} else {
1412+
seekfunc = NULL;
1413+
}
1414+
1415+
part = curl_mime_addpart(mime);
1416+
if (part == NULL) {
1417+
if (stream) {
1418+
php_stream_close(stream);
14221419
}
1420+
goto out_string;
1421+
}
14231422

1424-
zval_ptr_dtor(&ch->postfields);
1425-
ZVAL_COPY(&ch->postfields, zpostfields);
1423+
cb_arg = emalloc(sizeof *cb_arg);
1424+
cb_arg->filename = zend_string_copy(postval);
1425+
cb_arg->stream = stream;
14261426

1427-
php_stream *stream;
1428-
if ((stream = php_stream_open_wrapper(ZSTR_VAL(postval), "rb", STREAM_MUST_SEEK, NULL))) {
1429-
if (!stream->readfilters.head && !php_stream_stat(stream, &ssb)) {
1430-
filesize = ssb.sb.st_size;
1431-
}
1432-
} else {
1433-
seekfunc = NULL;
1434-
}
1427+
if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK
1428+
|| (form_error = curl_mime_data_cb(part, filesize, read_cb, seekfunc, free_cb, cb_arg)) != CURLE_OK
1429+
|| (form_error = curl_mime_filename(part, filename ? filename : ZSTR_VAL(postval))) != CURLE_OK
1430+
|| (form_error = curl_mime_type(part, type ? type : "application/octet-stream")) != CURLE_OK) {
1431+
error = form_error;
1432+
}
1433+
zend_llist_add_element(&ch->to_free->stream, &cb_arg);
1434+
}
14351435

1436-
part = curl_mime_addpart(mime);
1437-
if (part == NULL) {
1438-
if (stream) {
1439-
php_stream_close(stream);
1440-
}
1441-
goto out_string;
1442-
}
1436+
zend_string_release_ex(string_key, 0);
1437+
return error;
1438+
}
14431439

1444-
cb_arg = emalloc(sizeof *cb_arg);
1445-
cb_arg->filename = zend_string_copy(postval);
1446-
cb_arg->stream = stream;
1440+
if (Z_TYPE_P(current) == IS_OBJECT && instanceof_function(Z_OBJCE_P(current), curl_CURLStringFile_class)) {
1441+
/* new-style file upload from string */
1442+
zval *prop, rv;
1443+
char *type = NULL, *filename = NULL;
14471444

1448-
if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK
1449-
|| (form_error = curl_mime_data_cb(part, filesize, read_cb, seekfunc, free_cb, cb_arg)) != CURLE_OK
1450-
|| (form_error = curl_mime_filename(part, filename ? filename : ZSTR_VAL(postval))) != CURLE_OK
1451-
|| (form_error = curl_mime_type(part, type ? type : "application/octet-stream")) != CURLE_OK) {
1452-
error = form_error;
1453-
}
1454-
zend_llist_add_element(&ch->to_free->stream, &cb_arg);
1455-
}
1445+
prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "postname", sizeof("postname")-1, 0, &rv);
1446+
if (EG(exception)) {
1447+
goto out_string;
1448+
}
1449+
ZVAL_DEREF(prop);
1450+
ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);
14561451

1457-
zend_string_release_ex(string_key, 0);
1458-
continue;
1452+
filename = Z_STRVAL_P(prop);
1453+
1454+
prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "mime", sizeof("mime")-1, 0, &rv);
1455+
if (EG(exception)) {
1456+
goto out_string;
14591457
}
1458+
ZVAL_DEREF(prop);
1459+
ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);
14601460

1461-
if (Z_TYPE_P(current) == IS_OBJECT && instanceof_function(Z_OBJCE_P(current), curl_CURLStringFile_class)) {
1462-
/* new-style file upload from string */
1463-
zval *prop, rv;
1464-
char *type = NULL, *filename = NULL;
1461+
type = Z_STRVAL_P(prop);
14651462

1466-
prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "postname", sizeof("postname")-1, 0, &rv);
1467-
if (EG(exception)) {
1468-
goto out_string;
1469-
}
1470-
ZVAL_DEREF(prop);
1471-
ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);
1463+
prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "data", sizeof("data")-1, 0, &rv);
1464+
if (EG(exception)) {
1465+
goto out_string;
1466+
}
1467+
ZVAL_DEREF(prop);
1468+
ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);
14721469

1473-
filename = Z_STRVAL_P(prop);
1470+
postval = Z_STR_P(prop);
14741471

1475-
prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "mime", sizeof("mime")-1, 0, &rv);
1476-
if (EG(exception)) {
1477-
goto out_string;
1478-
}
1479-
ZVAL_DEREF(prop);
1480-
ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);
1472+
zval_ptr_dtor(&ch->postfields);
1473+
ZVAL_COPY(&ch->postfields, zpostfields);
14811474

1482-
type = Z_STRVAL_P(prop);
1475+
part = curl_mime_addpart(mime);
1476+
if (part == NULL) {
1477+
goto out_string;
1478+
}
1479+
if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK
1480+
|| (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK
1481+
|| (form_error = curl_mime_filename(part, filename)) != CURLE_OK
1482+
|| (form_error = curl_mime_type(part, type)) != CURLE_OK) {
1483+
error = form_error;
1484+
}
14831485

1484-
prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "data", sizeof("data")-1, 0, &rv);
1485-
if (EG(exception)) {
1486-
goto out_string;
1487-
}
1488-
ZVAL_DEREF(prop);
1489-
ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);
1486+
zend_string_release_ex(string_key, 0);
1487+
return error;
1488+
}
14901489

1491-
postval = Z_STR_P(prop);
1490+
return add_simple_field(mime, string_key, current);
14921491

1493-
zval_ptr_dtor(&ch->postfields);
1494-
ZVAL_COPY(&ch->postfields, zpostfields);
1492+
out_string:
1493+
zend_string_release_ex(string_key, false);
1494+
return CURLE_FAILED_INIT;
1495+
}
14951496

1496-
part = curl_mime_addpart(mime);
1497-
if (part == NULL) {
1498-
goto out_string;
1499-
}
1500-
if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK
1501-
|| (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK
1502-
|| (form_error = curl_mime_filename(part, filename)) != CURLE_OK
1503-
|| (form_error = curl_mime_type(part, type)) != CURLE_OK) {
1504-
error = form_error;
1505-
}
15061497

1507-
zend_string_release_ex(string_key, 0);
1508-
continue;
1498+
static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields) /* {{{ */
1499+
{
1500+
HashTable *postfields = Z_ARRVAL_P(zpostfields);
1501+
CURLcode error = CURLE_OK;
1502+
zval *current;
1503+
zend_string *string_key;
1504+
zend_ulong num_key;
1505+
curl_mime *mime = NULL;
1506+
1507+
if (zend_hash_num_elements(postfields) > 0) {
1508+
mime = curl_mime_init(ch->cp);
1509+
if (mime == NULL) {
1510+
return FAILURE;
15091511
}
1512+
}
1513+
1514+
ZEND_HASH_FOREACH_KEY_VAL(postfields, num_key, string_key, current) {
1515+
/* Pretend we have a string_key here */
1516+
if (!string_key) {
1517+
string_key = zend_long_to_str(num_key);
1518+
} else {
1519+
zend_string_addref(string_key);
1520+
}
1521+
1522+
ZVAL_DEREF(current);
15101523

15111524
if (Z_TYPE_P(current) == IS_ARRAY) {
15121525
zval *current_element;
15131526

15141527
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(current), current_element) {
1515-
add_simple_field(mime, string_key, current_element);
1528+
ZVAL_DEREF(current_element);
1529+
1530+
error = add_form_field(
1531+
ch, mime, string_key, current_element, zpostfields
1532+
);
1533+
1534+
if (error != CURLE_OK) {
1535+
zend_string_release_ex(string_key, 0);
1536+
goto out_mime;
1537+
}
15161538
} ZEND_HASH_FOREACH_END();
15171539

15181540
zend_string_release_ex(string_key, 0);
15191541
continue;
15201542
}
15211543

1522-
add_simple_field(mime, string_key, current);
1544+
error = add_form_field(
1545+
ch, mime, string_key, current, zpostfields
1546+
);
15231547

15241548
zend_string_release_ex(string_key, 0);
15251549
} ZEND_HASH_FOREACH_END();
@@ -1538,8 +1562,6 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo
15381562
SAVE_CURL_ERROR(ch, error);
15391563
return error == CURLE_OK ? SUCCESS : FAILURE;
15401564

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

0 commit comments

Comments
 (0)