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