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