Annotation of embedaddon/php/main/streams/filter.c, revision 1.1
1.1 ! misho 1: /*
! 2: +----------------------------------------------------------------------+
! 3: | PHP Version 5 |
! 4: +----------------------------------------------------------------------+
! 5: | Copyright (c) 1997-2012 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: filter.c 321634 2012-01-01 13:15:04Z felipe $ */
! 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, filter, 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: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>