LTP GCOV extension - code coverage report
Current view: directory - ext/http - http_send_api.c
Test: PHP Code Coverage
Date: 2007-04-10 Instrumented lines: 238
Code covered: 78.6 % Executed lines: 187
Legend: not executed executed

       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-2007, Michael Wallner <mike@php.net>            |
      10                 :     +--------------------------------------------------------------------+
      11                 : */
      12                 : 
      13                 : /* $Id: http_send_api.c,v 1.70 2007/02/07 11:50:27 mike Exp $ */
      14                 : 
      15                 : #define HTTP_WANT_SAPI
      16                 : #define HTTP_WANT_ZLIB
      17                 : #define HTTP_WANT_MAGIC
      18                 : #include "php_http.h"
      19                 : 
      20                 : #include "php_streams.h"
      21                 : #include "ext/standard/php_lcg.h"
      22                 : 
      23                 : #include "php_http_api.h"
      24                 : #include "php_http_cache_api.h"
      25                 : #include "php_http_date_api.h"
      26                 : #include "php_http_encoding_api.h"
      27                 : #include "php_http_headers_api.h"
      28                 : #include "php_http_send_api.h"
      29                 : 
      30                 : /* {{{ http_flush() */
      31                 : #define http_flush(d, l) _http_flush(NULL, (d), (l) TSRMLS_CC)
      32                 : static inline void _http_flush(void *nothing, const char *data, size_t data_len TSRMLS_DC)
      33              75 : {
      34              75 :         PHPWRITE(data, data_len);
      35                 :         /*      we really only need to flush when throttling is enabled,
      36                 :                 because we push the data as fast as possible anyway if not */
      37              75 :         if (HTTP_G->send.throttle_delay >= HTTP_DIFFSEC) {
      38              20 :                 if (OG(ob_nesting_level)) {
      39               0 :                         php_end_ob_buffer(1, 1 TSRMLS_CC);
      40                 :                 }
      41              20 :                 if (!OG(implicit_flush)) {
      42              20 :                         sapi_flush(TSRMLS_C);
      43                 :                 }
      44              20 :                 http_sleep(HTTP_G->send.throttle_delay);
      45                 :         }
      46              75 : }
      47                 : /* }}} */
      48                 : 
      49                 : /* {{{ http_send_response_start */
      50                 : #define http_send_response_start(b, cl) _http_send_response_start((b), (cl) TSRMLS_CC)
      51                 : static inline void _http_send_response_start(void **buffer, size_t content_length TSRMLS_DC)
      52              26 : {
      53                 :         int encoding;
      54                 :         
      55              26 :         if ((encoding = http_encoding_response_start(content_length, 0))) {
      56                 : #ifdef HTTP_HAVE_ZLIB
      57               2 :                 *((http_encoding_stream **) buffer) = http_encoding_deflate_stream_init(NULL, 
      58                 :                         (encoding == HTTP_ENCODING_GZIP) ? 
      59                 :                                 HTTP_DEFLATE_TYPE_GZIP : HTTP_DEFLATE_TYPE_ZLIB);
      60                 : #endif
      61                 :         }
      62                 :         /* flush headers */
      63              26 :         sapi_flush(TSRMLS_C);
      64              26 : }
      65                 : /* }}} */
      66                 : 
      67                 : /* {{{ http_send_response_data_plain */
      68                 : #define http_send_response_data_plain(b, d, dl) _http_send_response_data_plain((b), (d), (dl) TSRMLS_CC)
      69                 : static inline void _http_send_response_data_plain(void **buffer, const char *data, size_t data_len TSRMLS_DC)
      70              73 : {
      71              75 :         if (HTTP_G->send.deflate.response && HTTP_G->send.deflate.encoding) {
      72                 : #ifdef HTTP_HAVE_ZLIB
      73                 :                 char *encoded;
      74                 :                 size_t encoded_len;
      75               2 :                 http_encoding_stream *s = *((http_encoding_stream **) buffer);
      76                 :                 
      77               2 :                 http_encoding_deflate_stream_update(s, data, data_len, &encoded, &encoded_len);
      78               2 :                 if (HTTP_G->send.buffer_size) {
      79               0 :                         phpstr_chunked_output((phpstr **) &s->storage, encoded, encoded_len, HTTP_G->send.buffer_size, _http_flush, NULL TSRMLS_CC);
      80                 :                 } else {
      81               2 :                         http_flush(encoded, encoded_len);
      82                 :                 }
      83               2 :                 efree(encoded);
      84                 : #else
      85                 :                 http_error(HE_ERROR, HTTP_E_RESPONSE, "Attempt to send GZIP response despite being able to do so; please report this bug");
      86                 : #endif
      87              71 :         } else if (HTTP_G->send.buffer_size) {
      88              20 :                 phpstr_chunked_output((phpstr **) buffer, data, data_len, HTTP_G->send.buffer_size, _http_flush, NULL TSRMLS_CC);
      89                 :         } else {
      90              51 :                 http_flush(data, data_len);
      91                 :         }
      92              73 : }
      93                 : /* }}} */
      94                 : 
      95                 : /* {{{ http_send_response_data_fetch */
      96                 : #define http_send_response_data_fetch(b, d, l, m, s, e) _http_send_response_data_fetch((b), (d), (l), (m), (s), (e) TSRMLS_CC)
      97                 : static inline void _http_send_response_data_fetch(void **buffer, const void *data, size_t data_len, http_send_mode mode, size_t begin, size_t end TSRMLS_DC)
      98              32 : {
      99              32 :         long bsz, got, len = end - begin;
     100                 :         
     101              32 :         if (!(bsz = HTTP_G->send.buffer_size)) {
     102              31 :                 bsz = HTTP_SENDBUF_SIZE;
     103                 :         }
     104                 :         
     105              32 :         switch (mode) {
     106                 :                 case SEND_RSRC: {
     107              15 :                         php_stream *s = (php_stream *) data;
     108              15 :                         if (SUCCESS == php_stream_seek(s, begin, SEEK_SET)) {
     109              15 :                                 char *buf = emalloc(bsz);
     110                 :                                 
     111              45 :                                 while (len > 0) {
     112              15 :                                         got = php_stream_read(s, buf, MIN(len, bsz));
     113              15 :                                         http_send_response_data_plain(buffer, buf, got);
     114              15 :                                         len -= got;
     115                 :                                 }
     116                 :                                 
     117              15 :                                 efree(buf);
     118                 :                         }
     119              15 :                         break;
     120                 :                 }
     121                 :                 case SEND_DATA: {
     122              17 :                         const char *buf = ((const char *) data) + begin;
     123              70 :                         while (len > 0) {
     124              36 :                                 got = MIN(len, bsz);
     125              36 :                                 http_send_response_data_plain(buffer, buf, got);
     126              36 :                                 len -= got;
     127              36 :                                 buf += got;
     128                 :                         }
     129                 :                         break;
     130                 :                 }
     131                 :                 EMPTY_SWITCH_DEFAULT_CASE();
     132                 :         }
     133              32 : }
     134                 : /* }}} */
     135                 : 
     136                 : /* {{{ http_send_response_finish */
     137                 : #define http_send_response_finish(b) _http_send_response_finish((b) TSRMLS_CC)
     138                 : static inline void _http_send_response_finish(void **buffer TSRMLS_DC)
     139              26 : {
     140              28 :         if (HTTP_G->send.deflate.response && HTTP_G->send.deflate.encoding) {
     141                 : #ifdef HTTP_HAVE_ZLIB
     142               2 :                 char *encoded = NULL;
     143               2 :                 size_t encoded_len = 0;
     144               2 :                 http_encoding_stream *s = *((http_encoding_stream **) buffer);
     145                 :                 
     146               2 :                 http_encoding_deflate_stream_finish(s, &encoded, &encoded_len);
     147               2 :                 if (HTTP_G->send.buffer_size) {
     148               0 :                         phpstr_chunked_output((phpstr **) &s->storage, encoded, encoded_len, 0, _http_flush, NULL TSRMLS_CC);
     149                 :                 } else {
     150               2 :                         http_flush(encoded, encoded_len);
     151                 :                 }
     152               2 :                 http_encoding_deflate_stream_free(&s);
     153               2 :                 STR_FREE(encoded);
     154                 : #else
     155                 :                 http_error(HE_ERROR, HTTP_E_RESPONSE, "Attempt to send GZIP response despite being able to do so; please report this bug");
     156                 : #endif
     157              24 :         } else if (HTTP_G->send.buffer_size) {
     158               1 :                 phpstr_chunked_output((phpstr **) buffer, NULL, 0, 0, _http_flush, NULL TSRMLS_CC);
     159                 :         }
     160              26 : }
     161                 : /* }}} */
     162                 : 
     163                 : /* {{{ */
     164                 : PHP_MINIT_FUNCTION(http_send)
     165             220 : {
     166             220 :         HTTP_LONG_CONSTANT("HTTP_REDIRECT", HTTP_REDIRECT);
     167             220 :         HTTP_LONG_CONSTANT("HTTP_REDIRECT_PERM", HTTP_REDIRECT_PERM);
     168             220 :         HTTP_LONG_CONSTANT("HTTP_REDIRECT_FOUND", HTTP_REDIRECT_FOUND);
     169             220 :         HTTP_LONG_CONSTANT("HTTP_REDIRECT_POST", HTTP_REDIRECT_POST);
     170             220 :         HTTP_LONG_CONSTANT("HTTP_REDIRECT_PROXY", HTTP_REDIRECT_PROXY);
     171             220 :         HTTP_LONG_CONSTANT("HTTP_REDIRECT_TEMP", HTTP_REDIRECT_TEMP);
     172                 :         
     173             220 :         return SUCCESS;
     174                 : }
     175                 : /* }}} */
     176                 : 
     177                 : /* {{{ http_find_header */
     178                 : typedef struct {
     179                 :         const char *h;
     180                 :         size_t l;
     181                 : } http_response_header_t;
     182                 : 
     183                 : static int http_find_header(void *data, void *arg)
     184               3 : {
     185               3 :         http_response_header_t *h = arg;
     186               3 :         sapi_header_struct *s = data;
     187                 :         
     188               3 :         return (!strncasecmp(s->header, h->h, h->l)) && s->header[h->l] == ':';
     189                 : }
     190                 : /* }}} */
     191                 : 
     192                 : /* {{{ void http_hide_header(char *) */
     193                 : PHP_HTTP_API void _http_hide_header_ex(const char *name, size_t name_len TSRMLS_DC)
     194               1 : {
     195               1 :         http_response_header_t h = {name, name_len};
     196               1 :         zend_llist_del_element(&SG(sapi_headers).headers, (void *) &h, http_find_header);
     197               1 : }
     198                 : /* }}} */
     199                 : 
     200                 : /* {{{ void http_send_header_zval(char*, zval **, zend_bool) */
     201                 : PHP_HTTP_API void _http_send_header_zval_ex(const char *name, size_t name_len, zval **val, zend_bool replace TSRMLS_DC)
     202               0 : {
     203               0 :         if (!val || !*val || Z_TYPE_PP(val) == IS_NULL || (Z_TYPE_PP(val) == IS_STRING && !Z_STRLEN_PP(val))) {
     204               0 :                 http_hide_header_ex(name, name_len);
     205               0 :         } else if (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT) {
     206               0 :                 zend_bool first = replace;
     207                 :                 zval **data;
     208                 :                 HashPosition pos;
     209                 :                 
     210               0 :                 FOREACH_HASH_VAL(pos, HASH_OF(*val), data) {
     211               0 :                         zval *orig = *data;
     212                 :                         
     213               0 :                         convert_to_string_ex(data);
     214               0 :                         http_send_header_ex(name, name_len, Z_STRVAL_PP(data), Z_STRLEN_PP(data), first, NULL);
     215               0 :                         if (orig != *data) {
     216               0 :                                 zval_ptr_dtor(data);
     217                 :                         }
     218               0 :                         first = 0;
     219                 :                 }
     220                 :         } else {
     221               0 :                 zval *orig = *val;
     222                 :                 
     223               0 :                 convert_to_string_ex(val);
     224               0 :                 http_send_header_ex(name, name_len, Z_STRVAL_PP(val), Z_STRLEN_PP(val), replace, NULL);
     225               0 :                 if (orig != *val) {
     226               0 :                         zval_ptr_dtor(val);
     227                 :                 }
     228                 :         }
     229               0 : }
     230                 : /* }}} */
     231                 : 
     232                 : /* {{{ STATUS http_send_header(char *, char *, zend_bool) */
     233                 : PHP_HTTP_API STATUS _http_send_header_ex(const char *name, size_t name_len, const char *value, size_t value_len, zend_bool replace, char **sent_header TSRMLS_DC)
     234              41 : {
     235                 :         STATUS ret;
     236                 :         
     237              82 :         if (value && value_len) {
     238              41 :                 size_t header_len = sizeof(": ") + name_len + value_len + 1;
     239              41 :                 char *header = emalloc(header_len + 1);
     240                 :         
     241              41 :                 header[header_len] = '\0';
     242              41 :                 header_len = snprintf(header, header_len, "%s: %s", name, value);
     243              41 :                 ret = http_send_header_string_ex(header, header_len, replace);
     244              41 :                 if (sent_header) {
     245               2 :                         *sent_header = header;
     246                 :                 } else {
     247              39 :                         efree(header);
     248                 :                 }
     249                 :         } else {
     250               0 :                 http_hide_header_ex(name, name_len);
     251               0 :                 ret = SUCCESS;
     252                 :         }
     253              41 :         return ret;
     254                 : }
     255                 : /* }}} */
     256                 : 
     257                 : /* {{{ STATUS http_send_status_header(int, char *) */
     258                 : PHP_HTTP_API STATUS _http_send_status_header_ex(int status, const char *header, size_t header_len, zend_bool replace TSRMLS_DC)
     259             130 : {
     260                 :         STATUS ret;
     261             130 :         sapi_header_line h = {(char *) header, header_len, status};
     262             130 :         if (SUCCESS != (ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, &h TSRMLS_CC))) {
     263               0 :                 http_error_ex(HE_WARNING, HTTP_E_HEADER, "Could not send header: %s (%d)", header, status);
     264                 :         }
     265             130 :         return ret;
     266                 : }
     267                 : /* }}} */
     268                 : 
     269                 : /* {{{ STATUS http_send_last_modified(int) */
     270                 : PHP_HTTP_API STATUS _http_send_last_modified_ex(time_t t, char **sent_header TSRMLS_DC)
     271               5 : {
     272                 :         STATUS ret;
     273               5 :         char *date = http_date(t);
     274                 : 
     275               5 :         if (!date) {
     276               0 :                 return FAILURE;
     277                 :         }
     278                 : 
     279               5 :         ret = http_send_header_ex("Last-Modified", lenof("Last-Modified"), date, strlen(date), 1, sent_header);
     280               5 :         efree(date);
     281                 : 
     282                 :         /* remember */
     283               5 :         HTTP_G->send.last_modified = t;
     284                 : 
     285               5 :         return ret;
     286                 : }
     287                 : /* }}} */
     288                 : 
     289                 : /* {{{ STATUS http_send_etag(char *, size_t) */
     290                 : PHP_HTTP_API STATUS _http_send_etag_ex(const char *etag, size_t etag_len, char **sent_header TSRMLS_DC)
     291              16 : {
     292                 :         STATUS status;
     293                 :         char *etag_header;
     294                 :         size_t etag_header_len;
     295                 : 
     296              16 :         if (!etag_len){
     297               0 :                 http_error_ex(HE_WARNING, HTTP_E_HEADER, "Attempt to send empty ETag (previous: %s)\n", HTTP_G->send.unquoted_etag);
     298               0 :                 return FAILURE;
     299                 :         }
     300                 : 
     301              16 :         etag_header_len = spprintf(&etag_header, 0, "ETag: \"%s\"", etag);
     302              16 :         status = http_send_header_string_ex(etag_header, etag_header_len, 1);
     303                 :         
     304                 :         /* remember */
     305              16 :         STR_SET(HTTP_G->send.unquoted_etag, estrndup(etag, etag_len));
     306                 : 
     307              16 :         if (sent_header) {
     308              13 :                 *sent_header = etag_header;
     309                 :         } else {
     310               3 :                 efree(etag_header);
     311                 :         }
     312                 :         
     313              16 :         return status;
     314                 : }
     315                 : /* }}} */
     316                 : 
     317                 : /* {{{ STATUS http_send_content_type(char *, size_t) */
     318                 : PHP_HTTP_API STATUS _http_send_content_type(const char *content_type, size_t ct_len TSRMLS_DC)
     319              16 : {
     320              16 :         HTTP_CHECK_CONTENT_TYPE(content_type, return FAILURE);
     321                 : 
     322                 :         /* remember for multiple ranges */
     323              16 :         STR_FREE(HTTP_G->send.content_type);
     324              16 :         HTTP_G->send.content_type = estrndup(content_type, ct_len);
     325                 : 
     326              16 :         return http_send_header_ex("Content-Type", lenof("Content-Type"), content_type, ct_len, 1, NULL);
     327                 : }
     328                 : /* }}} */
     329                 : 
     330                 : /* {{{ STATUS http_send_content_disposition(char *, size_t, zend_bool) */
     331                 : PHP_HTTP_API STATUS _http_send_content_disposition(const char *filename, size_t f_len, zend_bool send_inline TSRMLS_DC)
     332               0 : {
     333                 :         STATUS status;
     334                 :         char *cd_header;
     335                 : 
     336               0 :         if (send_inline) {
     337               0 :                 cd_header = ecalloc(1, sizeof("Content-Disposition: inline; filename=\"\"") + f_len);
     338               0 :                 sprintf(cd_header, "Content-Disposition: inline; filename=\"%s\"", filename);
     339                 :         } else {
     340               0 :                 cd_header = ecalloc(1, sizeof("Content-Disposition: attachment; filename=\"\"") + f_len);
     341               0 :                 sprintf(cd_header, "Content-Disposition: attachment; filename=\"%s\"", filename);
     342                 :         }
     343                 : 
     344               0 :         status = http_send_header_string(cd_header);
     345               0 :         efree(cd_header);
     346               0 :         return status;
     347                 : }
     348                 : /* }}} */
     349                 : 
     350                 : /* {{{ STATUS http_send(void *, size_t, http_send_mode) */
     351                 : PHP_HTTP_API STATUS _http_send_ex(const void *data_ptr, size_t data_size, http_send_mode data_mode, zend_bool no_cache TSRMLS_DC)
     352              30 : {
     353              30 :         void *s = NULL;
     354                 :         HashTable ranges;
     355                 :         http_range_status range_status;
     356                 :         
     357              30 :         if (!data_ptr) {
     358               0 :                 return FAILURE;
     359                 :         }
     360              30 :         if (!data_size) {
     361               0 :                 return SUCCESS;
     362                 :         }
     363                 :         
     364                 :         /* enable partial dl and resume */
     365              30 :         http_send_header_string("Accept-Ranges: bytes");
     366                 : 
     367              30 :         zend_hash_init(&ranges, 0, NULL, ZVAL_PTR_DTOR, 0);
     368              30 :         range_status = http_get_request_ranges(&ranges, data_size);
     369                 : 
     370              30 :         switch (range_status) {
     371                 :                 case RANGE_ERR:
     372                 :                 {
     373               2 :                         zend_hash_destroy(&ranges);
     374               2 :                         http_send_status(416);
     375               2 :                         return FAILURE;
     376                 :                 }
     377                 :                 case RANGE_OK:
     378                 :                 {
     379                 :                         /* Range Request - only send ranges if entity hasn't changed */
     380              16 :                         if (    http_got_server_var("HTTP_IF_RANGE") &&
     381                 :                                         !http_match_etag("HTTP_IF_RANGE", HTTP_G->send.unquoted_etag) &&
     382                 :                                         !http_match_last_modified("HTTP_IF_RANGE", HTTP_G->send.last_modified)) {
     383                 :                                 /* fallthrough to send full entity with 200 Ok */
     384               1 :                                 no_cache = 1;
     385              14 :                         } else if (     !http_match_etag_ex("HTTP_IF_MATCH", HTTP_G->send.unquoted_etag, 0) ||
     386                 :                                                 !http_match_last_modified_ex("HTTP_IF_UNMODIFIED_SINCE", HTTP_G->send.last_modified, 0) ||
     387                 :                                                 !http_match_last_modified_ex("HTTP_UNLESS_MODIFIED_SINCE", HTTP_G->send.last_modified, 0)) {
     388                 :                                 /* 412 Precondition failed */
     389               1 :                                 zend_hash_destroy(&ranges);
     390               1 :                                 http_send_status(412);
     391               1 :                                 return FAILURE;
     392              13 :                         } else if (zend_hash_num_elements(&ranges) == 1) {
     393                 :                                 /* single range */
     394                 :                                 zval **range, **begin, **end;
     395                 :                                 
     396               9 :                                 if (    SUCCESS != zend_hash_index_find(&ranges, 0, (void *) &range) ||
     397                 :                                                 SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(range), 0, (void *) &begin) ||
     398                 :                                                 SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(range), 1, (void *) &end)) {
     399                 :                                         /* this should never happen */
     400               0 :                                         zend_hash_destroy(&ranges);
     401               0 :                                         http_send_status(500);
     402               0 :                                         return FAILURE;
     403                 :                                 } else {
     404                 :                                         char range_header_str[256];
     405                 :                                         size_t range_header_len;
     406                 :                                         
     407               9 :                                         range_header_len = snprintf(range_header_str, sizeof(range_header_str), "Content-Range: bytes %ld-%ld/%zu", Z_LVAL_PP(begin), Z_LVAL_PP(end), data_size);
     408               9 :                                         http_send_status_header_ex(206, range_header_str, range_header_len, 1);
     409               9 :                                         http_send_response_start(&s, Z_LVAL_PP(end)-Z_LVAL_PP(begin)+1);
     410               9 :                                         http_send_response_data_fetch(&s, data_ptr, data_size, data_mode, Z_LVAL_PP(begin), Z_LVAL_PP(end) + 1);
     411               9 :                                         http_send_response_finish(&s);
     412               9 :                                         zend_hash_destroy(&ranges);
     413               9 :                                         return SUCCESS;
     414                 :                                 }
     415                 :                         } else {
     416                 :                                 /* multi range */
     417                 :                                 HashPosition pos;
     418                 :                                 zval **range, **begin, **end;
     419               4 :                                 const char *content_type = HTTP_G->send.content_type;
     420                 :                                 char boundary_str[32], range_header_str[256];
     421                 :                                 size_t boundary_len, range_header_len;
     422                 :                                 
     423               4 :                                 boundary_len = snprintf(boundary_str, sizeof(boundary_str), "%lu%0.9f", (ulong) HTTP_G->request.time, (float) php_combined_lcg(TSRMLS_C));
     424               4 :                                 range_header_len = snprintf(range_header_str, sizeof(range_header_str), "Content-Type: multipart/byteranges; boundary=%s", boundary_str);
     425                 :                                 
     426               4 :                                 http_send_status_header_ex(206, range_header_str, range_header_len, 1);
     427               4 :                                 http_send_response_start(&s, 0);
     428                 :                                 
     429               4 :                                 if (!content_type) {
     430               0 :                                         content_type = "application/x-octetstream";
     431                 :                                 }
     432                 :                                 
     433              14 :                                 FOREACH_HASH_VAL(pos, &ranges, range) {
     434              10 :                                         if (    SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(range), 0, (void *) &begin) &&
     435                 :                                                         SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(range), 1, (void *) &end)) {
     436                 :                                                 char preface_str[512];
     437                 :                                                 size_t preface_len;
     438                 : 
     439                 : #define HTTP_RANGE_PREFACE \
     440                 :         HTTP_CRLF "--%s" \
     441                 :         HTTP_CRLF "Content-Type: %s" \
     442                 :         HTTP_CRLF "Content-Range: bytes %ld-%ld/%zu" \
     443                 :         HTTP_CRLF HTTP_CRLF
     444                 :                                                 
     445              10 :                                                 preface_len = snprintf(preface_str, sizeof(preface_str), HTTP_RANGE_PREFACE, boundary_str, content_type, Z_LVAL_PP(begin), Z_LVAL_PP(end), data_size);
     446              10 :                                                 http_send_response_data_plain(&s, preface_str, preface_len);
     447              10 :                                                 http_send_response_data_fetch(&s, data_ptr, data_size, data_mode, Z_LVAL_PP(begin), Z_LVAL_PP(end) + 1);
     448                 :                                         }
     449                 :                                 }
     450                 :                                 
     451               4 :                                 http_send_response_data_plain(&s, HTTP_CRLF "--", lenof(HTTP_CRLF "--"));
     452               4 :                                 http_send_response_data_plain(&s, boundary_str, boundary_len);
     453               4 :                                 http_send_response_data_plain(&s, "--", lenof("--"));
     454                 :                                 
     455               4 :                                 http_send_response_finish(&s);
     456               4 :                                 zend_hash_destroy(&ranges);
     457               4 :                                 return SUCCESS;
     458                 :                         }
     459                 :                 }
     460                 :                 case RANGE_NO:
     461                 :                 {
     462              14 :                         zend_hash_destroy(&ranges);
     463                 : 
     464                 :                         /* send 304 Not Modified if etag matches - DON'T return on ETag generation failure */
     465              14 :                         if (!no_cache && (http_interrupt_ob_etaghandler() || (HTTP_G->send.unquoted_etag != NULL))) {
     466               7 :                                 char *etag = NULL;
     467                 :                                 
     468               7 :                                 if (HTTP_G->send.unquoted_etag) {
     469               3 :                                         etag = estrdup(HTTP_G->send.unquoted_etag);
     470                 :                                 }
     471                 :                                 
     472               7 :                                 if (etag || (etag = http_etag(data_ptr, data_size, data_mode))) {
     473               7 :                                         char *sent_header = NULL;
     474                 :                                         
     475               7 :                                         http_send_etag_ex(etag, strlen(etag), &sent_header);
     476               7 :                                         if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) {
     477               1 :                                                 return http_exit_ex(304, sent_header, NULL, 0);
     478                 :                                         } else {
     479               6 :                                                 STR_FREE(sent_header);
     480                 :                                                 /* no caching for Last-Modified if ETags really don't match */
     481               6 :                                                 no_cache = http_got_server_var("HTTP_IF_NONE_MATCH");
     482                 :                                         }
     483               6 :                                         efree(etag);
     484                 :                                 }
     485                 :                         }
     486                 :                 
     487                 :                         /* send 304 Not Modified if last modified matches */
     488              13 :                         if (!no_cache && http_match_last_modified("HTTP_IF_MODIFIED_SINCE", HTTP_G->send.last_modified)) {
     489               0 :                                 char *sent_header = NULL;
     490               0 :                                 http_send_last_modified_ex(HTTP_G->send.last_modified, &sent_header);
     491               0 :                                 return http_exit_ex(304, sent_header, NULL, 0);
     492                 :                         }
     493                 :                         
     494                 :                         /* send full response */
     495              13 :                         http_send_response_start(&s, data_size);
     496              13 :                         http_send_response_data_fetch(&s, data_ptr, data_size, data_mode, 0, data_size);
     497              13 :                         http_send_response_finish(&s);
     498              13 :                         return SUCCESS;
     499                 :                 }
     500                 :         }
     501               0 :         return FAILURE;
     502                 : }
     503                 : /* }}} */
     504                 : 
     505                 : /* {{{ STATUS http_send_stream(php_stream *) */
     506                 : PHP_HTTP_API STATUS _http_send_stream_ex(php_stream *file, zend_bool close_stream, zend_bool no_cache TSRMLS_DC)
     507              15 : {
     508                 :         STATUS status;
     509                 :         php_stream_statbuf ssb;
     510                 :         int orig_flags;
     511                 : 
     512              15 :         if ((!file) || php_stream_stat(file, &ssb)) {
     513               1 :                 char *defct = sapi_get_default_content_type(TSRMLS_C);
     514                 :                 
     515               1 :                 http_hide_header("Content-Disposition");
     516               1 :                 http_send_content_type(defct, strlen(defct));
     517               1 :                 http_error(HE_WARNING, HTTP_E_RESPONSE, "File not found; stat failed");
     518               1 :                 STR_FREE(defct);
     519                 :                 
     520               1 :                 if (HTTP_G->send.not_found_404) {
     521               1 :                         http_exit_ex(404, NULL, estrdup("File not found\n"), 0);
     522                 :                 }
     523               0 :                 return FAILURE;
     524                 :         }
     525                 : 
     526              14 :         orig_flags = file->flags;
     527              14 :         file->flags |= PHP_STREAM_FLAG_NO_BUFFER;
     528              14 :         status = http_send_ex(file, ssb.sb.st_size, SEND_RSRC, no_cache);
     529              14 :         file->flags = orig_flags;
     530                 :         
     531              14 :         if (close_stream) {
     532              14 :                 php_stream_close(file);
     533                 :         }
     534                 : 
     535              14 :         return status;
     536                 : }
     537                 : /* }}} */
     538                 : 
     539                 : /* {{{ char *http_guess_content_type(char *magic_file, long magic_mode, void *data, size_t size, http_send_mode mode) */
     540                 : PHP_HTTP_API char *_http_guess_content_type(const char *magicfile, long magicmode, void *data_ptr, size_t data_len, http_send_mode data_mode TSRMLS_DC)
     541               0 : {
     542               0 :         char *ct = NULL;
     543                 : 
     544                 : #ifdef HTTP_HAVE_MAGIC
     545                 :         struct magic_set *magic = NULL;
     546                 :         
     547                 :         HTTP_CHECK_OPEN_BASEDIR(magicfile, return NULL);
     548                 :         
     549                 :         if (!data_ptr) {
     550                 :                 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Supplied payload is empty");
     551                 :         } else if (!(magic = magic_open(magicmode &~ MAGIC_MIME))) {
     552                 :                 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid magic mode: %ld", magicmode);
     553                 :         } else if (-1 == magic_load(magic, magicfile)) {
     554                 :                 http_error_ex(HE_WARNING, HTTP_E_RUNTIME, "Failed to load magic database '%s' (%s)", magicfile, magic_error(magic));
     555                 :         } else {
     556                 :                 const char *ctype = NULL;
     557                 :                 
     558                 :                 magic_setflags(magic, magicmode);
     559                 :                 
     560                 :                 switch (data_mode) {
     561                 :                         case SEND_RSRC:
     562                 :                         {
     563                 :                                 char *buffer;
     564                 :                                 size_t b_len;
     565                 :                                 
     566                 :                                 b_len = php_stream_copy_to_mem(data_ptr, &buffer, 65536, 0);
     567                 :                                 ctype = magic_buffer(magic, buffer, b_len);
     568                 :                                 efree(buffer);
     569                 :                                 break;
     570                 :                         }
     571                 :                         
     572                 :                         case SEND_DATA:
     573                 :                                 ctype = magic_buffer(magic, data_ptr, data_len);
     574                 :                                 break;
     575                 :                         
     576                 :                         default:
     577                 :                                 HTTP_CHECK_OPEN_BASEDIR(data_ptr, magic_close(magic); return NULL);
     578                 :                                 ctype = magic_file(magic, data_ptr);
     579                 :                                 break;
     580                 :                 }
     581                 :                 
     582                 :                 if (ctype) {
     583                 :                         ct = estrdup(ctype);
     584                 :                 } else {
     585                 :                         http_error_ex(HE_WARNING, HTTP_E_RUNTIME, "Failed to guess Content-Type: %s", magic_error(magic));
     586                 :                 }
     587                 :         }
     588                 :         if (magic) {
     589                 :                 magic_close(magic);
     590                 :         }
     591                 : #else
     592               0 :         http_error(HE_WARNING, HTTP_E_RUNTIME, "Cannot guess Content-Type; libmagic not available");
     593                 : #endif
     594                 :         
     595               0 :         return ct;
     596                 : }
     597                 : /* }}} */
     598                 : 
     599                 : /*
     600                 :  * Local variables:
     601                 :  * tab-width: 4
     602                 :  * c-basic-offset: 4
     603                 :  * End:
     604                 :  * vim600: sw=4 ts=4 fdm=marker
     605                 :  * vim<600: sw=4 ts=4
     606                 :  */
     607                 : 

Generated by: LTP GCOV extension version 1.5