Return to filter.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / main / streams |
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: */