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