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