Skip to content

Commit

Permalink
bug #46439 - better handling of CURL file uploads
Browse files Browse the repository at this point in the history
  • Loading branch information
smalyshev committed Jan 7, 2013
1 parent 1dda0c5 commit 7efa63e
Show file tree
Hide file tree
Showing 6 changed files with 285 additions and 18 deletions.
2 changes: 1 addition & 1 deletion ext/curl/config.m4
Expand Up @@ -149,6 +149,6 @@ int main(int argc, char *argv[])
AC_DEFINE(PHP_CURL_URL_WRAPPERS,1,[ ])
fi

PHP_NEW_EXTENSION(curl, interface.c multi.c share.c streams.c, $ext_shared)
PHP_NEW_EXTENSION(curl, interface.c multi.c share.c streams.c curl_file.c, $ext_shared)
PHP_SUBST(CURL_SHARED_LIBADD)
fi
151 changes: 151 additions & 0 deletions ext/curl/curl_file.c
@@ -0,0 +1,151 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Stanislav Malyshev <stas@php.net> |
+----------------------------------------------------------------------+
*/

/* $Id$ */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "php.h"
#if HAVE_CURL

PHPAPI zend_class_entry *curl_CURLFile_class;

/* {{{ proto string CURLFile::__construct(string $name, [string $mimetype [, string $postfilename]])
Create the CURLFile object */
ZEND_METHOD(CURLFile, __construct)
{
char *fname = NULL, *mime = NULL, *postname = NULL;
int fname_len, mime_len, postname_len;
zval *cf = getThis();

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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 TSRMLS_CC);
}

if (mime) {
zend_update_property_string(curl_CURLFile_class, cf, "mime", sizeof("mime")-1, mime TSRMLS_CC);
}

if (postname) {
zend_update_property_string(curl_CURLFile_class, cf, "postname", sizeof("postname")-1, postname TSRMLS_CC);
}
}

/* }}} */

static void curlfile_get_property(char *name, INTERNAL_FUNCTION_PARAMETERS)
{
zval *res;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
res = zend_read_property(curl_CURLFile_class, getThis(), name, strlen(name), 1 TSRMLS_CC);
*return_value = *res;
zval_copy_ctor(return_value);
INIT_PZVAL(return_value);
}

static void curlfile_set_property(char *name, INTERNAL_FUNCTION_PARAMETERS)
{
char *arg = NULL;
int arg_len;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {
return;
}
zend_update_property_string(curl_CURLFile_class, getThis(), name, strlen(name), arg);

This comment has been minimized.

Copy link
@weltling

weltling Jan 7, 2013

TSRMLS_CC missing here

}

/* {{{ proto string CURLFile::getFilename()
Get file name */
ZEND_METHOD(CURLFile, getFilename)
{
curlfile_get_property("name", INTERNAL_FUNCTION_PARAM_PASSTHRU);
}
/* }}} */

/* {{{ proto string CURLFile::getMimeType()
Get MIME type */
ZEND_METHOD(CURLFile, getMimeType)
{
curlfile_get_property("mime", INTERNAL_FUNCTION_PARAM_PASSTHRU);
}
/* }}} */

/* {{{ proto string CURLFile::getPostFilename()
Get file name for POST */
ZEND_METHOD(CURLFile, getPostFilename)
{
curlfile_get_property("postname", INTERNAL_FUNCTION_PARAM_PASSTHRU);
}
/* }}} */

/* {{{ proto string CURLFile::setMimeType(string $mime)
Set MIME type */
ZEND_METHOD(CURLFile, setMimeType)
{
curlfile_set_property("mime", INTERNAL_FUNCTION_PARAM_PASSTHRU);
}
/* }}} */

/* {{{ proto string CURLFile::setPostFilename(string $name)
Set file name for POST */
ZEND_METHOD(CURLFile, setPostFilename)
{
curlfile_set_property("postname", INTERNAL_FUNCTION_PARAM_PASSTHRU);
}
/* }}} */

ZEND_BEGIN_ARG_INFO_EX(arginfo_curlfile_create, 0, 0, 1)
ZEND_ARG_INFO(0, filename)
ZEND_ARG_INFO(0, mimetype)
ZEND_ARG_INFO(0, postname)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_curlfile_name, 0)
ZEND_ARG_INFO(0, name)
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, getFilename, NULL, 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)
PHP_ME(CURLFile, setPostFilename, arginfo_curlfile_name, ZEND_ACC_PUBLIC)
PHP_FE_END
};

void curlfile_register_class(TSRMLS_D)
{
zend_class_entry ce;
INIT_CLASS_ENTRY( ce, "CURLFile", curlfile_funcs );
curl_CURLFile_class = zend_register_internal_class(&ce TSRMLS_CC);
zend_declare_property_string(curl_CURLFile_class, "name", sizeof("name")-1, "", ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_string(curl_CURLFile_class, "mime", sizeof("mime")-1, "", ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_string(curl_CURLFile_class, "postname", sizeof("postname")-1, "", ZEND_ACC_PUBLIC TSRMLS_CC);
}

#endif
75 changes: 59 additions & 16 deletions ext/curl/interface.c
Expand Up @@ -1229,6 +1229,8 @@ PHP_MINIT_FUNCTION(curl)
}
#endif

curlfile_register_class(TSRMLS_C);

return SUCCESS;
}
/* }}} */
Expand Down Expand Up @@ -1275,7 +1277,7 @@ PHP_MSHUTDOWN_FUNCTION(curl)
/* {{{ curl_write_nothing
* Used as a work around. See _php_curl_close_ex
*/
static size_t curl_write_nothing(char *data, size_t size, size_t nmemb, void *ctx)
static size_t curl_write_nothing(char *data, size_t size, size_t nmemb, void *ctx)
{
return size * nmemb;
}
Expand Down Expand Up @@ -1853,13 +1855,13 @@ static void split_certinfo(char *string, zval *hash)
static void create_certinfo(struct curl_certinfo *ci, zval *listcode TSRMLS_DC)
{
int i;

if(ci) {
zval *certhash = NULL;

for(i=0; i<ci->num_of_certs; i++) {
struct curl_slist *slist;

MAKE_STD_ZVAL(certhash);
array_init(certhash);
for(slist = ci->certinfo[i]; slist; slist = slist->next) {
Expand All @@ -1876,14 +1878,14 @@ static void create_certinfo(struct curl_certinfo *ci, zval *listcode TSRMLS_DC)

MAKE_STD_ZVAL(hash);
array_init(hash);

split_certinfo(&slist->data[len+1], hash);
add_assoc_zval(certhash, s, hash);
} else {
add_assoc_string(certhash, s, &slist->data[len+1], 1);
}
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not extract hash key from certificate info");
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not extract hash key from certificate info");
}
}
add_next_index_zval(listcode, certhash);
Expand Down Expand Up @@ -2342,8 +2344,8 @@ static int _php_curl_setopt(php_curl *ch, long option, zval **zvalue, zval *retu
#if LIBCURL_VERSION_NUM >= 0x071100
/* Strings passed to libcurl as ’char *’ arguments, are copied by the library... NOTE: before 7.17.0 strings were not copied. */
error = curl_easy_setopt(ch->cp, option, Z_STRVAL_PP(zvalue));
#else
goto string_copy;
#else
goto string_copy;
#endif
}
}
Expand Down Expand Up @@ -2563,9 +2565,6 @@ static int _php_curl_setopt(php_curl *ch, long option, zval **zvalue, zval *retu
ulong num_key;
int numeric_key;

SEPARATE_ZVAL(current);
convert_to_string_ex(current);

zend_hash_get_current_key_ex(postfields, &string_key, &string_key_len, &num_key, 0, NULL);

/* Pretend we have a string_key here */
Expand All @@ -2577,6 +2576,48 @@ static int _php_curl_setopt(php_curl *ch, long option, zval **zvalue, zval *retu
numeric_key = 0;
}

if(Z_TYPE_PP(current) == IS_OBJECT && instanceof_function(Z_OBJCE_PP(current), curl_CURLFile_class TSRMLS_CC)) {
/* new-style file upload */
zval *prop;
char *type = NULL, *filename = NULL;

prop = zend_read_property(curl_CURLFile_class, *current, "name", sizeof("name")-1, 0 TSRMLS_CC);
if(Z_TYPE_P(prop) != IS_STRING) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid filename for key %s", string_key);
} else {
postval = Z_STRVAL_P(prop);

if (php_check_open_basedir(postval TSRMLS_CC)) {
RETVAL_FALSE;
return 1;
}

prop = zend_read_property(curl_CURLFile_class, *current, "mime", sizeof("mime")-1, 0 TSRMLS_CC);
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 TSRMLS_CC);
if(Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) {
filename = Z_STRVAL_P(prop);
}
error = curl_formadd(&first, &last,
CURLFORM_COPYNAME, string_key,
CURLFORM_NAMELENGTH, (long)string_key_len - 1,
CURLFORM_FILENAME, filename ? filename : postval,
CURLFORM_CONTENTTYPE, type ? type : "application/octet-stream",
CURLFORM_FILE, postval,
CURLFORM_END);
}

if (numeric_key) {
efree(string_key);
}
continue;
}

SEPARATE_ZVAL(current);
convert_to_string_ex(current);

postval = Z_STRVAL_PP(current);

/* The arguments after _NAMELENGTH and _CONTENTSLENGTH
Expand All @@ -2586,6 +2627,8 @@ static int _php_curl_setopt(php_curl *ch, long option, zval **zvalue, zval *retu
char *type, *filename;
++postval;

php_error_docref("curl.curlfile" TSRMLS_CC, E_DEPRECATED, "Usage of @filename API for file uploading is deprecated. Please use CURLFile parameter instead");

This comment has been minimized.

Copy link
@mj

mj Jan 7, 2013

Suggesting a small wording change here:

"The usage of the @filename API for file uploading is deprecated. Please use the CURLFile class instead."


if ((type = php_memnstr(postval, ";type=", sizeof(";type=") - 1, postval + Z_STRLEN_PP(current)))) {
*type = '\0';
}
Expand Down Expand Up @@ -3088,7 +3131,7 @@ PHP_FUNCTION(curl_getinfo)
struct curl_certinfo *ci = NULL;

array_init(return_value);

if (curl_easy_getinfo(ch->cp, CURLINFO_CERTINFO, &ci) == CURLE_OK) {
create_certinfo(ci, return_value TSRMLS_CC);
} else {
Expand Down Expand Up @@ -3228,16 +3271,16 @@ static void _php_curl_close_ex(php_curl *ch TSRMLS_DC)

_php_curl_verify_handlers(ch, 0 TSRMLS_CC);

/*
/*
* Libcurl is doing connection caching. When easy handle is cleaned up,
* if the handle was previously used by the curl_multi_api, the connection
* if the handle was previously used by the curl_multi_api, the connection
* remains open un the curl multi handle is cleaned up. Some protocols are
* sending content like the FTP one, and libcurl try to use the
* sending content like the FTP one, and libcurl try to use the
* WRITEFUNCTION or the HEADERFUNCTION. Since structures used in those
* callback are freed, we need to use an other callback to which avoid
* segfaults.
*
* Libcurl commit d021f2e8a00 fix this issue and should be part of 7.28.2
* Libcurl commit d021f2e8a00 fix this issue and should be part of 7.28.2
*/
curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_nothing);
curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write_nothing);
Expand Down
4 changes: 3 additions & 1 deletion ext/curl/php_curl.h
Expand Up @@ -212,14 +212,16 @@ typedef struct {

fd_set readfds, writefds, excfds;
int maxfd;

char errstr[CURL_ERROR_SIZE + 1];
CURLMcode mcode;
int pending;
zval *headers;
struct curl_slist *headers_slist; /* holds custom headers sent out in the request */
} php_curl_stream;

void curlfile_register_class(TSRMLS_D);
extern zend_class_entry *curl_CURLFile_class;

#else
#define curl_module_ptr NULL
Expand Down
2 changes: 2 additions & 0 deletions ext/curl/tests/bug27023.phpt
@@ -1,5 +1,7 @@
--TEST--
Bug #27023 (CURLOPT_POSTFIELDS does not parse content types for files)
--INI--
error_reporting = E_ALL & ~E_DEPRECATED
--SKIPIF--
<?php
if (!extension_loaded("curl")) {
Expand Down

0 comments on commit 7efa63e

Please sign in to comment.