X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=blobdiff_plain;f=php_http_message_body.c;h=41f036c1cc2c29de97624a7534cab82f99cdc559;hp=a33e330a2fa6d421fd06e2e2d74fba1a65735f91;hb=0e89d506db19cab9581a7317e0494001941a8c70;hpb=4d68865693332389b875e4466f8b5368c0876e15 diff --git a/php_http_message_body.c b/php_http_message_body.c index a33e330..41f036c 100644 --- a/php_http_message_body.c +++ b/php_http_message_body.c @@ -123,8 +123,12 @@ PHP_HTTP_API char *php_http_message_body_etag(php_http_message_body_t *body) } else { php_http_etag_t *etag = php_http_etag_init(PHP_HTTP_G->env.etag_mode TSRMLS_CC); - php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_etag_update, etag, 0, 0); - return php_http_etag_finish(etag); + if (etag) { + php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_etag_update, etag, 0, 0); + return php_http_etag_finish(etag); + } else { + return NULL; + } } } @@ -238,9 +242,10 @@ PHP_HTTP_API STATUS php_http_message_body_add_field(php_http_message_body_t *bod BOUNDARY_OPEN(body); php_http_message_body_appendf( body, - "Content-Disposition: form-data; name=\"%s\"" PHP_HTTP_CRLF - "" PHP_HTTP_CRLF, - safe_name); + "Content-Disposition: form-data; name=\"%s\"" PHP_HTTP_CRLF + "" PHP_HTTP_CRLF, + safe_name + ); php_http_message_body_append(body, value_str, value_len); BOUNDARY_CLOSE(body); @@ -250,8 +255,6 @@ PHP_HTTP_API STATUS php_http_message_body_add_field(php_http_message_body_t *bod PHP_HTTP_API STATUS php_http_message_body_add_file(php_http_message_body_t *body, const char *name, const char *ctype, const char *path, php_stream *in) { - php_stream_statbuf ssb = {{0}}; - php_stream_filter *tef = NULL; char *safe_name, *path_dup = estrdup(path); TSRMLS_FETCH_FROM_CTX(body->ts); @@ -261,39 +264,14 @@ PHP_HTTP_API STATUS php_http_message_body_add_file(php_http_message_body_t *body php_http_message_body_appendf( body, "Content-Disposition: attachment; name=\"%s\"; filename=\"%s\"" PHP_HTTP_CRLF - "Content-Type: %s" PHP_HTTP_CRLF, + "Content-Transfer-Encoding: binary" PHP_HTTP_CRLF + "Content-Type: %s" PHP_HTTP_CRLF + PHP_HTTP_CRLF, safe_name, basename(path_dup), ctype ); - - if (SUCCESS == php_stream_stat(in, &ssb)) { - php_http_message_body_appendf( - body, - "Content-Length: %zu" PHP_HTTP_CRLF - "" PHP_HTTP_CRLF, - ssb.sb.st_size - ); - } else { - php_http_message_body_append( - body, - ZEND_STRL( - "Transfer-Encoding: chunked" PHP_HTTP_CRLF - "" PHP_HTTP_CRLF - ) - ); - - if ((tef = php_http_filter_factory.create_filter("http.chunked_encode", NULL, 0 TSRMLS_CC))) { - php_stream_filter_append(&in->readfilters, tef); - } - } - php_stream_copy_to_stream_ex(in, php_http_message_body_stream(body), PHP_STREAM_COPY_ALL, NULL); BOUNDARY_CLOSE(body); - if (tef) { - php_stream_filter_remove(tef, 1 TSRMLS_CC); - php_stream_filter_free(tef TSRMLS_CC); - } - efree(safe_name); efree(path_dup); @@ -312,7 +290,7 @@ static inline char *format_key(uint type, char *str, ulong num, const char *pref } else if (type == HASH_KEY_IS_STRING) { new_key = estrdup(str); } else { - estrdup(""); + new_key = estrdup(""); } return new_key; @@ -374,13 +352,16 @@ static STATUS add_recursive_files(php_http_message_body_t *body, const char *nam if (!ht->nApplyCount) { ++ht->nApplyCount; FOREACH_HASH_KEYVAL(pos, ht, key, val) { - char *str = format_key(key.type, key.str, key.num, name); - if (SUCCESS != add_recursive_files(body, str, *val)) { + if (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT) { + char *str = format_key(key.type, key.str, key.num, name); + + if (SUCCESS != add_recursive_files(body, str, *val)) { + efree(str); + --ht->nApplyCount; + return FAILURE; + } efree(str); - --ht->nApplyCount; - return FAILURE; } - efree(str); } --ht->nApplyCount; } @@ -395,9 +376,7 @@ static STATUS add_recursive_files(php_http_message_body_t *body, const char *nam } else { zval *tmp = php_http_ztyp(IS_STRING, *zdata); - if ((stream = php_stream_temp_new())) { - php_stream_write(stream, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); - } + stream = php_stream_memory_open(TEMP_STREAM_READONLY, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); zval_ptr_dtor(&tmp); } } else { @@ -414,6 +393,7 @@ static STATUS add_recursive_files(php_http_message_body_t *body, const char *nam efree(key); zval_ptr_dtor(&znc); + zval_ptr_dtor(&ztc); zval_ptr_dtor(&zfc); if (!zdata || Z_TYPE_PP(zdata) != IS_RESOURCE) { php_stream_close(stream); @@ -424,6 +404,110 @@ static STATUS add_recursive_files(php_http_message_body_t *body, const char *nam } } +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) @@ -449,8 +533,8 @@ PHP_HTTP_BEGIN_ARGS(append, 1) PHP_HTTP_END_ARGS; PHP_HTTP_BEGIN_ARGS(add, 0) - PHP_HTTP_ARG_VAL(fields, 0) - PHP_HTTP_ARG_VAL(files, 0) + PHP_HTTP_ARG_ARR(fields, 1, 0) + PHP_HTTP_ARG_ARR(files, 1, 0) PHP_HTTP_END_ARGS; PHP_HTTP_EMPTY_ARGS(etag); @@ -653,7 +737,7 @@ PHP_METHOD(HttpMessageBody, add) { HashTable *fields = NULL, *files = NULL; - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|hh", &fields, &files)) { + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|h!h!", &fields, &files)) { php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); RETURN_SUCCESS(php_http_message_body_add(obj->body, fields, files));