root/main/streams/filter.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_get_stream_filters_hash_global
  2. _php_get_stream_filters_hash
  3. php_stream_filter_register_factory
  4. php_stream_filter_unregister_factory
  5. php_stream_filter_register_factory_volatile
  6. php_stream_bucket_new
  7. php_stream_bucket_make_writeable
  8. php_stream_bucket_split
  9. php_stream_bucket_delref
  10. php_stream_bucket_prepend
  11. php_stream_bucket_append
  12. php_stream_bucket_unlink
  13. php_stream_filter_create
  14. _php_stream_filter_alloc
  15. php_stream_filter_free
  16. php_stream_filter_prepend_ex
  17. _php_stream_filter_prepend
  18. php_stream_filter_append_ex
  19. _php_stream_filter_append
  20. _php_stream_filter_flush
  21. php_stream_filter_remove

   1 /*
   2    +----------------------------------------------------------------------+
   3    | PHP Version 7                                                        |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1997-2016 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    +----------------------------------------------------------------------+
  17  */
  18 
  19 /* $Id$ */
  20 
  21 #include "php.h"
  22 #include "php_globals.h"
  23 #include "php_network.h"
  24 #include "php_open_temporary_file.h"
  25 #include "ext/standard/file.h"
  26 #include <stddef.h>
  27 #include <fcntl.h>
  28 
  29 #include "php_streams_int.h"
  30 
  31 /* Global filter hash, copied to FG(stream_filters) on registration of volatile filter */
  32 static HashTable stream_filters_hash;
  33 
  34 /* Should only be used during core initialization */
  35 PHPAPI HashTable *php_get_stream_filters_hash_global(void)
  36 {
  37         return &stream_filters_hash;
  38 }
  39 
  40 /* Normal hash selection/retrieval call */
  41 PHPAPI HashTable *_php_get_stream_filters_hash(void)
  42 {
  43         return (FG(stream_filters) ? FG(stream_filters) : &stream_filters_hash);
  44 }
  45 
  46 /* API for registering GLOBAL filters */
  47 PHPAPI int php_stream_filter_register_factory(const char *filterpattern, php_stream_filter_factory *factory)
  48 {
  49         return zend_hash_str_add_ptr(&stream_filters_hash, filterpattern, strlen(filterpattern), factory) ? SUCCESS : FAILURE;
  50 }
  51 
  52 PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern)
  53 {
  54         return zend_hash_str_del(&stream_filters_hash, filterpattern, strlen(filterpattern));
  55 }
  56 
  57 /* API for registering VOLATILE wrappers */
  58 PHPAPI int php_stream_filter_register_factory_volatile(const char *filterpattern, php_stream_filter_factory *factory)
  59 {
  60         if (!FG(stream_filters)) {
  61                 ALLOC_HASHTABLE(FG(stream_filters));
  62                 zend_hash_init(FG(stream_filters), zend_hash_num_elements(&stream_filters_hash), NULL, NULL, 1);
  63                 zend_hash_copy(FG(stream_filters), &stream_filters_hash, NULL);
  64         }
  65 
  66         return zend_hash_str_add_ptr(FG(stream_filters), (char*)filterpattern, strlen(filterpattern), factory) ? SUCCESS : FAILURE;
  67 }
  68 
  69 /* Buckets */
  70 
  71 PHPAPI php_stream_bucket *php_stream_bucket_new(php_stream *stream, char *buf, size_t buflen, int own_buf, int buf_persistent)
  72 {
  73         int is_persistent = php_stream_is_persistent(stream);
  74         php_stream_bucket *bucket;
  75 
  76         bucket = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), is_persistent);
  77 
  78         if (bucket == NULL) {
  79                 return NULL;
  80         }
  81 
  82         bucket->next = bucket->prev = NULL;
  83 
  84         if (is_persistent && !buf_persistent) {
  85                 /* all data in a persistent bucket must also be persistent */
  86                 bucket->buf = pemalloc(buflen, 1);
  87 
  88                 if (bucket->buf == NULL) {
  89                         pefree(bucket, 1);
  90                         return NULL;
  91                 }
  92 
  93                 memcpy(bucket->buf, buf, buflen);
  94                 bucket->buflen = buflen;
  95                 bucket->own_buf = 1;
  96         } else {
  97                 bucket->buf = buf;
  98                 bucket->buflen = buflen;
  99                 bucket->own_buf = own_buf;
 100         }
 101         bucket->is_persistent = is_persistent;
 102         bucket->refcount = 1;
 103         bucket->brigade = NULL;
 104 
 105         return bucket;
 106 }
 107 
 108 /* Given a bucket, returns a version of that bucket with a writeable buffer.
 109  * If the original bucket has a refcount of 1 and owns its buffer, then it
 110  * is returned unchanged.
 111  * Otherwise, a copy of the buffer is made.
 112  * In both cases, the original bucket is unlinked from its brigade.
 113  * If a copy is made, the original bucket is delref'd.
 114  * */
 115 PHPAPI php_stream_bucket *php_stream_bucket_make_writeable(php_stream_bucket *bucket)
 116 {
 117         php_stream_bucket *retval;
 118 
 119         php_stream_bucket_unlink(bucket);
 120 
 121         if (bucket->refcount == 1 && bucket->own_buf) {
 122                 return bucket;
 123         }
 124 
 125         retval = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), bucket->is_persistent);
 126         memcpy(retval, bucket, sizeof(*retval));
 127 
 128         retval->buf = pemalloc(retval->buflen, retval->is_persistent);
 129         memcpy(retval->buf, bucket->buf, retval->buflen);
 130 
 131         retval->refcount = 1;
 132         retval->own_buf = 1;
 133 
 134         php_stream_bucket_delref(bucket);
 135 
 136         return retval;
 137 }
 138 
 139 PHPAPI int php_stream_bucket_split(php_stream_bucket *in, php_stream_bucket **left, php_stream_bucket **right, size_t length)
 140 {
 141         *left = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent);
 142         *right = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent);
 143 
 144         if (*left == NULL || *right == NULL) {
 145                 goto exit_fail;
 146         }
 147 
 148         (*left)->buf = pemalloc(length, in->is_persistent);
 149         (*left)->buflen = length;
 150         memcpy((*left)->buf, in->buf, length);
 151         (*left)->refcount = 1;
 152         (*left)->own_buf = 1;
 153         (*left)->is_persistent = in->is_persistent;
 154 
 155         (*right)->buflen = in->buflen - length;
 156         (*right)->buf = pemalloc((*right)->buflen, in->is_persistent);
 157         memcpy((*right)->buf, in->buf + length, (*right)->buflen);
 158         (*right)->refcount = 1;
 159         (*right)->own_buf = 1;
 160         (*right)->is_persistent = in->is_persistent;
 161 
 162         return SUCCESS;
 163 
 164 exit_fail:
 165         if (*right) {
 166                 if ((*right)->buf) {
 167                         pefree((*right)->buf, in->is_persistent);
 168                 }
 169                 pefree(*right, in->is_persistent);
 170         }
 171         if (*left) {
 172                 if ((*left)->buf) {
 173                         pefree((*left)->buf, in->is_persistent);
 174                 }
 175                 pefree(*left, in->is_persistent);
 176         }
 177         return FAILURE;
 178 }
 179 
 180 PHPAPI void php_stream_bucket_delref(php_stream_bucket *bucket)
 181 {
 182         if (--bucket->refcount == 0) {
 183                 if (bucket->own_buf) {
 184                         pefree(bucket->buf, bucket->is_persistent);
 185                 }
 186                 pefree(bucket, bucket->is_persistent);
 187         }
 188 }
 189 
 190 PHPAPI void php_stream_bucket_prepend(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket)
 191 {
 192         bucket->next = brigade->head;
 193         bucket->prev = NULL;
 194 
 195         if (brigade->head) {
 196                 brigade->head->prev = bucket;
 197         } else {
 198                 brigade->tail = bucket;
 199         }
 200         brigade->head = bucket;
 201         bucket->brigade = brigade;
 202 }
 203 
 204 PHPAPI void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket)
 205 {
 206         if (brigade->tail == bucket) {
 207                 return;
 208         }
 209 
 210         bucket->prev = brigade->tail;
 211         bucket->next = NULL;
 212 
 213         if (brigade->tail) {
 214                 brigade->tail->next = bucket;
 215         } else {
 216                 brigade->head = bucket;
 217         }
 218         brigade->tail = bucket;
 219         bucket->brigade = brigade;
 220 }
 221 
 222 PHPAPI void php_stream_bucket_unlink(php_stream_bucket *bucket)
 223 {
 224         if (bucket->prev) {
 225                 bucket->prev->next = bucket->next;
 226         } else if (bucket->brigade) {
 227                 bucket->brigade->head = bucket->next;
 228         }
 229         if (bucket->next) {
 230                 bucket->next->prev = bucket->prev;
 231         } else if (bucket->brigade) {
 232                 bucket->brigade->tail = bucket->prev;
 233         }
 234         bucket->brigade = NULL;
 235         bucket->next = bucket->prev = NULL;
 236 }
 237 
 238 
 239 
 240 
 241 
 242 
 243 
 244 
 245 /* We allow very simple pattern matching for filter factories:
 246  * if "convert.charset.utf-8/sjis" is requested, we search first for an exact
 247  * match. If that fails, we try "convert.charset.*", then "convert.*"
 248  * This means that we don't need to clog up the hashtable with a zillion
 249  * charsets (for example) but still be able to provide them all as filters */
 250 PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, zval *filterparams, int persistent)
 251 {
 252         HashTable *filter_hash = (FG(stream_filters) ? FG(stream_filters) : &stream_filters_hash);
 253         php_stream_filter_factory *factory = NULL;
 254         php_stream_filter *filter = NULL;
 255         int n;
 256         char *period;
 257 
 258         n = (int)strlen(filtername);
 259 
 260         if (NULL != (factory = zend_hash_str_find_ptr(filter_hash, filtername, n))) {
 261                 filter = factory->create_filter(filtername, filterparams, persistent);
 262         } else if ((period = strrchr(filtername, '.'))) {
 263                 /* try a wildcard */
 264                 char *wildname;
 265 
 266                 wildname = emalloc(n+3);
 267                 memcpy(wildname, filtername, n+1);
 268                 period = wildname + (period - filtername);
 269                 while (period && !filter) {
 270                         *period = '\0';
 271                         strncat(wildname, ".*", 2);
 272                         if (NULL != (factory = zend_hash_str_find_ptr(filter_hash, wildname, strlen(wildname)))) {
 273                                 filter = factory->create_filter(filtername, filterparams, persistent);
 274                         }
 275 
 276                         *period = '\0';
 277                         period = strrchr(wildname, '.');
 278                 }
 279                 efree(wildname);
 280         }
 281 
 282         if (filter == NULL) {
 283                 /* TODO: these need correct docrefs */
 284                 if (factory == NULL)
 285                         php_error_docref(NULL, E_WARNING, "unable to locate filter \"%s\"", filtername);
 286                 else
 287                         php_error_docref(NULL, E_WARNING, "unable to create or locate filter \"%s\"", filtername);
 288         }
 289 
 290         return filter;
 291 }
 292 
 293 PHPAPI php_stream_filter *_php_stream_filter_alloc(php_stream_filter_ops *fops, void *abstract, int persistent STREAMS_DC)
 294 {
 295         php_stream_filter *filter;
 296 
 297         filter = (php_stream_filter*) pemalloc_rel_orig(sizeof(php_stream_filter), persistent);
 298         memset(filter, 0, sizeof(php_stream_filter));
 299 
 300         filter->fops = fops;
 301         Z_PTR(filter->abstract) = abstract;
 302         filter->is_persistent = persistent;
 303 
 304         return filter;
 305 }
 306 
 307 PHPAPI void php_stream_filter_free(php_stream_filter *filter)
 308 {
 309         if (filter->fops->dtor)
 310                 filter->fops->dtor(filter);
 311         pefree(filter, filter->is_persistent);
 312 }
 313 
 314 PHPAPI int php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter)
 315 {
 316         filter->next = chain->head;
 317         filter->prev = NULL;
 318 
 319         if (chain->head) {
 320                 chain->head->prev = filter;
 321         } else {
 322                 chain->tail = filter;
 323         }
 324         chain->head = filter;
 325         filter->chain = chain;
 326 
 327         return SUCCESS;
 328 }
 329 
 330 PHPAPI void _php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter)
 331 {
 332         php_stream_filter_prepend_ex(chain, filter);
 333 }
 334 
 335 PHPAPI int php_stream_filter_append_ex(php_stream_filter_chain *chain, php_stream_filter *filter)
 336 {
 337         php_stream *stream = chain->stream;
 338 
 339         filter->prev = chain->tail;
 340         filter->next = NULL;
 341         if (chain->tail) {
 342                 chain->tail->next = filter;
 343         } else {
 344                 chain->head = filter;
 345         }
 346         chain->tail = filter;
 347         filter->chain = chain;
 348 
 349         if (&(stream->readfilters) == chain && (stream->writepos - stream->readpos) > 0) {
 350                 /* Let's going ahead and wind anything in the buffer through this filter */
 351                 php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL };
 352                 php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out;
 353                 php_stream_filter_status_t status;
 354                 php_stream_bucket *bucket;
 355                 size_t consumed = 0;
 356 
 357                 bucket = php_stream_bucket_new(stream, (char*) stream->readbuf + stream->readpos, stream->writepos - stream->readpos, 0, 0);
 358                 php_stream_bucket_append(brig_inp, bucket);
 359                 status = filter->fops->filter(stream, filter, brig_inp, brig_outp, &consumed, PSFS_FLAG_NORMAL);
 360 
 361                 if (stream->readpos + consumed > (uint)stream->writepos) {
 362                         /* No behaving filter should cause this. */
 363                         status = PSFS_ERR_FATAL;
 364                 }
 365 
 366                 switch (status) {
 367                         case PSFS_ERR_FATAL:
 368                                 while (brig_in.head) {
 369                                         bucket = brig_in.head;
 370                                         php_stream_bucket_unlink(bucket);
 371                                         php_stream_bucket_delref(bucket);
 372                                 }
 373                                 while (brig_out.head) {
 374                                         bucket = brig_out.head;
 375                                         php_stream_bucket_unlink(bucket);
 376                                         php_stream_bucket_delref(bucket);
 377                                 }
 378                                 php_error_docref(NULL, E_WARNING, "Filter failed to process pre-buffered data");
 379                                 return FAILURE;
 380                         case PSFS_FEED_ME:
 381                                 /* We don't actually need data yet,
 382                                    leave this filter in a feed me state until data is needed.
 383                                    Reset stream's internal read buffer since the filter is "holding" it. */
 384                                 stream->readpos = 0;
 385                                 stream->writepos = 0;
 386                                 break;
 387                         case PSFS_PASS_ON:
 388                                 /* If any data is consumed, we cannot rely upon the existing read buffer,
 389                                    as the filtered data must replace the existing data, so invalidate the cache */
 390                                 /* note that changes here should be reflected in
 391                                    main/streams/streams.c::php_stream_fill_read_buffer */
 392                                 stream->writepos = 0;
 393                                 stream->readpos = 0;
 394 
 395                                 while (brig_outp->head) {
 396                                         bucket = brig_outp->head;
 397                                         /* Grow buffer to hold this bucket if need be.
 398                                            TODO: See warning in main/stream/streams.c::php_stream_fill_read_buffer */
 399                                         if (stream->readbuflen - stream->writepos < bucket->buflen) {
 400                                                 stream->readbuflen += bucket->buflen;
 401                                                 stream->readbuf = perealloc(stream->readbuf, stream->readbuflen, stream->is_persistent);
 402                                         }
 403                                         memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen);
 404                                         stream->writepos += bucket->buflen;
 405 
 406                                         php_stream_bucket_unlink(bucket);
 407                                         php_stream_bucket_delref(bucket);
 408                                 }
 409                                 break;
 410                 }
 411         }
 412 
 413         return SUCCESS;
 414 }
 415 
 416 PHPAPI void _php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter)
 417 {
 418         if (php_stream_filter_append_ex(chain, filter) != SUCCESS) {
 419                 if (chain->head == filter) {
 420                         chain->head = NULL;
 421                         chain->tail = NULL;
 422                 } else {
 423                         filter->prev->next = NULL;
 424                         chain->tail = filter->prev;
 425                 }
 426         }
 427 }
 428 
 429 PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, int finish)
 430 {
 431         php_stream_bucket_brigade brig_a = { NULL, NULL }, brig_b = { NULL, NULL }, *inp = &brig_a, *outp = &brig_b, *brig_temp;
 432         php_stream_bucket *bucket;
 433         php_stream_filter_chain *chain;
 434         php_stream_filter *current;
 435         php_stream *stream;
 436         size_t flushed_size = 0;
 437         long flags = (finish ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC);
 438 
 439         if (!filter->chain || !filter->chain->stream) {
 440                 /* Filter is not attached to a chain, or chain is somehow not part of a stream */
 441                 return FAILURE;
 442         }
 443 
 444         chain = filter->chain;
 445         stream = chain->stream;
 446 
 447         for(current = filter; current; current = current->next) {
 448                 php_stream_filter_status_t status;
 449 
 450                 status = filter->fops->filter(stream, current, inp, outp, NULL, flags);
 451                 if (status == PSFS_FEED_ME) {
 452                         /* We've flushed the data far enough */
 453                         return SUCCESS;
 454                 }
 455                 if (status == PSFS_ERR_FATAL) {
 456                         return FAILURE;
 457                 }
 458                 /* Otherwise we have data available to PASS_ON
 459                         Swap the brigades and continue */
 460                 brig_temp = inp;
 461                 inp = outp;
 462                 outp = brig_temp;
 463                 outp->head = NULL;
 464                 outp->tail = NULL;
 465 
 466                 flags = PSFS_FLAG_NORMAL;
 467         }
 468 
 469         /* Last filter returned data via PSFS_PASS_ON
 470                 Do something with it */
 471 
 472         for(bucket = inp->head; bucket; bucket = bucket->next) {
 473                 flushed_size += bucket->buflen;
 474         }
 475 
 476         if (flushed_size == 0) {
 477                 /* Unlikely, but possible */
 478                 return SUCCESS;
 479         }
 480 
 481         if (chain == &(stream->readfilters)) {
 482                 /* Dump any newly flushed data to the read buffer */
 483                 if (stream->readpos > 0) {
 484                         /* Back the buffer up */
 485                         memcpy(stream->readbuf, stream->readbuf + stream->readpos, stream->writepos - stream->readpos);
 486                         stream->readpos = 0;
 487                         stream->writepos -= stream->readpos;
 488                 }
 489                 if (flushed_size > (stream->readbuflen - stream->writepos)) {
 490                         /* Grow the buffer */
 491                         stream->readbuf = perealloc(stream->readbuf, stream->writepos + flushed_size + stream->chunk_size, stream->is_persistent);
 492                 }
 493                 while ((bucket = inp->head)) {
 494                         memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen);
 495                         stream->writepos += bucket->buflen;
 496                         php_stream_bucket_unlink(bucket);
 497                         php_stream_bucket_delref(bucket);
 498                 }
 499         } else if (chain == &(stream->writefilters)) {
 500                 /* Send flushed data to the stream */
 501                 while ((bucket = inp->head)) {
 502                         stream->ops->write(stream, bucket->buf, bucket->buflen);
 503                         php_stream_bucket_unlink(bucket);
 504                         php_stream_bucket_delref(bucket);
 505                 }
 506         }
 507 
 508         return SUCCESS;
 509 }
 510 
 511 PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor)
 512 {
 513         if (filter->prev) {
 514                 filter->prev->next = filter->next;
 515         } else {
 516                 filter->chain->head = filter->next;
 517         }
 518         if (filter->next) {
 519                 filter->next->prev = filter->prev;
 520         } else {
 521                 filter->chain->tail = filter->prev;
 522         }
 523 
 524         if (filter->res) {
 525                 zend_list_delete(filter->res);
 526         }
 527 
 528         if (call_dtor) {
 529                 php_stream_filter_free(filter);
 530                 return NULL;
 531         }
 532         return filter;
 533 }
 534 
 535 /*
 536  * Local variables:
 537  * tab-width: 4
 538  * c-basic-offset: 4
 539  * End:
 540  * vim600: noet sw=4 ts=4 fdm=marker
 541  * vim<600: noet sw=4 ts=4
 542  */

/* [<][>][^][v][top][bottom][index][help] */