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