Return to streams.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: | Borrowed code from: | ! 17: | Rasmus Lerdorf <rasmus@lerdorf.on.ca> | ! 18: | Jim Winstead <jimw@php.net> | ! 19: +----------------------------------------------------------------------+ ! 20: */ ! 21: ! 22: /* $Id: streams.c 321634 2012-01-01 13:15:04Z felipe $ */ ! 23: ! 24: #define _GNU_SOURCE ! 25: #include "php.h" ! 26: #include "php_globals.h" ! 27: #include "php_network.h" ! 28: #include "php_open_temporary_file.h" ! 29: #include "ext/standard/file.h" ! 30: #include "ext/standard/basic_functions.h" /* for BG(mmap_file) (not strictly required) */ ! 31: #include "ext/standard/php_string.h" /* for php_memnstr, used by php_stream_get_record() */ ! 32: #include <stddef.h> ! 33: #include <fcntl.h> ! 34: #include "php_streams_int.h" ! 35: ! 36: /* {{{ resource and registration code */ ! 37: /* Global wrapper hash, copied to FG(stream_wrappers) on registration of volatile wrapper */ ! 38: static HashTable url_stream_wrappers_hash; ! 39: static int le_stream = FAILURE; /* true global */ ! 40: static int le_pstream = FAILURE; /* true global */ ! 41: static int le_stream_filter = FAILURE; /* true global */ ! 42: ! 43: PHPAPI int php_file_le_stream(void) ! 44: { ! 45: return le_stream; ! 46: } ! 47: ! 48: PHPAPI int php_file_le_pstream(void) ! 49: { ! 50: return le_pstream; ! 51: } ! 52: ! 53: PHPAPI int php_file_le_stream_filter(void) ! 54: { ! 55: return le_stream_filter; ! 56: } ! 57: ! 58: PHPAPI HashTable *_php_stream_get_url_stream_wrappers_hash(TSRMLS_D) ! 59: { ! 60: return (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash); ! 61: } ! 62: ! 63: PHPAPI HashTable *php_stream_get_url_stream_wrappers_hash_global(void) ! 64: { ! 65: return &url_stream_wrappers_hash; ! 66: } ! 67: ! 68: static int _php_stream_release_context(zend_rsrc_list_entry *le, void *pContext TSRMLS_DC) ! 69: { ! 70: if (le->ptr == pContext) { ! 71: return --le->refcount == 0; ! 72: } ! 73: return 0; ! 74: } ! 75: ! 76: static int forget_persistent_resource_id_numbers(zend_rsrc_list_entry *rsrc TSRMLS_DC) ! 77: { ! 78: php_stream *stream; ! 79: ! 80: if (Z_TYPE_P(rsrc) != le_pstream) { ! 81: return 0; ! 82: } ! 83: ! 84: stream = (php_stream*)rsrc->ptr; ! 85: ! 86: #if STREAM_DEBUG ! 87: fprintf(stderr, "forget_persistent: %s:%p\n", stream->ops->label, stream); ! 88: #endif ! 89: ! 90: stream->rsrc_id = FAILURE; ! 91: ! 92: if (stream->context) { ! 93: zend_hash_apply_with_argument(&EG(regular_list), ! 94: (apply_func_arg_t) _php_stream_release_context, ! 95: stream->context TSRMLS_CC); ! 96: stream->context = NULL; ! 97: } ! 98: ! 99: return 0; ! 100: } ! 101: ! 102: PHP_RSHUTDOWN_FUNCTION(streams) ! 103: { ! 104: zend_hash_apply(&EG(persistent_list), (apply_func_t)forget_persistent_resource_id_numbers TSRMLS_CC); ! 105: return SUCCESS; ! 106: } ! 107: ! 108: PHPAPI int php_stream_from_persistent_id(const char *persistent_id, php_stream **stream TSRMLS_DC) ! 109: { ! 110: zend_rsrc_list_entry *le; ! 111: ! 112: if (zend_hash_find(&EG(persistent_list), (char*)persistent_id, strlen(persistent_id)+1, (void*) &le) == SUCCESS) { ! 113: if (Z_TYPE_P(le) == le_pstream) { ! 114: if (stream) { ! 115: HashPosition pos; ! 116: zend_rsrc_list_entry *regentry; ! 117: ulong index = -1; /* intentional */ ! 118: ! 119: /* see if this persistent resource already has been loaded to the ! 120: * regular list; allowing the same resource in several entries in the ! 121: * regular list causes trouble (see bug #54623) */ ! 122: zend_hash_internal_pointer_reset_ex(&EG(regular_list), &pos); ! 123: while (zend_hash_get_current_data_ex(&EG(regular_list), ! 124: (void **)®entry, &pos) == SUCCESS) { ! 125: if (regentry->ptr == le->ptr) { ! 126: zend_hash_get_current_key_ex(&EG(regular_list), NULL, NULL, ! 127: &index, 0, &pos); ! 128: break; ! 129: } ! 130: zend_hash_move_forward_ex(&EG(regular_list), &pos); ! 131: } ! 132: ! 133: *stream = (php_stream*)le->ptr; ! 134: if (index == -1) { /* not found in regular list */ ! 135: le->refcount++; ! 136: (*stream)->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, *stream, le_pstream); ! 137: } else { ! 138: regentry->refcount++; ! 139: (*stream)->rsrc_id = index; ! 140: } ! 141: } ! 142: return PHP_STREAM_PERSISTENT_SUCCESS; ! 143: } ! 144: return PHP_STREAM_PERSISTENT_FAILURE; ! 145: } ! 146: return PHP_STREAM_PERSISTENT_NOT_EXIST; ! 147: } ! 148: ! 149: /* }}} */ ! 150: ! 151: /* {{{ wrapper error reporting */ ! 152: void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption TSRMLS_DC) ! 153: { ! 154: char *tmp = estrdup(path); ! 155: char *msg; ! 156: int free_msg = 0; ! 157: php_stream_wrapper orig_wrapper; ! 158: ! 159: if (wrapper) { ! 160: if (wrapper->err_count > 0) { ! 161: int i; ! 162: size_t l; ! 163: int brlen; ! 164: char *br; ! 165: ! 166: if (PG(html_errors)) { ! 167: brlen = 7; ! 168: br = "<br />\n"; ! 169: } else { ! 170: brlen = 1; ! 171: br = "\n"; ! 172: } ! 173: ! 174: for (i = 0, l = 0; i < wrapper->err_count; i++) { ! 175: l += strlen(wrapper->err_stack[i]); ! 176: if (i < wrapper->err_count - 1) { ! 177: l += brlen; ! 178: } ! 179: } ! 180: msg = emalloc(l + 1); ! 181: msg[0] = '\0'; ! 182: for (i = 0; i < wrapper->err_count; i++) { ! 183: strcat(msg, wrapper->err_stack[i]); ! 184: if (i < wrapper->err_count - 1) { ! 185: strcat(msg, br); ! 186: } ! 187: } ! 188: ! 189: free_msg = 1; ! 190: } else { ! 191: if (wrapper == &php_plain_files_wrapper) { ! 192: msg = strerror(errno); ! 193: } else { ! 194: msg = "operation failed"; ! 195: } ! 196: } ! 197: } else { ! 198: msg = "no suitable wrapper could be found"; ! 199: } ! 200: ! 201: php_strip_url_passwd(tmp); ! 202: if (wrapper) { ! 203: /* see bug #52935 */ ! 204: orig_wrapper = *wrapper; ! 205: wrapper->err_stack = NULL; ! 206: wrapper->err_count = 0; ! 207: } ! 208: php_error_docref1(NULL TSRMLS_CC, tmp, E_WARNING, "%s: %s", caption, msg); ! 209: if (wrapper) { ! 210: *wrapper = orig_wrapper; ! 211: } ! 212: efree(tmp); ! 213: if (free_msg) { ! 214: efree(msg); ! 215: } ! 216: } ! 217: ! 218: void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC) ! 219: { ! 220: if (wrapper) { ! 221: /* tidy up the error stack */ ! 222: int i; ! 223: ! 224: for (i = 0; i < wrapper->err_count; i++) { ! 225: efree(wrapper->err_stack[i]); ! 226: } ! 227: if (wrapper->err_stack) { ! 228: efree(wrapper->err_stack); ! 229: } ! 230: wrapper->err_stack = NULL; ! 231: wrapper->err_count = 0; ! 232: } ! 233: } ! 234: ! 235: PHPAPI void php_stream_wrapper_log_error(php_stream_wrapper *wrapper, int options TSRMLS_DC, const char *fmt, ...) ! 236: { ! 237: va_list args; ! 238: char *buffer = NULL; ! 239: ! 240: va_start(args, fmt); ! 241: vspprintf(&buffer, 0, fmt, args); ! 242: va_end(args); ! 243: ! 244: if (options & REPORT_ERRORS || wrapper == NULL) { ! 245: php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", buffer); ! 246: efree(buffer); ! 247: } else { ! 248: /* append to stack */ ! 249: wrapper->err_stack = erealloc(wrapper->err_stack, (wrapper->err_count + 1) * sizeof(char *)); ! 250: if (wrapper->err_stack) { ! 251: wrapper->err_stack[wrapper->err_count++] = buffer; ! 252: } ! 253: } ! 254: } ! 255: ! 256: ! 257: /* }}} */ ! 258: ! 259: /* allocate a new stream for a particular ops */ ! 260: PHPAPI php_stream *_php_stream_alloc(php_stream_ops *ops, void *abstract, const char *persistent_id, const char *mode STREAMS_DC TSRMLS_DC) /* {{{ */ ! 261: { ! 262: php_stream *ret; ! 263: ! 264: ret = (php_stream*) pemalloc_rel_orig(sizeof(php_stream), persistent_id ? 1 : 0); ! 265: ! 266: memset(ret, 0, sizeof(php_stream)); ! 267: ! 268: ret->readfilters.stream = ret; ! 269: ret->writefilters.stream = ret; ! 270: ! 271: #if STREAM_DEBUG ! 272: fprintf(stderr, "stream_alloc: %s:%p persistent=%s\n", ops->label, ret, persistent_id); ! 273: #endif ! 274: ! 275: ret->ops = ops; ! 276: ret->abstract = abstract; ! 277: ret->is_persistent = persistent_id ? 1 : 0; ! 278: ret->chunk_size = FG(def_chunk_size); ! 279: ! 280: #if ZEND_DEBUG ! 281: ret->open_filename = __zend_orig_filename ? __zend_orig_filename : __zend_filename; ! 282: ret->open_lineno = __zend_orig_lineno ? __zend_orig_lineno : __zend_lineno; ! 283: #endif ! 284: ! 285: if (FG(auto_detect_line_endings)) { ! 286: ret->flags |= PHP_STREAM_FLAG_DETECT_EOL; ! 287: } ! 288: ! 289: if (persistent_id) { ! 290: zend_rsrc_list_entry le; ! 291: ! 292: Z_TYPE(le) = le_pstream; ! 293: le.ptr = ret; ! 294: le.refcount = 0; ! 295: ! 296: if (FAILURE == zend_hash_update(&EG(persistent_list), (char *)persistent_id, ! 297: strlen(persistent_id) + 1, ! 298: (void *)&le, sizeof(le), NULL)) { ! 299: ! 300: pefree(ret, 1); ! 301: return NULL; ! 302: } ! 303: } ! 304: ! 305: ret->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, ret, persistent_id ? le_pstream : le_stream); ! 306: strlcpy(ret->mode, mode, sizeof(ret->mode)); ! 307: ! 308: return ret; ! 309: } ! 310: /* }}} */ ! 311: ! 312: static int _php_stream_free_persistent(zend_rsrc_list_entry *le, void *pStream TSRMLS_DC) ! 313: { ! 314: return le->ptr == pStream; ! 315: } ! 316: ! 317: PHPAPI int _php_stream_free(php_stream *stream, int close_options TSRMLS_DC) /* {{{ */ ! 318: { ! 319: int ret = 1; ! 320: int remove_rsrc = 1; ! 321: int preserve_handle = close_options & PHP_STREAM_FREE_PRESERVE_HANDLE ? 1 : 0; ! 322: int release_cast = 1; ! 323: php_stream_context *context = stream->context; ! 324: ! 325: if (stream->flags & PHP_STREAM_FLAG_NO_CLOSE) { ! 326: preserve_handle = 1; ! 327: } ! 328: ! 329: #if STREAM_DEBUG ! 330: fprintf(stderr, "stream_free: %s:%p[%s] in_free=%d opts=%08x\n", stream->ops->label, stream, stream->orig_path, stream->in_free, close_options); ! 331: #endif ! 332: ! 333: /* recursion protection */ ! 334: if (stream->in_free) { ! 335: return 1; ! 336: } ! 337: ! 338: stream->in_free++; ! 339: ! 340: /* if we are releasing the stream only (and preserving the underlying handle), ! 341: * we need to do things a little differently. ! 342: * We are only ever called like this when the stream is cast to a FILE* ! 343: * for include (or other similar) purposes. ! 344: * */ ! 345: if (preserve_handle) { ! 346: if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) { ! 347: /* If the stream was fopencookied, we must NOT touch anything ! 348: * here, as the cookied stream relies on it all. ! 349: * Instead, mark the stream as OK to auto-clean */ ! 350: php_stream_auto_cleanup(stream); ! 351: stream->in_free--; ! 352: return 0; ! 353: } ! 354: /* otherwise, make sure that we don't close the FILE* from a cast */ ! 355: release_cast = 0; ! 356: } ! 357: ! 358: #if STREAM_DEBUG ! 359: fprintf(stderr, "stream_free: %s:%p[%s] preserve_handle=%d release_cast=%d remove_rsrc=%d\n", ! 360: stream->ops->label, stream, stream->orig_path, preserve_handle, release_cast, remove_rsrc); ! 361: #endif ! 362: ! 363: /* make sure everything is saved */ ! 364: _php_stream_flush(stream, 1 TSRMLS_CC); ! 365: ! 366: /* If not called from the resource dtor, remove the stream from the resource list. */ ! 367: if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) == 0 && remove_rsrc) { ! 368: /* zend_list_delete actually only decreases the refcount; if we're ! 369: * releasing the stream, we want to actually delete the resource from ! 370: * the resource list, otherwise the resource will point to invalid memory. ! 371: * In any case, let's always completely delete it from the resource list, ! 372: * not only when PHP_STREAM_FREE_RELEASE_STREAM is set */ ! 373: while (zend_list_delete(stream->rsrc_id) == SUCCESS) {} ! 374: } ! 375: ! 376: /* Remove stream from any context link list */ ! 377: if (stream->context && stream->context->links) { ! 378: php_stream_context_del_link(stream->context, stream); ! 379: } ! 380: ! 381: if (close_options & PHP_STREAM_FREE_CALL_DTOR) { ! 382: if (release_cast && stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) { ! 383: /* calling fclose on an fopencookied stream will ultimately ! 384: call this very same function. If we were called via fclose, ! 385: the cookie_closer unsets the fclose_stdiocast flags, so ! 386: we can be sure that we only reach here when PHP code calls ! 387: php_stream_free. ! 388: Lets let the cookie code clean it all up. ! 389: */ ! 390: stream->in_free = 0; ! 391: return fclose(stream->stdiocast); ! 392: } ! 393: ! 394: ret = stream->ops->close(stream, preserve_handle ? 0 : 1 TSRMLS_CC); ! 395: stream->abstract = NULL; ! 396: ! 397: /* tidy up any FILE* that might have been fdopened */ ! 398: if (release_cast && stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FDOPEN && stream->stdiocast) { ! 399: fclose(stream->stdiocast); ! 400: stream->stdiocast = NULL; ! 401: stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE; ! 402: } ! 403: } ! 404: ! 405: if (close_options & PHP_STREAM_FREE_RELEASE_STREAM) { ! 406: while (stream->readfilters.head) { ! 407: php_stream_filter_remove(stream->readfilters.head, 1 TSRMLS_CC); ! 408: } ! 409: while (stream->writefilters.head) { ! 410: php_stream_filter_remove(stream->writefilters.head, 1 TSRMLS_CC); ! 411: } ! 412: ! 413: if (stream->wrapper && stream->wrapper->wops && stream->wrapper->wops->stream_closer) { ! 414: stream->wrapper->wops->stream_closer(stream->wrapper, stream TSRMLS_CC); ! 415: stream->wrapper = NULL; ! 416: } ! 417: ! 418: if (stream->wrapperdata) { ! 419: zval_ptr_dtor(&stream->wrapperdata); ! 420: stream->wrapperdata = NULL; ! 421: } ! 422: ! 423: if (stream->readbuf) { ! 424: pefree(stream->readbuf, stream->is_persistent); ! 425: stream->readbuf = NULL; ! 426: } ! 427: ! 428: if (stream->is_persistent && (close_options & PHP_STREAM_FREE_PERSISTENT)) { ! 429: /* we don't work with *stream but need its value for comparison */ ! 430: zend_hash_apply_with_argument(&EG(persistent_list), (apply_func_arg_t) _php_stream_free_persistent, stream TSRMLS_CC); ! 431: } ! 432: #if ZEND_DEBUG ! 433: if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) && (stream->__exposed == 0) && (EG(error_reporting) & E_WARNING)) { ! 434: /* it leaked: Lets deliberately NOT pefree it so that the memory manager shows it ! 435: * as leaked; it will log a warning, but lets help it out and display what kind ! 436: * of stream it was. */ ! 437: char *leakinfo; ! 438: spprintf(&leakinfo, 0, __FILE__ "(%d) : Stream of type '%s' %p (path:%s) was not closed\n", __LINE__, stream->ops->label, stream, stream->orig_path); ! 439: ! 440: if (stream->orig_path) { ! 441: pefree(stream->orig_path, stream->is_persistent); ! 442: stream->orig_path = NULL; ! 443: } ! 444: ! 445: # if defined(PHP_WIN32) ! 446: OutputDebugString(leakinfo); ! 447: # else ! 448: fprintf(stderr, "%s", leakinfo); ! 449: # endif ! 450: efree(leakinfo); ! 451: } else { ! 452: if (stream->orig_path) { ! 453: pefree(stream->orig_path, stream->is_persistent); ! 454: stream->orig_path = NULL; ! 455: } ! 456: ! 457: pefree(stream, stream->is_persistent); ! 458: } ! 459: #else ! 460: if (stream->orig_path) { ! 461: pefree(stream->orig_path, stream->is_persistent); ! 462: stream->orig_path = NULL; ! 463: } ! 464: ! 465: pefree(stream, stream->is_persistent); ! 466: #endif ! 467: } ! 468: ! 469: if (context) { ! 470: zend_list_delete(context->rsrc_id); ! 471: } ! 472: ! 473: return ret; ! 474: } ! 475: /* }}} */ ! 476: ! 477: /* {{{ generic stream operations */ ! 478: ! 479: static void php_stream_fill_read_buffer(php_stream *stream, size_t size TSRMLS_DC) ! 480: { ! 481: /* allocate/fill the buffer */ ! 482: ! 483: if (stream->readfilters.head) { ! 484: char *chunk_buf; ! 485: int err_flag = 0; ! 486: php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL }; ! 487: php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap; ! 488: ! 489: /* Invalidate the existing cache, otherwise reads can fail, see note in ! 490: main/streams/filter.c::_php_stream_filter_append */ ! 491: stream->writepos = stream->readpos = 0; ! 492: ! 493: /* allocate a buffer for reading chunks */ ! 494: chunk_buf = emalloc(stream->chunk_size); ! 495: ! 496: while (!stream->eof && !err_flag && (stream->writepos - stream->readpos < (off_t)size)) { ! 497: size_t justread = 0; ! 498: int flags; ! 499: php_stream_bucket *bucket; ! 500: php_stream_filter_status_t status = PSFS_ERR_FATAL; ! 501: php_stream_filter *filter; ! 502: ! 503: /* read a chunk into a bucket */ ! 504: justread = stream->ops->read(stream, chunk_buf, stream->chunk_size TSRMLS_CC); ! 505: if (justread && justread != (size_t)-1) { ! 506: bucket = php_stream_bucket_new(stream, chunk_buf, justread, 0, 0 TSRMLS_CC); ! 507: ! 508: /* after this call, bucket is owned by the brigade */ ! 509: php_stream_bucket_append(brig_inp, bucket TSRMLS_CC); ! 510: ! 511: flags = PSFS_FLAG_NORMAL; ! 512: } else { ! 513: flags = stream->eof ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC; ! 514: } ! 515: ! 516: /* wind the handle... */ ! 517: for (filter = stream->readfilters.head; filter; filter = filter->next) { ! 518: status = filter->fops->filter(stream, filter, brig_inp, brig_outp, NULL, flags TSRMLS_CC); ! 519: ! 520: if (status != PSFS_PASS_ON) { ! 521: break; ! 522: } ! 523: ! 524: /* brig_out becomes brig_in. ! 525: * brig_in will always be empty here, as the filter MUST attach any un-consumed buckets ! 526: * to its own brigade */ ! 527: brig_swap = brig_inp; ! 528: brig_inp = brig_outp; ! 529: brig_outp = brig_swap; ! 530: memset(brig_outp, 0, sizeof(*brig_outp)); ! 531: } ! 532: ! 533: switch (status) { ! 534: case PSFS_PASS_ON: ! 535: /* we get here when the last filter in the chain has data to pass on. ! 536: * in this situation, we are passing the brig_in brigade into the ! 537: * stream read buffer */ ! 538: while (brig_inp->head) { ! 539: bucket = brig_inp->head; ! 540: /* grow buffer to hold this bucket ! 541: * TODO: this can fail for persistent streams */ ! 542: if (stream->readbuflen - stream->writepos < bucket->buflen) { ! 543: stream->readbuflen += bucket->buflen; ! 544: stream->readbuf = perealloc(stream->readbuf, stream->readbuflen, ! 545: stream->is_persistent); ! 546: } ! 547: memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen); ! 548: stream->writepos += bucket->buflen; ! 549: ! 550: php_stream_bucket_unlink(bucket TSRMLS_CC); ! 551: php_stream_bucket_delref(bucket TSRMLS_CC); ! 552: } ! 553: break; ! 554: ! 555: case PSFS_FEED_ME: ! 556: /* when a filter needs feeding, there is no brig_out to deal with. ! 557: * we simply continue the loop; if the caller needs more data, ! 558: * we will read again, otherwise out job is done here */ ! 559: if (justread == 0) { ! 560: /* there is no data */ ! 561: err_flag = 1; ! 562: break; ! 563: } ! 564: continue; ! 565: ! 566: case PSFS_ERR_FATAL: ! 567: /* some fatal error. Theoretically, the stream is borked, so all ! 568: * further reads should fail. */ ! 569: err_flag = 1; ! 570: break; ! 571: } ! 572: ! 573: if (justread == 0 || justread == (size_t)-1) { ! 574: break; ! 575: } ! 576: } ! 577: ! 578: efree(chunk_buf); ! 579: ! 580: } else { ! 581: /* is there enough data in the buffer ? */ ! 582: if (stream->writepos - stream->readpos < (off_t)size) { ! 583: size_t justread = 0; ! 584: ! 585: /* reduce buffer memory consumption if possible, to avoid a realloc */ ! 586: if (stream->readbuf && stream->readbuflen - stream->writepos < stream->chunk_size) { ! 587: memmove(stream->readbuf, stream->readbuf + stream->readpos, stream->readbuflen - stream->readpos); ! 588: stream->writepos -= stream->readpos; ! 589: stream->readpos = 0; ! 590: } ! 591: ! 592: /* grow the buffer if required ! 593: * TODO: this can fail for persistent streams */ ! 594: if (stream->readbuflen - stream->writepos < stream->chunk_size) { ! 595: stream->readbuflen += stream->chunk_size; ! 596: stream->readbuf = perealloc(stream->readbuf, stream->readbuflen, ! 597: stream->is_persistent); ! 598: } ! 599: ! 600: justread = stream->ops->read(stream, stream->readbuf + stream->writepos, ! 601: stream->readbuflen - stream->writepos ! 602: TSRMLS_CC); ! 603: ! 604: if (justread != (size_t)-1) { ! 605: stream->writepos += justread; ! 606: } ! 607: } ! 608: } ! 609: } ! 610: ! 611: PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size TSRMLS_DC) ! 612: { ! 613: size_t toread = 0, didread = 0; ! 614: ! 615: while (size > 0) { ! 616: ! 617: /* take from the read buffer first. ! 618: * It is possible that a buffered stream was switched to non-buffered, so we ! 619: * drain the remainder of the buffer before using the "raw" read mode for ! 620: * the excess */ ! 621: if (stream->writepos > stream->readpos) { ! 622: ! 623: toread = stream->writepos - stream->readpos; ! 624: if (toread > size) { ! 625: toread = size; ! 626: } ! 627: ! 628: memcpy(buf, stream->readbuf + stream->readpos, toread); ! 629: stream->readpos += toread; ! 630: size -= toread; ! 631: buf += toread; ! 632: didread += toread; ! 633: } ! 634: ! 635: /* ignore eof here; the underlying state might have changed */ ! 636: if (size == 0) { ! 637: break; ! 638: } ! 639: ! 640: if (!stream->readfilters.head && (stream->flags & PHP_STREAM_FLAG_NO_BUFFER || stream->chunk_size == 1)) { ! 641: toread = stream->ops->read(stream, buf, size TSRMLS_CC); ! 642: } else { ! 643: php_stream_fill_read_buffer(stream, size TSRMLS_CC); ! 644: ! 645: toread = stream->writepos - stream->readpos; ! 646: if (toread > size) { ! 647: toread = size; ! 648: } ! 649: ! 650: if (toread > 0) { ! 651: memcpy(buf, stream->readbuf + stream->readpos, toread); ! 652: stream->readpos += toread; ! 653: } ! 654: } ! 655: if (toread > 0) { ! 656: didread += toread; ! 657: buf += toread; ! 658: size -= toread; ! 659: } else { ! 660: /* EOF, or temporary end of data (for non-blocking mode). */ ! 661: break; ! 662: } ! 663: ! 664: /* just break anyway, to avoid greedy read */ ! 665: if (stream->wrapper != &php_plain_files_wrapper) { ! 666: break; ! 667: } ! 668: } ! 669: ! 670: if (didread > 0) { ! 671: stream->position += didread; ! 672: } ! 673: ! 674: return didread; ! 675: } ! 676: ! 677: PHPAPI int _php_stream_eof(php_stream *stream TSRMLS_DC) ! 678: { ! 679: /* if there is data in the buffer, it's not EOF */ ! 680: if (stream->writepos - stream->readpos > 0) { ! 681: return 0; ! 682: } ! 683: ! 684: /* use the configured timeout when checking eof */ ! 685: if (!stream->eof && PHP_STREAM_OPTION_RETURN_ERR == ! 686: php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS, ! 687: 0, NULL)) { ! 688: stream->eof = 1; ! 689: } ! 690: ! 691: return stream->eof; ! 692: } ! 693: ! 694: PHPAPI int _php_stream_putc(php_stream *stream, int c TSRMLS_DC) ! 695: { ! 696: unsigned char buf = c; ! 697: ! 698: if (php_stream_write(stream, &buf, 1) > 0) { ! 699: return 1; ! 700: } ! 701: return EOF; ! 702: } ! 703: ! 704: PHPAPI int _php_stream_getc(php_stream *stream TSRMLS_DC) ! 705: { ! 706: char buf; ! 707: ! 708: if (php_stream_read(stream, &buf, 1) > 0) { ! 709: return buf & 0xff; ! 710: } ! 711: return EOF; ! 712: } ! 713: ! 714: PHPAPI int _php_stream_puts(php_stream *stream, char *buf TSRMLS_DC) ! 715: { ! 716: int len; ! 717: char newline[2] = "\n"; /* is this OK for Win? */ ! 718: len = strlen(buf); ! 719: ! 720: if (len > 0 && php_stream_write(stream, buf, len) && php_stream_write(stream, newline, 1)) { ! 721: return 1; ! 722: } ! 723: return 0; ! 724: } ! 725: ! 726: PHPAPI int _php_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) ! 727: { ! 728: memset(ssb, 0, sizeof(*ssb)); ! 729: ! 730: /* if the stream was wrapped, allow the wrapper to stat it */ ! 731: if (stream->wrapper && stream->wrapper->wops->stream_stat != NULL) { ! 732: return stream->wrapper->wops->stream_stat(stream->wrapper, stream, ssb TSRMLS_CC); ! 733: } ! 734: ! 735: /* if the stream doesn't directly support stat-ing, return with failure. ! 736: * We could try and emulate this by casting to a FD and fstat-ing it, ! 737: * but since the fd might not represent the actual underlying content ! 738: * this would give bogus results. */ ! 739: if (stream->ops->stat == NULL) { ! 740: return -1; ! 741: } ! 742: ! 743: return (stream->ops->stat)(stream, ssb TSRMLS_CC); ! 744: } ! 745: ! 746: PHPAPI char *php_stream_locate_eol(php_stream *stream, char *buf, size_t buf_len TSRMLS_DC) ! 747: { ! 748: size_t avail; ! 749: char *cr, *lf, *eol = NULL; ! 750: char *readptr; ! 751: ! 752: if (!buf) { ! 753: readptr = stream->readbuf + stream->readpos; ! 754: avail = stream->writepos - stream->readpos; ! 755: } else { ! 756: readptr = buf; ! 757: avail = buf_len; ! 758: } ! 759: ! 760: /* Look for EOL */ ! 761: if (stream->flags & PHP_STREAM_FLAG_DETECT_EOL) { ! 762: cr = memchr(readptr, '\r', avail); ! 763: lf = memchr(readptr, '\n', avail); ! 764: ! 765: if (cr && lf != cr + 1 && !(lf && lf < cr)) { ! 766: /* mac */ ! 767: stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL; ! 768: stream->flags |= PHP_STREAM_FLAG_EOL_MAC; ! 769: eol = cr; ! 770: } else if ((cr && lf && cr == lf - 1) || (lf)) { ! 771: /* dos or unix endings */ ! 772: stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL; ! 773: eol = lf; ! 774: } ! 775: } else if (stream->flags & PHP_STREAM_FLAG_EOL_MAC) { ! 776: eol = memchr(readptr, '\r', avail); ! 777: } else { ! 778: /* unix (and dos) line endings */ ! 779: eol = memchr(readptr, '\n', avail); ! 780: } ! 781: ! 782: return eol; ! 783: } ! 784: ! 785: /* If buf == NULL, the buffer will be allocated automatically and will be of an ! 786: * appropriate length to hold the line, regardless of the line length, memory ! 787: * permitting */ ! 788: PHPAPI char *_php_stream_get_line(php_stream *stream, char *buf, size_t maxlen, ! 789: size_t *returned_len TSRMLS_DC) ! 790: { ! 791: size_t avail = 0; ! 792: size_t current_buf_size = 0; ! 793: size_t total_copied = 0; ! 794: int grow_mode = 0; ! 795: char *bufstart = buf; ! 796: ! 797: if (buf == NULL) { ! 798: grow_mode = 1; ! 799: } else if (maxlen == 0) { ! 800: return NULL; ! 801: } ! 802: ! 803: /* ! 804: * If the underlying stream operations block when no new data is readable, ! 805: * we need to take extra precautions. ! 806: * ! 807: * If there is buffered data available, we check for a EOL. If it exists, ! 808: * we pass the data immediately back to the caller. This saves a call ! 809: * to the read implementation and will not block where blocking ! 810: * is not necessary at all. ! 811: * ! 812: * If the stream buffer contains more data than the caller requested, ! 813: * we can also avoid that costly step and simply return that data. ! 814: */ ! 815: ! 816: for (;;) { ! 817: avail = stream->writepos - stream->readpos; ! 818: ! 819: if (avail > 0) { ! 820: size_t cpysz = 0; ! 821: char *readptr; ! 822: char *eol; ! 823: int done = 0; ! 824: ! 825: readptr = stream->readbuf + stream->readpos; ! 826: eol = php_stream_locate_eol(stream, NULL, 0 TSRMLS_CC); ! 827: ! 828: if (eol) { ! 829: cpysz = eol - readptr + 1; ! 830: done = 1; ! 831: } else { ! 832: cpysz = avail; ! 833: } ! 834: ! 835: if (grow_mode) { ! 836: /* allow room for a NUL. If this realloc is really a realloc ! 837: * (ie: second time around), we get an extra byte. In most ! 838: * cases, with the default chunk size of 8K, we will only ! 839: * incur that overhead once. When people have lines longer ! 840: * than 8K, we waste 1 byte per additional 8K or so. ! 841: * That seems acceptable to me, to avoid making this code ! 842: * hard to follow */ ! 843: bufstart = erealloc(bufstart, current_buf_size + cpysz + 1); ! 844: current_buf_size += cpysz + 1; ! 845: buf = bufstart + total_copied; ! 846: } else { ! 847: if (cpysz >= maxlen - 1) { ! 848: cpysz = maxlen - 1; ! 849: done = 1; ! 850: } ! 851: } ! 852: ! 853: memcpy(buf, readptr, cpysz); ! 854: ! 855: stream->position += cpysz; ! 856: stream->readpos += cpysz; ! 857: buf += cpysz; ! 858: maxlen -= cpysz; ! 859: total_copied += cpysz; ! 860: ! 861: if (done) { ! 862: break; ! 863: } ! 864: } else if (stream->eof) { ! 865: break; ! 866: } else { ! 867: /* XXX: Should be fine to always read chunk_size */ ! 868: size_t toread; ! 869: ! 870: if (grow_mode) { ! 871: toread = stream->chunk_size; ! 872: } else { ! 873: toread = maxlen - 1; ! 874: if (toread > stream->chunk_size) { ! 875: toread = stream->chunk_size; ! 876: } ! 877: } ! 878: ! 879: php_stream_fill_read_buffer(stream, toread TSRMLS_CC); ! 880: ! 881: if (stream->writepos - stream->readpos == 0) { ! 882: break; ! 883: } ! 884: } ! 885: } ! 886: ! 887: if (total_copied == 0) { ! 888: if (grow_mode) { ! 889: assert(bufstart == NULL); ! 890: } ! 891: return NULL; ! 892: } ! 893: ! 894: buf[0] = '\0'; ! 895: if (returned_len) { ! 896: *returned_len = total_copied; ! 897: } ! 898: ! 899: return bufstart; ! 900: } ! 901: ! 902: PHPAPI char *php_stream_get_record(php_stream *stream, size_t maxlen, size_t *returned_len, char *delim, size_t delim_len TSRMLS_DC) ! 903: { ! 904: char *e, *buf; ! 905: size_t toread, len; ! 906: int skip = 0; ! 907: ! 908: len = stream->writepos - stream->readpos; ! 909: ! 910: /* make sure the stream read buffer has maxlen bytes */ ! 911: while (len < maxlen) { ! 912: ! 913: size_t just_read; ! 914: toread = MIN(maxlen - len, stream->chunk_size); ! 915: ! 916: php_stream_fill_read_buffer(stream, len + toread TSRMLS_CC); ! 917: ! 918: just_read = (stream->writepos - stream->readpos) - len; ! 919: len += just_read; ! 920: ! 921: /* Assume the stream is temporarily or permanently out of data */ ! 922: if (just_read == 0) { ! 923: break; ! 924: } ! 925: } ! 926: ! 927: if (delim_len == 0 || !delim) { ! 928: toread = maxlen; ! 929: } else { ! 930: size_t seek_len; ! 931: ! 932: /* set the maximum number of bytes we're allowed to read from buffer */ ! 933: seek_len = stream->writepos - stream->readpos; ! 934: if (seek_len > maxlen) { ! 935: seek_len = maxlen; ! 936: } ! 937: ! 938: if (delim_len == 1) { ! 939: e = memchr(stream->readbuf + stream->readpos, *delim, seek_len); ! 940: } else { ! 941: e = php_memnstr(stream->readbuf + stream->readpos, delim, delim_len, (stream->readbuf + stream->readpos + seek_len)); ! 942: } ! 943: ! 944: if (!e) { ! 945: /* return with error if the delimiter string was not found, we ! 946: * could not completely fill the read buffer with maxlen bytes ! 947: * and we don't know we've reached end of file. Added with ! 948: * non-blocking streams in mind, where this situation is frequent */ ! 949: if (seek_len < maxlen && !stream->eof) { ! 950: return NULL; ! 951: } ! 952: toread = maxlen; ! 953: } else { ! 954: toread = e - (char *) stream->readbuf - stream->readpos; ! 955: /* we found the delimiter, so advance the read pointer past it */ ! 956: skip = 1; ! 957: } ! 958: } ! 959: ! 960: if (toread > maxlen && maxlen > 0) { ! 961: toread = maxlen; ! 962: } ! 963: ! 964: buf = emalloc(toread + 1); ! 965: *returned_len = php_stream_read(stream, buf, toread); ! 966: ! 967: if (skip) { ! 968: stream->readpos += delim_len; ! 969: stream->position += delim_len; ! 970: } ! 971: buf[*returned_len] = '\0'; ! 972: return buf; ! 973: } ! 974: ! 975: /* Writes a buffer directly to a stream, using multiple of the chunk size */ ! 976: static size_t _php_stream_write_buffer(php_stream *stream, const char *buf, size_t count TSRMLS_DC) ! 977: { ! 978: size_t didwrite = 0, towrite, justwrote; ! 979: ! 980: /* if we have a seekable stream we need to ensure that data is written at the ! 981: * current stream->position. This means invalidating the read buffer and then ! 982: * performing a low-level seek */ ! 983: if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0 && stream->readpos != stream->writepos) { ! 984: stream->readpos = stream->writepos = 0; ! 985: ! 986: stream->ops->seek(stream, stream->position, SEEK_SET, &stream->position TSRMLS_CC); ! 987: } ! 988: ! 989: ! 990: while (count > 0) { ! 991: towrite = count; ! 992: if (towrite > stream->chunk_size) ! 993: towrite = stream->chunk_size; ! 994: ! 995: justwrote = stream->ops->write(stream, buf, towrite TSRMLS_CC); ! 996: ! 997: /* convert justwrote to an integer, since normally it is unsigned */ ! 998: if ((int)justwrote > 0) { ! 999: buf += justwrote; ! 1000: count -= justwrote; ! 1001: didwrite += justwrote; ! 1002: ! 1003: /* Only screw with the buffer if we can seek, otherwise we lose data ! 1004: * buffered from fifos and sockets */ ! 1005: if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) { ! 1006: stream->position += justwrote; ! 1007: } ! 1008: } else { ! 1009: break; ! 1010: } ! 1011: } ! 1012: return didwrite; ! 1013: ! 1014: } ! 1015: ! 1016: /* push some data through the write filter chain. ! 1017: * buf may be NULL, if flags are set to indicate a flush. ! 1018: * This may trigger a real write to the stream. ! 1019: * Returns the number of bytes consumed from buf by the first filter in the chain. ! 1020: * */ ! 1021: static size_t _php_stream_write_filtered(php_stream *stream, const char *buf, size_t count, int flags TSRMLS_DC) ! 1022: { ! 1023: size_t consumed = 0; ! 1024: php_stream_bucket *bucket; ! 1025: php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL }; ! 1026: php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap; ! 1027: php_stream_filter_status_t status = PSFS_ERR_FATAL; ! 1028: php_stream_filter *filter; ! 1029: ! 1030: if (buf) { ! 1031: bucket = php_stream_bucket_new(stream, (char *)buf, count, 0, 0 TSRMLS_CC); ! 1032: php_stream_bucket_append(&brig_in, bucket TSRMLS_CC); ! 1033: } ! 1034: ! 1035: for (filter = stream->writefilters.head; filter; filter = filter->next) { ! 1036: /* for our return value, we are interested in the number of bytes consumed from ! 1037: * the first filter in the chain */ ! 1038: status = filter->fops->filter(stream, filter, brig_inp, brig_outp, ! 1039: filter == stream->writefilters.head ? &consumed : NULL, flags TSRMLS_CC); ! 1040: ! 1041: if (status != PSFS_PASS_ON) { ! 1042: break; ! 1043: } ! 1044: /* brig_out becomes brig_in. ! 1045: * brig_in will always be empty here, as the filter MUST attach any un-consumed buckets ! 1046: * to its own brigade */ ! 1047: brig_swap = brig_inp; ! 1048: brig_inp = brig_outp; ! 1049: brig_outp = brig_swap; ! 1050: memset(brig_outp, 0, sizeof(*brig_outp)); ! 1051: } ! 1052: ! 1053: switch (status) { ! 1054: case PSFS_PASS_ON: ! 1055: /* filter chain generated some output; push it through to the ! 1056: * underlying stream */ ! 1057: while (brig_inp->head) { ! 1058: bucket = brig_inp->head; ! 1059: _php_stream_write_buffer(stream, bucket->buf, bucket->buflen TSRMLS_CC); ! 1060: /* Potential error situation - eg: no space on device. Perhaps we should keep this brigade ! 1061: * hanging around and try to write it later. ! 1062: * At the moment, we just drop it on the floor ! 1063: * */ ! 1064: ! 1065: php_stream_bucket_unlink(bucket TSRMLS_CC); ! 1066: php_stream_bucket_delref(bucket TSRMLS_CC); ! 1067: } ! 1068: break; ! 1069: case PSFS_FEED_ME: ! 1070: /* need more data before we can push data through to the stream */ ! 1071: break; ! 1072: ! 1073: case PSFS_ERR_FATAL: ! 1074: /* some fatal error. Theoretically, the stream is borked, so all ! 1075: * further writes should fail. */ ! 1076: break; ! 1077: } ! 1078: ! 1079: return consumed; ! 1080: } ! 1081: ! 1082: PHPAPI int _php_stream_flush(php_stream *stream, int closing TSRMLS_DC) ! 1083: { ! 1084: int ret = 0; ! 1085: ! 1086: if (stream->writefilters.head) { ! 1087: _php_stream_write_filtered(stream, NULL, 0, closing ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC TSRMLS_CC); ! 1088: } ! 1089: ! 1090: if (stream->ops->flush) { ! 1091: ret = stream->ops->flush(stream TSRMLS_CC); ! 1092: } ! 1093: ! 1094: return ret; ! 1095: } ! 1096: ! 1097: PHPAPI size_t _php_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) ! 1098: { ! 1099: if (buf == NULL || count == 0 || stream->ops->write == NULL) { ! 1100: return 0; ! 1101: } ! 1102: ! 1103: if (stream->writefilters.head) { ! 1104: return _php_stream_write_filtered(stream, buf, count, PSFS_FLAG_NORMAL TSRMLS_CC); ! 1105: } else { ! 1106: return _php_stream_write_buffer(stream, buf, count TSRMLS_CC); ! 1107: } ! 1108: } ! 1109: ! 1110: PHPAPI size_t _php_stream_printf(php_stream *stream TSRMLS_DC, const char *fmt, ...) ! 1111: { ! 1112: size_t count; ! 1113: char *buf; ! 1114: va_list ap; ! 1115: ! 1116: va_start(ap, fmt); ! 1117: count = vspprintf(&buf, 0, fmt, ap); ! 1118: va_end(ap); ! 1119: ! 1120: if (!buf) { ! 1121: return 0; /* error condition */ ! 1122: } ! 1123: ! 1124: count = php_stream_write(stream, buf, count); ! 1125: efree(buf); ! 1126: ! 1127: return count; ! 1128: } ! 1129: ! 1130: PHPAPI off_t _php_stream_tell(php_stream *stream TSRMLS_DC) ! 1131: { ! 1132: return stream->position; ! 1133: } ! 1134: ! 1135: PHPAPI int _php_stream_seek(php_stream *stream, off_t offset, int whence TSRMLS_DC) ! 1136: { ! 1137: if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) { ! 1138: /* flush to commit data written to the fopencookie FILE* */ ! 1139: fflush(stream->stdiocast); ! 1140: } ! 1141: ! 1142: /* handle the case where we are in the buffer */ ! 1143: if ((stream->flags & PHP_STREAM_FLAG_NO_BUFFER) == 0) { ! 1144: switch(whence) { ! 1145: case SEEK_CUR: ! 1146: if (offset > 0 && offset <= stream->writepos - stream->readpos) { ! 1147: stream->readpos += offset; /* if offset = ..., then readpos = writepos */ ! 1148: stream->position += offset; ! 1149: stream->eof = 0; ! 1150: return 0; ! 1151: } ! 1152: break; ! 1153: case SEEK_SET: ! 1154: if (offset > stream->position && ! 1155: offset <= stream->position + stream->writepos - stream->readpos) { ! 1156: stream->readpos += offset - stream->position; ! 1157: stream->position = offset; ! 1158: stream->eof = 0; ! 1159: return 0; ! 1160: } ! 1161: break; ! 1162: } ! 1163: } ! 1164: ! 1165: ! 1166: if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) { ! 1167: int ret; ! 1168: ! 1169: if (stream->writefilters.head) { ! 1170: _php_stream_flush(stream, 0 TSRMLS_CC); ! 1171: } ! 1172: ! 1173: switch(whence) { ! 1174: case SEEK_CUR: ! 1175: offset = stream->position + offset; ! 1176: whence = SEEK_SET; ! 1177: break; ! 1178: } ! 1179: ret = stream->ops->seek(stream, offset, whence, &stream->position TSRMLS_CC); ! 1180: ! 1181: if (((stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) || ret == 0) { ! 1182: if (ret == 0) { ! 1183: stream->eof = 0; ! 1184: } ! 1185: ! 1186: /* invalidate the buffer contents */ ! 1187: stream->readpos = stream->writepos = 0; ! 1188: ! 1189: return ret; ! 1190: } ! 1191: /* else the stream has decided that it can't support seeking after all; ! 1192: * fall through to attempt emulation */ ! 1193: } ! 1194: ! 1195: /* emulate forward moving seeks with reads */ ! 1196: if (whence == SEEK_CUR && offset >= 0) { ! 1197: char tmp[1024]; ! 1198: size_t didread; ! 1199: while(offset > 0) { ! 1200: if ((didread = php_stream_read(stream, tmp, MIN(offset, sizeof(tmp)))) == 0) { ! 1201: return -1; ! 1202: } ! 1203: offset -= didread; ! 1204: } ! 1205: stream->eof = 0; ! 1206: return 0; ! 1207: } ! 1208: ! 1209: php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream does not support seeking"); ! 1210: ! 1211: return -1; ! 1212: } ! 1213: ! 1214: PHPAPI int _php_stream_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) ! 1215: { ! 1216: int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL; ! 1217: ! 1218: if (stream->ops->set_option) { ! 1219: ret = stream->ops->set_option(stream, option, value, ptrparam TSRMLS_CC); ! 1220: } ! 1221: ! 1222: if (ret == PHP_STREAM_OPTION_RETURN_NOTIMPL) { ! 1223: switch(option) { ! 1224: case PHP_STREAM_OPTION_SET_CHUNK_SIZE: ! 1225: ret = stream->chunk_size; ! 1226: stream->chunk_size = value; ! 1227: return ret; ! 1228: ! 1229: case PHP_STREAM_OPTION_READ_BUFFER: ! 1230: /* try to match the buffer mode as best we can */ ! 1231: if (value == PHP_STREAM_BUFFER_NONE) { ! 1232: stream->flags |= PHP_STREAM_FLAG_NO_BUFFER; ! 1233: } else if (stream->flags & PHP_STREAM_FLAG_NO_BUFFER) { ! 1234: stream->flags ^= PHP_STREAM_FLAG_NO_BUFFER; ! 1235: } ! 1236: ret = PHP_STREAM_OPTION_RETURN_OK; ! 1237: break; ! 1238: ! 1239: default: ! 1240: ; ! 1241: } ! 1242: } ! 1243: ! 1244: return ret; ! 1245: } ! 1246: ! 1247: PHPAPI int _php_stream_truncate_set_size(php_stream *stream, size_t newsize TSRMLS_DC) ! 1248: { ! 1249: return php_stream_set_option(stream, PHP_STREAM_OPTION_TRUNCATE_API, PHP_STREAM_TRUNCATE_SET_SIZE, &newsize); ! 1250: } ! 1251: ! 1252: PHPAPI size_t _php_stream_passthru(php_stream * stream STREAMS_DC TSRMLS_DC) ! 1253: { ! 1254: size_t bcount = 0; ! 1255: char buf[8192]; ! 1256: int b; ! 1257: ! 1258: if (php_stream_mmap_possible(stream)) { ! 1259: char *p; ! 1260: size_t mapped; ! 1261: ! 1262: p = php_stream_mmap_range(stream, php_stream_tell(stream), PHP_STREAM_MMAP_ALL, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped); ! 1263: ! 1264: if (p) { ! 1265: PHPWRITE(p, mapped); ! 1266: ! 1267: php_stream_mmap_unmap_ex(stream, mapped); ! 1268: ! 1269: return mapped; ! 1270: } ! 1271: } ! 1272: ! 1273: while ((b = php_stream_read(stream, buf, sizeof(buf))) > 0) { ! 1274: PHPWRITE(buf, b); ! 1275: bcount += b; ! 1276: } ! 1277: ! 1278: return bcount; ! 1279: } ! 1280: ! 1281: ! 1282: PHPAPI size_t _php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen, int persistent STREAMS_DC TSRMLS_DC) ! 1283: { ! 1284: size_t ret = 0; ! 1285: char *ptr; ! 1286: size_t len = 0, max_len; ! 1287: int step = CHUNK_SIZE; ! 1288: int min_room = CHUNK_SIZE / 4; ! 1289: php_stream_statbuf ssbuf; ! 1290: ! 1291: if (maxlen == 0) { ! 1292: return 0; ! 1293: } ! 1294: ! 1295: if (maxlen == PHP_STREAM_COPY_ALL) { ! 1296: maxlen = 0; ! 1297: } ! 1298: ! 1299: if (maxlen > 0) { ! 1300: ptr = *buf = pemalloc_rel_orig(maxlen + 1, persistent); ! 1301: while ((len < maxlen) && !php_stream_eof(src)) { ! 1302: ret = php_stream_read(src, ptr, maxlen - len); ! 1303: if (!ret) { ! 1304: break; ! 1305: } ! 1306: len += ret; ! 1307: ptr += ret; ! 1308: } ! 1309: *ptr = '\0'; ! 1310: return len; ! 1311: } ! 1312: ! 1313: /* avoid many reallocs by allocating a good sized chunk to begin with, if ! 1314: * we can. Note that the stream may be filtered, in which case the stat ! 1315: * result may be inaccurate, as the filter may inflate or deflate the ! 1316: * number of bytes that we can read. In order to avoid an upsize followed ! 1317: * by a downsize of the buffer, overestimate by the step size (which is ! 1318: * 2K). */ ! 1319: if (php_stream_stat(src, &ssbuf) == 0 && ssbuf.sb.st_size > 0) { ! 1320: max_len = ssbuf.sb.st_size + step; ! 1321: } else { ! 1322: max_len = step; ! 1323: } ! 1324: ! 1325: ptr = *buf = pemalloc_rel_orig(max_len, persistent); ! 1326: ! 1327: while((ret = php_stream_read(src, ptr, max_len - len))) { ! 1328: len += ret; ! 1329: if (len + min_room >= max_len) { ! 1330: *buf = perealloc_rel_orig(*buf, max_len + step, persistent); ! 1331: max_len += step; ! 1332: ptr = *buf + len; ! 1333: } else { ! 1334: ptr += ret; ! 1335: } ! 1336: } ! 1337: if (len) { ! 1338: *buf = perealloc_rel_orig(*buf, len + 1, persistent); ! 1339: (*buf)[len] = '\0'; ! 1340: } else { ! 1341: pefree(*buf, persistent); ! 1342: *buf = NULL; ! 1343: } ! 1344: return len; ! 1345: } ! 1346: ! 1347: /* Returns SUCCESS/FAILURE and sets *len to the number of bytes moved */ ! 1348: PHPAPI size_t _php_stream_copy_to_stream_ex(php_stream *src, php_stream *dest, size_t maxlen, size_t *len STREAMS_DC TSRMLS_DC) ! 1349: { ! 1350: char buf[CHUNK_SIZE]; ! 1351: size_t readchunk; ! 1352: size_t haveread = 0; ! 1353: size_t didread; ! 1354: size_t dummy; ! 1355: php_stream_statbuf ssbuf; ! 1356: ! 1357: if (!len) { ! 1358: len = &dummy; ! 1359: } ! 1360: ! 1361: if (maxlen == 0) { ! 1362: *len = 0; ! 1363: return SUCCESS; ! 1364: } ! 1365: ! 1366: if (maxlen == PHP_STREAM_COPY_ALL) { ! 1367: maxlen = 0; ! 1368: } ! 1369: ! 1370: if (php_stream_stat(src, &ssbuf) == 0) { ! 1371: if (ssbuf.sb.st_size == 0 ! 1372: #ifdef S_ISREG ! 1373: && S_ISREG(ssbuf.sb.st_mode) ! 1374: #endif ! 1375: ) { ! 1376: *len = 0; ! 1377: return SUCCESS; ! 1378: } ! 1379: } ! 1380: ! 1381: if (php_stream_mmap_possible(src)) { ! 1382: char *p; ! 1383: size_t mapped; ! 1384: ! 1385: p = php_stream_mmap_range(src, php_stream_tell(src), maxlen, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped); ! 1386: ! 1387: if (p) { ! 1388: mapped = php_stream_write(dest, p, mapped); ! 1389: ! 1390: php_stream_mmap_unmap_ex(src, mapped); ! 1391: ! 1392: *len = mapped; ! 1393: ! 1394: /* we've got at least 1 byte to read. ! 1395: * less than 1 is an error */ ! 1396: ! 1397: if (mapped > 0) { ! 1398: return SUCCESS; ! 1399: } ! 1400: return FAILURE; ! 1401: } ! 1402: } ! 1403: ! 1404: while(1) { ! 1405: readchunk = sizeof(buf); ! 1406: ! 1407: if (maxlen && (maxlen - haveread) < readchunk) { ! 1408: readchunk = maxlen - haveread; ! 1409: } ! 1410: ! 1411: didread = php_stream_read(src, buf, readchunk); ! 1412: ! 1413: if (didread) { ! 1414: /* extra paranoid */ ! 1415: size_t didwrite, towrite; ! 1416: char *writeptr; ! 1417: ! 1418: towrite = didread; ! 1419: writeptr = buf; ! 1420: haveread += didread; ! 1421: ! 1422: while(towrite) { ! 1423: didwrite = php_stream_write(dest, writeptr, towrite); ! 1424: if (didwrite == 0) { ! 1425: *len = haveread - (didread - towrite); ! 1426: return FAILURE; ! 1427: } ! 1428: ! 1429: towrite -= didwrite; ! 1430: writeptr += didwrite; ! 1431: } ! 1432: } else { ! 1433: break; ! 1434: } ! 1435: ! 1436: if (maxlen - haveread == 0) { ! 1437: break; ! 1438: } ! 1439: } ! 1440: ! 1441: *len = haveread; ! 1442: ! 1443: /* we've got at least 1 byte to read. ! 1444: * less than 1 is an error */ ! 1445: ! 1446: if (haveread > 0 || src->eof) { ! 1447: return SUCCESS; ! 1448: } ! 1449: return FAILURE; ! 1450: } ! 1451: ! 1452: /* Returns the number of bytes moved. ! 1453: * Returns 1 when source len is 0. ! 1454: * Deprecated in favor of php_stream_copy_to_stream_ex() */ ! 1455: ZEND_ATTRIBUTE_DEPRECATED ! 1456: PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size_t maxlen STREAMS_DC TSRMLS_DC) ! 1457: { ! 1458: size_t len; ! 1459: int ret = _php_stream_copy_to_stream_ex(src, dest, maxlen, &len STREAMS_REL_CC TSRMLS_CC); ! 1460: if (ret == SUCCESS && len == 0 && maxlen != 0) { ! 1461: return 1; ! 1462: } ! 1463: return len; ! 1464: } ! 1465: /* }}} */ ! 1466: ! 1467: /* {{{ wrapper init and registration */ ! 1468: ! 1469: static void stream_resource_regular_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) ! 1470: { ! 1471: php_stream *stream = (php_stream*)rsrc->ptr; ! 1472: /* set the return value for pclose */ ! 1473: FG(pclose_ret) = php_stream_free(stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR); ! 1474: } ! 1475: ! 1476: static void stream_resource_persistent_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) ! 1477: { ! 1478: php_stream *stream = (php_stream*)rsrc->ptr; ! 1479: FG(pclose_ret) = php_stream_free(stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR); ! 1480: } ! 1481: ! 1482: void php_shutdown_stream_hashes(TSRMLS_D) ! 1483: { ! 1484: if (FG(stream_wrappers)) { ! 1485: zend_hash_destroy(FG(stream_wrappers)); ! 1486: efree(FG(stream_wrappers)); ! 1487: FG(stream_wrappers) = NULL; ! 1488: } ! 1489: ! 1490: if (FG(stream_filters)) { ! 1491: zend_hash_destroy(FG(stream_filters)); ! 1492: efree(FG(stream_filters)); ! 1493: FG(stream_filters) = NULL; ! 1494: } ! 1495: } ! 1496: ! 1497: int php_init_stream_wrappers(int module_number TSRMLS_DC) ! 1498: { ! 1499: le_stream = zend_register_list_destructors_ex(stream_resource_regular_dtor, NULL, "stream", module_number); ! 1500: le_pstream = zend_register_list_destructors_ex(NULL, stream_resource_persistent_dtor, "persistent stream", module_number); ! 1501: ! 1502: /* Filters are cleaned up by the streams they're attached to */ ! 1503: le_stream_filter = zend_register_list_destructors_ex(NULL, NULL, "stream filter", module_number); ! 1504: ! 1505: return ( ! 1506: zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1) == SUCCESS ! 1507: && ! 1508: zend_hash_init(php_get_stream_filters_hash_global(), 0, NULL, NULL, 1) == SUCCESS ! 1509: && ! 1510: zend_hash_init(php_stream_xport_get_hash(), 0, NULL, NULL, 1) == SUCCESS ! 1511: && ! 1512: php_stream_xport_register("tcp", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS ! 1513: && ! 1514: php_stream_xport_register("udp", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS ! 1515: #if defined(AF_UNIX) && !(defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE)) ! 1516: && ! 1517: php_stream_xport_register("unix", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS ! 1518: && ! 1519: php_stream_xport_register("udg", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS ! 1520: #endif ! 1521: ) ? SUCCESS : FAILURE; ! 1522: } ! 1523: ! 1524: int php_shutdown_stream_wrappers(int module_number TSRMLS_DC) ! 1525: { ! 1526: zend_hash_destroy(&url_stream_wrappers_hash); ! 1527: zend_hash_destroy(php_get_stream_filters_hash_global()); ! 1528: zend_hash_destroy(php_stream_xport_get_hash()); ! 1529: return SUCCESS; ! 1530: } ! 1531: ! 1532: /* Validate protocol scheme names during registration ! 1533: * Must conform to /^[a-zA-Z0-9+.-]+$/ ! 1534: */ ! 1535: static inline int php_stream_wrapper_scheme_validate(char *protocol, int protocol_len) ! 1536: { ! 1537: int i; ! 1538: ! 1539: for(i = 0; i < protocol_len; i++) { ! 1540: if (!isalnum((int)protocol[i]) && ! 1541: protocol[i] != '+' && ! 1542: protocol[i] != '-' && ! 1543: protocol[i] != '.') { ! 1544: return FAILURE; ! 1545: } ! 1546: } ! 1547: ! 1548: return SUCCESS; ! 1549: } ! 1550: ! 1551: /* API for registering GLOBAL wrappers */ ! 1552: PHPAPI int php_register_url_stream_wrapper(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC) ! 1553: { ! 1554: int protocol_len = strlen(protocol); ! 1555: ! 1556: if (php_stream_wrapper_scheme_validate(protocol, protocol_len) == FAILURE) { ! 1557: return FAILURE; ! 1558: } ! 1559: ! 1560: return zend_hash_add(&url_stream_wrappers_hash, protocol, protocol_len + 1, &wrapper, sizeof(wrapper), NULL); ! 1561: } ! 1562: ! 1563: PHPAPI int php_unregister_url_stream_wrapper(char *protocol TSRMLS_DC) ! 1564: { ! 1565: return zend_hash_del(&url_stream_wrappers_hash, protocol, strlen(protocol) + 1); ! 1566: } ! 1567: ! 1568: static void clone_wrapper_hash(TSRMLS_D) ! 1569: { ! 1570: php_stream_wrapper *tmp; ! 1571: ! 1572: ALLOC_HASHTABLE(FG(stream_wrappers)); ! 1573: zend_hash_init(FG(stream_wrappers), zend_hash_num_elements(&url_stream_wrappers_hash), NULL, NULL, 1); ! 1574: zend_hash_copy(FG(stream_wrappers), &url_stream_wrappers_hash, NULL, &tmp, sizeof(tmp)); ! 1575: } ! 1576: ! 1577: /* API for registering VOLATILE wrappers */ ! 1578: PHPAPI int php_register_url_stream_wrapper_volatile(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC) ! 1579: { ! 1580: int protocol_len = strlen(protocol); ! 1581: ! 1582: if (php_stream_wrapper_scheme_validate(protocol, protocol_len) == FAILURE) { ! 1583: return FAILURE; ! 1584: } ! 1585: ! 1586: if (!FG(stream_wrappers)) { ! 1587: clone_wrapper_hash(TSRMLS_C); ! 1588: } ! 1589: ! 1590: return zend_hash_add(FG(stream_wrappers), protocol, protocol_len + 1, &wrapper, sizeof(wrapper), NULL); ! 1591: } ! 1592: ! 1593: PHPAPI int php_unregister_url_stream_wrapper_volatile(char *protocol TSRMLS_DC) ! 1594: { ! 1595: if (!FG(stream_wrappers)) { ! 1596: clone_wrapper_hash(TSRMLS_C); ! 1597: } ! 1598: ! 1599: return zend_hash_del(FG(stream_wrappers), protocol, strlen(protocol) + 1); ! 1600: } ! 1601: /* }}} */ ! 1602: ! 1603: /* {{{ php_stream_locate_url_wrapper */ ! 1604: PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, char **path_for_open, int options TSRMLS_DC) ! 1605: { ! 1606: HashTable *wrapper_hash = (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash); ! 1607: php_stream_wrapper **wrapperpp = NULL; ! 1608: const char *p, *protocol = NULL; ! 1609: int n = 0; ! 1610: ! 1611: if (path_for_open) { ! 1612: *path_for_open = (char*)path; ! 1613: } ! 1614: ! 1615: if (options & IGNORE_URL) { ! 1616: return (options & STREAM_LOCATE_WRAPPERS_ONLY) ? NULL : &php_plain_files_wrapper; ! 1617: } ! 1618: ! 1619: for (p = path; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) { ! 1620: n++; ! 1621: } ! 1622: ! 1623: if ((*p == ':') && (n > 1) && (!strncmp("//", p+1, 2) || (n == 4 && !memcmp("data:", path, 5)))) { ! 1624: protocol = path; ! 1625: } else if (n == 5 && strncasecmp(path, "zlib:", 5) == 0) { ! 1626: /* BC with older php scripts and zlib wrapper */ ! 1627: protocol = "compress.zlib"; ! 1628: n = 13; ! 1629: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Use of \"zlib:\" wrapper is deprecated; please use \"compress.zlib://\" instead"); ! 1630: } ! 1631: ! 1632: if (protocol) { ! 1633: char *tmp = estrndup(protocol, n); ! 1634: if (FAILURE == zend_hash_find(wrapper_hash, (char*)tmp, n + 1, (void**)&wrapperpp)) { ! 1635: php_strtolower(tmp, n); ! 1636: if (FAILURE == zend_hash_find(wrapper_hash, (char*)tmp, n + 1, (void**)&wrapperpp)) { ! 1637: char wrapper_name[32]; ! 1638: ! 1639: if (n >= sizeof(wrapper_name)) { ! 1640: n = sizeof(wrapper_name) - 1; ! 1641: } ! 1642: PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n); ! 1643: ! 1644: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to find the wrapper \"%s\" - did you forget to enable it when you configured PHP?", wrapper_name); ! 1645: ! 1646: wrapperpp = NULL; ! 1647: protocol = NULL; ! 1648: } ! 1649: } ! 1650: efree(tmp); ! 1651: } ! 1652: /* TODO: curl based streams probably support file:// properly */ ! 1653: if (!protocol || !strncasecmp(protocol, "file", n)) { ! 1654: /* fall back on regular file access */ ! 1655: php_stream_wrapper *plain_files_wrapper = &php_plain_files_wrapper; ! 1656: ! 1657: if (protocol) { ! 1658: int localhost = 0; ! 1659: ! 1660: if (!strncasecmp(path, "file://localhost/", 17)) { ! 1661: localhost = 1; ! 1662: } ! 1663: ! 1664: #ifdef PHP_WIN32 ! 1665: if (localhost == 0 && path[n+3] != '\0' && path[n+3] != '/' && path[n+4] != ':') { ! 1666: #else ! 1667: if (localhost == 0 && path[n+3] != '\0' && path[n+3] != '/') { ! 1668: #endif ! 1669: if (options & REPORT_ERRORS) { ! 1670: php_error_docref(NULL TSRMLS_CC, E_WARNING, "remote host file access not supported, %s", path); ! 1671: } ! 1672: return NULL; ! 1673: } ! 1674: ! 1675: if (path_for_open) { ! 1676: /* skip past protocol and :/, but handle windows correctly */ ! 1677: *path_for_open = (char*)path + n + 1; ! 1678: if (localhost == 1) { ! 1679: (*path_for_open) += 11; ! 1680: } ! 1681: while (*(++*path_for_open)=='/'); ! 1682: #ifdef PHP_WIN32 ! 1683: if (*(*path_for_open + 1) != ':') ! 1684: #endif ! 1685: (*path_for_open)--; ! 1686: } ! 1687: } ! 1688: ! 1689: if (options & STREAM_LOCATE_WRAPPERS_ONLY) { ! 1690: return NULL; ! 1691: } ! 1692: ! 1693: if (FG(stream_wrappers)) { ! 1694: /* The file:// wrapper may have been disabled/overridden */ ! 1695: ! 1696: if (wrapperpp) { ! 1697: /* It was found so go ahead and provide it */ ! 1698: return *wrapperpp; ! 1699: } ! 1700: ! 1701: /* Check again, the original check might have not known the protocol name */ ! 1702: if (zend_hash_find(wrapper_hash, "file", sizeof("file"), (void**)&wrapperpp) == SUCCESS) { ! 1703: return *wrapperpp; ! 1704: } ! 1705: ! 1706: if (options & REPORT_ERRORS) { ! 1707: php_error_docref(NULL TSRMLS_CC, E_WARNING, "file:// wrapper is disabled in the server configuration"); ! 1708: } ! 1709: return NULL; ! 1710: } ! 1711: ! 1712: return plain_files_wrapper; ! 1713: } ! 1714: ! 1715: if (wrapperpp && (*wrapperpp)->is_url && ! 1716: (options & STREAM_DISABLE_URL_PROTECTION) == 0 && ! 1717: (!PG(allow_url_fopen) || ! 1718: (((options & STREAM_OPEN_FOR_INCLUDE) || ! 1719: PG(in_user_include)) && !PG(allow_url_include)))) { ! 1720: if (options & REPORT_ERRORS) { ! 1721: /* protocol[n] probably isn't '\0' */ ! 1722: char *protocol_dup = estrndup(protocol, n); ! 1723: if (!PG(allow_url_fopen)) { ! 1724: php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s:// wrapper is disabled in the server configuration by allow_url_fopen=0", protocol_dup); ! 1725: } else { ! 1726: php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s:// wrapper is disabled in the server configuration by allow_url_include=0", protocol_dup); ! 1727: } ! 1728: efree(protocol_dup); ! 1729: } ! 1730: return NULL; ! 1731: } ! 1732: ! 1733: return *wrapperpp; ! 1734: } ! 1735: /* }}} */ ! 1736: ! 1737: /* {{{ _php_stream_mkdir ! 1738: */ ! 1739: PHPAPI int _php_stream_mkdir(char *path, int mode, int options, php_stream_context *context TSRMLS_DC) ! 1740: { ! 1741: php_stream_wrapper *wrapper = NULL; ! 1742: ! 1743: wrapper = php_stream_locate_url_wrapper(path, NULL, ENFORCE_SAFE_MODE TSRMLS_CC); ! 1744: if (!wrapper || !wrapper->wops || !wrapper->wops->stream_mkdir) { ! 1745: return 0; ! 1746: } ! 1747: ! 1748: return wrapper->wops->stream_mkdir(wrapper, path, mode, options, context TSRMLS_CC); ! 1749: } ! 1750: /* }}} */ ! 1751: ! 1752: /* {{{ _php_stream_rmdir ! 1753: */ ! 1754: PHPAPI int _php_stream_rmdir(char *path, int options, php_stream_context *context TSRMLS_DC) ! 1755: { ! 1756: php_stream_wrapper *wrapper = NULL; ! 1757: ! 1758: wrapper = php_stream_locate_url_wrapper(path, NULL, ENFORCE_SAFE_MODE TSRMLS_CC); ! 1759: if (!wrapper || !wrapper->wops || !wrapper->wops->stream_rmdir) { ! 1760: return 0; ! 1761: } ! 1762: ! 1763: return wrapper->wops->stream_rmdir(wrapper, path, options, context TSRMLS_CC); ! 1764: } ! 1765: /* }}} */ ! 1766: ! 1767: /* {{{ _php_stream_stat_path */ ! 1768: PHPAPI int _php_stream_stat_path(char *path, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC) ! 1769: { ! 1770: php_stream_wrapper *wrapper = NULL; ! 1771: char *path_to_open = path; ! 1772: int ret; ! 1773: ! 1774: /* Try to hit the cache first */ ! 1775: if (flags & PHP_STREAM_URL_STAT_LINK) { ! 1776: if (BG(CurrentLStatFile) && strcmp(path, BG(CurrentLStatFile)) == 0) { ! 1777: memcpy(ssb, &BG(lssb), sizeof(php_stream_statbuf)); ! 1778: return 0; ! 1779: } ! 1780: } else { ! 1781: if (BG(CurrentStatFile) && strcmp(path, BG(CurrentStatFile)) == 0) { ! 1782: memcpy(ssb, &BG(ssb), sizeof(php_stream_statbuf)); ! 1783: return 0; ! 1784: } ! 1785: } ! 1786: ! 1787: wrapper = php_stream_locate_url_wrapper(path, &path_to_open, ENFORCE_SAFE_MODE TSRMLS_CC); ! 1788: if (wrapper && wrapper->wops->url_stat) { ! 1789: ret = wrapper->wops->url_stat(wrapper, path_to_open, flags, ssb, context TSRMLS_CC); ! 1790: if (ret == 0) { ! 1791: /* Drop into cache */ ! 1792: if (flags & PHP_STREAM_URL_STAT_LINK) { ! 1793: if (BG(CurrentLStatFile)) { ! 1794: efree(BG(CurrentLStatFile)); ! 1795: } ! 1796: BG(CurrentLStatFile) = estrdup(path); ! 1797: memcpy(&BG(lssb), ssb, sizeof(php_stream_statbuf)); ! 1798: } else { ! 1799: if (BG(CurrentStatFile)) { ! 1800: efree(BG(CurrentStatFile)); ! 1801: } ! 1802: BG(CurrentStatFile) = estrdup(path); ! 1803: memcpy(&BG(ssb), ssb, sizeof(php_stream_statbuf)); ! 1804: } ! 1805: } ! 1806: return ret; ! 1807: } ! 1808: return -1; ! 1809: } ! 1810: /* }}} */ ! 1811: ! 1812: /* {{{ php_stream_opendir */ ! 1813: PHPAPI php_stream *_php_stream_opendir(char *path, int options, ! 1814: php_stream_context *context STREAMS_DC TSRMLS_DC) ! 1815: { ! 1816: php_stream *stream = NULL; ! 1817: php_stream_wrapper *wrapper = NULL; ! 1818: char *path_to_open; ! 1819: ! 1820: if (!path || !*path) { ! 1821: return NULL; ! 1822: } ! 1823: ! 1824: path_to_open = path; ! 1825: ! 1826: wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options TSRMLS_CC); ! 1827: ! 1828: if (wrapper && wrapper->wops->dir_opener) { ! 1829: stream = wrapper->wops->dir_opener(wrapper, ! 1830: path_to_open, "r", options ^ REPORT_ERRORS, NULL, ! 1831: context STREAMS_REL_CC TSRMLS_CC); ! 1832: ! 1833: if (stream) { ! 1834: stream->wrapper = wrapper; ! 1835: stream->flags |= PHP_STREAM_FLAG_NO_BUFFER | PHP_STREAM_FLAG_IS_DIR; ! 1836: } ! 1837: } else if (wrapper) { ! 1838: php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS TSRMLS_CC, "not implemented"); ! 1839: } ! 1840: if (stream == NULL && (options & REPORT_ERRORS)) { ! 1841: php_stream_display_wrapper_errors(wrapper, path, "failed to open dir" TSRMLS_CC); ! 1842: } ! 1843: php_stream_tidy_wrapper_error_log(wrapper TSRMLS_CC); ! 1844: ! 1845: return stream; ! 1846: } ! 1847: /* }}} */ ! 1848: ! 1849: /* {{{ _php_stream_readdir */ ! 1850: PHPAPI php_stream_dirent *_php_stream_readdir(php_stream *dirstream, php_stream_dirent *ent TSRMLS_DC) ! 1851: { ! 1852: ! 1853: if (sizeof(php_stream_dirent) == php_stream_read(dirstream, (char*)ent, sizeof(php_stream_dirent))) { ! 1854: return ent; ! 1855: } ! 1856: ! 1857: return NULL; ! 1858: } ! 1859: /* }}} */ ! 1860: ! 1861: /* {{{ php_stream_open_wrapper_ex */ ! 1862: PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int options, ! 1863: char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) ! 1864: { ! 1865: php_stream *stream = NULL; ! 1866: php_stream_wrapper *wrapper = NULL; ! 1867: char *path_to_open; ! 1868: int persistent = options & STREAM_OPEN_PERSISTENT; ! 1869: char *resolved_path = NULL; ! 1870: char *copy_of_path = NULL; ! 1871: ! 1872: ! 1873: if (opened_path) { ! 1874: *opened_path = NULL; ! 1875: } ! 1876: ! 1877: if (!path || !*path) { ! 1878: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filename cannot be empty"); ! 1879: return NULL; ! 1880: } ! 1881: ! 1882: if (options & USE_PATH) { ! 1883: resolved_path = zend_resolve_path(path, strlen(path) TSRMLS_CC); ! 1884: if (resolved_path) { ! 1885: path = resolved_path; ! 1886: /* we've found this file, don't re-check include_path or run realpath */ ! 1887: options |= STREAM_ASSUME_REALPATH; ! 1888: options &= ~USE_PATH; ! 1889: } ! 1890: } ! 1891: ! 1892: path_to_open = path; ! 1893: ! 1894: wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options TSRMLS_CC); ! 1895: if (options & STREAM_USE_URL && (!wrapper || !wrapper->is_url)) { ! 1896: php_error_docref(NULL TSRMLS_CC, E_WARNING, "This function may only be used against URLs"); ! 1897: if (resolved_path) { ! 1898: efree(resolved_path); ! 1899: } ! 1900: return NULL; ! 1901: } ! 1902: ! 1903: if (wrapper) { ! 1904: if (!wrapper->wops->stream_opener) { ! 1905: php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS TSRMLS_CC, ! 1906: "wrapper does not support stream open"); ! 1907: } else { ! 1908: stream = wrapper->wops->stream_opener(wrapper, ! 1909: path_to_open, mode, options ^ REPORT_ERRORS, ! 1910: opened_path, context STREAMS_REL_CC TSRMLS_CC); ! 1911: } ! 1912: ! 1913: /* if the caller asked for a persistent stream but the wrapper did not ! 1914: * return one, force an error here */ ! 1915: if (stream && (options & STREAM_OPEN_PERSISTENT) && !stream->is_persistent) { ! 1916: php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS TSRMLS_CC, ! 1917: "wrapper does not support persistent streams"); ! 1918: php_stream_close(stream); ! 1919: stream = NULL; ! 1920: } ! 1921: ! 1922: if (stream) { ! 1923: stream->wrapper = wrapper; ! 1924: } ! 1925: } ! 1926: ! 1927: if (stream) { ! 1928: if (opened_path && !*opened_path && resolved_path) { ! 1929: *opened_path = resolved_path; ! 1930: resolved_path = NULL; ! 1931: } ! 1932: if (stream->orig_path) { ! 1933: pefree(stream->orig_path, persistent); ! 1934: } ! 1935: copy_of_path = pestrdup(path, persistent); ! 1936: stream->orig_path = copy_of_path; ! 1937: #if ZEND_DEBUG ! 1938: stream->open_filename = __zend_orig_filename ? __zend_orig_filename : __zend_filename; ! 1939: stream->open_lineno = __zend_orig_lineno ? __zend_orig_lineno : __zend_lineno; ! 1940: #endif ! 1941: } ! 1942: ! 1943: if (stream != NULL && (options & STREAM_MUST_SEEK)) { ! 1944: php_stream *newstream; ! 1945: ! 1946: switch(php_stream_make_seekable_rel(stream, &newstream, ! 1947: (options & STREAM_WILL_CAST) ! 1948: ? PHP_STREAM_PREFER_STDIO : PHP_STREAM_NO_PREFERENCE)) { ! 1949: case PHP_STREAM_UNCHANGED: ! 1950: if (resolved_path) { ! 1951: efree(resolved_path); ! 1952: } ! 1953: return stream; ! 1954: case PHP_STREAM_RELEASED: ! 1955: if (newstream->orig_path) { ! 1956: pefree(newstream->orig_path, persistent); ! 1957: } ! 1958: newstream->orig_path = pestrdup(path, persistent); ! 1959: if (resolved_path) { ! 1960: efree(resolved_path); ! 1961: } ! 1962: return newstream; ! 1963: default: ! 1964: php_stream_close(stream); ! 1965: stream = NULL; ! 1966: if (options & REPORT_ERRORS) { ! 1967: char *tmp = estrdup(path); ! 1968: php_strip_url_passwd(tmp); ! 1969: php_error_docref1(NULL TSRMLS_CC, tmp, E_WARNING, "could not make seekable - %s", ! 1970: tmp); ! 1971: efree(tmp); ! 1972: ! 1973: options ^= REPORT_ERRORS; ! 1974: } ! 1975: } ! 1976: } ! 1977: ! 1978: if (stream && stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0 && strchr(mode, 'a') && stream->position == 0) { ! 1979: off_t newpos = 0; ! 1980: ! 1981: /* if opened for append, we need to revise our idea of the initial file position */ ! 1982: if (0 == stream->ops->seek(stream, 0, SEEK_CUR, &newpos TSRMLS_CC)) { ! 1983: stream->position = newpos; ! 1984: } ! 1985: } ! 1986: ! 1987: if (stream == NULL && (options & REPORT_ERRORS)) { ! 1988: php_stream_display_wrapper_errors(wrapper, path, "failed to open stream" TSRMLS_CC); ! 1989: if (opened_path && *opened_path) { ! 1990: efree(*opened_path); ! 1991: *opened_path = NULL; ! 1992: } ! 1993: } ! 1994: php_stream_tidy_wrapper_error_log(wrapper TSRMLS_CC); ! 1995: #if ZEND_DEBUG ! 1996: if (stream == NULL && copy_of_path != NULL) { ! 1997: pefree(copy_of_path, persistent); ! 1998: } ! 1999: #endif ! 2000: if (resolved_path) { ! 2001: efree(resolved_path); ! 2002: } ! 2003: return stream; ! 2004: } ! 2005: /* }}} */ ! 2006: ! 2007: /* {{{ context API */ ! 2008: PHPAPI php_stream_context *php_stream_context_set(php_stream *stream, php_stream_context *context) ! 2009: { ! 2010: php_stream_context *oldcontext = stream->context; ! 2011: TSRMLS_FETCH(); ! 2012: ! 2013: stream->context = context; ! 2014: ! 2015: if (context) { ! 2016: zend_list_addref(context->rsrc_id); ! 2017: } ! 2018: if (oldcontext) { ! 2019: zend_list_delete(oldcontext->rsrc_id); ! 2020: } ! 2021: ! 2022: return oldcontext; ! 2023: } ! 2024: ! 2025: PHPAPI void php_stream_notification_notify(php_stream_context *context, int notifycode, int severity, ! 2026: char *xmsg, int xcode, size_t bytes_sofar, size_t bytes_max, void * ptr TSRMLS_DC) ! 2027: { ! 2028: if (context && context->notifier) ! 2029: context->notifier->func(context, notifycode, severity, xmsg, xcode, bytes_sofar, bytes_max, ptr TSRMLS_CC); ! 2030: } ! 2031: ! 2032: PHPAPI void php_stream_context_free(php_stream_context *context) ! 2033: { ! 2034: if (context->options) { ! 2035: zval_ptr_dtor(&context->options); ! 2036: context->options = NULL; ! 2037: } ! 2038: if (context->notifier) { ! 2039: php_stream_notification_free(context->notifier); ! 2040: context->notifier = NULL; ! 2041: } ! 2042: if (context->links) { ! 2043: zval_ptr_dtor(&context->links); ! 2044: context->links = NULL; ! 2045: } ! 2046: efree(context); ! 2047: } ! 2048: ! 2049: PHPAPI php_stream_context *php_stream_context_alloc(void) ! 2050: { ! 2051: php_stream_context *context; ! 2052: ! 2053: context = ecalloc(1, sizeof(php_stream_context)); ! 2054: context->notifier = NULL; ! 2055: MAKE_STD_ZVAL(context->options); ! 2056: array_init(context->options); ! 2057: ! 2058: context->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, context, php_le_stream_context()); ! 2059: return context; ! 2060: } ! 2061: ! 2062: PHPAPI php_stream_notifier *php_stream_notification_alloc(void) ! 2063: { ! 2064: return ecalloc(1, sizeof(php_stream_notifier)); ! 2065: } ! 2066: ! 2067: PHPAPI void php_stream_notification_free(php_stream_notifier *notifier) ! 2068: { ! 2069: if (notifier->dtor) { ! 2070: notifier->dtor(notifier); ! 2071: } ! 2072: efree(notifier); ! 2073: } ! 2074: ! 2075: PHPAPI int php_stream_context_get_option(php_stream_context *context, ! 2076: const char *wrappername, const char *optionname, zval ***optionvalue) ! 2077: { ! 2078: zval **wrapperhash; ! 2079: ! 2080: if (FAILURE == zend_hash_find(Z_ARRVAL_P(context->options), (char*)wrappername, strlen(wrappername)+1, (void**)&wrapperhash)) { ! 2081: return FAILURE; ! 2082: } ! 2083: return zend_hash_find(Z_ARRVAL_PP(wrapperhash), (char*)optionname, strlen(optionname)+1, (void**)optionvalue); ! 2084: } ! 2085: ! 2086: PHPAPI int php_stream_context_set_option(php_stream_context *context, ! 2087: const char *wrappername, const char *optionname, zval *optionvalue) ! 2088: { ! 2089: zval **wrapperhash; ! 2090: zval *category, *copied_val; ! 2091: ! 2092: ALLOC_INIT_ZVAL(copied_val); ! 2093: *copied_val = *optionvalue; ! 2094: zval_copy_ctor(copied_val); ! 2095: INIT_PZVAL(copied_val); ! 2096: ! 2097: if (FAILURE == zend_hash_find(Z_ARRVAL_P(context->options), (char*)wrappername, strlen(wrappername)+1, (void**)&wrapperhash)) { ! 2098: MAKE_STD_ZVAL(category); ! 2099: array_init(category); ! 2100: if (FAILURE == zend_hash_update(Z_ARRVAL_P(context->options), (char*)wrappername, strlen(wrappername)+1, (void**)&category, sizeof(zval *), NULL)) { ! 2101: return FAILURE; ! 2102: } ! 2103: ! 2104: wrapperhash = &category; ! 2105: } ! 2106: return zend_hash_update(Z_ARRVAL_PP(wrapperhash), (char*)optionname, strlen(optionname)+1, (void**)&copied_val, sizeof(zval *), NULL); ! 2107: } ! 2108: ! 2109: PHPAPI int php_stream_context_get_link(php_stream_context *context, ! 2110: const char *hostent, php_stream **stream) ! 2111: { ! 2112: php_stream **pstream; ! 2113: ! 2114: if (!stream || !hostent || !context || !(context->links)) { ! 2115: return FAILURE; ! 2116: } ! 2117: if (SUCCESS == zend_hash_find(Z_ARRVAL_P(context->links), (char*)hostent, strlen(hostent)+1, (void**)&pstream)) { ! 2118: *stream = *pstream; ! 2119: return SUCCESS; ! 2120: } ! 2121: return FAILURE; ! 2122: } ! 2123: ! 2124: PHPAPI int php_stream_context_set_link(php_stream_context *context, ! 2125: const char *hostent, php_stream *stream) ! 2126: { ! 2127: if (!context) { ! 2128: return FAILURE; ! 2129: } ! 2130: if (!context->links) { ! 2131: ALLOC_INIT_ZVAL(context->links); ! 2132: array_init(context->links); ! 2133: } ! 2134: if (!stream) { ! 2135: /* Delete any entry for <hostent> */ ! 2136: return zend_hash_del(Z_ARRVAL_P(context->links), (char*)hostent, strlen(hostent)+1); ! 2137: } ! 2138: return zend_hash_update(Z_ARRVAL_P(context->links), (char*)hostent, strlen(hostent)+1, (void**)&stream, sizeof(php_stream *), NULL); ! 2139: } ! 2140: ! 2141: PHPAPI int php_stream_context_del_link(php_stream_context *context, ! 2142: php_stream *stream) ! 2143: { ! 2144: php_stream **pstream; ! 2145: char *hostent; ! 2146: int ret = SUCCESS; ! 2147: ! 2148: if (!context || !context->links || !stream) { ! 2149: return FAILURE; ! 2150: } ! 2151: ! 2152: for(zend_hash_internal_pointer_reset(Z_ARRVAL_P(context->links)); ! 2153: SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(context->links), (void**)&pstream); ! 2154: zend_hash_move_forward(Z_ARRVAL_P(context->links))) { ! 2155: if (*pstream == stream) { ! 2156: if (SUCCESS == zend_hash_get_current_key(Z_ARRVAL_P(context->links), &hostent, NULL, 0)) { ! 2157: if (FAILURE == zend_hash_del(Z_ARRVAL_P(context->links), (char*)hostent, strlen(hostent)+1)) { ! 2158: ret = FAILURE; ! 2159: } ! 2160: } else { ! 2161: ret = FAILURE; ! 2162: } ! 2163: } ! 2164: } ! 2165: ! 2166: return ret; ! 2167: } ! 2168: /* }}} */ ! 2169: ! 2170: /* {{{ php_stream_dirent_alphasort ! 2171: */ ! 2172: PHPAPI int php_stream_dirent_alphasort(const char **a, const char **b) ! 2173: { ! 2174: return strcoll(*a, *b); ! 2175: } ! 2176: /* }}} */ ! 2177: ! 2178: /* {{{ php_stream_dirent_alphasortr ! 2179: */ ! 2180: PHPAPI int php_stream_dirent_alphasortr(const char **a, const char **b) ! 2181: { ! 2182: return strcoll(*b, *a); ! 2183: } ! 2184: /* }}} */ ! 2185: ! 2186: /* {{{ php_stream_scandir ! 2187: */ ! 2188: PHPAPI int _php_stream_scandir(char *dirname, char **namelist[], int flags, php_stream_context *context, ! 2189: int (*compare) (const char **a, const char **b) TSRMLS_DC) ! 2190: { ! 2191: php_stream *stream; ! 2192: php_stream_dirent sdp; ! 2193: char **vector = NULL; ! 2194: int vector_size = 0; ! 2195: int nfiles = 0; ! 2196: ! 2197: if (!namelist) { ! 2198: return FAILURE; ! 2199: } ! 2200: ! 2201: stream = php_stream_opendir(dirname, ENFORCE_SAFE_MODE | REPORT_ERRORS, context); ! 2202: if (!stream) { ! 2203: return FAILURE; ! 2204: } ! 2205: ! 2206: while (php_stream_readdir(stream, &sdp)) { ! 2207: if (nfiles == vector_size) { ! 2208: if (vector_size == 0) { ! 2209: vector_size = 10; ! 2210: } else { ! 2211: vector_size *= 2; ! 2212: } ! 2213: vector = (char **) erealloc(vector, vector_size * sizeof(char *)); ! 2214: } ! 2215: ! 2216: vector[nfiles] = estrdup(sdp.d_name); ! 2217: ! 2218: nfiles++; ! 2219: } ! 2220: php_stream_closedir(stream); ! 2221: ! 2222: *namelist = vector; ! 2223: ! 2224: if (compare) { ! 2225: qsort(*namelist, nfiles, sizeof(char *), (int(*)(const void *, const void *))compare); ! 2226: } ! 2227: return nfiles; ! 2228: } ! 2229: /* }}} */ ! 2230: ! 2231: /* ! 2232: * Local variables: ! 2233: * tab-width: 4 ! 2234: * c-basic-offset: 4 ! 2235: * End: ! 2236: * vim600: noet sw=4 ts=4 fdm=marker ! 2237: * vim<600: noet sw=4 ts=4 ! 2238: */