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