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