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