2 +--------------------------------------------------------------------+
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 +--------------------------------------------------------------------+
13 /* $Id: http_message_body.c 292841 2009-12-31 08:48:57Z mike $ */
17 typedef struct curl_httppost
*post_data
[2];
19 static inline STATUS
add_field(php_http_message_body_t
*body
, const char *name
, const char *value_str
, size_t value_len
);
20 static inline STATUS
add_file(php_http_message_body_t
*body
, const char *name
, const char *path
, const char *ctype
);
21 static STATUS
recursive_fields(post_data http_post_data
, HashTable
*fields
, const char *prefix TSRMLS_DC
);
22 static STATUS
recursive_files(post_data http_post_data
, HashTable
*files
, const char *prefix TSRMLS_DC
);
24 PHP_HTTP_API php_http_message_body_t
*php_http_message_body_init(php_http_message_body_t
*body
, php_stream
*stream TSRMLS_DC
)
27 body
= emalloc(sizeof(php_http_message_body_t
));
31 php_stream_auto_cleanup(stream
);
32 body
->stream_id
= php_stream_get_resource_id(stream
);
33 zend_list_addref(body
->stream_id
);
35 stream
= php_stream_temp_new();
36 php_stream_auto_cleanup(stream
);
37 body
->stream_id
= php_stream_get_resource_id(stream
);
39 TSRMLS_SET_CTX(body
->ts
);
44 PHP_HTTP_API php_http_message_body_t
*php_http_message_body_copy(php_http_message_body_t
*from
, php_http_message_body_t
*to
, zend_bool dup_internal_stream_and_contents
)
49 TSRMLS_FETCH_FROM_CTX(from
->ts
);
51 if (dup_internal_stream_and_contents
) {
52 to
= php_http_message_body_init(to
, NULL TSRMLS_CC
);
53 php_http_message_body_to_stream(from
, php_http_message_body_stream(to
), 0, 0);
55 to
= php_http_message_body_init(to
, php_http_message_body_stream(from
) TSRMLS_CC
);
62 PHP_HTTP_API
void php_http_message_body_dtor(php_http_message_body_t
*body
)
65 /* NO FIXME: shows leakinfo in DEBUG mode */
66 zend_list_delete(body
->stream_id
);
70 PHP_HTTP_API
void php_http_message_body_free(php_http_message_body_t
**body
)
73 php_http_message_body_dtor(*body
);
79 PHP_HTTP_API php_stream_statbuf
*php_http_message_body_stat(php_http_message_body_t
*body
)
81 TSRMLS_FETCH_FROM_CTX(body
->ts
);
82 php_stream_stat(php_http_message_body_stream(body
), &body
->ssb
);
86 PHP_HTTP_API
char *php_http_message_body_etag(php_http_message_body_t
*body
)
88 TSRMLS_FETCH_FROM_CTX(body
->ts
);
89 php_stream_statbuf
*ssb
= php_http_message_body_stat(body
);
91 /* real file or temp buffer ? */
92 if (body
->ssb
.sb
.st_mtime
) {
95 spprintf(&etag
, 0, "%lx-%lx-%lx", ssb
->sb
.st_ino
, ssb
->sb
.st_mtime
, ssb
->sb
.st_size
);
98 void *ctx
= php_http_etag_init(TSRMLS_C
);
100 php_http_message_body_to_callback(body
, php_http_etag_update
, ctx
, 0, 0);
101 return php_http_etag_finish(ctx TSRMLS_CC
);
105 PHP_HTTP_API
void php_http_message_body_to_string(php_http_message_body_t
*body
, char **buf
, size_t *len
, off_t offset
, size_t forlen
)
107 TSRMLS_FETCH_FROM_CTX(body
->ts
);
108 php_stream
*s
= php_http_message_body_stream(body
);
110 php_stream_seek(s
, offset
, SEEK_SET
);
114 *len
= php_stream_copy_to_mem(s
, buf
, forlen
, 0);
117 PHP_HTTP_API
void php_http_message_body_to_stream(php_http_message_body_t
*body
, php_stream
*dst
, off_t offset
, size_t forlen
)
119 TSRMLS_FETCH_FROM_CTX(body
->ts
);
120 php_stream
*s
= php_http_message_body_stream(body
);
122 php_stream_seek(s
, offset
, SEEK_SET
);
126 php_stream_copy_to_stream_ex(s
, dst
, forlen
, NULL
);
129 PHP_HTTP_API
void php_http_message_body_to_callback(php_http_message_body_t
*body
, php_http_pass_callback_t cb
, void *cb_arg
, off_t offset
, size_t forlen
)
131 TSRMLS_FETCH_FROM_CTX(body
->ts
);
132 php_stream
*s
= php_http_message_body_stream(body
);
134 php_stream_seek(s
, offset
, SEEK_SET
);
139 while (!php_stream_eof(s
)) {
141 size_t read
= php_stream_read(s
, buf
, MIN(forlen
, sizeof(buf
)));
144 cb(cb_arg
, buf
, read
);
147 if (read
< MIN(forlen
, sizeof(buf
))) {
151 if (forlen
&& !(forlen
-= read
)) {
157 PHP_HTTP_API STATUS
php_http_message_body_to_callback_in_chunks(php_http_message_body_t
*body
, php_http_pass_callback_t cb
, void *cb_arg
, HashTable
*chunks
)
159 TSRMLS_FETCH_FROM_CTX(body
->ts
);
160 php_stream
*s
= php_http_message_body_stream(body
);
164 FOREACH_HASH_VAL(pos
, chunks
, chunk
) {
167 if (IS_ARRAY
== Z_TYPE_PP(chunk
)
168 && SUCCESS
== zend_hash_index_find(Z_ARRVAL_PP(chunk
), 0, (void *) &begin
)
169 && IS_LONG
== Z_TYPE_PP(begin
)
170 && SUCCESS
== zend_hash_index_find(Z_ARRVAL_PP(chunk
), 1, (void *) &end
)
171 && IS_LONG
== Z_TYPE_PP(end
)
173 if (SUCCESS
!= php_stream_seek(s
, Z_LVAL_PP(begin
), SEEK_SET
)) {
176 long length
= Z_LVAL_PP(end
) - Z_LVAL_PP(begin
) + 1;
178 while (length
> 0 && !php_stream_eof(s
)) {
180 size_t read
= php_stream_read(s
, buf
, MIN(length
, sizeof(buf
)));
183 cb(cb_arg
, buf
, read
);
186 if (read
< MIN(length
, sizeof(buf
))) {
199 PHP_HTTP_API
size_t php_http_message_body_append(php_http_message_body_t
*body
, const char *buf
, size_t len
)
201 TSRMLS_FETCH_FROM_CTX(body
->ts
);
202 php_stream
*s
= php_http_message_body_stream(body
);
204 php_stream_seek(s
, 0, SEEK_END
);
205 return php_stream_write(s
, buf
, len
);
208 PHP_HTTP_API STATUS
php_http_message_body_add(php_http_message_body_t
*body
, HashTable
*fields
, HashTable
*files
)
210 post_data http_post_data
= {NULL
, NULL
};
211 TSRMLS_FETCH_FROM_CTX(body
->ts
);
213 if (fields
&& SUCCESS
!= recursive_fields(http_post_data
, fields
, NULL TSRMLS_CC
)) {
216 if (files
&& (zend_hash_num_elements(files
) > 0) && (SUCCESS
!= recursive_files(http_post_data
, files
, NULL TSRMLS_CC
))) {
219 if (CURLE_OK
!= curl_formget(http_post_data
[0], body
, (curl_formget_callback
) php_http_message_body_append
)) {
225 PHP_HTTP_API STATUS
php_http_message_body_add_field(php_http_message_body_t
*body
, const char *name
, const char *value_str
, size_t value_len
)
227 return add_field(body
, name
, value_str
, value_len
);
230 PHP_HTTP_API STATUS
php_http_message_body_add_file(php_http_message_body_t
*body
, const char *name
, const char *path
, const char *ctype
)
232 return add_file(body
, name
, path
, ctype
);
235 static inline char *format_key(uint type
, char *str
, ulong num
, const char *prefix
, int numeric_key_for_empty_prefix
) {
236 char *new_key
= NULL
;
238 if (prefix
&& *prefix
) {
239 if (type
== HASH_KEY_IS_STRING
) {
240 spprintf(&new_key
, 0, "%s[%s]", prefix
, str
);
242 spprintf(&new_key
, 0, "%s[%lu]", prefix
, num
);
244 } else if (type
== HASH_KEY_IS_STRING
) {
245 new_key
= estrdup(str
);
246 } else if (numeric_key_for_empty_prefix
) {
247 spprintf(&new_key
, 0, "%lu", num
);
253 static inline STATUS
add_field(php_http_message_body_t
*body
, const char *name
, const char *value_str
, size_t value_len
)
255 post_data http_post_data
= {NULL
, NULL
};
258 err
= curl_formadd(&http_post_data
[0], &http_post_data
[1],
259 CURLFORM_COPYNAME
, name
,
260 CURLFORM_COPYCONTENTS
, value_str
,
261 CURLFORM_CONTENTSLENGTH
, (long) value_len
,
265 if (CURLE_OK
!= err
) {
269 err
= curl_formget(http_post_data
[0], body
, (curl_formget_callback
) php_http_message_body_append
);
271 if (CURLE_OK
!= err
) {
272 curl_formfree(http_post_data
[0]);
276 curl_formfree(http_post_data
[0]);
280 static inline STATUS
add_file(php_http_message_body_t
*body
, const char *name
, const char *path
, const char *ctype
)
282 post_data http_post_data
= {NULL
, NULL
};
285 err
= curl_formadd(&http_post_data
[0], &http_post_data
[1],
286 CURLFORM_COPYNAME
, name
,
288 CURLFORM_CONTENTTYPE
, ctype
? ctype
: "application/octet-stream",
292 if (CURLE_OK
!= err
) {
296 err
= curl_formget(http_post_data
[0], body
, (curl_formget_callback
) php_http_message_body_append
);
298 if (CURLE_OK
!= err
) {
299 curl_formfree(http_post_data
[0]);
303 curl_formfree(http_post_data
[0]);
307 static STATUS
recursive_fields(post_data http_post_data
, HashTable
*fields
, const char *prefix TSRMLS_DC
) {
308 php_http_array_hashkey_t key
= php_http_array_hashkey_init(0);
311 char *new_key
= NULL
;
314 if (fields
&& !fields
->nApplyCount
) {
315 FOREACH_HASH_KEYVAL(pos
, fields
, key
, data_ptr
) {
316 if (key
.type
!= HASH_KEY_IS_STRING
|| *key
.str
) {
317 new_key
= format_key(key
.type
, key
.str
, key
.num
, prefix
, 1);
319 switch (Z_TYPE_PP(data_ptr
)) {
324 ++fields
->nApplyCount
;
325 status
= recursive_fields(http_post_data
, HASH_OF(*data_ptr
), new_key TSRMLS_CC
);
326 --fields
->nApplyCount
;
328 if (SUCCESS
!= status
) {
335 zval
*data
= php_http_zsep(IS_STRING
, *data_ptr
);
337 err
= curl_formadd(&http_post_data
[0], &http_post_data
[1],
338 CURLFORM_COPYNAME
, new_key
,
339 CURLFORM_COPYCONTENTS
, Z_STRVAL_P(data
),
340 CURLFORM_CONTENTSLENGTH
, (long) Z_STRLEN_P(data
),
344 zval_ptr_dtor(&data
);
346 if (CURLE_OK
!= err
) {
363 if (http_post_data
[0]) {
364 curl_formfree(http_post_data
[0]);
367 php_http_error(HE_WARNING
, PHP_HTTP_E_ENCODING
, "Could not encode post fields: %s", curl_easy_strerror(err
));
369 php_http_error(HE_WARNING
, PHP_HTTP_E_ENCODING
, "Could not encode post fields: unknown error");
374 static STATUS
recursive_files(post_data http_post_data
, HashTable
*files
, const char *prefix TSRMLS_DC
) {
375 php_http_array_hashkey_t key
= php_http_array_hashkey_init(0);
378 char *new_key
= NULL
;
381 if (files
&& !files
->nApplyCount
) {
382 FOREACH_HASH_KEYVAL(pos
, files
, key
, data_ptr
) {
383 zval
**file_ptr
, **type_ptr
, **name_ptr
;
385 if (key
.type
!= HASH_KEY_IS_STRING
|| *key
.str
) {
386 new_key
= format_key(key
.type
, key
.str
, key
.num
, prefix
, 0);
388 if (Z_TYPE_PP(data_ptr
) != IS_ARRAY
&& Z_TYPE_PP(data_ptr
) != IS_OBJECT
) {
389 if (new_key
|| key
.type
== HASH_KEY_IS_STRING
) {
390 php_http_error(HE_NOTICE
, PHP_HTTP_E_INVALID_PARAM
, "Unrecognized type of post file array entry '%s'", new_key
? new_key
: key
.str
);
392 php_http_error(HE_NOTICE
, PHP_HTTP_E_INVALID_PARAM
, "Unrecognized type of post file array entry '%lu'", key
.num
);
394 } else if ( SUCCESS
!= zend_hash_find(HASH_OF(*data_ptr
), "name", sizeof("name"), (void *) &name_ptr
) ||
395 SUCCESS
!= zend_hash_find(HASH_OF(*data_ptr
), "type", sizeof("type"), (void *) &type_ptr
) ||
396 SUCCESS
!= zend_hash_find(HASH_OF(*data_ptr
), "file", sizeof("file"), (void *) &file_ptr
)) {
399 ++files
->nApplyCount
;
400 status
= recursive_files(http_post_data
, HASH_OF(*data_ptr
), new_key TSRMLS_CC
);
401 --files
->nApplyCount
;
403 if (SUCCESS
!= status
) {
408 zval
*file
= php_http_zsep(IS_STRING
, *file_ptr
);
409 zval
*type
= php_http_zsep(IS_STRING
, *type_ptr
);
410 zval
*name
= php_http_zsep(IS_STRING
, *name_ptr
);
412 if (SUCCESS
!= php_check_open_basedir(Z_STRVAL_P(file
) TSRMLS_CC
)) {
416 /* this is blatant but should be sufficient for most cases */
417 if (strncasecmp(Z_STRVAL_P(file
), "file://", lenof("file://"))) {
418 path
= Z_STRVAL_P(file
);
420 path
= Z_STRVAL_P(file
) + lenof("file://");
424 char *tmp_key
= format_key(HASH_KEY_IS_STRING
, Z_STRVAL_P(name
), 0, new_key
, 0);
425 STR_SET(new_key
, tmp_key
);
428 err
= curl_formadd(&http_post_data
[0], &http_post_data
[1],
429 CURLFORM_COPYNAME
, new_key
? new_key
: Z_STRVAL_P(name
),
431 CURLFORM_CONTENTTYPE
, Z_STRVAL_P(type
),
435 zval_ptr_dtor(&file
);
436 zval_ptr_dtor(&type
);
437 zval_ptr_dtor(&name
);
439 if (CURLE_OK
!= err
) {
454 if (http_post_data
[0]) {
455 curl_formfree(http_post_data
[0]);
458 php_http_error(HE_WARNING
, PHP_HTTP_E_ENCODING
, "Could not encode post files: %s", curl_easy_strerror(err
));
460 php_http_error(HE_WARNING
, PHP_HTTP_E_ENCODING
, "Could not encode post files: unknown error");
467 #define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpMessageBody, method, 0, req_args)
468 #define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpMessageBody, method, 0)
469 #define PHP_HTTP_MESSAGE_BODY_ME(method, visibility) PHP_ME(HttpMessageBody, method, PHP_HTTP_ARGS(HttpMessageBody, method), visibility)
471 PHP_HTTP_BEGIN_ARGS(__construct
, 0)
472 PHP_HTTP_ARG_VAL(stream
, 0)
475 PHP_HTTP_EMPTY_ARGS(__toString
);
477 PHP_HTTP_BEGIN_ARGS(toStream
, 1)
478 PHP_HTTP_ARG_VAL(stream
, 0)
481 PHP_HTTP_BEGIN_ARGS(toCallback
, 1)
482 PHP_HTTP_ARG_VAL(callback
, 0)
485 PHP_HTTP_BEGIN_ARGS(append
, 1)
486 PHP_HTTP_ARG_VAL(string
, 0)
489 PHP_HTTP_BEGIN_ARGS(add
, 0)
490 PHP_HTTP_ARG_VAL(fields
, 0)
491 PHP_HTTP_ARG_VAL(files
, 0)
495 zend_class_entry
*php_http_message_body_class_entry
;
496 zend_function_entry php_http_message_body_method_entry
[] = {
497 PHP_HTTP_MESSAGE_BODY_ME(__construct
, ZEND_ACC_PUBLIC
|ZEND_ACC_CTOR
)
498 PHP_HTTP_MESSAGE_BODY_ME(__toString
, ZEND_ACC_PUBLIC
)
499 PHP_MALIAS(HttpMessageBody
, toString
, __toString
, args_for_HttpMessageBody___toString
, ZEND_ACC_PUBLIC
)
500 PHP_HTTP_MESSAGE_BODY_ME(toStream
, ZEND_ACC_PUBLIC
)
501 PHP_HTTP_MESSAGE_BODY_ME(toCallback
, ZEND_ACC_PUBLIC
)
502 PHP_HTTP_MESSAGE_BODY_ME(append
, ZEND_ACC_PUBLIC
)
503 PHP_HTTP_MESSAGE_BODY_ME(add
, ZEND_ACC_PUBLIC
)
506 static zend_object_handlers php_http_message_body_object_handlers
;
508 PHP_MINIT_FUNCTION(http_message_body
)
510 PHP_HTTP_REGISTER_CLASS(http
\\message
, Body
, http_message_body
, php_http_object_class_entry
, 0);
511 php_http_message_body_class_entry
->create_object
= php_http_message_body_object_new
;
512 memcpy(&php_http_message_body_object_handlers
, zend_get_std_object_handlers(), sizeof(zend_object_handlers
));
513 php_http_message_body_object_handlers
.clone_obj
= php_http_message_body_object_clone
;
518 zend_object_value
php_http_message_body_object_new(zend_class_entry
*ce TSRMLS_DC
)
520 return php_http_message_body_object_new_ex(ce
, NULL
, NULL TSRMLS_CC
);
523 zend_object_value
php_http_message_body_object_new_ex(zend_class_entry
*ce
, php_http_message_body_t
*body
, php_http_message_body_object_t
**ptr TSRMLS_DC
)
525 zend_object_value ov
;
526 php_http_message_body_object_t
*o
;
528 o
= ecalloc(1, sizeof(php_http_message_body_object_t
));
529 zend_object_std_init((zend_object
*) o
, php_http_message_body_class_entry TSRMLS_CC
);
530 object_properties_init((zend_object
*) o
, ce
);
540 ov
.handle
= zend_objects_store_put((zend_object
*) o
, NULL
, php_http_message_body_object_free
, NULL TSRMLS_CC
);
541 ov
.handlers
= &php_http_message_body_object_handlers
;
546 zend_object_value
php_http_message_body_object_clone(zval
*object TSRMLS_DC
)
548 zend_object_value new_ov
;
549 php_http_message_body_object_t
*new_obj
= NULL
;
550 php_http_message_body_object_t
*old_obj
= zend_object_store_get_object(object TSRMLS_CC
);
552 new_ov
= php_http_message_body_object_new_ex(old_obj
->zo
.ce
, php_http_message_body_copy(old_obj
->body
, NULL
, 1), &new_obj
);
553 zend_objects_clone_members(&new_obj
->zo
, new_ov
, &old_obj
->zo
, Z_OBJ_HANDLE_P(object
) TSRMLS_CC
);
558 void php_http_message_body_object_free(void *object TSRMLS_DC
)
560 php_http_message_body_object_t
*obj
= object
;
562 php_http_message_body_free(&obj
->body
);
564 zend_object_std_dtor((zend_object
*) obj TSRMLS_CC
);
568 PHP_METHOD(HttpMessageBody
, __construct
)
570 php_http_message_body_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
574 with_error_handling(EH_THROW
, PHP_HTTP_EX_CE(runtime
)) {
575 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "r", &zstream
)) {
576 php_stream_from_zval(stream
, &zstream
);
580 php_http_message_body_dtor(obj
->body
);
582 obj
->body
= php_http_message_body_init(obj
->body
, stream TSRMLS_CC
);
585 } end_error_handling();
588 PHP_METHOD(HttpMessageBody
, __toString
)
590 if (SUCCESS
== zend_parse_parameters_none()) {
591 php_http_message_body_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
595 php_http_message_body_to_string(obj
->body
, &str
, &len
, 0, 0);
597 RETURN_STRINGL(str
, len
, 0);
600 RETURN_EMPTY_STRING();
603 PHP_METHOD(HttpMessageBody
, toStream
)
606 long offset
= 0, forlen
= 0;
608 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "r|ll", &zstream
, &offset
, &forlen
)) {
610 php_http_message_body_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
612 php_stream_from_zval(stream
, &zstream
);
613 php_http_message_body_to_stream(obj
->body
, stream
, offset
, forlen
);
621 zend_fcall_info
*fci
;
622 zend_fcall_info_cache
*fcc
;
625 static size_t pass(void *cb_arg
, const char *str
, size_t len TSRMLS_DC
)
627 struct fcd
*fcd
= cb_arg
;
630 MAKE_STD_ZVAL(zdata
);
631 ZVAL_STRINGL(zdata
, str
, len
, 1);
632 if (SUCCESS
== zend_fcall_info_argn(fcd
->fci TSRMLS_CC
, 2, fcd
->fcz
, zdata
)) {
633 zend_fcall_info_call(fcd
->fci
, fcd
->fcc
, NULL
, NULL TSRMLS_CC
);
634 zend_fcall_info_args_clear(fcd
->fci
, 0);
636 zval_ptr_dtor(&zdata
);
640 PHP_METHOD(HttpMessageBody
, toCallback
)
643 long offset
= 0, forlen
= 0;
645 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "f|ll", &fcd
.fci
, &fcd
.fcc
, &offset
, &forlen
)) {
646 php_http_message_body_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
649 php_http_message_body_to_callback(obj
->body
, pass
, &fcd
, offset
, forlen
);
650 zval_ptr_dtor(&fcd
.fcz
);
656 PHP_METHOD(HttpMessageBody
, append
)
661 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s", &str
, &len
)) {
662 php_http_message_body_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
664 RETURN_LONG(php_http_message_body_append(obj
->body
, str
, len
));
669 PHP_METHOD(HttpMessageBody
, add
)
671 HashTable
*fields
= NULL
, *files
= NULL
;
673 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|hh", &fields
, &files
)) {
674 php_http_message_body_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
676 RETURN_SUCCESS(php_http_message_body_add(obj
->body
, fields
, files
));
685 * vim600: noet sw=4 ts=4 fdm=marker
686 * vim<600: noet sw=4 ts=4