1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2014 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, char *filename, 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, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC);
50 static int user_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC);
51 static int user_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC);
52 static int user_wrapper_mkdir(php_stream_wrapper *wrapper, char *url, int mode, int options, php_stream_context *context TSRMLS_DC);
53 static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC);
54 static int user_wrapper_metadata(php_stream_wrapper *wrapper, char *url, int option, void *value, php_stream_context *context TSRMLS_DC);
55 static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filename, 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,char * filename,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, char *filename, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
336 {
337 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
338 php_userstream_data_t *us;
339 zval *zfilename, *zmode, *zopened, *zoptions, *zretval = NULL, *zfuncname;
340 zval **args[4];
341 int call_result;
342 php_stream *stream = NULL;
343 zend_bool old_in_user_include;
344
345 /* Try to catch bad usage without preventing flexibility */
346 if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) {
347 php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "infinite recursion prevented");
348 return NULL;
349 }
350 FG(user_stream_current_filename) = filename;
351
352 /* if the user stream was registered as local and we are in include context,
353 we add allow_url_include restrictions to allow_url_fopen ones */
354 /* we need only is_url == 0 here since if is_url == 1 and remote wrappers
355 were restricted we wouldn't get here */
356 old_in_user_include = PG(in_user_include);
357 if(uwrap->wrapper.is_url == 0 &&
358 (options & STREAM_OPEN_FOR_INCLUDE) &&
359 !PG(allow_url_include)) {
360 PG(in_user_include) = 1;
361 }
362
363 us = emalloc(sizeof(*us));
364 us->wrapper = uwrap;
365
366 us->object = user_stream_create_object(uwrap, context TSRMLS_CC);
367 if(us->object == NULL) {
368 FG(user_stream_current_filename) = NULL;
369 PG(in_user_include) = old_in_user_include;
370 efree(us);
371 return NULL;
372 }
373
374 /* call it's stream_open method - set up params first */
375 MAKE_STD_ZVAL(zfilename);
376 ZVAL_STRING(zfilename, filename, 1);
377 args[0] = &zfilename;
378
379 MAKE_STD_ZVAL(zmode);
380 ZVAL_STRING(zmode, mode, 1);
381 args[1] = &zmode;
382
383 MAKE_STD_ZVAL(zoptions);
384 ZVAL_LONG(zoptions, options);
385 args[2] = &zoptions;
386
387 MAKE_STD_ZVAL(zopened);
388 Z_SET_REFCOUNT_P(zopened, 1);
389 Z_SET_ISREF_P(zopened);
390 ZVAL_NULL(zopened);
391 args[3] = &zopened;
392
393 MAKE_STD_ZVAL(zfuncname);
394 ZVAL_STRING(zfuncname, USERSTREAM_OPEN, 1);
395
396 call_result = call_user_function_ex(NULL,
397 &us->object,
398 zfuncname,
399 &zretval,
400 4, args,
401 0, NULL TSRMLS_CC);
402
403 if (call_result == SUCCESS && zretval != NULL && zval_is_true(zretval)) {
404 /* the stream is now open! */
405 stream = php_stream_alloc_rel(&php_stream_userspace_ops, us, 0, mode);
406
407 /* if the opened path is set, copy it out */
408 if (Z_TYPE_P(zopened) == IS_STRING && opened_path) {
409 *opened_path = estrndup(Z_STRVAL_P(zopened), Z_STRLEN_P(zopened));
410 }
411
412 /* set wrapper data to be a reference to our object */
413 stream->wrapperdata = us->object;
414 zval_add_ref(&stream->wrapperdata);
415 } else {
416 php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "\"%s::" USERSTREAM_OPEN "\" call failed",
417 us->wrapper->classname);
418 }
419
420 /* destroy everything else */
421 if (stream == NULL) {
422 zval_ptr_dtor(&us->object);
423 efree(us);
424 }
425 if (zretval)
426 zval_ptr_dtor(&zretval);
427
428 zval_ptr_dtor(&zfuncname);
429 zval_ptr_dtor(&zopened);
430 zval_ptr_dtor(&zoptions);
431 zval_ptr_dtor(&zmode);
432 zval_ptr_dtor(&zfilename);
433
434 FG(user_stream_current_filename) = NULL;
435
436 PG(in_user_include) = old_in_user_include;
437 return stream;
438 }
439
user_wrapper_opendir(php_stream_wrapper * wrapper,char * filename,char * mode,int options,char ** opened_path,php_stream_context * context STREAMS_DC TSRMLS_DC)440 static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filename, char *mode,
441 int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
442 {
443 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
444 php_userstream_data_t *us;
445 zval *zfilename, *zoptions, *zretval = NULL, *zfuncname;
446 zval **args[2];
447 int call_result;
448 php_stream *stream = NULL;
449
450 /* Try to catch bad usage without preventing flexibility */
451 if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) {
452 php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "infinite recursion prevented");
453 return NULL;
454 }
455 FG(user_stream_current_filename) = filename;
456
457 us = emalloc(sizeof(*us));
458 us->wrapper = uwrap;
459
460 us->object = user_stream_create_object(uwrap, context TSRMLS_CC);
461 if(us->object == NULL) {
462 FG(user_stream_current_filename) = NULL;
463 efree(us);
464 return NULL;
465 }
466
467 /* call it's dir_open method - set up params first */
468 MAKE_STD_ZVAL(zfilename);
469 ZVAL_STRING(zfilename, filename, 1);
470 args[0] = &zfilename;
471
472 MAKE_STD_ZVAL(zoptions);
473 ZVAL_LONG(zoptions, options);
474 args[1] = &zoptions;
475
476 MAKE_STD_ZVAL(zfuncname);
477 ZVAL_STRING(zfuncname, USERSTREAM_DIR_OPEN, 1);
478
479 call_result = call_user_function_ex(NULL,
480 &us->object,
481 zfuncname,
482 &zretval,
483 2, args,
484 0, NULL TSRMLS_CC);
485
486 if (call_result == SUCCESS && zretval != NULL && zval_is_true(zretval)) {
487 /* the stream is now open! */
488 stream = php_stream_alloc_rel(&php_stream_userspace_dir_ops, us, 0, mode);
489
490 /* set wrapper data to be a reference to our object */
491 stream->wrapperdata = us->object;
492 zval_add_ref(&stream->wrapperdata);
493 } else {
494 php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "\"%s::" USERSTREAM_DIR_OPEN "\" call failed",
495 us->wrapper->classname);
496 }
497
498 /* destroy everything else */
499 if (stream == NULL) {
500 zval_ptr_dtor(&us->object);
501 efree(us);
502 }
503 if (zretval)
504 zval_ptr_dtor(&zretval);
505
506 zval_ptr_dtor(&zfuncname);
507 zval_ptr_dtor(&zoptions);
508 zval_ptr_dtor(&zfilename);
509
510 FG(user_stream_current_filename) = NULL;
511
512 return stream;
513 }
514
515
516 /* {{{ proto bool stream_wrapper_register(string protocol, string classname[, integer flags])
517 Registers a custom URL protocol handler class */
PHP_FUNCTION(stream_wrapper_register)518 PHP_FUNCTION(stream_wrapper_register)
519 {
520 char *protocol, *classname;
521 int protocol_len, classname_len;
522 struct php_user_stream_wrapper * uwrap;
523 int rsrc_id;
524 long flags = 0;
525
526 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &protocol, &protocol_len, &classname, &classname_len, &flags) == FAILURE) {
527 RETURN_FALSE;
528 }
529
530 uwrap = (struct php_user_stream_wrapper *)ecalloc(1, sizeof(*uwrap));
531 uwrap->protoname = estrndup(protocol, protocol_len);
532 uwrap->classname = estrndup(classname, classname_len);
533 uwrap->wrapper.wops = &user_stream_wops;
534 uwrap->wrapper.abstract = uwrap;
535 uwrap->wrapper.is_url = ((flags & PHP_STREAM_IS_URL) != 0);
536
537 rsrc_id = ZEND_REGISTER_RESOURCE(NULL, uwrap, le_protocols);
538
539 if (zend_lookup_class(uwrap->classname, classname_len, (zend_class_entry***)&uwrap->ce TSRMLS_CC) == SUCCESS) {
540 uwrap->ce = *(zend_class_entry**)uwrap->ce;
541 if (php_register_url_stream_wrapper_volatile(protocol, &uwrap->wrapper TSRMLS_CC) == SUCCESS) {
542 RETURN_TRUE;
543 } else {
544 /* We failed. But why? */
545 if (zend_hash_exists(php_stream_get_url_stream_wrappers_hash(), protocol, protocol_len + 1)) {
546 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol %s:// is already defined.", protocol);
547 } else {
548 /* Hash doesn't exist so it must have been an invalid protocol scheme */
549 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid protocol scheme specified. Unable to register wrapper class %s to %s://", classname, protocol);
550 }
551 }
552 } else {
553 php_error_docref(NULL TSRMLS_CC, E_WARNING, "class '%s' is undefined", classname);
554 }
555
556 zend_list_delete(rsrc_id);
557 RETURN_FALSE;
558 }
559 /* }}} */
560
561 /* {{{ proto bool stream_wrapper_unregister(string protocol)
562 Unregister a wrapper for the life of the current request. */
PHP_FUNCTION(stream_wrapper_unregister)563 PHP_FUNCTION(stream_wrapper_unregister)
564 {
565 char *protocol;
566 int protocol_len;
567
568 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &protocol, &protocol_len) == FAILURE) {
569 RETURN_FALSE;
570 }
571
572 if (php_unregister_url_stream_wrapper_volatile(protocol TSRMLS_CC) == FAILURE) {
573 /* We failed */
574 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to unregister protocol %s://", protocol);
575 RETURN_FALSE;
576 }
577
578 RETURN_TRUE;
579 }
580 /* }}} */
581
582 /* {{{ proto bool stream_wrapper_restore(string protocol)
583 Restore the original protocol handler, overriding if necessary */
PHP_FUNCTION(stream_wrapper_restore)584 PHP_FUNCTION(stream_wrapper_restore)
585 {
586 char *protocol;
587 int protocol_len;
588 php_stream_wrapper **wrapperpp = NULL, *wrapper;
589 HashTable *global_wrapper_hash;
590
591 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &protocol, &protocol_len) == FAILURE) {
592 RETURN_FALSE;
593 }
594
595 global_wrapper_hash = php_stream_get_url_stream_wrappers_hash_global();
596 if (php_stream_get_url_stream_wrappers_hash() == global_wrapper_hash) {
597 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "%s:// was never changed, nothing to restore", protocol);
598 RETURN_TRUE;
599 }
600
601 if ((zend_hash_find(global_wrapper_hash, protocol, protocol_len + 1, (void**)&wrapperpp) == FAILURE) || !wrapperpp) {
602 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s:// never existed, nothing to restore", protocol);
603 RETURN_FALSE;
604 }
605
606 /* next line might delete the pointer that wrapperpp points at, so deref it now */
607 wrapper = *wrapperpp;
608
609 /* A failure here could be okay given that the protocol might have been merely unregistered */
610 php_unregister_url_stream_wrapper_volatile(protocol TSRMLS_CC);
611
612 if (php_register_url_stream_wrapper_volatile(protocol, wrapper TSRMLS_CC) == FAILURE) {
613 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to restore original %s:// wrapper", protocol);
614 RETURN_FALSE;
615 }
616
617 RETURN_TRUE;
618 }
619 /* }}} */
620
php_userstreamop_write(php_stream * stream,const char * buf,size_t count TSRMLS_DC)621 static size_t php_userstreamop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
622 {
623 zval func_name;
624 zval *retval = NULL;
625 int call_result;
626 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
627 zval **args[1];
628 zval *zbufptr;
629 size_t didwrite = 0;
630
631 assert(us != NULL);
632
633 ZVAL_STRINGL(&func_name, USERSTREAM_WRITE, sizeof(USERSTREAM_WRITE)-1, 0);
634
635 MAKE_STD_ZVAL(zbufptr);
636 ZVAL_STRINGL(zbufptr, (char*)buf, count, 1);;
637 args[0] = &zbufptr;
638
639 call_result = call_user_function_ex(NULL,
640 &us->object,
641 &func_name,
642 &retval,
643 1, args,
644 0, NULL TSRMLS_CC);
645 zval_ptr_dtor(&zbufptr);
646
647 didwrite = 0;
648 if (call_result == SUCCESS && retval != NULL) {
649 convert_to_long(retval);
650 didwrite = Z_LVAL_P(retval);
651 } else if (call_result == FAILURE) {
652 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " is not implemented!",
653 us->wrapper->classname);
654 }
655
656 /* don't allow strange buffer overruns due to bogus return */
657 if (didwrite > count) {
658 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " wrote %ld bytes more data than requested (%ld written, %ld max)",
659 us->wrapper->classname,
660 (long)(didwrite - count), (long)didwrite, (long)count);
661 didwrite = count;
662 }
663
664 if (retval)
665 zval_ptr_dtor(&retval);
666
667 return didwrite;
668 }
669
php_userstreamop_read(php_stream * stream,char * buf,size_t count TSRMLS_DC)670 static size_t php_userstreamop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
671 {
672 zval func_name;
673 zval *retval = NULL;
674 zval **args[1];
675 int call_result;
676 size_t didread = 0;
677 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
678 zval *zcount;
679
680 assert(us != NULL);
681
682 ZVAL_STRINGL(&func_name, USERSTREAM_READ, sizeof(USERSTREAM_READ)-1, 0);
683
684 MAKE_STD_ZVAL(zcount);
685 ZVAL_LONG(zcount, count);
686 args[0] = &zcount;
687
688 call_result = call_user_function_ex(NULL,
689 &us->object,
690 &func_name,
691 &retval,
692 1, args,
693 0, NULL TSRMLS_CC);
694
695 if (call_result == SUCCESS && retval != NULL) {
696 convert_to_string(retval);
697 didread = Z_STRLEN_P(retval);
698 if (didread > count) {
699 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",
700 us->wrapper->classname, (long)(didread - count), (long)didread, (long)count);
701 didread = count;
702 }
703 if (didread > 0)
704 memcpy(buf, Z_STRVAL_P(retval), didread);
705 } else if (call_result == FAILURE) {
706 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_READ " is not implemented!",
707 us->wrapper->classname);
708 }
709 zval_ptr_dtor(&zcount);
710
711 if (retval) {
712 zval_ptr_dtor(&retval);
713 retval = NULL;
714 }
715
716 /* since the user stream has no way of setting the eof flag directly, we need to ask it if we hit eof */
717
718 ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1, 0);
719
720 call_result = call_user_function_ex(NULL,
721 &us->object,
722 &func_name,
723 &retval,
724 0, NULL, 0, NULL TSRMLS_CC);
725
726 if (call_result == SUCCESS && retval != NULL && zval_is_true(retval)) {
727 stream->eof = 1;
728 } else if (call_result == FAILURE) {
729 php_error_docref(NULL TSRMLS_CC, E_WARNING,
730 "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
731 us->wrapper->classname);
732
733 stream->eof = 1;
734 }
735
736 if (retval) {
737 zval_ptr_dtor(&retval);
738 retval = NULL;
739 }
740
741 return didread;
742 }
743
php_userstreamop_close(php_stream * stream,int close_handle TSRMLS_DC)744 static int php_userstreamop_close(php_stream *stream, int close_handle TSRMLS_DC)
745 {
746 zval func_name;
747 zval *retval = NULL;
748 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
749
750 assert(us != NULL);
751
752 ZVAL_STRINGL(&func_name, USERSTREAM_CLOSE, sizeof(USERSTREAM_CLOSE)-1, 0);
753
754 call_user_function_ex(NULL,
755 &us->object,
756 &func_name,
757 &retval,
758 0, NULL, 0, NULL TSRMLS_CC);
759
760 if (retval)
761 zval_ptr_dtor(&retval);
762
763 zval_ptr_dtor(&us->object);
764
765 efree(us);
766
767 return 0;
768 }
769
php_userstreamop_flush(php_stream * stream TSRMLS_DC)770 static int php_userstreamop_flush(php_stream *stream TSRMLS_DC)
771 {
772 zval func_name;
773 zval *retval = NULL;
774 int call_result;
775 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
776
777 assert(us != NULL);
778
779 ZVAL_STRINGL(&func_name, USERSTREAM_FLUSH, sizeof(USERSTREAM_FLUSH)-1, 0);
780
781 call_result = call_user_function_ex(NULL,
782 &us->object,
783 &func_name,
784 &retval,
785 0, NULL, 0, NULL TSRMLS_CC);
786
787 if (call_result == SUCCESS && retval != NULL && zval_is_true(retval))
788 call_result = 0;
789 else
790 call_result = -1;
791
792 if (retval)
793 zval_ptr_dtor(&retval);
794
795 return call_result;
796 }
797
php_userstreamop_seek(php_stream * stream,off_t offset,int whence,off_t * newoffs TSRMLS_DC)798 static int php_userstreamop_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
799 {
800 zval func_name;
801 zval *retval = NULL;
802 int call_result, ret;
803 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
804 zval **args[2];
805 zval *zoffs, *zwhence;
806
807 assert(us != NULL);
808
809 ZVAL_STRINGL(&func_name, USERSTREAM_SEEK, sizeof(USERSTREAM_SEEK)-1, 0);
810
811 MAKE_STD_ZVAL(zoffs);
812 ZVAL_LONG(zoffs, offset);
813 args[0] = &zoffs;
814
815 MAKE_STD_ZVAL(zwhence);
816 ZVAL_LONG(zwhence, whence);
817 args[1] = &zwhence;
818
819 call_result = call_user_function_ex(NULL,
820 &us->object,
821 &func_name,
822 &retval,
823 2, args,
824 0, NULL TSRMLS_CC);
825
826 zval_ptr_dtor(&zoffs);
827 zval_ptr_dtor(&zwhence);
828
829 if (call_result == FAILURE) {
830 /* stream_seek is not implemented, so disable seeks for this stream */
831 stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
832 /* there should be no retval to clean up */
833
834 if (retval)
835 zval_ptr_dtor(&retval);
836
837 return -1;
838 } else if (call_result == SUCCESS && retval != NULL && zval_is_true(retval)) {
839 ret = 0;
840 } else {
841 ret = -1;
842 }
843
844 if (retval) {
845 zval_ptr_dtor(&retval);
846 retval = NULL;
847 }
848
849 if (ret) {
850 return ret;
851 }
852
853 /* now determine where we are */
854 ZVAL_STRINGL(&func_name, USERSTREAM_TELL, sizeof(USERSTREAM_TELL)-1, 0);
855
856 call_result = call_user_function_ex(NULL,
857 &us->object,
858 &func_name,
859 &retval,
860 0, NULL, 0, NULL TSRMLS_CC);
861
862 if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_LONG) {
863 *newoffs = Z_LVAL_P(retval);
864 ret = 0;
865 } else if (call_result == FAILURE) {
866 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_TELL " is not implemented!", us->wrapper->classname);
867 ret = -1;
868 } else {
869 ret = -1;
870 }
871
872 if (retval) {
873 zval_ptr_dtor(&retval);
874 }
875 return ret;
876 }
877
878 /* parse the return value from one of the stat functions and store the
879 * relevant fields into the statbuf provided */
statbuf_from_array(zval * array,php_stream_statbuf * ssb TSRMLS_DC)880 static int statbuf_from_array(zval *array, php_stream_statbuf *ssb TSRMLS_DC)
881 {
882 zval **elem;
883
884 #define STAT_PROP_ENTRY_EX(name, name2) \
885 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(array), #name, sizeof(#name), (void**)&elem)) { \
886 SEPARATE_ZVAL(elem); \
887 convert_to_long(*elem); \
888 ssb->sb.st_##name2 = Z_LVAL_PP(elem); \
889 }
890
891 #define STAT_PROP_ENTRY(name) STAT_PROP_ENTRY_EX(name,name)
892
893 memset(ssb, 0, sizeof(php_stream_statbuf));
894 STAT_PROP_ENTRY(dev);
895 STAT_PROP_ENTRY(ino);
896 STAT_PROP_ENTRY(mode);
897 STAT_PROP_ENTRY(nlink);
898 STAT_PROP_ENTRY(uid);
899 STAT_PROP_ENTRY(gid);
900 #if HAVE_ST_RDEV
901 STAT_PROP_ENTRY(rdev);
902 #endif
903 STAT_PROP_ENTRY(size);
904 #ifdef NETWARE
905 STAT_PROP_ENTRY_EX(atime, atime.tv_sec);
906 STAT_PROP_ENTRY_EX(mtime, mtime.tv_sec);
907 STAT_PROP_ENTRY_EX(ctime, ctime.tv_sec);
908 #else
909 STAT_PROP_ENTRY(atime);
910 STAT_PROP_ENTRY(mtime);
911 STAT_PROP_ENTRY(ctime);
912 #endif
913 #ifdef HAVE_ST_BLKSIZE
914 STAT_PROP_ENTRY(blksize);
915 #endif
916 #ifdef HAVE_ST_BLOCKS
917 STAT_PROP_ENTRY(blocks);
918 #endif
919
920 #undef STAT_PROP_ENTRY
921 #undef STAT_PROP_ENTRY_EX
922 return SUCCESS;
923 }
924
php_userstreamop_stat(php_stream * stream,php_stream_statbuf * ssb TSRMLS_DC)925 static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
926 {
927 zval func_name;
928 zval *retval = NULL;
929 int call_result;
930 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
931 int ret = -1;
932
933 ZVAL_STRINGL(&func_name, USERSTREAM_STAT, sizeof(USERSTREAM_STAT)-1, 0);
934
935 call_result = call_user_function_ex(NULL,
936 &us->object,
937 &func_name,
938 &retval,
939 0, NULL, 0, NULL TSRMLS_CC);
940
941 if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_ARRAY) {
942 if (SUCCESS == statbuf_from_array(retval, ssb TSRMLS_CC))
943 ret = 0;
944 } else {
945 if (call_result == FAILURE) {
946 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_STAT " is not implemented!",
947 us->wrapper->classname);
948 }
949 }
950
951 if (retval)
952 zval_ptr_dtor(&retval);
953
954 return ret;
955 }
956
957
php_userstreamop_set_option(php_stream * stream,int option,int value,void * ptrparam TSRMLS_DC)958 static int php_userstreamop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) {
959 zval func_name;
960 zval *retval = NULL;
961 int call_result;
962 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
963 int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL;
964 zval *zvalue = NULL;
965 zval **args[3];
966
967 switch (option) {
968 case PHP_STREAM_OPTION_CHECK_LIVENESS:
969 ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1, 0);
970 call_result = call_user_function_ex(NULL, &us->object, &func_name, &retval, 0, NULL, 0, NULL TSRMLS_CC);
971 if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_BOOL) {
972 ret = zval_is_true(retval) ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
973 } else {
974 ret = PHP_STREAM_OPTION_RETURN_ERR;
975 php_error_docref(NULL TSRMLS_CC, E_WARNING,
976 "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
977 us->wrapper->classname);
978 }
979 break;
980
981 case PHP_STREAM_OPTION_LOCKING:
982 MAKE_STD_ZVAL(zvalue);
983 ZVAL_LONG(zvalue, 0);
984
985 if (value & LOCK_NB) {
986 Z_LVAL_P(zvalue) |= PHP_LOCK_NB;
987 }
988 switch(value & ~LOCK_NB) {
989 case LOCK_SH:
990 Z_LVAL_P(zvalue) |= PHP_LOCK_SH;
991 break;
992 case LOCK_EX:
993 Z_LVAL_P(zvalue) |= PHP_LOCK_EX;
994 break;
995 case LOCK_UN:
996 Z_LVAL_P(zvalue) |= PHP_LOCK_UN;
997 break;
998 }
999
1000 args[0] = &zvalue;
1001
1002 /* TODO wouldblock */
1003 ZVAL_STRINGL(&func_name, USERSTREAM_LOCK, sizeof(USERSTREAM_LOCK)-1, 0);
1004
1005 call_result = call_user_function_ex(NULL,
1006 &us->object,
1007 &func_name,
1008 &retval,
1009 1, args, 0, NULL TSRMLS_CC);
1010
1011 if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_BOOL) {
1012 ret = !Z_LVAL_P(retval);
1013 } else if (call_result == FAILURE) {
1014 if (value == 0) {
1015 /* lock support test (TODO: more check) */
1016 ret = PHP_STREAM_OPTION_RETURN_OK;
1017 } else {
1018 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_LOCK " is not implemented!",
1019 us->wrapper->classname);
1020 ret = PHP_STREAM_OPTION_RETURN_ERR;
1021 }
1022 }
1023
1024 break;
1025
1026 case PHP_STREAM_OPTION_TRUNCATE_API:
1027 ZVAL_STRINGL(&func_name, USERSTREAM_TRUNCATE, sizeof(USERSTREAM_TRUNCATE)-1, 0);
1028
1029 switch (value) {
1030 case PHP_STREAM_TRUNCATE_SUPPORTED:
1031 if (zend_is_callable_ex(&func_name, us->object, IS_CALLABLE_CHECK_SILENT,
1032 NULL, NULL, NULL, NULL TSRMLS_CC))
1033 ret = PHP_STREAM_OPTION_RETURN_OK;
1034 else
1035 ret = PHP_STREAM_OPTION_RETURN_ERR;
1036 break;
1037
1038 case PHP_STREAM_TRUNCATE_SET_SIZE: {
1039 ptrdiff_t new_size = *(ptrdiff_t*) ptrparam;
1040 if (new_size >= 0 && new_size <= (ptrdiff_t)LONG_MAX) {
1041 MAKE_STD_ZVAL(zvalue);
1042 ZVAL_LONG(zvalue, (long)new_size);
1043 args[0] = &zvalue;
1044 call_result = call_user_function_ex(NULL,
1045 &us->object,
1046 &func_name,
1047 &retval,
1048 1, args, 0, NULL TSRMLS_CC);
1049 if (call_result == SUCCESS && retval != NULL) {
1050 if (Z_TYPE_P(retval) == IS_BOOL) {
1051 ret = Z_LVAL_P(retval) ? PHP_STREAM_OPTION_RETURN_OK :
1052 PHP_STREAM_OPTION_RETURN_ERR;
1053 } else {
1054 php_error_docref(NULL TSRMLS_CC, E_WARNING,
1055 "%s::" USERSTREAM_TRUNCATE " did not return a boolean!",
1056 us->wrapper->classname);
1057 }
1058 } else {
1059 php_error_docref(NULL TSRMLS_CC, E_WARNING,
1060 "%s::" USERSTREAM_TRUNCATE " is not implemented!",
1061 us->wrapper->classname);
1062 }
1063 } else { /* bad new size */
1064 ret = PHP_STREAM_OPTION_RETURN_ERR;
1065 }
1066 break;
1067 }
1068 }
1069 break;
1070
1071 case PHP_STREAM_OPTION_READ_BUFFER:
1072 case PHP_STREAM_OPTION_WRITE_BUFFER:
1073 case PHP_STREAM_OPTION_READ_TIMEOUT:
1074 case PHP_STREAM_OPTION_BLOCKING: {
1075 zval *zoption = NULL;
1076 zval *zptrparam = NULL;
1077
1078 ZVAL_STRINGL(&func_name, USERSTREAM_SET_OPTION, sizeof(USERSTREAM_SET_OPTION)-1, 0);
1079
1080 ALLOC_INIT_ZVAL(zoption);
1081 ZVAL_LONG(zoption, option);
1082
1083 ALLOC_INIT_ZVAL(zvalue);
1084 ALLOC_INIT_ZVAL(zptrparam);
1085
1086 args[0] = &zoption;
1087 args[1] = &zvalue;
1088 args[2] = &zptrparam;
1089
1090 switch(option) {
1091 case PHP_STREAM_OPTION_READ_BUFFER:
1092 case PHP_STREAM_OPTION_WRITE_BUFFER:
1093 ZVAL_LONG(zvalue, value);
1094 if (ptrparam) {
1095 ZVAL_LONG(zptrparam, *(long *)ptrparam);
1096 } else {
1097 ZVAL_LONG(zptrparam, BUFSIZ);
1098 }
1099 break;
1100 case PHP_STREAM_OPTION_READ_TIMEOUT: {
1101 struct timeval tv = *(struct timeval*)ptrparam;
1102 ZVAL_LONG(zvalue, tv.tv_sec);
1103 ZVAL_LONG(zptrparam, tv.tv_usec);
1104 break;
1105 }
1106 case PHP_STREAM_OPTION_BLOCKING:
1107 ZVAL_LONG(zvalue, value);
1108 break;
1109 default:
1110 break;
1111 }
1112
1113 call_result = call_user_function_ex(NULL,
1114 &us->object,
1115 &func_name,
1116 &retval,
1117 3, args, 0, NULL TSRMLS_CC);
1118
1119 if (call_result == FAILURE) {
1120 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_SET_OPTION " is not implemented!",
1121 us->wrapper->classname);
1122 ret = PHP_STREAM_OPTION_RETURN_ERR;
1123 } else if (retval && zend_is_true(retval)) {
1124 ret = PHP_STREAM_OPTION_RETURN_OK;
1125 } else {
1126 ret = PHP_STREAM_OPTION_RETURN_ERR;
1127 }
1128
1129 if (zoption) {
1130 zval_ptr_dtor(&zoption);
1131 }
1132 if (zptrparam) {
1133 zval_ptr_dtor(&zptrparam);
1134 }
1135
1136 break;
1137 }
1138 }
1139
1140 /* clean up */
1141 if (retval) {
1142 zval_ptr_dtor(&retval);
1143 }
1144
1145
1146 if (zvalue) {
1147 zval_ptr_dtor(&zvalue);
1148 }
1149
1150 return ret;
1151 }
1152
1153
user_wrapper_unlink(php_stream_wrapper * wrapper,char * url,int options,php_stream_context * context TSRMLS_DC)1154 static int user_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC)
1155 {
1156 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1157 zval *zfilename, *zfuncname, *zretval;
1158 zval **args[1];
1159 int call_result;
1160 zval *object;
1161 int ret = 0;
1162
1163 /* create an instance of our class */
1164 object = user_stream_create_object(uwrap, context TSRMLS_CC);
1165 if(object == NULL) {
1166 return ret;
1167 }
1168
1169 /* call the unlink method */
1170 MAKE_STD_ZVAL(zfilename);
1171 ZVAL_STRING(zfilename, url, 1);
1172 args[0] = &zfilename;
1173
1174 MAKE_STD_ZVAL(zfuncname);
1175 ZVAL_STRING(zfuncname, USERSTREAM_UNLINK, 1);
1176
1177 call_result = call_user_function_ex(NULL,
1178 &object,
1179 zfuncname,
1180 &zretval,
1181 1, args,
1182 0, NULL TSRMLS_CC);
1183
1184 if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
1185 ret = Z_LVAL_P(zretval);
1186 } else if (call_result == FAILURE) {
1187 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_UNLINK " is not implemented!", uwrap->classname);
1188 }
1189
1190 /* clean up */
1191 zval_ptr_dtor(&object);
1192 if (zretval)
1193 zval_ptr_dtor(&zretval);
1194
1195 zval_ptr_dtor(&zfuncname);
1196 zval_ptr_dtor(&zfilename);
1197
1198 return ret;
1199 }
1200
user_wrapper_rename(php_stream_wrapper * wrapper,char * url_from,char * url_to,int options,php_stream_context * context TSRMLS_DC)1201 static int user_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC)
1202 {
1203 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1204 zval *zold_name, *znew_name, *zfuncname, *zretval;
1205 zval **args[2];
1206 int call_result;
1207 zval *object;
1208 int ret = 0;
1209
1210 /* create an instance of our class */
1211 object = user_stream_create_object(uwrap, context TSRMLS_CC);
1212 if(object == NULL) {
1213 return ret;
1214 }
1215
1216 /* call the rename method */
1217 MAKE_STD_ZVAL(zold_name);
1218 ZVAL_STRING(zold_name, url_from, 1);
1219 args[0] = &zold_name;
1220
1221 MAKE_STD_ZVAL(znew_name);
1222 ZVAL_STRING(znew_name, url_to, 1);
1223 args[1] = &znew_name;
1224
1225 MAKE_STD_ZVAL(zfuncname);
1226 ZVAL_STRING(zfuncname, USERSTREAM_RENAME, 1);
1227
1228 call_result = call_user_function_ex(NULL,
1229 &object,
1230 zfuncname,
1231 &zretval,
1232 2, args,
1233 0, NULL TSRMLS_CC);
1234
1235 if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
1236 ret = Z_LVAL_P(zretval);
1237 } else if (call_result == FAILURE) {
1238 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_RENAME " is not implemented!", uwrap->classname);
1239 }
1240
1241 /* clean up */
1242 zval_ptr_dtor(&object);
1243 if (zretval)
1244 zval_ptr_dtor(&zretval);
1245
1246 zval_ptr_dtor(&zfuncname);
1247 zval_ptr_dtor(&zold_name);
1248 zval_ptr_dtor(&znew_name);
1249
1250 return ret;
1251 }
1252
user_wrapper_mkdir(php_stream_wrapper * wrapper,char * url,int mode,int options,php_stream_context * context TSRMLS_DC)1253 static int user_wrapper_mkdir(php_stream_wrapper *wrapper, char *url, int mode, int options, php_stream_context *context TSRMLS_DC)
1254 {
1255 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1256 zval *zfilename, *zmode, *zoptions, *zfuncname, *zretval;
1257 zval **args[3];
1258 int call_result;
1259 zval *object;
1260 int ret = 0;
1261
1262 /* create an instance of our class */
1263 object = user_stream_create_object(uwrap, context TSRMLS_CC);
1264 if(object == NULL) {
1265 return ret;
1266 }
1267
1268 /* call the mkdir method */
1269 MAKE_STD_ZVAL(zfilename);
1270 ZVAL_STRING(zfilename, url, 1);
1271 args[0] = &zfilename;
1272
1273 MAKE_STD_ZVAL(zmode);
1274 ZVAL_LONG(zmode, mode);
1275 args[1] = &zmode;
1276
1277 MAKE_STD_ZVAL(zoptions);
1278 ZVAL_LONG(zoptions, options);
1279 args[2] = &zoptions;
1280
1281 MAKE_STD_ZVAL(zfuncname);
1282 ZVAL_STRING(zfuncname, USERSTREAM_MKDIR, 1);
1283
1284 call_result = call_user_function_ex(NULL,
1285 &object,
1286 zfuncname,
1287 &zretval,
1288 3, args,
1289 0, NULL TSRMLS_CC);
1290
1291 if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
1292 ret = Z_LVAL_P(zretval);
1293 } else if (call_result == FAILURE) {
1294 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_MKDIR " is not implemented!", uwrap->classname);
1295 }
1296
1297 /* clean up */
1298 zval_ptr_dtor(&object);
1299 if (zretval) {
1300 zval_ptr_dtor(&zretval);
1301 }
1302
1303 zval_ptr_dtor(&zfuncname);
1304 zval_ptr_dtor(&zfilename);
1305 zval_ptr_dtor(&zmode);
1306 zval_ptr_dtor(&zoptions);
1307
1308 return ret;
1309 }
1310
user_wrapper_rmdir(php_stream_wrapper * wrapper,char * url,int options,php_stream_context * context TSRMLS_DC)1311 static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC)
1312 {
1313 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1314 zval *zfilename, *zoptions, *zfuncname, *zretval;
1315 zval **args[3];
1316 int call_result;
1317 zval *object;
1318 int ret = 0;
1319
1320 /* create an instance of our class */
1321 object = user_stream_create_object(uwrap, context TSRMLS_CC);
1322 if(object == NULL) {
1323 return ret;
1324 }
1325
1326 /* call the rmdir method */
1327 MAKE_STD_ZVAL(zfilename);
1328 ZVAL_STRING(zfilename, url, 1);
1329 args[0] = &zfilename;
1330
1331 MAKE_STD_ZVAL(zoptions);
1332 ZVAL_LONG(zoptions, options);
1333 args[1] = &zoptions;
1334
1335 MAKE_STD_ZVAL(zfuncname);
1336 ZVAL_STRING(zfuncname, USERSTREAM_RMDIR, 1);
1337
1338 call_result = call_user_function_ex(NULL,
1339 &object,
1340 zfuncname,
1341 &zretval,
1342 2, args,
1343 0, NULL TSRMLS_CC);
1344
1345 if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
1346 ret = Z_LVAL_P(zretval);
1347 } else if (call_result == FAILURE) {
1348 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_RMDIR " is not implemented!", uwrap->classname);
1349 }
1350
1351 /* clean up */
1352 zval_ptr_dtor(&object);
1353 if (zretval) {
1354 zval_ptr_dtor(&zretval);
1355 }
1356
1357 zval_ptr_dtor(&zfuncname);
1358 zval_ptr_dtor(&zfilename);
1359 zval_ptr_dtor(&zoptions);
1360
1361 return ret;
1362 }
1363
user_wrapper_metadata(php_stream_wrapper * wrapper,char * url,int option,void * value,php_stream_context * context TSRMLS_DC)1364 static int user_wrapper_metadata(php_stream_wrapper *wrapper, char *url, int option, void *value, php_stream_context *context TSRMLS_DC)
1365 {
1366 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1367 zval *zfilename, *zoption, *zvalue, *zfuncname, *zretval;
1368 zval **args[3];
1369 int call_result;
1370 zval *object;
1371 int ret = 0;
1372
1373 MAKE_STD_ZVAL(zvalue);
1374 switch(option) {
1375 case PHP_STREAM_META_TOUCH:
1376 array_init(zvalue);
1377 if(value) {
1378 struct utimbuf *newtime = (struct utimbuf *)value;
1379 add_index_long(zvalue, 0, newtime->modtime);
1380 add_index_long(zvalue, 1, newtime->actime);
1381 }
1382 break;
1383 case PHP_STREAM_META_GROUP:
1384 case PHP_STREAM_META_OWNER:
1385 case PHP_STREAM_META_ACCESS:
1386 ZVAL_LONG(zvalue, *(long *)value);
1387 break;
1388 case PHP_STREAM_META_GROUP_NAME:
1389 case PHP_STREAM_META_OWNER_NAME:
1390 ZVAL_STRING(zvalue, value, 1);
1391 break;
1392 default:
1393 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown option %d for " USERSTREAM_METADATA, option);
1394 zval_ptr_dtor(&zvalue);
1395 return ret;
1396 }
1397
1398 /* create an instance of our class */
1399 object = user_stream_create_object(uwrap, context TSRMLS_CC);
1400 if(object == NULL) {
1401 zval_ptr_dtor(&zvalue);
1402 return ret;
1403 }
1404
1405 /* call the mkdir method */
1406 MAKE_STD_ZVAL(zfilename);
1407 ZVAL_STRING(zfilename, url, 1);
1408 args[0] = &zfilename;
1409
1410 MAKE_STD_ZVAL(zoption);
1411 ZVAL_LONG(zoption, option);
1412 args[1] = &zoption;
1413
1414 args[2] = &zvalue;
1415
1416 MAKE_STD_ZVAL(zfuncname);
1417 ZVAL_STRING(zfuncname, USERSTREAM_METADATA, 1);
1418
1419 call_result = call_user_function_ex(NULL,
1420 &object,
1421 zfuncname,
1422 &zretval,
1423 3, args,
1424 0, NULL TSRMLS_CC);
1425
1426 if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
1427 ret = Z_LVAL_P(zretval);
1428 } else if (call_result == FAILURE) {
1429 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_METADATA " is not implemented!", uwrap->classname);
1430 }
1431
1432 /* clean up */
1433 zval_ptr_dtor(&object);
1434 if (zretval) {
1435 zval_ptr_dtor(&zretval);
1436 }
1437
1438 zval_ptr_dtor(&zfuncname);
1439 zval_ptr_dtor(&zfilename);
1440 zval_ptr_dtor(&zoption);
1441 zval_ptr_dtor(&zvalue);
1442
1443 return ret;
1444 }
1445
1446
user_wrapper_stat_url(php_stream_wrapper * wrapper,char * url,int flags,php_stream_statbuf * ssb,php_stream_context * context TSRMLS_DC)1447 static int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC)
1448 {
1449 struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1450 zval *zfilename, *zfuncname, *zretval, *zflags;
1451 zval **args[2];
1452 int call_result;
1453 zval *object;
1454 int ret = -1;
1455
1456 /* create an instance of our class */
1457 object = user_stream_create_object(uwrap, context TSRMLS_CC);
1458 if(object == NULL) {
1459 return ret;
1460 }
1461
1462 /* call it's stat_url method - set up params first */
1463 MAKE_STD_ZVAL(zfilename);
1464 ZVAL_STRING(zfilename, url, 1);
1465 args[0] = &zfilename;
1466
1467 MAKE_STD_ZVAL(zflags);
1468 ZVAL_LONG(zflags, flags);
1469 args[1] = &zflags;
1470
1471 MAKE_STD_ZVAL(zfuncname);
1472 ZVAL_STRING(zfuncname, USERSTREAM_STATURL, 1);
1473
1474 call_result = call_user_function_ex(NULL,
1475 &object,
1476 zfuncname,
1477 &zretval,
1478 2, args,
1479 0, NULL TSRMLS_CC);
1480
1481 if (call_result == SUCCESS && zretval != NULL && Z_TYPE_P(zretval) == IS_ARRAY) {
1482 /* We got the info we needed */
1483 if (SUCCESS == statbuf_from_array(zretval, ssb TSRMLS_CC))
1484 ret = 0;
1485 } else {
1486 if (call_result == FAILURE) {
1487 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_STATURL " is not implemented!",
1488 uwrap->classname);
1489 }
1490 }
1491
1492 /* clean up */
1493 zval_ptr_dtor(&object);
1494 if (zretval)
1495 zval_ptr_dtor(&zretval);
1496
1497 zval_ptr_dtor(&zfuncname);
1498 zval_ptr_dtor(&zfilename);
1499 zval_ptr_dtor(&zflags);
1500
1501 return ret;
1502
1503 }
1504
php_userstreamop_readdir(php_stream * stream,char * buf,size_t count TSRMLS_DC)1505 static size_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t count TSRMLS_DC)
1506 {
1507 zval func_name;
1508 zval *retval = NULL;
1509 int call_result;
1510 size_t didread = 0;
1511 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1512 php_stream_dirent *ent = (php_stream_dirent*)buf;
1513
1514 /* avoid problems if someone mis-uses the stream */
1515 if (count != sizeof(php_stream_dirent))
1516 return 0;
1517
1518 ZVAL_STRINGL(&func_name, USERSTREAM_DIR_READ, sizeof(USERSTREAM_DIR_READ)-1, 0);
1519
1520 call_result = call_user_function_ex(NULL,
1521 &us->object,
1522 &func_name,
1523 &retval,
1524 0, NULL,
1525 0, NULL TSRMLS_CC);
1526
1527 if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) != IS_BOOL) {
1528 convert_to_string(retval);
1529 PHP_STRLCPY(ent->d_name, Z_STRVAL_P(retval), sizeof(ent->d_name), Z_STRLEN_P(retval));
1530
1531 didread = sizeof(php_stream_dirent);
1532 } else if (call_result == FAILURE) {
1533 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_DIR_READ " is not implemented!",
1534 us->wrapper->classname);
1535 }
1536
1537 if (retval)
1538 zval_ptr_dtor(&retval);
1539
1540 return didread;
1541 }
1542
php_userstreamop_closedir(php_stream * stream,int close_handle TSRMLS_DC)1543 static int php_userstreamop_closedir(php_stream *stream, int close_handle TSRMLS_DC)
1544 {
1545 zval func_name;
1546 zval *retval = NULL;
1547 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1548
1549 assert(us != NULL);
1550
1551 ZVAL_STRINGL(&func_name, USERSTREAM_DIR_CLOSE, sizeof(USERSTREAM_DIR_CLOSE)-1, 0);
1552
1553 call_user_function_ex(NULL,
1554 &us->object,
1555 &func_name,
1556 &retval,
1557 0, NULL, 0, NULL TSRMLS_CC);
1558
1559 if (retval)
1560 zval_ptr_dtor(&retval);
1561
1562 zval_ptr_dtor(&us->object);
1563
1564 efree(us);
1565
1566 return 0;
1567 }
1568
php_userstreamop_rewinddir(php_stream * stream,off_t offset,int whence,off_t * newoffs TSRMLS_DC)1569 static int php_userstreamop_rewinddir(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
1570 {
1571 zval func_name;
1572 zval *retval = NULL;
1573 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1574
1575 ZVAL_STRINGL(&func_name, USERSTREAM_DIR_REWIND, sizeof(USERSTREAM_DIR_REWIND)-1, 0);
1576
1577 call_user_function_ex(NULL,
1578 &us->object,
1579 &func_name,
1580 &retval,
1581 0, NULL, 0, NULL TSRMLS_CC);
1582
1583 if (retval)
1584 zval_ptr_dtor(&retval);
1585
1586 return 0;
1587
1588 }
1589
php_userstreamop_cast(php_stream * stream,int castas,void ** retptr TSRMLS_DC)1590 static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr TSRMLS_DC)
1591 {
1592 php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1593 zval func_name;
1594 zval *retval = NULL;
1595 zval *zcastas = NULL;
1596 zval **args[1];
1597 php_stream * intstream = NULL;
1598 int call_result;
1599 int ret = FAILURE;
1600
1601 ZVAL_STRINGL(&func_name, USERSTREAM_CAST, sizeof(USERSTREAM_CAST)-1, 0);
1602
1603 ALLOC_INIT_ZVAL(zcastas);
1604 switch(castas) {
1605 case PHP_STREAM_AS_FD_FOR_SELECT:
1606 ZVAL_LONG(zcastas, PHP_STREAM_AS_FD_FOR_SELECT);
1607 break;
1608 default:
1609 ZVAL_LONG(zcastas, PHP_STREAM_AS_STDIO);
1610 break;
1611 }
1612 args[0] = &zcastas;
1613
1614 call_result = call_user_function_ex(NULL,
1615 &us->object,
1616 &func_name,
1617 &retval,
1618 1, args, 0, NULL TSRMLS_CC);
1619
1620 do {
1621 if (call_result == FAILURE) {
1622 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_CAST " is not implemented!",
1623 us->wrapper->classname);
1624 break;
1625 }
1626 if (retval == NULL || !zend_is_true(retval)) {
1627 break;
1628 }
1629 php_stream_from_zval_no_verify(intstream, &retval);
1630 if (!intstream) {
1631 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_CAST " must return a stream resource",
1632 us->wrapper->classname);
1633 break;
1634 }
1635 if (intstream == stream) {
1636 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_CAST " must not return itself",
1637 us->wrapper->classname);
1638 intstream = NULL;
1639 break;
1640 }
1641 ret = php_stream_cast(intstream, castas, retptr, 1);
1642 } while (0);
1643
1644 if (retval) {
1645 zval_ptr_dtor(&retval);
1646 }
1647 if (zcastas) {
1648 zval_ptr_dtor(&zcastas);
1649 }
1650
1651 return ret;
1652 }
1653
1654 php_stream_ops php_stream_userspace_ops = {
1655 php_userstreamop_write, php_userstreamop_read,
1656 php_userstreamop_close, php_userstreamop_flush,
1657 "user-space",
1658 php_userstreamop_seek,
1659 php_userstreamop_cast,
1660 php_userstreamop_stat,
1661 php_userstreamop_set_option,
1662 };
1663
1664 php_stream_ops php_stream_userspace_dir_ops = {
1665 NULL, /* write */
1666 php_userstreamop_readdir,
1667 php_userstreamop_closedir,
1668 NULL, /* flush */
1669 "user-space-dir",
1670 php_userstreamop_rewinddir,
1671 NULL, /* cast */
1672 NULL, /* stat */
1673 NULL /* set_option */
1674 };
1675
1676
1677