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

LCOV - code coverage report
Current view: top level - main/streams - streams.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 932 1036 90.0 %
Date: 2015-03-28 Functions: 70 73 95.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :    +----------------------------------------------------------------------+
       3             :    | PHP Version 7                                                        |
       4             :    +----------------------------------------------------------------------+
       5             :    | Copyright (c) 1997-2015 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$ */
      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     8456012 : PHPAPI int php_file_le_stream(void)
      44             : {
      45     8456012 :         return le_stream;
      46             : }
      47             : 
      48     8427040 : PHPAPI int php_file_le_pstream(void)
      49             : {
      50     8427040 :         return le_pstream;
      51             : }
      52             : 
      53         117 : PHPAPI int php_file_le_stream_filter(void)
      54             : {
      55         117 :         return le_stream_filter;
      56             : }
      57             : 
      58         145 : PHPAPI HashTable *_php_stream_get_url_stream_wrappers_hash(void)
      59             : {
      60         145 :         return (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash);
      61             : }
      62             : 
      63           1 : PHPAPI HashTable *php_stream_get_url_stream_wrappers_hash_global(void)
      64             : {
      65           1 :         return &url_stream_wrappers_hash;
      66             : }
      67             : 
      68           4 : static int _php_stream_release_context(zval *zv, void *pContext)
      69             : {
      70           4 :         zend_resource *le = Z_RES_P(zv);
      71           4 :         if (le->ptr == pContext) {
      72           1 :                 return --GC_REFCOUNT(le) == 0;
      73             :         }
      74           3 :         return 0;
      75             : }
      76             : 
      77          49 : static int forget_persistent_resource_id_numbers(zval *el)
      78             : {
      79             :         php_stream *stream;
      80          49 :         zend_resource *rsrc = Z_RES_P(el);
      81             : 
      82          49 :         if (rsrc->type != le_pstream) {
      83          42 :                 return 0;
      84             :         }
      85             : 
      86           7 :         stream = (php_stream*)rsrc->ptr;
      87             : 
      88             : #if STREAM_DEBUG
      89             : fprintf(stderr, "forget_persistent: %s:%p\n", stream->ops->label, stream);
      90             : #endif
      91             : 
      92           7 :         stream->res = NULL;
      93             : 
      94           7 :         if (PHP_STREAM_CONTEXT(stream)) {
      95           2 :                 zend_hash_apply_with_argument(&EG(regular_list),
      96             :                                 _php_stream_release_context,
      97           2 :                                 PHP_STREAM_CONTEXT(stream));
      98           1 :                 stream->ctx = NULL;
      99             :         }
     100             : 
     101           7 :         return 0;
     102             : }
     103             : 
     104       20864 : PHP_RSHUTDOWN_FUNCTION(streams)
     105             : {
     106       20864 :         zend_hash_apply(&EG(persistent_list), forget_persistent_resource_id_numbers);
     107       20864 :         return SUCCESS;
     108             : }
     109             : 
     110         154 : PHPAPI php_stream *php_stream_encloses(php_stream *enclosing, php_stream *enclosed)
     111             : {
     112         154 :         php_stream *orig = enclosed->enclosing_stream;
     113             : 
     114         154 :         php_stream_auto_cleanup(enclosed);
     115         154 :         enclosed->enclosing_stream = enclosing;
     116         154 :         return orig;
     117             : }
     118             : 
     119        1166 : PHPAPI int php_stream_from_persistent_id(const char *persistent_id, php_stream **stream)
     120             : {
     121             :         zend_resource *le;
     122             : 
     123        2332 :         if ((le = zend_hash_str_find_ptr(&EG(persistent_list), persistent_id, strlen(persistent_id))) != NULL) {
     124           1 :                 if (le->type == le_pstream) {
     125           1 :                         if (stream) {
     126           1 :                                 zend_resource *regentry = NULL;
     127             : 
     128             :                                 /* see if this persistent resource already has been loaded to the
     129             :                                  * regular list; allowing the same resource in several entries in the
     130             :                                  * regular list causes trouble (see bug #54623) */
     131           1 :                                 *stream = (php_stream*)le->ptr;
     132          10 :                                 ZEND_HASH_FOREACH_PTR(&EG(regular_list), regentry) {
     133           4 :                                         if (regentry->ptr == le->ptr) {
     134           1 :                                                 GC_REFCOUNT(regentry)++;
     135           1 :                                                 (*stream)->res = regentry;
     136           1 :                                                 return PHP_STREAM_PERSISTENT_SUCCESS;
     137             :                                         }
     138             :                                 } ZEND_HASH_FOREACH_END();
     139           0 :                                 GC_REFCOUNT(le)++;
     140           0 :                                 (*stream)->res = zend_register_resource(*stream, le_pstream);
     141             :                         }
     142           0 :                         return PHP_STREAM_PERSISTENT_SUCCESS;
     143             :                 }
     144           0 :                 return PHP_STREAM_PERSISTENT_FAILURE;
     145             :         }
     146        1165 :         return PHP_STREAM_PERSISTENT_NOT_EXIST;
     147             : }
     148             : 
     149             : /* }}} */
     150             : 
     151         482 : static zend_llist *php_get_wrapper_errors_list(php_stream_wrapper *wrapper)
     152             : {
     153         482 :     if (!FG(wrapper_errors)) {
     154         373 :         return NULL;
     155             :     } else {
     156         218 :         return (zend_llist*) zend_hash_str_find_ptr(FG(wrapper_errors), (const char*)&wrapper, sizeof(wrapper));
     157             :     }
     158             : }
     159             : 
     160             : /* {{{ wrapper error reporting */
     161         502 : void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption)
     162             : {
     163         502 :         char *tmp = estrdup(path);
     164             :         char *msg;
     165         502 :         int free_msg = 0;
     166             : 
     167         502 :         if (wrapper) {
     168         482 :                 zend_llist *err_list = php_get_wrapper_errors_list(wrapper);
     169         482 :                 if (err_list) {
     170         108 :                         size_t l = 0;
     171             :                         int brlen;
     172             :                         int i;
     173         108 :                         int count = (int)zend_llist_count(err_list);
     174             :                         const char *br;
     175             :                         const char **err_buf_p;
     176             :                         zend_llist_position pos;
     177             : 
     178         108 :                         if (PG(html_errors)) {
     179           0 :                                 brlen = 7;
     180           0 :                                 br = "<br />\n";
     181             :                         } else {
     182         108 :                                 brlen = 1;
     183         108 :                                 br = "\n";
     184             :                         }
     185             : 
     186         330 :                         for (err_buf_p = zend_llist_get_first_ex(err_list, &pos), i = 0;
     187             :                                         err_buf_p;
     188         114 :                                         err_buf_p = zend_llist_get_next_ex(err_list, &pos), i++) {
     189         114 :                                 l += strlen(*err_buf_p);
     190         114 :                                 if (i < count - 1) {
     191           6 :                                         l += brlen;
     192             :                                 }
     193             :                         }
     194         108 :                         msg = emalloc(l + 1);
     195         108 :                         msg[0] = '\0';
     196         330 :                         for (err_buf_p = zend_llist_get_first_ex(err_list, &pos), i = 0;
     197             :                                         err_buf_p;
     198         114 :                                         err_buf_p = zend_llist_get_next_ex(err_list, &pos), i++) {
     199         114 :                                 strcat(msg, *err_buf_p);
     200         114 :                                 if (i < count - 1) {
     201           6 :                                         strcat(msg, br);
     202             :                                 }
     203             :                         }
     204             : 
     205         108 :                         free_msg = 1;
     206             :                 } else {
     207         374 :                         if (wrapper == &php_plain_files_wrapper) {
     208         369 :                                 msg = strerror(errno); /* TODO: not ts on linux */
     209             :                         } else {
     210           5 :                                 msg = "operation failed";
     211             :                         }
     212             :                 }
     213             :         } else {
     214          20 :                 msg = "no suitable wrapper could be found";
     215             :         }
     216             : 
     217         502 :         php_strip_url_passwd(tmp);
     218         502 :         php_error_docref1(NULL, tmp, E_WARNING, "%s: %s", caption, msg);
     219         502 :         efree(tmp);
     220         502 :         if (free_msg) {
     221         108 :                 efree(msg);
     222             :         }
     223         502 : }
     224             : 
     225      132948 : void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper)
     226             : {
     227      132948 :         if (wrapper && FG(wrapper_errors)) {
     228         177 :                 zend_hash_str_del(FG(wrapper_errors), (const char*)&wrapper, sizeof(wrapper));
     229             :         }
     230      132948 : }
     231             : 
     232         114 : static void wrapper_error_dtor(void *error)
     233             : {
     234         114 :         efree(*(char**)error);
     235         114 : }
     236             : 
     237         108 : static void wrapper_list_dtor(zval *item) {
     238         108 :         zend_llist *list = (zend_llist*)Z_PTR_P(item);
     239         108 :         zend_llist_destroy(list);
     240         108 :         efree(list);
     241         108 : }
     242             : 
     243         148 : PHPAPI void php_stream_wrapper_log_error(php_stream_wrapper *wrapper, int options, const char *fmt, ...)
     244             : {
     245             :         va_list args;
     246         148 :         char *buffer = NULL;
     247             : 
     248         148 :         va_start(args, fmt);
     249         148 :         vspprintf(&buffer, 0, fmt, args);
     250         148 :         va_end(args);
     251             : 
     252         182 :         if (options & REPORT_ERRORS || wrapper == NULL) {
     253          34 :                 php_error_docref(NULL, E_WARNING, "%s", buffer);
     254          34 :                 efree(buffer);
     255             :         } else {
     256         114 :                 zend_llist *list = NULL;
     257         114 :                 if (!FG(wrapper_errors)) {
     258          67 :                         ALLOC_HASHTABLE(FG(wrapper_errors));
     259          67 :                         zend_hash_init(FG(wrapper_errors), 8, NULL, wrapper_list_dtor, 0);
     260             :                 } else {
     261          94 :                         list = zend_hash_str_find_ptr(FG(wrapper_errors), (const char*)&wrapper, sizeof(wrapper));
     262             :                 }
     263             : 
     264         114 :                 if (!list) {
     265             :                         zend_llist new_list;
     266         108 :                         zend_llist_init(&new_list, sizeof(buffer), wrapper_error_dtor, 0);
     267         216 :                         list = zend_hash_str_update_mem(FG(wrapper_errors), (const char*)&wrapper,
     268             :                                         sizeof(wrapper), &new_list, sizeof(new_list));
     269             :                 }
     270             : 
     271             :                 /* append to linked list */
     272         114 :                 zend_llist_add_element(list, &buffer);
     273             :         }
     274         148 : }
     275             : 
     276             : 
     277             : /* }}} */
     278             : 
     279             : /* allocate a new stream for a particular ops */
     280      221905 : PHPAPI php_stream *_php_stream_alloc(php_stream_ops *ops, void *abstract, const char *persistent_id, const char *mode STREAMS_DC) /* {{{ */
     281             : {
     282             :         php_stream *ret;
     283             : 
     284      443810 :         ret = (php_stream*) pemalloc_rel_orig(sizeof(php_stream), persistent_id ? 1 : 0);
     285             : 
     286      221905 :         memset(ret, 0, sizeof(php_stream));
     287             : 
     288      221905 :         ret->readfilters.stream = ret;
     289      221905 :         ret->writefilters.stream = ret;
     290             : 
     291             : #if STREAM_DEBUG
     292             : fprintf(stderr, "stream_alloc: %s:%p persistent=%s\n", ops->label, ret, persistent_id);
     293             : #endif
     294             : 
     295      221905 :         ret->ops = ops;
     296      221905 :         ret->abstract = abstract;
     297      221905 :         ret->is_persistent = persistent_id ? 1 : 0;
     298      221905 :         ret->chunk_size = FG(def_chunk_size);
     299             : 
     300             : #if ZEND_DEBUG
     301             :         ret->open_filename = __zend_orig_filename ? __zend_orig_filename : __zend_filename;
     302             :         ret->open_lineno = __zend_orig_lineno ? __zend_orig_lineno : __zend_lineno;
     303             : #endif
     304             : 
     305      221905 :         if (FG(auto_detect_line_endings)) {
     306           0 :                 ret->flags |= PHP_STREAM_FLAG_DETECT_EOL;
     307             :         }
     308             : 
     309      221905 :         if (persistent_id) {
     310             :                 zval tmp;
     311             : 
     312        1165 :                 ZVAL_NEW_PERSISTENT_RES(&tmp, -1, ret, le_pstream);
     313             : 
     314        1165 :                 if (NULL == zend_hash_str_update(&EG(persistent_list), persistent_id,
     315             :                                         strlen(persistent_id), &tmp)) {
     316           0 :                         pefree(ret, 1);
     317           0 :                         return NULL;
     318             :                 }
     319             :         }
     320             : 
     321      221905 :         ret->res = zend_register_resource(ret, persistent_id ? le_pstream : le_stream);
     322      221905 :         strlcpy(ret->mode, mode, sizeof(ret->mode));
     323             : 
     324      221905 :         ret->wrapper          = NULL;
     325      221905 :         ret->wrapperthis      = NULL;
     326      221905 :         ZVAL_UNDEF(&ret->wrapperdata);
     327      221905 :         ret->stdiocast        = NULL;
     328      221905 :         ret->orig_path        = NULL;
     329      221905 :         ret->ctx              = NULL;
     330      221905 :         ret->readbuf          = NULL;
     331      221905 :         ret->enclosing_stream = NULL;
     332             : 
     333      221905 :         return ret;
     334             : }
     335             : /* }}} */
     336             : 
     337         154 : PHPAPI int _php_stream_free_enclosed(php_stream *stream_enclosed, int close_options) /* {{{ */
     338             : {
     339         154 :         return _php_stream_free(stream_enclosed,
     340             :                 close_options | PHP_STREAM_FREE_IGNORE_ENCLOSING);
     341             : }
     342             : /* }}} */
     343             : 
     344             : #if STREAM_DEBUG
     345             : static const char *_php_stream_pretty_free_options(int close_options, char *out)
     346             : {
     347             :         if (close_options & PHP_STREAM_FREE_CALL_DTOR)
     348             :                 strcat(out, "CALL_DTOR, ");
     349             :         if (close_options & PHP_STREAM_FREE_RELEASE_STREAM)
     350             :                 strcat(out, "RELEASE_STREAM, ");
     351             :         if (close_options & PHP_STREAM_FREE_PRESERVE_HANDLE)
     352             :                 strcat(out, "PREVERSE_HANDLE, ");
     353             :         if (close_options & PHP_STREAM_FREE_RSRC_DTOR)
     354             :                 strcat(out, "RSRC_DTOR, ");
     355             :         if (close_options & PHP_STREAM_FREE_PERSISTENT)
     356             :                 strcat(out, "PERSISTENT, ");
     357             :         if (close_options & PHP_STREAM_FREE_IGNORE_ENCLOSING)
     358             :                 strcat(out, "IGNORE_ENCLOSING, ");
     359             :         if (out[0] != '\0')
     360             :                 out[strlen(out) - 2] = '\0';
     361             :         return out;
     362             : }
     363             : #endif
     364             : 
     365          45 : static int _php_stream_free_persistent(zval *zv, void *pStream)
     366             : {
     367          45 :         zend_resource *le = Z_RES_P(zv);
     368          45 :         return le->ptr == pStream;
     369             : }
     370             : 
     371             : 
     372      333563 : PHPAPI int _php_stream_free(php_stream *stream, int close_options) /* {{{ */
     373             : {
     374      333563 :         int ret = 1;
     375      333563 :         int preserve_handle = close_options & PHP_STREAM_FREE_PRESERVE_HANDLE ? 1 : 0;
     376      333563 :         int release_cast = 1;
     377      333563 :         php_stream_context *context = NULL;
     378             : 
     379             :         /* on an resource list destruction, the context, another resource, may have
     380             :          * already been freed (if it was created after the stream resource), so
     381             :          * don't reference it */
     382      333563 :         if (EG(active)) {
     383      333389 :                 context = PHP_STREAM_CONTEXT(stream);
     384             :         }
     385             : 
     386      333563 :         if (stream->flags & PHP_STREAM_FLAG_NO_CLOSE) {
     387           0 :                 preserve_handle = 1;
     388             :         }
     389             : 
     390             : #if STREAM_DEBUG
     391             :         {
     392             :                 char out[200] = "";
     393             :                 fprintf(stderr, "stream_free: %s:%p[%s] in_free=%d opts=%s\n",
     394             :                         stream->ops->label, stream, stream->orig_path, stream->in_free, _php_stream_pretty_free_options(close_options, out));
     395             :         }
     396             : 
     397             : #endif
     398             : 
     399      333563 :         if (stream->in_free) {
     400             :                 /* hopefully called recursively from the enclosing stream; the pointer was NULLed below */
     401      111553 :                 if ((stream->in_free == 1) && (close_options & PHP_STREAM_FREE_IGNORE_ENCLOSING) && (stream->enclosing_stream == NULL)) {
     402          44 :                         close_options |= PHP_STREAM_FREE_RSRC_DTOR; /* restore flag */
     403             :                 } else {
     404      111465 :                         return 1; /* recursion protection */
     405             :                 }
     406             :         }
     407             : 
     408      222098 :         stream->in_free++;
     409             : 
     410             :         /* force correct order on enclosing/enclosed stream destruction (only from resource
     411             :          * destructor as in when reverse destroying the resource list) */
     412      551689 :         if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) &&
     413      109893 :                         !(close_options & PHP_STREAM_FREE_IGNORE_ENCLOSING) &&
     414      109849 :                         (close_options & (PHP_STREAM_FREE_CALL_DTOR | PHP_STREAM_FREE_RELEASE_STREAM)) && /* always? */
     415      109849 :                         (stream->enclosing_stream != NULL)) {
     416          44 :                 php_stream *enclosing_stream = stream->enclosing_stream;
     417          44 :                 stream->enclosing_stream = NULL;
     418             :                 /* we force PHP_STREAM_CALL_DTOR because that's from where the
     419             :                  * enclosing stream can free this stream. We remove rsrc_dtor because
     420             :                  * we want the enclosing stream to be deleted from the resource list */
     421          44 :                 return _php_stream_free(enclosing_stream,
     422             :                         (close_options | PHP_STREAM_FREE_CALL_DTOR) & ~PHP_STREAM_FREE_RSRC_DTOR);
     423             :         }
     424             : 
     425             :         /* if we are releasing the stream only (and preserving the underlying handle),
     426             :          * we need to do things a little differently.
     427             :          * We are only ever called like this when the stream is cast to a FILE*
     428             :          * for include (or other similar) purposes.
     429             :          * */
     430      222054 :         if (preserve_handle) {
     431           0 :                 if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) {
     432             :                         /* If the stream was fopencookied, we must NOT touch anything
     433             :                          * here, as the cookied stream relies on it all.
     434             :                          * Instead, mark the stream as OK to auto-clean */
     435           0 :                         php_stream_auto_cleanup(stream);
     436           0 :                         stream->in_free--;
     437           0 :                         return 0;
     438             :                 }
     439             :                 /* otherwise, make sure that we don't close the FILE* from a cast */
     440           0 :                 release_cast = 0;
     441             :         }
     442             : 
     443             : #if STREAM_DEBUG
     444             : fprintf(stderr, "stream_free: %s:%p[%s] preserve_handle=%d release_cast=%d remove_rsrc=%d\n",
     445             :                 stream->ops->label, stream, stream->orig_path, preserve_handle, release_cast,
     446             :                 (close_options & PHP_STREAM_FREE_RSRC_DTOR) == 0);
     447             : #endif
     448             : 
     449             :         /* make sure everything is saved */
     450      222054 :         _php_stream_flush(stream, 1);
     451             : 
     452             :         /* If not called from the resource dtor, remove the stream from the resource list. */
     453      222054 :         if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) == 0 && stream->res) {
     454             :                 /* zend_list_delete actually only decreases the refcount; if we're
     455             :                  * releasing the stream, we want to actually delete the resource from
     456             :                  * the resource list, otherwise the resource will point to invalid memory.
     457             :                  * In any case, let's always completely delete it from the resource list,
     458             :                  * not only when PHP_STREAM_FREE_RELEASE_STREAM is set */
     459             : //???           while (zend_list_delete(stream->res) == SUCCESS) {}
     460             : //???           stream->res->gc.refcount = 0;
     461      111460 :                 zend_list_close(stream->res);
     462      111460 :                 if (!stream->__exposed) {
     463       57371 :                         zend_list_delete(stream->res);
     464       57371 :                         stream->res = NULL;
     465             :                 }
     466             :         }
     467             : 
     468      222054 :         if (close_options & PHP_STREAM_FREE_CALL_DTOR) {
     469      222054 :                 if (release_cast && stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) {
     470             :                         /* calling fclose on an fopencookied stream will ultimately
     471             :                                 call this very same function.  If we were called via fclose,
     472             :                                 the cookie_closer unsets the fclose_stdiocast flags, so
     473             :                                 we can be sure that we only reach here when PHP code calls
     474             :                                 php_stream_free.
     475             :                                 Lets let the cookie code clean it all up.
     476             :                          */
     477           1 :                         stream->in_free = 0;
     478           1 :                         return fclose(stream->stdiocast);
     479             :                 }
     480             : 
     481      222053 :                 ret = stream->ops->close(stream, preserve_handle ? 0 : 1);
     482      222053 :                 stream->abstract = NULL;
     483             : 
     484             :                 /* tidy up any FILE* that might have been fdopened */
     485      222053 :                 if (release_cast && stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FDOPEN && stream->stdiocast) {
     486           0 :                         fclose(stream->stdiocast);
     487           0 :                         stream->stdiocast = NULL;
     488           0 :                         stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE;
     489             :                 }
     490             :         }
     491             : 
     492      222053 :         if (close_options & PHP_STREAM_FREE_RELEASE_STREAM) {
     493      444176 :                 while (stream->readfilters.head) {
     494          70 :                         php_stream_filter_remove(stream->readfilters.head, 1);
     495             :                 }
     496      444157 :                 while (stream->writefilters.head) {
     497          51 :                         php_stream_filter_remove(stream->writefilters.head, 1);
     498             :                 }
     499             : 
     500      222053 :                 if (stream->wrapper && stream->wrapper->wops && stream->wrapper->wops->stream_closer) {
     501           0 :                         stream->wrapper->wops->stream_closer(stream->wrapper, stream);
     502           0 :                         stream->wrapper = NULL;
     503             :                 }
     504             : 
     505      444106 :                 if (Z_TYPE(stream->wrapperdata) != IS_UNDEF) {
     506          94 :                         zval_ptr_dtor(&stream->wrapperdata);
     507          94 :                         ZVAL_UNDEF(&stream->wrapperdata);
     508             :                 }
     509             : 
     510      222053 :                 if (stream->readbuf) {
     511       50676 :                         pefree(stream->readbuf, stream->is_persistent);
     512       50676 :                         stream->readbuf = NULL;
     513             :                 }
     514             : 
     515      222053 :                 if (stream->is_persistent && (close_options & PHP_STREAM_FREE_PERSISTENT)) {
     516             :                         /* we don't work with *stream but need its value for comparison */
     517        1115 :                         zend_hash_apply_with_argument(&EG(persistent_list), _php_stream_free_persistent, stream);
     518             :                 }
     519             : #if ZEND_DEBUG
     520             :                 if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) && (stream->__exposed == 0) && (EG(error_reporting) & E_WARNING)) {
     521             :                         /* it leaked: Lets deliberately NOT pefree it so that the memory manager shows it
     522             :                          * as leaked; it will log a warning, but lets help it out and display what kind
     523             :                          * of stream it was. */
     524             :                         if (!CG(unclean_shutdown)) {
     525             :                                 char *leakinfo;
     526             :                                 spprintf(&leakinfo, 0, __FILE__ "(%d) : Stream of type '%s' %p (path:%s) was not closed\n", __LINE__, stream->ops->label, stream, stream->orig_path);
     527             : 
     528             :                                 if (stream->orig_path) {
     529             :                                         pefree(stream->orig_path, stream->is_persistent);
     530             :                                         stream->orig_path = NULL;
     531             :                                 }
     532             : 
     533             : # if defined(PHP_WIN32)
     534             :                                 OutputDebugString(leakinfo);
     535             : # else
     536             :                                 fprintf(stderr, "%s", leakinfo);
     537             : # endif
     538             :                                 efree(leakinfo);
     539             :                         }
     540             :                 } else {
     541             :                         if (stream->orig_path) {
     542             :                                 pefree(stream->orig_path, stream->is_persistent);
     543             :                                 stream->orig_path = NULL;
     544             :                         }
     545             : 
     546             :                         pefree(stream, stream->is_persistent);
     547             :                 }
     548             : #else
     549      222053 :                 if (stream->orig_path) {
     550      154065 :                         pefree(stream->orig_path, stream->is_persistent);
     551      154065 :                         stream->orig_path = NULL;
     552             :                 }
     553             : 
     554      222053 :                 pefree(stream, stream->is_persistent);
     555             : #endif
     556             :         }
     557             : 
     558      222053 :         if (context) {
     559        1031 :                 zend_list_delete(context->res);
     560             :         }
     561             : 
     562      222053 :         return ret;
     563             : }
     564             : /* }}} */
     565             : 
     566             : /* {{{ generic stream operations */
     567             : 
     568     1082183 : static void php_stream_fill_read_buffer(php_stream *stream, size_t size)
     569             : {
     570             :         /* allocate/fill the buffer */
     571             : 
     572     1082183 :         if (stream->readfilters.head) {
     573             :                 char *chunk_buf;
     574         103 :                 int err_flag = 0;
     575         103 :                 php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL };
     576         103 :                 php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap;
     577             : 
     578             :                 /* Invalidate the existing cache, otherwise reads can fail, see note in
     579             :                    main/streams/filter.c::_php_stream_filter_append */
     580         103 :                 stream->writepos = stream->readpos = 0;
     581             : 
     582             :                 /* allocate a buffer for reading chunks */
     583         103 :                 chunk_buf = emalloc(stream->chunk_size);
     584             : 
     585         270 :                 while (!stream->eof && !err_flag && (stream->writepos - stream->readpos < (off_t)size)) {
     586         103 :                         size_t justread = 0;
     587             :                         int flags;
     588             :                         php_stream_bucket *bucket;
     589         103 :                         php_stream_filter_status_t status = PSFS_ERR_FATAL;
     590             :                         php_stream_filter *filter;
     591             : 
     592             :                         /* read a chunk into a bucket */
     593         103 :                         justread = stream->ops->read(stream, chunk_buf, stream->chunk_size);
     594         167 :                         if (justread && justread != (size_t)-1) {
     595          64 :                                 bucket = php_stream_bucket_new(stream, chunk_buf, justread, 0, 0);
     596             : 
     597             :                                 /* after this call, bucket is owned by the brigade */
     598          64 :                                 php_stream_bucket_append(brig_inp, bucket);
     599             : 
     600          64 :                                 flags = PSFS_FLAG_NORMAL;
     601             :                         } else {
     602          39 :                                 flags = stream->eof ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC;
     603             :                         }
     604             : 
     605             :                         /* wind the handle... */
     606         192 :                         for (filter = stream->readfilters.head; filter; filter = filter->next) {
     607         113 :                                 status = filter->fops->filter(stream, filter, brig_inp, brig_outp, NULL, flags);
     608             : 
     609         113 :                                 if (status != PSFS_PASS_ON) {
     610          24 :                                         break;
     611             :                                 }
     612             : 
     613             :                                 /* brig_out becomes brig_in.
     614             :                                  * brig_in will always be empty here, as the filter MUST attach any un-consumed buckets
     615             :                                  * to its own brigade */
     616          89 :                                 brig_swap = brig_inp;
     617          89 :                                 brig_inp = brig_outp;
     618          89 :                                 brig_outp = brig_swap;
     619          89 :                                 memset(brig_outp, 0, sizeof(*brig_outp));
     620             :                         }
     621             : 
     622         103 :                         switch (status) {
     623             :                                 case PSFS_PASS_ON:
     624             :                                         /* we get here when the last filter in the chain has data to pass on.
     625             :                                          * in this situation, we are passing the brig_in brigade into the
     626             :                                          * stream read buffer */
     627         989 :                                         while (brig_inp->head) {
     628         831 :                                                 bucket = brig_inp->head;
     629             :                                                 /* grow buffer to hold this bucket
     630             :                                                  * TODO: this can fail for persistent streams */
     631         831 :                                                 if (stream->readbuflen - stream->writepos < bucket->buflen) {
     632         822 :                                                         stream->readbuflen += bucket->buflen;
     633        1644 :                                                         stream->readbuf = perealloc(stream->readbuf, stream->readbuflen,
     634             :                                                                         stream->is_persistent);
     635             :                                                 }
     636         831 :                                                 memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen);
     637         831 :                                                 stream->writepos += bucket->buflen;
     638             : 
     639         831 :                                                 php_stream_bucket_unlink(bucket);
     640         831 :                                                 php_stream_bucket_delref(bucket);
     641             :                                         }
     642          79 :                                         break;
     643             : 
     644             :                                 case PSFS_FEED_ME:
     645             :                                         /* when a filter needs feeding, there is no brig_out to deal with.
     646             :                                          * we simply continue the loop; if the caller needs more data,
     647             :                                          * we will read again, otherwise out job is done here */
     648           7 :                                         if (justread == 0) {
     649             :                                                 /* there is no data */
     650           7 :                                                 err_flag = 1;
     651           7 :                                                 break;
     652             :                                         }
     653           0 :                                         continue;
     654             : 
     655             :                                 case PSFS_ERR_FATAL:
     656             :                                         /* some fatal error. Theoretically, the stream is borked, so all
     657             :                                          * further reads should fail. */
     658          17 :                                         err_flag = 1;
     659             :                                         break;
     660             :                         }
     661             : 
     662         103 :                         if (justread == 0 || justread == (size_t)-1) {
     663             :                                 break;
     664             :                         }
     665             :                 }
     666             : 
     667         103 :                 efree(chunk_buf);
     668             : 
     669             :         } else {
     670             :                 /* is there enough data in the buffer ? */
     671     1082080 :                 if (stream->writepos - stream->readpos < (zend_off_t)size) {
     672     1082078 :                         size_t justread = 0;
     673             : 
     674             :                         /* reduce buffer memory consumption if possible, to avoid a realloc */
     675     1082078 :                         if (stream->readbuf && stream->readbuflen - stream->writepos < stream->chunk_size) {
     676      977028 :                                 memmove(stream->readbuf, stream->readbuf + stream->readpos, stream->readbuflen - stream->readpos);
     677      977028 :                                 stream->writepos -= stream->readpos;
     678      977028 :                                 stream->readpos = 0;
     679             :                         }
     680             : 
     681             :                         /* grow the buffer if required
     682             :                          * TODO: this can fail for persistent streams */
     683     1082078 :                         if (stream->readbuflen - stream->writepos < stream->chunk_size) {
     684       52777 :                                 stream->readbuflen += stream->chunk_size;
     685      105554 :                                 stream->readbuf = perealloc(stream->readbuf, stream->readbuflen,
     686             :                                                 stream->is_persistent);
     687             :                         }
     688             : 
     689     1082078 :                         justread = stream->ops->read(stream, (char*)stream->readbuf + stream->writepos,
     690             :                                         stream->readbuflen - stream->writepos
     691             :                                         );
     692             : 
     693     1082078 :                         if (justread != (size_t)-1) {
     694     1081774 :                                 stream->writepos += justread;
     695             :                         }
     696             :                 }
     697             :         }
     698     1082183 : }
     699             : 
     700     2168759 : PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size)
     701             : {
     702     2168759 :         size_t toread = 0, didread = 0;
     703             : 
     704     4409253 :         while (size > 0) {
     705             : 
     706             :                 /* take from the read buffer first.
     707             :                  * It is possible that a buffered stream was switched to non-buffered, so we
     708             :                  * drain the remainder of the buffer before using the "raw" read mode for
     709             :                  * the excess */
     710     2174484 :                 if (stream->writepos > stream->readpos) {
     711             : 
     712     1173580 :                         toread = stream->writepos - stream->readpos;
     713     1173580 :                         if (toread > size) {
     714     1038638 :                                 toread = size;
     715             :                         }
     716             : 
     717     1173580 :                         memcpy(buf, stream->readbuf + stream->readpos, toread);
     718     1173580 :                         stream->readpos += toread;
     719     1173580 :                         size -= toread;
     720     1173580 :                         buf += toread;
     721     1173580 :                         didread += toread;
     722             :                 }
     723             : 
     724             :                 /* ignore eof here; the underlying state might have changed */
     725     2174484 :                 if (size == 0) {
     726     1092774 :                         break;
     727             :                 }
     728             : 
     729     1122311 :                 if (!stream->readfilters.head && (stream->flags & PHP_STREAM_FLAG_NO_BUFFER || stream->chunk_size == 1)) {
     730       49195 :                         toread = stream->ops->read(stream, buf, size);
     731       49195 :                         if (toread == (size_t) -1) {
     732             :                                 /* e.g. underlying read(2) returned -1 */
     733        8594 :                                 break;
     734             :                         }
     735             :                 } else {
     736     1032515 :                         php_stream_fill_read_buffer(stream, size);
     737             : 
     738     1032515 :                         toread = stream->writepos - stream->readpos;
     739     1032515 :                         if (toread > size) {
     740      144734 :                                 toread = size;
     741             :                         }
     742             : 
     743     1032515 :                         if (toread > 0) {
     744      991413 :                                 memcpy(buf, stream->readbuf + stream->readpos, toread);
     745      991413 :                                 stream->readpos += toread;
     746             :                         }
     747             :                 }
     748     1073116 :                 if (toread > 0) {
     749     1030817 :                         didread += toread;
     750     1030817 :                         buf += toread;
     751     1030817 :                         size -= toread;
     752             :                 } else {
     753             :                         /* EOF, or temporary end of data (for non-blocking mode). */
     754       42299 :                         break;
     755             :                 }
     756             : 
     757             :                 /* just break anyway, to avoid greedy read */
     758     1030817 :                 if (stream->wrapper != &php_plain_files_wrapper) {
     759      959082 :                         break;
     760             :                 }
     761             :         }
     762             : 
     763     2168759 :         if (didread > 0) {
     764     2126126 :                 stream->position += didread;
     765             :         }
     766             : 
     767     2168759 :         return didread;
     768             : }
     769             : 
     770     1170557 : PHPAPI int _php_stream_eof(php_stream *stream)
     771             : {
     772             :         /* if there is data in the buffer, it's not EOF */
     773     1170557 :         if (stream->writepos - stream->readpos > 0) {
     774     1133479 :                 return 0;
     775             :         }
     776             : 
     777             :         /* use the configured timeout when checking eof */
     778       37078 :         if (!stream->eof && PHP_STREAM_OPTION_RETURN_ERR ==
     779             :                         php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS,
     780             :                         0, NULL)) {
     781           4 :                 stream->eof = 1;
     782             :         }
     783             : 
     784       37078 :         return stream->eof;
     785             : }
     786             : 
     787          48 : PHPAPI int _php_stream_putc(php_stream *stream, int c)
     788             : {
     789          48 :         unsigned char buf = c;
     790             : 
     791          48 :         if (php_stream_write(stream, (char*)&buf, 1) > 0) {
     792          48 :                 return 1;
     793             :         }
     794           0 :         return EOF;
     795             : }
     796             : 
     797      537801 : PHPAPI int _php_stream_getc(php_stream *stream)
     798             : {
     799             :         char buf;
     800             : 
     801      537801 :         if (php_stream_read(stream, &buf, 1) > 0) {
     802      536356 :                 return buf & 0xff;
     803             :         }
     804        1445 :         return EOF;
     805             : }
     806             : 
     807           0 : PHPAPI int _php_stream_puts(php_stream *stream, const char *buf)
     808             : {
     809             :         size_t len;
     810           0 :         char newline[2] = "\n"; /* is this OK for Win? */
     811           0 :         len = strlen(buf);
     812             : 
     813           0 :         if (len > 0 && php_stream_write(stream, buf, len) && php_stream_write(stream, newline, 1)) {
     814           0 :                 return 1;
     815             :         }
     816           0 :         return 0;
     817             : }
     818             : 
     819       44205 : PHPAPI int _php_stream_stat(php_stream *stream, php_stream_statbuf *ssb)
     820             : {
     821       44205 :         memset(ssb, 0, sizeof(*ssb));
     822             : 
     823             :         /* if the stream was wrapped, allow the wrapper to stat it */
     824       44205 :         if (stream->wrapper && stream->wrapper->wops->stream_stat != NULL) {
     825          12 :                 return stream->wrapper->wops->stream_stat(stream->wrapper, stream, ssb);
     826             :         }
     827             : 
     828             :         /* if the stream doesn't directly support stat-ing, return with failure.
     829             :          * We could try and emulate this by casting to a FD and fstat-ing it,
     830             :          * but since the fd might not represent the actual underlying content
     831             :          * this would give bogus results. */
     832       44193 :         if (stream->ops->stat == NULL) {
     833          17 :                 return -1;
     834             :         }
     835             : 
     836       44176 :         return (stream->ops->stat)(stream, ssb);
     837             : }
     838             : 
     839     1725600 : PHPAPI const char *php_stream_locate_eol(php_stream *stream, zend_string *buf)
     840             : {
     841             :         size_t avail;
     842     1725600 :         const char *cr, *lf, *eol = NULL;
     843             :         const char *readptr;
     844             : 
     845     1725600 :         if (!buf) {
     846     1725473 :                 readptr = (char*)stream->readbuf + stream->readpos;
     847     1725473 :                 avail = stream->writepos - stream->readpos;
     848             :         } else {
     849         127 :                 readptr = buf->val;
     850         127 :                 avail = buf->len;
     851             :         }
     852             : 
     853             :         /* Look for EOL */
     854     1725600 :         if (stream->flags & PHP_STREAM_FLAG_DETECT_EOL) {
     855           0 :                 cr = memchr(readptr, '\r', avail);
     856           0 :                 lf = memchr(readptr, '\n', avail);
     857             : 
     858           0 :                 if (cr && lf != cr + 1 && !(lf && lf < cr)) {
     859             :                         /* mac */
     860           0 :                         stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL;
     861           0 :                         stream->flags |= PHP_STREAM_FLAG_EOL_MAC;
     862           0 :                         eol = cr;
     863           0 :                 } else if ((cr && lf && cr == lf - 1) || (lf)) {
     864             :                         /* dos or unix endings */
     865           0 :                         stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL;
     866           0 :                         eol = lf;
     867             :                 }
     868     1725600 :         } else if (stream->flags & PHP_STREAM_FLAG_EOL_MAC) {
     869           0 :                 eol = memchr(readptr, '\r', avail);
     870             :         } else {
     871             :                 /* unix (and dos) line endings */
     872     1725600 :                 eol = memchr(readptr, '\n', avail);
     873             :         }
     874             : 
     875     1725600 :         return eol;
     876             : }
     877             : 
     878             : /* If buf == NULL, the buffer will be allocated automatically and will be of an
     879             :  * appropriate length to hold the line, regardless of the line length, memory
     880             :  * permitting */
     881     1723122 : PHPAPI char *_php_stream_get_line(php_stream *stream, char *buf, size_t maxlen,
     882             :                 size_t *returned_len)
     883             : {
     884     1723122 :         size_t avail = 0;
     885     1723122 :         size_t current_buf_size = 0;
     886     1723122 :         size_t total_copied = 0;
     887     1723122 :         int grow_mode = 0;
     888     1723122 :         char *bufstart = buf;
     889             : 
     890     1723122 :         if (buf == NULL) {
     891     1147730 :                 grow_mode = 1;
     892      575392 :         } else if (maxlen == 0) {
     893           0 :                 return NULL;
     894             :         }
     895             : 
     896             :         /*
     897             :          * If the underlying stream operations block when no new data is readable,
     898             :          * we need to take extra precautions.
     899             :          *
     900             :          * If there is buffered data available, we check for a EOL. If it exists,
     901             :          * we pass the data immediately back to the caller. This saves a call
     902             :          * to the read implementation and will not block where blocking
     903             :          * is not necessary at all.
     904             :          *
     905             :          * If the stream buffer contains more data than the caller requested,
     906             :          * we can also avoid that costly step and simply return that data.
     907             :          */
     908             : 
     909             :         for (;;) {
     910     1775397 :                 avail = stream->writepos - stream->readpos;
     911             : 
     912     1775397 :                 if (avail > 0) {
     913     1725473 :                         size_t cpysz = 0;
     914             :                         char *readptr;
     915             :                         const char *eol;
     916     1725473 :                         int done = 0;
     917             : 
     918     1725473 :                         readptr = (char*)stream->readbuf + stream->readpos;
     919     1725473 :                         eol = php_stream_locate_eol(stream, NULL);
     920             : 
     921     1725473 :                         if (eol) {
     922     1707541 :                                 cpysz = eol - readptr + 1;
     923     1707541 :                                 done = 1;
     924             :                         } else {
     925       17932 :                                 cpysz = avail;
     926             :                         }
     927             : 
     928     1725473 :                         if (grow_mode) {
     929             :                                 /* allow room for a NUL. If this realloc is really a realloc
     930             :                                  * (ie: second time around), we get an extra byte. In most
     931             :                                  * cases, with the default chunk size of 8K, we will only
     932             :                                  * incur that overhead once.  When people have lines longer
     933             :                                  * than 8K, we waste 1 byte per additional 8K or so.
     934             :                                  * That seems acceptable to me, to avoid making this code
     935             :                                  * hard to follow */
     936     1135433 :                                 bufstart = erealloc(bufstart, current_buf_size + cpysz + 1);
     937     1135433 :                                 current_buf_size += cpysz + 1;
     938     1135433 :                                 buf = bufstart + total_copied;
     939             :                         } else {
     940      590040 :                                 if (cpysz >= maxlen - 1) {
     941         575 :                                         cpysz = maxlen - 1;
     942         575 :                                         done = 1;
     943             :                                 }
     944             :                         }
     945             : 
     946     1725473 :                         memcpy(buf, readptr, cpysz);
     947             : 
     948     1725473 :                         stream->position += cpysz;
     949     1725473 :                         stream->readpos += cpysz;
     950     1725473 :                         buf += cpysz;
     951     1725473 :                         maxlen -= cpysz;
     952     1725473 :                         total_copied += cpysz;
     953             : 
     954     1725473 :                         if (done) {
     955     1707635 :                                 break;
     956             :                         }
     957       49924 :                 } else if (stream->eof) {
     958         306 :                         break;
     959             :                 } else {
     960             :                         /* XXX: Should be fine to always read chunk_size */
     961             :                         size_t toread;
     962             : 
     963       49618 :                         if (grow_mode) {
     964       29988 :                                 toread = stream->chunk_size;
     965             :                         } else {
     966       19630 :                                 toread = maxlen - 1;
     967       19630 :                                 if (toread > stream->chunk_size) {
     968           0 :                                         toread = stream->chunk_size;
     969             :                                 }
     970             :                         }
     971             : 
     972       49618 :                         php_stream_fill_read_buffer(stream, toread);
     973             : 
     974       49618 :                         if (stream->writepos - stream->readpos == 0) {
     975       15181 :                                 break;
     976             :                         }
     977             :                 }
     978       52275 :         }
     979             : 
     980     1723122 :         if (total_copied == 0) {
     981             :                 if (grow_mode) {
     982             :                         assert(bufstart == NULL);
     983             :                 }
     984       13576 :                 return NULL;
     985             :         }
     986             : 
     987     1709546 :         buf[0] = '\0';
     988     1709546 :         if (returned_len) {
     989     1707972 :                 *returned_len = total_copied;
     990             :         }
     991             : 
     992     1709546 :         return bufstart;
     993             : }
     994             : 
     995             : #define STREAM_BUFFERED_AMOUNT(stream) \
     996             :         ((size_t)(((stream)->writepos) - (stream)->readpos))
     997             : 
     998          90 : static const char *_php_stream_search_delim(php_stream *stream,
     999             :                                                                                         size_t maxlen,
    1000             :                                                                                         size_t skiplen,
    1001             :                                                                                         const char *delim, /* non-empty! */
    1002             :                                                                                         size_t delim_len)
    1003             : {
    1004             :         size_t  seek_len;
    1005             : 
    1006             :         /* set the maximum number of bytes we're allowed to read from buffer */
    1007          90 :         seek_len = MIN(STREAM_BUFFERED_AMOUNT(stream), maxlen);
    1008          90 :         if (seek_len <= skiplen) {
    1009          35 :                 return NULL;
    1010             :         }
    1011             : 
    1012          55 :         if (delim_len == 1) {
    1013          50 :                 return memchr(&stream->readbuf[stream->readpos + skiplen],
    1014          25 :                         delim[0], seek_len - skiplen);
    1015             :         } else {
    1016          90 :                 return php_memnstr((char*)&stream->readbuf[stream->readpos + skiplen],
    1017             :                                 delim, delim_len,
    1018          30 :                                 (char*)&stream->readbuf[stream->readpos + seek_len]);
    1019             :         }
    1020             : }
    1021             : 
    1022          56 : PHPAPI zend_string *php_stream_get_record(php_stream *stream, size_t maxlen, const char *delim, size_t delim_len)
    1023             : {
    1024             :         zend_string     *ret_buf;                               /* returned buffer */
    1025          56 :         const char *found_delim = NULL;
    1026             :         size_t  buffered_len,
    1027             :                         tent_ret_len;                   /* tentative returned length */
    1028          56 :         int     has_delim = delim_len > 0;
    1029             : 
    1030          56 :         if (maxlen == 0) {
    1031           0 :                 return NULL;
    1032             :         }
    1033             : 
    1034          56 :         if (has_delim) {
    1035          56 :                 found_delim = _php_stream_search_delim(
    1036             :                         stream, maxlen, 0, delim, delim_len);
    1037             :         }
    1038             : 
    1039          56 :         buffered_len = STREAM_BUFFERED_AMOUNT(stream);
    1040             :         /* try to read up to maxlen length bytes while we don't find the delim */
    1041         121 :         while (!found_delim && buffered_len < maxlen) {
    1042             :                 size_t  just_read,
    1043             :                                 to_read_now;
    1044             : 
    1045          50 :                 to_read_now = MIN(maxlen - buffered_len, stream->chunk_size);
    1046             : 
    1047          50 :                 php_stream_fill_read_buffer(stream, buffered_len + to_read_now);
    1048             : 
    1049          50 :                 just_read = STREAM_BUFFERED_AMOUNT(stream) - buffered_len;
    1050             : 
    1051             :                 /* Assume the stream is temporarily or permanently out of data */
    1052          50 :                 if (just_read == 0) {
    1053          16 :                         break;
    1054             :                 }
    1055             : 
    1056          34 :                 if (has_delim) {
    1057             :                         /* search for delimiter, but skip buffered_len (the number of bytes
    1058             :                          * buffered before this loop iteration), as they have already been
    1059             :                          * searched for the delimiter.
    1060             :                          * The left part of the delimiter may still remain in the buffer,
    1061             :                          * so subtract up to <delim_len - 1> from buffered_len, which is
    1062             :                          * the amount of data we skip on this search  as an optimization
    1063             :                          */
    1064          52 :                         found_delim = _php_stream_search_delim(
    1065             :                                 stream, maxlen,
    1066          34 :                                 buffered_len >= (delim_len - 1)
    1067          18 :                                                 ? buffered_len - (delim_len - 1)
    1068             :                                                 : 0,
    1069             :                                 delim, delim_len);
    1070          34 :                         if (found_delim) {
    1071          25 :                                 break;
    1072             :                         }
    1073             :                 }
    1074           9 :                 buffered_len += just_read;
    1075             :         }
    1076             : 
    1077          91 :         if (has_delim && found_delim) {
    1078          35 :                 tent_ret_len = found_delim - (char*)&stream->readbuf[stream->readpos];
    1079          21 :         } else if (!has_delim && STREAM_BUFFERED_AMOUNT(stream) >= maxlen) {
    1080           0 :                 tent_ret_len = maxlen;
    1081             :         } else {
    1082             :                 /* return with error if the delimiter string (if any) was not found, we
    1083             :                  * could not completely fill the read buffer with maxlen bytes and we
    1084             :                  * don't know we've reached end of file. Added with non-blocking streams
    1085             :                  * in mind, where this situation is frequent */
    1086          21 :                 if (STREAM_BUFFERED_AMOUNT(stream) < maxlen && !stream->eof) {
    1087           6 :                         return NULL;
    1088          15 :                 } else if (STREAM_BUFFERED_AMOUNT(stream) == 0 && stream->eof) {
    1089             :                         /* refuse to return an empty string just because by accident
    1090             :                          * we knew of EOF in a read that returned no data */
    1091           4 :                         return NULL;
    1092             :                 } else {
    1093          11 :                         tent_ret_len = MIN(STREAM_BUFFERED_AMOUNT(stream), maxlen);
    1094             :                 }
    1095             :         }
    1096             : 
    1097          46 :         ret_buf = zend_string_alloc(tent_ret_len, 0);
    1098             :         /* php_stream_read will not call ops->read here because the necessary
    1099             :          * data is guaranteedly buffered */
    1100          46 :         ret_buf->len = php_stream_read(stream, ret_buf->val, tent_ret_len);
    1101             : 
    1102          46 :         if (found_delim) {
    1103          35 :                 stream->readpos += delim_len;
    1104          35 :                 stream->position += delim_len;
    1105             :         }
    1106          46 :         ret_buf->val[ret_buf->len] = '\0';
    1107          46 :         return ret_buf;
    1108             : }
    1109             : 
    1110             : /* Writes a buffer directly to a stream, using multiple of the chunk size */
    1111      251932 : static size_t _php_stream_write_buffer(php_stream *stream, const char *buf, size_t count)
    1112             : {
    1113      251932 :         size_t didwrite = 0, towrite, justwrote;
    1114             : 
    1115             :         /* if we have a seekable stream we need to ensure that data is written at the
    1116             :          * current stream->position. This means invalidating the read buffer and then
    1117             :          * performing a low-level seek */
    1118      251932 :         if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0 && stream->readpos != stream->writepos) {
    1119           1 :                 stream->readpos = stream->writepos = 0;
    1120             : 
    1121           1 :                 stream->ops->seek(stream, stream->position, SEEK_SET, &stream->position);
    1122             :         }
    1123             : 
    1124             : 
    1125      757723 :         while (count > 0) {
    1126      253934 :                 towrite = count;
    1127      253934 :                 if (towrite > stream->chunk_size)
    1128        2003 :                         towrite = stream->chunk_size;
    1129             : 
    1130      253934 :                 justwrote = stream->ops->write(stream, buf, towrite);
    1131             : 
    1132             :                 /* convert justwrote to an integer, since normally it is unsigned */
    1133      253934 :                 if ((int)justwrote > 0) {
    1134      253859 :                         buf += justwrote;
    1135      253859 :                         count -= justwrote;
    1136      253859 :                         didwrite += justwrote;
    1137             : 
    1138             :                         /* Only screw with the buffer if we can seek, otherwise we lose data
    1139             :                          * buffered from fifos and sockets */
    1140      253859 :                         if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) {
    1141      208673 :                                 stream->position += justwrote;
    1142             :                         }
    1143             :                 } else {
    1144          75 :                         break;
    1145             :                 }
    1146             :         }
    1147      251932 :         return didwrite;
    1148             : 
    1149             : }
    1150             : 
    1151             : /* push some data through the write filter chain.
    1152             :  * buf may be NULL, if flags are set to indicate a flush.
    1153             :  * This may trigger a real write to the stream.
    1154             :  * Returns the number of bytes consumed from buf by the first filter in the chain.
    1155             :  * */
    1156         295 : static size_t _php_stream_write_filtered(php_stream *stream, const char *buf, size_t count, int flags)
    1157             : {
    1158         295 :         size_t consumed = 0;
    1159             :         php_stream_bucket *bucket;
    1160         295 :         php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL };
    1161         295 :         php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap;
    1162         295 :         php_stream_filter_status_t status = PSFS_ERR_FATAL;
    1163             :         php_stream_filter *filter;
    1164             : 
    1165         295 :         if (buf) {
    1166         162 :                 bucket = php_stream_bucket_new(stream, (char *)buf, count, 0, 0);
    1167         162 :                 php_stream_bucket_append(&brig_in, bucket);
    1168             :         }
    1169             : 
    1170         438 :         for (filter = stream->writefilters.head; filter; filter = filter->next) {
    1171             :                 /* for our return value, we are interested in the number of bytes consumed from
    1172             :                  * the first filter in the chain */
    1173         614 :                 status = filter->fops->filter(stream, filter, brig_inp, brig_outp,
    1174         307 :                                 filter == stream->writefilters.head ? &consumed : NULL, flags);
    1175             : 
    1176         307 :                 if (status != PSFS_PASS_ON) {
    1177         164 :                         break;
    1178             :                 }
    1179             :                 /* brig_out becomes brig_in.
    1180             :                  * brig_in will always be empty here, as the filter MUST attach any un-consumed buckets
    1181             :                  * to its own brigade */
    1182         143 :                 brig_swap = brig_inp;
    1183         143 :                 brig_inp = brig_outp;
    1184         143 :                 brig_outp = brig_swap;
    1185         143 :                 memset(brig_outp, 0, sizeof(*brig_outp));
    1186             :         }
    1187             : 
    1188         295 :         switch (status) {
    1189             :                 case PSFS_PASS_ON:
    1190             :                         /* filter chain generated some output; push it through to the
    1191             :                          * underlying stream */
    1192         390 :                         while (brig_inp->head) {
    1193         128 :                                 bucket = brig_inp->head;
    1194         128 :                                 _php_stream_write_buffer(stream, bucket->buf, bucket->buflen);
    1195             :                                 /* Potential error situation - eg: no space on device. Perhaps we should keep this brigade
    1196             :                                  * hanging around and try to write it later.
    1197             :                                  * At the moment, we just drop it on the floor
    1198             :                                  * */
    1199             : 
    1200         128 :                                 php_stream_bucket_unlink(bucket);
    1201         128 :                                 php_stream_bucket_delref(bucket);
    1202             :                         }
    1203             :                         break;
    1204             :                 case PSFS_FEED_ME:
    1205             :                         /* need more data before we can push data through to the stream */
    1206             :                         break;
    1207             : 
    1208             :                 case PSFS_ERR_FATAL:
    1209             :                         /* some fatal error.  Theoretically, the stream is borked, so all
    1210             :                          * further writes should fail. */
    1211             :                         break;
    1212             :         }
    1213             : 
    1214         295 :         return consumed;
    1215             : }
    1216             : 
    1217      224388 : PHPAPI int _php_stream_flush(php_stream *stream, int closing)
    1218             : {
    1219      224388 :         int ret = 0;
    1220             : 
    1221      224388 :         if (stream->writefilters.head) {
    1222         133 :                 _php_stream_write_filtered(stream, NULL, 0, closing ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC );
    1223             :         }
    1224             : 
    1225      224388 :         if (stream->ops->flush) {
    1226      223531 :                 ret = stream->ops->flush(stream);
    1227             :         }
    1228             : 
    1229      224388 :         return ret;
    1230             : }
    1231             : 
    1232      252460 : PHPAPI size_t _php_stream_write(php_stream *stream, const char *buf, size_t count)
    1233             : {
    1234      252460 :         if (buf == NULL || count == 0 || stream->ops->write == NULL) {
    1235         494 :                 return 0;
    1236             :         }
    1237             : 
    1238      251966 :         if (stream->writefilters.head) {
    1239         162 :                 return _php_stream_write_filtered(stream, buf, count, PSFS_FLAG_NORMAL);
    1240             :         } else {
    1241      251804 :                 return _php_stream_write_buffer(stream, buf, count);
    1242             :         }
    1243             : }
    1244             : 
    1245         153 : PHPAPI size_t _php_stream_printf(php_stream *stream, const char *fmt, ...)
    1246             : {
    1247             :         size_t count;
    1248             :         char *buf;
    1249             :         va_list ap;
    1250             : 
    1251         153 :         va_start(ap, fmt);
    1252         153 :         count = vspprintf(&buf, 0, fmt, ap);
    1253         153 :         va_end(ap);
    1254             : 
    1255         153 :         if (!buf) {
    1256           0 :                 return 0; /* error condition */
    1257             :         }
    1258             : 
    1259         153 :         count = php_stream_write(stream, buf, count);
    1260         153 :         efree(buf);
    1261             : 
    1262         153 :         return count;
    1263             : }
    1264             : 
    1265       63071 : PHPAPI zend_off_t _php_stream_tell(php_stream *stream)
    1266             : {
    1267       63071 :         return stream->position;
    1268             : }
    1269             : 
    1270       80366 : PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence)
    1271             : {
    1272       80366 :         if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) {
    1273             :                 /* flush to commit data written to the fopencookie FILE* */
    1274           1 :                 fflush(stream->stdiocast);
    1275             :         }
    1276             : 
    1277             :         /* handle the case where we are in the buffer */
    1278       80366 :         if ((stream->flags & PHP_STREAM_FLAG_NO_BUFFER) == 0) {
    1279       79766 :                 switch(whence) {
    1280             :                         case SEEK_CUR:
    1281       18097 :                                 if (offset > 0 && offset <= stream->writepos - stream->readpos) {
    1282         441 :                                         stream->readpos += offset; /* if offset = ..., then readpos = writepos */
    1283         441 :                                         stream->position += offset;
    1284         441 :                                         stream->eof = 0;
    1285         441 :                                         return 0;
    1286             :                                 }
    1287       17656 :                                 break;
    1288             :                         case SEEK_SET:
    1289       61823 :                                 if (offset > stream->position &&
    1290        2478 :                                                 offset <= stream->position + stream->writepos - stream->readpos) {
    1291         831 :                                         stream->readpos += offset - stream->position;
    1292         831 :                                         stream->position = offset;
    1293         831 :                                         stream->eof = 0;
    1294         831 :                                         return 0;
    1295             :                                 }
    1296             :                                 break;
    1297             :                 }
    1298             :         }
    1299             : 
    1300             : 
    1301       79094 :         if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) {
    1302             :                 int ret;
    1303             : 
    1304       79091 :                 if (stream->writefilters.head) {
    1305           6 :                         _php_stream_flush(stream, 0);
    1306             :                 }
    1307             : 
    1308       79091 :                 switch(whence) {
    1309             :                         case SEEK_CUR:
    1310       17664 :                                 offset = stream->position + offset;
    1311       17664 :                                 whence = SEEK_SET;
    1312             :                                 break;
    1313             :                 }
    1314       79091 :                 ret = stream->ops->seek(stream, offset, whence, &stream->position);
    1315             : 
    1316       79091 :                 if (((stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) || ret == 0) {
    1317       79091 :                         if (ret == 0) {
    1318       78879 :                                 stream->eof = 0;
    1319             :                         }
    1320             : 
    1321             :                         /* invalidate the buffer contents */
    1322       79091 :                         stream->readpos = stream->writepos = 0;
    1323             : 
    1324       79091 :                         return ret;
    1325             :                 }
    1326             :                 /* else the stream has decided that it can't support seeking after all;
    1327             :                  * fall through to attempt emulation */
    1328             :         }
    1329             : 
    1330             :         /* emulate forward moving seeks with reads */
    1331           3 :         if (whence == SEEK_CUR && offset >= 0) {
    1332             :                 char tmp[1024];
    1333             :                 size_t didread;
    1334           2 :                 while(offset > 0) {
    1335           0 :                         if ((didread = php_stream_read(stream, tmp, MIN(offset, sizeof(tmp)))) == 0) {
    1336           0 :                                 return -1;
    1337             :                         }
    1338           0 :                         offset -= didread;
    1339             :                 }
    1340           1 :                 stream->eof = 0;
    1341           1 :                 return 0;
    1342             :         }
    1343             : 
    1344           2 :         php_error_docref(NULL, E_WARNING, "stream does not support seeking");
    1345             : 
    1346           2 :         return -1;
    1347             : }
    1348             : 
    1349      120152 : PHPAPI int _php_stream_set_option(php_stream *stream, int option, int value, void *ptrparam)
    1350             : {
    1351      120152 :         int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL;
    1352             : 
    1353      120152 :         if (stream->ops->set_option) {
    1354      119476 :                 ret = stream->ops->set_option(stream, option, value, ptrparam);
    1355             :         }
    1356             : 
    1357      120151 :         if (ret == PHP_STREAM_OPTION_RETURN_NOTIMPL) {
    1358       34124 :                 switch(option) {
    1359             :                         case PHP_STREAM_OPTION_SET_CHUNK_SIZE:
    1360             :                                 /* XXX chunk size itself is of size_t, that might be ok or not for a particular case*/
    1361           3 :                                 ret = stream->chunk_size > INT_MAX ? INT_MAX : (int)stream->chunk_size;
    1362           3 :                                 stream->chunk_size = value;
    1363           3 :                                 return ret;
    1364             : 
    1365             :                         case PHP_STREAM_OPTION_READ_BUFFER:
    1366             :                                 /* try to match the buffer mode as best we can */
    1367           9 :                                 if (value == PHP_STREAM_BUFFER_NONE) {
    1368           9 :                                         stream->flags |= PHP_STREAM_FLAG_NO_BUFFER;
    1369           0 :                                 } else if (stream->flags & PHP_STREAM_FLAG_NO_BUFFER) {
    1370           0 :                                         stream->flags ^= PHP_STREAM_FLAG_NO_BUFFER;
    1371             :                                 }
    1372           9 :                                 ret = PHP_STREAM_OPTION_RETURN_OK;
    1373             :                                 break;
    1374             : 
    1375             :                         default:
    1376             :                                 ;
    1377             :                 }
    1378             :         }
    1379             : 
    1380      120148 :         return ret;
    1381             : }
    1382             : 
    1383         407 : PHPAPI int _php_stream_truncate_set_size(php_stream *stream, size_t newsize)
    1384             : {
    1385         407 :         return php_stream_set_option(stream, PHP_STREAM_OPTION_TRUNCATE_API, PHP_STREAM_TRUNCATE_SET_SIZE, &newsize);
    1386             : }
    1387             : 
    1388         536 : PHPAPI size_t _php_stream_passthru(php_stream * stream STREAMS_DC)
    1389             : {
    1390         536 :         size_t bcount = 0;
    1391             :         char buf[8192];
    1392             :         size_t b;
    1393             : 
    1394         536 :         if (php_stream_mmap_possible(stream)) {
    1395             :                 char *p;
    1396             :                 size_t mapped;
    1397             : 
    1398         476 :                 p = php_stream_mmap_range(stream, php_stream_tell(stream), PHP_STREAM_MMAP_ALL, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
    1399             : 
    1400         476 :                 if (p) {
    1401             :                         do {
    1402             :                                 /* output functions return int, so pass in int max */
    1403         370 :                                 if (0 < (b = PHPWRITE(p, MIN(mapped - bcount, INT_MAX)))) {
    1404         370 :                                         bcount += b;
    1405             :                                 }
    1406         370 :                         } while (b > 0 && mapped > bcount);
    1407             : 
    1408         370 :                         php_stream_mmap_unmap_ex(stream, mapped);
    1409             : 
    1410         370 :                         return bcount;
    1411             :                 }
    1412             :         }
    1413             : 
    1414         409 :         while ((b = php_stream_read(stream, buf, sizeof(buf))) > 0) {
    1415          77 :                 PHPWRITE(buf, b);
    1416          77 :                 bcount += b;
    1417             :         }
    1418             : 
    1419         166 :         return bcount;
    1420             : }
    1421             : 
    1422             : 
    1423       12142 : PHPAPI zend_string *_php_stream_copy_to_mem(php_stream *src, size_t maxlen, int persistent STREAMS_DC)
    1424             : {
    1425       12142 :         size_t ret = 0;
    1426             :         char *ptr;
    1427       12142 :         size_t len = 0, max_len;
    1428       12142 :         int step = CHUNK_SIZE;
    1429       12142 :         int min_room = CHUNK_SIZE / 4;
    1430             :         php_stream_statbuf ssbuf;
    1431             :         zend_string *result;
    1432             : 
    1433       12142 :         if (maxlen == 0) {
    1434          10 :                 return STR_EMPTY_ALLOC();
    1435             :         }
    1436             : 
    1437       12132 :         if (maxlen == PHP_STREAM_COPY_ALL) {
    1438       12055 :                 maxlen = 0;
    1439             :         }
    1440             : 
    1441       12132 :         if (maxlen > 0) {
    1442          77 :                 result = zend_string_alloc(maxlen, persistent);
    1443          77 :                 ptr = result->val;
    1444         224 :                 while ((len < maxlen) && !php_stream_eof(src)) {
    1445          76 :                         ret = php_stream_read(src, ptr, maxlen - len);
    1446          76 :                         if (!ret) {
    1447           6 :                                 break;
    1448             :                         }
    1449          70 :                         len += ret;
    1450          70 :                         ptr += ret;
    1451             :                 }
    1452          77 :                 if (len) {
    1453          70 :                         *ptr = '\0';
    1454          70 :                         result->len = len;
    1455             :                 } else {
    1456             :                         zend_string_free(result);
    1457           7 :                         result = NULL;
    1458             :                 }
    1459          77 :                 return result;
    1460             :         }
    1461             : 
    1462             :         /* avoid many reallocs by allocating a good sized chunk to begin with, if
    1463             :          * we can.  Note that the stream may be filtered, in which case the stat
    1464             :          * result may be inaccurate, as the filter may inflate or deflate the
    1465             :          * number of bytes that we can read.  In order to avoid an upsize followed
    1466             :          * by a downsize of the buffer, overestimate by the step size (which is
    1467             :          * 2K).  */
    1468       14973 :         if (php_stream_stat(src, &ssbuf) == 0 && ssbuf.sb.st_size > 0) {
    1469        2918 :                 max_len = ssbuf.sb.st_size + step;
    1470             :         } else {
    1471        9137 :                 max_len = step;
    1472             :         }
    1473             : 
    1474       12055 :         result = zend_string_alloc(max_len, persistent);
    1475       12055 :         ptr = result->val;
    1476             : 
    1477       28989 :         while ((ret = php_stream_read(src, ptr, max_len - len)))        {
    1478        4879 :                 len += ret;
    1479        4879 :                 if (len + min_room >= max_len) {
    1480           8 :                         result = zend_string_realloc(result, max_len + step, persistent);
    1481           4 :                         max_len += step;
    1482           4 :                         ptr = result->val + len;
    1483             :                 } else {
    1484        4875 :                         ptr += ret;
    1485             :                 }
    1486             :         }
    1487       12055 :         if (len) {
    1488        3053 :                 result = zend_string_realloc(result, len, persistent);
    1489        3053 :                 result->val[len] = '\0';
    1490             :         } else {
    1491             :                 zend_string_free(result);
    1492        9002 :                 result = NULL;
    1493             :         }
    1494             : 
    1495       12055 :         return result;
    1496             : }
    1497             : 
    1498             : /* Returns SUCCESS/FAILURE and sets *len to the number of bytes moved */
    1499       33573 : PHPAPI int _php_stream_copy_to_stream_ex(php_stream *src, php_stream *dest, size_t maxlen, size_t *len STREAMS_DC)
    1500             : {
    1501             :         char buf[CHUNK_SIZE];
    1502             :         size_t readchunk;
    1503       33573 :         size_t haveread = 0;
    1504             :         size_t didread, didwrite, towrite;
    1505             :         size_t dummy;
    1506             :         php_stream_statbuf ssbuf;
    1507             : 
    1508       33573 :         if (!len) {
    1509        7381 :                 len = &dummy;
    1510             :         }
    1511             : 
    1512       33573 :         if (maxlen == 0) {
    1513       10683 :                 *len = 0;
    1514       10683 :                 return SUCCESS;
    1515             :         }
    1516             : 
    1517       22890 :         if (maxlen == PHP_STREAM_COPY_ALL) {
    1518       12486 :                 maxlen = 0;
    1519             :         }
    1520             : 
    1521       22890 :         if (php_stream_stat(src, &ssbuf) == 0) {
    1522       27013 :                 if (ssbuf.sb.st_size == 0
    1523             : #ifdef S_ISREG
    1524       27013 :                         && S_ISREG(ssbuf.sb.st_mode)
    1525             : #endif
    1526             :                 ) {
    1527        4123 :                         *len = 0;
    1528        4123 :                         return SUCCESS;
    1529             :                 }
    1530             :         }
    1531             : 
    1532       18767 :         if (php_stream_mmap_possible(src)) {
    1533             :                 char *p;
    1534             :                 size_t mapped;
    1535             : 
    1536       18743 :                 p = php_stream_mmap_range(src, php_stream_tell(src), maxlen, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
    1537             : 
    1538       18743 :                 if (p) {
    1539       14180 :                         didwrite = php_stream_write(dest, p, mapped);
    1540             : 
    1541       14180 :                         php_stream_mmap_unmap_ex(src, mapped);
    1542             : 
    1543       14180 :                         *len = didwrite;
    1544             : 
    1545             :                         /* we've got at least 1 byte to read
    1546             :                          * less than 1 is an error
    1547             :                          * AND read bytes match written */
    1548       14180 :                         if (mapped > 0 && mapped == didwrite) {
    1549       14180 :                                 return SUCCESS;
    1550             :                         }
    1551           0 :                         return FAILURE;
    1552             :                 }
    1553             :         }
    1554             : 
    1555             :         while(1) {
    1556        4620 :                 readchunk = sizeof(buf);
    1557             : 
    1558        4620 :                 if (maxlen && (maxlen - haveread) < readchunk) {
    1559        4579 :                         readchunk = maxlen - haveread;
    1560             :                 }
    1561             : 
    1562        4620 :                 didread = php_stream_read(src, buf, readchunk);
    1563             : 
    1564        4620 :                 if (didread) {
    1565             :                         /* extra paranoid */
    1566             :                         char *writeptr;
    1567             : 
    1568        4609 :                         towrite = didread;
    1569        4609 :                         writeptr = buf;
    1570        4609 :                         haveread += didread;
    1571             : 
    1572       13827 :                         while(towrite) {
    1573        4609 :                                 didwrite = php_stream_write(dest, writeptr, towrite);
    1574        4609 :                                 if (didwrite == 0) {
    1575           0 :                                         *len = haveread - (didread - towrite);
    1576           0 :                                         return FAILURE;
    1577             :                                 }
    1578             : 
    1579        4609 :                                 towrite -= didwrite;
    1580        4609 :                                 writeptr += didwrite;
    1581             :                         }
    1582             :                 } else {
    1583          11 :                         break;
    1584             :                 }
    1585             : 
    1586        4609 :                 if (maxlen - haveread == 0) {
    1587        4576 :                         break;
    1588             :                 }
    1589          33 :         }
    1590             : 
    1591        4587 :         *len = haveread;
    1592             : 
    1593             :         /* we've got at least 1 byte to read.
    1594             :          * less than 1 is an error */
    1595             : 
    1596        4587 :         if (haveread > 0 || src->eof) {
    1597        4587 :                 return SUCCESS;
    1598             :         }
    1599           0 :         return FAILURE;
    1600             : }
    1601             : 
    1602             : /* Returns the number of bytes moved.
    1603             :  * Returns 1 when source len is 0.
    1604             :  * Deprecated in favor of php_stream_copy_to_stream_ex() */
    1605             : ZEND_ATTRIBUTE_DEPRECATED
    1606           0 : PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size_t maxlen STREAMS_DC)
    1607             : {
    1608             :         size_t len;
    1609           0 :         int ret = _php_stream_copy_to_stream_ex(src, dest, maxlen, &len STREAMS_REL_CC);
    1610           0 :         if (ret == SUCCESS && len == 0 && maxlen != 0) {
    1611           0 :                 return 1;
    1612             :         }
    1613           0 :         return len;
    1614             : }
    1615             : /* }}} */
    1616             : 
    1617             : /* {{{ wrapper init and registration */
    1618             : 
    1619      220149 : static void stream_resource_regular_dtor(zend_resource *rsrc)
    1620             : {
    1621      220149 :         php_stream *stream = (php_stream*)rsrc->ptr;
    1622             :         /* set the return value for pclose */
    1623      220149 :         FG(pclose_ret) = php_stream_free(stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
    1624      220149 : }
    1625             : 
    1626          29 : static void stream_resource_persistent_dtor(zend_resource *rsrc)
    1627             : {
    1628          29 :         php_stream *stream = (php_stream*)rsrc->ptr;
    1629          29 :         FG(pclose_ret) = php_stream_free(stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
    1630          29 : }
    1631             : 
    1632       20864 : void php_shutdown_stream_hashes(void)
    1633             : {
    1634       20864 :         if (FG(stream_wrappers)) {
    1635          42 :                 zend_hash_destroy(FG(stream_wrappers));
    1636          42 :                 efree(FG(stream_wrappers));
    1637          42 :                 FG(stream_wrappers) = NULL;
    1638             :         }
    1639             : 
    1640       20864 :         if (FG(stream_filters)) {
    1641          10 :                 zend_hash_destroy(FG(stream_filters));
    1642          10 :                 efree(FG(stream_filters));
    1643          10 :                 FG(stream_filters) = NULL;
    1644             :         }
    1645             : 
    1646       20864 :     if (FG(wrapper_errors)) {
    1647          67 :                 zend_hash_destroy(FG(wrapper_errors));
    1648          67 :                 efree(FG(wrapper_errors));
    1649          67 :                 FG(wrapper_errors) = NULL;
    1650             :     }
    1651       20864 : }
    1652             : 
    1653       20871 : int php_init_stream_wrappers(int module_number)
    1654             : {
    1655       20871 :         le_stream = zend_register_list_destructors_ex(stream_resource_regular_dtor, NULL, "stream", module_number);
    1656       20871 :         le_pstream = zend_register_list_destructors_ex(NULL, stream_resource_persistent_dtor, "persistent stream", module_number);
    1657             : 
    1658             :         /* Filters are cleaned up by the streams they're attached to */
    1659       20871 :         le_stream_filter = zend_register_list_destructors_ex(NULL, NULL, "stream filter", module_number);
    1660             : 
    1661       20871 :         zend_hash_init(&url_stream_wrappers_hash, 8, NULL, NULL, 1);
    1662       20871 :         zend_hash_init(php_get_stream_filters_hash_global(), 8, NULL, NULL, 1);
    1663       20871 :         zend_hash_init(php_stream_xport_get_hash(), 8, NULL, NULL, 1);
    1664             : 
    1665       83484 :         return (php_stream_xport_register("tcp", php_stream_generic_socket_factory) == SUCCESS
    1666       20871 :                         &&
    1667             :                         php_stream_xport_register("udp", php_stream_generic_socket_factory) == SUCCESS
    1668             : #if defined(AF_UNIX) && !(defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE))
    1669       20871 :                         &&
    1670             :                         php_stream_xport_register("unix", php_stream_generic_socket_factory) == SUCCESS
    1671       20871 :                         &&
    1672       20871 :                         php_stream_xport_register("udg", php_stream_generic_socket_factory) == SUCCESS
    1673             : #endif
    1674             :                 ) ? SUCCESS : FAILURE;
    1675             : }
    1676             : 
    1677       20905 : int php_shutdown_stream_wrappers(int module_number)
    1678             : {
    1679       20905 :         zend_hash_destroy(&url_stream_wrappers_hash);
    1680       20905 :         zend_hash_destroy(php_get_stream_filters_hash_global());
    1681       20905 :         zend_hash_destroy(php_stream_xport_get_hash());
    1682       20905 :         return SUCCESS;
    1683             : }
    1684             : 
    1685             : /* Validate protocol scheme names during registration
    1686             :  * Must conform to /^[a-zA-Z0-9+.-]+$/
    1687             :  */
    1688      250503 : static inline int php_stream_wrapper_scheme_validate(const char *protocol, unsigned int protocol_len)
    1689             : {
    1690             :         unsigned int i;
    1691             : 
    1692     1607388 :         for(i = 0; i < protocol_len; i++) {
    1693     1482111 :                 if (!isalnum((int)protocol[i]) &&
    1694       41742 :                         protocol[i] != '+' &&
    1695       41742 :                         protocol[i] != '-' &&
    1696       41742 :                         protocol[i] != '.') {
    1697           0 :                         return FAILURE;
    1698             :                 }
    1699             :         }
    1700             : 
    1701      250503 :         return SUCCESS;
    1702             : }
    1703             : 
    1704             : /* API for registering GLOBAL wrappers */
    1705      250452 : PHPAPI int php_register_url_stream_wrapper(const char *protocol, php_stream_wrapper *wrapper)
    1706             : {
    1707      250452 :         unsigned int protocol_len = (unsigned int)strlen(protocol);
    1708             : 
    1709      250452 :         if (php_stream_wrapper_scheme_validate(protocol, protocol_len) == FAILURE) {
    1710           0 :                 return FAILURE;
    1711             :         }
    1712             : 
    1713      500904 :         return zend_hash_str_add_ptr(&url_stream_wrappers_hash, protocol, protocol_len, wrapper) ? SUCCESS : FAILURE;
    1714             : }
    1715             : 
    1716      188145 : PHPAPI int php_unregister_url_stream_wrapper(const char *protocol)
    1717             : {
    1718      188145 :         return zend_hash_str_del(&url_stream_wrappers_hash, protocol, strlen(protocol));
    1719             : }
    1720             : 
    1721          42 : static void clone_wrapper_hash(void)
    1722             : {
    1723          42 :         ALLOC_HASHTABLE(FG(stream_wrappers));
    1724          42 :         zend_hash_init(FG(stream_wrappers), zend_hash_num_elements(&url_stream_wrappers_hash), NULL, NULL, 1);
    1725          42 :         zend_hash_copy(FG(stream_wrappers), &url_stream_wrappers_hash, NULL);
    1726          42 : }
    1727             : 
    1728             : /* API for registering VOLATILE wrappers */
    1729          51 : PHPAPI int php_register_url_stream_wrapper_volatile(const char *protocol, php_stream_wrapper *wrapper)
    1730             : {
    1731          51 :         unsigned int protocol_len = (unsigned int)strlen(protocol);
    1732             : 
    1733          51 :         if (php_stream_wrapper_scheme_validate(protocol, protocol_len) == FAILURE) {
    1734           0 :                 return FAILURE;
    1735             :         }
    1736             : 
    1737          51 :         if (!FG(stream_wrappers)) {
    1738          41 :                 clone_wrapper_hash();
    1739             :         }
    1740             : 
    1741         102 :         return zend_hash_str_add_ptr(FG(stream_wrappers), protocol, protocol_len, wrapper) ? SUCCESS : FAILURE;
    1742             : }
    1743             : 
    1744           2 : PHPAPI int php_unregister_url_stream_wrapper_volatile(const char *protocol)
    1745             : {
    1746           2 :         if (!FG(stream_wrappers)) {
    1747           1 :                 clone_wrapper_hash();
    1748             :         }
    1749             : 
    1750           2 :         return zend_hash_str_del(FG(stream_wrappers), protocol, strlen(protocol));
    1751             : }
    1752             : /* }}} */
    1753             : 
    1754             : /* {{{ php_stream_locate_url_wrapper */
    1755      494431 : PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const char **path_for_open, int options)
    1756             : {
    1757      494431 :         HashTable *wrapper_hash = (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash);
    1758      494431 :         php_stream_wrapper *wrapper = NULL;
    1759      494431 :         const char *p, *protocol = NULL;
    1760      494431 :         int n = 0;
    1761             : 
    1762      494431 :         if (path_for_open) {
    1763      265079 :                 *path_for_open = (char*)path;
    1764             :         }
    1765             : 
    1766      494431 :         if (options & IGNORE_URL) {
    1767        5805 :                 return (options & STREAM_LOCATE_WRAPPERS_ONLY) ? NULL : &php_plain_files_wrapper;
    1768             :         }
    1769             : 
    1770      712405 :         for (p = path; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) {
    1771      223779 :                 n++;
    1772             :         }
    1773             : 
    1774      552571 :         if ((*p == ':') && (n > 1) && (!strncmp("//", p+1, 2) || (n == 4 && !memcmp("data:", path, 5)))) {
    1775       63945 :                 protocol = path;
    1776      424681 :         } else if (n == 5 && strncasecmp(path, "zlib:", 5) == 0) {
    1777             :                 /* BC with older php scripts and zlib wrapper */
    1778           0 :                 protocol = "compress.zlib";
    1779           0 :                 n = 13;
    1780           0 :                 php_error_docref(NULL, E_WARNING, "Use of \"zlib:\" wrapper is deprecated; please use \"compress.zlib://\" instead");
    1781             :         }
    1782             : 
    1783      488626 :         if (protocol) {
    1784       63945 :                 char *tmp = estrndup(protocol, n);
    1785      127890 :                 if (NULL == (wrapper = zend_hash_str_find_ptr(wrapper_hash, (char*)tmp, n))) {
    1786           3 :                         php_strtolower(tmp, n);
    1787           6 :                         if (NULL == (wrapper = zend_hash_str_find_ptr(wrapper_hash, (char*)tmp, n))) {
    1788             :                                 char wrapper_name[32];
    1789             : 
    1790           2 :                                 if (n >= sizeof(wrapper_name)) {
    1791           0 :                                         n = sizeof(wrapper_name) - 1;
    1792             :                                 }
    1793           2 :                                 PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n);
    1794             : 
    1795           2 :                                 php_error_docref(NULL, E_WARNING, "Unable to find the wrapper \"%s\" - did you forget to enable it when you configured PHP?", wrapper_name);
    1796             : 
    1797           2 :                                 wrapper = NULL;
    1798           2 :                                 protocol = NULL;
    1799             :                         }
    1800             :                 }
    1801       63945 :                 efree(tmp);
    1802             :         }
    1803             :         /* TODO: curl based streams probably support file:// properly */
    1804      488626 :         if (!protocol || !strncasecmp(protocol, "file", n))   {
    1805             :                 /* fall back on regular file access */
    1806      424743 :                 php_stream_wrapper *plain_files_wrapper = &php_plain_files_wrapper;
    1807             : 
    1808      424743 :                 if (protocol) {
    1809          60 :                         int localhost = 0;
    1810             : 
    1811          60 :                         if (!strncasecmp(path, "file://localhost/", 17)) {
    1812           0 :                                 localhost = 1;
    1813             :                         }
    1814             : 
    1815             : #ifdef PHP_WIN32
    1816             :                         if (localhost == 0 && path[n+3] != '\0' && path[n+3] != '/' && path[n+4] != ':')        {
    1817             : #else
    1818          60 :                         if (localhost == 0 && path[n+3] != '\0' && path[n+3] != '/') {
    1819             : #endif
    1820          18 :                                 if (options & REPORT_ERRORS) {
    1821          12 :                                         php_error_docref(NULL, E_WARNING, "remote host file access not supported, %s", path);
    1822             :                                 }
    1823          18 :                                 return NULL;
    1824             :                         }
    1825             : 
    1826          42 :                         if (path_for_open) {
    1827             :                                 /* skip past protocol and :/, but handle windows correctly */
    1828          39 :                                 *path_for_open = (char*)path + n + 1;
    1829          39 :                                 if (localhost == 1) {
    1830           0 :                                         (*path_for_open) += 11;
    1831             :                                 }
    1832         119 :                                 while (*(++*path_for_open)=='/');
    1833             : #ifdef PHP_WIN32
    1834             :                                 if (*(*path_for_open + 1) != ':')
    1835             : #endif
    1836          39 :                                         (*path_for_open)--;
    1837             :                         }
    1838             :                 }
    1839             : 
    1840      424725 :                 if (options & STREAM_LOCATE_WRAPPERS_ONLY) {
    1841         578 :                         return NULL;
    1842             :                 }
    1843             : 
    1844      424147 :                 if (FG(stream_wrappers)) {
    1845             :                 /* The file:// wrapper may have been disabled/overridden */
    1846             : 
    1847          15 :                         if (wrapper) {
    1848             :                                 /* It was found so go ahead and provide it */
    1849           3 :                                 return wrapper;
    1850             :                         }
    1851             : 
    1852             :                         /* Check again, the original check might have not known the protocol name */
    1853          12 :                         if ((wrapper = zend_hash_str_find_ptr(wrapper_hash, "file", sizeof("file")-1)) != NULL) {
    1854          11 :                                 return wrapper;
    1855             :                         }
    1856             : 
    1857           1 :                         if (options & REPORT_ERRORS) {
    1858           1 :                                 php_error_docref(NULL, E_WARNING, "file:// wrapper is disabled in the server configuration");
    1859             :                         }
    1860           1 :                         return NULL;
    1861             :                 }
    1862             : 
    1863      424132 :                 return plain_files_wrapper;
    1864             :         }
    1865             : 
    1866       64434 :         if (wrapper && wrapper->is_url &&
    1867         140 :         (options & STREAM_DISABLE_URL_PROTECTION) == 0 &&
    1868         140 :             (!PG(allow_url_fopen) ||
    1869         135 :              (((options & STREAM_OPEN_FOR_INCLUDE) ||
    1870         136 :                PG(in_user_include)) && !PG(allow_url_include)))) {
    1871          10 :                 if (options & REPORT_ERRORS) {
    1872             :                         /* protocol[n] probably isn't '\0' */
    1873           7 :                         char *protocol_dup = estrndup(protocol, n);
    1874           7 :                         if (!PG(allow_url_fopen)) {
    1875           4 :                                 php_error_docref(NULL, E_WARNING, "%s:// wrapper is disabled in the server configuration by allow_url_fopen=0", protocol_dup);
    1876             :                         } else {
    1877           3 :                                 php_error_docref(NULL, E_WARNING, "%s:// wrapper is disabled in the server configuration by allow_url_include=0", protocol_dup);
    1878             :                         }
    1879           7 :                         efree(protocol_dup);
    1880             :                 }
    1881          10 :                 return NULL;
    1882             :         }
    1883             : 
    1884       63873 :         return wrapper;
    1885             : }
    1886             : /* }}} */
    1887             : 
    1888             : /* {{{ _php_stream_mkdir
    1889             :  */
    1890        1697 : PHPAPI int _php_stream_mkdir(const char *path, int mode, int options, php_stream_context *context)
    1891             : {
    1892        1697 :         php_stream_wrapper *wrapper = NULL;
    1893             : 
    1894        1697 :         wrapper = php_stream_locate_url_wrapper(path, NULL, 0);
    1895        1697 :         if (!wrapper || !wrapper->wops || !wrapper->wops->stream_mkdir) {
    1896           1 :                 return 0;
    1897             :         }
    1898             : 
    1899        1696 :         return wrapper->wops->stream_mkdir(wrapper, path, mode, options, context);
    1900             : }
    1901             : /* }}} */
    1902             : 
    1903             : /* {{{ _php_stream_rmdir
    1904             :  */
    1905        1718 : PHPAPI int _php_stream_rmdir(const char *path, int options, php_stream_context *context)
    1906             : {
    1907        1718 :         php_stream_wrapper *wrapper = NULL;
    1908             : 
    1909        1718 :         wrapper = php_stream_locate_url_wrapper(path, NULL, 0);
    1910        1718 :         if (!wrapper || !wrapper->wops || !wrapper->wops->stream_rmdir) {
    1911           1 :                 return 0;
    1912             :         }
    1913             : 
    1914        1717 :         return wrapper->wops->stream_rmdir(wrapper, path, options, context);
    1915             : }
    1916             : /* }}} */
    1917             : 
    1918             : /* {{{ _php_stream_stat_path */
    1919       62063 : PHPAPI int _php_stream_stat_path(const char *path, int flags, php_stream_statbuf *ssb, php_stream_context *context)
    1920             : {
    1921       62063 :         php_stream_wrapper *wrapper = NULL;
    1922       62063 :         const char *path_to_open = path;
    1923             :         int ret;
    1924             : 
    1925       62063 :         if (!(flags & PHP_STREAM_URL_STAT_NOCACHE)) {
    1926             :                 /* Try to hit the cache first */
    1927       61889 :                 if (flags & PHP_STREAM_URL_STAT_LINK) {
    1928        2638 :                         if (BG(CurrentLStatFile) && strcmp(path, BG(CurrentLStatFile)) == 0) {
    1929           8 :                                 memcpy(ssb, &BG(lssb), sizeof(php_stream_statbuf));
    1930           8 :                                 return 0;
    1931             :                         }
    1932             :                 } else {
    1933       59251 :                         if (BG(CurrentStatFile) && strcmp(path, BG(CurrentStatFile)) == 0) {
    1934        6054 :                                 memcpy(ssb, &BG(ssb), sizeof(php_stream_statbuf));
    1935        6054 :                                 return 0;
    1936             :                         }
    1937             :                 }
    1938             :         }
    1939             : 
    1940       56001 :         wrapper = php_stream_locate_url_wrapper(path, &path_to_open, 0);
    1941       56001 :         if (wrapper && wrapper->wops->url_stat) {
    1942       55984 :                 ret = wrapper->wops->url_stat(wrapper, path_to_open, flags, ssb, context);
    1943       55984 :                 if (ret == 0) {
    1944       54035 :                         if (!(flags & PHP_STREAM_URL_STAT_NOCACHE)) {
    1945             :                                 /* Drop into cache */
    1946       54024 :                                 if (flags & PHP_STREAM_URL_STAT_LINK) {
    1947        2608 :                                         if (BG(CurrentLStatFile)) {
    1948        2528 :                                                 efree(BG(CurrentLStatFile));
    1949             :                                         }
    1950        2608 :                                         BG(CurrentLStatFile) = estrdup(path);
    1951        2608 :                                         memcpy(&BG(lssb), ssb, sizeof(php_stream_statbuf));
    1952             :                                 } else {
    1953       51416 :                                         if (BG(CurrentStatFile)) {
    1954       29941 :                                                 efree(BG(CurrentStatFile));
    1955             :                                         }
    1956       51416 :                                         BG(CurrentStatFile) = estrdup(path);
    1957       51416 :                                         memcpy(&BG(ssb), ssb, sizeof(php_stream_statbuf));
    1958             :                                 }
    1959             :                         }
    1960             :                 }
    1961       55984 :                 return ret;
    1962             :         }
    1963          17 :         return -1;
    1964             : }
    1965             : /* }}} */
    1966             : 
    1967             : /* {{{ php_stream_opendir */
    1968        1421 : PHPAPI php_stream *_php_stream_opendir(const char *path, int options,
    1969             :                 php_stream_context *context STREAMS_DC)
    1970             : {
    1971        1421 :         php_stream *stream = NULL;
    1972        1421 :         php_stream_wrapper *wrapper = NULL;
    1973             :         const char *path_to_open;
    1974             : 
    1975        1421 :         if (!path || !*path) {
    1976          17 :                 return NULL;
    1977             :         }
    1978             : 
    1979        1404 :         path_to_open = path;
    1980             : 
    1981        1404 :         wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options);
    1982             : 
    1983        2807 :         if (wrapper && wrapper->wops->dir_opener) {
    1984        1403 :                 stream = wrapper->wops->dir_opener(wrapper,
    1985             :                                 path_to_open, "r", options ^ REPORT_ERRORS, NULL,
    1986             :                                 context STREAMS_REL_CC);
    1987             : 
    1988        1403 :                 if (stream) {
    1989        1309 :                         stream->wrapper = wrapper;
    1990        1309 :                         stream->flags |= PHP_STREAM_FLAG_NO_BUFFER | PHP_STREAM_FLAG_IS_DIR;
    1991             :                 }
    1992           1 :         } else if (wrapper) {
    1993           1 :                 php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS, "not implemented");
    1994             :         }
    1995        1404 :         if (stream == NULL && (options & REPORT_ERRORS)) {
    1996          95 :                 php_stream_display_wrapper_errors(wrapper, path, "failed to open dir");
    1997             :         }
    1998        1404 :         php_stream_tidy_wrapper_error_log(wrapper);
    1999             : 
    2000        1404 :         return stream;
    2001             : }
    2002             : /* }}} */
    2003             : 
    2004             : /* {{{ _php_stream_readdir */
    2005       39554 : PHPAPI php_stream_dirent *_php_stream_readdir(php_stream *dirstream, php_stream_dirent *ent)
    2006             : {
    2007             : 
    2008       39554 :         if (sizeof(php_stream_dirent) == php_stream_read(dirstream, (char*)ent, sizeof(php_stream_dirent))) {
    2009       38585 :                 return ent;
    2010             :         }
    2011             : 
    2012         969 :         return NULL;
    2013             : }
    2014             : /* }}} */
    2015             : 
    2016             : /* {{{ php_stream_open_wrapper_ex */
    2017      140738 : PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mode, int options,
    2018             :                 char **opened_path, php_stream_context *context STREAMS_DC)
    2019             : {
    2020      140738 :         php_stream *stream = NULL;
    2021      140738 :         php_stream_wrapper *wrapper = NULL;
    2022             :         const char *path_to_open;
    2023      140738 :         int persistent = options & STREAM_OPEN_PERSISTENT;
    2024      140738 :         char *resolved_path = NULL;
    2025      140738 :         char *copy_of_path = NULL;
    2026             : 
    2027      140738 :         if (opened_path) {
    2028       12880 :                 *opened_path = NULL;
    2029             :         }
    2030             : 
    2031      140738 :         if (!path || !*path) {
    2032          66 :                 php_error_docref(NULL, E_WARNING, "Filename cannot be empty");
    2033          66 :                 return NULL;
    2034             :         }
    2035             : 
    2036      140672 :         if (options & USE_PATH) {
    2037        9092 :                 resolved_path = zend_resolve_path(path, (int)strlen(path));
    2038        9092 :                 if (resolved_path) {
    2039        8730 :                         path = resolved_path;
    2040             :                         /* we've found this file, don't re-check include_path or run realpath */
    2041        8730 :                         options |= STREAM_ASSUME_REALPATH;
    2042        8730 :                         options &= ~USE_PATH;
    2043             :                 }
    2044             :         }
    2045             : 
    2046      140672 :         path_to_open = path;
    2047             : 
    2048      140672 :         wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options);
    2049      140672 :         if (options & STREAM_USE_URL && (!wrapper || !wrapper->is_url)) {
    2050           0 :                 php_error_docref(NULL, E_WARNING, "This function may only be used against URLs");
    2051           0 :                 if (resolved_path) {
    2052           0 :                         efree(resolved_path);
    2053             :                 }
    2054           0 :                 return NULL;
    2055             :         }
    2056             : 
    2057      140672 :         if (wrapper) {
    2058      140652 :                 if (!wrapper->wops->stream_opener) {
    2059           0 :                         php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS,
    2060             :                                         "wrapper does not support stream open");
    2061             :                 } else {
    2062      140652 :                         stream = wrapper->wops->stream_opener(wrapper,
    2063             :                                 path_to_open, mode, options ^ REPORT_ERRORS,
    2064             :                                 opened_path, context STREAMS_REL_CC);
    2065             :                 }
    2066             : 
    2067             :                 /* if the caller asked for a persistent stream but the wrapper did not
    2068             :                  * return one, force an error here */
    2069      140651 :                 if (stream && (options & STREAM_OPEN_PERSISTENT) && !stream->is_persistent) {
    2070           0 :                         php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS,
    2071             :                                         "wrapper does not support persistent streams");
    2072           0 :                         php_stream_close(stream);
    2073           0 :                         stream = NULL;
    2074             :                 }
    2075             : 
    2076      140651 :                 if (stream) {
    2077      139856 :                         stream->wrapper = wrapper;
    2078             :                 }
    2079             :         }
    2080             : 
    2081      140671 :         if (stream) {
    2082      139856 :                 if (opened_path && !*opened_path && resolved_path) {
    2083           2 :                         *opened_path = resolved_path;
    2084           2 :                         resolved_path = NULL;
    2085             :                 }
    2086      139856 :                 if (stream->orig_path) {
    2087           1 :                         pefree(stream->orig_path, persistent);
    2088             :                 }
    2089      139856 :                 copy_of_path = pestrdup(path, persistent);
    2090      139856 :                 stream->orig_path = copy_of_path;
    2091             : #if ZEND_DEBUG
    2092             :                 stream->open_filename = __zend_orig_filename ? __zend_orig_filename : __zend_filename;
    2093             :                 stream->open_lineno = __zend_orig_lineno ? __zend_orig_lineno : __zend_lineno;
    2094             : #endif
    2095             :         }
    2096             : 
    2097      140671 :         if (stream != NULL && (options & STREAM_MUST_SEEK)) {
    2098             :                 php_stream *newstream;
    2099             : 
    2100        9127 :                 switch(php_stream_make_seekable_rel(stream, &newstream,
    2101             :                                         (options & STREAM_WILL_CAST)
    2102             :                                                 ? PHP_STREAM_PREFER_STDIO : PHP_STREAM_NO_PREFERENCE)) {
    2103             :                         case PHP_STREAM_UNCHANGED:
    2104        9127 :                                 if (resolved_path) {
    2105          41 :                                         efree(resolved_path);
    2106             :                                 }
    2107        9127 :                                 return stream;
    2108             :                         case PHP_STREAM_RELEASED:
    2109           0 :                                 if (newstream->orig_path) {
    2110           0 :                                         pefree(newstream->orig_path, persistent);
    2111             :                                 }
    2112           0 :                                 newstream->orig_path = pestrdup(path, persistent);
    2113           0 :                                 if (resolved_path) {
    2114           0 :                                         efree(resolved_path);
    2115             :                                 }
    2116           0 :                                 return newstream;
    2117             :                         default:
    2118           0 :                                 php_stream_close(stream);
    2119           0 :                                 stream = NULL;
    2120           0 :                                 if (options & REPORT_ERRORS) {
    2121           0 :                                         char *tmp = estrdup(path);
    2122           0 :                                         php_strip_url_passwd(tmp);
    2123           0 :                                         php_error_docref1(NULL, tmp, E_WARNING, "could not make seekable - %s",
    2124             :                                                         tmp);
    2125           0 :                                         efree(tmp);
    2126             : 
    2127           0 :                                         options ^= REPORT_ERRORS;
    2128             :                                 }
    2129             :                 }
    2130             :         }
    2131             : 
    2132      131544 :         if (stream && stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0 && strchr(mode, 'a') && stream->position == 0) {
    2133        1564 :                 zend_off_t newpos = 0;
    2134             : 
    2135             :                 /* if opened for append, we need to revise our idea of the initial file position */
    2136        1564 :                 if (0 == stream->ops->seek(stream, 0, SEEK_CUR, &newpos)) {
    2137        1564 :                         stream->position = newpos;
    2138             :                 }
    2139             :         }
    2140             : 
    2141      131544 :         if (stream == NULL && (options & REPORT_ERRORS)) {
    2142         407 :                 php_stream_display_wrapper_errors(wrapper, path, "failed to open stream");
    2143         407 :                 if (opened_path && *opened_path) {
    2144           0 :                         efree(*opened_path);
    2145           0 :                         *opened_path = NULL;
    2146             :                 }
    2147             :         }
    2148      131544 :         php_stream_tidy_wrapper_error_log(wrapper);
    2149             : #if ZEND_DEBUG
    2150             :         if (stream == NULL && copy_of_path != NULL) {
    2151             :                 pefree(copy_of_path, persistent);
    2152             :         }
    2153             : #endif
    2154      131544 :         if (resolved_path) {
    2155        8687 :                 efree(resolved_path);
    2156             :         }
    2157      131544 :         return stream;
    2158             : }
    2159             : /* }}} */
    2160             : 
    2161             : /* {{{ context API */
    2162        3802 : PHPAPI php_stream_context *php_stream_context_set(php_stream *stream, php_stream_context *context)
    2163             : {
    2164        3802 :         php_stream_context *oldcontext = PHP_STREAM_CONTEXT(stream);
    2165             : 
    2166        3802 :         if (context) {
    2167         964 :                 stream->ctx = context->res;
    2168         964 :                 GC_REFCOUNT(context->res)++;
    2169             :         } else {
    2170        2838 :                 stream->ctx = NULL;
    2171             :         }
    2172        3802 :         if (oldcontext) {
    2173          59 :                 zend_list_delete(oldcontext->res);
    2174             :         }
    2175             : 
    2176        3802 :         return oldcontext;
    2177             : }
    2178             : 
    2179           0 : PHPAPI void php_stream_notification_notify(php_stream_context *context, int notifycode, int severity,
    2180             :                 char *xmsg, int xcode, size_t bytes_sofar, size_t bytes_max, void * ptr)
    2181             : {
    2182           0 :         if (context && context->notifier)
    2183           0 :                 context->notifier->func(context, notifycode, severity, xmsg, xcode, bytes_sofar, bytes_max, ptr);
    2184           0 : }
    2185             : 
    2186        4698 : PHPAPI void php_stream_context_free(php_stream_context *context)
    2187             : {
    2188        9396 :         if (Z_TYPE(context->options) != IS_UNDEF) {
    2189           0 :                 zval_ptr_dtor(&context->options);
    2190           0 :                 ZVAL_UNDEF(&context->options);
    2191             :         }
    2192        4698 :         if (context->notifier) {
    2193           1 :                 php_stream_notification_free(context->notifier);
    2194           1 :                 context->notifier = NULL;
    2195             :         }
    2196        4698 :         efree(context);
    2197        4698 : }
    2198             : 
    2199        4667 : PHPAPI php_stream_context *php_stream_context_alloc(void)
    2200             : {
    2201             :         php_stream_context *context;
    2202             : 
    2203        4667 :         context = ecalloc(1, sizeof(php_stream_context));
    2204        4667 :         context->notifier = NULL;
    2205        4667 :         array_init(&context->options);
    2206             : 
    2207        4667 :         context->res = zend_register_resource(context, php_le_stream_context());
    2208        4667 :         return context;
    2209             : }
    2210             : 
    2211           2 : PHPAPI php_stream_notifier *php_stream_notification_alloc(void)
    2212             : {
    2213           2 :         return ecalloc(1, sizeof(php_stream_notifier));
    2214             : }
    2215             : 
    2216           2 : PHPAPI void php_stream_notification_free(php_stream_notifier *notifier)
    2217             : {
    2218           2 :         if (notifier->dtor) {
    2219           2 :                 notifier->dtor(notifier);
    2220             :         }
    2221           2 :         efree(notifier);
    2222           2 : }
    2223             : 
    2224        5631 : PHPAPI zval *php_stream_context_get_option(php_stream_context *context,
    2225             :                 const char *wrappername, const char *optionname)
    2226             : {
    2227             :         zval *wrapperhash;
    2228             : 
    2229        5631 :         if (NULL == (wrapperhash = zend_hash_str_find(Z_ARRVAL(context->options), wrappername, strlen(wrappername)))) {
    2230        2545 :                 return NULL;
    2231             :         }
    2232        3086 :         return zend_hash_str_find(Z_ARRVAL_P(wrapperhash), optionname, strlen(optionname));
    2233             : }
    2234             : 
    2235        1748 : PHPAPI int php_stream_context_set_option(php_stream_context *context,
    2236             :                 const char *wrappername, const char *optionname, zval *optionvalue)
    2237             : {
    2238             :         zval *wrapperhash;
    2239             :         zval category, copied_val;
    2240             : 
    2241        1748 :         ZVAL_DUP(&copied_val, optionvalue);
    2242             : 
    2243        1748 :         if (NULL == (wrapperhash = zend_hash_str_find(Z_ARRVAL(context->options), wrappername, strlen(wrappername)))) {
    2244         852 :                 array_init(&category);
    2245         852 :                 if (NULL == zend_hash_str_update(Z_ARRVAL(context->options), (char*)wrappername, strlen(wrappername), &category)) {
    2246           0 :                         return FAILURE;
    2247             :                 }
    2248             : 
    2249         852 :                 wrapperhash = &category;
    2250             :         }
    2251        1748 :         return zend_hash_str_update(Z_ARRVAL_P(wrapperhash), optionname, strlen(optionname), &copied_val) ? SUCCESS : FAILURE;
    2252             : }
    2253             : /* }}} */
    2254             : 
    2255             : /* {{{ php_stream_dirent_alphasort
    2256             :  */
    2257         834 : PHPAPI int php_stream_dirent_alphasort(const zend_string **a, const zend_string **b)
    2258             : {
    2259         834 :         return strcoll((*a)->val, (*b)->val);
    2260             : }
    2261             : /* }}} */
    2262             : 
    2263             : /* {{{ php_stream_dirent_alphasortr
    2264             :  */
    2265          30 : PHPAPI int php_stream_dirent_alphasortr(const zend_string **a, const zend_string **b)
    2266             : {
    2267          30 :         return strcoll((*b)->val, (*a)->val);
    2268             : }
    2269             : /* }}} */
    2270             : 
    2271             : /* {{{ php_stream_scandir
    2272             :  */
    2273          81 : PHPAPI int _php_stream_scandir(const char *dirname, zend_string **namelist[], int flags, php_stream_context *context,
    2274             :                           int (*compare) (const zend_string **a, const zend_string **b))
    2275             : {
    2276             :         php_stream *stream;
    2277             :         php_stream_dirent sdp;
    2278          81 :         zend_string **vector = NULL;
    2279          81 :         unsigned int vector_size = 0;
    2280          81 :         unsigned int nfiles = 0;
    2281             : 
    2282          81 :         if (!namelist) {
    2283           0 :                 return FAILURE;
    2284             :         }
    2285             : 
    2286          81 :         stream = php_stream_opendir(dirname, REPORT_ERRORS, context);
    2287          81 :         if (!stream) {
    2288          31 :                 return FAILURE;
    2289             :         }
    2290             : 
    2291         377 :         while (php_stream_readdir(stream, &sdp)) {
    2292         277 :                 if (nfiles == vector_size) {
    2293          55 :                         if (vector_size == 0) {
    2294          50 :                                 vector_size = 10;
    2295             :                         } else {
    2296           5 :                                 if(vector_size*2 < vector_size) {
    2297             :                                         /* overflow */
    2298           0 :                                         php_stream_closedir(stream);
    2299           0 :                                         efree(vector);
    2300           0 :                                         return FAILURE;
    2301             :                                 }
    2302           5 :                                 vector_size *= 2;
    2303             :                         }
    2304          55 :                         vector = (zend_string **) safe_erealloc(vector, vector_size, sizeof(char *), 0);
    2305             :                 }
    2306             : 
    2307         554 :                 vector[nfiles] = zend_string_init(sdp.d_name, strlen(sdp.d_name), 0);
    2308             : 
    2309         277 :                 nfiles++;
    2310         277 :                 if(vector_size < 10 || nfiles == 0) {
    2311             :                         /* overflow */
    2312           0 :                         php_stream_closedir(stream);
    2313           0 :                         efree(vector);
    2314           0 :                         return FAILURE;
    2315             :                 }
    2316             :         }
    2317          50 :         php_stream_closedir(stream);
    2318             : 
    2319          50 :         *namelist = vector;
    2320             : 
    2321          50 :         if (nfiles > 0 && compare) {
    2322          49 :                 qsort(*namelist, nfiles, sizeof(zend_string *), (int(*)(const void *, const void *))compare);
    2323             :         }
    2324          50 :         return nfiles;
    2325             : }
    2326             : /* }}} */
    2327             : 
    2328             : /*
    2329             :  * Local variables:
    2330             :  * tab-width: 4
    2331             :  * c-basic-offset: 4
    2332             :  * End:
    2333             :  * vim600: noet sw=4 ts=4 fdm=marker
    2334             :  * vim<600: noet sw=4 ts=4
    2335             :  */

Generated by: LCOV version 1.10

Generated at Sun, 29 Mar 2015 03:46:04 +0000 (29 hours ago)

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