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