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