branch off v1 as R_1_7
[m6w6/ext-http] / http_request_body_api.c
index 8386bf884b77cc07a16267348ab03247bf6076f7..7d7f4f9ab3a6d2e00d677ed0c800445d93f762bb 100644 (file)
@@ -6,16 +6,12 @@
     | modification, are permitted provided that the conditions mentioned |
     | in the accompanying LICENSE file are met.                          |
     +--------------------------------------------------------------------+
-    | Copyright (c) 2004-2005, Michael Wallner <mike@php.net>            |
+    | Copyright (c) 2004-2010, Michael Wallner <mike@php.net>            |
     +--------------------------------------------------------------------+
 */
 
 /* $Id$ */
 
-#ifdef HAVE_CONFIG_H
-#      include "config.h"
-#endif
-
 #define HTTP_WANT_CURL
 #include "php_http.h"
 
 #include "php_http_url_api.h"
 #include "php_http_request_body_api.h"
 
-ZEND_EXTERN_MODULE_GLOBALS(http);
+/* {{{ */
+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)
@@ -36,6 +36,7 @@ PHP_HTTP_API http_request_body *_http_request_body_init_ex(http_request_body *bo
        
        body->type = type;
        body->free = free;
+       body->priv = 0;
        body->data = data;
        body->size = size;
        
@@ -47,73 +48,16 @@ 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)) {
-               char *key = NULL;
-               ulong idx;
-               zval **data;
-               HashPosition pos;
                struct curl_httppost *http_post_data[2] = {NULL, NULL};
 
-               /* normal data */
-               FOREACH_HASH_KEYVAL(pos, fields, key, idx, data) {
-                       CURLcode err;
-                       if (key) {
-                               zval *orig = *data;
-                               
-                               convert_to_string_ex(data);
-                               err = curl_formadd(&http_post_data[0], &http_post_data[1],
-                                       CURLFORM_COPYNAME,                      key,
-                                       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;
-                               }
-
-                               /* reset */
-                               key = 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;
-                               
-                               HTTP_CHECK_OPEN_BASEDIR(Z_STRVAL_PP(file), curl_formfree(http_post_data[0]); return NULL);
-                               
-                               err = curl_formadd(&http_post_data[0], &http_post_data[1],
-                                       CURLFORM_COPYNAME,              Z_STRVAL_PP(name),
-                                       CURLFORM_FILE,                  Z_STRVAL_PP(file),
-                                       CURLFORM_CONTENTTYPE,   Z_STRVAL_PP(type),
-                                       CURLFORM_END
-                               );
-                               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 {
+       } else if (fields) {
                char *encoded;
                size_t encoded_len;
 
@@ -123,30 +67,66 @@ PHP_HTTP_API http_request_body *_http_request_body_fill(http_request_body *body,
                }
                
                return http_request_body_init_rel(body, HTTP_REQUEST_BODY_CSTRING, encoded, encoded_len, 1);
+       } else {
+               return http_request_body_init_rel(body, HTTP_REQUEST_BODY_CSTRING, estrndup("", 0), 0, 1);
        }
 }
 
-
-/* {{{ void http_request_body_dtor(http_request_body *) */
-PHP_HTTP_API void _http_request_body_dtor(http_request_body *body TSRMLS_DC)
+/* STATUS http_request_body_encode(http_request_body *, char**, size_t *) */
+PHP_HTTP_API STATUS _http_request_body_encode(http_request_body *body, char **buf, size_t *len TSRMLS_DC)
 {
-       if (body && body->free) {
-               switch (body->type)
+       switch (body->type) {
+               case HTTP_REQUEST_BODY_CURLPOST:
                {
-                       case HTTP_REQUEST_BODY_CSTRING:
-                               if (body->data) {
-                                       efree(body->data);
-                               }
+#ifdef HAVE_CURL_FORMGET
+                       phpstr str;
+                       
+                       phpstr_init_ex(&str, 0x8000, 0);
+                       if (curl_formget(body->data, &str, (curl_formget_callback) phpstr_append)) {
+                               phpstr_dtor(&str);
+                       } else {
+                               phpstr_fix(&str);
+                               *buf = PHPSTR_VAL(&str);
+                               *len = PHPSTR_LEN(&str);
+                               return SUCCESS;
+                       }
+#endif
                        break;
-
-                       case HTTP_REQUEST_BODY_CURLPOST:
-                               curl_formfree(body->data);
+               }
+               
+               case HTTP_REQUEST_BODY_CSTRING:
+                       *buf = estrndup(body->data, *len = body->size);
+                       return SUCCESS;
+               
+               default:
                        break;
+       }
+       return FAILURE;
+}
+/* }}} */
 
-                       case HTTP_REQUEST_BODY_UPLOADFILE:
-                               php_stream_close(body->data);
-                       break;
+/* {{{ void http_request_body_dtor(http_request_body *) */
+PHP_HTTP_API void _http_request_body_dtor(http_request_body *body TSRMLS_DC)
+{
+       if (body) {
+               if (body->free) {
+                       switch (body->type) {
+                               case HTTP_REQUEST_BODY_CSTRING:
+                                       if (body->data) {
+                                               efree(body->data);
+                                       }
+                                       break;
+       
+                               case HTTP_REQUEST_BODY_CURLPOST:
+                                       curl_formfree(body->data);
+                                       break;
+       
+                               case HTTP_REQUEST_BODY_UPLOADFILE:
+                                       php_stream_close(body->data);
+                                       break;
+                       }
                }
+               memset(body, 0, sizeof(http_request_body));
        }
 }
 /* }}} */
@@ -162,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 */
 
 /*