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