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