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