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