year bump
[m6w6/ext-http] / http_request_body_api.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2010, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #define HTTP_WANT_CURL
16 #include "php_http.h"
17
18 #ifdef HTTP_HAVE_CURL
19
20 #include "php_http_api.h"
21 #include "php_http_url_api.h"
22 #include "php_http_request_body_api.h"
23
24 /* {{{ */
25 typedef struct curl_httppost *post_data[2];
26 static STATUS recursive_fields(post_data http_post_data, HashTable *fields, const char *prefix TSRMLS_DC);
27 static STATUS recursive_files(post_data http_post_data, HashTable *files, const char *prefix TSRMLS_DC);
28 /* }}} */
29
30 /* {{{ http_request_body *http_request_body_new() */
31 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)
32 {
33 if (!body) {
34 body = emalloc_rel(sizeof(http_request_body));
35 }
36
37 body->type = type;
38 body->free = free;
39 body->priv = 0;
40 body->data = data;
41 body->size = size;
42
43 return body;
44 }
45 /* }}} */
46
47 /* {{{ http_request_body *http_request_body_fill(http_request_body *body, HashTable *, HashTable *) */
48 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)
49 {
50 if (files && (zend_hash_num_elements(files) > 0)) {
51 struct curl_httppost *http_post_data[2] = {NULL, NULL};
52
53 if (fields && SUCCESS != recursive_fields(http_post_data, fields, NULL TSRMLS_CC)) {
54 return NULL;
55 }
56 if (SUCCESS != recursive_files(http_post_data, files, NULL TSRMLS_CC)) {
57 return NULL;
58 }
59 return http_request_body_init_rel(body, HTTP_REQUEST_BODY_CURLPOST, http_post_data[0], 0, 1);
60 } else if (fields) {
61 char *encoded;
62 size_t encoded_len;
63
64 if (SUCCESS != http_urlencode_hash_ex(fields, 1, NULL, 0, &encoded, &encoded_len)) {
65 http_error(HE_WARNING, HTTP_E_ENCODING, "Could not encode post data");
66 return NULL;
67 }
68
69 return http_request_body_init_rel(body, HTTP_REQUEST_BODY_CSTRING, encoded, encoded_len, 1);
70 } else {
71 return http_request_body_init_rel(body, HTTP_REQUEST_BODY_CSTRING, estrndup("", 0), 0, 1);
72 }
73 }
74
75 /* STATUS http_request_body_encode(http_request_body *, char**, size_t *) */
76 PHP_HTTP_API STATUS _http_request_body_encode(http_request_body *body, char **buf, size_t *len TSRMLS_DC)
77 {
78 switch (body->type) {
79 case HTTP_REQUEST_BODY_CURLPOST:
80 {
81 #ifdef HAVE_CURL_FORMGET
82 phpstr str;
83
84 phpstr_init_ex(&str, 0x8000, 0);
85 if (curl_formget(body->data, &str, (curl_formget_callback) phpstr_append)) {
86 phpstr_dtor(&str);
87 } else {
88 phpstr_fix(&str);
89 *buf = PHPSTR_VAL(&str);
90 *len = PHPSTR_LEN(&str);
91 return SUCCESS;
92 }
93 #endif
94 break;
95 }
96
97 case HTTP_REQUEST_BODY_CSTRING:
98 *buf = estrndup(body->data, *len = body->size);
99 return SUCCESS;
100
101 default:
102 break;
103 }
104 return FAILURE;
105 }
106 /* }}} */
107
108 /* {{{ void http_request_body_dtor(http_request_body *) */
109 PHP_HTTP_API void _http_request_body_dtor(http_request_body *body TSRMLS_DC)
110 {
111 if (body) {
112 if (body->free) {
113 switch (body->type) {
114 case HTTP_REQUEST_BODY_CSTRING:
115 if (body->data) {
116 efree(body->data);
117 }
118 break;
119
120 case HTTP_REQUEST_BODY_CURLPOST:
121 curl_formfree(body->data);
122 break;
123
124 case HTTP_REQUEST_BODY_UPLOADFILE:
125 php_stream_close(body->data);
126 break;
127 }
128 }
129 memset(body, 0, sizeof(http_request_body));
130 }
131 }
132 /* }}} */
133
134 /* {{{ void http_request_body_free(http_request_body *) */
135 PHP_HTTP_API void _http_request_body_free(http_request_body **body TSRMLS_DC)
136 {
137 if (*body) {
138 http_request_body_dtor(*body);
139 efree(*body);
140 *body = NULL;
141 }
142 }
143 /* }}} */
144
145 static inline char *format_key(uint type, char *str, ulong num, const char *prefix, int numeric_key_for_empty_prefix) {
146 char *new_key = NULL;
147
148 if (prefix && *prefix) {
149 if (type == HASH_KEY_IS_STRING) {
150 spprintf(&new_key, 0, "%s[%s]", prefix, str);
151 } else {
152 spprintf(&new_key, 0, "%s[%lu]", prefix, num);
153 }
154 } else if (type == HASH_KEY_IS_STRING) {
155 new_key = estrdup(str);
156 } else if (numeric_key_for_empty_prefix) {
157 spprintf(&new_key, 0, "%lu", num);
158 }
159
160 return new_key;
161 }
162
163 /* {{{ static STATUS recursive_fields(post_data d, HashTable *f, const char *p TSRMLS_DC) */
164 static STATUS recursive_fields(post_data http_post_data, HashTable *fields, const char *prefix TSRMLS_DC) {
165 HashKey key = initHashKey(0);
166 zval **data_ptr;
167 HashPosition pos;
168 char *new_key = NULL;
169 CURLcode err = 0;
170
171 if (fields && !fields->nApplyCount) {
172 FOREACH_HASH_KEYVAL(pos, fields, key, data_ptr) {
173 if (key.type != HASH_KEY_IS_STRING || *key.str) {
174 new_key = format_key(key.type, key.str, key.num, prefix, 1);
175
176 switch (Z_TYPE_PP(data_ptr)) {
177 case IS_ARRAY:
178 case IS_OBJECT: {
179 STATUS status;
180
181 ++fields->nApplyCount;
182 status = recursive_fields(http_post_data, HASH_OF(*data_ptr), new_key TSRMLS_CC);
183 --fields->nApplyCount;
184
185 if (SUCCESS != status) {
186 goto error;
187 }
188 break;
189 }
190
191 default: {
192 zval *data = http_zsep(IS_STRING, *data_ptr);
193
194 err = curl_formadd(&http_post_data[0], &http_post_data[1],
195 CURLFORM_COPYNAME, new_key,
196 CURLFORM_COPYCONTENTS, Z_STRVAL_P(data),
197 CURLFORM_CONTENTSLENGTH, (long) Z_STRLEN_P(data),
198 CURLFORM_END
199 );
200
201 zval_ptr_dtor(&data);
202
203 if (CURLE_OK != err) {
204 goto error;
205 }
206 break;
207 }
208 }
209 STR_FREE(new_key);
210 }
211 }
212 }
213
214 return SUCCESS;
215
216 error:
217 if (new_key) {
218 efree(new_key);
219 }
220 if (http_post_data[0]) {
221 curl_formfree(http_post_data[0]);
222 }
223 if (err) {
224 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not encode post fields: %s", curl_easy_strerror(err));
225 } else {
226 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not encode post fields: unknown error");
227 }
228 return FAILURE;
229 }
230 /* }}} */
231
232 /* {{{ static STATUS recursive_files(post_data d, HashTable *f, const char *p TSRMLS_DC) */
233 static STATUS recursive_files(post_data http_post_data, HashTable *files, const char *prefix TSRMLS_DC) {
234 HashKey key = initHashKey(0);
235 zval **data_ptr;
236 HashPosition pos;
237 char *new_key = NULL;
238 CURLcode err = 0;
239
240 if (files && !files->nApplyCount) {
241 FOREACH_HASH_KEYVAL(pos, files, key, data_ptr) {
242 zval **file_ptr, **type_ptr, **name_ptr;
243
244 if (key.type != HASH_KEY_IS_STRING || *key.str) {
245 new_key = format_key(key.type, key.str, key.num, prefix, 0);
246
247 if (Z_TYPE_PP(data_ptr) != IS_ARRAY && Z_TYPE_PP(data_ptr) != IS_OBJECT) {
248 if (new_key || key.type == HASH_KEY_IS_STRING) {
249 http_error_ex(HE_NOTICE, HTTP_E_INVALID_PARAM, "Unrecognized type of post file array entry '%s'", new_key ? new_key : key.str);
250 } else {
251 http_error_ex(HE_NOTICE, HTTP_E_INVALID_PARAM, "Unrecognized type of post file array entry '%lu'", key.num);
252 }
253 } else if ( SUCCESS != zend_hash_find(HASH_OF(*data_ptr), "name", sizeof("name"), (void *) &name_ptr) ||
254 SUCCESS != zend_hash_find(HASH_OF(*data_ptr), "type", sizeof("type"), (void *) &type_ptr) ||
255 SUCCESS != zend_hash_find(HASH_OF(*data_ptr), "file", sizeof("file"), (void *) &file_ptr)) {
256 STATUS status;
257
258 ++files->nApplyCount;
259 status = recursive_files(http_post_data, HASH_OF(*data_ptr), new_key TSRMLS_CC);
260 --files->nApplyCount;
261
262 if (SUCCESS != status) {
263 goto error;
264 }
265 } else {
266 const char *path;
267 zval *file = http_zsep(IS_STRING, *file_ptr);
268 zval *type = http_zsep(IS_STRING, *type_ptr);
269 zval *name = http_zsep(IS_STRING, *name_ptr);
270
271 HTTP_CHECK_OPEN_BASEDIR(Z_STRVAL_P(file), goto error);
272
273 /* this is blatant but should be sufficient for most cases */
274 if (strncasecmp(Z_STRVAL_P(file), "file://", lenof("file://"))) {
275 path = Z_STRVAL_P(file);
276 } else {
277 path = Z_STRVAL_P(file) + lenof("file://");
278 }
279
280 if (new_key) {
281 char *tmp_key = format_key(HASH_KEY_IS_STRING, Z_STRVAL_P(name), 0, new_key, 0);
282 STR_SET(new_key, tmp_key);
283 }
284
285 err = curl_formadd(&http_post_data[0], &http_post_data[1],
286 CURLFORM_COPYNAME, new_key ? new_key : Z_STRVAL_P(name),
287 CURLFORM_FILE, path,
288 CURLFORM_CONTENTTYPE, Z_STRVAL_P(type),
289 CURLFORM_END
290 );
291
292 zval_ptr_dtor(&file);
293 zval_ptr_dtor(&type);
294 zval_ptr_dtor(&name);
295
296 if (CURLE_OK != err) {
297 goto error;
298 }
299 }
300 STR_FREE(new_key);
301 }
302 }
303 }
304
305 return SUCCESS;
306
307 error:
308 if (new_key) {
309 efree(new_key);
310 }
311 if (http_post_data[0]) {
312 curl_formfree(http_post_data[0]);
313 }
314 if (err) {
315 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not encode post files: %s", curl_easy_strerror(err));
316 } else {
317 http_error(HE_WARNING, HTTP_E_ENCODING, "Could not encode post files: unknown error");
318 }
319 return FAILURE;
320 }
321 /* }}} */
322
323 #endif /* HTTP_HAVE_CURL */
324
325 /*
326 * Local variables:
327 * tab-width: 4
328 * c-basic-offset: 4
329 * End:
330 * vim600: noet sw=4 ts=4 fdm=marker
331 * vim<600: noet sw=4 ts=4
332 */