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