Back to index

php5  5.3.10
filter.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Authors: Wez Furlong <wez@thebrainroom.com>                          |
00016    +----------------------------------------------------------------------+
00017  */
00018 
00019 /* $Id: filter.c 321634 2012-01-01 13:15:04Z felipe $ */
00020 
00021 #include "php.h"
00022 #include "php_globals.h"
00023 #include "php_network.h"
00024 #include "php_open_temporary_file.h"
00025 #include "ext/standard/file.h"
00026 #include <stddef.h>
00027 #include <fcntl.h>
00028 
00029 #include "php_streams_int.h"
00030 
00031 /* Global filter hash, copied to FG(stream_filters) on registration of volatile filter */
00032 static HashTable stream_filters_hash;
00033 
00034 /* Should only be used during core initialization */
00035 PHPAPI HashTable *php_get_stream_filters_hash_global(void)
00036 {
00037        return &stream_filters_hash;
00038 }
00039 
00040 /* Normal hash selection/retrieval call */
00041 PHPAPI HashTable *_php_get_stream_filters_hash(TSRMLS_D)
00042 {
00043        return (FG(stream_filters) ? FG(stream_filters) : &stream_filters_hash);
00044 }
00045 
00046 /* API for registering GLOBAL filters */
00047 PHPAPI int php_stream_filter_register_factory(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC)
00048 {
00049        return zend_hash_add(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern) + 1, factory, sizeof(*factory), NULL);
00050 }
00051 
00052 PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern TSRMLS_DC)
00053 {
00054        return zend_hash_del(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern) + 1);
00055 }
00056 
00057 /* API for registering VOLATILE wrappers */
00058 PHPAPI int php_stream_filter_register_factory_volatile(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC)
00059 {
00060        if (!FG(stream_filters)) {
00061               php_stream_filter_factory tmpfactory;
00062 
00063               ALLOC_HASHTABLE(FG(stream_filters));
00064               zend_hash_init(FG(stream_filters), zend_hash_num_elements(&stream_filters_hash), NULL, NULL, 1);
00065               zend_hash_copy(FG(stream_filters), &stream_filters_hash, NULL, &tmpfactory, sizeof(php_stream_filter_factory));
00066        }
00067 
00068        return zend_hash_add(FG(stream_filters), (char*)filterpattern, strlen(filterpattern) + 1, factory, sizeof(*factory), NULL);
00069 }
00070 
00071 /* Buckets */
00072 
00073 PHPAPI php_stream_bucket *php_stream_bucket_new(php_stream *stream, char *buf, size_t buflen, int own_buf, int buf_persistent TSRMLS_DC)
00074 {
00075        int is_persistent = php_stream_is_persistent(stream);
00076        php_stream_bucket *bucket;
00077 
00078        bucket = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), is_persistent);
00079 
00080        if (bucket == NULL) {
00081               return NULL;
00082        }
00083        
00084        bucket->next = bucket->prev = NULL;
00085 
00086        if (is_persistent && !buf_persistent) {
00087               /* all data in a persistent bucket must also be persistent */
00088               bucket->buf = pemalloc(buflen, 1);
00089               
00090               if (bucket->buf == NULL) {
00091                      pefree(bucket, 1);
00092                      return NULL;
00093               }
00094               
00095               memcpy(bucket->buf, buf, buflen);
00096               bucket->buflen = buflen;
00097               bucket->own_buf = 1;
00098        } else {
00099               bucket->buf = buf;
00100               bucket->buflen = buflen;
00101               bucket->own_buf = own_buf;
00102        }
00103        bucket->is_persistent = is_persistent;
00104        bucket->refcount = 1;
00105        bucket->brigade = NULL;
00106 
00107        return bucket;
00108 }
00109 
00110 /* Given a bucket, returns a version of that bucket with a writeable buffer.
00111  * If the original bucket has a refcount of 1 and owns its buffer, then it
00112  * is returned unchanged.
00113  * Otherwise, a copy of the buffer is made.
00114  * In both cases, the original bucket is unlinked from its brigade.
00115  * If a copy is made, the original bucket is delref'd.
00116  * */
00117 PHPAPI php_stream_bucket *php_stream_bucket_make_writeable(php_stream_bucket *bucket TSRMLS_DC)
00118 {
00119        php_stream_bucket *retval;
00120 
00121        php_stream_bucket_unlink(bucket TSRMLS_CC);
00122        
00123        if (bucket->refcount == 1 && bucket->own_buf) {
00124               return bucket;
00125        }
00126 
00127        retval = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), bucket->is_persistent);
00128        memcpy(retval, bucket, sizeof(*retval));
00129 
00130        retval->buf = pemalloc(retval->buflen, retval->is_persistent);
00131        memcpy(retval->buf, bucket->buf, retval->buflen);
00132 
00133        retval->refcount = 1;
00134        retval->own_buf = 1;
00135 
00136        php_stream_bucket_delref(bucket TSRMLS_CC);
00137        
00138        return retval;
00139 }
00140 
00141 PHPAPI int php_stream_bucket_split(php_stream_bucket *in, php_stream_bucket **left, php_stream_bucket **right, size_t length TSRMLS_DC)
00142 {
00143        *left = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent);
00144        *right = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent);
00145 
00146        if (*left == NULL || *right == NULL) {
00147               goto exit_fail;
00148        }
00149 
00150        (*left)->buf = pemalloc(length, in->is_persistent);
00151        (*left)->buflen = length;
00152        memcpy((*left)->buf, in->buf, length);
00153        (*left)->refcount = 1;
00154        (*left)->own_buf = 1;
00155        (*left)->is_persistent = in->is_persistent;
00156        
00157        (*right)->buflen = in->buflen - length;
00158        (*right)->buf = pemalloc((*right)->buflen, in->is_persistent);
00159        memcpy((*right)->buf, in->buf + length, (*right)->buflen);
00160        (*right)->refcount = 1;
00161        (*right)->own_buf = 1;
00162        (*right)->is_persistent = in->is_persistent;
00163        
00164        return SUCCESS;
00165        
00166 exit_fail:
00167        if (*right) {
00168               if ((*right)->buf) {
00169                      pefree((*right)->buf, in->is_persistent);
00170               }
00171               pefree(*right, in->is_persistent);
00172        }
00173        if (*left) {
00174               if ((*left)->buf) {
00175                      pefree((*left)->buf, in->is_persistent);
00176               }
00177               pefree(*left, in->is_persistent);
00178        }
00179        return FAILURE;
00180 }
00181 
00182 PHPAPI void php_stream_bucket_delref(php_stream_bucket *bucket TSRMLS_DC)
00183 {
00184        if (--bucket->refcount == 0) {
00185               if (bucket->own_buf) {
00186                      pefree(bucket->buf, bucket->is_persistent);
00187               }
00188               pefree(bucket, bucket->is_persistent);
00189        }
00190 }
00191 
00192 PHPAPI void php_stream_bucket_prepend(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC)
00193 {
00194        bucket->next = brigade->head;
00195        bucket->prev = NULL;
00196 
00197        if (brigade->head) {
00198               brigade->head->prev = bucket;
00199        } else {
00200               brigade->tail = bucket;
00201        }
00202        brigade->head = bucket;
00203        bucket->brigade = brigade;
00204 }
00205 
00206 PHPAPI void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC)
00207 {
00208        if (brigade->tail == bucket) {
00209               return;
00210        }
00211 
00212        bucket->prev = brigade->tail;
00213        bucket->next = NULL;
00214 
00215        if (brigade->tail) {
00216               brigade->tail->next = bucket;
00217        } else {
00218               brigade->head = bucket;
00219        }
00220        brigade->tail = bucket;
00221        bucket->brigade = brigade;
00222 }
00223 
00224 PHPAPI void php_stream_bucket_unlink(php_stream_bucket *bucket TSRMLS_DC)
00225 {
00226        if (bucket->prev) {
00227               bucket->prev->next = bucket->next;
00228        } else if (bucket->brigade) {
00229               bucket->brigade->head = bucket->next;
00230        }
00231        if (bucket->next) {
00232               bucket->next->prev = bucket->prev;
00233        } else if (bucket->brigade) {
00234               bucket->brigade->tail = bucket->prev;
00235        }
00236        bucket->brigade = NULL;
00237        bucket->next = bucket->prev = NULL;
00238 }
00239        
00240 
00241 
00242 
00243 
00244 
00245 
00246 
00247 /* We allow very simple pattern matching for filter factories:
00248  * if "convert.charset.utf-8/sjis" is requested, we search first for an exact
00249  * match. If that fails, we try "convert.charset.*", then "convert.*"
00250  * This means that we don't need to clog up the hashtable with a zillion
00251  * charsets (for example) but still be able to provide them all as filters */
00252 PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
00253 {
00254        HashTable *filter_hash = (FG(stream_filters) ? FG(stream_filters) : &stream_filters_hash);
00255        php_stream_filter_factory *factory = NULL;
00256        php_stream_filter *filter = NULL;
00257        int n;
00258        char *period;
00259 
00260        n = strlen(filtername);
00261        
00262        if (SUCCESS == zend_hash_find(filter_hash, (char*)filtername, n + 1, (void**)&factory)) {
00263               filter = factory->create_filter(filtername, filterparams, persistent TSRMLS_CC);
00264        } else if ((period = strrchr(filtername, '.'))) {
00265               /* try a wildcard */
00266               char *wildname;
00267 
00268               wildname = emalloc(n+3);
00269               memcpy(wildname, filtername, n+1);
00270               period = wildname + (period - filtername);
00271               while (period && !filter) {
00272                      *period = '\0';
00273                      strncat(wildname, ".*", 2);
00274                      if (SUCCESS == zend_hash_find(filter_hash, wildname, strlen(wildname) + 1, (void**)&factory)) {
00275                             filter = factory->create_filter(filtername, filterparams, persistent TSRMLS_CC);
00276                      }
00277 
00278                      *period = '\0';
00279                      period = strrchr(wildname, '.');
00280               }
00281               efree(wildname);
00282        }
00283 
00284        if (filter == NULL) {
00285               /* TODO: these need correct docrefs */
00286               if (factory == NULL)
00287                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to locate filter \"%s\"", filtername);
00288               else
00289                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to create or locate filter \"%s\"", filtername);
00290        }
00291        
00292        return filter;
00293 }
00294 
00295 PHPAPI php_stream_filter *_php_stream_filter_alloc(php_stream_filter_ops *fops, void *abstract, int persistent STREAMS_DC TSRMLS_DC)
00296 {
00297        php_stream_filter *filter;
00298 
00299        filter = (php_stream_filter*) pemalloc_rel_orig(sizeof(php_stream_filter), persistent);
00300        memset(filter, 0, sizeof(php_stream_filter));
00301 
00302        filter->fops = fops;
00303        filter->abstract = abstract;
00304        filter->is_persistent = persistent;
00305        
00306        return filter;
00307 }
00308 
00309 PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC)
00310 {
00311        if (filter->fops->dtor)
00312               filter->fops->dtor(filter TSRMLS_CC);
00313        pefree(filter, filter->is_persistent);
00314 }
00315 
00316 PHPAPI int php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
00317 {
00318        filter->next = chain->head;
00319        filter->prev = NULL;
00320 
00321        if (chain->head) {
00322               chain->head->prev = filter;
00323        } else {
00324               chain->tail = filter;
00325        }
00326        chain->head = filter;
00327        filter->chain = chain;
00328 
00329        return SUCCESS;
00330 }
00331 
00332 PHPAPI void _php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
00333 {
00334        php_stream_filter_prepend_ex(chain, filter TSRMLS_CC);
00335 }
00336 
00337 PHPAPI int php_stream_filter_append_ex(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
00338 {
00339        php_stream *stream = chain->stream;
00340 
00341        filter->prev = chain->tail;
00342        filter->next = NULL;
00343        if (chain->tail) {
00344               chain->tail->next = filter;
00345        } else {
00346               chain->head = filter;
00347        }
00348        chain->tail = filter;
00349        filter->chain = chain;
00350 
00351        if (&(stream->readfilters) == chain && (stream->writepos - stream->readpos) > 0) {
00352               /* Let's going ahead and wind anything in the buffer through this filter */
00353               php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL };
00354               php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out;
00355               php_stream_filter_status_t status;
00356               php_stream_bucket *bucket;
00357               size_t consumed = 0;
00358 
00359               bucket = php_stream_bucket_new(stream, (char*) stream->readbuf + stream->readpos, stream->writepos - stream->readpos, 0, 0 TSRMLS_CC);
00360               php_stream_bucket_append(brig_inp, bucket TSRMLS_CC);
00361               status = filter->fops->filter(stream, filter, brig_inp, brig_outp, &consumed, PSFS_FLAG_NORMAL TSRMLS_CC);
00362 
00363               if (stream->readpos + consumed > (uint)stream->writepos) {
00364                      /* No behaving filter should cause this. */
00365                      status = PSFS_ERR_FATAL;
00366               }
00367 
00368               switch (status) {
00369                      case PSFS_ERR_FATAL:
00370                             while (brig_in.head) {
00371                                    bucket = brig_in.head;
00372                                    php_stream_bucket_unlink(bucket TSRMLS_CC);
00373                                    php_stream_bucket_delref(bucket TSRMLS_CC);
00374                             }
00375                             while (brig_out.head) {
00376                                    bucket = brig_out.head;
00377                                    php_stream_bucket_unlink(bucket TSRMLS_CC);
00378                                    php_stream_bucket_delref(bucket TSRMLS_CC);
00379                             }
00380                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filter failed to process pre-buffered data");
00381                             return FAILURE;
00382                      case PSFS_FEED_ME:
00383                             /* We don't actually need data yet,
00384                                leave this filter in a feed me state until data is needed. 
00385                                Reset stream's internal read buffer since the filter is "holding" it. */
00386                             stream->readpos = 0;
00387                             stream->writepos = 0;
00388                             break;
00389                      case PSFS_PASS_ON:
00390                             /* If any data is consumed, we cannot rely upon the existing read buffer,
00391                                as the filtered data must replace the existing data, so invalidate the cache */
00392                             /* note that changes here should be reflected in
00393                                main/streams/streams.c::php_stream_fill_read_buffer */
00394                             stream->writepos = 0;
00395                             stream->readpos = 0;
00396 
00397                             while (brig_outp->head) {
00398                                    bucket = brig_outp->head;
00399                                    /* Grow buffer to hold this bucket if need be.
00400                                       TODO: See warning in main/stream/streams.c::php_stream_fill_read_buffer */
00401                                    if (stream->readbuflen - stream->writepos < bucket->buflen) {
00402                                           stream->readbuflen += bucket->buflen;
00403                                           stream->readbuf = perealloc(stream->readbuf, stream->readbuflen, stream->is_persistent);
00404                                    }
00405                                    memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen);
00406                                    stream->writepos += bucket->buflen;
00407 
00408                                    php_stream_bucket_unlink(bucket TSRMLS_CC);
00409                                    php_stream_bucket_delref(bucket TSRMLS_CC);
00410                             }
00411                             break;
00412               }
00413        }
00414 
00415        return SUCCESS;
00416 }
00417 
00418 PHPAPI void _php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
00419 {
00420        if (php_stream_filter_append_ex(chain, filter TSRMLS_CC) != SUCCESS) {
00421               if (chain->head == filter) {
00422                      chain->head = NULL;
00423                      chain->tail = NULL;
00424               } else {
00425                      filter->prev->next = NULL;
00426                      chain->tail = filter->prev;
00427               }
00428        }
00429 }
00430 
00431 PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, int finish TSRMLS_DC)
00432 {
00433        php_stream_bucket_brigade brig_a = { NULL, NULL }, brig_b = { NULL, NULL }, *inp = &brig_a, *outp = &brig_b, *brig_temp;
00434        php_stream_bucket *bucket;
00435        php_stream_filter_chain *chain;
00436        php_stream_filter *current;
00437        php_stream *stream;
00438        size_t flushed_size = 0;
00439        long flags = (finish ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC);
00440 
00441        if (!filter->chain || !filter->chain->stream) {
00442               /* Filter is not attached to a chain, or chain is somehow not part of a stream */
00443               return FAILURE;
00444        }
00445 
00446        chain = filter->chain;
00447        stream = chain->stream;
00448 
00449        for(current = filter; current; current = current->next) {
00450               php_stream_filter_status_t status;
00451 
00452               status = filter->fops->filter(stream, filter, inp, outp, NULL, flags TSRMLS_CC);
00453               if (status == PSFS_FEED_ME) {
00454                      /* We've flushed the data far enough */
00455                      return SUCCESS;
00456               }
00457               if (status == PSFS_ERR_FATAL) {
00458                      return FAILURE;
00459               }
00460               /* Otherwise we have data available to PASS_ON
00461                      Swap the brigades and continue */
00462               brig_temp = inp;
00463               inp = outp;
00464               outp = brig_temp;
00465               outp->head = NULL;
00466               outp->tail = NULL;
00467 
00468               flags = PSFS_FLAG_NORMAL;
00469        }
00470 
00471        /* Last filter returned data via PSFS_PASS_ON
00472               Do something with it */
00473 
00474        for(bucket = inp->head; bucket; bucket = bucket->next) {
00475               flushed_size += bucket->buflen;
00476        }
00477 
00478        if (flushed_size == 0) {
00479               /* Unlikely, but possible */
00480               return SUCCESS;
00481        }
00482 
00483        if (chain == &(stream->readfilters)) {
00484               /* Dump any newly flushed data to the read buffer */
00485               if (stream->readpos > 0) {
00486                      /* Back the buffer up */
00487                      memcpy(stream->readbuf, stream->readbuf + stream->readpos, stream->writepos - stream->readpos);
00488                      stream->readpos = 0;
00489                      stream->writepos -= stream->readpos;
00490               }
00491               if (flushed_size > (stream->readbuflen - stream->writepos)) {
00492                      /* Grow the buffer */
00493                      stream->readbuf = perealloc(stream->readbuf, stream->writepos + flushed_size + stream->chunk_size, stream->is_persistent);
00494               }
00495               while ((bucket = inp->head)) {
00496                      memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen);
00497                      stream->writepos += bucket->buflen;
00498                      php_stream_bucket_unlink(bucket TSRMLS_CC);
00499                      php_stream_bucket_delref(bucket TSRMLS_CC);
00500               }
00501        } else if (chain == &(stream->writefilters)) {
00502               /* Send flushed data to the stream */
00503               while ((bucket = inp->head)) {
00504                      stream->ops->write(stream, bucket->buf, bucket->buflen TSRMLS_CC);
00505                      php_stream_bucket_unlink(bucket TSRMLS_CC);
00506                      php_stream_bucket_delref(bucket TSRMLS_CC);
00507               }
00508        }
00509 
00510        return SUCCESS;
00511 }
00512 
00513 PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor TSRMLS_DC)
00514 {
00515        if (filter->prev) {
00516               filter->prev->next = filter->next;
00517        } else {
00518               filter->chain->head = filter->next;
00519        }
00520        if (filter->next) {
00521               filter->next->prev = filter->prev;
00522        } else {
00523               filter->chain->tail = filter->prev;
00524        }
00525 
00526        if (filter->rsrc_id > 0) {
00527               zend_list_delete(filter->rsrc_id);
00528        }
00529 
00530        if (call_dtor) {
00531               php_stream_filter_free(filter TSRMLS_CC);
00532               return NULL;
00533        }
00534        return filter;
00535 }
00536 
00537 /*
00538  * Local variables:
00539  * tab-width: 4
00540  * c-basic-offset: 4
00541  * End:
00542  * vim600: noet sw=4 ts=4 fdm=marker
00543  * vim<600: noet sw=4 ts=4
00544  */