- Fixed build on php-trunk
[m6w6/ext-http] / http_request_body_api.c
index 39cba652c70cf7e14390fbaa564b1236f36e45c7..7d7f4f9ab3a6d2e00d677ed0c800445d93f762bb 100644 (file)
@@ -6,7 +6,7 @@
     | modification, are permitted provided that the conditions mentioned |
     | in the accompanying LICENSE file are met.                          |
     +--------------------------------------------------------------------+
-    | Copyright (c) 2004-2007, Michael Wallner <mike@php.net>            |
+    | Copyright (c) 2004-2010, Michael Wallner <mike@php.net>            |
     +--------------------------------------------------------------------+
 */
 
 #include "php_http_url_api.h"
 #include "php_http_request_body_api.h"
 
+/* {{{ */
+typedef struct curl_httppost *post_data[2];
+static STATUS recursive_fields(post_data http_post_data, HashTable *fields, const char *prefix TSRMLS_DC);
+static STATUS recursive_files(post_data http_post_data, HashTable *files, const char *prefix TSRMLS_DC);
+/* }}} */
+
 /* {{{ http_request_body *http_request_body_new() */
 PHP_HTTP_API http_request_body *_http_request_body_init_ex(http_request_body *body, int type, void *data, size_t size, zend_bool free ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
 {
@@ -42,88 +48,15 @@ PHP_HTTP_API http_request_body *_http_request_body_init_ex(http_request_body *bo
 PHP_HTTP_API http_request_body *_http_request_body_fill(http_request_body *body, HashTable *fields, HashTable *files ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
 {
        if (files && (zend_hash_num_elements(files) > 0)) {
-               HashKey key = initHashKey(0);
-               zval **data;
-               HashPosition pos;
                struct curl_httppost *http_post_data[2] = {NULL, NULL};
 
-               /* normal data */
-               if (fields) {
-                       FOREACH_HASH_KEYVAL(pos, fields, key, data) {
-                               if (key.type == HASH_KEY_IS_STRING) {
-                                       CURLcode err;
-                                       zval *orig = *data;
-                                       
-                                       convert_to_string_ex(data);
-                                       err = curl_formadd(&http_post_data[0], &http_post_data[1],
-                                               CURLFORM_COPYNAME,                      key.str,
-                                               CURLFORM_COPYCONTENTS,          Z_STRVAL_PP(data),
-                                               CURLFORM_CONTENTSLENGTH,        (long) Z_STRLEN_PP(data),
-                                               CURLFORM_END
-                                       );
-                                       
-                                       if (orig != *data) {
-                                               zval_ptr_dtor(data);
-                                       }
-                                       
-                                       if (CURLE_OK != err) {
-                                               http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not encode post fields: %s", curl_easy_strerror(err));
-                                               curl_formfree(http_post_data[0]);
-                                               return NULL;
-                                       }
-                               }
-                       }
+               if (fields && SUCCESS != recursive_fields(http_post_data, fields, NULL TSRMLS_CC)) {
+                       return NULL;
                }
-
-               /* file data */
-               FOREACH_HASH_VAL(pos, files, data) {
-                       zval **file, **type, **name;
-                       
-                       if (Z_TYPE_PP(data) != IS_ARRAY) {
-                               http_error(HE_NOTICE, HTTP_E_INVALID_PARAM, "Unrecognized type of post file array entry");
-                       } else if (     SUCCESS != zend_hash_find(Z_ARRVAL_PP(data), "name", sizeof("name"), (void *) &name) ||
-                                               SUCCESS != zend_hash_find(Z_ARRVAL_PP(data), "type", sizeof("type"), (void *) &type) ||
-                                               SUCCESS != zend_hash_find(Z_ARRVAL_PP(data), "file", sizeof("file"), (void *) &file)) {
-                               http_error(HE_NOTICE, HTTP_E_INVALID_PARAM, "Post file array entry misses either 'name', 'type' or 'file' entry");
-                       } else {
-                               CURLcode err;
-                               const char *path;
-                               zval *ofile = *file, *otype = *type, *oname = *name;
-                               
-                               convert_to_string_ex(file);
-                               convert_to_string_ex(type);
-                               convert_to_string_ex(name);
-                               
-                               HTTP_CHECK_OPEN_BASEDIR(Z_STRVAL_PP(file), curl_formfree(http_post_data[0]); return NULL);
-                               
-                               /* this is blatant but should be sufficient for most cases */
-                               if (strncasecmp(Z_STRVAL_PP(file), "file://", lenof("file://"))) {
-                                       path = Z_STRVAL_PP(file);
-                               } else {
-                                       path = Z_STRVAL_PP(file) + lenof("file://");
-                               }
-                               
-                               err = curl_formadd(&http_post_data[0], &http_post_data[1],
-                                       CURLFORM_COPYNAME,              Z_STRVAL_PP(name),
-                                       CURLFORM_FILE,                  path,
-                                       CURLFORM_CONTENTTYPE,   Z_STRVAL_PP(type),
-                                       CURLFORM_END
-                               );
-                               
-                               if (ofile != *file) zval_ptr_dtor(file);
-                               if (otype != *type) zval_ptr_dtor(type);
-                               if (oname != *name) zval_ptr_dtor(name);
-                               
-                               if (CURLE_OK != err) {
-                                       http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not encode post files: %s", curl_easy_strerror(err));
-                                       curl_formfree(http_post_data[0]);
-                                       return NULL;
-                               }
-                       }
+               if (SUCCESS != recursive_files(http_post_data, files, NULL TSRMLS_CC)) {
+                       return NULL;
                }
-               
                return http_request_body_init_rel(body, HTTP_REQUEST_BODY_CURLPOST, http_post_data[0], 0, 1);
-
        } else if (fields) {
                char *encoded;
                size_t encoded_len;
@@ -145,7 +78,7 @@ PHP_HTTP_API STATUS _http_request_body_encode(http_request_body *body, char **bu
        switch (body->type) {
                case HTTP_REQUEST_BODY_CURLPOST:
                {
-#if defined(HAVE_CURL_FORMGET)
+#ifdef HAVE_CURL_FORMGET
                        phpstr str;
                        
                        phpstr_init_ex(&str, 0x8000, 0);
@@ -209,6 +142,184 @@ PHP_HTTP_API void _http_request_body_free(http_request_body **body TSRMLS_DC)
 }
 /* }}} */
 
+static inline char *format_key(uint type, char *str, ulong num, const char *prefix, int numeric_key_for_empty_prefix) {
+       char *new_key = NULL;
+       
+       if (prefix && *prefix) {
+               if (type == HASH_KEY_IS_STRING) {
+                       spprintf(&new_key, 0, "%s[%s]", prefix, str);
+               } else {
+                       spprintf(&new_key, 0, "%s[%lu]", prefix, num);
+               }
+       } else if (type == HASH_KEY_IS_STRING) {
+               new_key = estrdup(str);
+       } else if (numeric_key_for_empty_prefix) {
+               spprintf(&new_key, 0, "%lu", num);
+       }
+       
+       return new_key;
+}
+
+/* {{{ static STATUS recursive_fields(post_data d, HashTable *f, const char *p TSRMLS_DC) */
+static STATUS recursive_fields(post_data http_post_data, HashTable *fields, const char *prefix TSRMLS_DC) {
+       HashKey key = initHashKey(0);
+       zval **data_ptr;
+       HashPosition pos;
+       char *new_key = NULL;
+       CURLcode err = 0;
+       
+       if (fields && !fields->nApplyCount) {
+               FOREACH_HASH_KEYVAL(pos, fields, key, data_ptr) {
+                       if (key.type != HASH_KEY_IS_STRING || *key.str) {
+                               new_key = format_key(key.type, key.str, key.num, prefix, 1);
+                               
+                               switch (Z_TYPE_PP(data_ptr)) {
+                                       case IS_ARRAY:
+                                       case IS_OBJECT: {
+                                               STATUS status;
+                                               
+                                               ++fields->nApplyCount;
+                                               status = recursive_fields(http_post_data, HASH_OF(*data_ptr), new_key TSRMLS_CC);
+                                               --fields->nApplyCount;
+                                               
+                                               if (SUCCESS != status) {
+                                                       goto error;
+                                               }
+                                               break;
+                                       }
+                                               
+                                       default: {
+                                               zval *data = http_zsep(IS_STRING, *data_ptr);
+                                               
+                                               err = curl_formadd(&http_post_data[0], &http_post_data[1],
+                                                       CURLFORM_COPYNAME,                      new_key,
+                                                       CURLFORM_COPYCONTENTS,          Z_STRVAL_P(data),
+                                                       CURLFORM_CONTENTSLENGTH,        (long) Z_STRLEN_P(data),
+                                                       CURLFORM_END
+                                               );
+                                               
+                                               zval_ptr_dtor(&data);
+                                               
+                                               if (CURLE_OK != err) {
+                                                       goto error;
+                                               }
+                                               break;
+                                       }
+                               }
+                               STR_FREE(new_key);
+                       }
+               }
+       }
+       
+       return SUCCESS;
+       
+error:
+       if (new_key) {
+               efree(new_key);
+       }
+       if (http_post_data[0]) {
+               curl_formfree(http_post_data[0]);
+       }
+       if (err) {
+               http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not encode post fields: %s", curl_easy_strerror(err));
+       } else {
+               http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not encode post fields: unknown error");
+       }
+       return FAILURE;
+}
+/* }}} */
+
+/* {{{ static STATUS recursive_files(post_data d, HashTable *f, const char *p TSRMLS_DC) */
+static STATUS recursive_files(post_data http_post_data, HashTable *files, const char *prefix TSRMLS_DC) {
+       HashKey key = initHashKey(0);
+       zval **data_ptr;
+       HashPosition pos;
+       char *new_key = NULL;
+       CURLcode err = 0;
+       
+       if (files && !files->nApplyCount) {
+               FOREACH_HASH_KEYVAL(pos, files, key, data_ptr) {
+                       zval **file_ptr, **type_ptr, **name_ptr;
+                       
+                       if (key.type != HASH_KEY_IS_STRING || *key.str) {
+                               new_key = format_key(key.type, key.str, key.num, prefix, 0);
+                               
+                               if (Z_TYPE_PP(data_ptr) != IS_ARRAY && Z_TYPE_PP(data_ptr) != IS_OBJECT) {
+                                       if (new_key || key.type == HASH_KEY_IS_STRING) {
+                                               http_error_ex(HE_NOTICE, HTTP_E_INVALID_PARAM, "Unrecognized type of post file array entry '%s'", new_key ? new_key : key.str);
+                                       } else {
+                                               http_error_ex(HE_NOTICE, HTTP_E_INVALID_PARAM, "Unrecognized type of post file array entry '%lu'", key.num);
+                                       }
+                               } else if (     SUCCESS != zend_hash_find(HASH_OF(*data_ptr), "name", sizeof("name"), (void *) &name_ptr) ||
+                                                       SUCCESS != zend_hash_find(HASH_OF(*data_ptr), "type", sizeof("type"), (void *) &type_ptr) ||
+                                                       SUCCESS != zend_hash_find(HASH_OF(*data_ptr), "file", sizeof("file"), (void *) &file_ptr)) {
+                                       STATUS status;
+                                       
+                                       ++files->nApplyCount;
+                                       status = recursive_files(http_post_data, HASH_OF(*data_ptr), new_key TSRMLS_CC);
+                                       --files->nApplyCount;
+                                       
+                                       if (SUCCESS != status) {
+                                               goto error;
+                                       }
+                               } else {
+                                       const char *path;
+                                       zval *file = http_zsep(IS_STRING, *file_ptr);
+                                       zval *type = http_zsep(IS_STRING, *type_ptr);
+                                       zval *name = http_zsep(IS_STRING, *name_ptr);
+                                       
+                                       HTTP_CHECK_OPEN_BASEDIR(Z_STRVAL_P(file), goto error);
+                                       
+                                       /* this is blatant but should be sufficient for most cases */
+                                       if (strncasecmp(Z_STRVAL_P(file), "file://", lenof("file://"))) {
+                                               path = Z_STRVAL_P(file);
+                                       } else {
+                                               path = Z_STRVAL_P(file) + lenof("file://");
+                                       }
+                                       
+                                       if (new_key) {
+                                               char *tmp_key = format_key(HASH_KEY_IS_STRING, Z_STRVAL_P(name), 0, new_key, 0);
+                                               STR_SET(new_key, tmp_key);
+                                       }
+                                       
+                                       err = curl_formadd(&http_post_data[0], &http_post_data[1],
+                                               CURLFORM_COPYNAME,              new_key ? new_key : Z_STRVAL_P(name),
+                                               CURLFORM_FILE,                  path,
+                                               CURLFORM_CONTENTTYPE,   Z_STRVAL_P(type),
+                                               CURLFORM_END
+                                       );
+                                       
+                                       zval_ptr_dtor(&file);
+                                       zval_ptr_dtor(&type);
+                                       zval_ptr_dtor(&name);
+                                       
+                                       if (CURLE_OK != err) {
+                                               goto error;
+                                       }
+                               }
+                               STR_FREE(new_key);
+                       }
+               }
+       }
+       
+       return SUCCESS;
+       
+error:
+       if (new_key) {
+               efree(new_key);
+       }
+       if (http_post_data[0]) {
+               curl_formfree(http_post_data[0]);
+       }
+       if (err) {
+               http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not encode post files: %s", curl_easy_strerror(err));
+       } else {
+               http_error(HE_WARNING, HTTP_E_ENCODING, "Could not encode post files: unknown error");
+       }
+       return FAILURE;
+}
+/* }}} */
+
 #endif /* HTTP_HAVE_CURL */
 
 /*