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