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