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