1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Wez Furlong <wez@thebrainroom.com> |
14 | Sara Golemon <pollita@php.net> |
15 +----------------------------------------------------------------------+
16 */
17
18 #include "php.h"
19 #include "php_globals.h"
20 #include "ext/standard/file.h"
21 #include "ext/standard/flock_compat.h"
22 #ifdef HAVE_SYS_FILE_H
23 #include <sys/file.h>
24 #endif
25 #include <stddef.h>
26
27 #if HAVE_UTIME
28 # ifdef PHP_WIN32
29 # include <sys/utime.h>
30 # else
31 # include <utime.h>
32 # endif
33 #endif
34 #include "userspace_arginfo.h"
35
36 static int le_protocols;
37
38 struct php_user_stream_wrapper {
39 php_stream_wrapper wrapper;
40 char * protoname;
41 zend_class_entry *ce;
42 zend_resource *resource;
43 };
44
45 static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC);
46 static int user_wrapper_close(php_stream_wrapper *wrapper, php_stream *stream);
47 static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context);
48 static int user_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context);
49 static int user_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from, const char *url_to, int options, php_stream_context *context);
50 static int user_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url, int mode, int options, php_stream_context *context);
51 static int user_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context);
52 static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, int option, void *value, php_stream_context *context);
53 static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char *filename, const char *mode,
54 int options, zend_string **opened_path, php_stream_context *context STREAMS_DC);
55
56 static const php_stream_wrapper_ops user_stream_wops = {
57 user_wrapper_opener,
58 user_wrapper_close,
59 NULL, /* stat - the streams themselves know how */
60 user_wrapper_stat_url,
61 user_wrapper_opendir,
62 "user-space",
63 user_wrapper_unlink,
64 user_wrapper_rename,
65 user_wrapper_mkdir,
66 user_wrapper_rmdir,
67 user_wrapper_metadata
68 };
69
70
stream_wrapper_dtor(zend_resource * rsrc)71 static void stream_wrapper_dtor(zend_resource *rsrc)
72 {
73 struct php_user_stream_wrapper * uwrap = (struct php_user_stream_wrapper*)rsrc->ptr;
74
75 efree(uwrap->protoname);
76 efree(uwrap);
77 }
78
79
PHP_MINIT_FUNCTION(user_streams)80 PHP_MINIT_FUNCTION(user_streams)
81 {
82 le_protocols = zend_register_list_destructors_ex(stream_wrapper_dtor, NULL, "stream factory", 0);
83 if (le_protocols == FAILURE)
84 return FAILURE;
85
86 register_userspace_symbols(module_number);
87
88 return SUCCESS;
89 }
90
91 struct _php_userstream_data {
92 struct php_user_stream_wrapper * wrapper;
93 zval object;
94 };
95 typedef struct _php_userstream_data php_userstream_data_t;
96
97 /* names of methods */
98 #define USERSTREAM_OPEN "stream_open"
99 #define USERSTREAM_CLOSE "stream_close"
100 #define USERSTREAM_READ "stream_read"
101 #define USERSTREAM_WRITE "stream_write"
102 #define USERSTREAM_FLUSH "stream_flush"
103 #define USERSTREAM_SEEK "stream_seek"
104 #define USERSTREAM_TELL "stream_tell"
105 #define USERSTREAM_EOF "stream_eof"
106 #define USERSTREAM_STAT "stream_stat"
107 #define USERSTREAM_STATURL "url_stat"
108 #define USERSTREAM_UNLINK "unlink"
109 #define USERSTREAM_RENAME "rename"
110 #define USERSTREAM_MKDIR "mkdir"
111 #define USERSTREAM_RMDIR "rmdir"
112 #define USERSTREAM_DIR_OPEN "dir_opendir"
113 #define USERSTREAM_DIR_READ "dir_readdir"
114 #define USERSTREAM_DIR_REWIND "dir_rewinddir"
115 #define USERSTREAM_DIR_CLOSE "dir_closedir"
116 #define USERSTREAM_LOCK "stream_lock"
117 #define USERSTREAM_CAST "stream_cast"
118 #define USERSTREAM_SET_OPTION "stream_set_option"
119 #define USERSTREAM_TRUNCATE "stream_truncate"
120 #define USERSTREAM_METADATA "stream_metadata"
121
122 /* {{{ class should have methods like these:
123
124 function stream_open($path, $mode, $options, &$opened_path)
125 {
126 return true/false;
127 }
128
129 function stream_read($count)
130 {
131 return false on error;
132 else return string;
133 }
134
135 function stream_write($data)
136 {
137 return false on error;
138 else return count written;
139 }
140
141 function stream_close()
142 {
143 }
144
145 function stream_flush()
146 {
147 return true/false;
148 }
149
150 function stream_seek($offset, $whence)
151 {
152 return true/false;
153 }
154
155 function stream_tell()
156 {
157 return (int)$position;
158 }
159
160 function stream_eof()
161 {
162 return true/false;
163 }
164
165 function stream_stat()
166 {
167 return array( just like that returned by fstat() );
168 }
169
170 function stream_cast($castas)
171 {
172 if ($castas == STREAM_CAST_FOR_SELECT) {
173 return $this->underlying_stream;
174 }
175 return false;
176 }
177
178 function stream_set_option($option, $arg1, $arg2)
179 {
180 switch($option) {
181 case STREAM_OPTION_BLOCKING:
182 $blocking = $arg1;
183 ...
184 case STREAM_OPTION_READ_TIMEOUT:
185 $sec = $arg1;
186 $usec = $arg2;
187 ...
188 case STREAM_OPTION_WRITE_BUFFER:
189 $mode = $arg1;
190 $size = $arg2;
191 ...
192 default:
193 return false;
194 }
195 }
196
197 function url_stat(string $url, int $flags)
198 {
199 return array( just like that returned by stat() );
200 }
201
202 function unlink(string $url)
203 {
204 return true / false;
205 }
206
207 function rename(string $from, string $to)
208 {
209 return true / false;
210 }
211
212 function mkdir($dir, $mode, $options)
213 {
214 return true / false;
215 }
216
217 function rmdir($dir, $options)
218 {
219 return true / false;
220 }
221
222 function dir_opendir(string $url, int $options)
223 {
224 return true / false;
225 }
226
227 function dir_readdir()
228 {
229 return string next filename in dir ;
230 }
231
232 function dir_closedir()
233 {
234 release dir related resources;
235 }
236
237 function dir_rewinddir()
238 {
239 reset to start of dir list;
240 }
241
242 function stream_lock($operation)
243 {
244 return true / false;
245 }
246
247 function stream_truncate($new_size)
248 {
249 return true / false;
250 }
251
252 }}} **/
253
call_method_if_exists(zval * object,zval * method_name,zval * retval,uint32_t param_count,zval * params)254 static zend_result call_method_if_exists(
255 zval *object, zval *method_name, zval *retval, uint32_t param_count, zval *params)
256 {
257 return zend_call_method_if_exists(
258 Z_OBJ_P(object), Z_STR_P(method_name), retval, param_count, params);
259 }
260
user_stream_create_object(struct php_user_stream_wrapper * uwrap,php_stream_context * context,zval * object)261 static void user_stream_create_object(struct php_user_stream_wrapper *uwrap, php_stream_context *context, zval *object)
262 {
263 if (uwrap->ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) {
264 ZVAL_UNDEF(object);
265 return;
266 }
267
268 /* create an instance of our class */
269 if (object_init_ex(object, uwrap->ce) == FAILURE) {
270 ZVAL_UNDEF(object);
271 return;
272 }
273
274 if (context) {
275 GC_ADDREF(context->res);
276 add_property_resource(object, "context", context->res);
277 } else {
278 add_property_null(object, "context");
279 }
280
281 if (EG(exception) != NULL) {
282 zval_ptr_dtor(object);
283 ZVAL_UNDEF(object);
284 return;
285 }
286
287 if (uwrap->ce->constructor) {
288 zend_call_known_instance_method_with_0_params(
289 uwrap->ce->constructor, Z_OBJ_P(object), NULL);
290 }
291 }
292
user_wrapper_opener(php_stream_wrapper * wrapper,const char * filename,const char * mode,int options,zend_string ** opened_path,php_stream_context * context STREAMS_DC)293 static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *filename, const char *mode,
294 int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
295 {
296 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
297 php_userstream_data_t *us;
298 zval zretval, zfuncname;
299 zval args[4];
300 int call_result;
301 php_stream *stream = NULL;
302 bool old_in_user_include;
303
304 /* Try to catch bad usage without preventing flexibility */
305 if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) {
306 php_stream_wrapper_log_error(wrapper, options, "infinite recursion prevented");
307 return NULL;
308 }
309 FG(user_stream_current_filename) = filename;
310
311 /* if the user stream was registered as local and we are in include context,
312 we add allow_url_include restrictions to allow_url_fopen ones */
313 /* we need only is_url == 0 here since if is_url == 1 and remote wrappers
314 were restricted we wouldn't get here */
315 old_in_user_include = PG(in_user_include);
316 if(uwrap->wrapper.is_url == 0 &&
317 (options & STREAM_OPEN_FOR_INCLUDE) &&
318 !PG(allow_url_include)) {
319 PG(in_user_include) = 1;
320 }
321
322 us = emalloc(sizeof(*us));
323 us->wrapper = uwrap;
324 /* call_method_if_exists() may unregister the stream wrapper. Hold on to it. */
325 GC_ADDREF(us->wrapper->resource);
326
327 user_stream_create_object(uwrap, context, &us->object);
328 if (Z_TYPE(us->object) == IS_UNDEF) {
329 FG(user_stream_current_filename) = NULL;
330 PG(in_user_include) = old_in_user_include;
331 efree(us);
332 return NULL;
333 }
334
335 /* call it's stream_open method - set up params first */
336 ZVAL_STRING(&args[0], filename);
337 ZVAL_STRING(&args[1], mode);
338 ZVAL_LONG(&args[2], options);
339 ZVAL_NEW_REF(&args[3], &EG(uninitialized_zval));
340
341 ZVAL_STRING(&zfuncname, USERSTREAM_OPEN);
342
343 zend_try {
344 call_result = call_method_if_exists(&us->object, &zfuncname, &zretval, 4, args);
345 } zend_catch {
346 FG(user_stream_current_filename) = NULL;
347 zend_bailout();
348 } zend_end_try();
349
350 if (call_result == SUCCESS && Z_TYPE(zretval) != IS_UNDEF && zval_is_true(&zretval)) {
351 /* the stream is now open! */
352 stream = php_stream_alloc_rel(&php_stream_userspace_ops, us, 0, mode);
353
354 /* if the opened path is set, copy it out */
355 if (Z_ISREF(args[3]) && Z_TYPE_P(Z_REFVAL(args[3])) == IS_STRING && opened_path) {
356 *opened_path = zend_string_copy(Z_STR_P(Z_REFVAL(args[3])));
357 }
358
359 /* set wrapper data to be a reference to our object */
360 ZVAL_COPY(&stream->wrapperdata, &us->object);
361 } else {
362 php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_OPEN "\" call failed",
363 ZSTR_VAL(us->wrapper->ce->name));
364 }
365
366 /* destroy everything else */
367 if (stream == NULL) {
368 zval_ptr_dtor(&us->object);
369 ZVAL_UNDEF(&us->object);
370 zend_list_delete(us->wrapper->resource);
371 efree(us);
372 }
373 zval_ptr_dtor(&zretval);
374 zval_ptr_dtor(&zfuncname);
375 zval_ptr_dtor(&args[3]);
376 zval_ptr_dtor(&args[2]);
377 zval_ptr_dtor(&args[1]);
378 zval_ptr_dtor(&args[0]);
379
380 FG(user_stream_current_filename) = NULL;
381
382 PG(in_user_include) = old_in_user_include;
383 return stream;
384 }
385
user_wrapper_close(php_stream_wrapper * wrapper,php_stream * stream)386 static int user_wrapper_close(php_stream_wrapper *wrapper, php_stream *stream)
387 {
388 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
389 zend_list_delete(uwrap->resource);
390 // FIXME: Unused?
391 return 0;
392 }
393
user_wrapper_opendir(php_stream_wrapper * wrapper,const char * filename,const char * mode,int options,zend_string ** opened_path,php_stream_context * context STREAMS_DC)394 static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char *filename, const char *mode,
395 int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
396 {
397 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
398 php_userstream_data_t *us;
399 zval zretval, zfuncname;
400 zval args[2];
401 int call_result;
402 php_stream *stream = NULL;
403
404 /* Try to catch bad usage without preventing flexibility */
405 if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) {
406 php_stream_wrapper_log_error(wrapper, options, "infinite recursion prevented");
407 return NULL;
408 }
409 FG(user_stream_current_filename) = filename;
410
411 us = emalloc(sizeof(*us));
412 us->wrapper = uwrap;
413 /* call_method_if_exists() may unregister the stream wrapper. Hold on to it. */
414 GC_ADDREF(us->wrapper->resource);
415
416 user_stream_create_object(uwrap, context, &us->object);
417 if (Z_TYPE(us->object) == IS_UNDEF) {
418 FG(user_stream_current_filename) = NULL;
419 efree(us);
420 return NULL;
421 }
422
423 /* call it's dir_open method - set up params first */
424 ZVAL_STRING(&args[0], filename);
425 ZVAL_LONG(&args[1], options);
426
427 ZVAL_STRING(&zfuncname, USERSTREAM_DIR_OPEN);
428
429 call_result = call_method_if_exists(&us->object, &zfuncname, &zretval, 2, args);
430
431 if (call_result == SUCCESS && Z_TYPE(zretval) != IS_UNDEF && zval_is_true(&zretval)) {
432 /* the stream is now open! */
433 stream = php_stream_alloc_rel(&php_stream_userspace_dir_ops, us, 0, mode);
434
435 /* set wrapper data to be a reference to our object */
436 ZVAL_COPY(&stream->wrapperdata, &us->object);
437 } else {
438 php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_DIR_OPEN "\" call failed",
439 ZSTR_VAL(us->wrapper->ce->name));
440 }
441
442 /* destroy everything else */
443 if (stream == NULL) {
444 zval_ptr_dtor(&us->object);
445 ZVAL_UNDEF(&us->object);
446 zend_list_delete(us->wrapper->resource);
447 efree(us);
448 }
449 zval_ptr_dtor(&zretval);
450
451 zval_ptr_dtor(&zfuncname);
452 zval_ptr_dtor(&args[1]);
453 zval_ptr_dtor(&args[0]);
454
455 FG(user_stream_current_filename) = NULL;
456
457 return stream;
458 }
459
460
461 /* {{{ Registers a custom URL protocol handler class */
PHP_FUNCTION(stream_wrapper_register)462 PHP_FUNCTION(stream_wrapper_register)
463 {
464 zend_string *protocol;
465 struct php_user_stream_wrapper *uwrap;
466 zend_class_entry *ce = NULL;
467 zend_resource *rsrc;
468 zend_long flags = 0;
469
470 if (zend_parse_parameters(ZEND_NUM_ARGS(), "SC|l", &protocol, &ce, &flags) == FAILURE) {
471 RETURN_THROWS();
472 }
473
474 uwrap = (struct php_user_stream_wrapper *)ecalloc(1, sizeof(*uwrap));
475 uwrap->ce = ce;
476 uwrap->protoname = estrndup(ZSTR_VAL(protocol), ZSTR_LEN(protocol));
477 uwrap->wrapper.wops = &user_stream_wops;
478 uwrap->wrapper.abstract = uwrap;
479 uwrap->wrapper.is_url = ((flags & PHP_STREAM_IS_URL) != 0);
480
481 rsrc = zend_register_resource(uwrap, le_protocols);
482
483 if (php_register_url_stream_wrapper_volatile(protocol, &uwrap->wrapper) == SUCCESS) {
484 uwrap->resource = rsrc;
485 RETURN_TRUE;
486 }
487
488 /* We failed. But why? */
489 if (zend_hash_exists(php_stream_get_url_stream_wrappers_hash(), protocol)) {
490 php_error_docref(NULL, E_WARNING, "Protocol %s:// is already defined.", ZSTR_VAL(protocol));
491 } else {
492 /* Hash doesn't exist so it must have been an invalid protocol scheme */
493 php_error_docref(NULL, E_WARNING, "Invalid protocol scheme specified. Unable to register wrapper class %s to %s://", ZSTR_VAL(uwrap->ce->name), ZSTR_VAL(protocol));
494 }
495
496 zend_list_delete(rsrc);
497 RETURN_FALSE;
498 }
499 /* }}} */
500
501 /* {{{ Unregister a wrapper for the life of the current request. */
PHP_FUNCTION(stream_wrapper_unregister)502 PHP_FUNCTION(stream_wrapper_unregister)
503 {
504 zend_string *protocol;
505
506 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &protocol) == FAILURE) {
507 RETURN_THROWS();
508 }
509
510 php_stream_wrapper *wrapper = zend_hash_find_ptr(php_stream_get_url_stream_wrappers_hash(), protocol);
511 if (php_unregister_url_stream_wrapper_volatile(protocol) == FAILURE) {
512 /* We failed */
513 php_error_docref(NULL, E_WARNING, "Unable to unregister protocol %s://", ZSTR_VAL(protocol));
514 RETURN_FALSE;
515 }
516
517 ZEND_ASSERT(wrapper != NULL);
518 if (wrapper->wops == &user_stream_wops) {
519 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper *)wrapper;
520 // uwrap will be released by resource destructor
521 zend_list_delete(uwrap->resource);
522 }
523
524 RETURN_TRUE;
525 }
526 /* }}} */
527
528 /* {{{ Restore the original protocol handler, overriding if necessary */
PHP_FUNCTION(stream_wrapper_restore)529 PHP_FUNCTION(stream_wrapper_restore)
530 {
531 zend_string *protocol;
532 php_stream_wrapper *wrapper;
533 HashTable *global_wrapper_hash, *wrapper_hash;
534
535 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &protocol) == FAILURE) {
536 RETURN_THROWS();
537 }
538
539 global_wrapper_hash = php_stream_get_url_stream_wrappers_hash_global();
540 if ((wrapper = zend_hash_find_ptr(global_wrapper_hash, protocol)) == NULL) {
541 php_error_docref(NULL, E_WARNING, "%s:// never existed, nothing to restore", ZSTR_VAL(protocol));
542 RETURN_FALSE;
543 }
544
545 wrapper_hash = php_stream_get_url_stream_wrappers_hash();
546 if (wrapper_hash == global_wrapper_hash || zend_hash_find_ptr(wrapper_hash, protocol) == wrapper) {
547 php_error_docref(NULL, E_NOTICE, "%s:// was never changed, nothing to restore", ZSTR_VAL(protocol));
548 RETURN_TRUE;
549 }
550
551 /* A failure here could be okay given that the protocol might have been merely unregistered */
552 php_unregister_url_stream_wrapper_volatile(protocol);
553
554 if (php_register_url_stream_wrapper_volatile(protocol, wrapper) == FAILURE) {
555 php_error_docref(NULL, E_WARNING, "Unable to restore original %s:// wrapper", ZSTR_VAL(protocol));
556 RETURN_FALSE;
557 }
558
559 RETURN_TRUE;
560 }
561 /* }}} */
562
php_userstreamop_write(php_stream * stream,const char * buf,size_t count)563 static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_t count)
564 {
565 zval func_name;
566 zval retval;
567 int call_result;
568 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
569 zval args[1];
570 ssize_t didwrite;
571
572 assert(us != NULL);
573
574 ZVAL_STRINGL(&func_name, USERSTREAM_WRITE, sizeof(USERSTREAM_WRITE)-1);
575
576 ZVAL_STRINGL(&args[0], (char*)buf, count);
577
578 call_result = call_method_if_exists(&us->object, &func_name, &retval, 1, args);
579 zval_ptr_dtor(&args[0]);
580 zval_ptr_dtor(&func_name);
581
582 if (EG(exception)) {
583 return -1;
584 }
585
586 if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
587 if (Z_TYPE(retval) == IS_FALSE) {
588 didwrite = -1;
589 } else {
590 convert_to_long(&retval);
591 didwrite = Z_LVAL(retval);
592 }
593 } else {
594 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_WRITE " is not implemented!",
595 ZSTR_VAL(us->wrapper->ce->name));
596 didwrite = -1;
597 }
598
599 /* don't allow strange buffer overruns due to bogus return */
600 if (didwrite > 0 && didwrite > count) {
601 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_WRITE " wrote " ZEND_LONG_FMT " bytes more data than requested (" ZEND_LONG_FMT " written, " ZEND_LONG_FMT " max)",
602 ZSTR_VAL(us->wrapper->ce->name),
603 (zend_long)(didwrite - count), (zend_long)didwrite, (zend_long)count);
604 didwrite = count;
605 }
606
607 zval_ptr_dtor(&retval);
608
609 return didwrite;
610 }
611
php_userstreamop_read(php_stream * stream,char * buf,size_t count)612 static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count)
613 {
614 zval func_name;
615 zval retval;
616 zval args[1];
617 int call_result;
618 size_t didread = 0;
619 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
620
621 assert(us != NULL);
622
623 ZVAL_STRINGL(&func_name, USERSTREAM_READ, sizeof(USERSTREAM_READ)-1);
624
625 ZVAL_LONG(&args[0], count);
626
627 call_result = call_method_if_exists(&us->object, &func_name, &retval, 1, args);
628
629 zval_ptr_dtor(&args[0]);
630 zval_ptr_dtor(&func_name);
631
632 if (EG(exception)) {
633 return -1;
634 }
635
636 if (call_result == FAILURE) {
637 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_READ " is not implemented!",
638 ZSTR_VAL(us->wrapper->ce->name));
639 return -1;
640 }
641
642 if (Z_TYPE(retval) == IS_FALSE) {
643 return -1;
644 }
645
646 if (!try_convert_to_string(&retval)) {
647 zval_ptr_dtor(&retval);
648 return -1;
649 }
650
651 didread = Z_STRLEN(retval);
652 if (didread > 0) {
653 if (didread > count) {
654 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_READ " - read " ZEND_LONG_FMT " bytes more data than requested (" ZEND_LONG_FMT " read, " ZEND_LONG_FMT " max) - excess data will be lost",
655 ZSTR_VAL(us->wrapper->ce->name), (zend_long)(didread - count), (zend_long)didread, (zend_long)count);
656 didread = count;
657 }
658 memcpy(buf, Z_STRVAL(retval), didread);
659 }
660
661 zval_ptr_dtor(&retval);
662 ZVAL_UNDEF(&retval);
663
664 /* since the user stream has no way of setting the eof flag directly, we need to ask it if we hit eof */
665
666 ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1);
667 call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
668 zval_ptr_dtor(&func_name);
669
670 if (EG(exception)) {
671 stream->eof = 1;
672 return -1;
673 }
674
675 if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF && zval_is_true(&retval)) {
676 stream->eof = 1;
677 } else if (call_result == FAILURE) {
678 php_error_docref(NULL, E_WARNING,
679 "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
680 ZSTR_VAL(us->wrapper->ce->name));
681
682 stream->eof = 1;
683 }
684
685 zval_ptr_dtor(&retval);
686
687 return didread;
688 }
689
php_userstreamop_close(php_stream * stream,int close_handle)690 static int php_userstreamop_close(php_stream *stream, int close_handle)
691 {
692 zval func_name;
693 zval retval;
694 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
695
696 assert(us != NULL);
697
698 ZVAL_STRINGL(&func_name, USERSTREAM_CLOSE, sizeof(USERSTREAM_CLOSE)-1);
699
700 call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
701
702 zval_ptr_dtor(&retval);
703 zval_ptr_dtor(&func_name);
704
705 zval_ptr_dtor(&us->object);
706 ZVAL_UNDEF(&us->object);
707
708 efree(us);
709
710 return 0;
711 }
712
php_userstreamop_flush(php_stream * stream)713 static int php_userstreamop_flush(php_stream *stream)
714 {
715 zval func_name;
716 zval retval;
717 int call_result;
718 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
719
720 assert(us != NULL);
721
722 ZVAL_STRINGL(&func_name, USERSTREAM_FLUSH, sizeof(USERSTREAM_FLUSH)-1);
723
724 call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
725
726 if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF && zval_is_true(&retval))
727 call_result = 0;
728 else
729 call_result = -1;
730
731 zval_ptr_dtor(&retval);
732 zval_ptr_dtor(&func_name);
733
734 return call_result;
735 }
736
php_userstreamop_seek(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffs)737 static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs)
738 {
739 zval func_name;
740 zval retval;
741 int call_result, ret;
742 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
743 zval args[2];
744
745 assert(us != NULL);
746
747 ZVAL_STRINGL(&func_name, USERSTREAM_SEEK, sizeof(USERSTREAM_SEEK)-1);
748
749 ZVAL_LONG(&args[0], offset);
750 ZVAL_LONG(&args[1], whence);
751
752 call_result = call_method_if_exists(&us->object, &func_name, &retval, 2, args);
753
754 zval_ptr_dtor(&args[0]);
755 zval_ptr_dtor(&args[1]);
756 zval_ptr_dtor(&func_name);
757
758 if (call_result == FAILURE) {
759 /* stream_seek is not implemented, so disable seeks for this stream */
760 stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
761 /* there should be no retval to clean up */
762
763 zval_ptr_dtor(&retval);
764
765 return -1;
766 } else if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF && zval_is_true(&retval)) {
767 ret = 0;
768 } else {
769 ret = -1;
770 }
771
772 zval_ptr_dtor(&retval);
773 ZVAL_UNDEF(&retval);
774
775 if (ret) {
776 return ret;
777 }
778
779 /* now determine where we are */
780 ZVAL_STRINGL(&func_name, USERSTREAM_TELL, sizeof(USERSTREAM_TELL)-1);
781
782 call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
783
784 if (call_result == SUCCESS && Z_TYPE(retval) == IS_LONG) {
785 *newoffs = Z_LVAL(retval);
786 ret = 0;
787 } else if (call_result == FAILURE) {
788 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_TELL " is not implemented!", ZSTR_VAL(us->wrapper->ce->name));
789 ret = -1;
790 } else {
791 ret = -1;
792 }
793
794 zval_ptr_dtor(&retval);
795 zval_ptr_dtor(&func_name);
796 return ret;
797 }
798
799 /* parse the return value from one of the stat functions and store the
800 * relevant fields into the statbuf provided */
statbuf_from_array(zval * array,php_stream_statbuf * ssb)801 static int statbuf_from_array(zval *array, php_stream_statbuf *ssb)
802 {
803 zval *elem;
804
805 #define STAT_PROP_ENTRY_EX(name, name2) \
806 if (NULL != (elem = zend_hash_str_find(Z_ARRVAL_P(array), #name, sizeof(#name)-1))) { \
807 ssb->sb.st_##name2 = zval_get_long(elem); \
808 }
809
810 #define STAT_PROP_ENTRY(name) STAT_PROP_ENTRY_EX(name,name)
811
812 memset(ssb, 0, sizeof(php_stream_statbuf));
813 STAT_PROP_ENTRY(dev);
814 STAT_PROP_ENTRY(ino);
815 STAT_PROP_ENTRY(mode);
816 STAT_PROP_ENTRY(nlink);
817 STAT_PROP_ENTRY(uid);
818 STAT_PROP_ENTRY(gid);
819 #if HAVE_STRUCT_STAT_ST_RDEV
820 STAT_PROP_ENTRY(rdev);
821 #endif
822 STAT_PROP_ENTRY(size);
823 STAT_PROP_ENTRY(atime);
824 STAT_PROP_ENTRY(mtime);
825 STAT_PROP_ENTRY(ctime);
826 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
827 STAT_PROP_ENTRY(blksize);
828 #endif
829 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
830 STAT_PROP_ENTRY(blocks);
831 #endif
832
833 #undef STAT_PROP_ENTRY
834 #undef STAT_PROP_ENTRY_EX
835 return SUCCESS;
836 }
837
php_userstreamop_stat(php_stream * stream,php_stream_statbuf * ssb)838 static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb)
839 {
840 zval func_name;
841 zval retval;
842 int call_result;
843 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
844 int ret = -1;
845
846 ZVAL_STRINGL(&func_name, USERSTREAM_STAT, sizeof(USERSTREAM_STAT)-1);
847
848 call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
849
850 if (call_result == SUCCESS && Z_TYPE(retval) == IS_ARRAY) {
851 if (SUCCESS == statbuf_from_array(&retval, ssb))
852 ret = 0;
853 } else {
854 if (call_result == FAILURE) {
855 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STAT " is not implemented!",
856 ZSTR_VAL(us->wrapper->ce->name));
857 }
858 }
859
860 zval_ptr_dtor(&retval);
861 zval_ptr_dtor(&func_name);
862
863 return ret;
864 }
865
866
php_userstreamop_set_option(php_stream * stream,int option,int value,void * ptrparam)867 static int php_userstreamop_set_option(php_stream *stream, int option, int value, void *ptrparam) {
868 zval func_name;
869 zval retval;
870 int call_result;
871 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
872 int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL;
873 zval args[3];
874
875 switch (option) {
876 case PHP_STREAM_OPTION_CHECK_LIVENESS:
877 ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1);
878 call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
879 if (call_result == SUCCESS && (Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
880 ret = zval_is_true(&retval) ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
881 } else {
882 ret = PHP_STREAM_OPTION_RETURN_ERR;
883 php_error_docref(NULL, E_WARNING,
884 "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
885 ZSTR_VAL(us->wrapper->ce->name));
886 }
887 zval_ptr_dtor(&retval);
888 zval_ptr_dtor(&func_name);
889 break;
890
891 case PHP_STREAM_OPTION_LOCKING:
892 ZVAL_LONG(&args[0], 0);
893
894 if (value & LOCK_NB) {
895 Z_LVAL_P(&args[0]) |= PHP_LOCK_NB;
896 }
897 switch(value & ~LOCK_NB) {
898 case LOCK_SH:
899 Z_LVAL_P(&args[0]) |= PHP_LOCK_SH;
900 break;
901 case LOCK_EX:
902 Z_LVAL_P(&args[0]) |= PHP_LOCK_EX;
903 break;
904 case LOCK_UN:
905 Z_LVAL_P(&args[0]) |= PHP_LOCK_UN;
906 break;
907 }
908
909 /* TODO wouldblock */
910 ZVAL_STRINGL(&func_name, USERSTREAM_LOCK, sizeof(USERSTREAM_LOCK)-1);
911
912 call_result = call_method_if_exists(&us->object, &func_name, &retval, 1, args);
913
914 if (call_result == SUCCESS && (Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
915 ret = (Z_TYPE(retval) == IS_FALSE);
916 } else if (call_result == FAILURE) {
917 if (value == 0) {
918 /* lock support test (TODO: more check) */
919 ret = PHP_STREAM_OPTION_RETURN_OK;
920 } else {
921 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_LOCK " is not implemented!",
922 ZSTR_VAL(us->wrapper->ce->name));
923 ret = PHP_STREAM_OPTION_RETURN_ERR;
924 }
925 }
926
927 zval_ptr_dtor(&retval);
928 zval_ptr_dtor(&func_name);
929 zval_ptr_dtor(&args[0]);
930 break;
931
932 case PHP_STREAM_OPTION_TRUNCATE_API:
933 ZVAL_STRINGL(&func_name, USERSTREAM_TRUNCATE, sizeof(USERSTREAM_TRUNCATE)-1);
934
935 switch (value) {
936 case PHP_STREAM_TRUNCATE_SUPPORTED:
937 if (zend_is_callable_ex(&func_name, Z_OBJ(us->object), IS_CALLABLE_SUPPRESS_DEPRECATIONS, NULL, NULL, NULL))
938 ret = PHP_STREAM_OPTION_RETURN_OK;
939 else
940 ret = PHP_STREAM_OPTION_RETURN_ERR;
941 break;
942
943 case PHP_STREAM_TRUNCATE_SET_SIZE: {
944 ptrdiff_t new_size = *(ptrdiff_t*) ptrparam;
945 if (new_size >= 0 && new_size <= (ptrdiff_t)LONG_MAX) {
946 ZVAL_LONG(&args[0], (zend_long)new_size);
947 call_result = call_method_if_exists(&us->object, &func_name, &retval, 1, args);
948 if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
949 if (Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE) {
950 ret = (Z_TYPE(retval) == IS_TRUE) ? PHP_STREAM_OPTION_RETURN_OK :
951 PHP_STREAM_OPTION_RETURN_ERR;
952 } else {
953 php_error_docref(NULL, E_WARNING,
954 "%s::" USERSTREAM_TRUNCATE " did not return a boolean!",
955 ZSTR_VAL(us->wrapper->ce->name));
956 }
957 } else {
958 php_error_docref(NULL, E_WARNING,
959 "%s::" USERSTREAM_TRUNCATE " is not implemented!",
960 ZSTR_VAL(us->wrapper->ce->name));
961 }
962 zval_ptr_dtor(&retval);
963 zval_ptr_dtor(&args[0]);
964 } else { /* bad new size */
965 ret = PHP_STREAM_OPTION_RETURN_ERR;
966 }
967 break;
968 }
969 }
970 zval_ptr_dtor(&func_name);
971 break;
972
973 case PHP_STREAM_OPTION_READ_BUFFER:
974 case PHP_STREAM_OPTION_WRITE_BUFFER:
975 case PHP_STREAM_OPTION_READ_TIMEOUT:
976 case PHP_STREAM_OPTION_BLOCKING: {
977
978 ZVAL_STRINGL(&func_name, USERSTREAM_SET_OPTION, sizeof(USERSTREAM_SET_OPTION)-1);
979
980 ZVAL_LONG(&args[0], option);
981 ZVAL_NULL(&args[1]);
982 ZVAL_NULL(&args[2]);
983
984 switch(option) {
985 case PHP_STREAM_OPTION_READ_BUFFER:
986 case PHP_STREAM_OPTION_WRITE_BUFFER:
987 ZVAL_LONG(&args[1], value);
988 if (ptrparam) {
989 ZVAL_LONG(&args[2], *(long *)ptrparam);
990 } else {
991 ZVAL_LONG(&args[2], BUFSIZ);
992 }
993 break;
994 case PHP_STREAM_OPTION_READ_TIMEOUT: {
995 struct timeval tv = *(struct timeval*)ptrparam;
996 ZVAL_LONG(&args[1], tv.tv_sec);
997 ZVAL_LONG(&args[2], tv.tv_usec);
998 break;
999 }
1000 case PHP_STREAM_OPTION_BLOCKING:
1001 ZVAL_LONG(&args[1], value);
1002 break;
1003 default:
1004 break;
1005 }
1006
1007 call_result = call_method_if_exists(&us->object, &func_name, &retval, 3, args);
1008
1009 if (call_result == FAILURE) {
1010 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_SET_OPTION " is not implemented!",
1011 ZSTR_VAL(us->wrapper->ce->name));
1012 ret = PHP_STREAM_OPTION_RETURN_ERR;
1013 } else if (zend_is_true(&retval)) {
1014 ret = PHP_STREAM_OPTION_RETURN_OK;
1015 } else {
1016 ret = PHP_STREAM_OPTION_RETURN_ERR;
1017 }
1018
1019 zval_ptr_dtor(&retval);
1020 zval_ptr_dtor(&args[2]);
1021 zval_ptr_dtor(&args[1]);
1022 zval_ptr_dtor(&args[0]);
1023 zval_ptr_dtor(&func_name);
1024
1025 break;
1026 }
1027 }
1028
1029 return ret;
1030 }
1031
1032
user_wrapper_unlink(php_stream_wrapper * wrapper,const char * url,int options,php_stream_context * context)1033 static int user_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context)
1034 {
1035 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1036 zval zfuncname, zretval;
1037 zval args[1];
1038 int call_result;
1039 zval object;
1040 int ret = 0;
1041
1042 /* create an instance of our class */
1043 user_stream_create_object(uwrap, context, &object);
1044 if (Z_TYPE(object) == IS_UNDEF) {
1045 return ret;
1046 }
1047
1048 /* call the unlink method */
1049 ZVAL_STRING(&args[0], url);
1050
1051 ZVAL_STRING(&zfuncname, USERSTREAM_UNLINK);
1052
1053 call_result = call_method_if_exists(&object, &zfuncname, &zretval, 1, args);
1054
1055 if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
1056 ret = (Z_TYPE(zretval) == IS_TRUE);
1057 } else if (call_result == FAILURE) {
1058 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_UNLINK " is not implemented!", ZSTR_VAL(uwrap->ce->name));
1059 }
1060
1061 /* clean up */
1062 zval_ptr_dtor(&object);
1063 zval_ptr_dtor(&zretval);
1064 zval_ptr_dtor(&zfuncname);
1065
1066 zval_ptr_dtor(&args[0]);
1067
1068 return ret;
1069 }
1070
user_wrapper_rename(php_stream_wrapper * wrapper,const char * url_from,const char * url_to,int options,php_stream_context * context)1071 static int user_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from, const char *url_to,
1072 int options, php_stream_context *context)
1073 {
1074 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1075 zval zfuncname, zretval;
1076 zval args[2];
1077 int call_result;
1078 zval object;
1079 int ret = 0;
1080
1081 /* create an instance of our class */
1082 user_stream_create_object(uwrap, context, &object);
1083 if (Z_TYPE(object) == IS_UNDEF) {
1084 return ret;
1085 }
1086
1087 /* call the rename method */
1088 ZVAL_STRING(&args[0], url_from);
1089 ZVAL_STRING(&args[1], url_to);
1090
1091 ZVAL_STRING(&zfuncname, USERSTREAM_RENAME);
1092
1093 call_result = call_method_if_exists(&object, &zfuncname, &zretval, 2, args);
1094
1095 if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
1096 ret = (Z_TYPE(zretval) == IS_TRUE);
1097 } else if (call_result == FAILURE) {
1098 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_RENAME " is not implemented!", ZSTR_VAL(uwrap->ce->name));
1099 }
1100
1101 /* clean up */
1102 zval_ptr_dtor(&object);
1103 zval_ptr_dtor(&zretval);
1104
1105 zval_ptr_dtor(&zfuncname);
1106 zval_ptr_dtor(&args[1]);
1107 zval_ptr_dtor(&args[0]);
1108
1109 return ret;
1110 }
1111
user_wrapper_mkdir(php_stream_wrapper * wrapper,const char * url,int mode,int options,php_stream_context * context)1112 static int user_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url, int mode,
1113 int options, php_stream_context *context)
1114 {
1115 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1116 zval zfuncname, zretval;
1117 zval args[3];
1118 int call_result;
1119 zval object;
1120 int ret = 0;
1121
1122 /* create an instance of our class */
1123 user_stream_create_object(uwrap, context, &object);
1124 if (Z_TYPE(object) == IS_UNDEF) {
1125 return ret;
1126 }
1127
1128 /* call the mkdir method */
1129 ZVAL_STRING(&args[0], url);
1130 ZVAL_LONG(&args[1], mode);
1131 ZVAL_LONG(&args[2], options);
1132
1133 ZVAL_STRING(&zfuncname, USERSTREAM_MKDIR);
1134
1135 call_result = call_method_if_exists(&object, &zfuncname, &zretval, 3, args);
1136
1137 if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
1138 ret = (Z_TYPE(zretval) == IS_TRUE);
1139 } else if (call_result == FAILURE) {
1140 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_MKDIR " is not implemented!", ZSTR_VAL(uwrap->ce->name));
1141 }
1142
1143 /* clean up */
1144 zval_ptr_dtor(&object);
1145 zval_ptr_dtor(&zretval);
1146
1147 zval_ptr_dtor(&zfuncname);
1148 zval_ptr_dtor(&args[2]);
1149 zval_ptr_dtor(&args[1]);
1150 zval_ptr_dtor(&args[0]);
1151
1152 return ret;
1153 }
1154
user_wrapper_rmdir(php_stream_wrapper * wrapper,const char * url,int options,php_stream_context * context)1155 static int user_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url,
1156 int options, php_stream_context *context)
1157 {
1158 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1159 zval zfuncname, zretval;
1160 zval args[2];
1161 int call_result;
1162 zval object;
1163 int ret = 0;
1164
1165 /* create an instance of our class */
1166 user_stream_create_object(uwrap, context, &object);
1167 if (Z_TYPE(object) == IS_UNDEF) {
1168 return ret;
1169 }
1170
1171 /* call the rmdir method */
1172 ZVAL_STRING(&args[0], url);
1173 ZVAL_LONG(&args[1], options);
1174
1175 ZVAL_STRING(&zfuncname, USERSTREAM_RMDIR);
1176
1177 call_result = call_method_if_exists(&object, &zfuncname, &zretval, 2, args);
1178
1179 if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
1180 ret = (Z_TYPE(zretval) == IS_TRUE);
1181 } else if (call_result == FAILURE) {
1182 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_RMDIR " is not implemented!", ZSTR_VAL(uwrap->ce->name));
1183 }
1184
1185 /* clean up */
1186 zval_ptr_dtor(&object);
1187 zval_ptr_dtor(&zretval);
1188
1189 zval_ptr_dtor(&zfuncname);
1190 zval_ptr_dtor(&args[1]);
1191 zval_ptr_dtor(&args[0]);
1192
1193 return ret;
1194 }
1195
user_wrapper_metadata(php_stream_wrapper * wrapper,const char * url,int option,void * value,php_stream_context * context)1196 static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, int option,
1197 void *value, php_stream_context *context)
1198 {
1199 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1200 zval zfuncname, zretval;
1201 zval args[3];
1202 int call_result;
1203 zval object;
1204 int ret = 0;
1205
1206 switch(option) {
1207 case PHP_STREAM_META_TOUCH:
1208 array_init(&args[2]);
1209 if(value) {
1210 struct utimbuf *newtime = (struct utimbuf *)value;
1211 add_index_long(&args[2], 0, newtime->modtime);
1212 add_index_long(&args[2], 1, newtime->actime);
1213 }
1214 break;
1215 case PHP_STREAM_META_GROUP:
1216 case PHP_STREAM_META_OWNER:
1217 case PHP_STREAM_META_ACCESS:
1218 ZVAL_LONG(&args[2], *(long *)value);
1219 break;
1220 case PHP_STREAM_META_GROUP_NAME:
1221 case PHP_STREAM_META_OWNER_NAME:
1222 ZVAL_STRING(&args[2], value);
1223 break;
1224 default:
1225 php_error_docref(NULL, E_WARNING, "Unknown option %d for " USERSTREAM_METADATA, option);
1226 zval_ptr_dtor(&args[2]);
1227 return ret;
1228 }
1229
1230 /* create an instance of our class */
1231 user_stream_create_object(uwrap, context, &object);
1232 if (Z_TYPE(object) == IS_UNDEF) {
1233 zval_ptr_dtor(&args[2]);
1234 return ret;
1235 }
1236
1237 /* call the mkdir method */
1238 ZVAL_STRING(&args[0], url);
1239 ZVAL_LONG(&args[1], option);
1240
1241 ZVAL_STRING(&zfuncname, USERSTREAM_METADATA);
1242
1243 call_result = call_method_if_exists(&object, &zfuncname, &zretval, 3, args);
1244
1245 if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
1246 ret = Z_TYPE(zretval) == IS_TRUE;
1247 } else if (call_result == FAILURE) {
1248 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_METADATA " is not implemented!", ZSTR_VAL(uwrap->ce->name));
1249 }
1250
1251 /* clean up */
1252 zval_ptr_dtor(&object);
1253 zval_ptr_dtor(&zretval);
1254
1255 zval_ptr_dtor(&zfuncname);
1256 zval_ptr_dtor(&args[0]);
1257 zval_ptr_dtor(&args[1]);
1258 zval_ptr_dtor(&args[2]);
1259
1260 return ret;
1261 }
1262
1263
user_wrapper_stat_url(php_stream_wrapper * wrapper,const char * url,int flags,php_stream_statbuf * ssb,php_stream_context * context)1264 static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url, int flags,
1265 php_stream_statbuf *ssb, php_stream_context *context)
1266 {
1267 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1268 zval zfuncname, zretval;
1269 zval args[2];
1270 int call_result;
1271 zval object;
1272 int ret = -1;
1273
1274 /* create an instance of our class */
1275 user_stream_create_object(uwrap, context, &object);
1276 if (Z_TYPE(object) == IS_UNDEF) {
1277 return ret;
1278 }
1279
1280 /* call it's stat_url method - set up params first */
1281 ZVAL_STRING(&args[0], url);
1282 ZVAL_LONG(&args[1], flags);
1283
1284 ZVAL_STRING(&zfuncname, USERSTREAM_STATURL);
1285
1286 call_result = call_method_if_exists(&object, &zfuncname, &zretval, 2, args);
1287
1288 if (call_result == SUCCESS && Z_TYPE(zretval) == IS_ARRAY) {
1289 /* We got the info we needed */
1290 if (SUCCESS == statbuf_from_array(&zretval, ssb))
1291 ret = 0;
1292 } else {
1293 if (call_result == FAILURE) {
1294 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STATURL " is not implemented!",
1295 ZSTR_VAL(uwrap->ce->name));
1296 }
1297 }
1298
1299 /* clean up */
1300 zval_ptr_dtor(&object);
1301 zval_ptr_dtor(&zretval);
1302
1303 zval_ptr_dtor(&zfuncname);
1304 zval_ptr_dtor(&args[1]);
1305 zval_ptr_dtor(&args[0]);
1306
1307 return ret;
1308
1309 }
1310
php_userstreamop_readdir(php_stream * stream,char * buf,size_t count)1311 static ssize_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t count)
1312 {
1313 zval func_name;
1314 zval retval;
1315 int call_result;
1316 size_t didread = 0;
1317 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1318 php_stream_dirent *ent = (php_stream_dirent*)buf;
1319
1320 /* avoid problems if someone mis-uses the stream */
1321 if (count != sizeof(php_stream_dirent))
1322 return -1;
1323
1324 ZVAL_STRINGL(&func_name, USERSTREAM_DIR_READ, sizeof(USERSTREAM_DIR_READ)-1);
1325
1326 call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
1327
1328 if (call_result == SUCCESS && Z_TYPE(retval) != IS_FALSE && Z_TYPE(retval) != IS_TRUE) {
1329 convert_to_string(&retval);
1330 PHP_STRLCPY(ent->d_name, Z_STRVAL(retval), sizeof(ent->d_name), Z_STRLEN(retval));
1331 ent->d_type = DT_UNKNOWN;
1332
1333 didread = sizeof(php_stream_dirent);
1334 } else if (call_result == FAILURE) {
1335 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_DIR_READ " is not implemented!",
1336 ZSTR_VAL(us->wrapper->ce->name));
1337 }
1338
1339 zval_ptr_dtor(&retval);
1340 zval_ptr_dtor(&func_name);
1341
1342 return didread;
1343 }
1344
php_userstreamop_closedir(php_stream * stream,int close_handle)1345 static int php_userstreamop_closedir(php_stream *stream, int close_handle)
1346 {
1347 zval func_name;
1348 zval retval;
1349 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1350
1351 assert(us != NULL);
1352
1353 ZVAL_STRINGL(&func_name, USERSTREAM_DIR_CLOSE, sizeof(USERSTREAM_DIR_CLOSE)-1);
1354
1355 call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
1356
1357 zval_ptr_dtor(&retval);
1358 zval_ptr_dtor(&func_name);
1359 zval_ptr_dtor(&us->object);
1360 ZVAL_UNDEF(&us->object);
1361
1362 efree(us);
1363
1364 return 0;
1365 }
1366
php_userstreamop_rewinddir(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffs)1367 static int php_userstreamop_rewinddir(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs)
1368 {
1369 zval func_name;
1370 zval retval;
1371 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1372
1373 ZVAL_STRINGL(&func_name, USERSTREAM_DIR_REWIND, sizeof(USERSTREAM_DIR_REWIND)-1);
1374
1375 call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
1376
1377 zval_ptr_dtor(&retval);
1378 zval_ptr_dtor(&func_name);
1379
1380 return 0;
1381
1382 }
1383
php_userstreamop_cast(php_stream * stream,int castas,void ** retptr)1384 static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr)
1385 {
1386 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1387 zval func_name;
1388 zval retval;
1389 zval args[1];
1390 php_stream * intstream = NULL;
1391 int call_result;
1392 int ret = FAILURE;
1393 /* If we are checking if the stream can cast, no return pointer is provided, so do not emit errors */
1394 bool report_errors = retptr;
1395
1396 ZVAL_STRINGL(&func_name, USERSTREAM_CAST, sizeof(USERSTREAM_CAST)-1);
1397
1398 switch(castas) {
1399 case PHP_STREAM_AS_FD_FOR_SELECT:
1400 ZVAL_LONG(&args[0], PHP_STREAM_AS_FD_FOR_SELECT);
1401 break;
1402 default:
1403 ZVAL_LONG(&args[0], PHP_STREAM_AS_STDIO);
1404 break;
1405 }
1406
1407 call_result = call_method_if_exists(&us->object, &func_name, &retval, 1, args);
1408
1409 do {
1410 if (call_result == FAILURE) {
1411 if (report_errors) {
1412 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " is not implemented!",
1413 ZSTR_VAL(us->wrapper->ce->name));
1414 }
1415 break;
1416 }
1417 if (!zend_is_true(&retval)) {
1418 break;
1419 }
1420 php_stream_from_zval_no_verify(intstream, &retval);
1421 if (!intstream) {
1422 if (report_errors) {
1423 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " must return a stream resource",
1424 ZSTR_VAL(us->wrapper->ce->name));
1425 }
1426 break;
1427 }
1428 if (intstream == stream) {
1429 if (report_errors) {
1430 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " must not return itself",
1431 ZSTR_VAL(us->wrapper->ce->name));
1432 }
1433 intstream = NULL;
1434 break;
1435 }
1436 ret = php_stream_cast(intstream, castas, retptr, 1);
1437 } while (0);
1438
1439 zval_ptr_dtor(&retval);
1440 zval_ptr_dtor(&func_name);
1441 zval_ptr_dtor(&args[0]);
1442
1443 return ret;
1444 }
1445
1446 const php_stream_ops php_stream_userspace_ops = {
1447 php_userstreamop_write, php_userstreamop_read,
1448 php_userstreamop_close, php_userstreamop_flush,
1449 "user-space",
1450 php_userstreamop_seek,
1451 php_userstreamop_cast,
1452 php_userstreamop_stat,
1453 php_userstreamop_set_option,
1454 };
1455
1456 const php_stream_ops php_stream_userspace_dir_ops = {
1457 NULL, /* write */
1458 php_userstreamop_readdir,
1459 php_userstreamop_closedir,
1460 NULL, /* flush */
1461 "user-space-dir",
1462 php_userstreamop_rewinddir,
1463 NULL, /* cast */
1464 NULL, /* stat */
1465 NULL /* set_option */
1466 };
1467