From 6319f954ca0674c77bb1f8f8409c9ac841fe301d Mon Sep 17 00:00:00 2001 From: Alexander Moskalev Date: Fri, 8 May 2015 16:25:39 +0300 Subject: [PATCH 1/3] curl: add support to upload files from buffer string --- ext/curl/curl_file.c | 102 +++++++++++++++++++----- ext/curl/interface.c | 87 ++++++++++++++------ ext/curl/php_curl.h | 1 + ext/curl/tests/curl_file_serialize.phpt | 23 +++++- ext/curl/tests/curl_file_upload.phpt | 53 ++++++++++++ 5 files changed, 220 insertions(+), 46 deletions(-) diff --git a/ext/curl/curl_file.c b/ext/curl/curl_file.c index f2937b4f0ddac..8c3f25c7038d8 100644 --- a/ext/curl/curl_file.c +++ b/ext/curl/curl_file.c @@ -29,35 +29,41 @@ PHP_CURL_API zend_class_entry *curl_CURLFile_class; -static void curlfile_ctor(INTERNAL_FUNCTION_PARAMETERS) +static void curlfile_ctor(char *fname, size_t fname_len, char *mime, size_t mime_len, char *postname, size_t postname_len, char *buffer, size_t buffer_len, INTERNAL_FUNCTION_PARAMETERS) { - char *fname = NULL, *mime = NULL, *postname = NULL; - size_t fname_len, mime_len, postname_len; zval *cf = return_value; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ss", &fname, &fname_len, &mime, &mime_len, &postname, &postname_len) == FAILURE) { - return; - } - if (fname) { - zend_update_property_string(curl_CURLFile_class, cf, "name", sizeof("name")-1, fname); + zend_update_property_stringl(curl_CURLFile_class, cf, "name", sizeof("name")-1, fname, fname_len); } if (mime) { - zend_update_property_string(curl_CURLFile_class, cf, "mime", sizeof("mime")-1, mime); + zend_update_property_stringl(curl_CURLFile_class, cf, "mime", sizeof("mime")-1, mime, mime_len); } if (postname) { - zend_update_property_string(curl_CURLFile_class, cf, "postname", sizeof("postname")-1, postname); + zend_update_property_stringl(curl_CURLFile_class, cf, "postname", sizeof("postname")-1, postname, postname_len); + } + + if (buffer) { + zend_update_property_stringl(curl_CURLFile_class, cf, "buffer", sizeof("buffer")-1, buffer, buffer_len); } } -/* {{{ proto void CURLFile::__construct(string $name, [string $mimetype [, string $postfilename]]) +/* {{{ proto void CURLFile::__construct([string $name, [string $mimetype [, string $postfilename]]]) Create the CURLFile object */ ZEND_METHOD(CURLFile, __construct) { + char *fname = NULL, *mime = NULL, *postname = NULL, *buffer = NULL; + size_t fname_len, mime_len, postname_len, buffer_len; + return_value = getThis(); - curlfile_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sss", &fname, &fname_len, &mime, &mime_len, &postname, &postname_len) == FAILURE) { + return; + } + + curlfile_ctor(fname, fname_len, mime, mime_len, postname, postname_len, buffer, buffer_len, INTERNAL_FUNCTION_PARAM_PASSTHRU); } /* }}} */ @@ -65,8 +71,33 @@ ZEND_METHOD(CURLFile, __construct) Create the CURLFile object */ PHP_FUNCTION(curl_file_create) { - object_init_ex( return_value, curl_CURLFile_class ); - curlfile_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU); + char *fname = NULL, *mime = NULL, *postname = NULL, *buffer = NULL; + size_t fname_len, mime_len, postname_len, buffer_len; + + object_init_ex( return_value, curl_CURLFile_class ); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ss", &fname, &fname_len, &mime, &mime_len, &postname, &postname_len) == FAILURE) { + return; + } + + curlfile_ctor(fname, fname_len, mime, mime_len, postname, postname_len, buffer, buffer_len, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto CURLBufferFile curl_buffer_file_create(string $buffer, string $postfilename, [string $mimetype]) + Create the CURLBufferFile object */ +PHP_FUNCTION(curl_buffer_file_create) +{ + char *fname = NULL, *mime = NULL, *postname = NULL, *buffer = NULL; + size_t fname_len, mime_len, postname_len, buffer_len; + + object_init_ex( return_value, curl_CURLFile_class ); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|s", &buffer, &buffer_len, &postname, &postname_len, &mime, &mime_len) == FAILURE) { + return; + } + + curlfile_ctor(fname, fname_len, mime, mime_len, postname, postname_len, buffer, buffer_len, INTERNAL_FUNCTION_PARAM_PASSTHRU); } /* }}} */ @@ -89,7 +120,7 @@ static void curlfile_set_property(char *name, INTERNAL_FUNCTION_PARAMETERS) if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) == FAILURE) { return; } - zend_update_property_string(curl_CURLFile_class, getThis(), name, strlen(name), arg); + zend_update_property_stringl(curl_CURLFile_class, getThis(), name, strlen(name), arg, arg_len); } /* {{{ proto string CURLFile::getFilename() @@ -100,6 +131,14 @@ ZEND_METHOD(CURLFile, getFilename) } /* }}} */ +/* {{{ proto string CURLFile::getBuffer() + Get buffer */ +ZEND_METHOD(CURLFile, getBuffer) +{ + curlfile_get_property("buffer", INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + /* {{{ proto string CURLFile::getMimeType() Get MIME type */ ZEND_METHOD(CURLFile, getMimeType) @@ -116,6 +155,22 @@ ZEND_METHOD(CURLFile, getPostFilename) } /* }}} */ +/* {{{ proto void CURLFile::setFilename(string $name) + Set file name */ +ZEND_METHOD(CURLFile, setFilename) +{ + curlfile_set_property("name", INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto void CURLFile::setBuffer(string $buffer) + Set buffer */ +ZEND_METHOD(CURLFile, setBuffer) +{ + curlfile_set_property("buffer", INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + /* {{{ proto void CURLFile::setMimeType(string $mime) Set MIME type */ ZEND_METHOD(CURLFile, setMimeType) @@ -136,12 +191,17 @@ ZEND_METHOD(CURLFile, setPostFilename) Unserialization handler */ ZEND_METHOD(CURLFile, __wakeup) { - zend_update_property_string(curl_CURLFile_class, getThis(), "name", sizeof("name")-1, ""); - zend_throw_exception(NULL, "Unserialization of CURLFile instances is not allowed", 0); + zval *fname, rv; + + fname = zend_read_property(curl_CURLFile_class, getThis(), "name", strlen("name"), 1, &rv); + if (zval_is_true(fname)) { + zend_update_property_string(curl_CURLFile_class, getThis(), "name", sizeof("name")-1, ""); + zend_throw_exception(NULL, "Unserialization of CURLFile instances with file name is not allowed", 0); + } } /* }}} */ -ZEND_BEGIN_ARG_INFO_EX(arginfo_curlfile_create, 0, 0, 1) +ZEND_BEGIN_ARG_INFO_EX(arginfo_curlfile_ctor, 0, 0, 0) ZEND_ARG_INFO(0, filename) ZEND_ARG_INFO(0, mimetype) ZEND_ARG_INFO(0, postname) @@ -153,8 +213,11 @@ ZEND_END_ARG_INFO() static const zend_function_entry curlfile_funcs[] = { - PHP_ME(CURLFile, __construct, arginfo_curlfile_create, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC) + PHP_ME(CURLFile, __construct, arginfo_curlfile_ctor, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC) PHP_ME(CURLFile, getFilename, NULL, ZEND_ACC_PUBLIC) + PHP_ME(CURLFile, setFilename, arginfo_curlfile_name, ZEND_ACC_PUBLIC) + PHP_ME(CURLFile, getBuffer, NULL, ZEND_ACC_PUBLIC) + PHP_ME(CURLFile, setBuffer, arginfo_curlfile_name, ZEND_ACC_PUBLIC) PHP_ME(CURLFile, getMimeType, NULL, ZEND_ACC_PUBLIC) PHP_ME(CURLFile, setMimeType, arginfo_curlfile_name, ZEND_ACC_PUBLIC) PHP_ME(CURLFile, getPostFilename, NULL, ZEND_ACC_PUBLIC) @@ -169,6 +232,7 @@ void curlfile_register_class(void) INIT_CLASS_ENTRY( ce, "CURLFile", curlfile_funcs ); curl_CURLFile_class = zend_register_internal_class(&ce); zend_declare_property_string(curl_CURLFile_class, "name", sizeof("name")-1, "", ZEND_ACC_PUBLIC); + zend_declare_property_string(curl_CURLFile_class, "buffer", sizeof("buffer")-1, "", ZEND_ACC_PUBLIC); zend_declare_property_string(curl_CURLFile_class, "mime", sizeof("mime")-1, "", ZEND_ACC_PUBLIC); zend_declare_property_string(curl_CURLFile_class, "postname", sizeof("postname")-1, "", ZEND_ACC_PUBLIC); } diff --git a/ext/curl/interface.c b/ext/curl/interface.c index e0b917620071f..5cbdee89a2407 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -426,6 +426,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_curlfile_create, 0, 0, 1) ZEND_ARG_INFO(0, mimetype) ZEND_ARG_INFO(0, postname) ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_curlbufferfile_create, 0, 0, 2) + ZEND_ARG_INFO(0, buffer) + ZEND_ARG_INFO(0, postname) + ZEND_ARG_INFO(0, mimetype) +ZEND_END_ARG_INFO() /* }}} */ /* {{{ curl_functions[] @@ -470,6 +476,7 @@ const zend_function_entry curl_functions[] = { PHP_FE(curl_share_close, arginfo_curl_share_close) PHP_FE(curl_share_setopt, arginfo_curl_share_setopt) PHP_FE(curl_file_create, arginfo_curlfile_create) + PHP_FE(curl_buffer_file_create, arginfo_curlbufferfile_create) PHP_FE_END }; /* }}} */ @@ -2520,34 +2527,64 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ zval *prop, rv; char *type = NULL, *filename = NULL; - prop = zend_read_property(curl_CURLFile_class, current, "name", sizeof("name")-1, 0, &rv); - if (Z_TYPE_P(prop) != IS_STRING) { - php_error_docref(NULL, E_WARNING, "Invalid filename for key %s", string_key->val); - } else { - postval = Z_STR_P(prop); + prop = zend_read_property(curl_CURLFile_class, current, "mime", sizeof("mime")-1, 0, &rv); + if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) { + type = Z_STRVAL_P(prop); + } - if (php_check_open_basedir(postval->val)) { - return 1; - } + prop = zend_read_property(curl_CURLFile_class, current, "postname", sizeof("postname")-1, 0, &rv); + if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) { + filename = Z_STRVAL_P(prop); + } - prop = zend_read_property(curl_CURLFile_class, current, "mime", sizeof("mime")-1, 0, &rv); - if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) { - type = Z_STRVAL_P(prop); - } - prop = zend_read_property(curl_CURLFile_class, current, "postname", sizeof("postname")-1, 0, &rv); - if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) { - filename = Z_STRVAL_P(prop); + prop = zend_read_property(curl_CURLFile_class, current, "name", sizeof("name")-1, 0, &rv); + if (zval_is_true(prop)) { + /* upload from "name" */ + if (Z_TYPE_P(prop) != IS_STRING) { + php_error_docref(NULL, E_WARNING, "Invalid filename for key %s", string_key->val); + } else { + postval = Z_STR_P(prop); + + if (php_check_open_basedir(postval->val)) { + return 1; + } + + form_error = curl_formadd(&first, &last, + CURLFORM_COPYNAME, string_key->val, + CURLFORM_NAMELENGTH, (zend_long)string_key->len, + CURLFORM_FILENAME, filename ? filename : postval->val, + CURLFORM_CONTENTTYPE, type ? type : "application/octet-stream", + CURLFORM_FILE, postval->val, + CURLFORM_END); + + if (form_error != CURL_FORMADD_OK) { + /* Not nice to convert between enums but we only have place for one error type */ + error = (CURLcode)form_error; + } } - form_error = curl_formadd(&first, &last, - CURLFORM_COPYNAME, string_key->val, - CURLFORM_NAMELENGTH, string_key->len, - CURLFORM_FILENAME, filename ? filename : postval->val, - CURLFORM_CONTENTTYPE, type ? type : "application/octet-stream", - CURLFORM_FILE, postval->val, - CURLFORM_END); - if (form_error != CURL_FORMADD_OK) { - /* Not nice to convert between enums but we only have place for one error type */ - error = (CURLcode)form_error; + } else { + /* upload from "buffer" */ + prop = zend_read_property(curl_CURLFile_class, current, "buffer", sizeof("buffer")-1, 0, &rv); + if (Z_TYPE_P(prop) != IS_STRING) { + php_error_docref(NULL, E_WARNING, "Invalid buffer for key %s", string_key->val); + } else if(!filename) { + php_error_docref(NULL, E_WARNING, "Invalid post file name for key %s", string_key->val); + } else { + postval = Z_STR_P(prop); + + form_error = curl_formadd(&first, &last, + CURLFORM_COPYNAME, string_key->val, + CURLFORM_NAMELENGTH, (zend_long)string_key->len, + CURLFORM_BUFFER, filename, + CURLFORM_BUFFERPTR, postval->val, + CURLFORM_BUFFERLENGTH, (zend_long)postval->len, + CURLFORM_CONTENTTYPE, type ? type : "application/octet-stream", + CURLFORM_END); + + if (form_error != CURL_FORMADD_OK) { + /* Not nice to convert between enums but we only have place for one error type */ + error = (CURLcode)form_error; + } } } diff --git a/ext/curl/php_curl.h b/ext/curl/php_curl.h index b3853ef6fd213..4e0012d0d6c6f 100644 --- a/ext/curl/php_curl.h +++ b/ext/curl/php_curl.h @@ -115,6 +115,7 @@ PHP_FUNCTION(curl_multi_setopt); PHP_FUNCTION(curl_pause); #endif PHP_FUNCTION(curl_file_create); +PHP_FUNCTION(curl_buffer_file_create); void _php_curl_multi_close(zend_resource *); diff --git a/ext/curl/tests/curl_file_serialize.phpt b/ext/curl/tests/curl_file_serialize.phpt index 43b272ad64a01..a06b07f8bc738 100644 --- a/ext/curl/tests/curl_file_serialize.phpt +++ b/ext/curl/tests/curl_file_serialize.phpt @@ -8,11 +8,30 @@ if (!extension_loaded("curl")) { ?> --FILE-- --EXPECTF-- -Fatal error: Uncaught exception 'Exception' with message 'Unserialization of CURLFile instances is not allowed' in %s +array(2) { + ["file"]=> + object(CURLFile)#1 (4) { + ["name"]=> + string(0) "" + ["buffer"]=> + string(9) "testdata2" + ["mime"]=> + string(9) "mime_type" + ["postname"]=> + string(9) "post_name" + } + ["data"]=> + string(3) "foo" +} + +Fatal error: Uncaught exception 'Exception' with message 'Unserialization of CURLFile instances with file name is not allowed' in %s Stack trace: #0 [internal function]: CURLFile->__wakeup() #1 %s diff --git a/ext/curl/tests/curl_file_upload.phpt b/ext/curl/tests/curl_file_upload.phpt index c64e67aa5c7a2..75dbfd4a8b8ee 100644 --- a/ext/curl/tests/curl_file_upload.phpt +++ b/ext/curl/tests/curl_file_upload.phpt @@ -37,12 +37,48 @@ var_dump($file->getFilename()); curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); var_dump(curl_exec($ch)); +$file = new CurlFile(); +$file->setMimeType('text/plain'); +$file->setFilename(__DIR__ . '/curl_testdata1.txt'); +var_dump($file->getMimeType()); +var_dump($file->getFilename()); +curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); +var_dump(curl_exec($ch)); + +$file = new CurlFile(); +$file->name = true; +curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); +var_dump(curl_exec($ch)); + +$file = new CurlFile(); +$file->setPostFilename('foo.txt'); +$file->setBuffer("buf_\0_fer"); +var_dump($file->getPostFilename()); +var_dump($file->getBuffer()); +curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); +var_dump(curl_exec($ch)); + +$file = new CurlFile(); +$file->setBuffer("buffer"); +curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); +var_dump(curl_exec($ch)); + +$file = new CurlFile(); +$file->buffer = 123; +curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); +var_dump(curl_exec($ch)); + $file = curl_file_create(__DIR__ . '/curl_testdata1.txt'); $file->setPostFilename('foo.txt'); var_dump($file->getPostFilename()); curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); var_dump(curl_exec($ch)); +$file = curl_buffer_file_create("buffer", 'foo.txt'); +var_dump($file->getPostFilename()); +curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); +var_dump(curl_exec($ch)); + curl_setopt($ch, CURLOPT_SAFE_UPLOAD, 0); $params = array('file' => '@' . __DIR__ . '/curl_testdata1.txt'); curl_setopt($ch, CURLOPT_POSTFIELDS, $params); @@ -68,6 +104,23 @@ string(%d) "foo.txt|text/plain" string(%d) "text/plain" string(%d) "%s/curl_testdata1.txt" string(%d) "curl_testdata1.txt|text/plain" +string(%d) "text/plain" +string(%d) "%s/curl_testdata1.txt" +string(%d) "curl_testdata1.txt|text/plain" + +Warning: curl_setopt(): Invalid filename for key file in %s on line %d +string(%d) "" +string(%d) "foo.txt" +string(%d) "buf_%s_fer" +string(%d) "foo.txt|application/octet-stream" + +Warning: curl_setopt(): Invalid post file name for key file in %s on line %d +string(%d) "" + +Warning: curl_setopt(): Invalid buffer for key file in %s on line %d +string(%d) "" +string(%d) "foo.txt" +string(%d) "foo.txt|application/octet-stream" string(%d) "foo.txt" string(%d) "foo.txt|application/octet-stream" From e59e9de2f2a8e76969be55cee4faaf56dfcb194d Mon Sep 17 00:00:00 2001 From: Alexander Moskaliov Date: Sat, 12 Mar 2016 11:38:15 +0300 Subject: [PATCH 2/3] remove excess arguments from curlfile_ctor --- ext/curl/curl_file.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/curl/curl_file.c b/ext/curl/curl_file.c index 0306b553a9d06..27d3674c9ba24 100644 --- a/ext/curl/curl_file.c +++ b/ext/curl/curl_file.c @@ -29,7 +29,7 @@ PHP_CURL_API zend_class_entry *curl_CURLFile_class; -static void curlfile_ctor(char *fname, size_t fname_len, char *mime, size_t mime_len, char *postname, size_t postname_len, char *buffer, size_t buffer_len, INTERNAL_FUNCTION_PARAMETERS) +static void curlfile_ctor(char *fname, size_t fname_len, char *mime, size_t mime_len, char *postname, size_t postname_len, char *buffer, size_t buffer_len, zval *return_value) { zval *cf = return_value; @@ -63,7 +63,7 @@ ZEND_METHOD(CURLFile, __construct) return; } - curlfile_ctor(fname, fname_len, mime, mime_len, postname, postname_len, buffer, buffer_len, INTERNAL_FUNCTION_PARAM_PASSTHRU); + curlfile_ctor(fname, fname_len, mime, mime_len, postname, postname_len, buffer, buffer_len, return_value); } /* }}} */ @@ -80,7 +80,7 @@ PHP_FUNCTION(curl_file_create) return; } - curlfile_ctor(fname, fname_len, mime, mime_len, postname, postname_len, buffer, buffer_len, INTERNAL_FUNCTION_PARAM_PASSTHRU); + curlfile_ctor(fname, fname_len, mime, mime_len, postname, postname_len, buffer, buffer_len, return_value); } /* }}} */ @@ -97,7 +97,7 @@ PHP_FUNCTION(curl_buffer_file_create) return; } - curlfile_ctor(fname, fname_len, mime, mime_len, postname, postname_len, buffer, buffer_len, INTERNAL_FUNCTION_PARAM_PASSTHRU); + curlfile_ctor(fname, fname_len, mime, mime_len, postname, postname_len, buffer, buffer_len, return_value); } /* }}} */ From afbe03b55f77a0953033842c7fdaaf1c3db101c0 Mon Sep 17 00:00:00 2001 From: Alexander Moskaliov Date: Sat, 12 Mar 2016 11:45:34 +0300 Subject: [PATCH 3/3] fix curl_buffer_file_create doc comment --- ext/curl/curl_file.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/curl/curl_file.c b/ext/curl/curl_file.c index 27d3674c9ba24..18efd4a9b591c 100644 --- a/ext/curl/curl_file.c +++ b/ext/curl/curl_file.c @@ -68,7 +68,7 @@ ZEND_METHOD(CURLFile, __construct) /* }}} */ /* {{{ proto CURLFile curl_file_create(string $name, [string $mimetype [, string $postfilename]]) - Create the CURLFile object */ + Create the CURLFile object from file */ PHP_FUNCTION(curl_file_create) { char *fname = NULL, *mime = NULL, *postname = NULL, *buffer = NULL; @@ -84,8 +84,8 @@ PHP_FUNCTION(curl_file_create) } /* }}} */ -/* {{{ proto CURLBufferFile curl_buffer_file_create(string $buffer, string $postfilename, [string $mimetype]) - Create the CURLBufferFile object */ +/* {{{ proto CURLFile curl_buffer_file_create(string $buffer, string $postfilename, [string $mimetype]) + Create the CURLFile object from string buffer */ PHP_FUNCTION(curl_buffer_file_create) { char *fname = NULL, *mime = NULL, *postname = NULL, *buffer = NULL;