STR_FREE(chunk);
}
-PHP_HTTP_BUFFER_API ssize_t php_http_buffer_passthru(php_http_buffer_t *s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *passin_arg, php_http_buffer_pass_func_t passon, void *passon_arg TSRMLS_DC)
+PHP_HTTP_BUFFER_API ssize_t php_http_buffer_passthru(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *passin_arg, php_http_buffer_pass_func_t passon, void *passon_arg TSRMLS_DC)
{
- size_t passed_on = 0, passed_in = php_http_buffer_chunked_input(&s, chunk_size, passin, passin_arg TSRMLS_CC);
+ size_t passed_on = 0, passed_in = php_http_buffer_chunked_input(s, chunk_size, passin, passin_arg TSRMLS_CC);
if (passed_in == PHP_HTTP_BUFFER_PASS0) {
return passed_in;
}
- if (passed_in) {
- passed_on = passon(passon_arg, s->data, passed_in TSRMLS_CC);
+ if (passed_in || (*s)->used) {
+ passed_on = passon(passon_arg, (*s)->data, (*s)->used TSRMLS_CC);
if (passed_on == PHP_HTTP_BUFFER_PASS0) {
return passed_on;
}
if (passed_on) {
- php_http_buffer_cut(s, 0, passed_on);
+ php_http_buffer_cut(*s, 0, passed_on);
}
}
/* stores data in a php_http_buffer_t until it reaches chunk_size */
PHP_HTTP_BUFFER_API size_t php_http_buffer_chunk_buffer(php_http_buffer_t **s, const char *data, size_t data_len, char **chunk, size_t chunk_size);
-typedef size_t (*php_http_buffer_pass_func_t)(void *opaque, const char *, size_t TSRMLS_DC);
+typedef size_t (*php_http_buffer_pass_func_t)(void *opaque, char *, size_t TSRMLS_DC);
+
+PHP_HTTP_BUFFER_API ssize_t php_http_buffer_passthru(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *passin_arg, php_http_buffer_pass_func_t passon, void *passon_arg TSRMLS_DC);
/* wrapper around php_http_buffer_chunk_buffer, which passes available chunks to passthru() */
PHP_HTTP_BUFFER_API void php_http_buffer_chunked_output(php_http_buffer_t **s, const char *data, size_t data_len, size_t chunk_size, php_http_buffer_pass_func_t passout, void *opaque TSRMLS_DC);
zend_object_value ov;
php_http_message_body_t *body = php_http_env_get_request_body(TSRMLS_C);
- if (SUCCESS == php_http_new(&ov, class_entry, (php_http_new_t) php_http_message_body_object_new_ex, php_http_message_body_class_entry, body, NULL TSRMLS_CC)) {
+ if (SUCCESS == php_http_new(&ov, class_entry, (php_http_new_t) php_http_message_body_object_new_ex, php_http_message_body_class_entry, php_http_message_body_copy(body, NULL, 0), NULL TSRMLS_CC)) {
RETURN_OBJVAL(ov, 0);
}
}
if (!old || old->type || zend_hash_num_elements(&old->hdrs) || PHP_HTTP_BUFFER_LEN(old)) {
(*message) = php_http_message_init(NULL, 0 TSRMLS_CC);
(*message)->parent = old;
- (*headers) = &((*message)->hdrs);
+ if (headers) {
+ (*headers) = &((*message)->hdrs);
+ }
}
if (info) {
return ret;
}
+PHP_HTTP_API zend_bool php_http_message_is_multipart(php_http_message_t *msg, char **boundary)
+{
+ zval *ct = php_http_message_header(msg, ZEND_STRL("Content-Type"), 1);
+ zend_bool is_multipart = 0;
+ TSRMLS_FETCH_FROM_CTX(msg->ts);
+
+ if (ct) {
+ php_http_params_opts_t popts;
+ HashTable params;
+
+ ZEND_INIT_SYMTABLE(¶ms);
+ php_http_params_opts_default_get(&popts);
+ popts.input.str = Z_STRVAL_P(ct);
+ popts.input.len = Z_STRLEN_P(ct);
+
+ if (php_http_params_parse(¶ms, &popts TSRMLS_CC)) {
+ zval **cur, **arg;
+ char *ct_str;
+
+ zend_hash_internal_pointer_reset(¶ms);
+
+ if (SUCCESS == zend_hash_get_current_data(¶ms, (void *) &cur)
+ && Z_TYPE_PP(cur) == IS_ARRAY
+ && HASH_KEY_IS_STRING == zend_hash_get_current_key(¶ms, &ct_str, NULL, 0)
+ ) {
+ if (php_http_match(ct_str, "multipart", PHP_HTTP_MATCH_WORD)) {
+ is_multipart = 1;
+
+ /* get boundary */
+ if (boundary
+ && SUCCESS == zend_hash_find(Z_ARRVAL_PP(cur), ZEND_STRS("arguments"), (void *) &arg)
+ && Z_TYPE_PP(arg) == IS_ARRAY
+ ) {
+ zval **val;
+ HashPosition pos;
+ php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+
+ FOREACH_KEYVAL(pos, *arg, key, val) {
+ if (key.type == HASH_KEY_IS_STRING && !strcasecmp(key.str, "boundary")) {
+ zval *bnd = php_http_ztyp(IS_STRING, *val);
+
+ if (Z_STRLEN_P(bnd)) {
+ *boundary = estrndup(Z_STRVAL_P(bnd), Z_STRLEN_P(bnd));
+ }
+ zval_ptr_dtor(&bnd);
+ }
+ }
+ }
+ }
+ }
+ }
+ zend_hash_destroy(¶ms);
+ zval_ptr_dtor(&ct);
+ }
+
+ return is_multipart;
+}
/* */
PHP_HTTP_API void php_http_message_set_type(php_http_message_t *message, php_http_message_type_t type)
PHP_HTTP_END_ARGS;
PHP_HTTP_EMPTY_ARGS(reverse);
+PHP_HTTP_BEGIN_ARGS(isMultipart, 0)
+ PHP_HTTP_ARG_VAL(boundary, 1)
+PHP_HTTP_END_ARGS;
+PHP_HTTP_EMPTY_ARGS(splitMultipartBody);
+
static zval *php_http_message_object_read_prop(zval *object, zval *member, int type, const zend_literal *literal_key TSRMLS_DC);
static void php_http_message_object_write_prop(zval *object, zval *member, zval *value, const zend_literal *literal_key TSRMLS_DC);
static zval **php_http_message_object_get_prop_ptr(zval *object, zval *member, const zend_literal *literal_key TSRMLS_DC);
PHP_HTTP_MESSAGE_ME(prepend, ZEND_ACC_PUBLIC)
PHP_HTTP_MESSAGE_ME(reverse, ZEND_ACC_PUBLIC)
+ PHP_HTTP_MESSAGE_ME(isMultipart, ZEND_ACC_PUBLIC)
+ PHP_HTTP_MESSAGE_ME(splitMultipartBody, ZEND_ACC_PUBLIC)
+
EMPTY_FUNCTION_ENTRY
};
static zend_object_handlers php_http_message_object_handlers;
}
}
+PHP_METHOD(HttpMessage, isMultipart)
+{
+ zval *zboundary = NULL;
+
+ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z", &zboundary)) {
+ php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+ char *boundary = NULL;
+
+ RETVAL_BOOL(php_http_message_is_multipart(obj->message, zboundary ? &boundary : NULL));
+
+ if (zboundary && boundary) {
+ zval_dtor(zboundary);
+ ZVAL_STRING(zboundary, boundary, 0);
+ }
+ }
+}
+
+PHP_METHOD(HttpMessage, splitMultipartBody)
+{
+ if (SUCCESS == zend_parse_parameters_none()) {
+ php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+ char *boundary = NULL;
+
+ if (php_http_message_is_multipart(obj->message, &boundary)) {
+ php_http_message_t *msg;
+
+ if ((msg = php_http_message_body_split(&obj->message->body, boundary))) {
+ RETVAL_OBJVAL(php_http_message_object_new_ex(php_http_message_class_entry, msg, NULL TSRMLS_CC), 0);
+ }
+ }
+ STR_FREE(boundary);
+ }
+}
+
PHP_METHOD(HttpMessage, count)
{
if (SUCCESS == zend_parse_parameters_none()) {
PHP_HTTP_API void php_http_message_set_info(php_http_message_t *message, php_http_info_t *info);
PHP_HTTP_API zval *php_http_message_header(php_http_message_t *msg, char *key_str, size_t key_len, int join);
+PHP_HTTP_API zend_bool php_http_message_is_multipart(php_http_message_t *msg, char **boundary);
PHP_HTTP_API void php_http_message_to_string(php_http_message_t *msg, char **string, size_t *length);
PHP_HTTP_API void php_http_message_to_struct(php_http_message_t *msg, zval *strct);
PHP_METHOD(HttpMessage, prepend);
PHP_METHOD(HttpMessage, reverse);
+PHP_METHOD(HttpMessage, isMultipart);
+PHP_METHOD(HttpMessage, splitMultipartBody);
#endif
/*
}
}
+struct splitbody_arg {
+ php_http_buffer_t buf;
+ php_http_message_parser_t *parser;
+ char *boundary_str;
+ size_t boundary_len;
+ size_t consumed;
+};
+
+static size_t splitbody(void *opaque, char *buf, size_t len TSRMLS_DC)
+{
+ struct splitbody_arg *arg = opaque;
+ const char *boundary = NULL;
+ size_t consumed = 0;
+ int first_boundary;
+
+ do {
+ first_boundary = !(consumed || arg->consumed);
+
+ if ((boundary = php_http_locate_str(buf, len, arg->boundary_str + first_boundary, arg->boundary_len - first_boundary))) {
+ size_t real_boundary_len = arg->boundary_len - 1, cut;
+ const char *real_boundary = boundary + !first_boundary;
+
+ if (buf + len <= real_boundary + real_boundary_len) {
+ /* if we just have enough data for the boundary, it's just a byte too less */
+ arg->consumed += consumed;
+ return consumed;
+ }
+
+ if (!first_boundary) {
+ /* this is not the first boundary, read rest of this message */
+ php_http_buffer_append(&arg->buf, buf, real_boundary - buf);
+ php_http_message_parser_parse(arg->parser, &arg->buf, 0, &arg->parser->message);
+ }
+
+ /* move after the boundary */
+ cut = real_boundary - buf + real_boundary_len;
+ buf += cut;
+ len -= cut;
+ consumed += cut;
+
+ if (buf == php_http_locate_bin_eol(buf, len, NULL)) {
+ if (!first_boundary) {
+ /* advance messages */
+ php_http_message_t *msg;
+
+ msg = php_http_message_init(NULL, 0 TSRMLS_CC);
+ msg->parent = arg->parser->message;
+ arg->parser->message = msg;
+ }
+ } else {
+ /* is this the last boundary? */
+ if (*buf == '-') {
+ /* ignore the rest */
+ consumed += len;
+ len = 0;
+ } else {
+ /* let this be garbage */
+ php_http_error(HE_WARNING, PHP_HTTP_E_MESSAGE_BODY, "Malformed multipart boundary at pos %zu", consumed);
+ return -1;
+ }
+ }
+ }
+ } while (boundary && len);
+
+ /* let there be room for the next boundary */
+ if (len > arg->boundary_len) {
+ consumed += len - arg->boundary_len;
+ php_http_buffer_append(&arg->buf, buf, len - arg->boundary_len);
+ php_http_message_parser_parse(arg->parser, &arg->buf, 0, &arg->parser->message);
+ }
+
+ arg->consumed += consumed;
+ return consumed;
+}
+
+PHP_HTTP_API php_http_message_t *php_http_message_body_split(php_http_message_body_t *body, const char *boundary)
+{
+ php_stream *s = php_http_message_body_stream(body);
+ php_http_buffer_t *tmp = NULL;
+ php_http_message_t *msg = NULL;
+ struct splitbody_arg arg;
+ TSRMLS_FETCH_FROM_CTX(body->ts);
+
+ php_http_buffer_init(&arg.buf);
+ arg.parser = php_http_message_parser_init(NULL TSRMLS_CC);
+ arg.boundary_len = spprintf(&arg.boundary_str, 0, "\n--%s", boundary);
+ arg.consumed = 0;
+
+ php_stream_rewind(s);
+ while (!php_stream_eof(s)) {
+ php_http_buffer_passthru(&tmp, 0x1000, (php_http_buffer_pass_func_t) _php_stream_read, s, splitbody, &arg TSRMLS_CC);
+ }
+
+ msg = arg.parser->message;
+ arg.parser->message = NULL;
+
+ php_http_buffer_free(&tmp);
+ php_http_message_parser_free(&arg.parser);
+ php_http_buffer_dtor(&arg.buf);
+ STR_FREE(arg.boundary_str);
+
+ return msg;
+}
+
/* PHP */
#define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpMessageBody, method, 0, req_args)
#endif
} php_http_message_body_t;
-typedef enum php_http_message_body_file_is {
- PHP_HTTP_MESSAGE_BODY_FILE_IS_EMPTY,
- PHP_HTTP_MESSAGE_BODY_FILE_IS_DATA,
- PHP_HTTP_MESSAGE_BODY_FILE_IS_PATH,
- PHP_HTTP_MESSAGE_BODY_FILE_IS_STREAM
-} php_http_message_body_file_is_t;
-
-typedef struct php_http_message_body_file {
- php_http_message_body_file_is_t type;
- size_t length;
- union {
- char *data;
- char *path;
- php_stream *stream;
- } file;
-} php_http_message_body_file_t;
+typedef struct php_http_message php_http_message_t;
PHP_HTTP_API php_http_message_body_t *php_http_message_body_init(php_http_message_body_t *body, php_stream *stream TSRMLS_DC);
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);
#define php_http_message_body_mtime(b) (php_http_message_body_stat((b))->sb.st_mtime)
PHP_HTTP_API char *php_http_message_body_etag(php_http_message_body_t *body);
PHP_HTTP_API const char *php_http_message_body_boundary(php_http_message_body_t *body);
+PHP_HTTP_API php_http_message_t *php_http_message_body_split(php_http_message_body_t *body, const char *boundary);
static inline php_stream *php_http_message_body_stream(php_http_message_body_t *body)
{
len = buffer->used;
cut = len;
- php_http_message_parser_state_push(parser, 2, !buffer->used?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
+ php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
break;
}
for (eol = bin; eol - bin < len; ++eol) {
if (*eol == '\r' || *eol == '\n') {
- *eol_len = eol ? ((eol[0] == '\r' && eol[1] == '\n') ? 2 : 1) : 0;
+ if (eol_len) {
+ *eol_len = eol ? ((eol[0] == '\r' && eol[1] == '\n') ? 2 : 1) : 0;
+ }
return eol;
}
}