1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 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 ssize_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 ssize_t didwrite;
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 if (EG(exception)) {
617 return -1;
618 }
619
620 if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
621 if (Z_TYPE(retval) == IS_FALSE) {
622 didwrite = -1;
623 } else {
624 convert_to_long(&retval);
625 didwrite = Z_LVAL(retval);
626 }
627 } else {
628 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_WRITE " is not implemented!",
629 us->wrapper->classname);
630 didwrite = -1;
631 }
632
633 /* don't allow strange buffer overruns due to bogus return */
634 if (didwrite > 0 && didwrite > count) {
635 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_WRITE " wrote " ZEND_LONG_FMT " bytes more data than requested (" ZEND_LONG_FMT " written, " ZEND_LONG_FMT " max)",
636 us->wrapper->classname,
637 (zend_long)(didwrite - count), (zend_long)didwrite, (zend_long)count);
638 didwrite = count;
639 }
640
641 zval_ptr_dtor(&retval);
642
643 return didwrite;
644 }
645
php_userstreamop_read(php_stream * stream,char * buf,size_t count)646 static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count)
647 {
648 zval func_name;
649 zval retval;
650 zval args[1];
651 int call_result;
652 size_t didread = 0;
653 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
654
655 assert(us != NULL);
656
657 ZVAL_STRINGL(&func_name, USERSTREAM_READ, sizeof(USERSTREAM_READ)-1);
658
659 ZVAL_LONG(&args[0], count);
660
661 call_result = call_user_function_ex(NULL,
662 Z_ISUNDEF(us->object)? NULL : &us->object,
663 &func_name,
664 &retval,
665 1, args,
666 0, NULL);
667
668 zval_ptr_dtor(&args[0]);
669 zval_ptr_dtor(&func_name);
670
671 if (EG(exception)) {
672 return -1;
673 }
674
675 if (call_result == FAILURE) {
676 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_READ " is not implemented!",
677 us->wrapper->classname);
678 return -1;
679 }
680
681 if (Z_TYPE(retval) == IS_FALSE) {
682 return -1;
683 }
684
685 if (!try_convert_to_string(&retval)) {
686 return -1;
687 }
688
689 didread = Z_STRLEN(retval);
690 if (didread > 0) {
691 if (didread > count) {
692 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",
693 us->wrapper->classname, (zend_long)(didread - count), (zend_long)didread, (zend_long)count);
694 didread = count;
695 }
696 memcpy(buf, Z_STRVAL(retval), didread);
697 }
698
699 zval_ptr_dtor(&retval);
700 ZVAL_UNDEF(&retval);
701
702 /* since the user stream has no way of setting the eof flag directly, we need to ask it if we hit eof */
703
704 ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1);
705 call_result = call_user_function(NULL,
706 Z_ISUNDEF(us->object)? NULL : &us->object,
707 &func_name,
708 &retval,
709 0, NULL);
710 zval_ptr_dtor(&func_name);
711
712 if (EG(exception)) {
713 stream->eof = 1;
714 return -1;
715 }
716
717 if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF && zval_is_true(&retval)) {
718 stream->eof = 1;
719 } else if (call_result == FAILURE) {
720 php_error_docref(NULL, E_WARNING,
721 "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
722 us->wrapper->classname);
723
724 stream->eof = 1;
725 }
726
727 zval_ptr_dtor(&retval);
728
729 return didread;
730 }
731
php_userstreamop_close(php_stream * stream,int close_handle)732 static int php_userstreamop_close(php_stream *stream, int close_handle)
733 {
734 zval func_name;
735 zval retval;
736 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
737
738 assert(us != NULL);
739
740 ZVAL_STRINGL(&func_name, USERSTREAM_CLOSE, sizeof(USERSTREAM_CLOSE)-1);
741
742 call_user_function(NULL,
743 Z_ISUNDEF(us->object)? NULL : &us->object,
744 &func_name,
745 &retval,
746 0, NULL);
747
748 zval_ptr_dtor(&retval);
749 zval_ptr_dtor(&func_name);
750
751 zval_ptr_dtor(&us->object);
752 ZVAL_UNDEF(&us->object);
753
754 efree(us);
755
756 return 0;
757 }
758
php_userstreamop_flush(php_stream * stream)759 static int php_userstreamop_flush(php_stream *stream)
760 {
761 zval func_name;
762 zval retval;
763 int call_result;
764 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
765
766 assert(us != NULL);
767
768 ZVAL_STRINGL(&func_name, USERSTREAM_FLUSH, sizeof(USERSTREAM_FLUSH)-1);
769
770 call_result = call_user_function(NULL,
771 Z_ISUNDEF(us->object)? NULL : &us->object,
772 &func_name,
773 &retval,
774 0, NULL);
775
776 if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF && zval_is_true(&retval))
777 call_result = 0;
778 else
779 call_result = -1;
780
781 zval_ptr_dtor(&retval);
782 zval_ptr_dtor(&func_name);
783
784 return call_result;
785 }
786
php_userstreamop_seek(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffs)787 static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs)
788 {
789 zval func_name;
790 zval retval;
791 int call_result, ret;
792 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
793 zval args[2];
794
795 assert(us != NULL);
796
797 ZVAL_STRINGL(&func_name, USERSTREAM_SEEK, sizeof(USERSTREAM_SEEK)-1);
798
799 ZVAL_LONG(&args[0], offset);
800 ZVAL_LONG(&args[1], whence);
801
802 call_result = call_user_function_ex(NULL,
803 Z_ISUNDEF(us->object)? NULL : &us->object,
804 &func_name,
805 &retval,
806 2, args,
807 0, NULL);
808
809 zval_ptr_dtor(&args[0]);
810 zval_ptr_dtor(&args[1]);
811 zval_ptr_dtor(&func_name);
812
813 if (call_result == FAILURE) {
814 /* stream_seek is not implemented, so disable seeks for this stream */
815 stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
816 /* there should be no retval to clean up */
817
818 zval_ptr_dtor(&retval);
819
820 return -1;
821 } else if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF && zval_is_true(&retval)) {
822 ret = 0;
823 } else {
824 ret = -1;
825 }
826
827 zval_ptr_dtor(&retval);
828 ZVAL_UNDEF(&retval);
829
830 if (ret) {
831 return ret;
832 }
833
834 /* now determine where we are */
835 ZVAL_STRINGL(&func_name, USERSTREAM_TELL, sizeof(USERSTREAM_TELL)-1);
836
837 call_result = call_user_function(NULL,
838 Z_ISUNDEF(us->object)? NULL : &us->object,
839 &func_name,
840 &retval,
841 0, NULL);
842
843 if (call_result == SUCCESS && Z_TYPE(retval) == IS_LONG) {
844 *newoffs = Z_LVAL(retval);
845 ret = 0;
846 } else if (call_result == FAILURE) {
847 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_TELL " is not implemented!", us->wrapper->classname);
848 ret = -1;
849 } else {
850 ret = -1;
851 }
852
853 zval_ptr_dtor(&retval);
854 zval_ptr_dtor(&func_name);
855 return ret;
856 }
857
858 /* parse the return value from one of the stat functions and store the
859 * relevant fields into the statbuf provided */
statbuf_from_array(zval * array,php_stream_statbuf * ssb)860 static int statbuf_from_array(zval *array, php_stream_statbuf *ssb)
861 {
862 zval *elem;
863
864 #define STAT_PROP_ENTRY_EX(name, name2) \
865 if (NULL != (elem = zend_hash_str_find(Z_ARRVAL_P(array), #name, sizeof(#name)-1))) { \
866 ssb->sb.st_##name2 = zval_get_long(elem); \
867 }
868
869 #define STAT_PROP_ENTRY(name) STAT_PROP_ENTRY_EX(name,name)
870
871 memset(ssb, 0, sizeof(php_stream_statbuf));
872 STAT_PROP_ENTRY(dev);
873 STAT_PROP_ENTRY(ino);
874 STAT_PROP_ENTRY(mode);
875 STAT_PROP_ENTRY(nlink);
876 STAT_PROP_ENTRY(uid);
877 STAT_PROP_ENTRY(gid);
878 #if HAVE_STRUCT_STAT_ST_RDEV
879 STAT_PROP_ENTRY(rdev);
880 #endif
881 STAT_PROP_ENTRY(size);
882 STAT_PROP_ENTRY(atime);
883 STAT_PROP_ENTRY(mtime);
884 STAT_PROP_ENTRY(ctime);
885 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
886 STAT_PROP_ENTRY(blksize);
887 #endif
888 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
889 STAT_PROP_ENTRY(blocks);
890 #endif
891
892 #undef STAT_PROP_ENTRY
893 #undef STAT_PROP_ENTRY_EX
894 return SUCCESS;
895 }
896
php_userstreamop_stat(php_stream * stream,php_stream_statbuf * ssb)897 static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb)
898 {
899 zval func_name;
900 zval retval;
901 int call_result;
902 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
903 int ret = -1;
904
905 ZVAL_STRINGL(&func_name, USERSTREAM_STAT, sizeof(USERSTREAM_STAT)-1);
906
907 call_result = call_user_function(NULL,
908 Z_ISUNDEF(us->object)? NULL : &us->object,
909 &func_name,
910 &retval,
911 0, NULL);
912
913 if (call_result == SUCCESS && Z_TYPE(retval) == IS_ARRAY) {
914 if (SUCCESS == statbuf_from_array(&retval, ssb))
915 ret = 0;
916 } else {
917 if (call_result == FAILURE) {
918 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STAT " is not implemented!",
919 us->wrapper->classname);
920 }
921 }
922
923 zval_ptr_dtor(&retval);
924 zval_ptr_dtor(&func_name);
925
926 return ret;
927 }
928
929
php_userstreamop_set_option(php_stream * stream,int option,int value,void * ptrparam)930 static int php_userstreamop_set_option(php_stream *stream, int option, int value, void *ptrparam) {
931 zval func_name;
932 zval retval;
933 int call_result;
934 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
935 int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL;
936 zval args[3];
937
938 switch (option) {
939 case PHP_STREAM_OPTION_CHECK_LIVENESS:
940 ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1);
941 call_result = call_user_function(NULL, Z_ISUNDEF(us->object)? NULL : &us->object, &func_name, &retval, 0, NULL);
942 if (call_result == SUCCESS && (Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
943 ret = zval_is_true(&retval) ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
944 } else {
945 ret = PHP_STREAM_OPTION_RETURN_ERR;
946 php_error_docref(NULL, E_WARNING,
947 "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
948 us->wrapper->classname);
949 }
950 zval_ptr_dtor(&retval);
951 zval_ptr_dtor(&func_name);
952 break;
953
954 case PHP_STREAM_OPTION_LOCKING:
955 ZVAL_LONG(&args[0], 0);
956
957 if (value & LOCK_NB) {
958 Z_LVAL_P(&args[0]) |= PHP_LOCK_NB;
959 }
960 switch(value & ~LOCK_NB) {
961 case LOCK_SH:
962 Z_LVAL_P(&args[0]) |= PHP_LOCK_SH;
963 break;
964 case LOCK_EX:
965 Z_LVAL_P(&args[0]) |= PHP_LOCK_EX;
966 break;
967 case LOCK_UN:
968 Z_LVAL_P(&args[0]) |= PHP_LOCK_UN;
969 break;
970 }
971
972 /* TODO wouldblock */
973 ZVAL_STRINGL(&func_name, USERSTREAM_LOCK, sizeof(USERSTREAM_LOCK)-1);
974
975 call_result = call_user_function_ex(NULL,
976 Z_ISUNDEF(us->object)? NULL : &us->object,
977 &func_name,
978 &retval,
979 1, args, 0, NULL);
980
981 if (call_result == SUCCESS && (Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
982 ret = (Z_TYPE(retval) == IS_FALSE);
983 } else if (call_result == FAILURE) {
984 if (value == 0) {
985 /* lock support test (TODO: more check) */
986 ret = PHP_STREAM_OPTION_RETURN_OK;
987 } else {
988 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_LOCK " is not implemented!",
989 us->wrapper->classname);
990 ret = PHP_STREAM_OPTION_RETURN_ERR;
991 }
992 }
993
994 zval_ptr_dtor(&retval);
995 zval_ptr_dtor(&func_name);
996 zval_ptr_dtor(&args[0]);
997 break;
998
999 case PHP_STREAM_OPTION_TRUNCATE_API:
1000 ZVAL_STRINGL(&func_name, USERSTREAM_TRUNCATE, sizeof(USERSTREAM_TRUNCATE)-1);
1001
1002 switch (value) {
1003 case PHP_STREAM_TRUNCATE_SUPPORTED:
1004 if (zend_is_callable_ex(&func_name,
1005 Z_ISUNDEF(us->object)? NULL : Z_OBJ(us->object),
1006 IS_CALLABLE_CHECK_SILENT, NULL, NULL, NULL))
1007 ret = PHP_STREAM_OPTION_RETURN_OK;
1008 else
1009 ret = PHP_STREAM_OPTION_RETURN_ERR;
1010 break;
1011
1012 case PHP_STREAM_TRUNCATE_SET_SIZE: {
1013 ptrdiff_t new_size = *(ptrdiff_t*) ptrparam;
1014 if (new_size >= 0 && new_size <= (ptrdiff_t)LONG_MAX) {
1015 ZVAL_LONG(&args[0], (zend_long)new_size);
1016 call_result = call_user_function_ex(NULL,
1017 Z_ISUNDEF(us->object)? NULL : &us->object,
1018 &func_name,
1019 &retval,
1020 1, args, 0, NULL);
1021 if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
1022 if (Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE) {
1023 ret = (Z_TYPE(retval) == IS_TRUE) ? PHP_STREAM_OPTION_RETURN_OK :
1024 PHP_STREAM_OPTION_RETURN_ERR;
1025 } else {
1026 php_error_docref(NULL, E_WARNING,
1027 "%s::" USERSTREAM_TRUNCATE " did not return a boolean!",
1028 us->wrapper->classname);
1029 }
1030 } else {
1031 php_error_docref(NULL, E_WARNING,
1032 "%s::" USERSTREAM_TRUNCATE " is not implemented!",
1033 us->wrapper->classname);
1034 }
1035 zval_ptr_dtor(&retval);
1036 zval_ptr_dtor(&args[0]);
1037 } else { /* bad new size */
1038 ret = PHP_STREAM_OPTION_RETURN_ERR;
1039 }
1040 break;
1041 }
1042 }
1043 zval_ptr_dtor(&func_name);
1044 break;
1045
1046 case PHP_STREAM_OPTION_READ_BUFFER:
1047 case PHP_STREAM_OPTION_WRITE_BUFFER:
1048 case PHP_STREAM_OPTION_READ_TIMEOUT:
1049 case PHP_STREAM_OPTION_BLOCKING: {
1050
1051 ZVAL_STRINGL(&func_name, USERSTREAM_SET_OPTION, sizeof(USERSTREAM_SET_OPTION)-1);
1052
1053 ZVAL_LONG(&args[0], option);
1054 ZVAL_NULL(&args[1]);
1055 ZVAL_NULL(&args[2]);
1056
1057 switch(option) {
1058 case PHP_STREAM_OPTION_READ_BUFFER:
1059 case PHP_STREAM_OPTION_WRITE_BUFFER:
1060 ZVAL_LONG(&args[1], value);
1061 if (ptrparam) {
1062 ZVAL_LONG(&args[2], *(long *)ptrparam);
1063 } else {
1064 ZVAL_LONG(&args[2], BUFSIZ);
1065 }
1066 break;
1067 case PHP_STREAM_OPTION_READ_TIMEOUT: {
1068 struct timeval tv = *(struct timeval*)ptrparam;
1069 ZVAL_LONG(&args[1], tv.tv_sec);
1070 ZVAL_LONG(&args[2], tv.tv_usec);
1071 break;
1072 }
1073 case PHP_STREAM_OPTION_BLOCKING:
1074 ZVAL_LONG(&args[1], value);
1075 break;
1076 default:
1077 break;
1078 }
1079
1080 call_result = call_user_function_ex(NULL,
1081 Z_ISUNDEF(us->object)? NULL : &us->object,
1082 &func_name,
1083 &retval,
1084 3, args, 0, NULL);
1085
1086 if (call_result == FAILURE) {
1087 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_SET_OPTION " is not implemented!",
1088 us->wrapper->classname);
1089 ret = PHP_STREAM_OPTION_RETURN_ERR;
1090 } else if (zend_is_true(&retval)) {
1091 ret = PHP_STREAM_OPTION_RETURN_OK;
1092 } else {
1093 ret = PHP_STREAM_OPTION_RETURN_ERR;
1094 }
1095
1096 zval_ptr_dtor(&retval);
1097 zval_ptr_dtor(&args[2]);
1098 zval_ptr_dtor(&args[1]);
1099 zval_ptr_dtor(&args[0]);
1100 zval_ptr_dtor(&func_name);
1101
1102 break;
1103 }
1104 }
1105
1106 return ret;
1107 }
1108
1109
user_wrapper_unlink(php_stream_wrapper * wrapper,const char * url,int options,php_stream_context * context)1110 static int user_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context)
1111 {
1112 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1113 zval zfuncname, zretval;
1114 zval args[1];
1115 int call_result;
1116 zval object;
1117 int ret = 0;
1118
1119 /* create an instance of our class */
1120 user_stream_create_object(uwrap, context, &object);
1121 if (Z_TYPE(object) == IS_UNDEF) {
1122 return ret;
1123 }
1124
1125 /* call the unlink method */
1126 ZVAL_STRING(&args[0], url);
1127
1128 ZVAL_STRING(&zfuncname, USERSTREAM_UNLINK);
1129
1130 call_result = call_user_function_ex(NULL,
1131 &object,
1132 &zfuncname,
1133 &zretval,
1134 1, args,
1135 0, NULL );
1136
1137 if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
1138 ret = (Z_TYPE(zretval) == IS_TRUE);
1139 } else if (call_result == FAILURE) {
1140 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_UNLINK " is not implemented!", uwrap->classname);
1141 }
1142
1143 /* clean up */
1144 zval_ptr_dtor(&object);
1145 zval_ptr_dtor(&zretval);
1146 zval_ptr_dtor(&zfuncname);
1147
1148 zval_ptr_dtor(&args[0]);
1149
1150 return ret;
1151 }
1152
user_wrapper_rename(php_stream_wrapper * wrapper,const char * url_from,const char * url_to,int options,php_stream_context * context)1153 static int user_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from, const char *url_to,
1154 int options, php_stream_context *context)
1155 {
1156 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1157 zval zfuncname, zretval;
1158 zval args[2];
1159 int call_result;
1160 zval object;
1161 int ret = 0;
1162
1163 /* create an instance of our class */
1164 user_stream_create_object(uwrap, context, &object);
1165 if (Z_TYPE(object) == IS_UNDEF) {
1166 return ret;
1167 }
1168
1169 /* call the rename method */
1170 ZVAL_STRING(&args[0], url_from);
1171 ZVAL_STRING(&args[1], url_to);
1172
1173 ZVAL_STRING(&zfuncname, USERSTREAM_RENAME);
1174
1175 call_result = call_user_function_ex(NULL,
1176 &object,
1177 &zfuncname,
1178 &zretval,
1179 2, args,
1180 0, NULL );
1181
1182 if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
1183 ret = (Z_TYPE(zretval) == IS_TRUE);
1184 } else if (call_result == FAILURE) {
1185 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_RENAME " is not implemented!", uwrap->classname);
1186 }
1187
1188 /* clean up */
1189 zval_ptr_dtor(&object);
1190 zval_ptr_dtor(&zretval);
1191
1192 zval_ptr_dtor(&zfuncname);
1193 zval_ptr_dtor(&args[1]);
1194 zval_ptr_dtor(&args[0]);
1195
1196 return ret;
1197 }
1198
user_wrapper_mkdir(php_stream_wrapper * wrapper,const char * url,int mode,int options,php_stream_context * context)1199 static int user_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url, int mode,
1200 int options, php_stream_context *context)
1201 {
1202 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1203 zval zfuncname, zretval;
1204 zval args[3];
1205 int call_result;
1206 zval object;
1207 int ret = 0;
1208
1209 /* create an instance of our class */
1210 user_stream_create_object(uwrap, context, &object);
1211 if (Z_TYPE(object) == IS_UNDEF) {
1212 return ret;
1213 }
1214
1215 /* call the mkdir method */
1216 ZVAL_STRING(&args[0], url);
1217 ZVAL_LONG(&args[1], mode);
1218 ZVAL_LONG(&args[2], options);
1219
1220 ZVAL_STRING(&zfuncname, USERSTREAM_MKDIR);
1221
1222 call_result = call_user_function_ex(NULL,
1223 &object,
1224 &zfuncname,
1225 &zretval,
1226 3, args,
1227 0, NULL );
1228
1229 if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
1230 ret = (Z_TYPE(zretval) == IS_TRUE);
1231 } else if (call_result == FAILURE) {
1232 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_MKDIR " is not implemented!", uwrap->classname);
1233 }
1234
1235 /* clean up */
1236 zval_ptr_dtor(&object);
1237 zval_ptr_dtor(&zretval);
1238
1239 zval_ptr_dtor(&zfuncname);
1240 zval_ptr_dtor(&args[2]);
1241 zval_ptr_dtor(&args[1]);
1242 zval_ptr_dtor(&args[0]);
1243
1244 return ret;
1245 }
1246
user_wrapper_rmdir(php_stream_wrapper * wrapper,const char * url,int options,php_stream_context * context)1247 static int user_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url,
1248 int options, php_stream_context *context)
1249 {
1250 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1251 zval zfuncname, zretval;
1252 zval args[2];
1253 int call_result;
1254 zval object;
1255 int ret = 0;
1256
1257 /* create an instance of our class */
1258 user_stream_create_object(uwrap, context, &object);
1259 if (Z_TYPE(object) == IS_UNDEF) {
1260 return ret;
1261 }
1262
1263 /* call the rmdir method */
1264 ZVAL_STRING(&args[0], url);
1265 ZVAL_LONG(&args[1], options);
1266
1267 ZVAL_STRING(&zfuncname, USERSTREAM_RMDIR);
1268
1269 call_result = call_user_function_ex(NULL,
1270 &object,
1271 &zfuncname,
1272 &zretval,
1273 2, args,
1274 0, NULL );
1275
1276 if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
1277 ret = (Z_TYPE(zretval) == IS_TRUE);
1278 } else if (call_result == FAILURE) {
1279 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_RMDIR " is not implemented!", uwrap->classname);
1280 }
1281
1282 /* clean up */
1283 zval_ptr_dtor(&object);
1284 zval_ptr_dtor(&zretval);
1285
1286 zval_ptr_dtor(&zfuncname);
1287 zval_ptr_dtor(&args[1]);
1288 zval_ptr_dtor(&args[0]);
1289
1290 return ret;
1291 }
1292
user_wrapper_metadata(php_stream_wrapper * wrapper,const char * url,int option,void * value,php_stream_context * context)1293 static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, int option,
1294 void *value, php_stream_context *context)
1295 {
1296 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1297 zval zfuncname, zretval;
1298 zval args[3];
1299 int call_result;
1300 zval object;
1301 int ret = 0;
1302
1303 switch(option) {
1304 case PHP_STREAM_META_TOUCH:
1305 array_init(&args[2]);
1306 if(value) {
1307 struct utimbuf *newtime = (struct utimbuf *)value;
1308 add_index_long(&args[2], 0, newtime->modtime);
1309 add_index_long(&args[2], 1, newtime->actime);
1310 }
1311 break;
1312 case PHP_STREAM_META_GROUP:
1313 case PHP_STREAM_META_OWNER:
1314 case PHP_STREAM_META_ACCESS:
1315 ZVAL_LONG(&args[2], *(long *)value);
1316 break;
1317 case PHP_STREAM_META_GROUP_NAME:
1318 case PHP_STREAM_META_OWNER_NAME:
1319 ZVAL_STRING(&args[2], value);
1320 break;
1321 default:
1322 php_error_docref(NULL, E_WARNING, "Unknown option %d for " USERSTREAM_METADATA, option);
1323 zval_ptr_dtor(&args[2]);
1324 return ret;
1325 }
1326
1327 /* create an instance of our class */
1328 user_stream_create_object(uwrap, context, &object);
1329 if (Z_TYPE(object) == IS_UNDEF) {
1330 zval_ptr_dtor(&args[2]);
1331 return ret;
1332 }
1333
1334 /* call the mkdir method */
1335 ZVAL_STRING(&args[0], url);
1336 ZVAL_LONG(&args[1], option);
1337
1338 ZVAL_STRING(&zfuncname, USERSTREAM_METADATA);
1339
1340 call_result = call_user_function_ex(NULL,
1341 &object,
1342 &zfuncname,
1343 &zretval,
1344 3, args,
1345 0, NULL );
1346
1347 if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
1348 ret = Z_TYPE(zretval) == IS_TRUE;
1349 } else if (call_result == FAILURE) {
1350 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_METADATA " is not implemented!", uwrap->classname);
1351 }
1352
1353 /* clean up */
1354 zval_ptr_dtor(&object);
1355 zval_ptr_dtor(&zretval);
1356
1357 zval_ptr_dtor(&zfuncname);
1358 zval_ptr_dtor(&args[0]);
1359 zval_ptr_dtor(&args[1]);
1360 zval_ptr_dtor(&args[2]);
1361
1362 return ret;
1363 }
1364
1365
user_wrapper_stat_url(php_stream_wrapper * wrapper,const char * url,int flags,php_stream_statbuf * ssb,php_stream_context * context)1366 static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url, int flags,
1367 php_stream_statbuf *ssb, php_stream_context *context)
1368 {
1369 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1370 zval zfuncname, zretval;
1371 zval args[2];
1372 int call_result;
1373 zval object;
1374 int ret = -1;
1375
1376 /* create an instance of our class */
1377 user_stream_create_object(uwrap, context, &object);
1378 if (Z_TYPE(object) == IS_UNDEF) {
1379 return ret;
1380 }
1381
1382 /* call it's stat_url method - set up params first */
1383 ZVAL_STRING(&args[0], url);
1384 ZVAL_LONG(&args[1], flags);
1385
1386 ZVAL_STRING(&zfuncname, USERSTREAM_STATURL);
1387
1388 call_result = call_user_function_ex(NULL,
1389 &object,
1390 &zfuncname,
1391 &zretval,
1392 2, args,
1393 0, NULL );
1394
1395 if (call_result == SUCCESS && Z_TYPE(zretval) == IS_ARRAY) {
1396 /* We got the info we needed */
1397 if (SUCCESS == statbuf_from_array(&zretval, ssb))
1398 ret = 0;
1399 } else {
1400 if (call_result == FAILURE) {
1401 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STATURL " is not implemented!",
1402 uwrap->classname);
1403 }
1404 }
1405
1406 /* clean up */
1407 zval_ptr_dtor(&object);
1408 zval_ptr_dtor(&zretval);
1409
1410 zval_ptr_dtor(&zfuncname);
1411 zval_ptr_dtor(&args[1]);
1412 zval_ptr_dtor(&args[0]);
1413
1414 return ret;
1415
1416 }
1417
php_userstreamop_readdir(php_stream * stream,char * buf,size_t count)1418 static ssize_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t count)
1419 {
1420 zval func_name;
1421 zval retval;
1422 int call_result;
1423 size_t didread = 0;
1424 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1425 php_stream_dirent *ent = (php_stream_dirent*)buf;
1426
1427 /* avoid problems if someone mis-uses the stream */
1428 if (count != sizeof(php_stream_dirent))
1429 return -1;
1430
1431 ZVAL_STRINGL(&func_name, USERSTREAM_DIR_READ, sizeof(USERSTREAM_DIR_READ)-1);
1432
1433 call_result = call_user_function(NULL,
1434 Z_ISUNDEF(us->object)? NULL : &us->object,
1435 &func_name,
1436 &retval,
1437 0, NULL);
1438
1439 if (call_result == SUCCESS && Z_TYPE(retval) != IS_FALSE && Z_TYPE(retval) != IS_TRUE) {
1440 convert_to_string(&retval);
1441 PHP_STRLCPY(ent->d_name, Z_STRVAL(retval), sizeof(ent->d_name), Z_STRLEN(retval));
1442
1443 didread = sizeof(php_stream_dirent);
1444 } else if (call_result == FAILURE) {
1445 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_DIR_READ " is not implemented!",
1446 us->wrapper->classname);
1447 }
1448
1449 zval_ptr_dtor(&retval);
1450 zval_ptr_dtor(&func_name);
1451
1452 return didread;
1453 }
1454
php_userstreamop_closedir(php_stream * stream,int close_handle)1455 static int php_userstreamop_closedir(php_stream *stream, int close_handle)
1456 {
1457 zval func_name;
1458 zval retval;
1459 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1460
1461 assert(us != NULL);
1462
1463 ZVAL_STRINGL(&func_name, USERSTREAM_DIR_CLOSE, sizeof(USERSTREAM_DIR_CLOSE)-1);
1464
1465 call_user_function(NULL,
1466 Z_ISUNDEF(us->object)? NULL : &us->object,
1467 &func_name,
1468 &retval,
1469 0, NULL);
1470
1471 zval_ptr_dtor(&retval);
1472 zval_ptr_dtor(&func_name);
1473 zval_ptr_dtor(&us->object);
1474 ZVAL_UNDEF(&us->object);
1475
1476 efree(us);
1477
1478 return 0;
1479 }
1480
php_userstreamop_rewinddir(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffs)1481 static int php_userstreamop_rewinddir(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs)
1482 {
1483 zval func_name;
1484 zval retval;
1485 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1486
1487 ZVAL_STRINGL(&func_name, USERSTREAM_DIR_REWIND, sizeof(USERSTREAM_DIR_REWIND)-1);
1488
1489 call_user_function(NULL,
1490 Z_ISUNDEF(us->object)? NULL : &us->object,
1491 &func_name,
1492 &retval,
1493 0, NULL);
1494
1495 zval_ptr_dtor(&retval);
1496 zval_ptr_dtor(&func_name);
1497
1498 return 0;
1499
1500 }
1501
php_userstreamop_cast(php_stream * stream,int castas,void ** retptr)1502 static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr)
1503 {
1504 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1505 zval func_name;
1506 zval retval;
1507 zval args[1];
1508 php_stream * intstream = NULL;
1509 int call_result;
1510 int ret = FAILURE;
1511
1512 ZVAL_STRINGL(&func_name, USERSTREAM_CAST, sizeof(USERSTREAM_CAST)-1);
1513
1514 switch(castas) {
1515 case PHP_STREAM_AS_FD_FOR_SELECT:
1516 ZVAL_LONG(&args[0], PHP_STREAM_AS_FD_FOR_SELECT);
1517 break;
1518 default:
1519 ZVAL_LONG(&args[0], PHP_STREAM_AS_STDIO);
1520 break;
1521 }
1522
1523 call_result = call_user_function_ex(NULL,
1524 Z_ISUNDEF(us->object)? NULL : &us->object,
1525 &func_name,
1526 &retval,
1527 1, args, 0, NULL);
1528
1529 do {
1530 if (call_result == FAILURE) {
1531 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " is not implemented!",
1532 us->wrapper->classname);
1533 break;
1534 }
1535 if (!zend_is_true(&retval)) {
1536 break;
1537 }
1538 php_stream_from_zval_no_verify(intstream, &retval);
1539 if (!intstream) {
1540 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " must return a stream resource",
1541 us->wrapper->classname);
1542 break;
1543 }
1544 if (intstream == stream) {
1545 php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " must not return itself",
1546 us->wrapper->classname);
1547 intstream = NULL;
1548 break;
1549 }
1550 ret = php_stream_cast(intstream, castas, retptr, 1);
1551 } while (0);
1552
1553 zval_ptr_dtor(&retval);
1554 zval_ptr_dtor(&func_name);
1555 zval_ptr_dtor(&args[0]);
1556
1557 return ret;
1558 }
1559
1560 const php_stream_ops php_stream_userspace_ops = {
1561 php_userstreamop_write, php_userstreamop_read,
1562 php_userstreamop_close, php_userstreamop_flush,
1563 "user-space",
1564 php_userstreamop_seek,
1565 php_userstreamop_cast,
1566 php_userstreamop_stat,
1567 php_userstreamop_set_option,
1568 };
1569
1570 const php_stream_ops php_stream_userspace_dir_ops = {
1571 NULL, /* write */
1572 php_userstreamop_readdir,
1573 php_userstreamop_closedir,
1574 NULL, /* flush */
1575 "user-space-dir",
1576 php_userstreamop_rewinddir,
1577 NULL, /* cast */
1578 NULL, /* stat */
1579 NULL /* set_option */
1580 };
1581