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