LCOV - code coverage report
Current view: top level - http - php_http_message_body.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 370 408 90.7 %
Date: 2014-11-03 12:21:11 Functions: 38 38 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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-2014, Michael Wallner <mike@php.net>            |
      10             :     +--------------------------------------------------------------------+
      11             : */
      12             : 
      13             : #include "php_http_api.h"
      14             : 
      15             : #include <ext/standard/php_lcg.h>
      16             : 
      17             : #define BOUNDARY_OPEN(body) \
      18             :         do {\
      19             :                 size_t size = php_http_message_body_size(body); \
      20             :                 if (size) { \
      21             :                         php_stream_truncate_set_size(php_http_message_body_stream(body), size - lenof("--" PHP_HTTP_CRLF)); \
      22             :                         php_http_message_body_append(body, ZEND_STRL(PHP_HTTP_CRLF)); \
      23             :                 } else { \
      24             :                         php_http_message_body_appendf(body, "--%s" PHP_HTTP_CRLF, php_http_message_body_boundary(body)); \
      25             :                 } \
      26             :         } while(0)
      27             : 
      28             : #define BOUNDARY_CLOSE(body) \
      29             :                 php_http_message_body_appendf(body, PHP_HTTP_CRLF "--%s--" PHP_HTTP_CRLF, php_http_message_body_boundary(body))
      30             : 
      31             : static STATUS add_recursive_fields(php_http_message_body_t *body, const char *name, zval *value);
      32             : static STATUS add_recursive_files(php_http_message_body_t *body, const char *name, zval *value);
      33             : 
      34         573 : php_http_message_body_t *php_http_message_body_init(php_http_message_body_t **body_ptr, php_stream *stream TSRMLS_DC)
      35             : {
      36             :         php_http_message_body_t *body;
      37             : 
      38         573 :         if (body_ptr && *body_ptr) {
      39         176 :                 body = *body_ptr;
      40         176 :                 ++body->refcount;
      41         176 :                 return body;
      42             :         }
      43             :         
      44         397 :         body = ecalloc(1, sizeof(php_http_message_body_t));
      45         397 :         body->refcount = 1;
      46             : 
      47         397 :         if (stream) {
      48          33 :                 php_stream_auto_cleanup(stream);
      49          33 :                 body->stream_id = php_stream_get_resource_id(stream);
      50          33 :                 zend_list_addref(body->stream_id);
      51             :         } else {
      52         364 :                 stream = php_stream_temp_create(TEMP_STREAM_DEFAULT, 0xffff);
      53         364 :                 php_stream_auto_cleanup(stream);
      54         364 :                 body->stream_id = php_stream_get_resource_id(stream);
      55             :         }
      56             :         TSRMLS_SET_CTX(body->ts);
      57             : 
      58         397 :         if (body_ptr) {
      59           0 :                 *body_ptr = body;
      60             :         }
      61             : 
      62         397 :         return body;
      63             : }
      64             : 
      65          27 : unsigned php_http_message_body_addref(php_http_message_body_t *body)
      66             : {
      67          27 :         return ++body->refcount;
      68             : }
      69             : 
      70         106 : php_http_message_body_t *php_http_message_body_copy(php_http_message_body_t *from, php_http_message_body_t *to)
      71             : {
      72         106 :         if (from) {
      73             :                 TSRMLS_FETCH_FROM_CTX(from->ts);
      74             :                 
      75         106 :                 if (to) {
      76           0 :                         php_stream_truncate_set_size(php_http_message_body_stream(to), 0);
      77             :                 } else {
      78         106 :                         to = php_http_message_body_init(NULL, NULL TSRMLS_CC);
      79             :                 }
      80         106 :                 php_http_message_body_to_stream(from, php_http_message_body_stream(to), 0, 0);
      81             : 
      82         106 :                 if (to->boundary) {
      83           0 :                         efree(to->boundary);
      84             :                 }
      85         106 :                 if (from->boundary) {
      86           0 :                         to->boundary = estrdup(from->boundary);
      87             :                 }
      88             :         } else {
      89           0 :                 to = NULL;
      90             :         }
      91         106 :         return to;
      92             : }
      93             : 
      94         600 : void php_http_message_body_free(php_http_message_body_t **body_ptr)
      95             : {
      96         600 :         if (*body_ptr) {
      97         600 :                 php_http_message_body_t *body = *body_ptr;
      98             : 
      99         600 :                 if (!--body->refcount) {
     100             :                         TSRMLS_FETCH_FROM_CTX(body->ts);
     101             :                         /* NOFIXME: shows leakinfo in DEBUG mode */
     102         397 :                         zend_list_delete(body->stream_id);
     103         397 :                         STR_FREE(body->boundary);
     104         397 :                         efree(body);
     105             :                 }
     106         600 :                 *body_ptr = NULL;
     107             :         }
     108         600 : }
     109             : 
     110         469 : const php_stream_statbuf *php_http_message_body_stat(php_http_message_body_t *body)
     111             : {
     112             :         TSRMLS_FETCH_FROM_CTX(body->ts);
     113         469 :         php_stream_stat(php_http_message_body_stream(body), &body->ssb);
     114         469 :         return &body->ssb;
     115             : }
     116             : 
     117          12 : const char *php_http_message_body_boundary(php_http_message_body_t *body)
     118             : {
     119          12 :         if (!body->boundary) {
     120             :                 union { double dbl; int num[2]; } data;
     121             :                 TSRMLS_FETCH_FROM_CTX(body->ts);
     122             : 
     123           4 :                 data.dbl = php_combined_lcg(TSRMLS_C);
     124           4 :                 spprintf(&body->boundary, 0, "%x.%x", data.num[0], data.num[1]);
     125             :         }
     126          12 :         return body->boundary;
     127             : }
     128             : 
     129          56 : char *php_http_message_body_etag(php_http_message_body_t *body)
     130             : {
     131          56 :         const php_stream_statbuf *ssb = php_http_message_body_stat(body);
     132             :         TSRMLS_FETCH_FROM_CTX(body->ts);
     133             : 
     134             :         /* real file or temp buffer ? */
     135          56 :         if (ssb && ssb->sb.st_mtime) {
     136             :                 char *etag;
     137             : 
     138           7 :                 spprintf(&etag, 0, "%lx-%lx-%lx", ssb->sb.st_ino, ssb->sb.st_mtime, ssb->sb.st_size);
     139           7 :                 return etag;
     140             :         } else {
     141          49 :                 php_http_etag_t *etag = php_http_etag_init(PHP_HTTP_G->env.etag_mode TSRMLS_CC);
     142             : 
     143          49 :                 if (etag) {
     144          49 :                         php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_etag_update, etag, 0, 0);
     145          49 :                         return php_http_etag_finish(etag);
     146             :                 } else {
     147           0 :                         return NULL;
     148             :                 }
     149             :         }
     150             : }
     151             : 
     152          19 : void php_http_message_body_to_string(php_http_message_body_t *body, char **buf, size_t *len, off_t offset, size_t forlen)
     153             : {
     154          19 :         php_stream *s = php_http_message_body_stream(body);
     155             :         TSRMLS_FETCH_FROM_CTX(body->ts);
     156             : 
     157          19 :         php_stream_seek(s, offset, SEEK_SET);
     158          19 :         if (!forlen) {
     159          19 :                 forlen = -1;
     160             :         }
     161          19 :         *len = php_stream_copy_to_mem(s, buf, forlen, 0);
     162          19 : }
     163             : 
     164         107 : STATUS php_http_message_body_to_stream(php_http_message_body_t *body, php_stream *dst, off_t offset, size_t forlen)
     165             : {
     166         107 :         php_stream *s = php_http_message_body_stream(body);
     167             :         TSRMLS_FETCH_FROM_CTX(body->ts);
     168             : 
     169         107 :         php_stream_seek(s, offset, SEEK_SET);
     170             : 
     171         107 :         if (!forlen) {
     172         107 :                 forlen = -1;
     173             :         }
     174         107 :         return php_stream_copy_to_stream_ex(s, dst, forlen, NULL);
     175             : }
     176             : 
     177          90 : STATUS 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)
     178             : {
     179          90 :         php_stream *s = php_http_message_body_stream(body);
     180          90 :         char *buf = emalloc(0x1000);
     181             :         TSRMLS_FETCH_FROM_CTX(body->ts);
     182             : 
     183          90 :         php_stream_seek(s, offset, SEEK_SET);
     184             : 
     185          90 :         if (!forlen) {
     186          82 :                 forlen = -1;
     187             :         }
     188         263 :         while (!php_stream_eof(s)) {
     189         106 :                 size_t read = php_stream_read(s, buf, MIN(forlen, 0x1000));
     190             : 
     191         106 :                 if (read) {
     192         104 :                         if (-1 == cb(cb_arg, buf, read)) {
     193           0 :                                 return FAILURE;
     194             :                         }
     195             :                 }
     196             : 
     197         106 :                 if (read < MIN(forlen, sizeof(buf))) {
     198          15 :                         break;
     199             :                 }
     200             : 
     201          91 :                 if (forlen && !(forlen -= read)) {
     202           8 :                         break;
     203             :                 }
     204             :         }
     205          90 :         efree(buf);
     206             : 
     207          90 :         return SUCCESS;
     208             : }
     209             : 
     210          52 : size_t php_http_message_body_append(php_http_message_body_t *body, const char *buf, size_t len)
     211             : {
     212             :         php_stream *s;
     213             :         size_t written;
     214             :         TSRMLS_FETCH_FROM_CTX(body->ts);
     215             : 
     216          52 :         if (!(s = php_http_message_body_stream(body))) {
     217           0 :                 return -1;
     218             :         }
     219             : 
     220          52 :         if (s->ops->seek) {
     221          52 :                 php_stream_seek(s, 0, SEEK_END);
     222             :         }
     223             : 
     224          52 :         written = php_stream_write(s, buf, len);
     225             : 
     226          52 :         if (written != len) {
     227           1 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to append %zu bytes to body; wrote %zu", len, written);
     228             :         }
     229             : 
     230          52 :         return len;
     231             : }
     232             : 
     233          18 : size_t php_http_message_body_appendf(php_http_message_body_t *body, const char *fmt, ...)
     234             : {
     235             :         va_list argv;
     236             :         char *print_str;
     237             :         size_t print_len;
     238             : 
     239          18 :         va_start(argv, fmt);
     240          18 :         print_len = vspprintf(&print_str, 0, fmt, argv);
     241          18 :         va_end(argv);
     242             : 
     243          18 :         print_len = php_http_message_body_append(body, print_str, print_len);
     244          18 :         efree(print_str);
     245             : 
     246          18 :         return print_len;
     247             : }
     248             : 
     249           2 : STATUS php_http_message_body_add_form(php_http_message_body_t *body, HashTable *fields, HashTable *files)
     250             : {
     251             :         zval tmp;
     252             : 
     253           2 :         if (fields) {
     254           1 :                 INIT_PZVAL_ARRAY(&tmp, fields);
     255           1 :                 if (SUCCESS != add_recursive_fields(body, NULL, &tmp)) {
     256           0 :                         return FAILURE;
     257             :                 }
     258             :         }
     259           2 :         if (files) {
     260           2 :                 INIT_PZVAL_ARRAY(&tmp, files);
     261           2 :                 if (SUCCESS != add_recursive_files(body, NULL, &tmp)) {
     262           0 :                         return FAILURE;
     263             :                 }
     264             :         }
     265             : 
     266           2 :         return SUCCESS;
     267             : }
     268             : 
     269           2 : void php_http_message_body_add_part(php_http_message_body_t *body, php_http_message_t *part)
     270             : {
     271             :         TSRMLS_FETCH_FROM_CTX(body->ts);
     272             : 
     273           2 :         BOUNDARY_OPEN(body);
     274           2 :         php_http_message_to_callback(part, (php_http_pass_callback_t) php_http_message_body_append, body);
     275           2 :         BOUNDARY_CLOSE(body);
     276           2 : }
     277             : 
     278             : 
     279           4 : STATUS php_http_message_body_add_form_field(php_http_message_body_t *body, const char *name, const char *value_str, size_t value_len)
     280             : {
     281             :         char *safe_name;
     282             :         TSRMLS_FETCH_FROM_CTX(body->ts);
     283             : 
     284           4 :         safe_name = php_addslashes(estrdup(name), strlen(name), NULL, 1 TSRMLS_CC);
     285             : 
     286           4 :         BOUNDARY_OPEN(body);
     287           4 :         php_http_message_body_appendf(
     288             :                 body,
     289             :                 "Content-Disposition: form-data; name=\"%s\"" PHP_HTTP_CRLF
     290             :                 "" PHP_HTTP_CRLF,
     291             :                 safe_name
     292             :         );
     293           4 :         php_http_message_body_append(body, value_str, value_len);
     294           4 :         BOUNDARY_CLOSE(body);
     295             : 
     296           4 :         efree(safe_name);
     297           4 :         return SUCCESS;
     298             : }
     299             : 
     300           2 : STATUS php_http_message_body_add_form_file(php_http_message_body_t *body, const char *name, const char *ctype, const char *path, php_stream *in)
     301             : {
     302           2 :         char *safe_name, *path_dup = estrdup(path), *bname;
     303             :         size_t bname_len;
     304             :         TSRMLS_FETCH_FROM_CTX(body->ts);
     305             : 
     306           2 :         safe_name = php_addslashes(estrdup(name), strlen(name), NULL, 1 TSRMLS_CC);
     307             :         
     308           2 :         php_basename(path_dup, strlen(path_dup), NULL, 0, &bname, &bname_len TSRMLS_CC); 
     309             : 
     310           2 :         BOUNDARY_OPEN(body);
     311           2 :         php_http_message_body_appendf(
     312             :                 body,
     313             :                 "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"" PHP_HTTP_CRLF
     314             :                 "Content-Transfer-Encoding: binary" PHP_HTTP_CRLF
     315             :                 "Content-Type: %s" PHP_HTTP_CRLF
     316             :                 PHP_HTTP_CRLF,
     317             :                 safe_name, bname, ctype
     318             :         );
     319           2 :         php_stream_copy_to_stream_ex(in, php_http_message_body_stream(body), PHP_STREAM_COPY_ALL, NULL);
     320           2 :         BOUNDARY_CLOSE(body);
     321             : 
     322           2 :         efree(safe_name);
     323           2 :         efree(path_dup);
     324           2 :         efree(bname);
     325             : 
     326           2 :         return SUCCESS;
     327             : }
     328             : 
     329           8 : static inline char *format_key(uint type, char *str, ulong num, const char *prefix) {
     330           8 :         char *new_key = NULL;
     331             : 
     332           8 :         if (prefix && *prefix) {
     333           6 :                 if (type == HASH_KEY_IS_STRING) {
     334           0 :                         spprintf(&new_key, 0, "%s[%s]", prefix, str);
     335             :                 } else {
     336           3 :                         spprintf(&new_key, 0, "%s[%lu]", prefix, num);
     337             :                 }
     338           5 :         } else if (type == HASH_KEY_IS_STRING) {
     339           4 :                 new_key = estrdup(str);
     340             :         } else {
     341           1 :                 new_key = estrdup("");
     342             :         }
     343             : 
     344           8 :         return new_key;
     345             : }
     346             : 
     347           6 : static STATUS add_recursive_fields(php_http_message_body_t *body, const char *name, zval *value)
     348             : {
     349           8 :         if (Z_TYPE_P(value) == IS_ARRAY || Z_TYPE_P(value) == IS_OBJECT) {
     350             :                 zval **val;
     351             :                 HashTable *ht;
     352             :                 HashPosition pos;
     353           2 :                 php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
     354             :                 TSRMLS_FETCH_FROM_CTX(body->ts);
     355             : 
     356           2 :                 ht = HASH_OF(value);
     357           2 :                 if (!ht->nApplyCount) {
     358           2 :                         ++ht->nApplyCount;
     359           7 :                         FOREACH_KEYVAL(pos, value, key, val) {
     360           5 :                                 char *str = format_key(key.type, key.str, key.num, name);
     361           5 :                                 if (SUCCESS != add_recursive_fields(body, str, *val)) {
     362           0 :                                         efree(str);
     363           0 :                                         ht->nApplyCount--;
     364           0 :                                         return FAILURE;
     365             :                                 }
     366           5 :                                 efree(str);
     367             :                         }
     368           2 :                         --ht->nApplyCount;
     369             :                 }
     370             :         } else {
     371           4 :                 zval *cpy = php_http_ztyp(IS_STRING, value);
     372           4 :                 php_http_message_body_add_form_field(body, name, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy));
     373           4 :                 zval_ptr_dtor(&cpy);
     374             :         }
     375             : 
     376           6 :         return SUCCESS;
     377             : }
     378             : 
     379           3 : static STATUS add_recursive_files(php_http_message_body_t *body, const char *name, zval *value)
     380             : {
     381           3 :         zval **zdata = NULL, **zfile, **zname, **ztype;
     382             :         HashTable *ht;
     383             :         TSRMLS_FETCH_FROM_CTX(body->ts);
     384             : 
     385           3 :         if (Z_TYPE_P(value) != IS_ARRAY && Z_TYPE_P(value) != IS_OBJECT) {
     386           0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected array or object (name, type, file) for message body file to add");
     387           0 :                 return FAILURE;
     388             :         }
     389             : 
     390           3 :         ht = HASH_OF(value);
     391             : 
     392           3 :         if ((SUCCESS != zend_hash_find(ht, ZEND_STRS("name"), (void *) &zname))
     393           2 :         ||      (SUCCESS != zend_hash_find(ht, ZEND_STRS("type"), (void *) &ztype))
     394           2 :         ||      (SUCCESS != zend_hash_find(ht, ZEND_STRS("file"), (void *) &zfile))
     395             :         ) {
     396             :                 zval **val;
     397             :                 HashPosition pos;
     398           1 :                 php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
     399             : 
     400           1 :                 if (!ht->nApplyCount) {
     401           1 :                         ++ht->nApplyCount;
     402           2 :                         FOREACH_HASH_KEYVAL(pos, ht, key, val) {
     403           1 :                                 if (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT) {
     404           1 :                                         char *str = format_key(key.type, key.str, key.num, name);
     405             : 
     406           1 :                                         if (SUCCESS != add_recursive_files(body, str, *val)) {
     407           0 :                                                 efree(str);
     408           0 :                                                 --ht->nApplyCount;
     409           0 :                                                 return FAILURE;
     410             :                                         }
     411           1 :                                         efree(str);
     412             :                                 }
     413             :                         }
     414           1 :                         --ht->nApplyCount;
     415             :                 }
     416           1 :                 return SUCCESS;
     417             :         } else {
     418             :                 php_stream *stream;
     419           2 :                 zval *zfc = php_http_ztyp(IS_STRING, *zfile);
     420             : 
     421           2 :                 if (SUCCESS == zend_hash_find(ht, ZEND_STRS("data"), (void *) &zdata)) {
     422           0 :                         if (Z_TYPE_PP(zdata) == IS_RESOURCE) {
     423           0 :                                 php_stream_from_zval_no_verify(stream, zdata);
     424             :                         } else {
     425           0 :                                 zval *tmp = php_http_ztyp(IS_STRING, *zdata);
     426             : 
     427           0 :                                 stream = php_stream_memory_open(TEMP_STREAM_READONLY, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
     428           0 :                                 zval_ptr_dtor(&tmp);
     429             :                         }
     430             :                 } else {
     431           2 :                         stream = php_stream_open_wrapper(Z_STRVAL_P(zfc), "r", REPORT_ERRORS|USE_PATH, NULL);
     432             :                 }
     433             : 
     434           2 :                 if (!stream) {
     435           0 :                         zval_ptr_dtor(&zfc);
     436           0 :                         return FAILURE;
     437             :                 } else {
     438           2 :                         zval *znc = php_http_ztyp(IS_STRING, *zname), *ztc = php_http_ztyp(IS_STRING, *ztype);
     439           2 :                         char *key = format_key(HASH_KEY_IS_STRING, Z_STRVAL_P(znc), 0, name);
     440           2 :                         STATUS ret =  php_http_message_body_add_form_file(body, key, Z_STRVAL_P(ztc), Z_STRVAL_P(zfc), stream);
     441             : 
     442           2 :                         efree(key);
     443           2 :                         zval_ptr_dtor(&znc);
     444           2 :                         zval_ptr_dtor(&ztc);
     445           2 :                         zval_ptr_dtor(&zfc);
     446           2 :                         if (!zdata || Z_TYPE_PP(zdata) != IS_RESOURCE) {
     447           2 :                                 php_stream_close(stream);
     448             :                         }
     449           2 :                         return ret;
     450             :                 }
     451             : 
     452             :         }
     453             : }
     454             : 
     455             : struct splitbody_arg {
     456             :         php_http_buffer_t buf;
     457             :         php_http_message_parser_t *parser;
     458             :         char *boundary_str;
     459             :         size_t boundary_len;
     460             :         size_t consumed;
     461             : };
     462             : 
     463           1 : static size_t splitbody(void *opaque, char *buf, size_t len TSRMLS_DC)
     464             : {
     465           1 :         struct splitbody_arg *arg = opaque;
     466           1 :         const char *boundary = NULL;
     467           1 :         size_t consumed = 0;
     468             :         int first_boundary;
     469             : 
     470             :         do {
     471           3 :                 first_boundary = !(consumed || arg->consumed);
     472             : 
     473           3 :                 if ((boundary = php_http_locate_str(buf, len, arg->boundary_str + first_boundary, arg->boundary_len - first_boundary))) {
     474           3 :                         size_t real_boundary_len = arg->boundary_len - 1, cut;
     475           3 :                         const char *real_boundary = boundary + !first_boundary;
     476           3 :                         int eol_len = 0;
     477             : 
     478           3 :                         if (buf + len <= real_boundary + real_boundary_len) {
     479             :                                 /* if we just have enough data for the boundary, it's just a byte too less */
     480           0 :                                 arg->consumed += consumed;
     481           0 :                                 return consumed;
     482             :                         }
     483             : 
     484           3 :                         if (!first_boundary) {
     485             :                                 /* this is not the first boundary, read rest of this message */
     486           2 :                                 php_http_buffer_append(&arg->buf, buf, real_boundary - buf);
     487           2 :                                 php_http_message_parser_parse(arg->parser, &arg->buf, 0, &arg->parser->message);
     488             :                         }
     489             : 
     490             :                         /* move after the boundary */
     491           3 :                         cut = real_boundary - buf + real_boundary_len;
     492           3 :                         buf += cut;
     493           3 :                         len -= cut;
     494           3 :                         consumed += cut;
     495             : 
     496           3 :                         if (buf == php_http_locate_bin_eol(buf, len, &eol_len)) {
     497             :                                 /* skip CRLF */
     498           2 :                                 buf += eol_len;
     499           2 :                                 len -= eol_len;
     500           2 :                                 consumed += eol_len;
     501             : 
     502           2 :                                 if (!first_boundary) {
     503             :                                         /* advance messages */
     504             :                                         php_http_message_t *msg;
     505             : 
     506           1 :                                         msg = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
     507           1 :                                         msg->parent = arg->parser->message;
     508           1 :                                         arg->parser->message = msg;
     509             :                                 }
     510             :                         } else {
     511             :                                 /* is this the last boundary? */
     512           1 :                                 if (*buf == '-') {
     513             :                                         /* ignore the rest */
     514           1 :                                         consumed += len;
     515           1 :                                         len = 0;
     516             :                                 } else {
     517             :                                         /* let this be garbage */
     518           0 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Malformed multipart boundary at pos %zu", consumed);
     519           0 :                                         return -1;
     520             :                                 }
     521             :                         }
     522             :                 }
     523           3 :         } while (boundary && len);
     524             : 
     525             :         /* let there be room for the next boundary */
     526           1 :         if (len > arg->boundary_len) {
     527           0 :                 consumed += len - arg->boundary_len;
     528           0 :                 php_http_buffer_append(&arg->buf, buf, len - arg->boundary_len);
     529           0 :                 php_http_message_parser_parse(arg->parser, &arg->buf, 0, &arg->parser->message);
     530             :         }
     531             : 
     532           1 :         arg->consumed += consumed;
     533           1 :         return consumed;
     534             : }
     535             : 
     536           1 : php_http_message_t *php_http_message_body_split(php_http_message_body_t *body, const char *boundary)
     537             : {
     538           1 :         php_stream *s = php_http_message_body_stream(body);
     539           1 :         php_http_buffer_t *tmp = NULL;
     540           1 :         php_http_message_t *msg = NULL;
     541             :         struct splitbody_arg arg;
     542             :         TSRMLS_FETCH_FROM_CTX(body->ts);
     543             : 
     544           1 :         php_http_buffer_init(&arg.buf);
     545           1 :         arg.parser = php_http_message_parser_init(NULL TSRMLS_CC);
     546           1 :         arg.boundary_len = spprintf(&arg.boundary_str, 0, "\n--%s", boundary);
     547           1 :         arg.consumed = 0;
     548             : 
     549           1 :         php_stream_rewind(s);
     550           3 :         while (!php_stream_eof(s)) {
     551           1 :                 php_http_buffer_passthru(&tmp, 0x1000, (php_http_buffer_pass_func_t) _php_stream_read, s, splitbody, &arg TSRMLS_CC);
     552             :         }
     553             : 
     554           1 :         msg = arg.parser->message;
     555           1 :         arg.parser->message = NULL;
     556             : 
     557           1 :         php_http_buffer_free(&tmp);
     558           1 :         php_http_message_parser_free(&arg.parser);
     559           1 :         php_http_buffer_dtor(&arg.buf);
     560           1 :         STR_FREE(arg.boundary_str);
     561             : 
     562           1 :         return msg;
     563             : }
     564             : 
     565             : static zend_object_handlers php_http_message_body_object_handlers;
     566             : 
     567          33 : zend_object_value php_http_message_body_object_new(zend_class_entry *ce TSRMLS_DC)
     568             : {
     569          33 :         return php_http_message_body_object_new_ex(ce, NULL, NULL TSRMLS_CC);
     570             : }
     571             : 
     572         211 : 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)
     573             : {
     574             :         php_http_message_body_object_t *o;
     575             : 
     576         211 :         o = ecalloc(1, sizeof(php_http_message_body_object_t));
     577         211 :         zend_object_std_init((zend_object *) o, php_http_message_body_class_entry TSRMLS_CC);
     578         211 :         object_properties_init((zend_object *) o, ce);
     579             : 
     580         211 :         if (ptr) {
     581         175 :                 *ptr = o;
     582             :         }
     583             : 
     584         211 :         if (body) {
     585         178 :                 o->body = body;
     586             :         }
     587             : 
     588         211 :         o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_body_object_free, NULL TSRMLS_CC);
     589         211 :         o->zv.handlers = &php_http_message_body_object_handlers;
     590             : 
     591         211 :         return o->zv;
     592             : }
     593             : 
     594           1 : zend_object_value php_http_message_body_object_clone(zval *object TSRMLS_DC)
     595             : {
     596             :         zend_object_value new_ov;
     597           1 :         php_http_message_body_object_t *new_obj = NULL;
     598           1 :         php_http_message_body_object_t *old_obj = zend_object_store_get_object(object TSRMLS_CC);
     599           1 :         php_http_message_body_t *body = php_http_message_body_copy(old_obj->body, NULL);
     600             : 
     601           1 :         new_ov = php_http_message_body_object_new_ex(old_obj->zo.ce, body, &new_obj TSRMLS_CC);
     602           1 :         zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(object) TSRMLS_CC);
     603             : 
     604           1 :         return new_ov;
     605             : }
     606             : 
     607         211 : void php_http_message_body_object_free(void *object TSRMLS_DC)
     608             : {
     609         211 :         php_http_message_body_object_t *obj = object;
     610             : 
     611         211 :         php_http_message_body_free(&obj->body);
     612         211 :         zend_object_std_dtor((zend_object *) obj TSRMLS_CC);
     613         211 :         efree(obj);
     614         211 : }
     615             : 
     616             : #define PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj) \
     617             :         do { \
     618             :                 if (!obj->body) { \
     619             :                         obj->body = php_http_message_body_init(NULL, NULL TSRMLS_CC); \
     620             :                 } \
     621             :         } while(0)
     622             : 
     623             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody___construct, 0, 0, 0)
     624             :         ZEND_ARG_INFO(0, stream)
     625             : ZEND_END_ARG_INFO();
     626          32 : PHP_METHOD(HttpMessageBody, __construct)
     627             : {
     628          32 :         php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
     629          32 :         zval *zstream = NULL;
     630             :         php_stream *stream;
     631             : 
     632          32 :         php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r!", &zstream), invalid_arg, return);
     633             : 
     634          32 :         if (zstream) {
     635          19 :                 php_http_expect(php_stream_from_zval_no_verify(stream, &zstream), unexpected_val, return);
     636             : 
     637          19 :                 if (obj->body) {
     638           0 :                         php_http_message_body_free(&obj->body);
     639             :                 }
     640          19 :                 obj->body = php_http_message_body_init(NULL, stream TSRMLS_CC);
     641             :         }
     642             : }
     643             : 
     644             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody___toString, 0, 0, 0)
     645             : ZEND_END_ARG_INFO();
     646          19 : PHP_METHOD(HttpMessageBody, __toString)
     647             : {
     648          19 :         if (SUCCESS == zend_parse_parameters_none()) {
     649          19 :                 php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
     650             :                 char *str;
     651             :                 size_t len;
     652             : 
     653          19 :                 PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
     654             : 
     655          19 :                 php_http_message_body_to_string(obj->body, &str, &len, 0, 0);
     656          19 :                 if (str) {
     657          17 :                         RETURN_STRINGL(str, len, 0);
     658             :                 }
     659             :         }
     660           2 :         RETURN_EMPTY_STRING();
     661             : }
     662             : 
     663             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_unserialize, 0, 0, 1)
     664             :         ZEND_ARG_INFO(0, serialized)
     665             : ZEND_END_ARG_INFO();
     666           1 : PHP_METHOD(HttpMessageBody, unserialize)
     667             : {
     668             :         char *us_str;
     669             :         int us_len;
     670             : 
     671           1 :         if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &us_str, &us_len)) {
     672           1 :                 php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
     673           1 :                 php_stream *s = php_stream_memory_open(0, us_str, us_len);
     674             : 
     675           1 :                 obj->body = php_http_message_body_init(NULL, s TSRMLS_CC);
     676             :         }
     677           1 : }
     678             : 
     679             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_toStream, 0, 0, 1)
     680             :         ZEND_ARG_INFO(0, stream)
     681             :         ZEND_ARG_INFO(0, offset)
     682             :         ZEND_ARG_INFO(0, maxlen)
     683             : ZEND_END_ARG_INFO();
     684           1 : PHP_METHOD(HttpMessageBody, toStream)
     685             : {
     686             :         zval *zstream;
     687           1 :         long offset = 0, forlen = 0;
     688             : 
     689           1 :         if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|ll", &zstream, &offset, &forlen)) {
     690             :                 php_stream *stream;
     691           1 :                 php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
     692             : 
     693           1 :                 PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
     694             : 
     695           2 :                 php_stream_from_zval(stream, &zstream);
     696           1 :                 php_http_message_body_to_stream(obj->body, stream, offset, forlen);
     697           1 :                 RETURN_ZVAL(getThis(), 1, 0);
     698             :         }
     699             : }
     700             : 
     701             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_toCallback, 0, 0, 1)
     702             :         ZEND_ARG_INFO(0, callback)
     703             :         ZEND_ARG_INFO(0, offset)
     704             :         ZEND_ARG_INFO(0, maxlen)
     705             : ZEND_END_ARG_INFO();
     706           1 : PHP_METHOD(HttpMessageBody, toCallback)
     707             : {
     708             :         php_http_pass_fcall_arg_t fcd;
     709           1 :         long offset = 0, forlen = 0;
     710             : 
     711           1 :         if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f|ll", &fcd.fci, &fcd.fcc, &offset, &forlen)) {
     712           1 :                 php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
     713             : 
     714           1 :                 PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
     715             : 
     716           1 :                 fcd.fcz = getThis();
     717           1 :                 Z_ADDREF_P(fcd.fcz);
     718             :                 TSRMLS_SET_CTX(fcd.ts);
     719             : 
     720           1 :                 php_http_message_body_to_callback(obj->body, php_http_pass_fcall_callback, &fcd, offset, forlen);
     721           1 :                 zend_fcall_info_args_clear(&fcd.fci, 1);
     722             : 
     723           1 :                 zval_ptr_dtor(&fcd.fcz);
     724           1 :                 RETURN_ZVAL(getThis(), 1, 0);
     725             :         }
     726             : }
     727             : 
     728             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_getResource, 0, 0, 0)
     729             : ZEND_END_ARG_INFO();
     730          12 : PHP_METHOD(HttpMessageBody, getResource)
     731             : {
     732          12 :         if (SUCCESS == zend_parse_parameters_none()) {
     733          12 :                 php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
     734             : 
     735          12 :                 PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
     736             : 
     737          12 :                 zend_list_addref(obj->body->stream_id);
     738          12 :                 RETVAL_RESOURCE(obj->body->stream_id);
     739             :         }
     740          12 : }
     741             : 
     742             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_getBoundary, 0, 0, 0)
     743             : ZEND_END_ARG_INFO();
     744           2 : PHP_METHOD(HttpMessageBody, getBoundary)
     745             : {
     746           2 :         if (SUCCESS == zend_parse_parameters_none()) {
     747           2 :                 php_http_message_body_object_t * obj = zend_object_store_get_object(getThis() TSRMLS_CC);
     748             : 
     749           2 :                 PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
     750             : 
     751           2 :                 if (obj->body->boundary) {
     752           1 :                         RETURN_STRING(obj->body->boundary, 1);
     753             :                 }
     754             :         }
     755             : }
     756             : 
     757             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_append, 0, 0, 1)
     758             :         ZEND_ARG_INFO(0, string)
     759             : ZEND_END_ARG_INFO();
     760          14 : PHP_METHOD(HttpMessageBody, append)
     761             : {
     762             :         char *str;
     763             :         int len;
     764             :         php_http_message_body_object_t *obj;
     765             : 
     766          14 :         php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len), invalid_arg, return);
     767             : 
     768          14 :         obj = zend_object_store_get_object(getThis() TSRMLS_CC);
     769             : 
     770          14 :         PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
     771             : 
     772          14 :         php_http_expect(len == php_http_message_body_append(obj->body, str, len), runtime, return);
     773             : 
     774          14 :         RETURN_ZVAL(getThis(), 1, 0);
     775             : }
     776             : 
     777             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_addForm, 0, 0, 0)
     778             :         ZEND_ARG_ARRAY_INFO(0, fields, 1)
     779             :         ZEND_ARG_ARRAY_INFO(0, files, 1)
     780             : ZEND_END_ARG_INFO();
     781           2 : PHP_METHOD(HttpMessageBody, addForm)
     782             : {
     783           2 :         HashTable *fields = NULL, *files = NULL;
     784             :         php_http_message_body_object_t *obj;
     785             : 
     786           2 :         php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|h!h!", &fields, &files), invalid_arg, return);
     787             : 
     788           2 :         obj = zend_object_store_get_object(getThis() TSRMLS_CC);
     789             : 
     790           2 :         PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
     791             : 
     792           2 :         php_http_expect(SUCCESS == php_http_message_body_add_form(obj->body, fields, files), runtime, return);
     793             : 
     794           2 :         RETURN_ZVAL(getThis(), 1, 0);
     795             : }
     796             : 
     797             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_addPart, 0, 0, 1)
     798             :         ZEND_ARG_OBJ_INFO(0, message, http\\Message, 0)
     799             : ZEND_END_ARG_INFO();
     800           2 : PHP_METHOD(HttpMessageBody, addPart)
     801             : {
     802             :         zval *zobj;
     803             :         php_http_message_body_object_t *obj;
     804             :         php_http_message_object_t *mobj;
     805             :         zend_error_handling zeh;
     806             : 
     807           2 :         php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zobj, php_http_message_class_entry), invalid_arg, return);
     808             : 
     809           2 :         obj = zend_object_store_get_object(getThis() TSRMLS_CC);
     810           2 :         mobj = zend_object_store_get_object(zobj TSRMLS_CC);
     811             : 
     812           2 :         PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
     813             : 
     814           2 :         zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh TSRMLS_CC);
     815           2 :         php_http_message_body_add_part(obj->body, mobj->message);
     816           2 :         zend_restore_error_handling(&zeh TSRMLS_CC);
     817             : 
     818           2 :         if (!EG(exception)) {
     819           2 :                 RETURN_ZVAL(getThis(), 1, 0);
     820             :         }
     821             : }
     822             : 
     823             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_etag, 0, 0, 0)
     824             : ZEND_END_ARG_INFO();
     825          45 : PHP_METHOD(HttpMessageBody, etag)
     826             : {
     827          45 :         if (SUCCESS == zend_parse_parameters_none()) {
     828          45 :                 php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
     829             :                 char *etag;
     830             : 
     831          45 :                 PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
     832             : 
     833          45 :                 if ((etag = php_http_message_body_etag(obj->body))) {
     834          45 :                         RETURN_STRING(etag, 0);
     835             :                 } else {
     836           0 :                         RETURN_FALSE;
     837             :                 }
     838             :         }
     839             : }
     840             : 
     841             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_stat, 0, 0, 0)
     842             :         ZEND_ARG_INFO(0, field)
     843             : ZEND_END_ARG_INFO();
     844           5 : PHP_METHOD(HttpMessageBody, stat)
     845             : {
     846           5 :         char *field_str = NULL;
     847           5 :         int field_len = 0;
     848             : 
     849           5 :         if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &field_str, &field_len)) {
     850           5 :                 php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
     851             :                 const php_stream_statbuf *sb;
     852             : 
     853           5 :                 PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
     854             : 
     855           5 :                 if ((sb = php_http_message_body_stat(obj->body))) {
     856           5 :                         if (field_str && field_len) {
     857           4 :                                         switch (*field_str) {
     858             :                                                 case 's':
     859             :                                                 case 'S':
     860           5 :                                                         RETURN_LONG(sb->sb.st_size);
     861             :                                                         break;
     862             :                                                 case 'a':
     863             :                                                 case 'A':
     864           1 :                                                         RETURN_LONG(sb->sb.st_atime);
     865             :                                                         break;
     866             :                                                 case 'm':
     867             :                                                 case 'M':
     868           1 :                                                         RETURN_LONG(sb->sb.st_mtime);
     869             :                                                         break;
     870             :                                                 case 'c':
     871             :                                                 case 'C':
     872           1 :                                                         RETURN_LONG(sb->sb.st_ctime);
     873             :                                                         break;
     874             :                                                 default:
     875           0 :                                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown stat field: '%s' (should be one of [s]ize, [a]time, [m]time or [c]time)", field_str);
     876           0 :                                                         break;
     877             :                                         }
     878           0 :                         } else {
     879           1 :                                 object_init(return_value);
     880           1 :                                 add_property_long_ex(return_value, ZEND_STRS("size"), sb->sb.st_size TSRMLS_CC);
     881           1 :                                 add_property_long_ex(return_value, ZEND_STRS("atime"), sb->sb.st_atime TSRMLS_CC);
     882           1 :                                 add_property_long_ex(return_value, ZEND_STRS("mtime"), sb->sb.st_mtime TSRMLS_CC);
     883           1 :                                 add_property_long_ex(return_value, ZEND_STRS("ctime"), sb->sb.st_ctime TSRMLS_CC);
     884             :                         }
     885             :                 }
     886             :         }
     887             : }
     888             : 
     889             : static zend_function_entry php_http_message_body_methods[] = {
     890             :         PHP_ME(HttpMessageBody, __construct,  ai_HttpMessageBody___construct,  ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
     891             :         PHP_ME(HttpMessageBody, __toString,   ai_HttpMessageBody___toString,   ZEND_ACC_PUBLIC)
     892             :         PHP_MALIAS(HttpMessageBody, toString, __toString, ai_HttpMessageBody___toString, ZEND_ACC_PUBLIC)
     893             :         PHP_MALIAS(HttpMessageBody, serialize, __toString, ai_HttpMessageBody___toString, ZEND_ACC_PUBLIC)
     894             :         PHP_ME(HttpMessageBody, unserialize,  ai_HttpMessageBody_unserialize,  ZEND_ACC_PUBLIC)
     895             :         PHP_ME(HttpMessageBody, toStream,     ai_HttpMessageBody_toStream,     ZEND_ACC_PUBLIC)
     896             :         PHP_ME(HttpMessageBody, toCallback,   ai_HttpMessageBody_toCallback,   ZEND_ACC_PUBLIC)
     897             :         PHP_ME(HttpMessageBody, getResource,  ai_HttpMessageBody_getResource,  ZEND_ACC_PUBLIC)
     898             :         PHP_ME(HttpMessageBody, getBoundary,  ai_HttpMessageBody_getBoundary,  ZEND_ACC_PUBLIC)
     899             :         PHP_ME(HttpMessageBody, append,       ai_HttpMessageBody_append,       ZEND_ACC_PUBLIC)
     900             :         PHP_ME(HttpMessageBody, addForm,      ai_HttpMessageBody_addForm,      ZEND_ACC_PUBLIC)
     901             :         PHP_ME(HttpMessageBody, addPart,      ai_HttpMessageBody_addPart,      ZEND_ACC_PUBLIC)
     902             :         PHP_ME(HttpMessageBody, etag,         ai_HttpMessageBody_etag,         ZEND_ACC_PUBLIC)
     903             :         PHP_ME(HttpMessageBody, stat,         ai_HttpMessageBody_stat,         ZEND_ACC_PUBLIC)
     904             :         EMPTY_FUNCTION_ENTRY
     905             : };
     906             : 
     907             : zend_class_entry *php_http_message_body_class_entry;
     908             : 
     909         408 : PHP_MINIT_FUNCTION(http_message_body)
     910             : {
     911         408 :         zend_class_entry ce = {0};
     912             : 
     913         408 :         INIT_NS_CLASS_ENTRY(ce, "http\\Message", "Body", php_http_message_body_methods);
     914         408 :         php_http_message_body_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
     915         408 :         php_http_message_body_class_entry->create_object = php_http_message_body_object_new;
     916         408 :         memcpy(&php_http_message_body_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
     917         408 :         php_http_message_body_object_handlers.clone_obj = php_http_message_body_object_clone;
     918         408 :         zend_class_implements(php_http_message_body_class_entry TSRMLS_CC, 1, zend_ce_serializable);
     919             : 
     920         408 :         return SUCCESS;
     921             : }
     922             : 
     923             : /*
     924             :  * Local variables:
     925             :  * tab-width: 4
     926             :  * c-basic-offset: 4
     927             :  * End:
     928             :  * vim600: noet sw=4 ts=4 fdm=marker
     929             :  * vim<600: noet sw=4 ts=4
     930             :  */

Generated by: LCOV version 1.11