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