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