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

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