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