xref: /PHP-5.5/main/streams/userspace.c (revision c4c3dca3)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2015 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 
649 	if (EG(exception)) {
650 		return 0;
651 	}
652 
653 	if (call_result == SUCCESS && retval != NULL) {
654 		convert_to_long(retval);
655 		didwrite = Z_LVAL_P(retval);
656 	} else if (call_result == FAILURE) {
657 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " is not implemented!",
658 				us->wrapper->classname);
659 	}
660 
661 	/* don't allow strange buffer overruns due to bogus return */
662 	if (didwrite > count) {
663 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " wrote %ld bytes more data than requested (%ld written, %ld max)",
664 				us->wrapper->classname,
665 				(long)(didwrite - count), (long)didwrite, (long)count);
666 		didwrite = count;
667 	}
668 
669 	if (retval)
670 		zval_ptr_dtor(&retval);
671 
672 	return didwrite;
673 }
674 
php_userstreamop_read(php_stream * stream,char * buf,size_t count TSRMLS_DC)675 static size_t php_userstreamop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
676 {
677 	zval func_name;
678 	zval *retval = NULL;
679 	zval **args[1];
680 	int call_result;
681 	size_t didread = 0;
682 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
683 	zval *zcount;
684 
685 	assert(us != NULL);
686 
687 	ZVAL_STRINGL(&func_name, USERSTREAM_READ, sizeof(USERSTREAM_READ)-1, 0);
688 
689 	MAKE_STD_ZVAL(zcount);
690 	ZVAL_LONG(zcount, count);
691 	args[0] = &zcount;
692 
693 	call_result = call_user_function_ex(NULL,
694 			&us->object,
695 			&func_name,
696 			&retval,
697 			1, args,
698 			0, NULL TSRMLS_CC);
699 
700 	zval_ptr_dtor(&zcount);
701 
702 	if (EG(exception)) {
703 		return 0;
704 	}
705 
706 	if (call_result == SUCCESS && retval != NULL) {
707 		convert_to_string(retval);
708 		didread = Z_STRLEN_P(retval);
709 		if (didread > count) {
710 			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",
711 					us->wrapper->classname, (long)(didread - count), (long)didread, (long)count);
712 			didread = count;
713 		}
714 		if (didread > 0)
715 			memcpy(buf, Z_STRVAL_P(retval), didread);
716 	} else if (call_result == FAILURE) {
717 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_READ " is not implemented!",
718 				us->wrapper->classname);
719 	}
720 
721 	if (retval) {
722 		zval_ptr_dtor(&retval);
723 		retval = NULL;
724 	}
725 
726 	/* since the user stream has no way of setting the eof flag directly, we need to ask it if we hit eof */
727 
728 	ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1, 0);
729 
730 	call_result = call_user_function_ex(NULL,
731 			&us->object,
732 			&func_name,
733 			&retval,
734 			0, NULL, 0, NULL TSRMLS_CC);
735 
736 	if (call_result == SUCCESS && retval != NULL && zval_is_true(retval)) {
737 		stream->eof = 1;
738 	} else if (call_result == FAILURE) {
739 		php_error_docref(NULL TSRMLS_CC, E_WARNING,
740 				"%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
741 				us->wrapper->classname);
742 
743 		stream->eof = 1;
744 	}
745 
746 	if (retval) {
747 		zval_ptr_dtor(&retval);
748 		retval = NULL;
749 	}
750 
751 	return didread;
752 }
753 
php_userstreamop_close(php_stream * stream,int close_handle TSRMLS_DC)754 static int php_userstreamop_close(php_stream *stream, int close_handle TSRMLS_DC)
755 {
756 	zval func_name;
757 	zval *retval = NULL;
758 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
759 
760 	assert(us != NULL);
761 
762 	ZVAL_STRINGL(&func_name, USERSTREAM_CLOSE, sizeof(USERSTREAM_CLOSE)-1, 0);
763 
764 	call_user_function_ex(NULL,
765 			&us->object,
766 			&func_name,
767 			&retval,
768 			0, NULL, 0, NULL TSRMLS_CC);
769 
770 	if (retval)
771 		zval_ptr_dtor(&retval);
772 
773 	zval_ptr_dtor(&us->object);
774 
775 	efree(us);
776 
777 	return 0;
778 }
779 
php_userstreamop_flush(php_stream * stream TSRMLS_DC)780 static int php_userstreamop_flush(php_stream *stream TSRMLS_DC)
781 {
782 	zval func_name;
783 	zval *retval = NULL;
784 	int call_result;
785 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
786 
787 	assert(us != NULL);
788 
789 	ZVAL_STRINGL(&func_name, USERSTREAM_FLUSH, sizeof(USERSTREAM_FLUSH)-1, 0);
790 
791 	call_result = call_user_function_ex(NULL,
792 			&us->object,
793 			&func_name,
794 			&retval,
795 			0, NULL, 0, NULL TSRMLS_CC);
796 
797 	if (call_result == SUCCESS && retval != NULL && zval_is_true(retval))
798 		call_result = 0;
799 	else
800 		call_result = -1;
801 
802 	if (retval)
803 		zval_ptr_dtor(&retval);
804 
805 	return call_result;
806 }
807 
php_userstreamop_seek(php_stream * stream,off_t offset,int whence,off_t * newoffs TSRMLS_DC)808 static int php_userstreamop_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
809 {
810 	zval func_name;
811 	zval *retval = NULL;
812 	int call_result, ret;
813 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
814 	zval **args[2];
815 	zval *zoffs, *zwhence;
816 
817 	assert(us != NULL);
818 
819 	ZVAL_STRINGL(&func_name, USERSTREAM_SEEK, sizeof(USERSTREAM_SEEK)-1, 0);
820 
821 	MAKE_STD_ZVAL(zoffs);
822 	ZVAL_LONG(zoffs, offset);
823 	args[0] = &zoffs;
824 
825 	MAKE_STD_ZVAL(zwhence);
826 	ZVAL_LONG(zwhence, whence);
827 	args[1] = &zwhence;
828 
829 	call_result = call_user_function_ex(NULL,
830 			&us->object,
831 			&func_name,
832 			&retval,
833 			2, args,
834 			0, NULL TSRMLS_CC);
835 
836 	zval_ptr_dtor(&zoffs);
837 	zval_ptr_dtor(&zwhence);
838 
839 	if (call_result == FAILURE) {
840 		/* stream_seek is not implemented, so disable seeks for this stream */
841 		stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
842 		/* there should be no retval to clean up */
843 
844 		if (retval)
845 			zval_ptr_dtor(&retval);
846 
847 		return -1;
848 	} else if (call_result == SUCCESS && retval != NULL && zval_is_true(retval)) {
849 		ret = 0;
850 	} else {
851 		ret = -1;
852 	}
853 
854 	if (retval) {
855 		zval_ptr_dtor(&retval);
856 		retval = NULL;
857 	}
858 
859 	if (ret) {
860 		return ret;
861 	}
862 
863 	/* now determine where we are */
864 	ZVAL_STRINGL(&func_name, USERSTREAM_TELL, sizeof(USERSTREAM_TELL)-1, 0);
865 
866 	call_result = call_user_function_ex(NULL,
867 		&us->object,
868 		&func_name,
869 		&retval,
870 		0, NULL, 0, NULL TSRMLS_CC);
871 
872 	if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_LONG) {
873 		*newoffs = Z_LVAL_P(retval);
874 		ret = 0;
875 	} else if (call_result == FAILURE) {
876 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_TELL " is not implemented!", us->wrapper->classname);
877 		ret = -1;
878 	} else {
879 		ret = -1;
880 	}
881 
882 	if (retval) {
883 		zval_ptr_dtor(&retval);
884 	}
885 	return ret;
886 }
887 
888 /* parse the return value from one of the stat functions and store the
889  * relevant fields into the statbuf provided */
statbuf_from_array(zval * array,php_stream_statbuf * ssb TSRMLS_DC)890 static int statbuf_from_array(zval *array, php_stream_statbuf *ssb TSRMLS_DC)
891 {
892 	zval **elem;
893 
894 #define STAT_PROP_ENTRY_EX(name, name2)                        \
895 	if (SUCCESS == zend_hash_find(Z_ARRVAL_P(array), #name, sizeof(#name), (void**)&elem)) {     \
896 		SEPARATE_ZVAL(elem);																	 \
897 		convert_to_long(*elem);                                                                   \
898 		ssb->sb.st_##name2 = Z_LVAL_PP(elem);                                                      \
899 	}
900 
901 #define STAT_PROP_ENTRY(name) STAT_PROP_ENTRY_EX(name,name)
902 
903 	memset(ssb, 0, sizeof(php_stream_statbuf));
904 	STAT_PROP_ENTRY(dev);
905 	STAT_PROP_ENTRY(ino);
906 	STAT_PROP_ENTRY(mode);
907 	STAT_PROP_ENTRY(nlink);
908 	STAT_PROP_ENTRY(uid);
909 	STAT_PROP_ENTRY(gid);
910 #if HAVE_ST_RDEV
911 	STAT_PROP_ENTRY(rdev);
912 #endif
913 	STAT_PROP_ENTRY(size);
914 #ifdef NETWARE
915 	STAT_PROP_ENTRY_EX(atime, atime.tv_sec);
916 	STAT_PROP_ENTRY_EX(mtime, mtime.tv_sec);
917 	STAT_PROP_ENTRY_EX(ctime, ctime.tv_sec);
918 #else
919 	STAT_PROP_ENTRY(atime);
920 	STAT_PROP_ENTRY(mtime);
921 	STAT_PROP_ENTRY(ctime);
922 #endif
923 #ifdef HAVE_ST_BLKSIZE
924 	STAT_PROP_ENTRY(blksize);
925 #endif
926 #ifdef HAVE_ST_BLOCKS
927 	STAT_PROP_ENTRY(blocks);
928 #endif
929 
930 #undef STAT_PROP_ENTRY
931 #undef STAT_PROP_ENTRY_EX
932 	return SUCCESS;
933 }
934 
php_userstreamop_stat(php_stream * stream,php_stream_statbuf * ssb TSRMLS_DC)935 static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
936 {
937 	zval func_name;
938 	zval *retval = NULL;
939 	int call_result;
940 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
941 	int ret = -1;
942 
943 	ZVAL_STRINGL(&func_name, USERSTREAM_STAT, sizeof(USERSTREAM_STAT)-1, 0);
944 
945 	call_result = call_user_function_ex(NULL,
946 			&us->object,
947 			&func_name,
948 			&retval,
949 			0, NULL, 0, NULL TSRMLS_CC);
950 
951 	if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_ARRAY) {
952 		if (SUCCESS == statbuf_from_array(retval, ssb TSRMLS_CC))
953 			ret = 0;
954 	} else {
955 		if (call_result == FAILURE) {
956 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_STAT " is not implemented!",
957 					us->wrapper->classname);
958 		}
959 	}
960 
961 	if (retval)
962 		zval_ptr_dtor(&retval);
963 
964 	return ret;
965 }
966 
967 
php_userstreamop_set_option(php_stream * stream,int option,int value,void * ptrparam TSRMLS_DC)968 static int php_userstreamop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) {
969 	zval func_name;
970 	zval *retval = NULL;
971 	int call_result;
972 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
973 	int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL;
974 	zval *zvalue = NULL;
975 	zval **args[3];
976 
977 	switch (option) {
978 	case PHP_STREAM_OPTION_CHECK_LIVENESS:
979 		ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1, 0);
980 		call_result = call_user_function_ex(NULL, &us->object, &func_name, &retval, 0, NULL, 0, NULL TSRMLS_CC);
981 		if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_BOOL) {
982 			ret = zval_is_true(retval) ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
983 		} else {
984 			ret = PHP_STREAM_OPTION_RETURN_ERR;
985 			php_error_docref(NULL TSRMLS_CC, E_WARNING,
986 					"%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
987 					us->wrapper->classname);
988 		}
989 		break;
990 
991 	case PHP_STREAM_OPTION_LOCKING:
992 		MAKE_STD_ZVAL(zvalue);
993 		ZVAL_LONG(zvalue, 0);
994 
995 		if (value & LOCK_NB) {
996 			Z_LVAL_P(zvalue) |= PHP_LOCK_NB;
997 		}
998 		switch(value & ~LOCK_NB) {
999 		case LOCK_SH:
1000 			Z_LVAL_P(zvalue) |= PHP_LOCK_SH;
1001 			break;
1002 		case LOCK_EX:
1003 			Z_LVAL_P(zvalue) |= PHP_LOCK_EX;
1004 			break;
1005 		case LOCK_UN:
1006 			Z_LVAL_P(zvalue) |= PHP_LOCK_UN;
1007 			break;
1008 		}
1009 
1010 		args[0] = &zvalue;
1011 
1012 		/* TODO wouldblock */
1013 		ZVAL_STRINGL(&func_name, USERSTREAM_LOCK, sizeof(USERSTREAM_LOCK)-1, 0);
1014 
1015 		call_result = call_user_function_ex(NULL,
1016 											&us->object,
1017 											&func_name,
1018 											&retval,
1019 											1, args, 0, NULL TSRMLS_CC);
1020 
1021 		if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_BOOL) {
1022 			ret = !Z_LVAL_P(retval);
1023 		} else if (call_result == FAILURE) {
1024 			if (value == 0) {
1025 			   	/* lock support test (TODO: more check) */
1026 				ret = PHP_STREAM_OPTION_RETURN_OK;
1027 			} else {
1028 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_LOCK " is not implemented!",
1029 								 us->wrapper->classname);
1030 				ret = PHP_STREAM_OPTION_RETURN_ERR;
1031 			}
1032 		}
1033 
1034 		break;
1035 
1036 	case PHP_STREAM_OPTION_TRUNCATE_API:
1037 		ZVAL_STRINGL(&func_name, USERSTREAM_TRUNCATE, sizeof(USERSTREAM_TRUNCATE)-1, 0);
1038 
1039 		switch (value) {
1040 		case PHP_STREAM_TRUNCATE_SUPPORTED:
1041 			if (zend_is_callable_ex(&func_name, us->object, IS_CALLABLE_CHECK_SILENT,
1042 					NULL, NULL, NULL, NULL TSRMLS_CC))
1043 				ret = PHP_STREAM_OPTION_RETURN_OK;
1044 			else
1045 				ret = PHP_STREAM_OPTION_RETURN_ERR;
1046 			break;
1047 
1048 		case PHP_STREAM_TRUNCATE_SET_SIZE: {
1049 			ptrdiff_t new_size = *(ptrdiff_t*) ptrparam;
1050 			if (new_size >= 0 && new_size <= (ptrdiff_t)LONG_MAX) {
1051 				MAKE_STD_ZVAL(zvalue);
1052 				ZVAL_LONG(zvalue, (long)new_size);
1053 				args[0] = &zvalue;
1054 				call_result = call_user_function_ex(NULL,
1055 													&us->object,
1056 													&func_name,
1057 													&retval,
1058 													1, args, 0, NULL TSRMLS_CC);
1059 				if (call_result == SUCCESS && retval != NULL) {
1060 					if (Z_TYPE_P(retval) == IS_BOOL) {
1061 						ret = Z_LVAL_P(retval) ? PHP_STREAM_OPTION_RETURN_OK :
1062 												 PHP_STREAM_OPTION_RETURN_ERR;
1063 					} else {
1064 						php_error_docref(NULL TSRMLS_CC, E_WARNING,
1065 								"%s::" USERSTREAM_TRUNCATE " did not return a boolean!",
1066 								us->wrapper->classname);
1067 					}
1068 				} else {
1069 					php_error_docref(NULL TSRMLS_CC, E_WARNING,
1070 							"%s::" USERSTREAM_TRUNCATE " is not implemented!",
1071 							us->wrapper->classname);
1072 				}
1073 			} else { /* bad new size */
1074 				ret = PHP_STREAM_OPTION_RETURN_ERR;
1075 			}
1076 			break;
1077 		}
1078 		}
1079 		break;
1080 
1081 	case PHP_STREAM_OPTION_READ_BUFFER:
1082 	case PHP_STREAM_OPTION_WRITE_BUFFER:
1083 	case PHP_STREAM_OPTION_READ_TIMEOUT:
1084 	case PHP_STREAM_OPTION_BLOCKING: {
1085 		zval *zoption = NULL;
1086 		zval *zptrparam = NULL;
1087 
1088 		ZVAL_STRINGL(&func_name, USERSTREAM_SET_OPTION, sizeof(USERSTREAM_SET_OPTION)-1, 0);
1089 
1090 		ALLOC_INIT_ZVAL(zoption);
1091 		ZVAL_LONG(zoption, option);
1092 
1093 		ALLOC_INIT_ZVAL(zvalue);
1094 		ALLOC_INIT_ZVAL(zptrparam);
1095 
1096 		args[0] = &zoption;
1097 		args[1] = &zvalue;
1098 		args[2] = &zptrparam;
1099 
1100 		switch(option) {
1101 		case PHP_STREAM_OPTION_READ_BUFFER:
1102 		case PHP_STREAM_OPTION_WRITE_BUFFER:
1103 			ZVAL_LONG(zvalue, value);
1104 			if (ptrparam) {
1105 				ZVAL_LONG(zptrparam, *(long *)ptrparam);
1106 			} else {
1107 				ZVAL_LONG(zptrparam, BUFSIZ);
1108 			}
1109 			break;
1110 		case PHP_STREAM_OPTION_READ_TIMEOUT: {
1111 			struct timeval tv = *(struct timeval*)ptrparam;
1112 			ZVAL_LONG(zvalue, tv.tv_sec);
1113 			ZVAL_LONG(zptrparam, tv.tv_usec);
1114 			break;
1115 			}
1116 		case PHP_STREAM_OPTION_BLOCKING:
1117 			ZVAL_LONG(zvalue, value);
1118 			break;
1119 		default:
1120 			break;
1121 		}
1122 
1123 		call_result = call_user_function_ex(NULL,
1124 			&us->object,
1125 			&func_name,
1126 			&retval,
1127 			3, args, 0, NULL TSRMLS_CC);
1128 
1129 		if (call_result == FAILURE) {
1130 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_SET_OPTION " is not implemented!",
1131 					us->wrapper->classname);
1132 			ret = PHP_STREAM_OPTION_RETURN_ERR;
1133 		} else if (retval && zend_is_true(retval)) {
1134 			ret = PHP_STREAM_OPTION_RETURN_OK;
1135 		} else {
1136 			ret = PHP_STREAM_OPTION_RETURN_ERR;
1137 		}
1138 
1139 		if (zoption) {
1140 			zval_ptr_dtor(&zoption);
1141 		}
1142 		if (zptrparam) {
1143 			zval_ptr_dtor(&zptrparam);
1144 		}
1145 
1146 		break;
1147 		}
1148 	}
1149 
1150 	/* clean up */
1151 	if (retval) {
1152 		zval_ptr_dtor(&retval);
1153 	}
1154 
1155 
1156 	if (zvalue) {
1157 		zval_ptr_dtor(&zvalue);
1158 	}
1159 
1160 	return ret;
1161 }
1162 
1163 
user_wrapper_unlink(php_stream_wrapper * wrapper,char * url,int options,php_stream_context * context TSRMLS_DC)1164 static int user_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC)
1165 {
1166 	struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1167 	zval *zfilename, *zfuncname, *zretval;
1168 	zval **args[1];
1169 	int call_result;
1170 	zval *object;
1171 	int ret = 0;
1172 
1173 	/* create an instance of our class */
1174 	object = user_stream_create_object(uwrap, context TSRMLS_CC);
1175 	if(object == NULL) {
1176 		return ret;
1177 	}
1178 
1179 	/* call the unlink method */
1180 	MAKE_STD_ZVAL(zfilename);
1181 	ZVAL_STRING(zfilename, url, 1);
1182 	args[0] = &zfilename;
1183 
1184 	MAKE_STD_ZVAL(zfuncname);
1185 	ZVAL_STRING(zfuncname, USERSTREAM_UNLINK, 1);
1186 
1187 	call_result = call_user_function_ex(NULL,
1188 			&object,
1189 			zfuncname,
1190 			&zretval,
1191 			1, args,
1192 			0, NULL	TSRMLS_CC);
1193 
1194 	if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
1195 		ret = Z_LVAL_P(zretval);
1196 	} else if (call_result == FAILURE) {
1197 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_UNLINK " is not implemented!", uwrap->classname);
1198  	}
1199 
1200 	/* clean up */
1201 	zval_ptr_dtor(&object);
1202 	if (zretval)
1203 		zval_ptr_dtor(&zretval);
1204 
1205 	zval_ptr_dtor(&zfuncname);
1206 	zval_ptr_dtor(&zfilename);
1207 
1208 	return ret;
1209 }
1210 
user_wrapper_rename(php_stream_wrapper * wrapper,char * url_from,char * url_to,int options,php_stream_context * context TSRMLS_DC)1211 static int user_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC)
1212 {
1213 	struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1214 	zval *zold_name, *znew_name, *zfuncname, *zretval;
1215 	zval **args[2];
1216 	int call_result;
1217 	zval *object;
1218 	int ret = 0;
1219 
1220 	/* create an instance of our class */
1221 	object = user_stream_create_object(uwrap, context TSRMLS_CC);
1222 	if(object == NULL) {
1223 		return ret;
1224 	}
1225 
1226 	/* call the rename method */
1227 	MAKE_STD_ZVAL(zold_name);
1228 	ZVAL_STRING(zold_name, url_from, 1);
1229 	args[0] = &zold_name;
1230 
1231 	MAKE_STD_ZVAL(znew_name);
1232 	ZVAL_STRING(znew_name, url_to, 1);
1233 	args[1] = &znew_name;
1234 
1235 	MAKE_STD_ZVAL(zfuncname);
1236 	ZVAL_STRING(zfuncname, USERSTREAM_RENAME, 1);
1237 
1238 	call_result = call_user_function_ex(NULL,
1239 			&object,
1240 			zfuncname,
1241 			&zretval,
1242 			2, args,
1243 			0, NULL	TSRMLS_CC);
1244 
1245 	if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
1246 		ret = Z_LVAL_P(zretval);
1247 	} else if (call_result == FAILURE) {
1248 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_RENAME " is not implemented!", uwrap->classname);
1249  	}
1250 
1251 	/* clean up */
1252 	zval_ptr_dtor(&object);
1253 	if (zretval)
1254 		zval_ptr_dtor(&zretval);
1255 
1256 	zval_ptr_dtor(&zfuncname);
1257 	zval_ptr_dtor(&zold_name);
1258 	zval_ptr_dtor(&znew_name);
1259 
1260 	return ret;
1261 }
1262 
user_wrapper_mkdir(php_stream_wrapper * wrapper,char * url,int mode,int options,php_stream_context * context TSRMLS_DC)1263 static int user_wrapper_mkdir(php_stream_wrapper *wrapper, char *url, int mode, int options, php_stream_context *context TSRMLS_DC)
1264 {
1265 	struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1266 	zval *zfilename, *zmode, *zoptions, *zfuncname, *zretval;
1267 	zval **args[3];
1268 	int call_result;
1269 	zval *object;
1270 	int ret = 0;
1271 
1272 	/* create an instance of our class */
1273 	object = user_stream_create_object(uwrap, context TSRMLS_CC);
1274 	if(object == NULL) {
1275 		return ret;
1276 	}
1277 
1278 	/* call the mkdir method */
1279 	MAKE_STD_ZVAL(zfilename);
1280 	ZVAL_STRING(zfilename, url, 1);
1281 	args[0] = &zfilename;
1282 
1283 	MAKE_STD_ZVAL(zmode);
1284 	ZVAL_LONG(zmode, mode);
1285 	args[1] = &zmode;
1286 
1287 	MAKE_STD_ZVAL(zoptions);
1288 	ZVAL_LONG(zoptions, options);
1289 	args[2] = &zoptions;
1290 
1291 	MAKE_STD_ZVAL(zfuncname);
1292 	ZVAL_STRING(zfuncname, USERSTREAM_MKDIR, 1);
1293 
1294 	call_result = call_user_function_ex(NULL,
1295 			&object,
1296 			zfuncname,
1297 			&zretval,
1298 			3, 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_MKDIR " 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(&zmode);
1316 	zval_ptr_dtor(&zoptions);
1317 
1318 	return ret;
1319 }
1320 
user_wrapper_rmdir(php_stream_wrapper * wrapper,char * url,int options,php_stream_context * context TSRMLS_DC)1321 static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC)
1322 {
1323 	struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1324 	zval *zfilename, *zoptions, *zfuncname, *zretval;
1325 	zval **args[3];
1326 	int call_result;
1327 	zval *object;
1328 	int ret = 0;
1329 
1330 	/* create an instance of our class */
1331 	object = user_stream_create_object(uwrap, context TSRMLS_CC);
1332 	if(object == NULL) {
1333 		return ret;
1334 	}
1335 
1336 	/* call the rmdir method */
1337 	MAKE_STD_ZVAL(zfilename);
1338 	ZVAL_STRING(zfilename, url, 1);
1339 	args[0] = &zfilename;
1340 
1341 	MAKE_STD_ZVAL(zoptions);
1342 	ZVAL_LONG(zoptions, options);
1343 	args[1] = &zoptions;
1344 
1345 	MAKE_STD_ZVAL(zfuncname);
1346 	ZVAL_STRING(zfuncname, USERSTREAM_RMDIR, 1);
1347 
1348 	call_result = call_user_function_ex(NULL,
1349 			&object,
1350 			zfuncname,
1351 			&zretval,
1352 			2, args,
1353 			0, NULL	TSRMLS_CC);
1354 
1355 	if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
1356 		ret = Z_LVAL_P(zretval);
1357 	} else if (call_result == FAILURE) {
1358 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_RMDIR " is not implemented!", uwrap->classname);
1359  	}
1360 
1361 	/* clean up */
1362 	zval_ptr_dtor(&object);
1363 	if (zretval) {
1364 		zval_ptr_dtor(&zretval);
1365 	}
1366 
1367 	zval_ptr_dtor(&zfuncname);
1368 	zval_ptr_dtor(&zfilename);
1369 	zval_ptr_dtor(&zoptions);
1370 
1371 	return ret;
1372 }
1373 
user_wrapper_metadata(php_stream_wrapper * wrapper,char * url,int option,void * value,php_stream_context * context TSRMLS_DC)1374 static int user_wrapper_metadata(php_stream_wrapper *wrapper, char *url, int option, void *value, php_stream_context *context TSRMLS_DC)
1375 {
1376 	struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1377 	zval *zfilename, *zoption, *zvalue, *zfuncname, *zretval;
1378 	zval **args[3];
1379 	int call_result;
1380 	zval *object;
1381 	int ret = 0;
1382 
1383 	MAKE_STD_ZVAL(zvalue);
1384 	switch(option) {
1385 		case PHP_STREAM_META_TOUCH:
1386 			array_init(zvalue);
1387 			if(value) {
1388 				struct utimbuf *newtime = (struct utimbuf *)value;
1389 				add_index_long(zvalue, 0, newtime->modtime);
1390 				add_index_long(zvalue, 1, newtime->actime);
1391 			}
1392 			break;
1393 		case PHP_STREAM_META_GROUP:
1394 		case PHP_STREAM_META_OWNER:
1395 		case PHP_STREAM_META_ACCESS:
1396 			ZVAL_LONG(zvalue, *(long *)value);
1397 			break;
1398 		case PHP_STREAM_META_GROUP_NAME:
1399 		case PHP_STREAM_META_OWNER_NAME:
1400 			ZVAL_STRING(zvalue, value, 1);
1401 			break;
1402 		default:
1403 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown option %d for " USERSTREAM_METADATA, option);
1404 			zval_ptr_dtor(&zvalue);
1405 			return ret;
1406 	}
1407 
1408 	/* create an instance of our class */
1409 	object = user_stream_create_object(uwrap, context TSRMLS_CC);
1410 	if(object == NULL) {
1411 		zval_ptr_dtor(&zvalue);
1412 		return ret;
1413 	}
1414 
1415 	/* call the mkdir method */
1416 	MAKE_STD_ZVAL(zfilename);
1417 	ZVAL_STRING(zfilename, url, 1);
1418 	args[0] = &zfilename;
1419 
1420 	MAKE_STD_ZVAL(zoption);
1421 	ZVAL_LONG(zoption, option);
1422 	args[1] = &zoption;
1423 
1424 	args[2] = &zvalue;
1425 
1426 	MAKE_STD_ZVAL(zfuncname);
1427 	ZVAL_STRING(zfuncname, USERSTREAM_METADATA, 1);
1428 
1429 	call_result = call_user_function_ex(NULL,
1430 			&object,
1431 			zfuncname,
1432 			&zretval,
1433 			3, args,
1434 			0, NULL	TSRMLS_CC);
1435 
1436 	if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
1437 		ret = Z_LVAL_P(zretval);
1438 	} else if (call_result == FAILURE) {
1439 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_METADATA " is not implemented!", uwrap->classname);
1440  	}
1441 
1442 	/* clean up */
1443 	zval_ptr_dtor(&object);
1444 	if (zretval) {
1445 		zval_ptr_dtor(&zretval);
1446 	}
1447 
1448 	zval_ptr_dtor(&zfuncname);
1449 	zval_ptr_dtor(&zfilename);
1450 	zval_ptr_dtor(&zoption);
1451 	zval_ptr_dtor(&zvalue);
1452 
1453 	return ret;
1454 }
1455 
1456 
user_wrapper_stat_url(php_stream_wrapper * wrapper,char * url,int flags,php_stream_statbuf * ssb,php_stream_context * context TSRMLS_DC)1457 static int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC)
1458 {
1459 	struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1460 	zval *zfilename, *zfuncname, *zretval, *zflags;
1461 	zval **args[2];
1462 	int call_result;
1463 	zval *object;
1464 	int ret = -1;
1465 
1466 	/* create an instance of our class */
1467 	object = user_stream_create_object(uwrap, context TSRMLS_CC);
1468 	if(object == NULL) {
1469 		return ret;
1470 	}
1471 
1472 	/* call it's stat_url method - set up params first */
1473 	MAKE_STD_ZVAL(zfilename);
1474 	ZVAL_STRING(zfilename, url, 1);
1475 	args[0] = &zfilename;
1476 
1477 	MAKE_STD_ZVAL(zflags);
1478 	ZVAL_LONG(zflags, flags);
1479 	args[1] = &zflags;
1480 
1481 	MAKE_STD_ZVAL(zfuncname);
1482 	ZVAL_STRING(zfuncname, USERSTREAM_STATURL, 1);
1483 
1484 	call_result = call_user_function_ex(NULL,
1485 			&object,
1486 			zfuncname,
1487 			&zretval,
1488 			2, args,
1489 			0, NULL	TSRMLS_CC);
1490 
1491 	if (call_result == SUCCESS && zretval != NULL && Z_TYPE_P(zretval) == IS_ARRAY) {
1492 		/* We got the info we needed */
1493 		if (SUCCESS == statbuf_from_array(zretval, ssb TSRMLS_CC))
1494 			ret = 0;
1495 	} else {
1496 		if (call_result == FAILURE) {
1497 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_STATURL " is not implemented!",
1498 					uwrap->classname);
1499 		}
1500 	}
1501 
1502 	/* clean up */
1503 	zval_ptr_dtor(&object);
1504 	if (zretval)
1505 		zval_ptr_dtor(&zretval);
1506 
1507 	zval_ptr_dtor(&zfuncname);
1508 	zval_ptr_dtor(&zfilename);
1509 	zval_ptr_dtor(&zflags);
1510 
1511 	return ret;
1512 
1513 }
1514 
php_userstreamop_readdir(php_stream * stream,char * buf,size_t count TSRMLS_DC)1515 static size_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t count TSRMLS_DC)
1516 {
1517 	zval func_name;
1518 	zval *retval = NULL;
1519 	int call_result;
1520 	size_t didread = 0;
1521 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1522 	php_stream_dirent *ent = (php_stream_dirent*)buf;
1523 
1524 	/* avoid problems if someone mis-uses the stream */
1525 	if (count != sizeof(php_stream_dirent))
1526 		return 0;
1527 
1528 	ZVAL_STRINGL(&func_name, USERSTREAM_DIR_READ, sizeof(USERSTREAM_DIR_READ)-1, 0);
1529 
1530 	call_result = call_user_function_ex(NULL,
1531 			&us->object,
1532 			&func_name,
1533 			&retval,
1534 			0, NULL,
1535 			0, NULL TSRMLS_CC);
1536 
1537 	if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) != IS_BOOL) {
1538 		convert_to_string(retval);
1539 		PHP_STRLCPY(ent->d_name, Z_STRVAL_P(retval), sizeof(ent->d_name), Z_STRLEN_P(retval));
1540 
1541 		didread = sizeof(php_stream_dirent);
1542 	} else if (call_result == FAILURE) {
1543 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_DIR_READ " is not implemented!",
1544 				us->wrapper->classname);
1545 	}
1546 
1547 	if (retval)
1548 		zval_ptr_dtor(&retval);
1549 
1550 	return didread;
1551 }
1552 
php_userstreamop_closedir(php_stream * stream,int close_handle TSRMLS_DC)1553 static int php_userstreamop_closedir(php_stream *stream, int close_handle TSRMLS_DC)
1554 {
1555 	zval func_name;
1556 	zval *retval = NULL;
1557 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1558 
1559 	assert(us != NULL);
1560 
1561 	ZVAL_STRINGL(&func_name, USERSTREAM_DIR_CLOSE, sizeof(USERSTREAM_DIR_CLOSE)-1, 0);
1562 
1563 	call_user_function_ex(NULL,
1564 			&us->object,
1565 			&func_name,
1566 			&retval,
1567 			0, NULL, 0, NULL TSRMLS_CC);
1568 
1569 	if (retval)
1570 		zval_ptr_dtor(&retval);
1571 
1572 	zval_ptr_dtor(&us->object);
1573 
1574 	efree(us);
1575 
1576 	return 0;
1577 }
1578 
php_userstreamop_rewinddir(php_stream * stream,off_t offset,int whence,off_t * newoffs TSRMLS_DC)1579 static int php_userstreamop_rewinddir(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
1580 {
1581 	zval func_name;
1582 	zval *retval = NULL;
1583 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1584 
1585 	ZVAL_STRINGL(&func_name, USERSTREAM_DIR_REWIND, sizeof(USERSTREAM_DIR_REWIND)-1, 0);
1586 
1587 	call_user_function_ex(NULL,
1588 			&us->object,
1589 			&func_name,
1590 			&retval,
1591 			0, NULL, 0, NULL TSRMLS_CC);
1592 
1593 	if (retval)
1594 		zval_ptr_dtor(&retval);
1595 
1596 	return 0;
1597 
1598 }
1599 
php_userstreamop_cast(php_stream * stream,int castas,void ** retptr TSRMLS_DC)1600 static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr TSRMLS_DC)
1601 {
1602 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1603 	zval func_name;
1604 	zval *retval = NULL;
1605 	zval *zcastas = NULL;
1606 	zval **args[1];
1607 	php_stream * intstream = NULL;
1608 	int call_result;
1609 	int ret = FAILURE;
1610 
1611 	ZVAL_STRINGL(&func_name, USERSTREAM_CAST, sizeof(USERSTREAM_CAST)-1, 0);
1612 
1613 	ALLOC_INIT_ZVAL(zcastas);
1614 	switch(castas) {
1615 	case PHP_STREAM_AS_FD_FOR_SELECT:
1616 		ZVAL_LONG(zcastas, PHP_STREAM_AS_FD_FOR_SELECT);
1617 		break;
1618 	default:
1619 		ZVAL_LONG(zcastas, PHP_STREAM_AS_STDIO);
1620 		break;
1621 	}
1622 	args[0] = &zcastas;
1623 
1624 	call_result = call_user_function_ex(NULL,
1625 			&us->object,
1626 			&func_name,
1627 			&retval,
1628 			1, args, 0, NULL TSRMLS_CC);
1629 
1630 	do {
1631 		if (call_result == FAILURE) {
1632 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_CAST " is not implemented!",
1633 					us->wrapper->classname);
1634 			break;
1635 		}
1636 		if (retval == NULL || !zend_is_true(retval)) {
1637 			break;
1638 		}
1639 		php_stream_from_zval_no_verify(intstream, &retval);
1640 		if (!intstream) {
1641 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_CAST " must return a stream resource",
1642 					us->wrapper->classname);
1643 			break;
1644 		}
1645 		if (intstream == stream) {
1646 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_CAST " must not return itself",
1647 					us->wrapper->classname);
1648 			intstream = NULL;
1649 			break;
1650 		}
1651 		ret = php_stream_cast(intstream, castas, retptr, 1);
1652 	} while (0);
1653 
1654 	if (retval) {
1655 		zval_ptr_dtor(&retval);
1656 	}
1657 	if (zcastas) {
1658 		zval_ptr_dtor(&zcastas);
1659 	}
1660 
1661 	return ret;
1662 }
1663 
1664 php_stream_ops php_stream_userspace_ops = {
1665 	php_userstreamop_write, php_userstreamop_read,
1666 	php_userstreamop_close, php_userstreamop_flush,
1667 	"user-space",
1668 	php_userstreamop_seek,
1669 	php_userstreamop_cast,
1670 	php_userstreamop_stat,
1671 	php_userstreamop_set_option,
1672 };
1673 
1674 php_stream_ops php_stream_userspace_dir_ops = {
1675 	NULL, /* write */
1676 	php_userstreamop_readdir,
1677 	php_userstreamop_closedir,
1678 	NULL, /* flush */
1679 	"user-space-dir",
1680 	php_userstreamop_rewinddir,
1681 	NULL, /* cast */
1682 	NULL, /* stat */
1683 	NULL  /* set_option */
1684 };
1685 
1686 
1687