PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LTP GCOV extension - code coverage report
Current view: directory - var/php_gcov/PHP_HEAD/main/streams - streams.c
Test: PHP Code Coverage
Date: 2009-11-23 Instrumented lines: 1420
Code covered: 75.4 % Executed lines: 1070
Legend: not executed executed

       1                 : /*
       2                 :    +----------------------------------------------------------------------+
       3                 :    | PHP Version 6                                                        |
       4                 :    +----------------------------------------------------------------------+
       5                 :    | Copyright (c) 1997-2009 The PHP Group                                |
       6                 :    +----------------------------------------------------------------------+
       7                 :    | This source file is subject to version 3.01 of the PHP license,      |
       8                 :    | that is bundled with this package in the file LICENSE, and is        |
       9                 :    | available through the world-wide-web at the following url:           |
      10                 :    | http://www.php.net/license/3_01.txt                                  |
      11                 :    | If you did not receive a copy of the PHP license and are unable to   |
      12                 :    | obtain it through the world-wide-web, please send a note to          |
      13                 :    | license@php.net so we can mail you a copy immediately.               |
      14                 :    +----------------------------------------------------------------------+
      15                 :    | Authors: Wez Furlong <wez@thebrainroom.com>                          |
      16                 :    | Borrowed code from:                                                  |
      17                 :    |          Rasmus Lerdorf <rasmus@lerdorf.on.ca>                       |
      18                 :    |          Jim Winstead <jimw@php.net>                                 |
      19                 :    +----------------------------------------------------------------------+
      20                 :  */
      21                 : 
      22                 : /* $Id: streams.c 290796 2009-11-15 20:30:57Z felipe $ */
      23                 : 
      24                 : #define _GNU_SOURCE
      25                 : #include "php.h"
      26                 : #include "php_globals.h"
      27                 : #include "php_network.h"
      28                 : #include "php_open_temporary_file.h"
      29                 : #include "ext/standard/file.h"
      30                 : #include "ext/standard/basic_functions.h" /* for BG(mmap_file) (not strictly required) */
      31                 : #include "ext/standard/php_string.h" /* for php_memnstr, used by php_stream_get_record() */
      32                 : #include <stddef.h>
      33                 : #include <fcntl.h>
      34                 : #include "php_streams_int.h"
      35                 : 
      36                 : /* {{{ resource and registration code */
      37                 : /* Global wrapper hash, copied to FG(stream_wrappers) on registration of volatile wrapper */
      38                 : static HashTable url_stream_wrappers_hash;
      39                 : static int le_stream = FAILURE; /* true global */
      40                 : static int le_pstream = FAILURE; /* true global */
      41                 : static int le_stream_filter = FAILURE; /* true global */
      42                 : 
      43                 : PHPAPI int php_file_le_stream(void)
      44        10424209 : {
      45        10424209 :         return le_stream;
      46                 : }
      47                 : 
      48                 : PHPAPI int php_file_le_pstream(void)
      49        10361922 : {
      50        10361922 :         return le_pstream;
      51                 : }
      52                 : 
      53                 : PHPAPI int php_file_le_stream_filter(void)
      54              80 : {
      55              80 :         return le_stream_filter;
      56                 : }
      57                 : 
      58                 : PHPAPI HashTable *_php_stream_get_url_stream_wrappers_hash(TSRMLS_D)
      59              32 : {
      60              32 :         return (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash);
      61                 : }
      62                 : 
      63                 : PHPAPI HashTable *php_stream_get_url_stream_wrappers_hash_global(void)
      64               1 : {
      65               1 :         return &url_stream_wrappers_hash;
      66                 : }
      67                 : 
      68                 : static int _php_stream_release_context(zend_rsrc_list_entry *le, void *pContext TSRMLS_DC)
      69               0 : {
      70               0 :         if (le->ptr == pContext) {
      71               0 :                 return --le->refcount == 0;
      72                 :         }
      73               0 :         return 0;
      74                 : }
      75                 : 
      76                 : static int forget_persistent_resource_id_numbers(zend_rsrc_list_entry *rsrc TSRMLS_DC)
      77             323 : {
      78                 :         php_stream *stream;
      79                 : 
      80             323 :         if (Z_TYPE_P(rsrc) != le_pstream) {
      81             317 :                 return 0;
      82                 :         }
      83                 : 
      84               6 :         stream = (php_stream*)rsrc->ptr;
      85                 : 
      86                 : #if STREAM_DEBUG
      87                 : fprintf(stderr, "forget_persistent: %s:%p\n", stream->ops->label, stream);
      88                 : #endif
      89                 : 
      90               6 :         stream->rsrc_id = FAILURE;
      91                 : 
      92               6 :         if (stream->context) {
      93               0 :                 zend_hash_apply_with_argument(&EG(regular_list),
      94                 :                                 (apply_func_arg_t) _php_stream_release_context,
      95                 :                                 stream->context TSRMLS_CC);
      96               0 :                 stream->context = NULL;
      97                 :         }
      98                 :         
      99               6 :         return 0;
     100                 : }
     101                 : 
     102                 : PHP_RSHUTDOWN_FUNCTION(streams)
     103           17025 : {
     104           17025 :         zend_hash_apply(&EG(persistent_list), (apply_func_t)forget_persistent_resource_id_numbers TSRMLS_CC);
     105           17025 :         return SUCCESS;
     106                 : }
     107                 : 
     108                 : PHPAPI int php_stream_from_persistent_id(const char *persistent_id, php_stream **stream TSRMLS_DC)
     109             987 : {
     110                 :         zend_rsrc_list_entry *le;
     111                 : 
     112             987 :         if (zend_hash_find(&EG(persistent_list), (char*)persistent_id, strlen(persistent_id)+1, (void*) &le) == SUCCESS) {
     113               0 :                 if (Z_TYPE_P(le) == le_pstream) {
     114               0 :                         if (stream) {
     115               0 :                                 *stream = (php_stream*)le->ptr;
     116               0 :                                 le->refcount++;
     117               0 :                                 (*stream)->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, *stream, le_pstream);
     118                 :                         }
     119               0 :                         return PHP_STREAM_PERSISTENT_SUCCESS;
     120                 :                 }
     121               0 :                 return PHP_STREAM_PERSISTENT_FAILURE;
     122                 :         }
     123             987 :         return PHP_STREAM_PERSISTENT_NOT_EXIST;
     124                 : }
     125                 : 
     126                 : /* }}} */
     127                 : 
     128                 : /* {{{ wrapper error reporting */
     129                 : void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption TSRMLS_DC)
     130             545 : {
     131             545 :         char *tmp = estrdup(path);
     132                 :         char *msg;
     133             545 :         int free_msg = 0;
     134                 : 
     135             545 :         if (wrapper) {
     136             526 :                 if (wrapper->err_count > 0) {
     137                 :                         int i;
     138                 :                         size_t l;
     139                 :                         int brlen;
     140                 :                         char *br;
     141                 : 
     142              85 :                         if (PG(html_errors)) {
     143               0 :                                 brlen = 7;
     144               0 :                                 br = "<br />\n";
     145                 :                         } else {
     146              85 :                                 brlen = 1;
     147              85 :                                 br = "\n";
     148                 :                         }
     149                 : 
     150             176 :                         for (i = 0, l = 0; i < wrapper->err_count; i++) {
     151              91 :                                 l += strlen(wrapper->err_stack[i]);
     152              91 :                                 if (i < wrapper->err_count - 1) {
     153               6 :                                         l += brlen;
     154                 :                                 }
     155                 :                         }
     156              85 :                         msg = emalloc(l + 1);
     157              85 :                         msg[0] = '\0';
     158             176 :                         for (i = 0; i < wrapper->err_count; i++) {
     159              91 :                                 strcat(msg, wrapper->err_stack[i]);
     160              91 :                                 if (i < wrapper->err_count - 1) { 
     161               6 :                                         strcat(msg, br);
     162                 :                                 }
     163                 :                         }
     164                 : 
     165              85 :                         free_msg = 1;
     166                 :                 } else {
     167             441 :                         if (wrapper == &php_plain_files_wrapper) {
     168             439 :                                 msg = strerror(errno);
     169                 :                         } else {
     170               2 :                                 msg = "operation failed";
     171                 :                         }
     172                 :                 }
     173                 :         } else {
     174              19 :                 msg = "no suitable wrapper could be found";
     175                 :         }
     176                 : 
     177             545 :         php_strip_url_passwd(tmp);
     178             545 :         php_error_docref1(NULL TSRMLS_CC, tmp, E_WARNING, "%s: %s", caption, msg);
     179             545 :         efree(tmp);
     180             545 :         if (free_msg) {
     181              85 :                 efree(msg);
     182                 :         }
     183             545 : }
     184                 : 
     185                 : void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC)
     186          116424 : {
     187          116424 :         if (wrapper) {
     188                 :                 /* tidy up the error stack */
     189                 :                 int i;
     190                 : 
     191          116496 :                 for (i = 0; i < wrapper->err_count; i++) {
     192              91 :                         efree(wrapper->err_stack[i]);
     193                 :                 }
     194          116405 :                 if (wrapper->err_stack) {
     195              85 :                         efree(wrapper->err_stack);
     196                 :                 }
     197          116405 :                 wrapper->err_stack = NULL;
     198          116405 :                 wrapper->err_count = 0;
     199                 :         }
     200          116424 : }
     201                 : 
     202                 : PHPAPI void php_stream_wrapper_log_error(php_stream_wrapper *wrapper, int options TSRMLS_DC, const char *fmt, ...)
     203             125 : {
     204                 :         va_list args;
     205             125 :         char *buffer = NULL;
     206                 : 
     207             125 :         va_start(args, fmt);
     208             125 :         vspprintf(&buffer, 0, fmt, args);
     209             125 :         va_end(args);
     210                 : 
     211             159 :         if (options & REPORT_ERRORS || wrapper == NULL) {
     212              34 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", buffer);
     213              34 :                 efree(buffer);
     214                 :         } else {
     215                 :                 /* append to stack */
     216              91 :                 wrapper->err_stack = erealloc(wrapper->err_stack, (wrapper->err_count + 1) * sizeof(char *));
     217              91 :                 if (wrapper->err_stack) {
     218              91 :                         wrapper->err_stack[wrapper->err_count++] = buffer;
     219                 :                 }
     220                 :         }
     221             125 : }
     222                 : 
     223                 : 
     224                 : /* }}} */
     225                 : 
     226                 : /* allocate a new stream for a particular ops */
     227                 : PHPAPI php_stream *_php_stream_alloc(php_stream_ops *ops, void *abstract, const char *persistent_id, const char *mode STREAMS_DC TSRMLS_DC) /* {{{ */
     228          191273 : {
     229                 :         php_stream *ret;
     230                 : 
     231          191273 :         ret = (php_stream*) pemalloc_rel_orig(sizeof(php_stream), persistent_id ? 1 : 0);
     232                 : 
     233          191273 :         memset(ret, 0, sizeof(php_stream));
     234                 : 
     235          191273 :         ret->readfilters.stream = ret;
     236          191273 :         ret->writefilters.stream = ret;
     237                 : 
     238                 : #if STREAM_DEBUG
     239                 : fprintf(stderr, "stream_alloc: %s:%p persistent=%s\n", ops->label, ret, persistent_id);
     240                 : #endif
     241                 : 
     242          191273 :         ret->ops = ops;
     243          191273 :         ret->abstract = abstract;
     244          191273 :         ret->is_persistent = persistent_id ? 1 : 0;
     245          191273 :         ret->chunk_size = FG(def_chunk_size);
     246          191273 :         ret->readbuf_type = IS_STRING;
     247                 : 
     248                 : #if ZEND_DEBUG
     249                 :         ret->open_filename = __zend_orig_filename ? __zend_orig_filename : __zend_filename;
     250                 :         ret->open_lineno = __zend_orig_lineno ? __zend_orig_lineno : __zend_lineno;
     251                 : #endif
     252                 : 
     253          191273 :         if (FG(auto_detect_line_endings)) {
     254               0 :                 ret->flags |= PHP_STREAM_FLAG_DETECT_EOL;
     255                 :         }
     256                 : 
     257          191273 :         if (persistent_id) {
     258                 :                 zend_rsrc_list_entry le;
     259                 : 
     260             987 :                 Z_TYPE(le) = le_pstream;
     261             987 :                 le.ptr = ret;
     262             987 :                 le.refcount = 0;
     263                 : 
     264             987 :                 if (FAILURE == zend_hash_update(&EG(persistent_list), (char *)persistent_id,
     265                 :                                         strlen(persistent_id) + 1,
     266                 :                                         (void *)&le, sizeof(le), NULL)) {
     267                 :                         
     268               0 :                         pefree(ret, 1);
     269               0 :                         return NULL;
     270                 :                 }
     271                 :         }
     272                 : 
     273          191273 :         ret->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, ret, persistent_id ? le_pstream : le_stream);
     274          191273 :         strlcpy(ret->mode, mode, sizeof(ret->mode));
     275                 : 
     276          191273 :         return ret;
     277                 : }
     278                 : /* }}} */
     279                 : 
     280                 : static int _php_stream_free_persistent(zend_rsrc_list_entry *le, void *pStream TSRMLS_DC)
     281              35 : {
     282              35 :         return le->ptr == pStream;
     283                 : }
     284                 : 
     285                 : PHPAPI int _php_stream_free(php_stream *stream, int close_options TSRMLS_DC) /* {{{ */
     286          256748 : {
     287          256748 :         int ret = 1;
     288          256748 :         int remove_rsrc = 1;
     289          256748 :         int preserve_handle = close_options & PHP_STREAM_FREE_PRESERVE_HANDLE ? 1 : 0;
     290          256748 :         int release_cast = 1;
     291          256748 :         php_stream_context *context = stream->context;
     292                 : 
     293          256748 :         if (stream->flags & PHP_STREAM_FLAG_NO_CLOSE) {
     294               0 :                 preserve_handle = 1;
     295                 :         }
     296                 : 
     297                 : #if STREAM_DEBUG
     298                 : fprintf(stderr, "stream_free: %s:%p[%s] in_free=%d opts=%08x\n", stream->ops->label, stream, stream->orig_path, stream->in_free, close_options);
     299                 : #endif
     300                 : 
     301                 :         /* recursion protection */
     302          256748 :         if (stream->in_free) {
     303           65342 :                 return 1;
     304                 :         }
     305                 : 
     306          191406 :         stream->in_free++;
     307                 : 
     308                 :         /* if we are releasing the stream only (and preserving the underlying handle),
     309                 :          * we need to do things a little differently.
     310                 :          * We are only ever called like this when the stream is cast to a FILE*
     311                 :          * for include (or other similar) purposes.
     312                 :          * */
     313          191406 :         if (preserve_handle) {
     314               0 :                 if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) {
     315                 :                         /* If the stream was fopencookied, we must NOT touch anything
     316                 :                          * here, as the cookied stream relies on it all.
     317                 :                          * Instead, mark the stream as OK to auto-clean */
     318                 :                         php_stream_auto_cleanup(stream);
     319               0 :                         stream->in_free--;
     320               0 :                         return 0;
     321                 :                 }
     322                 :                 /* otherwise, make sure that we don't close the FILE* from a cast */
     323               0 :                 release_cast = 0;
     324                 :         }
     325                 : 
     326                 : #if STREAM_DEBUG
     327                 : fprintf(stderr, "stream_free: %s:%p[%s] preserve_handle=%d release_cast=%d remove_rsrc=%d\n",
     328                 :                 stream->ops->label, stream, stream->orig_path, preserve_handle, release_cast, remove_rsrc);
     329                 : #endif
     330                 : 
     331                 :         /* make sure everything is saved */
     332          191406 :         _php_stream_flush(stream, 1 TSRMLS_CC);
     333                 :                 
     334                 :         /* If not called from the resource dtor, remove the stream from the resource list. */
     335          191406 :         if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) == 0 && remove_rsrc) {
     336           64429 :                 zend_list_delete(stream->rsrc_id);
     337                 :         }
     338                 : 
     339                 :         /* Remove stream from any context link list */
     340          191406 :         if (stream->context && stream->context->links) {
     341               0 :                 php_stream_context_del_link(stream->context, stream);
     342                 :         }
     343                 : 
     344          191406 :         if (close_options & PHP_STREAM_FREE_CALL_DTOR) {
     345          191406 :                 if (release_cast && stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) {
     346                 :                         /* calling fclose on an fopencookied stream will ultimately
     347                 :                                 call this very same function.  If we were called via fclose,
     348                 :                                 the cookie_closer unsets the fclose_stdiocast flags, so
     349                 :                                 we can be sure that we only reach here when PHP code calls
     350                 :                                 php_stream_free.
     351                 :                                 Lets let the cookie code clean it all up.
     352                 :                          */
     353               0 :                         stream->in_free = 0;
     354               0 :                         return fclose(stream->stdiocast);
     355                 :                 }
     356                 : 
     357          191406 :                 ret = stream->ops->close(stream, preserve_handle ? 0 : 1 TSRMLS_CC);
     358          191404 :                 stream->abstract = NULL;
     359                 : 
     360                 :                 /* tidy up any FILE* that might have been fdopened */
     361          191404 :                 if (release_cast && stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FDOPEN && stream->stdiocast) {
     362               0 :                         fclose(stream->stdiocast);
     363               0 :                         stream->stdiocast = NULL;
     364               0 :                         stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE;
     365                 :                 }
     366                 :         }
     367                 : 
     368          191404 :         if (close_options & PHP_STREAM_FREE_RELEASE_STREAM) {
     369          395600 :                 while (stream->readfilters.head) {
     370           12792 :                         php_stream_filter_remove(stream->readfilters.head, 1 TSRMLS_CC);
     371                 :                 }
     372          388833 :                 while (stream->writefilters.head) {
     373            6025 :                         php_stream_filter_remove(stream->writefilters.head, 1 TSRMLS_CC);
     374                 :                 }
     375                 : 
     376          191404 :                 if (stream->wrapper && stream->wrapper->wops && stream->wrapper->wops->stream_closer) {
     377               0 :                         stream->wrapper->wops->stream_closer(stream->wrapper, stream TSRMLS_CC);
     378               0 :                         stream->wrapper = NULL;
     379                 :                 }
     380                 : 
     381          191404 :                 if (stream->wrapperdata) {
     382              65 :                         zval_ptr_dtor(&stream->wrapperdata);
     383              65 :                         stream->wrapperdata = NULL;
     384                 :                 }
     385                 : 
     386          191404 :                 if (stream->readbuf.v) {
     387           42508 :                         pefree(stream->readbuf.v, stream->is_persistent);
     388           42508 :                         stream->readbuf.v = NULL;
     389                 :                 }
     390                 : 
     391          191404 :                 if (stream->is_persistent && (close_options & PHP_STREAM_FREE_PERSISTENT)) {
     392                 :                         /* we don't work with *stream but need its value for comparison */
     393             989 :                         zend_hash_apply_with_argument(&EG(persistent_list), (apply_func_arg_t) _php_stream_free_persistent, stream TSRMLS_CC);
     394                 :                 }
     395                 : #if ZEND_DEBUG
     396                 :                 if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) && (stream->__exposed == 0) && (EG(error_reporting) & E_WARNING)) {
     397                 :                         /* it leaked: Lets deliberately NOT pefree it so that the memory manager shows it
     398                 :                          * as leaked; it will log a warning, but lets help it out and display what kind
     399                 :                          * of stream it was. */
     400                 :                         char *leakinfo;
     401                 :                         spprintf(&leakinfo, 0, __FILE__ "(%d) : Stream of type '%s' %p (path:%s) was not closed (opened in %s on line %d)\n", __LINE__, stream->ops->label, stream, stream->orig_path, stream->open_filename, stream->open_lineno);
     402                 : 
     403                 :                         if (stream->orig_path) {
     404                 :                                 pefree(stream->orig_path, stream->is_persistent);
     405                 :                                 stream->orig_path = NULL;
     406                 :                         }
     407                 :                         
     408                 : # if defined(PHP_WIN32)
     409                 :                         OutputDebugString(leakinfo);
     410                 : # else
     411                 :                         fprintf(stderr, "%s", leakinfo);
     412                 : # endif
     413                 :                         efree(leakinfo);
     414                 :                 } else {
     415                 :                         if (stream->orig_path) {
     416                 :                                 pefree(stream->orig_path, stream->is_persistent);
     417                 :                                 stream->orig_path = NULL;
     418                 :                         }
     419                 : 
     420                 :                         pefree(stream, stream->is_persistent);
     421                 :                 }
     422                 : #else
     423          191404 :                 if (stream->orig_path) {
     424          134571 :                         pefree(stream->orig_path, stream->is_persistent);
     425          134571 :                         stream->orig_path = NULL;
     426                 :                 }
     427                 : 
     428          191404 :                 pefree(stream, stream->is_persistent);
     429                 : #endif
     430                 :         }
     431                 : 
     432          191404 :         if (context) {
     433             178 :                 zend_list_delete(context->rsrc_id);
     434                 :         }
     435                 : 
     436          191404 :         return ret;
     437                 : }
     438                 : /* }}} */
     439                 : 
     440                 : /* {{{ generic stream operations */
     441                 : 
     442                 : /* size == full characters (char, UChar, or 2x UChar)
     443                 :    TODO: Needs better handling of surrogate pairs */
     444                 : static void php_stream_fill_read_buffer(php_stream *stream, size_t size TSRMLS_DC)
     445          991679 : {
     446          991679 :         if (stream->readpos == stream->writepos) {
     447          991666 :                 stream->readpos = stream->writepos = 0;
     448                 :         }
     449                 : 
     450                 :         /* allocate/fill the buffer */
     451                 : 
     452          991679 :         if (stream->readfilters.head) {
     453                 :                 char *chunk_buf;
     454           12936 :                 int err_flag = 0;
     455           12936 :                 php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL };
     456           12936 :                 php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap;
     457                 : 
     458                 :                 /* Invalidate the existing cache, otherwise reads can fail, see note in
     459                 :                    main/streams/filter.c::_php_stream_filter_append */
     460           12936 :                 stream->writepos = stream->readpos = 0;
     461                 : 
     462                 :                 /* allocate a buffer for reading chunks */
     463           12936 :                 chunk_buf = emalloc(stream->chunk_size);
     464                 : 
     465           38297 :                 while (!stream->eof && !err_flag && (stream->writepos - stream->readpos < (off_t)size)) {
     466           24353 :                         size_t justread = 0;
     467                 :                         int flags;
     468                 :                         php_stream_bucket *bucket;
     469           24353 :                         php_stream_filter_status_t status = PSFS_ERR_FATAL;
     470                 :                         php_stream_filter *filter;
     471                 : 
     472                 :                         /* read a chunk into a bucket */
     473           24353 :                         justread = stream->ops->read(stream, chunk_buf, stream->chunk_size TSRMLS_CC);
     474           36778 :                         if (justread && justread != (size_t)-1) {
     475           12425 :                                 bucket = php_stream_bucket_new(stream, chunk_buf, justread, 0, 0 TSRMLS_CC);
     476                 : 
     477                 :                                 /* after this call, bucket is owned by the brigade */
     478           12425 :                                 php_stream_bucket_append(brig_inp, bucket TSRMLS_CC);
     479                 : 
     480           12425 :                                 flags = PSFS_FLAG_NORMAL;
     481                 :                         } else {
     482           11928 :                                 flags = stream->eof ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC;
     483                 :                         }
     484                 :                 
     485                 :                         /* wind the handle... */
     486           36668 :                         for (filter = stream->readfilters.head; filter; filter = filter->next) {
     487           24363 :                                 status = filter->fops->filter(stream, filter, brig_inp, brig_outp, NULL, flags TSRMLS_CC);
     488                 : 
     489           24363 :                                 if (status != PSFS_PASS_ON) {
     490           12048 :                                         break;
     491                 :                                 }
     492                 :                                 
     493                 :                                 /* brig_out becomes brig_in.
     494                 :                                  * brig_in will always be empty here, as the filter MUST attach any un-consumed buckets
     495                 :                                  * to its own brigade */
     496           12315 :                                 brig_swap = brig_inp;
     497           12315 :                                 brig_inp = brig_outp;
     498           12315 :                                 brig_outp = brig_swap;
     499           12315 :                                 memset(brig_outp, 0, sizeof(*brig_outp));
     500                 :                         }
     501                 :                         
     502           24353 :                         switch (status) {
     503                 :                                 case PSFS_PASS_ON:
     504                 :                                         /* we get here when the last filter in the chain has data to pass on.
     505                 :                                          * in this situation, we are passing the brig_in brigade into the
     506                 :                                          * stream read buffer */
     507           36903 :                                         while (brig_inp->head) {
     508           12293 :                                                 bucket = brig_inp->head;
     509           12293 :                                                 if (bucket->buf_type != stream->readbuf_type) {
     510                 :                                                         /* Stream expects different datatype than bucket has, convert slopily */
     511               1 :                                                         php_stream_bucket_convert_notranscode(bucket, stream->readbuf_type);
     512                 :                                                 }
     513                 :                                                 /* Bucket type now matches stream type */
     514                 : 
     515                 :                                                 /* grow buffer to hold this bucket
     516                 :                                                  * TODO: this can fail for persistent streams */
     517           12293 :                                                 if (stream->readbuflen - stream->writepos < (unsigned int)bucket->buflen) {
     518           11676 :                                                         stream->readbuflen += bucket->buflen;
     519           11676 :                                                         stream->readbuf.v = perealloc(stream->readbuf.v, PS_ULEN(stream->readbuf_type == IS_UNICODE, stream->readbuflen), stream->is_persistent);
     520                 :                                                 }
     521           12293 :                                                 memcpy(stream->readbuf.s + PS_ULEN(stream->readbuf_type == IS_UNICODE, stream->writepos), bucket->buf.s, PS_ULEN(stream->readbuf_type == IS_UNICODE, bucket->buflen));
     522           12293 :                                                 stream->writepos += bucket->buflen;
     523                 : 
     524           12293 :                                                 php_stream_bucket_unlink(bucket TSRMLS_CC);
     525           12293 :                                                 php_stream_bucket_delref(bucket TSRMLS_CC);
     526                 :                                         }
     527           12305 :                                         break;
     528                 : 
     529                 :                                 case PSFS_FEED_ME:
     530                 :                                         /* when a filter needs feeding, there is no brig_out to deal with.
     531                 :                                          * we simply continue the loop; if the caller needs more data,
     532                 :                                          * we will read again, otherwise out job is done here */
     533           12031 :                                         if (justread == 0) {
     534                 :                                                 /* there is no data */
     535           11916 :                                                 err_flag = 1;
     536           11916 :                                                 break;
     537                 :                                         }
     538             115 :                                         continue;
     539                 : 
     540                 :                                 case PSFS_ERR_FATAL:
     541                 :                                         /* some fatal error. Theoretically, the stream is borked, so all
     542                 :                                          * further reads should fail. */
     543              17 :                                         err_flag = 1;
     544                 :                                         break;
     545                 :                         }
     546                 : 
     547           24238 :                         if (justread == 0 || justread == (size_t)-1) {
     548                 :                                 break;
     549                 :                         }
     550                 :                 }
     551                 : 
     552           12936 :                 efree(chunk_buf);
     553                 :         } else {        /* Unfiltered Binary stream */
     554                 :                 /* is there enough data in the buffer ? */
     555          978743 :                 if (stream->writepos - stream->readpos < (off_t)size) {
     556          978743 :                         size_t justread = 0;
     557                 : 
     558                 :                         /* reduce buffer memory consumption if possible, to avoid a realloc */
     559          978743 :                         if (stream->readbuf.s && stream->readbuflen - stream->writepos < stream->chunk_size) {
     560            1846 :                                 memmove(stream->readbuf.s, stream->readbuf.s + stream->readpos, stream->writepos - stream->readpos);
     561            1846 :                                 stream->writepos -= stream->readpos;
     562            1846 :                                 stream->readpos = 0;
     563                 :                         }
     564                 : 
     565                 :                         /* grow the buffer if required
     566                 :                          * TODO: this can fail for persistent streams */
     567          978743 :                         if (stream->readbuflen - stream->writepos < stream->chunk_size) {
     568           32699 :                                 stream->readbuflen += stream->chunk_size;
     569           32699 :                                 stream->readbuf.s = (char*)perealloc(stream->readbuf.s, stream->readbuflen, stream->is_persistent);
     570                 :                         }
     571                 : 
     572          978743 :                         justread = stream->ops->read(stream, stream->readbuf.s + stream->writepos, stream->readbuflen - stream->writepos TSRMLS_CC);
     573          978743 :                         if (justread != (size_t)-1 && justread != 0) {
     574          944122 :                                 stream->writepos += justread;
     575                 :                         }
     576                 :                 }
     577                 :         }
     578          991679 : }
     579                 : 
     580                 : /* Reads binary data from stream, if the stream is unicode (text), the raw unicode data will be returned */
     581                 : PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size TSRMLS_DC)
     582         1754629 : {
     583         1754629 :         size_t toread = 0, didread = 0;
     584                 : 
     585         3604138 :         while (size > 0) {
     586                 : 
     587                 :                 /* take from the read buffer first.
     588                 :                  * It is possible that a buffered stream was switched to non-buffered, so we
     589                 :                  * drain the remainder of the buffer before using the "raw" read mode for
     590                 :                  * the excess */
     591         1758450 :                 if (stream->writepos - stream->readpos > 0) {
     592          787645 :                         toread = PS_ULEN(stream->readbuf_type == IS_UNICODE, stream->writepos - stream->readpos);
     593                 : 
     594          787645 :                         if (toread > size) {
     595          718484 :                                 toread = size;
     596                 :                         }
     597                 : 
     598          787645 :                         if (stream->readbuf_type == IS_UNICODE) {
     599                 :                                 /* Sloppy read, anyone using php_stream_read() on a unicode stream
     600                 :                                  * had better know what they're doing */
     601                 :                                 
     602               0 :                                 memcpy(buf, stream->readbuf.u + stream->readpos, toread);
     603               0 :                                 stream->readpos += ceil(toread / UBYTES(1));
     604                 :                         } else {
     605          787645 :                                 memcpy(buf, stream->readbuf.s + stream->readpos, toread);
     606          787645 :                                 stream->readpos += toread;
     607                 :                         }
     608                 : 
     609          787645 :                         size -= toread;
     610          787645 :                         buf += toread;
     611          787645 :                         didread += toread;
     612                 :                 }
     613                 : 
     614                 :                 /* ignore eof here; the underlying state might have changed */
     615         1758450 :                 if (size == 0) {
     616          757366 :                         break;
     617                 :                 }
     618                 : 
     619         1070501 :                 if (!stream->readfilters.head && (stream->flags & PHP_STREAM_FLAG_NO_BUFFER || stream->chunk_size == 1)) {
     620           69417 :                         toread = stream->ops->read(stream, buf, size TSRMLS_CC);
     621                 :                 } else {
     622          931667 :                         php_stream_fill_read_buffer(stream, size TSRMLS_CC);
     623                 : 
     624          931667 :                         toread = stream->writepos - stream->readpos;
     625          931667 :                         if (toread > size) {
     626           78862 :                                 toread = size;
     627                 :                         }
     628                 : 
     629          931667 :                         if (toread > 0) {
     630          898482 :                                 if (stream->readbuf_type == IS_UNICODE) {
     631                 :                                         /* Sloppy read, anyone using php_stream_read() on a unicode stream
     632                 :                                          * had better know what they're doing */
     633                 :                                 
     634               0 :                                         memcpy(buf, stream->readbuf.u + stream->readpos, toread);
     635               0 :                                         stream->readpos += ceil(toread / UBYTES(1));
     636                 :                                 } else {
     637          898482 :                                         memcpy(buf, stream->readbuf.s + stream->readpos, toread);
     638                 :                                 }
     639          898482 :                                 stream->readpos += toread;
     640                 :                         }
     641                 :                 }
     642         1001084 :                 if (toread > 0) {
     643          964336 :                         didread += toread;
     644          964336 :                         buf += toread;
     645          964336 :                         size -= toread;
     646                 :                 } else {
     647                 :                         /* EOF, or temporary end of data (for non-blocking mode). */
     648           36748 :                         break;
     649                 :                 }
     650                 :                 /* just break anyway, to avoid greedy read */
     651          964336 :                 if (stream->wrapper != &php_plain_files_wrapper) {
     652          869456 :                         break;
     653                 :                 }
     654                 :         }
     655                 : 
     656         1754629 :         if (didread > 0) {
     657         1724830 :                 stream->position += didread;
     658                 :         }
     659                 : 
     660         1754629 :         return didread;
     661                 : }
     662                 : 
     663                 : /* Read unicode data from a stream.  Returns failure (-1) if not a unicode stream */
     664                 : PHPAPI size_t _php_stream_read_unicode(php_stream *stream, UChar *buf, int size, int maxchars TSRMLS_DC)
     665             282 : {
     666             282 :         size_t toread = 0, didread = 0, string_length = 0;
     667                 : 
     668             282 :         if (stream->readbuf_type != IS_UNICODE) {
     669               0 :                 return -1;
     670                 :         }
     671                 : 
     672             703 :         while (size > 0) {
     673                 :                 /* take from the read buffer first.
     674                 :                  * It is possible that a buffered stream was switched to non-buffered, so we
     675                 :                  * drain the remainder of the buffer before using the "raw" read mode for
     676                 :                  * the excess */
     677                 : 
     678             981 :                 while (size > 0 && (toread = (stream->writepos - stream->readpos)) &&
     679                 :               (toread > 1 ||
     680                 :                            !U16_IS_SURROGATE(stream->readbuf.u[stream->readpos]) ||
     681                 :                            !U16_IS_SURROGATE_LEAD(stream->readbuf.u[stream->readpos]) )) {
     682                 :                         int length;
     683                 : 
     684             139 :                         if (toread > size) {
     685               0 :                                 toread = size;
     686                 :                         }
     687                 : 
     688             139 :                         if (U16_IS_SURROGATE(stream->readbuf.u[stream->readpos + toread - 1]) &&
     689                 :                                 U16_IS_SURROGATE_LEAD(stream->readbuf.u[stream->readpos + toread - 1])) {
     690                 :                                 /* Don't split surrogates */
     691               0 :                                 toread--;
     692               0 :                                 if (!toread) {
     693               0 :                                         break;
     694                 :                                 }
     695                 :                         }
     696                 : 
     697             139 :                         if (maxchars > -1) {
     698               0 :                                 length = u_countChar32(stream->readbuf.u + stream->readpos, toread);
     699               0 :                                 if (string_length + length > maxchars) {
     700                 :                                         /* Don't read more U32 points than the caller asked for */
     701               0 :                                         toread = 0;
     702               0 :                                         length = size - string_length;
     703               0 :                                         U16_FWD_N(stream->readbuf.u + stream->readpos, toread, stream->writepos - stream->readpos, length);
     704                 :                                 }
     705               0 :                                 string_length += length;
     706                 :                         }
     707                 : 
     708             139 :                         memcpy(buf, stream->readbuf.u + stream->readpos, UBYTES(toread));
     709             139 :                         stream->readpos += toread;
     710             139 :                         size -= toread;
     711             139 :                         buf += toread;
     712             139 :                         didread += toread;
     713                 :                 }
     714                 : 
     715                 :                 /* ignore eof here; the underlying state might have changed */
     716             421 :                 if (size == 0) {
     717               0 :                         break;
     718                 :                 }
     719                 : 
     720                 :                 /* just break anyway, to avoid greedy read */
     721             421 :                 if (didread > 0 && (stream->wrapper != &php_plain_files_wrapper)) {
     722              13 :                         break;
     723                 :                 }
     724                 : 
     725             408 :                 php_stream_fill_read_buffer(stream, size * sizeof(UChar) TSRMLS_CC);
     726             408 :                 if (stream->writepos - stream->readpos <= 0) {
     727                 :                         /* EOF, or temporary end of data (for non-blocking mode). */
     728             269 :                         break;
     729                 :                 }
     730                 :         }
     731                 : 
     732             282 :         if (didread > 0) {
     733             139 :                 stream->position += didread;
     734                 :         }
     735                 : 
     736             282 :         return didread;
     737                 : }
     738                 : 
     739                 : PHPAPI UChar *_php_stream_read_unicode_chars(php_stream *stream, int *pchars TSRMLS_DC)
     740             131 : {
     741             131 :         int size = *pchars;
     742                 :         UChar *bufstart, *buf;
     743             131 :         int buflen = size;
     744             131 :         size_t toread = 0, didread = 0, string_length = 0;
     745                 : 
     746             131 :         if (stream->readbuf_type != IS_UNICODE) {
     747               0 :                 return NULL;
     748                 :         }
     749                 : 
     750                 :         /* Allocate for ideal size first, add more later if needed */
     751             131 :         bufstart = buf = eumalloc(buflen + 1);
     752                 : 
     753             334 :         while (size > 0) {
     754                 :                 /* take from the read buffer first.
     755                 :                  * It is possible that a buffered stream was switched to non-buffered, so we
     756                 :                  * drain the remainder of the buffer before using the "raw" read mode for
     757                 :                  * the excess */
     758                 : 
     759             526 :                 while (size > 0 && (toread = (stream->writepos - stream->readpos)) &&
     760                 :               (toread > 1 || 
     761                 :                            !U16_IS_SURROGATE(stream->readbuf.u[stream->readpos]) ||
     762                 :                            !U16_IS_SURROGATE_LEAD(stream->readbuf.u[stream->readpos]) )) {
     763                 :                         int length;
     764                 : 
     765             120 :                         if (toread > size) {
     766              77 :                                 toread = size;
     767                 :                         }
     768                 : 
     769             120 :                         if (U16_IS_SURROGATE(stream->readbuf.u[stream->readpos + toread - 1]) &&
     770                 :                                 U16_IS_SURROGATE_LEAD(stream->readbuf.u[stream->readpos + toread - 1])) {
     771                 :                                 /* Don't split surrogates */
     772               0 :                                 toread--;
     773               0 :                                 if (!toread) {
     774               0 :                                         break;
     775                 :                                 }
     776                 :                         }
     777                 : 
     778             120 :                         length = u_countChar32(stream->readbuf.u + stream->readpos, toread);
     779             120 :                         if (string_length + length > size) {
     780                 :                                 /* Don't read more U32 points than the caller asked for */
     781               0 :                                 toread = 0;
     782               0 :                                 length = size - string_length;
     783               0 :                                 U16_FWD_N(stream->readbuf.u + stream->readpos, toread, stream->writepos - stream->readpos, length);
     784                 :                         }
     785                 : 
     786             120 :                         if (toread > (buflen - didread)) {
     787                 :                                 /* We know we're in surrogated territory at this point, allocate aggressively */
     788               0 :                                 int ofs = buf - bufstart;
     789               0 :                                 buflen += size * 2;
     790               0 :                                 bufstart = eurealloc(bufstart, buflen + 1);
     791               0 :                                 buf = bufstart + ofs;
     792                 :                         }
     793                 : 
     794             120 :                         memcpy(buf, stream->readbuf.u + stream->readpos, UBYTES(toread));
     795             120 :                         stream->readpos += toread;
     796             120 :                         size -= toread;
     797             120 :                         buf += toread;
     798             120 :                         didread += toread;
     799             120 :                         string_length += length;
     800                 :                 }
     801                 : 
     802                 :                 /* ignore eof here; the underlying state might have changed */
     803             203 :                 if (size == 0) {
     804              98 :                         break;
     805                 :                 }
     806                 : 
     807                 :                 /* just break anyway, to avoid greedy read */
     808             105 :                 if (didread > 0 && (stream->wrapper != &php_plain_files_wrapper)) {
     809               0 :                         break;
     810                 :                 }
     811                 : 
     812             105 :                 php_stream_fill_read_buffer(stream, UBYTES(size) TSRMLS_CC);
     813             105 :                 if (stream->writepos - stream->readpos <= 0) {
     814                 :                         /* EOF, or temporary end of data (for non-blocking mode). */
     815              33 :                         break;
     816                 :                 }
     817                 :         }
     818                 : 
     819             131 :         if (didread > 0) {
     820             120 :                 stream->position += didread;
     821                 :         }
     822                 : 
     823             131 :         *pchars = string_length;
     824             131 :         buf[0] = 0;
     825             131 :         return bufstart;
     826                 : }
     827                 : 
     828                 : PHPAPI int _php_stream_eof(php_stream *stream TSRMLS_DC)
     829         1037170 : {
     830                 :         /* if there is data in the buffer, it's not EOF */
     831         1037170 :         if (stream->writepos - stream->readpos > 0) {
     832         1005885 :                 return 0;
     833                 :         }
     834                 : 
     835                 :         /* use the configured timeout when checking eof */
     836           31285 :         if (!stream->eof && PHP_STREAM_OPTION_RETURN_ERR ==
     837                 :                         php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS,
     838                 :                         0, NULL)) {
     839               4 :                 stream->eof = 1;
     840                 :         }
     841                 : 
     842           31285 :         return stream->eof;
     843                 : }
     844                 : 
     845                 : PHPAPI int _php_stream_putc(php_stream *stream, int c TSRMLS_DC)
     846              23 : {
     847              23 :         unsigned char buf = c;
     848                 : 
     849              23 :         if (php_stream_write(stream, &buf, 1) > 0) {
     850              23 :                 return 1;
     851                 :         }
     852               0 :         return EOF;
     853                 : }
     854                 : 
     855                 : PHPAPI int _php_stream_getc(php_stream *stream TSRMLS_DC)
     856          531894 : {
     857                 :         char buf;
     858                 : 
     859          531894 :         if (php_stream_read(stream, &buf, 1) > 0) {
     860          531872 :                 return buf & 0xff;
     861                 :         }
     862              22 :         return EOF;
     863                 : }
     864                 : 
     865                 : PHPAPI int _php_stream_puts(php_stream *stream, char *buf TSRMLS_DC)
     866               0 : {
     867                 :         int len;
     868               0 :         char newline[2] = "\n"; /* is this OK for Win? */
     869               0 :         len = strlen(buf);
     870                 : 
     871               0 :         if (len > 0 && php_stream_write(stream, buf, len) && php_stream_write(stream, newline, 1)) {
     872               0 :                 return 1;
     873                 :         }
     874               0 :         return 0;
     875                 : }
     876                 : 
     877                 : PHPAPI int _php_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
     878           33007 : {
     879           33007 :         memset(ssb, 0, sizeof(*ssb));
     880                 : 
     881                 :         /* if the stream was wrapped, allow the wrapper to stat it */
     882           33007 :         if (stream->wrapper && stream->wrapper->wops->stream_stat != NULL) {
     883               0 :                 return stream->wrapper->wops->stream_stat(stream->wrapper, stream, ssb TSRMLS_CC);
     884                 :         }
     885                 : 
     886                 :         /* if the stream doesn't directly support stat-ing, return with failure.
     887                 :          * We could try and emulate this by casting to a FD and fstat-ing it,
     888                 :          * but since the fd might not represent the actual underlying content
     889                 :          * this would give bogus results. */
     890           33007 :         if (stream->ops->stat == NULL) {
     891               8 :                 return -1;
     892                 :         }
     893                 : 
     894           32999 :         return (stream->ops->stat)(stream, ssb TSRMLS_CC);
     895                 : }
     896                 : 
     897                 : PHPAPI void *php_stream_locate_eol(php_stream *stream, zstr zbuf, int buf_len TSRMLS_DC)
     898         1376952 : {
     899                 :         size_t avail;
     900         1376952 :         char *cr, *lf, *eol = NULL;
     901         1376952 :         char *readptr, *buf = zbuf.s;
     902                 : 
     903         1376952 :         if (!buf) {
     904         1376847 :                 readptr = stream->readbuf.s + PS_ULEN(stream->readbuf_type == IS_UNICODE, stream->readpos);
     905         1376847 :                 avail = stream->writepos - stream->readpos;
     906                 :         } else {
     907             105 :                 readptr = zbuf.s;
     908             105 :                 avail = buf_len;
     909                 :         }
     910                 : 
     911         1376952 :         if (stream->flags & PHP_STREAM_FLAG_DETECT_EOL) {
     912               0 :                 if (stream->readbuf_type == IS_UNICODE) {
     913               0 :                         cr = (char*)u_memchr((UChar*)readptr, '\r', avail);
     914               0 :                         lf = (char*)u_memchr((UChar*)readptr, '\n', avail);
     915                 :                 } else {
     916               0 :                         cr = memchr(readptr, '\r', avail);
     917               0 :                         lf = memchr(readptr, '\n', avail);
     918                 :                 }
     919                 : 
     920               0 :                 if (cr && lf != cr + 1 && !(lf && lf < cr)) {
     921                 :                         /* mac */
     922               0 :                         stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL;
     923               0 :                         stream->flags |= PHP_STREAM_FLAG_EOL_MAC;
     924               0 :                         eol = cr;
     925               0 :                 } else if ((cr && lf && cr == lf - 1) || (lf)) {
     926                 :                         /* dos or unix endings */
     927               0 :                         stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL;
     928               0 :                         eol = lf;
     929                 :                 }
     930         1376952 :         } else if (stream->flags & PHP_STREAM_FLAG_EOL_MAC) {
     931               0 :                 eol = (stream->readbuf_type == IS_UNICODE) ? u_memchr((UChar*)readptr, '\r', avail) : memchr(readptr, '\r', avail);
     932                 :         } else {
     933                 :                 /* unix (and dos) line endings */
     934         1376952 :                 eol = (stream->readbuf_type == IS_UNICODE) ? u_memchr((UChar*)readptr, '\n', avail) : memchr(readptr, '\n', avail);
     935                 :         }
     936                 : 
     937         1376952 :         return (void*)eol;
     938                 : }
     939                 : 
     940                 : /* If buf == NULL, the buffer will be allocated automatically and will be of an
     941                 :  * appropriate length to hold the line, regardless of the line length, memory
     942                 :  * permitting -- returned string will be up to (maxlen-1) units of (maxchars) characters, last byte holding terminating NULL
     943                 :  * Like php_stream_read(), this will (UTODO) treat unicode streams as ugly binary data (use with caution) */
     944                 : PHPAPI void *_php_stream_get_line(php_stream *stream, int buf_type, zstr buf, size_t maxlen, size_t maxchars, size_t *returned_len TSRMLS_DC)
     945         1336979 : {
     946         1336979 :         size_t avail = 0;
     947         1336979 :         size_t current_buf_size = 0;
     948         1336979 :         size_t total_copied = 0;
     949         1336979 :         int grow_mode = 0;
     950         1336979 :         int is_unicode = stream->readbuf_type == IS_UNICODE;
     951         1336979 :         int split_surrogate = 0;
     952         1336979 :         zstr bufstart = buf;
     953                 : 
     954         1336979 :         if ((buf_type == IS_STRING && is_unicode) ||
     955                 :                 (buf_type == IS_UNICODE && !is_unicode)) {
     956                 :                 /* UTODO: Allow sloppy conversion */
     957               0 :                 return NULL;
     958                 :         }
     959                 : 
     960         1336979 :         if (buf.v == NULL) {
     961         1014048 :                 grow_mode = 1;
     962          322931 :         } else if (maxlen == 0) {
     963               0 :                 return NULL;
     964                 :         }
     965                 : 
     966                 :         /*
     967                 :          * If the underlying stream operations block when no new data is readable,
     968                 :          * we need to take extra precautions.
     969                 :          *
     970                 :          * If there is buffered data available, we check for a EOL. If it exists,
     971                 :          * we pass the data immediately back to the caller. This saves a call
     972                 :          * to the read implementation and will not block where blocking
     973                 :          * is not necessary at all.
     974                 :          *
     975                 :          * If the stream buffer contains more data than the caller requested,
     976                 :          * we can also avoid that costly step and simply return that data.
     977                 :          */
     978                 : 
     979                 :         for (;;) {
     980         1438122 :                 avail = stream->writepos - stream->readpos;
     981                 : 
     982         1481586 :                 if (!split_surrogate && avail > 0) {
     983         1376847 :                         size_t cpysz = avail;
     984                 :                         zstr readptr;
     985         1376847 :                         int done = 0;
     986                 : 
     987         1376847 :                         if (is_unicode) {
     988                 :                                 UChar *eol;
     989         1005261 :                                 readptr.u = stream->readbuf.u + stream->readpos;
     990                 : 
     991         1005261 :                                 eol = php_stream_locate_eol(stream, NULL_ZSTR, 0 TSRMLS_CC);
     992         1005261 :                                 if (eol) {
     993         1003223 :                                         cpysz = eol - readptr.u + 1;
     994         1003223 :                                         done = 1;
     995                 :                                 }
     996                 : 
     997         1005261 :                                 if (U16_IS_SURROGATE(readptr.u[cpysz - 1]) &&
     998                 :                                         U16_IS_SURROGATE_LEAD(readptr.u[cpysz - 1])) {
     999                 :                                         /* Don't orphan */
    1000               0 :                                         cpysz--;
    1001               0 :                                         if (!cpysz) {
    1002                 :                                                 /* Force the loop to land on fill_read_buffer */
    1003               0 :                                                 split_surrogate = 1; /* must specifically be 1 */
    1004               0 :                                                 continue;
    1005                 :                                         }
    1006                 :                                 }
    1007                 :                         } else {
    1008                 :                                 char *eol;
    1009          371586 :                                 readptr.s = stream->readbuf.s + stream->readpos;
    1010          371586 :                                 eol = php_stream_locate_eol(stream, NULL_ZSTR, 0 TSRMLS_CC);
    1011          371586 :                                 if (eol) {
    1012          330072 :                                         cpysz = eol - readptr.s + 1;
    1013          330072 :                                         done = 1;
    1014                 :                                 }
    1015                 :                         }
    1016                 : 
    1017         1376847 :                         if (grow_mode) {
    1018                 :                                 /* allow room for a NUL. If this realloc is really a realloc
    1019                 :                                  * (ie: second time around), we get an extra byte. In most
    1020                 :                                  * cases, with the default chunk size of 8K, we will only
    1021                 :                                  * incur that overhead once.  When people have lines longer
    1022                 :                                  * than 8K, we waste 1 byte per additional 8K or so.
    1023                 :                                  * That seems acceptable to me, to avoid making this code
    1024                 :                                  * hard to follow */
    1025         1013222 :                                 bufstart.s = erealloc(bufstart.s, PS_ULEN(stream->readbuf_type == IS_UNICODE, current_buf_size + cpysz + 1));
    1026         1013222 :                                 buf.s = bufstart.s + PS_ULEN(stream->readbuf_type == IS_UNICODE, total_copied);
    1027         1013222 :                                 current_buf_size += cpysz + 1;
    1028                 :                         } else {
    1029          363625 :                                 if (cpysz >= maxlen - 1) {
    1030             579 :                                         cpysz = maxlen - 1;
    1031             579 :                                         done = 1;
    1032                 :                                 }
    1033                 :                         }
    1034                 : 
    1035         1376847 :                         if (is_unicode) {
    1036         1005261 :                                 if (maxchars) {
    1037         1005018 :                                         int ulen = u_countChar32(readptr.u, cpysz);
    1038                 :         
    1039         1005018 :                                         if (ulen > maxchars) {
    1040               0 :                                                 int32_t i = 0;
    1041                 :         
    1042               0 :                                                 ulen = maxchars;
    1043               0 :                                                 U16_FWD_N(readptr.u, i, cpysz, ulen);
    1044               0 :                                                 cpysz = i;
    1045                 :                                         }
    1046         1005018 :                                         maxchars -= ulen;
    1047                 :                                 }
    1048         1005261 :                                 memcpy(buf.u, readptr.u, UBYTES(cpysz));
    1049         1005261 :                                 buf.u += cpysz;
    1050                 :                         } else {
    1051          371586 :                                 if (maxchars && cpysz > maxchars) {
    1052               0 :                                         cpysz = maxchars;
    1053                 :                                 }
    1054          371586 :                                 memcpy(buf.s, readptr.s, cpysz);
    1055          371586 :                                 buf.s += cpysz;
    1056                 :                         }
    1057                 : 
    1058         1376847 :                         stream->position += cpysz;
    1059         1376847 :                         stream->readpos += cpysz;
    1060         1376847 :                         maxlen -= cpysz;
    1061         1376847 :                         total_copied += cpysz;
    1062                 : 
    1063         1376847 :                         if (done) {
    1064         1333383 :                                 break;
    1065                 :                         }
    1066           61275 :                 } else if (stream->eof) {
    1067            1808 :                         break;
    1068                 :                 } else {
    1069                 :                         /* XXX: Should be fine to always read chunk_size */
    1070                 :                         size_t toread;
    1071                 :                         
    1072           59467 :                         if (grow_mode) {
    1073           13963 :                                 toread = stream->chunk_size;
    1074                 :                         } else {
    1075           45504 :                                 toread = maxlen - 1;
    1076           45504 :                                 if (toread > stream->chunk_size) {
    1077               0 :                                         toread = stream->chunk_size;
    1078                 :                                 }
    1079                 :                         }
    1080                 : 
    1081           59467 :                         php_stream_fill_read_buffer(stream, toread TSRMLS_CC);
    1082                 : 
    1083           59467 :                         if (stream->writepos - stream->readpos <= split_surrogate) {
    1084            1788 :                                 break;
    1085                 :                         }
    1086           57679 :                         split_surrogate = 0;
    1087                 :                 }
    1088          101143 :         }
    1089                 : 
    1090         1336979 :         if (returned_len) {
    1091         1326656 :                 *returned_len = total_copied;
    1092                 :         }
    1093                 : 
    1094         1336979 :         if (total_copied == 0) {
    1095                 :                 assert(stream->eof || !grow_mode ||
    1096                 :                                 (grow_mode && bufstart.v == NULL));
    1097            1928 :                 return NULL;
    1098                 :         }
    1099                 : 
    1100         1335051 :         if (is_unicode) {
    1101         1004763 :                 buf.u[0] = 0;
    1102                 :         } else {
    1103          330288 :                 buf.s[0] = 0;
    1104                 :         }
    1105                 : 
    1106         1335051 :         return bufstart.s;
    1107                 : }
    1108                 : 
    1109                 : PHPAPI char *php_stream_get_record(php_stream *stream, size_t maxlen, size_t *returned_len, char *delim, size_t delim_len TSRMLS_DC)
    1110              37 : {
    1111                 :         char *e, *buf;
    1112                 :         size_t toread, len;
    1113              37 :         int skip = 0;
    1114                 : 
    1115              37 :         len = stream->writepos - stream->readpos;
    1116                 : 
    1117              82 :         while (len < maxlen) {
    1118                 : 
    1119                 :                 size_t just_read;
    1120              32 :                 toread = MIN(maxlen - len, stream->chunk_size);
    1121                 : 
    1122              32 :                 php_stream_fill_read_buffer(stream, len + toread TSRMLS_CC);
    1123                 : 
    1124              32 :                 just_read = (stream->writepos - stream->readpos) - len;
    1125              32 :                 len += just_read;
    1126                 : 
    1127              32 :                 if (just_read < toread) {
    1128              24 :                         break;
    1129                 :                 }
    1130                 :         }
    1131                 : 
    1132              37 :         if (delim_len == 0 || !delim) {
    1133               0 :                 toread = maxlen;
    1134                 :         } else {
    1135                 :                 size_t seek_len;
    1136                 : 
    1137              37 :                 seek_len = stream->writepos - stream->readpos;
    1138              37 :                 if (seek_len > maxlen) {
    1139              11 :                         seek_len = maxlen;
    1140                 :                 }
    1141                 : 
    1142              37 :                 if (delim_len == 1) {
    1143              12 :                         e = memchr(stream->readbuf.s + stream->readpos, *delim, seek_len);
    1144                 :                 } else {
    1145              25 :                         e = php_memnstr(stream->readbuf.s + stream->readpos, delim, delim_len, (stream->readbuf.s + stream->readpos + seek_len));
    1146                 :                 }
    1147                 : 
    1148              37 :                 if (!e) {
    1149              14 :                         if (seek_len < maxlen && !stream->eof) {
    1150               6 :                                 return NULL;
    1151                 :                         }
    1152               8 :                         toread = maxlen;
    1153                 :                 } else {
    1154              23 :                         toread = e - stream->readbuf.s - stream->readpos;
    1155              23 :                         skip = 1;
    1156                 :                 }
    1157                 :         }
    1158                 : 
    1159              31 :         if (toread > maxlen && maxlen > 0) {
    1160               0 :                 toread = maxlen;
    1161                 :         }
    1162                 : 
    1163              31 :         buf = emalloc(toread + 1);
    1164              31 :         *returned_len = php_stream_read(stream, buf, toread);
    1165                 : 
    1166                 :         if (*returned_len >= 0) {
    1167              31 :                 if (skip) {
    1168              23 :                         stream->readpos += delim_len;
    1169              23 :                         stream->position += delim_len;
    1170                 :                 }
    1171              31 :                 buf[*returned_len] = '\0';
    1172              31 :                 return buf;
    1173                 :         } else {
    1174                 :                 efree(buf);
    1175                 :                 return NULL;
    1176                 :         }
    1177                 : }
    1178                 : 
    1179                 : PHPAPI UChar *php_stream_get_record_unicode(php_stream *stream, size_t maxlen, size_t maxchars, size_t *returned_len, UChar *delim, size_t delim_len TSRMLS_DC)
    1180               0 : {
    1181                 :         UChar *e, *buf;
    1182                 :         size_t toread;
    1183               0 :         int skip = 0;
    1184                 : 
    1185               0 :         if (stream->readbuf_type != IS_UNICODE) {
    1186               0 :                 return NULL;
    1187                 :         }
    1188                 : 
    1189               0 :         php_stream_fill_read_buffer(stream, maxlen TSRMLS_CC);
    1190                 : 
    1191               0 :         if (delim_len == 0 || !delim) {
    1192               0 :                 toread = maxlen;
    1193                 :         } else {
    1194                 :                 size_t seek_len;
    1195                 : 
    1196               0 :                 seek_len = stream->writepos - stream->readpos;
    1197               0 :                 if (seek_len > maxlen) {
    1198               0 :                         seek_len = maxlen;
    1199                 :                 }
    1200                 : 
    1201               0 :                 if (delim_len == 1) {
    1202               0 :                         e = u_memchr(stream->readbuf.u + stream->readpos, *delim, seek_len);
    1203                 :                 } else {
    1204               0 :                         e = u_strFindFirst(stream->readbuf.u + stream->readpos, stream->readbuf.u + stream->readpos + seek_len, delim, delim_len);
    1205                 :                 }
    1206                 : 
    1207               0 :                 if (!e) {
    1208               0 :                         toread = maxlen;
    1209                 :                 } else {
    1210               0 :                         toread = e - (stream->readbuf.u + stream->readpos);
    1211               0 :                         skip = 1;
    1212                 :                 }
    1213                 :         }
    1214                 : 
    1215               0 :         if (toread > maxlen && maxlen > 0) {
    1216               0 :                 toread = maxlen;
    1217                 :         }
    1218                 : 
    1219               0 :         if (U16_IS_SURROGATE(stream->readbuf.u[stream->readpos + toread - 1]) &&
    1220                 :                 U16_IS_SURROGATE_LEAD(stream->readbuf.u[stream->readpos + toread - 1])) {
    1221                 :                 /* Don't orphan */
    1222               0 :                 toread--;
    1223                 :         }
    1224                 : 
    1225               0 :         if (maxchars > 0) {
    1226               0 :                 size_t ulen = u_countChar32(stream->readbuf.u + stream->readpos, toread);
    1227                 : 
    1228               0 :                 if (maxchars > ulen) {
    1229               0 :                         int i = 0;
    1230               0 :                         UChar *s = stream->readbuf.u + stream->readpos;
    1231                 : 
    1232               0 :                         U16_FWD_N(s, i, toread, maxchars);
    1233               0 :                         toread = i;
    1234                 :                 }
    1235                 :         }
    1236                 : 
    1237               0 :         buf = eumalloc(toread + 1);
    1238               0 :         *returned_len = php_stream_read_unicode(stream, buf, toread);
    1239                 : 
    1240                 :         if (*returned_len >= 0) {
    1241               0 :                 if (skip) {
    1242               0 :                         stream->readpos += delim_len;
    1243               0 :                         stream->position += delim_len;
    1244                 :                 }
    1245               0 :                 buf[*returned_len] = 0;
    1246               0 :                 return buf;
    1247                 :         } else {
    1248                 :                 efree(buf);
    1249                 :                 return NULL;
    1250                 :         }
    1251                 : }
    1252                 : 
    1253                 : /* Writes a buffer directly to a stream, using multiple of the chunk size */
    1254                 : static size_t _php_stream_write_buffer(php_stream *stream, int buf_type, zstr buf, int buflen TSRMLS_DC)
    1255          205301 : {
    1256          205301 :         size_t didwrite = 0, towrite, justwrote, shouldwrite;
    1257          205301 :         char *freeme = NULL;
    1258          205301 :         void *buf_orig = buf.v;
    1259          205301 :         int buflen_orig = buflen, conv_err = 0;
    1260                 : 
    1261                 :         /* if we have a seekable stream we need to ensure that data is written at the
    1262                 :          * current stream->position. This means invalidating the read buffer and then
    1263                 :          * performing a low-level seek */
    1264          205301 :         if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0 && stream->readpos != stream->writepos) {
    1265               1 :                 stream->readpos = stream->writepos = 0;
    1266                 : 
    1267               1 :                 stream->ops->seek(stream, stream->position, SEEK_SET, &stream->position TSRMLS_CC);
    1268                 :         }
    1269                 : 
    1270          205301 :         if (buf_type == IS_UNICODE) {
    1271            8752 :                 int len, num_conv, ulen = u_countChar32(buf.u, buflen);
    1272                 :                 char *str;
    1273            8752 :                 UErrorCode status = U_ZERO_ERROR;
    1274                 : 
    1275                 :                 /* Use runtime_encoding to map to binary */
    1276            8752 :                 num_conv = zend_unicode_to_string_ex(ZEND_U_CONVERTER(UG(runtime_encoding_conv)), &str, &len, buf.u, buflen, &status);
    1277            8752 :                 if (U_FAILURE(status)) {
    1278               0 :                         zend_raise_conversion_error_ex("Unable to convert data to be written", ZEND_U_CONVERTER(UG(runtime_encoding_conv)),
    1279                 :                                                                         ZEND_FROM_UNICODE, num_conv TSRMLS_CC);
    1280                 :                 } else {
    1281            8752 :                         php_error_docref(NULL TSRMLS_CC, E_NOTICE, "%d character unicode buffer downcoded for binary stream runtime_encoding", ulen);
    1282                 :                 }
    1283                 : 
    1284            8752 :                 if (num_conv < buflen) {
    1285               0 :                         conv_err = 1;
    1286                 :                 }
    1287                 : 
    1288            8752 :                 freeme = buf.s = str;
    1289            8752 :                 buflen = len;
    1290                 :         }
    1291                 : 
    1292          205301 :         shouldwrite = buflen;
    1293                 : 
    1294          617069 :         while (buflen > 0) {
    1295          206538 :                 towrite = buflen;
    1296          206538 :                 if (towrite > stream->chunk_size) {
    1297            1233 :                         towrite = stream->chunk_size;
    1298                 :                 }
    1299                 : 
    1300          206538 :                 justwrote = stream->ops->write(stream, buf.s, towrite TSRMLS_CC);
    1301                 : 
    1302                 :                 /* convert justwrote to an integer, since normally it is unsigned */
    1303          206538 :                 if ((int)justwrote > 0) {
    1304          206467 :                         buf.s += justwrote;
    1305          206467 :                         buflen -= justwrote;
    1306          206467 :                         didwrite += justwrote;
    1307                 :                         
    1308                 :                         /* Only screw with the buffer if we can seek, otherwise we lose data
    1309                 :                          * buffered from fifos and sockets */
    1310          206467 :                         if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) {
    1311          175016 :                                 stream->position += justwrote;
    1312                 :                         }
    1313                 :                 } else {
    1314              71 :                         break;
    1315                 :                 }
    1316                 :         }
    1317                 : 
    1318          205301 :         if (buf_type == IS_UNICODE) {
    1319                 :                 /* Map bytes written back to UChars written */
    1320                 : 
    1321           17499 :                 if (shouldwrite == didwrite && !conv_err) {
    1322                 :                         /* wrote it all */
    1323            8747 :                         didwrite = buflen_orig;
    1324                 :                 } else {
    1325                 :                         /* Figure out how didwrite corresponds to the input buffer */
    1326               5 :                         char *tmp = emalloc(didwrite + 1), *t = tmp;
    1327               5 :                         const UChar *s = buf_orig;
    1328               5 :                         UErrorCode status = U_ZERO_ERROR;
    1329                 : 
    1330               5 :                         ucnv_resetFromUnicode(ZEND_U_CONVERTER(UG(runtime_encoding_conv)));
    1331               5 :                         ucnv_fromUnicode(ZEND_U_CONVERTER(UG(runtime_encoding_conv)), &t, t + didwrite, &s, s + buflen_orig, NULL, TRUE, &status);
    1332                 : 
    1333               5 :                         didwrite = s - ((UChar*)buf_orig);
    1334               5 :                         efree(tmp);
    1335                 :                 }
    1336                 :         }
    1337                 : 
    1338          205301 :         if (freeme) {
    1339            8752 :                 efree(freeme);
    1340                 :         }
    1341                 : 
    1342          205301 :         return didwrite;
    1343                 : }
    1344                 : 
    1345                 : /* push some data through the write filter chain.
    1346                 :  * buf may be NULL, if flags are set to indicate a flush.
    1347                 :  * This may trigger a real write to the stream.
    1348                 :  * Returns the number of bytes consumed from buf by the first filter in the chain.
    1349                 :  * */
    1350                 : static size_t _php_stream_write_filtered(php_stream *stream, int buf_type, zstr buf, int count, int flags TSRMLS_DC)
    1351           11235 : {
    1352           11235 :         size_t consumed = 0;
    1353                 :         php_stream_bucket *bucket;
    1354           11235 :         php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL };
    1355           11235 :         php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap;
    1356           11235 :         php_stream_filter_status_t status = PSFS_ERR_FATAL;
    1357                 :         php_stream_filter *filter;
    1358                 : 
    1359           11235 :         if (buf.v) {
    1360            3424 :                 if (buf_type == IS_UNICODE) {
    1361            2659 :                         bucket = php_stream_bucket_new_unicode(stream, buf.u, count, 0, 0 TSRMLS_CC);
    1362                 :                 } else {
    1363             765 :                         bucket = php_stream_bucket_new(stream, buf.s, count, 0, 0 TSRMLS_CC);
    1364                 :                 }
    1365            3424 :                 php_stream_bucket_append(brig_inp, bucket TSRMLS_CC);
    1366                 :         }
    1367                 : 
    1368           14632 :         for (filter = stream->writefilters.head; filter; filter = filter->next) {
    1369                 :                 /* for our return value, we are interested in the number of bytes consumed from
    1370                 :                  * the first filter in the chain */
    1371           11248 :                 status = filter->fops->filter(stream, filter, brig_inp, brig_outp, (filter == stream->writefilters.head) ? &consumed : NULL, flags TSRMLS_CC);
    1372           11248 :                 if (status != PSFS_PASS_ON) {
    1373            7851 :                         break;
    1374                 :                 }
    1375                 :                 /* brig_out becomes brig_in.
    1376                 :                  * brig_in will always be empty here, as the filter MUST attach any un-consumed buckets
    1377                 :                  * to its own brigade */
    1378            3397 :                 brig_swap = brig_inp;
    1379            3397 :                 brig_inp = brig_outp;
    1380            3397 :                 brig_outp = brig_swap;
    1381            3397 :                 memset(brig_outp, 0, sizeof(*brig_outp));
    1382                 :         }
    1383                 : 
    1384           11235 :         switch (status) {
    1385                 :                 case PSFS_PASS_ON:
    1386                 :                         /* filter chain generated some output; push it through to the
    1387                 :                          * underlying stream */
    1388           10302 :                         while (brig_inp->head) {
    1389            3534 :                                 bucket = brig_inp->head;
    1390            3534 :                                 _php_stream_write_buffer(stream, bucket->buf_type, bucket->buf, bucket->buflen TSRMLS_CC);
    1391                 :                                 /* Potential error situation - eg: no space on device. Perhaps we should keep this brigade
    1392                 :                                  * hanging around and try to write it later.
    1393                 :                                  * At the moment, we just drop it on the floor
    1394                 :                                  * */
    1395                 : 
    1396            3534 :                                 php_stream_bucket_unlink(bucket TSRMLS_CC);
    1397            3534 :                                 php_stream_bucket_delref(bucket TSRMLS_CC);
    1398                 :                         }
    1399                 :                         break;
    1400                 :                 case PSFS_FEED_ME:
    1401                 :                         /* need more data before we can push data through to the stream */
    1402                 :                         break;
    1403                 : 
    1404                 :                 case PSFS_ERR_FATAL:
    1405                 :                         /* some fatal error.  Theoretically, the stream is borked, so all
    1406                 :                          * further writes should fail. */
    1407                 :                         break;
    1408                 :         }
    1409                 : 
    1410           11235 :         return consumed;
    1411                 : }
    1412                 : 
    1413                 : PHPAPI int _php_stream_flush(php_stream *stream, int closing TSRMLS_DC)
    1414          194101 : {
    1415          194101 :         int ret = 0;
    1416                 : 
    1417          194101 :         if (stream->writefilters.head) {
    1418            7811 :                 _php_stream_write_filtered(stream, IS_STRING, NULL_ZSTR, 0, closing ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC  TSRMLS_CC);
    1419                 :         }
    1420                 : 
    1421          194101 :         if (stream->ops->flush) {
    1422          190788 :                 ret = stream->ops->flush(stream TSRMLS_CC);
    1423                 :         }
    1424                 : 
    1425          194101 :         return ret;
    1426                 : }
    1427                 : 
    1428                 : PHPAPI size_t _php_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
    1429          201717 : {
    1430          201717 :         if (buf == NULL || count == 0 || stream->ops->write == NULL) {
    1431            7936 :                 return 0;
    1432                 :         }
    1433                 : 
    1434          193781 :         if (stream->writefilters.head) {
    1435             765 :                 return _php_stream_write_filtered(stream, IS_STRING, ZSTR((void*)buf), count, PSFS_FLAG_NORMAL TSRMLS_CC);
    1436                 :         } else {
    1437          193016 :                 return _php_stream_write_buffer(stream, IS_STRING, ZSTR((void*)buf), count TSRMLS_CC);
    1438                 :         }
    1439                 : }
    1440                 : 
    1441                 : PHPAPI size_t _php_stream_write_unicode(php_stream *stream, const UChar *buf, int count TSRMLS_DC)
    1442           11464 : {
    1443                 :         int32_t ret;
    1444                 :         
    1445           11464 :         if (buf == NULL || count == 0 || stream->ops->write == NULL) {
    1446              54 :                 return 0;
    1447                 :         }
    1448                 : 
    1449           11410 :         if (stream->writefilters.head) {
    1450            2659 :                 ret = _php_stream_write_filtered(stream, IS_UNICODE, ZSTR((void*)buf), count, PSFS_FLAG_NORMAL TSRMLS_CC);
    1451                 :         } else {
    1452            8751 :                 ret = _php_stream_write_buffer(stream, IS_UNICODE, ZSTR((void*)buf), count TSRMLS_CC);
    1453                 :         }
    1454                 : 
    1455           11410 :         return ret;
    1456                 : }
    1457                 : 
    1458                 : PHPAPI size_t _php_stream_printf(php_stream *stream TSRMLS_DC, const char *fmt, ...)
    1459             130 : {
    1460                 :         size_t count;
    1461                 :         char *buf;
    1462                 :         va_list ap;
    1463                 : 
    1464             130 :         va_start(ap, fmt);
    1465             130 :         count = vspprintf(&buf, 0, fmt, ap);
    1466             130 :         va_end(ap);
    1467                 : 
    1468             130 :         if (!buf) {
    1469               0 :                 return 0; /* error condition */
    1470                 :         }
    1471                 : 
    1472             130 :         count = php_stream_write(stream, buf, count);
    1473             130 :         efree(buf);
    1474                 : 
    1475             130 :         return count;
    1476                 : }
    1477                 : 
    1478                 : PHPAPI off_t _php_stream_tell(php_stream *stream TSRMLS_DC)
    1479           61924 : {
    1480           61924 :         return stream->position;
    1481                 : }
    1482                 : 
    1483                 : PHPAPI int _php_stream_seek(php_stream *stream, off_t offset, int whence TSRMLS_DC)
    1484           72719 : {
    1485                 :         /* handle the case where we are in the buffer */
    1486           72719 :         if ((stream->flags & PHP_STREAM_FLAG_NO_BUFFER) == 0) {
    1487           72042 :                 switch(whence) {
    1488                 :                         case SEEK_CUR:
    1489           17767 :                                 if (offset > 0 && offset < stream->writepos - stream->readpos) {
    1490             436 :                                         stream->readpos += offset;
    1491             436 :                                         stream->position += offset;
    1492             436 :                                         stream->eof = 0;
    1493             436 :                                         return 0;
    1494                 :                                 }
    1495           17331 :                                 break;
    1496                 :                         case SEEK_SET:
    1497           51948 :                                 if (offset > stream->position &&
    1498                 :                                                 offset < stream->position + stream->writepos - stream->readpos) {
    1499             811 :                                         stream->readpos += offset - stream->position;
    1500             811 :                                         stream->position = offset;
    1501             811 :                                         stream->eof = 0;
    1502             811 :                                         return 0;
    1503                 :                                 }
    1504                 :                                 break;
    1505                 :                 }
    1506                 :         }
    1507                 : 
    1508                 : 
    1509           71472 :         if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) {
    1510                 :                 int ret;
    1511                 :                 
    1512           71470 :                 if (stream->writefilters.head) {
    1513            1652 :                         _php_stream_flush(stream, 0 TSRMLS_CC);
    1514                 :                 }
    1515                 :                 
    1516           71470 :                 switch(whence) {
    1517                 :                         case SEEK_CUR:
    1518           17339 :                                 offset = stream->position + offset;
    1519           17339 :                                 whence = SEEK_SET;
    1520                 :                                 break;
    1521                 :                 }
    1522           71470 :                 ret = stream->ops->seek(stream, offset, whence, &stream->position TSRMLS_CC);
    1523                 : 
    1524           71470 :                 if (((stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) || ret == 0) {
    1525           71470 :                         if (ret == 0) {
    1526           71263 :                                 stream->eof = 0;
    1527                 :                         }
    1528                 : 
    1529                 :                         /* invalidate the buffer contents */
    1530           71470 :                         stream->readpos = stream->writepos = 0;
    1531                 : 
    1532           71470 :                         return ret;
    1533                 :                 }
    1534                 :                 /* else the stream has decided that it can't support seeking after all;
    1535                 :                  * fall through to attempt emulation */
    1536                 :         }
    1537                 : 
    1538                 :         /* emulate forward moving seeks with reads */
    1539               2 :         if (whence == SEEK_CUR && offset > 0) {
    1540                 :                 char tmp[1024];
    1541               0 :                 while(offset >= sizeof(tmp)) {
    1542               0 :                         if (php_stream_read(stream, tmp, sizeof(tmp)) == 0) {
    1543               0 :                                 return -1;
    1544                 :                         }
    1545               0 :                         offset -= sizeof(tmp);
    1546                 :                 }
    1547               0 :                 if (offset && (php_stream_read(stream, tmp, offset) == 0)) {
    1548               0 :                         return -1;
    1549                 :                 }
    1550               0 :                 stream->eof = 0;
    1551               0 :                 return 0;
    1552                 :         }
    1553                 : 
    1554               2 :         php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream does not support seeking");
    1555                 : 
    1556               2 :         return -1;
    1557                 : }
    1558                 : 
    1559                 : PHPAPI int _php_stream_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
    1560           97537 : {
    1561           97537 :         int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL;
    1562                 : 
    1563           97537 :         if (stream->ops->set_option) {
    1564           96848 :                 ret = stream->ops->set_option(stream, option, value, ptrparam TSRMLS_CC);
    1565                 :         }
    1566                 : 
    1567           97537 :         if (ret == PHP_STREAM_OPTION_RETURN_NOTIMPL) {
    1568           18981 :                 switch(option) {
    1569                 :                         case PHP_STREAM_OPTION_SET_CHUNK_SIZE:
    1570               0 :                                 ret = stream->chunk_size;
    1571               0 :                                 stream->chunk_size = value;
    1572               0 :                                 return ret;
    1573                 : 
    1574                 :                         case PHP_STREAM_OPTION_READ_BUFFER:
    1575                 :                                 /* try to match the buffer mode as best we can */
    1576               0 :                                 if (value == PHP_STREAM_BUFFER_NONE) {
    1577               0 :                                         stream->flags |= PHP_STREAM_FLAG_NO_BUFFER;
    1578                 :                                 } else {
    1579               0 :                                         stream->flags ^= PHP_STREAM_FLAG_NO_BUFFER;
    1580                 :                                 }
    1581               0 :                                 ret = PHP_STREAM_OPTION_RETURN_OK;
    1582                 :                                 break;
    1583                 :                                 
    1584                 :                         default:
    1585                 :                                 ;
    1586                 :                 }
    1587                 :         }
    1588                 : 
    1589           97537 :         return ret;
    1590                 : }
    1591                 : 
    1592                 : PHPAPI int _php_stream_truncate_set_size(php_stream *stream, size_t newsize TSRMLS_DC)
    1593             398 : {
    1594             398 :         return php_stream_set_option(stream, PHP_STREAM_OPTION_TRUNCATE_API, PHP_STREAM_TRUNCATE_SET_SIZE, &newsize);
    1595                 : }
    1596                 : 
    1597                 : PHPAPI size_t _php_stream_passthru(php_stream * stream STREAMS_DC TSRMLS_DC)
    1598             528 : {
    1599             528 :         size_t count = 0;
    1600                 : 
    1601             528 :         if (php_stream_mmap_possible(stream)) {
    1602                 :                 /* mmap_possible == non-filtered stream == binary stream */
    1603                 :                 char *p;
    1604                 :                 size_t mapped;
    1605                 : 
    1606             458 :                 p = php_stream_mmap_range(stream, php_stream_tell(stream), PHP_STREAM_MMAP_ALL, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
    1607                 : 
    1608             458 :                 if (p) {
    1609             356 :                         PHPWRITE(p, mapped);
    1610                 : 
    1611             356 :                         php_stream_mmap_unmap_ex(stream, mapped);
    1612                 : 
    1613             356 :                         return mapped;
    1614                 :                 }
    1615                 :         }
    1616                 : 
    1617             172 :         if (stream->readbuf_type == IS_UNICODE) {
    1618                 :                 UChar inbuf_start[8192];
    1619              14 :                 UConverter *conv = ZEND_U_CONVERTER(UG(output_encoding_conv));
    1620              14 :                 int outbuflen = UCNV_GET_MAX_BYTES_FOR_STRING(8192, ucnv_getMaxCharSize(conv));
    1621              14 :                 char *outbuf_start = emalloc(outbuflen + 1);
    1622                 :                 int b;
    1623                 : 
    1624              14 :                 ucnv_resetFromUnicode(conv);
    1625                 : 
    1626              42 :                 while ((b = php_stream_read_unicode(stream, inbuf_start, 8192)) > 0) {
    1627              14 :                         char *outbuf = outbuf_start;
    1628              14 :                         const UChar *inbuf = inbuf_start;
    1629              14 :                         UErrorCode status = U_ZERO_ERROR;
    1630                 :                         int len;
    1631                 : 
    1632              14 :                         ucnv_fromUnicode(conv, &outbuf, outbuf + outbuflen, &inbuf, inbuf + b, NULL, TRUE, &status);
    1633              14 :                         len = u_countChar32(inbuf_start, inbuf - inbuf_start);
    1634              14 :                         if (U_FAILURE(status)) {
    1635                 :                                 /* Memory overflow isn't a problem becuase MAX_BYTES_FOR_STRING was allocated,
    1636                 :                                    anything else is a more serious problem */
    1637               0 :                                 zend_raise_conversion_error_ex("Unable to convert Unicode character using output_encoding, at least one character was lost",
    1638                 :                                                                                            conv, ZEND_FROM_UNICODE, len TSRMLS_CC);
    1639                 :                         }
    1640              14 :                         if (outbuf > outbuf_start) {
    1641              14 :                                 PHPWRITE(outbuf_start, outbuf - outbuf_start);
    1642              14 :                                 count += len;
    1643                 :                         }
    1644                 :                 }
    1645              14 :                 efree(outbuf_start);
    1646                 :         } else {
    1647                 :                 char buf[8192];
    1648                 :                 int b;
    1649                 : 
    1650             389 :                 while ((b = php_stream_read(stream, buf, sizeof(buf))) > 0) {
    1651              73 :                         PHPWRITE(buf, b);
    1652              73 :                         count += b;
    1653                 :                 }
    1654                 :         }
    1655                 : 
    1656             172 :         return count;
    1657                 : }
    1658                 : 
    1659                 : 
    1660                 : PHPAPI size_t _php_stream_copy_to_mem_ex(php_stream *src, zend_uchar rettype, void **buf, size_t maxlen, size_t maxchars, int persistent STREAMS_DC TSRMLS_DC)
    1661            2565 : {
    1662            2565 :         size_t ret = 0;
    1663                 :         zstr ptr;
    1664            2565 :         size_t len = 0, max_len;
    1665            2565 :         int step = CHUNK_SIZE;
    1666            2565 :         int min_room = CHUNK_SIZE / 4;
    1667                 :         php_stream_statbuf ssbuf;
    1668                 : 
    1669            2565 :         if (rettype != src->readbuf_type) {
    1670                 :                 /* UTODO: Introduce sloppy buffer conversion */
    1671               0 :                 return 0;
    1672                 :         }
    1673                 : 
    1674            2565 :         if (maxlen == 0) { 
    1675               9 :                 return 0;
    1676                 :         }
    1677                 : 
    1678            2556 :         if (maxlen == PHP_STREAM_COPY_ALL) {
    1679            2483 :                 maxlen = 0;
    1680                 :         }
    1681                 : 
    1682            2556 :         if (maxlen > 0) {
    1683              73 :                 if (rettype == IS_UNICODE) {
    1684               0 :                         ptr.u = *buf = pemalloc_rel_orig(UBYTES(maxlen + 1), persistent);
    1685               0 :                         while ((len < maxlen) && !php_stream_eof(src)) {
    1686                 :                                 int ulen;
    1687                 : 
    1688               0 :                                 ret = php_stream_read_unicode_ex(src, ptr.u, maxlen - len, maxchars);
    1689               0 :                                 ulen = u_countChar32(ptr.u, ret);
    1690               0 :                                 len += ret;
    1691               0 :                                 ptr.u += ret;
    1692               0 :                                 maxchars -= ret;
    1693                 :                         }
    1694               0 :                         *(ptr.u) = 0;
    1695               0 :                         return len;
    1696                 :                 } else {
    1697              73 :                         ptr.s = *buf = pemalloc_rel_orig(maxlen + 1, persistent);
    1698             218 :                         while ((len < maxlen) && !php_stream_eof(src)) {
    1699              72 :                                 ret = php_stream_read(src, ptr.s, maxlen - len);
    1700              72 :                                 len += ret;
    1701              72 :                                 ptr.s += ret;
    1702                 :                         }
    1703              73 :                         *(ptr.s) = 0;
    1704              73 :                         return len;
    1705                 :                 }
    1706                 :         }
    1707                 : 
    1708                 :         /* avoid many reallocs by allocating a good sized chunk to begin with, if
    1709                 :          * we can.  Note that the stream may be filtered, in which case the stat
    1710                 :          * result may be inaccurate, as the filter may inflate or deflate the
    1711                 :          * number of bytes that we can read.  In order to avoid an upsize followed
    1712                 :          * by a downsize of the buffer, overestimate by the step size (which is
    1713                 :          * 2K).  */
    1714            4437 :         if (php_stream_stat(src, &ssbuf) == 0 && ssbuf.sb.st_size > 0) {
    1715            1954 :                 max_len = ssbuf.sb.st_size + step;
    1716                 :         } else {
    1717             529 :                 max_len = step;
    1718                 :         }
    1719                 : 
    1720            2483 :         if (rettype == IS_UNICODE) {
    1721             129 :                 ptr.u = *buf = pemalloc_rel_orig(UBYTES(max_len + 1), persistent);
    1722                 : 
    1723             383 :                 while((ret = php_stream_read_unicode_ex(src, ptr.u, max_len - len, maxchars)))  {
    1724             125 :                         int ulen = u_countChar32(ptr.u, ret);
    1725                 : 
    1726             125 :                         len += ret;
    1727             125 :                         if (len + min_room >= max_len) {
    1728               0 :                                 *buf = perealloc_rel_orig(*buf, UBYTES(max_len + step), persistent);
    1729               0 :                                 max_len += step;
    1730               0 :                                 ptr.u = ((UChar*)(*buf)) + len;
    1731                 :                         } else {
    1732             125 :                                 ptr.u += ret;
    1733                 :                         }
    1734             125 :                         maxchars -= ulen;
    1735                 :                 }
    1736                 :         } else {
    1737            2354 :                 ptr.s = *buf = pemalloc_rel_orig(max_len + 1, persistent);
    1738                 : 
    1739           15525 :                 while((ret = php_stream_read(src, ptr.s, max_len - len)))       {
    1740           10817 :                         len += ret;
    1741           10817 :                         if (len + min_room >= max_len) {
    1742              18 :                                 *buf = perealloc_rel_orig(*buf, max_len + step, persistent);
    1743              18 :                                 max_len += step;
    1744              18 :                                 ptr.s = ((char*)(*buf)) + len;
    1745                 :                         } else {
    1746           10799 :                                 ptr.s += ret;
    1747                 :                         }
    1748                 :                 }
    1749                 :         }
    1750                 : 
    1751            2483 :         if (len) {
    1752            2081 :                 if (rettype == IS_UNICODE) {
    1753             125 :                         if ((max_len - len) > (2 * step)) {
    1754               0 :                                 *buf = perealloc_rel_orig(*buf, UBYTES(len + 1), persistent);
    1755                 :                         }
    1756             125 :                         ((UChar*)(*buf))[len] = 0;
    1757                 :                 } else {
    1758            1956 :                         if ((max_len - len) > (2 * step)) {
    1759               1 :                                 *buf = perealloc_rel_orig(*buf, len + 1, persistent);
    1760                 :                         }
    1761            1956 :                         ((char*)(*buf))[len] = 0;
    1762                 :                 }
    1763                 :         } else {
    1764             402 :                 pefree(*buf, persistent);
    1765             402 :                 *buf = NULL;
    1766                 :         }
    1767            2483 :         return len;
    1768                 : }
    1769                 : 
    1770                 : zend_always_inline
    1771                 : static size_t _php_stream_copy_to_stream_common(php_stream *src, php_stream *dest, size_t maxlen, size_t maxchars, size_t *len, int utype STREAMS_DC TSRMLS_DC)
    1772           26734 : {
    1773           26734 :         size_t haveread = 0;
    1774                 :         php_stream_statbuf ssbuf;
    1775                 :         size_t dummy;
    1776                 : 
    1777           26734 :         if (!len) {
    1778            7221 :                 len = &dummy;
    1779                 :         }
    1780                 : 
    1781           26734 :         if (maxlen == 0 || (utype == IS_UNICODE && maxchars == 0)) {
    1782            4125 :                 *len = 0;
    1783            4125 :                 return SUCCESS;
    1784                 :         }
    1785                 : 
    1786           22609 :         if (maxlen == PHP_STREAM_COPY_ALL) {
    1787           12353 :                 maxlen = 0;
    1788                 :         }
    1789                 : 
    1790           22609 :         if (php_stream_stat(src, &ssbuf) == 0) {
    1791           22607 :                 if (ssbuf.sb.st_size == 0
    1792                 : #ifdef S_ISREG
    1793                 :                         && S_ISREG(ssbuf.sb.st_mode)
    1794                 : #endif
    1795                 :                 ) {
    1796            4122 :                         *len = 0;
    1797            4122 :                         return SUCCESS;
    1798                 :                 }
    1799                 :         }
    1800                 : 
    1801           18487 :         if (utype == IS_STRING && php_stream_mmap_possible(src)) {
    1802                 :                 char *p;
    1803                 :                 size_t mapped;
    1804                 : 
    1805           18463 :                 p = php_stream_mmap_range(src, php_stream_tell(src), maxlen, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
    1806                 : 
    1807           18463 :                 if (p) {
    1808           13920 :                         mapped = php_stream_write(dest, p, mapped);
    1809                 : 
    1810           13920 :                         php_stream_mmap_unmap_ex(src, mapped);
    1811                 : 
    1812           13920 :                         *len = mapped;
    1813                 :                         
    1814                 :                         /* we've got at least 1 byte to read. 
    1815                 :                          * less than 1 is an error */
    1816                 : 
    1817           13920 :                         if (mapped > 0) {
    1818           13920 :                                 return SUCCESS;
    1819                 :                         }
    1820               0 :                         return FAILURE;
    1821                 :                 }
    1822                 :         }
    1823                 : 
    1824                 :         while(1) {
    1825                 :                 zstr buf;
    1826                 :                 union { char s; UChar u; } _buf[CHUNK_SIZE];
    1827            4599 :                 size_t readchunk = sizeof(_buf) / sizeof(_buf[0]);
    1828                 :                 size_t didread;
    1829                 : 
    1830            4599 :                 buf.v = _buf;
    1831                 : 
    1832            4599 :                 if (maxlen && (maxlen - haveread) < readchunk) {
    1833            4558 :                         readchunk = maxlen - haveread;
    1834                 :                 }
    1835                 : 
    1836            4599 :                 didread = php_stream_u_read_ex(src, utype, buf, readchunk, maxchars);
    1837                 : 
    1838            4599 :                 if (didread) {
    1839                 :                         /* extra paranoid */
    1840                 :                         size_t didwrite, towrite;
    1841                 :                         zstr writeptr;
    1842                 : 
    1843            4589 :                         if (utype == IS_UNICODE && maxchars > 0) {
    1844                 :                                 /* Determine number of chars in this buf */
    1845               0 :                                 maxchars -= u_countChar32(buf.u, didread);
    1846                 :                         }
    1847                 : 
    1848            4589 :                         towrite = didread;
    1849            4589 :                         writeptr = buf;
    1850            4589 :                         haveread += didread;
    1851                 : 
    1852           13767 :                         while(towrite) {
    1853            4589 :                                 didwrite = php_stream_u_write(dest, utype, writeptr, towrite);
    1854            4589 :                                 if (didwrite == 0) {
    1855               0 :                                         *len = haveread - (didread - towrite);
    1856               0 :                                         return FAILURE;
    1857                 :                                 }
    1858                 : 
    1859            4589 :                                 towrite -= didwrite;
    1860            4589 :                                 if (utype == IS_UNICODE) {
    1861               0 :                                         writeptr.u += didwrite;
    1862                 :                                 } else {
    1863            4589 :                                         writeptr.s += didwrite;
    1864                 :                                 }
    1865                 :                         }
    1866                 :                 } else {
    1867              10 :                         break;
    1868                 :                 }
    1869                 : 
    1870            4589 :                 if (maxlen - haveread == 0 || (utype == IS_UNICODE && maxchars == 0)) {
    1871                 :                         break;
    1872                 :                 }
    1873              32 :         }
    1874                 : 
    1875            4567 :         *len = haveread;
    1876                 : 
    1877                 :         /* we've got at least 1 byte to read. 
    1878                 :          * less than 1 is an error */
    1879                 : 
    1880            4567 :         if (haveread > 0) {
    1881            4565 :                 return SUCCESS;
    1882                 :         }
    1883               2 :         return FAILURE;
    1884                 : }
    1885                 : 
    1886                 : /* Designed for copying UChars (taking into account both maxlen and maxchars) */
    1887                 : PHPAPI size_t _php_stream_ucopy_to_stream_ex(php_stream *src, php_stream *dest, size_t maxlen, size_t maxchars, size_t *len STREAMS_DC TSRMLS_DC)
    1888               0 : {
    1889               0 :         return _php_stream_copy_to_stream_common(src, dest, maxlen, maxchars, len, IS_UNICODE STREAMS_REL_CC TSRMLS_CC);
    1890                 : }
    1891                 : 
    1892                 : /* see _php_stream_copy_to_stream() */
    1893                 : ZEND_ATTRIBUTE_DEPRECATED
    1894                 : PHPAPI size_t _php_stream_ucopy_to_stream(php_stream *src, php_stream *dest, size_t maxlen, size_t maxchars STREAMS_DC TSRMLS_DC)
    1895               0 : {
    1896                 :         size_t len;
    1897               0 :         int ret = _php_stream_ucopy_to_stream_ex(src, dest, maxlen, maxchars, &len STREAMS_REL_CC TSRMLS_CC);
    1898               0 :         if (ret == SUCCESS && maxlen != 0 && maxchars != 0) {
    1899               0 :                 return 1;
    1900                 :         }
    1901               0 :         return len;
    1902                 : }
    1903                 : 
    1904                 : /* Optimized for copying octets from source stream */
    1905                 : /* Returns SUCCESS/FAILURE and sets *len to the number of bytes moved */
    1906                 : PHPAPI size_t _php_stream_copy_to_stream_ex(php_stream *src, php_stream *dest, size_t maxlen, size_t *len STREAMS_DC TSRMLS_DC)
    1907           26734 : {
    1908           26734 :         return _php_stream_copy_to_stream_common(src, dest, maxlen, 0, len, IS_STRING STREAMS_REL_CC TSRMLS_CC);
    1909                 : }
    1910                 : 
    1911                 : /* Returns the number of bytes moved.
    1912                 :  * Returns 1 when source len is 0. 
    1913                 :  * Deprecated in favor of php_stream_copy_to_stream_ex() */
    1914                 : ZEND_ATTRIBUTE_DEPRECATED
    1915                 : PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size_t maxlen STREAMS_DC TSRMLS_DC)
    1916              24 : {
    1917                 :         size_t len;
    1918              24 :         int ret = _php_stream_copy_to_stream_ex(src, dest, maxlen, &len STREAMS_REL_CC TSRMLS_CC);
    1919              24 :         if (ret == SUCCESS && len == 0 && maxlen != 0) {
    1920               0 :                 return 1;
    1921                 :         }
    1922              24 :         return len;
    1923                 : }
    1924                 : /* }}} */
    1925                 : 
    1926                 : /* {{{ wrapper init and registration */
    1927                 : 
    1928                 : static void stream_resource_regular_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
    1929          190358 : {
    1930          190358 :         php_stream *stream = (php_stream*)rsrc->ptr;
    1931                 :         /* set the return value for pclose */
    1932          190358 :         FG(pclose_ret) = php_stream_free(stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
    1933          190357 : }
    1934                 : 
    1935                 : static void stream_resource_persistent_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
    1936             987 : {
    1937             987 :         php_stream *stream = (php_stream*)rsrc->ptr;
    1938             987 :         FG(pclose_ret) = php_stream_free(stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
    1939             987 : }
    1940                 : 
    1941                 : void php_shutdown_stream_hashes(TSRMLS_D)
    1942           17025 : {
    1943           17025 :         if (FG(stream_wrappers)) {
    1944              26 :                 zend_hash_destroy(FG(stream_wrappers));
    1945              26 :                 efree(FG(stream_wrappers));
    1946              26 :                 FG(stream_wrappers) = NULL;
    1947                 :         }
    1948                 : 
    1949           17025 :         if (FG(stream_filters)) {
    1950               8 :                 zend_hash_destroy(FG(stream_filters));
    1951               8 :                 efree(FG(stream_filters));
    1952               8 :                 FG(stream_filters) = NULL;
    1953                 :         }
    1954           17025 : }
    1955                 : 
    1956                 : int php_init_stream_wrappers(int module_number TSRMLS_DC)
    1957           17007 : {
    1958           17007 :         le_stream = zend_register_list_destructors_ex(stream_resource_regular_dtor, NULL, "stream", module_number);
    1959           17007 :         le_pstream = zend_register_list_destructors_ex(NULL, stream_resource_persistent_dtor, "persistent stream", module_number);
    1960                 : 
    1961                 :         /* Filters are cleaned up by the streams they're attached to */
    1962           17007 :         le_stream_filter = zend_register_list_destructors_ex(NULL, NULL, "stream filter", module_number);
    1963                 : 
    1964           17007 :         return (
    1965                 :                         zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1) == SUCCESS
    1966                 :                         && 
    1967                 :                         zend_hash_init(php_get_stream_filters_hash_global(), 0, NULL, NULL, 1) == SUCCESS
    1968                 :                         &&
    1969                 :                         zend_hash_init(php_stream_xport_get_hash(), 0, NULL, NULL, 1) == SUCCESS
    1970                 :                         &&
    1971                 :                         php_stream_xport_register("tcp", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
    1972                 :                         &&
    1973                 :                         php_stream_xport_register("udp", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
    1974                 : #if defined(AF_UNIX) && !(defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE))
    1975                 :                         &&
    1976                 :                         php_stream_xport_register("unix", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
    1977                 :                         &&
    1978                 :                         php_stream_xport_register("udg", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
    1979                 : #endif
    1980                 :                 ) ? SUCCESS : FAILURE;
    1981                 : }
    1982                 : 
    1983                 : int php_shutdown_stream_wrappers(int module_number TSRMLS_DC)
    1984           17039 : {
    1985           17039 :         zend_hash_destroy(&url_stream_wrappers_hash);
    1986           17039 :         zend_hash_destroy(php_get_stream_filters_hash_global());
    1987           17039 :         zend_hash_destroy(php_stream_xport_get_hash());
    1988           17039 :         return SUCCESS;
    1989                 : }
    1990                 : 
    1991                 : /* Validate protocol scheme names during registration
    1992                 :  * Must conform to /^[a-zA-Z0-9+.-]+$/
    1993                 :  */
    1994                 : static inline int php_stream_wrapper_scheme_validate(char *protocol, int protocol_len)
    1995          306159 : {
    1996                 :         int i;
    1997                 : 
    1998         1870988 :         for(i = 0; i < protocol_len; i++) {
    1999         1564829 :                 if (!isalnum((int)protocol[i]) &&
    2000                 :                         protocol[i] != '+' &&
    2001                 :                         protocol[i] != '-' &&
    2002                 :                         protocol[i] != '.') {
    2003               0 :                         return FAILURE;
    2004                 :                 }
    2005                 :         }
    2006                 : 
    2007          306159 :         return SUCCESS;
    2008                 : }
    2009                 : 
    2010                 : /* API for registering GLOBAL wrappers */
    2011                 : PHPAPI int php_register_url_stream_wrapper(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC)
    2012          306126 : {
    2013          306126 :         int protocol_len = strlen(protocol);
    2014                 : 
    2015          306126 :         if (php_stream_wrapper_scheme_validate(protocol, protocol_len) == FAILURE) {
    2016               0 :                 return FAILURE;
    2017                 :         }
    2018                 : 
    2019          306126 :         return zend_hash_add(&url_stream_wrappers_hash, protocol, protocol_len + 1, &wrapper, sizeof(wrapper), NULL);
    2020                 : }
    2021                 : 
    2022                 : PHPAPI int php_unregister_url_stream_wrapper(char *protocol TSRMLS_DC)
    2023          323485 : {
    2024          323485 :         return zend_hash_del(&url_stream_wrappers_hash, protocol, strlen(protocol) + 1);
    2025                 : }
    2026                 : 
    2027                 : static void clone_wrapper_hash(TSRMLS_D)
    2028              26 : {
    2029                 :         php_stream_wrapper *tmp;
    2030                 : 
    2031              26 :         ALLOC_HASHTABLE(FG(stream_wrappers));
    2032              26 :         zend_hash_init(FG(stream_wrappers), zend_hash_num_elements(&url_stream_wrappers_hash), NULL, NULL, 1);
    2033              26 :         zend_hash_copy(FG(stream_wrappers), &url_stream_wrappers_hash, NULL, &tmp, sizeof(tmp));
    2034              26 : }
    2035                 : 
    2036                 : /* API for registering VOLATILE wrappers */
    2037                 : PHPAPI int php_register_url_stream_wrapper_volatile(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC)
    2038              33 : {
    2039              33 :         int protocol_len = strlen(protocol);
    2040                 : 
    2041              33 :         if (php_stream_wrapper_scheme_validate(protocol, protocol_len) == FAILURE) {
    2042               0 :                 return FAILURE;
    2043                 :         }
    2044                 : 
    2045              33 :         if (!FG(stream_wrappers)) {
    2046              25 :                 clone_wrapper_hash(TSRMLS_C);
    2047                 :         }
    2048                 : 
    2049              33 :         return zend_hash_add(FG(stream_wrappers), protocol, protocol_len + 1, &wrapper, sizeof(wrapper), NULL);
    2050                 : }
    2051                 : 
    2052                 : PHPAPI int php_unregister_url_stream_wrapper_volatile(char *protocol TSRMLS_DC)
    2053               2 : {
    2054               2 :         if (!FG(stream_wrappers)) {
    2055               1 :                 clone_wrapper_hash(TSRMLS_C);
    2056                 :         }
    2057                 : 
    2058               2 :         return zend_hash_del(FG(stream_wrappers), protocol, strlen(protocol) + 1);
    2059                 : }
    2060                 : /* }}} */
    2061                 : 
    2062                 : /* {{{ php_stream_locate_url_wrapper */
    2063                 : PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, char **path_for_open, int options TSRMLS_DC)
    2064          469928 : {
    2065          469928 :         HashTable *wrapper_hash = (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash);
    2066          469928 :         php_stream_wrapper **wrapperpp = NULL;
    2067          469928 :         const char *p, *protocol = NULL;
    2068          469928 :         int n = 0;
    2069                 : 
    2070          469928 :         if (path_for_open) {
    2071          300347 :                 *path_for_open = (char*)path;
    2072                 :         }
    2073                 : 
    2074          469928 :         if (options & IGNORE_URL) {
    2075            5675 :                 return (options & STREAM_LOCATE_WRAPPERS_ONLY) ? NULL : &php_plain_files_wrapper;
    2076                 :         }
    2077                 : 
    2078          666593 :         for (p = path; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) {
    2079          202340 :                 n++;
    2080                 :         }
    2081                 : 
    2082          517613 :         if ((*p == ':') && (n > 1) && (!strncmp("//", p+1, 2) || (n == 4 && !memcmp("data:", path, 5)))) {
    2083           53360 :                 protocol = path;
    2084          410893 :         } else if (n == 5 && strncasecmp(path, "zlib:", 5) == 0) {
    2085                 :                 /* BC with older php scripts and zlib wrapper */
    2086               0 :                 protocol = "compress.zlib";
    2087               0 :                 n = 13;
    2088               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Use of \"zlib:\" wrapper is deprecated; please use \"compress.zlib://\" instead");
    2089                 :         }
    2090                 : 
    2091          464253 :         if (protocol) {
    2092           53360 :                 char *tmp = estrndup(protocol, n);
    2093           53360 :                 if (FAILURE == zend_hash_find(wrapper_hash, (char*)tmp, n + 1, (void**)&wrapperpp)) {
    2094               4 :                         php_strtolower(tmp, n);
    2095               4 :                         if (FAILURE == zend_hash_find(wrapper_hash, (char*)tmp, n + 1, (void**)&wrapperpp)) {
    2096                 :                                 char wrapper_name[32];
    2097                 : 
    2098               2 :                                 if (n >= sizeof(wrapper_name)) {
    2099               0 :                                         n = sizeof(wrapper_name) - 1;
    2100                 :                                 }
    2101               2 :                                 PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n);
    2102                 : 
    2103               2 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to find the wrapper \"%s\" - did you forget to enable it when you configured PHP?", wrapper_name);
    2104                 : 
    2105               2 :                                 wrapperpp = NULL;
    2106               2 :                                 protocol = NULL;
    2107                 :                         }
    2108                 :                 }
    2109           53360 :                 efree(tmp);
    2110                 :         }
    2111                 :         /* TODO: curl based streams probably support file:// properly */
    2112          464253 :         if (!protocol || !strncasecmp(protocol, "file", n))   {
    2113                 :                 /* fall back on regular file access */
    2114          411052 :                 php_stream_wrapper *plain_files_wrapper = &php_plain_files_wrapper;
    2115                 : 
    2116          411052 :                 if (protocol) {
    2117             157 :                         int localhost = 0;
    2118                 : 
    2119             157 :                         if (!strncasecmp(path, "file://localhost/", 17)) {
    2120               0 :                                 localhost = 1;
    2121                 :                         }
    2122                 : 
    2123                 : #ifdef PHP_WIN32
    2124                 :                         if (localhost == 0 && path[n+3] != '\0' && path[n+3] != '/' && path[n+4] != ':')        {
    2125                 : #else
    2126             157 :                         if (localhost == 0 && path[n+3] != '\0' && path[n+3] != '/') {
    2127                 : #endif
    2128              18 :                                 if (options & REPORT_ERRORS) {
    2129              12 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "remote host file access not supported, %s", path);
    2130                 :                                 }
    2131              18 :                                 return NULL;
    2132                 :                         }
    2133                 : 
    2134             139 :                         if (path_for_open) {
    2135                 :                                 /* skip past protocol and :/, but handle windows correctly */
    2136              24 :                                 *path_for_open = (char*)path + n + 1;
    2137              24 :                                 if (localhost == 1) {
    2138               0 :                                         (*path_for_open) += 11;
    2139                 :                                 }
    2140              73 :                                 while (*(++*path_for_open)=='/');
    2141                 : #ifdef PHP_WIN32
    2142                 :                                 if (*(*path_for_open + 1) != ':')
    2143                 : #endif
    2144              24 :                                         (*path_for_open)--;
    2145                 :                         }
    2146                 :                 }
    2147                 : 
    2148          411034 :                 if (options & STREAM_LOCATE_WRAPPERS_ONLY) {
    2149             584 :                         return NULL;
    2150                 :                 }
    2151                 : 
    2152                 :                 /* The file:// wrapper may have been disabled/overridden */
    2153          410450 :                 if (FG(stream_wrappers)) {
    2154              12 :                         if (zend_hash_find(wrapper_hash, "file", sizeof("file"), (void**)&wrapperpp) == FAILURE) {
    2155               1 :                                 if (options & REPORT_ERRORS) {
    2156               1 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Plainfiles wrapper disabled");
    2157                 :                                 }
    2158               1 :                                 return NULL;
    2159                 :                         } else {
    2160                 :                                 /* Handles overridden plain files wrapper */
    2161              11 :                                 plain_files_wrapper = *wrapperpp;
    2162                 :                         }
    2163                 :                 }
    2164                 : 
    2165          410449 :                 if (!php_stream_allow_url_fopen("file", sizeof("file") - 1) ||
    2166                 :                         ((options & STREAM_OPEN_FOR_INCLUDE) && !php_stream_allow_url_include("file", sizeof("file") - 1)) ) {
    2167               0 :                         if (options & REPORT_ERRORS) {
    2168               0 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "file:// wrapper is disabled in the server configuration");
    2169                 :                         }
    2170               0 :                         return NULL;
    2171                 :                 }
    2172                 :                 
    2173          410449 :                 return plain_files_wrapper;
    2174                 :         }
    2175                 : 
    2176           53201 :         if (((options & STREAM_DISABLE_URL_PROTECTION) == 0) &&
    2177                 :             (!php_stream_allow_url_fopen(protocol, n) ||
    2178                 :                  ((options & STREAM_OPEN_FOR_INCLUDE) && !php_stream_allow_url_include(protocol, n)))) {
    2179              10 :                 if (options & REPORT_ERRORS) {
    2180                 :                         /* protocol[n] probably isn't '\0' */
    2181               7 :                         char *protocol_dup = estrndup(protocol, n);
    2182               7 :                         if (!php_stream_allow_url_fopen(protocol, n)) {
    2183               5 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s:// wrapper is disabled in the server configuration by allow_url_fopen=0", protocol_dup);
    2184                 :                         } else {
    2185               2 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s:// wrapper is disabled in the server configuration by allow_url_include=0", protocol_dup);
    2186                 :                         }
    2187               7 :                         efree(protocol_dup);
    2188                 :                 }
    2189              10 :                 return NULL;
    2190                 :         }
    2191                 : 
    2192           53191 :         return *wrapperpp;
    2193                 : }
    2194                 : /* }}} */
    2195                 : 
    2196                 : /* {{{ _php_stream_mkdir
    2197                 :  */
    2198                 : PHPAPI int _php_stream_mkdir(char *path, int mode, int options, php_stream_context *context TSRMLS_DC)
    2199            1675 : {
    2200            1675 :         php_stream_wrapper *wrapper = NULL;
    2201                 : 
    2202            1675 :         wrapper = php_stream_locate_url_wrapper(path, NULL, 0 TSRMLS_CC);
    2203            1675 :         if (!wrapper || !wrapper->wops || !wrapper->wops->stream_mkdir) {
    2204               1 :                 return 0;
    2205                 :         }
    2206                 : 
    2207            1674 :         return wrapper->wops->stream_mkdir(wrapper, path, mode, options, context TSRMLS_CC);
    2208                 : }
    2209                 : /* }}} */
    2210                 : 
    2211                 : /* {{{ _php_stream_rmdir
    2212                 :  */
    2213                 : PHPAPI int _php_stream_rmdir(char *path, int options, php_stream_context *context TSRMLS_DC)
    2214            1703 : {
    2215            1703 :         php_stream_wrapper *wrapper = NULL;
    2216                 : 
    2217            1703 :         wrapper = php_stream_locate_url_wrapper(path, NULL, 0 TSRMLS_CC);
    2218            1703 :         if (!wrapper || !wrapper->wops || !wrapper->wops->stream_rmdir) {
    2219               1 :                 return 0;
    2220                 :         }
    2221                 : 
    2222            1702 :         return wrapper->wops->stream_rmdir(wrapper, path, options, context TSRMLS_CC);
    2223                 : }
    2224                 : /* }}} */
    2225                 : 
    2226                 : /* {{{ _php_stream_stat_path */
    2227                 : PHPAPI int _php_stream_stat_path(char *path, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC)
    2228           87511 : {
    2229           87511 :         php_stream_wrapper *wrapper = NULL;
    2230           87511 :         char *path_to_open = path;
    2231                 :         int ret;
    2232                 : 
    2233                 :         /* Try to hit the cache first */
    2234           87511 :         if (flags & PHP_STREAM_URL_STAT_LINK) {
    2235            3299 :                 if (BG(CurrentLStatFile) && strcmp(path, BG(CurrentLStatFile)) == 0) {
    2236               8 :                         memcpy(ssb, &BG(lssb), sizeof(php_stream_statbuf));
    2237               8 :                         return 0;
    2238                 :                 }
    2239                 :         } else {
    2240           84212 :                 if (BG(CurrentStatFile) && strcmp(path, BG(CurrentStatFile)) == 0) {
    2241            4964 :                         memcpy(ssb, &BG(ssb), sizeof(php_stream_statbuf));
    2242            4964 :                         return 0;
    2243                 :                 }
    2244                 :         }
    2245                 : 
    2246           82539 :         wrapper = php_stream_locate_url_wrapper(path, &path_to_open, 0 TSRMLS_CC);
    2247           82539 :         if (wrapper && wrapper->wops->url_stat) {
    2248           82526 :                 ret = wrapper->wops->url_stat(wrapper, path_to_open, flags, ssb, context TSRMLS_CC);
    2249           82526 :                 if (ret == 0) {
    2250                 :                         /* Drop into cache */
    2251           81395 :                         if (flags & PHP_STREAM_URL_STAT_LINK) {
    2252            3266 :                                 if (BG(CurrentLStatFile)) {
    2253            3190 :                                         efree(BG(CurrentLStatFile));
    2254                 :                                 }
    2255            3266 :                                 BG(CurrentLStatFile) = estrdup(path);
    2256            3266 :                                 memcpy(&BG(lssb), ssb, sizeof(php_stream_statbuf));
    2257                 :                         } else {
    2258           78129 :                                 if (BG(CurrentStatFile)) {
    2259           59538 :                                         efree(BG(CurrentStatFile));
    2260                 :                                 }
    2261           78129 :                                 BG(CurrentStatFile) = estrdup(path);
    2262           78129 :                                 memcpy(&BG(ssb), ssb, sizeof(php_stream_statbuf));
    2263                 :                         }
    2264                 :                 }
    2265           82526 :                 return ret;
    2266                 :         }
    2267              13 :         return -1;
    2268                 : }
    2269                 : /* }}} */
    2270                 : 
    2271                 : /* {{{ php_stream_opendir */
    2272                 : PHPAPI php_stream *_php_stream_opendir(char *path, int options,
    2273                 :                 php_stream_context *context STREAMS_DC TSRMLS_DC)
    2274            3894 : {
    2275            3894 :         php_stream *stream = NULL;
    2276            3894 :         php_stream_wrapper *wrapper = NULL;
    2277                 :         char *path_to_open;
    2278                 : 
    2279            3894 :         if (!path || !*path) {
    2280              24 :                 return NULL;
    2281                 :         }
    2282                 : 
    2283            3870 :         path_to_open = path;
    2284                 : 
    2285            3870 :         wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options TSRMLS_CC);
    2286                 : 
    2287            7737 :         if (wrapper && wrapper->wops->dir_opener) {
    2288            3867 :                 stream = wrapper->wops->dir_opener(wrapper,
    2289                 :                                 path_to_open, "r", options ^ REPORT_ERRORS, NULL,
    2290                 :                                 context STREAMS_REL_CC TSRMLS_CC);
    2291                 : 
    2292            3867 :                 if (stream) {
    2293            3763 :                         stream->wrapper = wrapper;
    2294            3763 :                         stream->flags |= PHP_STREAM_FLAG_NO_BUFFER | PHP_STREAM_FLAG_IS_DIR;
    2295                 :                 }
    2296               3 :         } else if (wrapper) {
    2297               3 :                 php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS TSRMLS_CC, "not implemented");
    2298                 :         }
    2299            3870 :         if (stream == NULL && (options & REPORT_ERRORS)) {
    2300             107 :                 php_stream_display_wrapper_errors(wrapper, path, "failed to open dir" TSRMLS_CC);
    2301                 :         }
    2302            3870 :         php_stream_tidy_wrapper_error_log(wrapper TSRMLS_CC);
    2303                 : 
    2304            3870 :         return stream;
    2305                 : }
    2306                 : /* }}} */
    2307                 : 
    2308                 : PHPAPI php_stream *_php_stream_u_opendir(zend_uchar type, zstr path, int path_len, int options, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */
    2309             548 : {
    2310                 :         char *filename;
    2311                 :         int filename_len;
    2312                 :         php_stream *stream;
    2313                 : 
    2314             548 :         if (type == IS_STRING) {
    2315             443 :                 return php_stream_opendir(path.s, options, context);
    2316                 :         }
    2317                 : 
    2318                 :         /* type == IS_UNICODE */
    2319             105 :         if (FAILURE == php_stream_path_encode(NULL, &filename, &filename_len, path.u, path_len, options, context)) {
    2320               0 :                 return NULL;
    2321                 :         }
    2322                 : 
    2323             105 :         stream = php_stream_opendir(filename, options, context);
    2324             105 :         efree(filename);
    2325             105 :         return stream;
    2326                 : }
    2327                 : /* }}} */
    2328                 : 
    2329                 : /* {{{ _php_stream_readdir */
    2330                 : PHPAPI php_stream_dirent *_php_stream_readdir(php_stream *dirstream, php_stream_dirent *ent TSRMLS_DC)
    2331           68700 : {
    2332                 : 
    2333           68700 :         if (sizeof(php_stream_dirent) == php_stream_read(dirstream, (char*)ent, sizeof(php_stream_dirent))) {
    2334           65274 :                 return ent;
    2335                 :         }
    2336                 : 
    2337            3426 :         return NULL;
    2338                 : }
    2339                 : /* }}} */
    2340                 : 
    2341                 : /* {{{ php_stream_fix_encoding
    2342                 :  * Sets read/write encoding on a stream based on the fopen mode, context options, and INI setting */
    2343                 : PHPAPI void php_stream_fix_encoding(php_stream *stream, const char *mode, php_stream_context *context TSRMLS_DC)
    2344          276076 : {
    2345                 :         /* Output encoding on text mode streams defaults to utf8 unless specified in context parameter */
    2346          276076 :         if (stream && strchr(mode, 't')) {
    2347                 :                 /* Only apply implicit unicode.to. filter if the wrapper didn't do it for us */
    2348           34831 :                 if ((php_stream_filter_product(&stream->writefilters, IS_UNICODE) == IS_UNICODE) && 
    2349                 :                         (strchr(mode, 'w') || strchr(mode, 'a') || strchr(mode, '+'))) {
    2350            5960 :                         char *encoding = (context && context->output_encoding) ? context->output_encoding : UG(stream_encoding);
    2351                 : 
    2352                 :                         /* UTODO: (Maybe?) Allow overriding the default error handlers on a per-stream basis via context params */
    2353            5960 :                         php_stream_encoding_apply(stream, 1, encoding, UG(from_error_mode), UG(from_subst_char));
    2354                 :                 }
    2355                 : 
    2356                 :                 /* Only apply implicit unicode.from. filter if the wrapper didn't do it for us */
    2357           34831 :                 if ((stream->readbuf_type == IS_STRING) && (strchr(mode, 'r') || strchr(mode, '+'))) {
    2358           12711 :                         char *encoding = (context && context->input_encoding) ? context->input_encoding : UG(stream_encoding);
    2359                 : 
    2360                 :                         /* UTODO: (Maybe?) Allow overriding the default error handlers on a per-stream basis via context params */
    2361           12711 :                         php_stream_encoding_apply(stream, 0, encoding, UG(to_error_mode), NULL);
    2362                 :                 }
    2363                 :         }
    2364          276076 : }
    2365                 : /* }}} */
    2366                 : 
    2367                 : /* {{{ php_stream_open_wrapper_ex */
    2368                 : PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int options,
    2369                 :                 char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
    2370          121556 : {
    2371          121556 :         php_stream *stream = NULL;
    2372          121556 :         php_stream_wrapper *wrapper = NULL;
    2373                 :         char *path_to_open;
    2374          121556 :         int persistent = options & STREAM_OPEN_PERSISTENT;
    2375          121556 :         char *resolved_path = NULL;
    2376          121556 :         char *copy_of_path = NULL;
    2377                 :         char implicit_mode[16];
    2378          121556 :         int modelen = strlen(mode);
    2379                 :         
    2380          121556 :         if (opened_path) {
    2381           11540 :                 *opened_path = NULL;
    2382                 :         }
    2383                 : 
    2384          121556 :         if (!path || !*path) {
    2385              67 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filename cannot be empty");
    2386              67 :                 return NULL;
    2387                 :         }
    2388                 : 
    2389          121489 :         if (options & USE_PATH) {
    2390            7786 :                 resolved_path = zend_resolve_path(path, strlen(path) TSRMLS_CC);
    2391            7786 :                 if (resolved_path) {
    2392            7424 :                         path = resolved_path;
    2393                 :                         /* we've found this file, don't re-check include_path or run realpath */
    2394            7424 :                         options |= STREAM_ASSUME_REALPATH;
    2395            7424 :                         options &= ~USE_PATH;
    2396                 :                 }
    2397                 :         }
    2398                 : 
    2399          121489 :         path_to_open = path;
    2400                 : 
    2401          121489 :         wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options TSRMLS_CC);
    2402          121489 :         if (options & STREAM_USE_URL && (!wrapper || !wrapper->is_url)) {
    2403               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "This function may only be used against URLs");
    2404               0 :                 if (resolved_path) {
    2405               0 :                         efree(resolved_path);
    2406                 :                 }
    2407               0 :                 return NULL;
    2408                 :         }
    2409                 : 
    2410          121489 :         strlcpy(implicit_mode, mode, sizeof(implicit_mode));
    2411          121489 :         if (context && context->default_mode && modelen < 15 && !strchr(mode, 't') && !strchr(mode, 'b')) {
    2412               0 :                 if (context->default_mode & PHP_FILE_BINARY) {
    2413               0 :                         implicit_mode[modelen++] = 'b';
    2414               0 :                 } else if (context->default_mode & PHP_FILE_TEXT) {
    2415               0 :                         implicit_mode[modelen++] = 't';
    2416                 :                 }
    2417               0 :                 implicit_mode[modelen] = '\0';
    2418                 :         }
    2419                 : 
    2420          121489 :         if (wrapper) {
    2421          121470 :                 if (!wrapper->wops->stream_opener) {
    2422               0 :                         php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS TSRMLS_CC,
    2423                 :                                         "wrapper does not support stream open");
    2424                 :                 } else {
    2425          121470 :                         stream = wrapper->wops->stream_opener(wrapper,
    2426                 :                                 path_to_open, implicit_mode, options ^ REPORT_ERRORS,
    2427                 :                                 opened_path, context STREAMS_REL_CC TSRMLS_CC);
    2428                 :                 }
    2429                 : 
    2430                 :                 /* if the caller asked for a persistent stream but the wrapper did not
    2431                 :                  * return one, force an error here */
    2432          121469 :                 if (stream && (options & STREAM_OPEN_PERSISTENT) && !stream->is_persistent) {
    2433               0 :                         php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS TSRMLS_CC,
    2434                 :                                         "wrapper does not support persistent streams");
    2435               0 :                         php_stream_close(stream);
    2436               0 :                         stream = NULL;
    2437                 :                 }
    2438                 :                 
    2439          121469 :                 if (stream) {
    2440          120648 :                         stream->wrapper = wrapper;
    2441          120648 :                         strlcpy(stream->mode, implicit_mode, sizeof(stream->mode));
    2442                 :                 }
    2443                 :         }
    2444                 : 
    2445          121488 :         if (stream) {
    2446          120648 :                 if (opened_path && !*opened_path && resolved_path) {
    2447               2 :                         *opened_path = resolved_path;
    2448               2 :                         resolved_path = NULL;
    2449                 :                 }
    2450          120648 :                 if (stream->orig_path) {
    2451               1 :                         pefree(stream->orig_path, persistent);
    2452                 :                 }
    2453          120648 :                 copy_of_path = pestrdup(path, persistent);
    2454          120648 :                 stream->orig_path = copy_of_path;
    2455                 : #if ZEND_DEBUG
    2456                 :                 stream->open_filename = __zend_orig_filename ? __zend_orig_filename : __zend_filename;
    2457                 :                 stream->open_lineno = __zend_orig_lineno ? __zend_orig_lineno : __zend_lineno;
    2458                 : #endif
    2459                 :         }
    2460                 : 
    2461          121488 :         if (stream != NULL && (options & STREAM_MUST_SEEK)) {
    2462                 :                 php_stream *newstream;
    2463                 : 
    2464            8934 :                 switch(php_stream_make_seekable_rel(stream, &newstream,
    2465                 :                                         (options & STREAM_WILL_CAST)
    2466                 :                                                 ? PHP_STREAM_PREFER_STDIO : PHP_STREAM_NO_PREFERENCE)) {
    2467                 :                         case PHP_STREAM_UNCHANGED:
    2468            8934 :                                 if (resolved_path) {
    2469              42 :                                         efree(resolved_path);
    2470                 :                                 }
    2471            8934 :                                 return stream;
    2472                 :                         case PHP_STREAM_RELEASED:
    2473               0 :                                 if (newstream->orig_path) {
    2474               0 :                                         pefree(newstream->orig_path, persistent);
    2475                 :                                 }
    2476               0 :                                 newstream->orig_path = pestrdup(path, persistent);
    2477               0 :                                 if (resolved_path) {
    2478               0 :                                         efree(resolved_path);
    2479                 :                                 }
    2480               0 :                                 return newstream;
    2481                 :                         default:
    2482               0 :                                 php_stream_close(stream);
    2483               0 :                                 stream = NULL;
    2484               0 :                                 if (options & REPORT_ERRORS) {
    2485               0 :                                         char *tmp = estrdup(path);
    2486               0 :                                         php_strip_url_passwd(tmp);
    2487               0 :                                         php_error_docref1(NULL TSRMLS_CC, tmp, E_WARNING, "could not make seekable - %s",
    2488                 :                                                         tmp);
    2489               0 :                                         efree(tmp);
    2490                 : 
    2491               0 :                                         options ^= REPORT_ERRORS;
    2492                 :                                 }
    2493                 :                 }
    2494                 :         }
    2495                 : 
    2496          112554 :         if (stream && stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0 && strchr(mode, 'a') && stream->position == 0) {
    2497            1367 :                 off_t newpos = 0;
    2498                 : 
    2499                 :                 /* if opened for append, we need to revise our idea of the initial file position */
    2500            1367 :                 if (0 == stream->ops->seek(stream, 0, SEEK_CUR, &newpos TSRMLS_CC)) {
    2501            1367 :                         stream->position = newpos;
    2502                 :                 }
    2503                 :         }
    2504                 : 
    2505                 : 
    2506          112554 :         php_stream_fix_encoding(stream, implicit_mode, context TSRMLS_CC);
    2507                 : 
    2508          112554 :         if (stream == NULL && (options & REPORT_ERRORS)) {
    2509             438 :                 php_stream_display_wrapper_errors(wrapper, path, "failed to open stream" TSRMLS_CC);
    2510             438 :                 if (opened_path && *opened_path) {
    2511               0 :                         efree(*opened_path);
    2512               0 :                         *opened_path = NULL;
    2513                 :                 }
    2514                 :         }
    2515          112554 :         php_stream_tidy_wrapper_error_log(wrapper TSRMLS_CC);
    2516                 : #if ZEND_DEBUG
    2517                 :         if (stream == NULL && copy_of_path != NULL) {
    2518                 :                 pefree(copy_of_path, persistent);
    2519                 :         }
    2520                 : #endif
    2521                 : 
    2522          112554 :         if (resolved_path) {
    2523            7380 :                 efree(resolved_path);
    2524                 :         }
    2525          112554 :         return stream;
    2526                 : }
    2527                 : /* }}} */
    2528                 : 
    2529                 : /* {{{ _php_stream_u_open_wrapper */
    2530                 : PHPAPI php_stream *_php_stream_u_open_wrapper(zend_uchar type, zstr path, int path_len,
    2531                 :                         char *mode, int options, zstr *opened_path, int *opened_path_len,
    2532                 :                         php_stream_context *context STREAMS_DC TSRMLS_DC)
    2533              47 : {
    2534                 :         php_stream *stream;
    2535              47 :         char *filename = NULL;
    2536                 :         int filename_len;
    2537                 : 
    2538              47 :         if (opened_path) {
    2539               0 :                 opened_path->v = NULL;
    2540                 :         }
    2541              47 :         if (opened_path_len) {
    2542               0 :                 *opened_path_len = 0;
    2543                 :         }
    2544                 : 
    2545              47 :         if (type == IS_STRING) {
    2546              15 :                 stream = php_stream_open_wrapper_ex(path.s, mode, options, (char**)opened_path, context);
    2547                 : 
    2548              15 :                 if (opened_path_len && opened_path && opened_path->s) {
    2549               0 :                         *opened_path_len = strlen(opened_path->s);
    2550                 :                 }
    2551                 : 
    2552              15 :                 return stream;
    2553                 :         }
    2554                 : 
    2555                 :         /* type == IS_UNICODE */
    2556              32 :         if (FAILURE == php_stream_path_encode(NULL, &filename, &filename_len, path.u, path_len, options, context)) {
    2557               0 :                 return NULL;
    2558                 :         }
    2559                 : 
    2560              32 :         stream = php_stream_open_wrapper_ex(filename, mode, options, (char**)opened_path, context);
    2561              32 :         efree(filename);
    2562                 : 
    2563              32 :         if (opened_path && opened_path->s) {
    2564                 :                 UChar *upath;
    2565                 :                 int upath_len;
    2566                 : 
    2567               0 :                 if (SUCCESS == php_stream_path_decode(NULL, &upath, &upath_len, opened_path->s, strlen(opened_path->s), options, context)) {
    2568               0 :                         efree(opened_path->s);
    2569               0 :                         opened_path->u = upath;
    2570               0 :                         if (opened_path_len) {
    2571               0 :                                 *opened_path_len = upath_len;
    2572                 :                         }
    2573                 :                 } else {
    2574                 :                         /* Shouldn't happen */
    2575               0 :                         efree(opened_path->s);
    2576               0 :                         opened_path->s = NULL;
    2577                 :                 }
    2578                 :         }
    2579                 : 
    2580              32 :         return stream;
    2581                 : }
    2582                 : /* }}} */
    2583                 : 
    2584                 : /* {{{ context API */
    2585                 : PHPAPI php_stream_context *php_stream_context_set(php_stream *stream, php_stream_context *context TSRMLS_DC)
    2586            1775 : {
    2587            1775 :         php_stream_context *oldcontext = stream->context;
    2588                 : 
    2589            1775 :         stream->context = context;
    2590                 : 
    2591            1775 :         if (context) {
    2592             109 :                 zend_list_addref(context->rsrc_id);
    2593                 :         }
    2594            1775 :         if (oldcontext) {
    2595               0 :                 zend_list_delete(oldcontext->rsrc_id);
    2596                 :         }
    2597                 : 
    2598            1775 :         return oldcontext;
    2599                 : }
    2600                 : 
    2601                 : PHPAPI void php_stream_notification_notify(php_stream_context *context, int notifycode, int severity,
    2602                 :                 char *xmsg, int xcode, size_t bytes_sofar, size_t bytes_max, void * ptr TSRMLS_DC)
    2603               0 : {
    2604               0 :         if (context && context->notifier)
    2605               0 :                 context->notifier->func(context, notifycode, severity, xmsg, xcode, bytes_sofar, bytes_max, ptr TSRMLS_CC);
    2606               0 : }
    2607                 : 
    2608                 : PHPAPI void php_stream_context_free(php_stream_context *context)
    2609            3095 : {
    2610            3095 :         if (context->options) {
    2611               0 :                 zval_ptr_dtor(&context->options);
    2612               0 :                 context->options = NULL;
    2613                 :         }
    2614            3095 :         if (context->notifier) {
    2615               1 :                 php_stream_notification_free(context->notifier);
    2616               1 :                 context->notifier = NULL;
    2617                 :         }
    2618            3095 :         if (context->input_encoding) {
    2619               0 :                 efree(context->input_encoding);
    2620                 :         }
    2621            3095 :         if (context->output_encoding) {
    2622               0 :                 efree(context->output_encoding);
    2623                 :         }
    2624            3095 :         if (context->links) {
    2625               0 :                 zval_ptr_dtor(&context->links);
    2626               0 :                 context->links = NULL;
    2627                 :         }
    2628            3095 :         efree(context);
    2629            3095 : }
    2630                 : 
    2631                 : PHPAPI php_stream_context *php_stream_context_alloc(void)
    2632            3069 : {
    2633                 :         php_stream_context *context;
    2634                 : 
    2635            3069 :         context = ecalloc(1, sizeof(php_stream_context));
    2636            3069 :         context->notifier = NULL;
    2637            3069 :         MAKE_STD_ZVAL(context->options);
    2638            3069 :         array_init(context->options);
    2639                 : 
    2640            3069 :         context->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, context, php_le_stream_context());
    2641            3069 :         return context;
    2642                 : }
    2643                 : 
    2644                 : PHPAPI php_stream_notifier *php_stream_notification_alloc(void)
    2645               2 : {
    2646               2 :         return ecalloc(1, sizeof(php_stream_notifier));
    2647                 : }
    2648                 : 
    2649                 : PHPAPI void php_stream_notification_free(php_stream_notifier *notifier)
    2650               2 : {
    2651               2 :         if (notifier->dtor) {
    2652               2 :                 notifier->dtor(notifier);
    2653                 :         }
    2654               2 :         efree(notifier);
    2655               2 : }
    2656                 : 
    2657                 : PHPAPI int php_stream_context_get_option(php_stream_context *context,
    2658                 :                 const char *wrappername, const char *optionname, zval ***optionvalue)
    2659             643 : {
    2660                 :         zval **wrapperhash;
    2661                 : 
    2662             643 :         if (FAILURE == zend_rt_hash_find(Z_ARRVAL_P(context->options), (char*)wrappername, strlen(wrappername)+1, (void**)&wrapperhash)) {
    2663             463 :                 return FAILURE;
    2664                 :         }
    2665             180 :         return zend_rt_hash_find(Z_ARRVAL_PP(wrapperhash), (char*)optionname, strlen(optionname)+1, (void**)optionvalue);
    2666                 : }
    2667                 : 
    2668                 : PHPAPI int php_stream_context_set_option(php_stream_context *context,
    2669                 :                 const char *wrappername, const char *optionname, zval *optionvalue)
    2670             661 : {
    2671                 :         zval **wrapperhash;
    2672                 :         zval *category, *copied_val;
    2673                 : 
    2674             661 :         ALLOC_INIT_ZVAL(copied_val);
    2675             661 :         *copied_val = *optionvalue;
    2676             661 :         zval_copy_ctor(copied_val);
    2677             661 :         INIT_PZVAL(copied_val);
    2678                 : 
    2679             661 :         if (FAILURE == zend_rt_hash_find(Z_ARRVAL_P(context->options), (char*)wrappername, strlen(wrappername)+1, (void**)&wrapperhash)) {
    2680             334 :                 MAKE_STD_ZVAL(category);
    2681             334 :                 array_init(category);
    2682             334 :                 if (FAILURE == zend_rt_hash_update(Z_ARRVAL_P(context->options), (char*)wrappername, strlen(wrappername)+1, (void**)&category, sizeof(zval *), NULL)) {
    2683               0 :                         return FAILURE;
    2684                 :                 }
    2685                 : 
    2686             334 :                 wrapperhash = &category;
    2687                 :         }
    2688             661 :         return zend_rt_hash_update(Z_ARRVAL_PP(wrapperhash), (char*)optionname, strlen(optionname)+1, (void**)&copied_val, sizeof(zval *), NULL);
    2689                 : }
    2690                 : 
    2691                 : PHPAPI int php_stream_context_get_link(php_stream_context *context,
    2692                 :         const char *hostent, php_stream **stream)
    2693               0 : {
    2694                 :         php_stream **pstream;
    2695                 : 
    2696               0 :         if (!stream || !hostent || !context || !(context->links)) {
    2697               0 :                 return FAILURE;
    2698                 :         }
    2699               0 :         if (SUCCESS == zend_rt_hash_find(Z_ARRVAL_P(context->links), (char*)hostent, strlen(hostent)+1, (void**)&pstream)) {
    2700               0 :                 *stream = *pstream;
    2701               0 :                 return SUCCESS;
    2702                 :         }
    2703               0 :         return FAILURE;
    2704                 : }
    2705                 : 
    2706                 : PHPAPI int php_stream_context_set_link(php_stream_context *context,
    2707                 :         const char *hostent, php_stream *stream)
    2708               0 : {
    2709               0 :         if (!context) {
    2710               0 :                 return FAILURE;
    2711                 :         }
    2712               0 :         if (!context->links) {
    2713               0 :                 ALLOC_INIT_ZVAL(context->links);
    2714               0 :                 array_init(context->links);
    2715                 :         }
    2716               0 :         if (!stream) {
    2717                 :                 /* Delete any entry for <hostent> */
    2718               0 :                 return zend_rt_hash_del(Z_ARRVAL_P(context->links), (char*)hostent, strlen(hostent)+1);
    2719                 :         }
    2720               0 :         return zend_rt_hash_update(Z_ARRVAL_P(context->links), (char*)hostent, strlen(hostent)+1, (void**)&stream, sizeof(php_stream *), NULL);
    2721                 : }
    2722                 : 
    2723                 : PHPAPI int php_stream_context_del_link(php_stream_context *context,
    2724                 :         php_stream *stream)
    2725               0 : {
    2726                 :         php_stream **pstream;
    2727                 :         zend_uchar type;
    2728                 :         zstr hostent;
    2729                 :         unsigned int hostent_len;
    2730               0 :         int ret = SUCCESS;
    2731                 : 
    2732               0 :         if (!context || !context->links || !stream) {
    2733               0 :                 return FAILURE;
    2734                 :         }
    2735                 : 
    2736               0 :         for(zend_hash_internal_pointer_reset(Z_ARRVAL_P(context->links));
    2737               0 :                 SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(context->links), (void**)&pstream);
    2738               0 :                 zend_hash_move_forward(Z_ARRVAL_P(context->links))) {
    2739               0 :                 if (*pstream == stream) {
    2740               0 :                         type = zend_hash_get_current_key_ex(Z_ARRVAL_P(context->links), &hostent, &hostent_len, NULL, 0, NULL);
    2741               0 :                         if (type == HASH_KEY_IS_STRING || type == HASH_KEY_IS_UNICODE) {
    2742               0 :                                 if (FAILURE == zend_u_hash_del(Z_ARRVAL_P(context->links), type, hostent, hostent_len)) {
    2743               0 :                                         ret = FAILURE;
    2744                 :                                 }
    2745                 :                         } else {
    2746               0 :                                 ret = FAILURE;
    2747                 :                         }
    2748                 :                 }
    2749                 :         }
    2750                 : 
    2751               0 :         return ret;
    2752                 : }
    2753                 : /* }}} */
    2754                 : 
    2755                 : /* {{{ php_stream_dirent_alphasort
    2756                 :  */
    2757                 : PHPAPI int php_stream_dirent_alphasort(const char **a, const char **b)
    2758             124 : {
    2759             124 :         return strcoll(*a, *b);
    2760                 : }
    2761                 : /* }}} */
    2762                 : 
    2763                 : /* {{{ php_stream_dirent_alphasortr
    2764                 :  */
    2765                 : PHPAPI int php_stream_dirent_alphasortr(const char **a, const char **b)
    2766              24 : {
    2767              24 :         return strcoll(*b, *a);
    2768                 : }
    2769                 : /* }}} */
    2770                 : 
    2771                 : /* {{{ php_stream_scandir
    2772                 :  */
    2773                 : PHPAPI int _php_stream_scandir(char *dirname, char **namelist[], int flags, php_stream_context *context,
    2774                 :                           int (*compare) (const char **a, const char **b) TSRMLS_DC)
    2775              86 : {
    2776                 :         php_stream *stream;
    2777                 :         php_stream_dirent sdp;
    2778              86 :         char **vector = NULL;
    2779              86 :         int vector_size = 0;
    2780              86 :         int nfiles = 0;
    2781                 : 
    2782              86 :         if (!namelist) {
    2783               0 :                 return FAILURE;
    2784                 :         }
    2785                 : 
    2786              86 :         stream = php_stream_opendir(dirname, REPORT_ERRORS, context);
    2787              86 :         if (!stream) {
    2788              41 :                 return FAILURE;
    2789                 :         }
    2790                 : 
    2791             229 :         while (php_stream_readdir(stream, &sdp)) {
    2792             139 :                 if (nfiles == vector_size) {
    2793              46 :                         if (vector_size == 0) {
    2794              45 :                                 vector_size = 10;
    2795                 :                         } else {
    2796               1 :                                 vector_size *= 2;
    2797                 :                         }
    2798              46 :                         vector = (char **) erealloc(vector, vector_size * sizeof(char *));
    2799                 :                 }
    2800                 : 
    2801             139 :                 vector[nfiles] = estrdup(sdp.d_name);
    2802                 : 
    2803             139 :                 nfiles++;
    2804                 :         }
    2805              45 :         php_stream_closedir(stream);
    2806                 : 
    2807              45 :         *namelist = vector;
    2808                 : 
    2809              45 :         if (compare) {
    2810              45 :                 qsort(*namelist, nfiles, sizeof(char *), (int(*)(const void *, const void *))compare);
    2811                 :         }
    2812              45 :         return nfiles;
    2813                 : }
    2814                 : /* }}} */
    2815                 : 
    2816                 : /* {{{ php_stream_path_encode
    2817                 : Encode a filepath to the appropriate characterset.
    2818                 : If the wrapper supports its own encoding rules it will be dispatched to wrapper->wops->path_encode()
    2819                 : Otherwise the INI defined filesystem_encoding converter will be used
    2820                 : If wrapper == NULL, the path will be explored to locate the correct wrapper
    2821                 : */
    2822                 : PHPAPI int _php_stream_path_encode(php_stream_wrapper *wrapper,
    2823                 :                                 char **pathenc, int *pathenc_len, const UChar *path, int path_len,
    2824                 :                                 int options, php_stream_context *context TSRMLS_DC)
    2825          391534 : {
    2826          391534 :         UErrorCode status = U_ZERO_ERROR;
    2827                 :         int num_conv;
    2828                 : 
    2829          391534 :         if (!wrapper) {
    2830                 :                 UChar *p;
    2831                 :                 U_STRING_DECL(delim, "://", 3);
    2832          391534 :                 int delim_len = 3;
    2833                 : 
    2834          391534 :                 U_STRING_INIT(delim, "://", 3);
    2835                 : 
    2836          391534 :                 p = u_strFindFirst(path, path_len, delim, delim_len);
    2837          391534 :                 if (p) {
    2838             973 :                         char *scheme = NULL;
    2839             973 :                         int scheme_len = 0;
    2840                 : 
    2841                 :                         /* Convert just the scheme using utf8 in order to look it up in the registry */
    2842             973 :                         num_conv = zend_unicode_to_string_ex(UG(utf8_conv), &scheme, &scheme_len, path, (p - path) + delim_len, &status);
    2843             973 :                         if (U_FAILURE(status)) {
    2844               0 :                                 if (options & REPORT_ERRORS) {
    2845               0 :                                         zend_raise_conversion_error_ex("Unable to convert filepath", UG(utf8_conv), ZEND_FROM_UNICODE, num_conv TSRMLS_CC);
    2846                 :                                 }
    2847               0 :                                 *pathenc = NULL;
    2848               0 :                                 *pathenc_len = 0;
    2849                 : 
    2850               0 :                                 return FAILURE;
    2851                 :                         }
    2852             973 :                         wrapper = php_stream_locate_url_wrapper(scheme, NULL, options | STREAM_DISABLE_URL_PROTECTION TSRMLS_CC);
    2853             973 :                         efree(scheme);
    2854             973 :                         if (!wrapper) {
    2855               1 :                                 *pathenc = NULL;
    2856               1 :                                 *pathenc_len = 0;
    2857                 : 
    2858               1 :                                 return FAILURE;
    2859                 :                         }                       
    2860                 :                 } else {
    2861          390561 :                         wrapper = &php_plain_files_wrapper;
    2862                 :                 }
    2863                 :         }
    2864                 : 
    2865          391533 :         if (wrapper->wops->path_encode) {
    2866               0 :                 if (wrapper->wops->path_encode(wrapper, pathenc, pathenc_len, path, path_len, options, context TSRMLS_CC) == FAILURE) {
    2867               0 :                         *pathenc = NULL;
    2868               0 :                         *pathenc_len = 0;
    2869                 : 
    2870               0 :                         return FAILURE;
    2871                 :                 }
    2872                 : 
    2873               0 :                 return SUCCESS;
    2874                 :         }
    2875                 : 
    2876                 :         /* Otherwise, fallback on filesystem_encoding */
    2877          391533 :         status = U_ZERO_ERROR;
    2878                 : 
    2879          391533 :         num_conv = zend_unicode_to_string_ex(ZEND_U_CONVERTER(UG(filesystem_encoding_conv)),
    2880                 :                                 pathenc, pathenc_len, path, path_len, &status);
    2881          391533 :         if (U_FAILURE(status)) {
    2882               0 :                 if (options & REPORT_ERRORS) {
    2883               0 :                         zend_raise_conversion_error_ex("Unable to convert filepath", ZEND_U_CONVERTER(UG(filesystem_encoding_conv)),
    2884                 :                                                         ZEND_FROM_UNICODE, num_conv TSRMLS_CC);
    2885                 :                 }
    2886                 : 
    2887               0 :                 *pathenc = NULL;
    2888               0 :                 *pathenc_len = 0;
    2889                 : 
    2890               0 :                 return FAILURE;
    2891                 :         }
    2892                 : 
    2893          391533 :         return SUCCESS;
    2894                 : }
    2895                 : /* }}} */
    2896                 : 
    2897                 : 
    2898                 : /* {{{ php_stream_path_decode
    2899                 : Decode a filepath from its character set to unicode
    2900                 : If the wrapper supports its own decoding rules it will be dispatched to wrapper->wops->path_encode()
    2901                 : Otherwise (or if wrapper == NULL) the INI defined filesystem_encoding converter will be used.
    2902                 : */
    2903                 : PHPAPI int _php_stream_path_decode(php_stream_wrapper *wrapper,
    2904                 :                                 UChar **pathdec, int *pathdec_len, const char *path, int path_len,
    2905                 :                                 int options, php_stream_context *context TSRMLS_DC)
    2906           42270 : {
    2907                 :         int num_conv;
    2908           42270 :         UErrorCode status = U_ZERO_ERROR;
    2909                 : 
    2910           42270 :         if (wrapper && wrapper->wops->path_decode) {
    2911               0 :                 if (wrapper->wops->path_decode(wrapper, pathdec, pathdec_len, path, path_len, options, context TSRMLS_CC) == FAILURE) {
    2912               0 :                         *pathdec = NULL;
    2913               0 :                         *pathdec_len = 0;
    2914                 : 
    2915               0 :                         return FAILURE;
    2916                 :                 }
    2917               0 :                 return SUCCESS;
    2918                 :         }
    2919                 : 
    2920                 :         /* Otherwise fallback on filesystem_encoding */
    2921           42270 :         num_conv = zend_string_to_unicode_ex(ZEND_U_CONVERTER(UG(filesystem_encoding_conv)),
    2922                 :                                 pathdec, pathdec_len, path, path_len, &status);
    2923           42270 :         if (U_FAILURE(status)) {
    2924               0 :                 if (options & REPORT_ERRORS) {
    2925               0 :                         zend_raise_conversion_error_ex("Unable to convert filepath", ZEND_U_CONVERTER(UG(filesystem_encoding_conv)),
    2926                 :                                                         ZEND_TO_UNICODE, num_conv TSRMLS_CC);
    2927                 :                 }
    2928                 : 
    2929               0 :                 *pathdec = NULL;
    2930               0 :                 *pathdec_len = 0;
    2931                 : 
    2932               0 :                 return FAILURE;
    2933                 :         }
    2934                 : 
    2935           42270 :         return SUCCESS;
    2936                 : 
    2937                 : }
    2938                 : /* }}} */
    2939                 : 
    2940                 : /* {{{ allow_url_fopen / allow_url_include Handlers */
    2941                 : 
    2942                 : PHPAPI int php_stream_wrapper_is_allowed(const char *wrapper, int wrapper_len, const char *setting TSRMLS_DC)
    2943          470715 : {
    2944          470715 :         HashTable *wrapper_hash = (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash);
    2945                 :         php_stream_wrapper **wrapperpp;
    2946          470715 :         int setting_len = setting ? strlen(setting) : 0;
    2947          470715 :         const char *s = setting, *e = s + setting_len;
    2948                 :         char *wrapper_dup;
    2949                 : 
    2950                 :         /* BC: allow_url_* == on */
    2951          470715 :         if (setting_len == 1 && *setting == '*') {
    2952                 :                 /* "*" means everything is allowed */
    2953          462791 :                 return 1;
    2954                 :         }
    2955                 : 
    2956            7924 :         if (wrapper_len == (sizeof("zlib") - 1) && strncasecmp("zlib", wrapper, sizeof("zlib") - 1) == 0) {
    2957               0 :                 wrapper = "compress.zlib";
    2958               0 :                 wrapper_len = sizeof("compress.zlib") - 1;
    2959                 :         }
    2960                 : 
    2961            7924 :         wrapper_dup = estrndup(wrapper, wrapper_len);
    2962            7924 :         php_strtolower(wrapper_dup, wrapper_len);
    2963            7924 :         if (FAILURE == zend_hash_find(wrapper_hash, wrapper_dup, wrapper_len + 1, (void**)&wrapperpp)) {
    2964                 :                 /* Wrapper does not exist, assume disallow */
    2965               0 :                 efree(wrapper_dup);
    2966               0 :                 return 0;
    2967                 :         }
    2968            7924 :         efree(wrapper_dup);
    2969                 : 
    2970                 :         /* BC: allow_url_* == off */
    2971            7924 :         if (!setting || !setting_len) {
    2972                 :                 /* NULL or empty indicates that only is_url == 0 wrappers are allowed */
    2973                 : 
    2974            7924 :                 if (wrapper_len == (sizeof("file") - 1) && strncasecmp("file", wrapper, sizeof("file") - 1) == 0) {
    2975                 :                         /* file:// is non-url */
    2976            7296 :                         return 1;
    2977                 :                 }
    2978                 : 
    2979             628 :                 if ((*wrapperpp)->is_url) {
    2980                 :                         /* is_url types are disabled, but this is an is_url wrapper, disallow */
    2981              15 :                         return 0;
    2982                 :                 }
    2983                 : 
    2984                 :                 /* Wrapper is not is_url, allow it */
    2985             613 :                 return 1;
    2986                 :         }
    2987                 : 
    2988                 :         /* Otherwise, scan list */
    2989               0 :         while (s < e) {
    2990               0 :                 const char *p = php_memnstr((char*)s, ":", 1, (char*)e);
    2991                 : 
    2992               0 :                 if (!p) {
    2993               0 :                         p = e;
    2994                 :                 }
    2995                 : 
    2996               0 :                 if (wrapper_len == (p - s) &&
    2997                 :                         strncasecmp(s, wrapper, p - s) == 0) {
    2998                 :                         /* wrapper found in list */
    2999               0 :                         return 1;
    3000                 :                 }
    3001                 : 
    3002               0 :                 if ((*wrapperpp)->wops == php_stream_user_wrapper_ops &&
    3003                 :                         (sizeof("user") - 1) == (p - s) &&
    3004                 :                         strncasecmp(s, "user", sizeof("user") - 1) == 0) {
    3005                 :                         /* Wrapper is userspace wrapper and meta-wrapper "user" is enabled */
    3006               0 :                         return 1;
    3007                 :                 }
    3008                 : 
    3009               0 :                 s = p + 1;
    3010                 :         }
    3011                 : 
    3012               0 :         return 0;
    3013                 : }
    3014                 : 
    3015                 : /* allow_url_*_list accepts:
    3016                 :  *
    3017                 :  * 1/on to enable all URL prefixes
    3018                 :  * 0/off to disable all is_url=1 wrappers
    3019                 :  * A colon delimited list of wrappers to allow (wildcards allowed)
    3020                 :  * e.g.    file:gzip:compress.*:php
    3021                 :  */
    3022                 : PHP_INI_MH(OnUpdateAllowUrl)
    3023           35893 : {
    3024                 : #ifndef ZTS
    3025           35893 :         char *base = (char *) mh_arg2;
    3026                 : #else
    3027                 :         char *base = (char *) ts_resource(*((int *) mh_arg2));
    3028                 : #endif
    3029           35893 :         char **allow = (char **) (base+(size_t) mh_arg1);
    3030                 : 
    3031                 :         /* BC Enable */
    3032           35893 :         if ((new_value_length == 1 && *new_value == '1') ||
    3033                 :                 (new_value_length == (sizeof("on") - 1) && strncasecmp(new_value, "on", sizeof("on") - 1) == 0) ) {
    3034                 : 
    3035           17443 :                 if (*allow && strcmp(*allow, "*") == 0) {
    3036                 :                         /* Turning on, but that's no change from current, so leave it alone */
    3037             435 :                         return SUCCESS;
    3038                 :                 }
    3039                 :                         
    3040           17008 :                 if (stage != PHP_INI_STAGE_STARTUP) {
    3041                 :                         /* Not already on, and not in SYSTEM context, fail */
    3042               0 :                         return FAILURE;
    3043                 :                 }
    3044                 : 
    3045                 :                 /* Otherwise, turn on setting */
    3046           17008 :                 if (*allow) {
    3047               0 :                         free(*allow);
    3048                 :                 }
    3049                 : 
    3050           17008 :                 *allow = zend_strndup("*", 1);
    3051                 : 
    3052           17008 :                 return SUCCESS;
    3053                 :         }
    3054                 : 
    3055                 :         /* BC disable */
    3056           18450 :         if ((new_value_length == 1 && *new_value == '0') ||
    3057                 :                 (new_value_length == (sizeof("off") - 1) && strncasecmp(new_value, "off", sizeof("off") - 1) == 0) ) {
    3058                 : 
    3059                 :                 /* Always permit shutting off allowurl settings */
    3060           17004 :                 if (*allow) {
    3061               0 :                         free(*allow);
    3062                 :                 }
    3063           17004 :                 *allow = NULL;
    3064                 : 
    3065           17004 :                 return SUCCESS;
    3066                 :         }
    3067                 : 
    3068                 :         /* Specify as list */
    3069            1446 :         if (stage == PHP_INI_STAGE_STARTUP) {
    3070                 :                 /* Always allow new settings in startup stage */
    3071               2 :                 if (*allow) {
    3072               0 :                         free(*allow);
    3073                 :                 }
    3074               2 :                 *allow = zend_strndup(new_value, new_value_length);
    3075                 : 
    3076               2 :                 return SUCCESS;
    3077                 :         }
    3078                 : 
    3079                 :         /* In PERDIR/RUNTIME context, do more work to ensure we're only tightening the restriction */
    3080                 : 
    3081            1444 :         if (*allow && strcmp(*allow, "*") == 0) {
    3082                 :                 /* Currently allowing everying, so whatever we set it to will be more restrictive */
    3083            1444 :                 free(*allow);
    3084            1444 :                 *allow = zend_strndup(new_value, new_value_length);
    3085                 : 
    3086            1444 :                 return SUCCESS;
    3087                 :         }
    3088                 : 
    3089               0 :         if (!*allow) {
    3090                 :                 /* Currently allowing anything with is_url == 0
    3091                 :                  * So long as this list doesn't contain any is_url == 1, allow it
    3092                 :                  */
    3093               0 :                 HashTable *wrapper_hash = (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash);
    3094               0 :                 char *s = new_value, *e = new_value + new_value_length;
    3095                 : 
    3096               0 :                 while (s < e) {
    3097                 :                         php_stream_wrapper **wrapper;
    3098               0 :                         char *p = php_memnstr(s, ":", 1, e);
    3099                 :                         char *scan;
    3100                 :                         int scan_len;
    3101                 : 
    3102               0 :                         if (!p) {
    3103               0 :                                 p = e;
    3104                 :                         }
    3105                 : 
    3106                 :                         /* file:// is never a URL */
    3107               0 :                         if ( (p - s) == (sizeof("file") - 1) && strncasecmp(s, "file", sizeof("file") - 1) == 0 ) {
    3108                 :                                 /* file is not a URL */
    3109               0 :                                 s = p + 1;
    3110               0 :                                 continue;
    3111                 :                         }
    3112                 : 
    3113               0 :                         if ( (p - s) == (sizeof("zlib") - 1) && strncasecmp(s, "zlib", sizeof("zlib") - 1) == 0 ) {
    3114                 :                                 /* Wastful since we know that compress.zlib is already lower cased, but forgivable */
    3115               0 :                                 scan = estrndup("compress.zlib", sizeof("compress.zlib") - 1);
    3116               0 :                                 scan_len = sizeof("compress.zlib") - 1;
    3117                 :                         } else {
    3118               0 :                                 scan = estrndup(s, p - s);;
    3119               0 :                                 scan_len = p - s;
    3120               0 :                                 php_strtolower(scan, scan_len);
    3121                 :                         }
    3122                 : 
    3123               0 :                         if (FAILURE == zend_hash_find(wrapper_hash, scan, scan_len + 1, (void**) &wrapper)) {
    3124                 :                                 /* Unknown wrapper, not allowed in this context */
    3125               0 :                                 efree(scan);
    3126               0 :                                 return FAILURE;
    3127                 :                         }
    3128               0 :                         efree(scan);
    3129                 : 
    3130               0 :                         if ((*wrapper)->is_url) {
    3131                 :                                 /* Disallowed is_url wrapper specified when trying to escape is_url == 0 context */
    3132               0 :                                 return FAILURE;
    3133                 :                         }
    3134                 : 
    3135                 :                         /* Seems alright so far... */
    3136               0 :                         s = p+1;
    3137                 :                 }
    3138                 : 
    3139                 :                 /* All tests passed, allow it */
    3140               0 :                 *allow = zend_strndup(new_value, new_value_length);
    3141                 : 
    3142               0 :                 return SUCCESS;
    3143                 :         }
    3144                 : 
    3145                 :         /* The current allows are restricted to a specific list,
    3146                 :          * Make certain that our new list is a subset of that list
    3147                 :          */
    3148                 :         {
    3149               0 :                 char *s = new_value, *e = new_value + new_value_length;
    3150                 : 
    3151               0 :                 while (s < e) {
    3152               0 :                         char *p = php_memnstr(s, ":", 1, e);
    3153                 : 
    3154               0 :                         if (!p) {
    3155               0 :                                 p = e;
    3156                 :                         }
    3157                 : 
    3158               0 :                         if (!php_stream_wrapper_is_allowed(s, p - s, *allow TSRMLS_CC)) {
    3159                 :                                 /* Current settings don't allow this wrapper, deny */
    3160               0 :                                 return FAILURE;
    3161                 :                         }
    3162                 : 
    3163               0 :                         s = p + 1;
    3164                 :                 }
    3165                 : 
    3166               0 :                 free(*allow);
    3167               0 :                 *allow = zend_strndup(new_value, new_value_length);
    3168                 : 
    3169               0 :                 return SUCCESS;
    3170                 :         }
    3171                 : }
    3172                 : 
    3173                 : /* }}} */
    3174                 : 
    3175                 : /*
    3176                 :  * Local variables:
    3177                 :  * tab-width: 4
    3178                 :  * c-basic-offset: 4
    3179                 :  * End:
    3180                 :  * vim600: noet sw=4 ts=4 fdm=marker
    3181                 :  * vim<600: noet sw=4 ts=4
    3182                 :  */

Generated by: LTP GCOV extension version 1.5

Generated at Mon, 23 Nov 2009 17:39:47 +0000 (36 hours ago)

Copyright © 2005-2009 The PHP Group
All rights reserved.