Back to index

php5  5.3.10
userspace.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Authors: Wez Furlong <wez@thebrainroom.com>                          |
00016    |          Sara Golemon <pollita@php.net>                              |
00017    +----------------------------------------------------------------------+
00018 */
00019 
00020 /* $Id: userspace.c 321634 2012-01-01 13:15:04Z felipe $ */
00021 
00022 #include "php.h"
00023 #include "php_globals.h"
00024 #include "ext/standard/file.h"
00025 #include "ext/standard/flock_compat.h"
00026 #ifdef HAVE_SYS_FILE_H
00027 #include <sys/file.h>
00028 #endif
00029 
00030 static int le_protocols;
00031 
00032 struct php_user_stream_wrapper {
00033        char * protoname;
00034        char * classname;
00035        zend_class_entry *ce;
00036        php_stream_wrapper wrapper;
00037 };
00038 
00039 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);
00040 static int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC);
00041 static int user_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC);
00042 static int user_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC);
00043 static int user_wrapper_mkdir(php_stream_wrapper *wrapper, char *url, int mode, int options, php_stream_context *context TSRMLS_DC);
00044 static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC);
00045 static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filename, char *mode,
00046               int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
00047 
00048 static php_stream_wrapper_ops user_stream_wops = {
00049        user_wrapper_opener,
00050        NULL, /* close - the streams themselves know how */
00051        NULL, /* stat - the streams themselves know how */
00052        user_wrapper_stat_url,
00053        user_wrapper_opendir,
00054        "user-space",
00055        user_wrapper_unlink,
00056        user_wrapper_rename,
00057        user_wrapper_mkdir,
00058        user_wrapper_rmdir
00059 };
00060 
00061 
00062 static void stream_wrapper_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
00063 {
00064        struct php_user_stream_wrapper * uwrap = (struct php_user_stream_wrapper*)rsrc->ptr;
00065 
00066        efree(uwrap->protoname);
00067        efree(uwrap->classname);
00068        efree(uwrap);
00069 }
00070 
00071 
00072 PHP_MINIT_FUNCTION(user_streams)
00073 {
00074        le_protocols = zend_register_list_destructors_ex(stream_wrapper_dtor, NULL, "stream factory", 0);
00075        if (le_protocols == FAILURE)
00076               return FAILURE;
00077 
00078        REGISTER_LONG_CONSTANT("STREAM_USE_PATH",                      USE_PATH, CONST_CS|CONST_PERSISTENT);
00079        REGISTER_LONG_CONSTANT("STREAM_IGNORE_URL",             IGNORE_URL, CONST_CS|CONST_PERSISTENT);
00080        REGISTER_LONG_CONSTANT("STREAM_ENFORCE_SAFE_MODE",      ENFORCE_SAFE_MODE, CONST_CS|CONST_PERSISTENT);
00081        REGISTER_LONG_CONSTANT("STREAM_REPORT_ERRORS",          REPORT_ERRORS, CONST_CS|CONST_PERSISTENT);
00082        REGISTER_LONG_CONSTANT("STREAM_MUST_SEEK",                     STREAM_MUST_SEEK, CONST_CS|CONST_PERSISTENT);
00083 
00084        REGISTER_LONG_CONSTANT("STREAM_URL_STAT_LINK",          PHP_STREAM_URL_STAT_LINK,          CONST_CS|CONST_PERSISTENT);
00085        REGISTER_LONG_CONSTANT("STREAM_URL_STAT_QUIET",  PHP_STREAM_URL_STAT_QUIET,         CONST_CS|CONST_PERSISTENT);
00086        REGISTER_LONG_CONSTANT("STREAM_MKDIR_RECURSIVE", PHP_STREAM_MKDIR_RECURSIVE,        CONST_CS|CONST_PERSISTENT);
00087 
00088        REGISTER_LONG_CONSTANT("STREAM_IS_URL",   PHP_STREAM_IS_URL,          CONST_CS|CONST_PERSISTENT);
00089 
00090        REGISTER_LONG_CONSTANT("STREAM_OPTION_BLOCKING", PHP_STREAM_OPTION_BLOCKING,        CONST_CS|CONST_PERSISTENT);
00091        REGISTER_LONG_CONSTANT("STREAM_OPTION_READ_TIMEOUT",    PHP_STREAM_OPTION_READ_TIMEOUT,           CONST_CS|CONST_PERSISTENT);
00092        REGISTER_LONG_CONSTANT("STREAM_OPTION_READ_BUFFER",     PHP_STREAM_OPTION_READ_BUFFER,            CONST_CS|CONST_PERSISTENT);
00093        REGISTER_LONG_CONSTANT("STREAM_OPTION_WRITE_BUFFER",    PHP_STREAM_OPTION_WRITE_BUFFER,           CONST_CS|CONST_PERSISTENT);
00094 
00095        REGISTER_LONG_CONSTANT("STREAM_BUFFER_NONE",            PHP_STREAM_BUFFER_NONE,                   CONST_CS|CONST_PERSISTENT);
00096        REGISTER_LONG_CONSTANT("STREAM_BUFFER_LINE",            PHP_STREAM_BUFFER_LINE,                   CONST_CS|CONST_PERSISTENT);
00097        REGISTER_LONG_CONSTANT("STREAM_BUFFER_FULL",            PHP_STREAM_BUFFER_FULL,                   CONST_CS|CONST_PERSISTENT);
00098 
00099        REGISTER_LONG_CONSTANT("STREAM_CAST_AS_STREAM",         PHP_STREAM_AS_STDIO,               CONST_CS|CONST_PERSISTENT);
00100        REGISTER_LONG_CONSTANT("STREAM_CAST_FOR_SELECT", PHP_STREAM_AS_FD_FOR_SELECT,              CONST_CS|CONST_PERSISTENT);
00101 
00102        return SUCCESS;
00103 }
00104 
00105 struct _php_userstream_data {
00106        struct php_user_stream_wrapper * wrapper;
00107        zval * object;
00108 };
00109 typedef struct _php_userstream_data php_userstream_data_t;
00110 
00111 /* names of methods */
00112 #define USERSTREAM_OPEN            "stream_open"
00113 #define USERSTREAM_CLOSE    "stream_close"
00114 #define USERSTREAM_READ            "stream_read"
00115 #define USERSTREAM_WRITE    "stream_write"
00116 #define USERSTREAM_FLUSH    "stream_flush"
00117 #define USERSTREAM_SEEK            "stream_seek"
00118 #define USERSTREAM_TELL            "stream_tell"
00119 #define USERSTREAM_EOF             "stream_eof"
00120 #define USERSTREAM_STAT            "stream_stat"
00121 #define USERSTREAM_STATURL  "url_stat"
00122 #define USERSTREAM_UNLINK   "unlink"
00123 #define USERSTREAM_RENAME   "rename"
00124 #define USERSTREAM_MKDIR    "mkdir"
00125 #define USERSTREAM_RMDIR    "rmdir"
00126 #define USERSTREAM_DIR_OPEN        "dir_opendir"
00127 #define USERSTREAM_DIR_READ        "dir_readdir"
00128 #define USERSTREAM_DIR_REWIND      "dir_rewinddir"
00129 #define USERSTREAM_DIR_CLOSE       "dir_closedir"
00130 #define USERSTREAM_LOCK     "stream_lock"
00131 #define USERSTREAM_CAST            "stream_cast"
00132 #define USERSTREAM_SET_OPTION      "stream_set_option"
00133 
00134 /* {{{ class should have methods like these:
00135  
00136        function stream_open($path, $mode, $options, &$opened_path)
00137        {
00138               return true/false;
00139        }
00140        
00141        function stream_read($count)
00142        {
00143               return false on error;
00144               else return string;
00145        }
00146        
00147        function stream_write($data)
00148        {
00149               return false on error;
00150               else return count written;
00151        }
00152        
00153        function stream_close()
00154        {
00155        }
00156        
00157        function stream_flush()
00158        {
00159               return true/false;
00160        }
00161        
00162        function stream_seek($offset, $whence)
00163        {
00164               return true/false;
00165        }
00166 
00167        function stream_tell()
00168        {
00169               return (int)$position;
00170        }
00171 
00172        function stream_eof()
00173        {
00174               return true/false;
00175        }
00176 
00177        function stream_stat()
00178        {
00179               return array( just like that returned by fstat() );
00180        }
00181 
00182        function stream_cast($castas)
00183        {
00184               if ($castas == STREAM_CAST_FOR_SELECT) {
00185                      return $this->underlying_stream;
00186               }
00187               return false;
00188        }
00189 
00190        function stream_set_option($option, $arg1, $arg2)
00191        {
00192               switch($option) {
00193               case STREAM_OPTION_BLOCKING:
00194                      $blocking = $arg1;
00195                      ...
00196               case STREAM_OPTION_READ_TIMEOUT:
00197                      $sec = $arg1;
00198                      $usec = $arg2;
00199                      ...
00200               case STREAM_OPTION_WRITE_BUFFER:
00201                      $mode = $arg1;
00202                      $size = $arg2;
00203                      ...
00204               default:
00205                      return false;
00206               }
00207        }
00208 
00209        function url_stat(string $url, int $flags)
00210        {
00211               return array( just like that returned by stat() );
00212        }
00213 
00214        function unlink(string $url)
00215        {
00216               return true / false;
00217        }
00218 
00219        function rename(string $from, string $to)
00220        {
00221               return true / false;
00222        }
00223 
00224        function mkdir($dir, $mode, $options)
00225        {
00226               return true / false;
00227        }
00228 
00229        function rmdir($dir, $options)
00230        {
00231               return true / false;
00232        }
00233 
00234        function dir_opendir(string $url, int $options)
00235        {
00236               return true / false;
00237        }
00238 
00239        function dir_readdir()
00240        {
00241               return string next filename in dir ;
00242        }
00243 
00244        function dir_closedir()
00245        {
00246               release dir related resources;
00247        }
00248 
00249        function dir_rewinddir()
00250        {
00251               reset to start of dir list;
00252        }
00253 
00254        function stream_lock($operation)
00255        {
00256               return true / false;
00257        }
00258   
00259        }}} **/
00260 
00261 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)
00262 {
00263        struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
00264        php_userstream_data_t *us;
00265        zval *zfilename, *zmode, *zopened, *zoptions, *zretval = NULL, *zfuncname;
00266        zval **args[4];      
00267        int call_result;
00268        php_stream *stream = NULL;
00269        zend_bool old_in_user_include;
00270 
00271        /* Try to catch bad usage without preventing flexibility */
00272        if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) {
00273               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "infinite recursion prevented");
00274               return NULL;
00275        }
00276        FG(user_stream_current_filename) = filename;
00277        
00278        /* if the user stream was registered as local and we are in include context,
00279               we add allow_url_include restrictions to allow_url_fopen ones */
00280        /* we need only is_url == 0 here since if is_url == 1 and remote wrappers
00281               were restricted we wouldn't get here */
00282        old_in_user_include = PG(in_user_include);
00283        if(uwrap->wrapper.is_url == 0 && 
00284               (options & STREAM_OPEN_FOR_INCLUDE) &&
00285               !PG(allow_url_include)) {
00286               PG(in_user_include) = 1;
00287        }
00288 
00289        us = emalloc(sizeof(*us));
00290        us->wrapper = uwrap; 
00291 
00292        /* create an instance of our class */
00293        ALLOC_ZVAL(us->object);
00294        object_init_ex(us->object, uwrap->ce);
00295        Z_SET_REFCOUNT_P(us->object, 1);
00296        Z_SET_ISREF_P(us->object);
00297        
00298        if (uwrap->ce->constructor) {
00299               zend_fcall_info fci;
00300               zend_fcall_info_cache fcc;
00301               zval *retval_ptr;
00302               
00303               fci.size = sizeof(fci);
00304               fci.function_table = &uwrap->ce->function_table;
00305               fci.function_name = NULL;
00306               fci.symbol_table = NULL;
00307               fci.object_ptr = us->object;
00308               fci.retval_ptr_ptr = &retval_ptr;
00309               fci.param_count = 0;
00310               fci.params = NULL;
00311               fci.no_separation = 1;
00312               
00313               fcc.initialized = 1;
00314               fcc.function_handler = uwrap->ce->constructor;
00315               fcc.calling_scope = EG(scope);
00316               fcc.called_scope = Z_OBJCE_P(us->object);
00317               fcc.object_ptr = us->object;
00318 
00319               if (zend_call_function(&fci, &fcc TSRMLS_CC) == FAILURE) {
00320                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not execute %s::%s()", uwrap->ce->name, uwrap->ce->constructor->common.function_name);
00321                      zval_dtor(us->object);
00322                      FREE_ZVAL(us->object);
00323                      efree(us);
00324                      FG(user_stream_current_filename) = NULL;
00325                      PG(in_user_include) = old_in_user_include;
00326                      return NULL;
00327               } else {
00328                      if (retval_ptr) {
00329                             zval_ptr_dtor(&retval_ptr);
00330                      }
00331               }
00332        }
00333 
00334        if (context) {
00335               add_property_resource(us->object, "context", context->rsrc_id);
00336               zend_list_addref(context->rsrc_id);
00337        } else {
00338               add_property_null(us->object, "context");
00339        }
00340        
00341        /* call it's stream_open method - set up params first */
00342        MAKE_STD_ZVAL(zfilename);
00343        ZVAL_STRING(zfilename, filename, 1);
00344        args[0] = &zfilename;
00345 
00346        MAKE_STD_ZVAL(zmode);
00347        ZVAL_STRING(zmode, mode, 1);
00348        args[1] = &zmode;
00349 
00350        MAKE_STD_ZVAL(zoptions);
00351        ZVAL_LONG(zoptions, options);
00352        args[2] = &zoptions;
00353 
00354        MAKE_STD_ZVAL(zopened);
00355        Z_SET_REFCOUNT_P(zopened, 1);
00356        Z_SET_ISREF_P(zopened);
00357        ZVAL_NULL(zopened);
00358        args[3] = &zopened;
00359 
00360        MAKE_STD_ZVAL(zfuncname);
00361        ZVAL_STRING(zfuncname, USERSTREAM_OPEN, 1);
00362        
00363        call_result = call_user_function_ex(NULL,
00364                      &us->object,
00365                      zfuncname,
00366                      &zretval,
00367                      4, args,
00368                      0, NULL       TSRMLS_CC);
00369        
00370        if (call_result == SUCCESS && zretval != NULL && zval_is_true(zretval)) {
00371               /* the stream is now open! */
00372               stream = php_stream_alloc_rel(&php_stream_userspace_ops, us, 0, mode);
00373 
00374               /* if the opened path is set, copy it out */
00375               if (Z_TYPE_P(zopened) == IS_STRING && opened_path) {
00376                      *opened_path = estrndup(Z_STRVAL_P(zopened), Z_STRLEN_P(zopened));
00377               }
00378 
00379               /* set wrapper data to be a reference to our object */
00380               stream->wrapperdata = us->object;
00381               zval_add_ref(&stream->wrapperdata);
00382        } else {
00383               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "\"%s::" USERSTREAM_OPEN "\" call failed",
00384                      us->wrapper->classname);
00385        }
00386        
00387        /* destroy everything else */
00388        if (stream == NULL) {
00389               zval_ptr_dtor(&us->object);
00390               efree(us);
00391        }
00392        if (zretval)
00393               zval_ptr_dtor(&zretval);
00394        
00395        zval_ptr_dtor(&zfuncname);
00396        zval_ptr_dtor(&zopened);
00397        zval_ptr_dtor(&zoptions);
00398        zval_ptr_dtor(&zmode);
00399        zval_ptr_dtor(&zfilename);
00400 
00401        FG(user_stream_current_filename) = NULL;
00402               
00403        PG(in_user_include) = old_in_user_include;
00404        return stream;
00405 }
00406 
00407 static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filename, char *mode,
00408               int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
00409 {
00410        struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
00411        php_userstream_data_t *us;
00412        zval *zfilename, *zoptions, *zretval = NULL, *zfuncname;
00413        zval **args[2];      
00414        int call_result;
00415        php_stream *stream = NULL;
00416 
00417        /* Try to catch bad usage without preventing flexibility */
00418        if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) {
00419               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "infinite recursion prevented");
00420               return NULL;
00421        }
00422        FG(user_stream_current_filename) = filename;
00423        
00424        us = emalloc(sizeof(*us));
00425        us->wrapper = uwrap; 
00426 
00427        /* create an instance of our class */
00428        ALLOC_ZVAL(us->object);
00429        object_init_ex(us->object, uwrap->ce);
00430        Z_SET_REFCOUNT_P(us->object, 1);
00431        Z_SET_ISREF_P(us->object);
00432 
00433        if (context) {
00434               add_property_resource(us->object, "context", context->rsrc_id);
00435               zend_list_addref(context->rsrc_id);
00436        } else {
00437               add_property_null(us->object, "context");
00438        }
00439        
00440        /* call it's dir_open method - set up params first */
00441        MAKE_STD_ZVAL(zfilename);
00442        ZVAL_STRING(zfilename, filename, 1);
00443        args[0] = &zfilename;
00444 
00445        MAKE_STD_ZVAL(zoptions);
00446        ZVAL_LONG(zoptions, options);
00447        args[1] = &zoptions;
00448 
00449        MAKE_STD_ZVAL(zfuncname);
00450        ZVAL_STRING(zfuncname, USERSTREAM_DIR_OPEN, 1);
00451        
00452        call_result = call_user_function_ex(NULL,
00453                      &us->object,
00454                      zfuncname,
00455                      &zretval,
00456                      2, args,
00457                      0, NULL       TSRMLS_CC);
00458        
00459        if (call_result == SUCCESS && zretval != NULL && zval_is_true(zretval)) {
00460               /* the stream is now open! */
00461               stream = php_stream_alloc_rel(&php_stream_userspace_dir_ops, us, 0, mode);
00462 
00463               /* set wrapper data to be a reference to our object */
00464               stream->wrapperdata = us->object;
00465               zval_add_ref(&stream->wrapperdata);
00466        } else {
00467               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "\"%s::" USERSTREAM_DIR_OPEN "\" call failed",
00468                      us->wrapper->classname);
00469        }
00470        
00471        /* destroy everything else */
00472        if (stream == NULL) {
00473               zval_ptr_dtor(&us->object);
00474               efree(us);
00475        }
00476        if (zretval)
00477               zval_ptr_dtor(&zretval);
00478        
00479        zval_ptr_dtor(&zfuncname);
00480        zval_ptr_dtor(&zoptions);
00481        zval_ptr_dtor(&zfilename);
00482 
00483        FG(user_stream_current_filename) = NULL;
00484               
00485        return stream;
00486 }
00487 
00488 
00489 /* {{{ proto bool stream_wrapper_register(string protocol, string classname[, integer flags])
00490    Registers a custom URL protocol handler class */
00491 PHP_FUNCTION(stream_wrapper_register)
00492 {
00493        char *protocol, *classname;
00494        int protocol_len, classname_len;
00495        struct php_user_stream_wrapper * uwrap;
00496        int rsrc_id;
00497        long flags = 0;
00498        
00499        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &protocol, &protocol_len, &classname, &classname_len, &flags) == FAILURE) {
00500               RETURN_FALSE;
00501        }
00502        
00503        uwrap = (struct php_user_stream_wrapper *)ecalloc(1, sizeof(*uwrap));
00504        uwrap->protoname = estrndup(protocol, protocol_len);
00505        uwrap->classname = estrndup(classname, classname_len);
00506        uwrap->wrapper.wops = &user_stream_wops;
00507        uwrap->wrapper.abstract = uwrap;
00508        uwrap->wrapper.is_url = ((flags & PHP_STREAM_IS_URL) != 0);
00509 
00510        rsrc_id = ZEND_REGISTER_RESOURCE(NULL, uwrap, le_protocols);
00511 
00512        if (zend_lookup_class(uwrap->classname, classname_len, (zend_class_entry***)&uwrap->ce TSRMLS_CC) == SUCCESS) {
00513               uwrap->ce = *(zend_class_entry**)uwrap->ce;
00514               if (php_register_url_stream_wrapper_volatile(protocol, &uwrap->wrapper TSRMLS_CC) == SUCCESS) {
00515                      RETURN_TRUE;
00516               } else {
00517                      /* We failed.  But why? */
00518                      if (zend_hash_exists(php_stream_get_url_stream_wrappers_hash(), protocol, protocol_len + 1)) {
00519                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol %s:// is already defined.", protocol);
00520                      } else {
00521                             /* Hash doesn't exist so it must have been an invalid protocol scheme */
00522                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid protocol scheme specified. Unable to register wrapper class %s to %s://", classname, protocol);
00523                      }
00524               }
00525        } else {
00526               php_error_docref(NULL TSRMLS_CC, E_WARNING, "class '%s' is undefined", classname);
00527        }
00528 
00529        zend_list_delete(rsrc_id);
00530        RETURN_FALSE;
00531 }
00532 /* }}} */
00533 
00534 /* {{{ proto bool stream_wrapper_unregister(string protocol)
00535        Unregister a wrapper for the life of the current request. */
00536 PHP_FUNCTION(stream_wrapper_unregister)
00537 {
00538        char *protocol;
00539        int protocol_len;
00540 
00541        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &protocol, &protocol_len) == FAILURE) {
00542               RETURN_FALSE;
00543        }
00544 
00545        if (php_unregister_url_stream_wrapper_volatile(protocol TSRMLS_CC) == FAILURE) {
00546               /* We failed */
00547               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to unregister protocol %s://", protocol);
00548               RETURN_FALSE;
00549        }
00550 
00551        RETURN_TRUE;
00552 }
00553 /* }}} */
00554 
00555 /* {{{ proto bool stream_wrapper_restore(string protocol)
00556        Restore the original protocol handler, overriding if necessary */
00557 PHP_FUNCTION(stream_wrapper_restore)
00558 {
00559        char *protocol;
00560        int protocol_len;
00561        php_stream_wrapper **wrapperpp = NULL, *wrapper;
00562        HashTable *global_wrapper_hash;
00563 
00564        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &protocol, &protocol_len) == FAILURE) {
00565               RETURN_FALSE;
00566        }
00567 
00568        global_wrapper_hash = php_stream_get_url_stream_wrappers_hash_global();
00569        if (php_stream_get_url_stream_wrappers_hash() == global_wrapper_hash) {
00570               php_error_docref(NULL TSRMLS_CC, E_NOTICE, "%s:// was never changed, nothing to restore", protocol);
00571               RETURN_TRUE;
00572        }
00573 
00574        if ((zend_hash_find(global_wrapper_hash, protocol, protocol_len + 1, (void**)&wrapperpp) == FAILURE) || !wrapperpp) {
00575               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s:// never existed, nothing to restore", protocol);
00576               RETURN_FALSE;
00577        }
00578 
00579        /* next line might delete the pointer that wrapperpp points at, so deref it now */
00580        wrapper = *wrapperpp;
00581 
00582        /* A failure here could be okay given that the protocol might have been merely unregistered */
00583        php_unregister_url_stream_wrapper_volatile(protocol TSRMLS_CC);
00584 
00585        if (php_register_url_stream_wrapper_volatile(protocol, wrapper TSRMLS_CC) == FAILURE) {
00586               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to restore original %s:// wrapper", protocol);
00587               RETURN_FALSE;
00588        }      
00589 
00590        RETURN_TRUE;
00591 }
00592 /* }}} */
00593 
00594 static size_t php_userstreamop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
00595 {
00596        zval func_name;
00597        zval *retval = NULL;
00598        int call_result;
00599        php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
00600        zval **args[1];
00601        zval *zbufptr;
00602        size_t didwrite = 0;
00603 
00604        assert(us != NULL);
00605 
00606        ZVAL_STRINGL(&func_name, USERSTREAM_WRITE, sizeof(USERSTREAM_WRITE)-1, 0);
00607 
00608        MAKE_STD_ZVAL(zbufptr);
00609        ZVAL_STRINGL(zbufptr, (char*)buf, count, 1);;
00610        args[0] = &zbufptr;
00611 
00612        call_result = call_user_function_ex(NULL,
00613                      &us->object,
00614                      &func_name,
00615                      &retval,
00616                      1, args,
00617                      0, NULL TSRMLS_CC);
00618        zval_ptr_dtor(&zbufptr);
00619 
00620        didwrite = 0;
00621        if (call_result == SUCCESS && retval != NULL) {
00622               convert_to_long(retval);
00623               didwrite = Z_LVAL_P(retval);
00624        } else if (call_result == FAILURE) {
00625               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " is not implemented!",
00626                             us->wrapper->classname);
00627        }
00628 
00629        /* don't allow strange buffer overruns due to bogus return */
00630        if (didwrite > count) {
00631               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " wrote %ld bytes more data than requested (%ld written, %ld max)",
00632                             us->wrapper->classname,
00633                             (long)(didwrite - count), (long)didwrite, (long)count);
00634               didwrite = count;
00635        }
00636        
00637        if (retval)
00638               zval_ptr_dtor(&retval);
00639        
00640        return didwrite;
00641 }
00642 
00643 static size_t php_userstreamop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
00644 {
00645        zval func_name;
00646        zval *retval = NULL;
00647        zval **args[1];
00648        int call_result;
00649        size_t didread = 0;
00650        php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
00651        zval *zcount;
00652 
00653        assert(us != NULL);
00654 
00655        ZVAL_STRINGL(&func_name, USERSTREAM_READ, sizeof(USERSTREAM_READ)-1, 0);
00656 
00657        MAKE_STD_ZVAL(zcount);
00658        ZVAL_LONG(zcount, count);
00659        args[0] = &zcount;
00660 
00661        call_result = call_user_function_ex(NULL,
00662                      &us->object,
00663                      &func_name,
00664                      &retval,
00665                      1, args,
00666                      0, NULL TSRMLS_CC);
00667 
00668        if (call_result == SUCCESS && retval != NULL) {
00669               convert_to_string(retval);
00670               didread = Z_STRLEN_P(retval);
00671               if (didread > count) {
00672                      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",
00673                                    us->wrapper->classname, (long)(didread - count), (long)didread, (long)count);
00674                      didread = count;
00675               }
00676               if (didread > 0)
00677                      memcpy(buf, Z_STRVAL_P(retval), didread);
00678        } else if (call_result == FAILURE) {
00679               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_READ " is not implemented!",
00680                             us->wrapper->classname);
00681        }
00682        zval_ptr_dtor(&zcount);
00683 
00684        if (retval) {
00685               zval_ptr_dtor(&retval);
00686               retval = NULL;
00687        }
00688 
00689        /* since the user stream has no way of setting the eof flag directly, we need to ask it if we hit eof */
00690 
00691        ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1, 0);
00692 
00693        call_result = call_user_function_ex(NULL,
00694                      &us->object,
00695                      &func_name,
00696                      &retval,
00697                      0, NULL, 0, NULL TSRMLS_CC);
00698 
00699        if (call_result == SUCCESS && retval != NULL && zval_is_true(retval)) {
00700               stream->eof = 1;
00701        } else if (call_result == FAILURE) {
00702               php_error_docref(NULL TSRMLS_CC, E_WARNING,
00703                             "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
00704                             us->wrapper->classname);
00705 
00706               stream->eof = 1;
00707        }
00708 
00709        if (retval) {
00710               zval_ptr_dtor(&retval);
00711               retval = NULL;
00712        }
00713 
00714        return didread;
00715 }
00716 
00717 static int php_userstreamop_close(php_stream *stream, int close_handle TSRMLS_DC)
00718 {
00719        zval func_name;
00720        zval *retval = NULL;
00721        php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
00722 
00723        assert(us != NULL);
00724        
00725        ZVAL_STRINGL(&func_name, USERSTREAM_CLOSE, sizeof(USERSTREAM_CLOSE)-1, 0);
00726        
00727        call_user_function_ex(NULL,
00728                      &us->object,
00729                      &func_name,
00730                      &retval,
00731                      0, NULL, 0, NULL TSRMLS_CC);
00732 
00733        if (retval)
00734               zval_ptr_dtor(&retval);
00735        
00736        zval_ptr_dtor(&us->object);
00737 
00738        efree(us);
00739        
00740        return 0;
00741 }
00742 
00743 static int php_userstreamop_flush(php_stream *stream TSRMLS_DC)
00744 {
00745        zval func_name;
00746        zval *retval = NULL;
00747        int call_result;
00748        php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
00749 
00750        assert(us != NULL);
00751 
00752        ZVAL_STRINGL(&func_name, USERSTREAM_FLUSH, sizeof(USERSTREAM_FLUSH)-1, 0);
00753        
00754        call_result = call_user_function_ex(NULL,
00755                      &us->object,
00756                      &func_name,
00757                      &retval,
00758                      0, NULL, 0, NULL TSRMLS_CC);
00759 
00760        if (call_result == SUCCESS && retval != NULL && zval_is_true(retval))
00761               call_result = 0;
00762        else
00763               call_result = -1;
00764        
00765        if (retval)
00766               zval_ptr_dtor(&retval);
00767        
00768        return call_result;
00769 }
00770 
00771 static int php_userstreamop_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
00772 {
00773        zval func_name;
00774        zval *retval = NULL;
00775        int call_result, ret;
00776        php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
00777        zval **args[2];
00778        zval *zoffs, *zwhence;
00779 
00780        assert(us != NULL);
00781 
00782        ZVAL_STRINGL(&func_name, USERSTREAM_SEEK, sizeof(USERSTREAM_SEEK)-1, 0);
00783 
00784        MAKE_STD_ZVAL(zoffs);
00785        ZVAL_LONG(zoffs, offset);
00786        args[0] = &zoffs;
00787 
00788        MAKE_STD_ZVAL(zwhence);
00789        ZVAL_LONG(zwhence, whence);
00790        args[1] = &zwhence;
00791 
00792        call_result = call_user_function_ex(NULL,
00793                      &us->object,
00794                      &func_name,
00795                      &retval,
00796                      2, args,
00797                      0, NULL TSRMLS_CC);
00798 
00799        zval_ptr_dtor(&zoffs);
00800        zval_ptr_dtor(&zwhence);
00801 
00802        if (call_result == FAILURE) {
00803               /* stream_seek is not implemented, so disable seeks for this stream */
00804               stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
00805               /* there should be no retval to clean up */
00806               
00807               if (retval) 
00808                      zval_ptr_dtor(&retval);
00809               
00810               return -1;
00811        } else if (call_result == SUCCESS && retval != NULL && zval_is_true(retval)) {
00812               ret = 0;
00813        } else {
00814               ret = -1;
00815        }
00816 
00817        if (retval) {
00818               zval_ptr_dtor(&retval);
00819               retval = NULL;
00820        }
00821 
00822        if (ret) {
00823               return ret;
00824        }
00825 
00826        /* now determine where we are */
00827        ZVAL_STRINGL(&func_name, USERSTREAM_TELL, sizeof(USERSTREAM_TELL)-1, 0);
00828 
00829        call_result = call_user_function_ex(NULL,
00830               &us->object,
00831               &func_name,
00832               &retval,
00833               0, NULL, 0, NULL TSRMLS_CC);
00834 
00835        if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_LONG) {
00836               *newoffs = Z_LVAL_P(retval);
00837               ret = 0;
00838        } else if (call_result == FAILURE) {
00839               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_TELL " is not implemented!", us->wrapper->classname);
00840               ret = -1;
00841        } else {
00842               ret = -1;
00843        }
00844 
00845        if (retval) {
00846               zval_ptr_dtor(&retval);
00847        }
00848        return ret;
00849 }
00850 
00851 /* parse the return value from one of the stat functions and store the
00852  * relevant fields into the statbuf provided */
00853 static int statbuf_from_array(zval *array, php_stream_statbuf *ssb TSRMLS_DC)
00854 {
00855        zval **elem;
00856 
00857 #define STAT_PROP_ENTRY_EX(name, name2)                        \
00858        if (SUCCESS == zend_hash_find(Z_ARRVAL_P(array), #name, sizeof(#name), (void**)&elem)) {     \
00859               SEPARATE_ZVAL(elem);                                                                                                                  \
00860               convert_to_long(*elem);                                                                   \
00861               ssb->sb.st_##name2 = Z_LVAL_PP(elem);                                                      \
00862        }
00863 
00864 #define STAT_PROP_ENTRY(name) STAT_PROP_ENTRY_EX(name,name)
00865 
00866        memset(ssb, 0, sizeof(php_stream_statbuf));
00867        STAT_PROP_ENTRY(dev);
00868        STAT_PROP_ENTRY(ino);
00869        STAT_PROP_ENTRY(mode);
00870        STAT_PROP_ENTRY(nlink);
00871        STAT_PROP_ENTRY(uid);
00872        STAT_PROP_ENTRY(gid);
00873 #if HAVE_ST_RDEV
00874        STAT_PROP_ENTRY(rdev);
00875 #endif
00876        STAT_PROP_ENTRY(size);
00877 #ifdef NETWARE
00878        STAT_PROP_ENTRY_EX(atime, atime.tv_sec);
00879        STAT_PROP_ENTRY_EX(mtime, mtime.tv_sec);
00880        STAT_PROP_ENTRY_EX(ctime, ctime.tv_sec);
00881 #else
00882        STAT_PROP_ENTRY(atime);
00883        STAT_PROP_ENTRY(mtime);
00884        STAT_PROP_ENTRY(ctime);
00885 #endif
00886 #ifdef HAVE_ST_BLKSIZE
00887        STAT_PROP_ENTRY(blksize);
00888 #endif
00889 #ifdef HAVE_ST_BLOCKS
00890        STAT_PROP_ENTRY(blocks);
00891 #endif
00892 
00893 #undef STAT_PROP_ENTRY      
00894 #undef STAT_PROP_ENTRY_EX   
00895        return SUCCESS;
00896 }
00897 
00898 static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
00899 {
00900        zval func_name;
00901        zval *retval = NULL;
00902        int call_result;
00903        php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
00904        int ret = -1;
00905 
00906        ZVAL_STRINGL(&func_name, USERSTREAM_STAT, sizeof(USERSTREAM_STAT)-1, 0);
00907 
00908        call_result = call_user_function_ex(NULL,
00909                      &us->object,
00910                      &func_name,
00911                      &retval,
00912                      0, NULL, 0, NULL TSRMLS_CC);
00913 
00914        if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_ARRAY) {
00915               if (SUCCESS == statbuf_from_array(retval, ssb TSRMLS_CC))
00916                      ret = 0;
00917        } else {
00918               if (call_result == FAILURE) {
00919                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_STAT " is not implemented!",
00920                                    us->wrapper->classname);
00921               }
00922        }
00923 
00924        if (retval) 
00925               zval_ptr_dtor(&retval);
00926        
00927        return ret;
00928 }
00929 
00930 
00931 static int php_userstreamop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) {
00932        zval func_name;
00933        zval *retval = NULL;
00934        int call_result;
00935        php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
00936        int ret = -1;
00937        zval *zvalue = NULL;
00938        zval **args[3];
00939 
00940        switch (option) {
00941        case PHP_STREAM_OPTION_CHECK_LIVENESS:
00942               ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1, 0);
00943               call_result = call_user_function_ex(NULL, &us->object, &func_name, &retval, 0, NULL, 0, NULL TSRMLS_CC);
00944               if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_BOOL) {
00945                      ret = zval_is_true(retval) ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
00946               } else {
00947                      ret = PHP_STREAM_OPTION_RETURN_ERR;
00948                      php_error_docref(NULL TSRMLS_CC, E_WARNING,
00949                                    "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
00950                                    us->wrapper->classname);
00951               }
00952               break;
00953 
00954        case PHP_STREAM_OPTION_LOCKING:
00955               MAKE_STD_ZVAL(zvalue);
00956               ZVAL_LONG(zvalue, 0);
00957 
00958               if (value & LOCK_NB) {
00959                      Z_LVAL_P(zvalue) |= PHP_LOCK_NB;
00960               }
00961               switch(value & ~LOCK_NB) {
00962               case LOCK_SH:
00963                      Z_LVAL_P(zvalue) |= PHP_LOCK_SH;
00964                      break;
00965               case LOCK_EX:
00966                      Z_LVAL_P(zvalue) |= PHP_LOCK_EX;
00967                      break;
00968               case LOCK_UN:
00969                      Z_LVAL_P(zvalue) |= PHP_LOCK_UN;
00970                      break;
00971               }
00972 
00973               args[0] = &zvalue;
00974               
00975               /* TODO wouldblock */
00976               ZVAL_STRINGL(&func_name, USERSTREAM_LOCK, sizeof(USERSTREAM_LOCK)-1, 0);
00977               
00978               call_result = call_user_function_ex(NULL,
00979                                                                              &us->object,
00980                                                                              &func_name,
00981                                                                              &retval,
00982                                                                              1, args, 0, NULL TSRMLS_CC);
00983               
00984               if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_BOOL) {
00985                      ret = !Z_LVAL_P(retval);
00986               } else if (call_result == FAILURE) {
00987                      if (value == 0) { 
00988                             /* lock support test (TODO: more check) */
00989                             ret = 0;
00990                      } else {
00991                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_LOCK " is not implemented!", 
00992                                                          us->wrapper->classname);
00993                      }
00994               }
00995 
00996               break;
00997        
00998        case PHP_STREAM_OPTION_READ_BUFFER:
00999        case PHP_STREAM_OPTION_WRITE_BUFFER:
01000        case PHP_STREAM_OPTION_READ_TIMEOUT:
01001        case PHP_STREAM_OPTION_BLOCKING: {
01002               zval *zoption = NULL;
01003               zval *zptrparam = NULL;
01004               
01005               ZVAL_STRINGL(&func_name, USERSTREAM_SET_OPTION, sizeof(USERSTREAM_SET_OPTION)-1, 0);
01006 
01007               ALLOC_INIT_ZVAL(zoption);
01008               ZVAL_LONG(zoption, option);
01009 
01010               ALLOC_INIT_ZVAL(zvalue);
01011               ALLOC_INIT_ZVAL(zptrparam);
01012 
01013               args[0] = &zoption;
01014               args[1] = &zvalue;
01015               args[2] = &zptrparam;
01016 
01017               switch(option) {
01018               case PHP_STREAM_OPTION_READ_BUFFER:
01019               case PHP_STREAM_OPTION_WRITE_BUFFER:
01020                      ZVAL_LONG(zvalue, value);
01021                      if (ptrparam) {
01022                             ZVAL_LONG(zptrparam, *(long *)ptrparam);
01023                      } else {
01024                             ZVAL_LONG(zptrparam, BUFSIZ);
01025                      }
01026                      break;
01027               case PHP_STREAM_OPTION_READ_TIMEOUT: {
01028                      struct timeval tv = *(struct timeval*)ptrparam;
01029                      ZVAL_LONG(zvalue, tv.tv_sec);
01030                      ZVAL_LONG(zptrparam, tv.tv_usec);
01031                      break;
01032                      }
01033               case PHP_STREAM_OPTION_BLOCKING:
01034                      ZVAL_LONG(zvalue, value);
01035                      break;
01036               default:
01037                      break;
01038               }
01039 
01040               call_result = call_user_function_ex(NULL,
01041                      &us->object,
01042                      &func_name,
01043                      &retval,
01044                      3, args, 0, NULL TSRMLS_CC);
01045        
01046               do {
01047                      if (call_result == FAILURE) {
01048                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_SET_OPTION " is not implemented!",
01049                                           us->wrapper->classname);
01050                             break;
01051                      }
01052                      if (retval && zend_is_true(retval)) {
01053                             ret = PHP_STREAM_OPTION_RETURN_OK;
01054                      }
01055               } while (0);
01056 
01057               if (zoption) {
01058                      zval_ptr_dtor(&zoption);
01059               }
01060               if (zptrparam) {
01061                      zval_ptr_dtor(&zptrparam);
01062               }
01063 
01064               break;
01065               }
01066        }
01067 
01068        /* clean up */
01069        if (retval) {
01070               zval_ptr_dtor(&retval);
01071        }
01072   
01073 
01074        if (zvalue) {
01075               zval_ptr_dtor(&zvalue);
01076        }
01077 
01078        return ret;
01079 }
01080 
01081 
01082 static int user_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC)
01083 {
01084        struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
01085        zval *zfilename, *zfuncname, *zretval;
01086        zval **args[1];
01087        int call_result;
01088        zval *object;
01089        int ret = 0;
01090 
01091        /* create an instance of our class */
01092        ALLOC_ZVAL(object);
01093        object_init_ex(object, uwrap->ce);
01094        Z_SET_REFCOUNT_P(object, 1);
01095        Z_SET_ISREF_P(object);
01096 
01097        if (context) {
01098               add_property_resource(object, "context", context->rsrc_id);
01099               zend_list_addref(context->rsrc_id);
01100        } else {
01101               add_property_null(object, "context");
01102        }
01103 
01104        /* call the unlink method */
01105        MAKE_STD_ZVAL(zfilename);
01106        ZVAL_STRING(zfilename, url, 1);
01107        args[0] = &zfilename;
01108 
01109        MAKE_STD_ZVAL(zfuncname);
01110        ZVAL_STRING(zfuncname, USERSTREAM_UNLINK, 1);
01111        
01112        call_result = call_user_function_ex(NULL,
01113                      &object,
01114                      zfuncname,
01115                      &zretval,
01116                      1, args,
01117                      0, NULL       TSRMLS_CC);
01118 
01119        if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
01120               ret = Z_LVAL_P(zretval);
01121        } else if (call_result == FAILURE) {
01122               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_UNLINK " is not implemented!", uwrap->classname);
01123        }
01124 
01125        /* clean up */
01126        zval_ptr_dtor(&object);
01127        if (zretval)
01128               zval_ptr_dtor(&zretval);
01129        
01130        zval_ptr_dtor(&zfuncname);
01131        zval_ptr_dtor(&zfilename);
01132 
01133        return ret;
01134 }
01135 
01136 static int user_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC)
01137 {
01138        struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
01139        zval *zold_name, *znew_name, *zfuncname, *zretval;
01140        zval **args[2];
01141        int call_result;
01142        zval *object;
01143        int ret = 0;
01144 
01145        /* create an instance of our class */
01146        ALLOC_ZVAL(object);
01147        object_init_ex(object, uwrap->ce);
01148        Z_SET_REFCOUNT_P(object, 1);
01149        Z_SET_ISREF_P(object);
01150 
01151        if (context) {
01152               add_property_resource(object, "context", context->rsrc_id);
01153               zend_list_addref(context->rsrc_id);
01154        } else {
01155               add_property_null(object, "context");
01156        }
01157 
01158        /* call the rename method */
01159        MAKE_STD_ZVAL(zold_name);
01160        ZVAL_STRING(zold_name, url_from, 1);
01161        args[0] = &zold_name;
01162 
01163        MAKE_STD_ZVAL(znew_name);
01164        ZVAL_STRING(znew_name, url_to, 1);
01165        args[1] = &znew_name;
01166 
01167        MAKE_STD_ZVAL(zfuncname);
01168        ZVAL_STRING(zfuncname, USERSTREAM_RENAME, 1);
01169        
01170        call_result = call_user_function_ex(NULL,
01171                      &object,
01172                      zfuncname,
01173                      &zretval,
01174                      2, args,
01175                      0, NULL       TSRMLS_CC);
01176 
01177        if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
01178               ret = Z_LVAL_P(zretval);
01179        } else if (call_result == FAILURE) {
01180               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_RENAME " is not implemented!", uwrap->classname);
01181        }
01182 
01183        /* clean up */
01184        zval_ptr_dtor(&object);
01185        if (zretval)
01186               zval_ptr_dtor(&zretval);
01187        
01188        zval_ptr_dtor(&zfuncname);
01189        zval_ptr_dtor(&zold_name);
01190        zval_ptr_dtor(&znew_name);
01191 
01192        return ret;
01193 }
01194 
01195 static int user_wrapper_mkdir(php_stream_wrapper *wrapper, char *url, int mode, int options, php_stream_context *context TSRMLS_DC)
01196 {
01197        struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
01198        zval *zfilename, *zmode, *zoptions, *zfuncname, *zretval;
01199        zval **args[3];
01200        int call_result;
01201        zval *object;
01202        int ret = 0;
01203 
01204        /* create an instance of our class */
01205        ALLOC_ZVAL(object);
01206        object_init_ex(object, uwrap->ce);
01207        Z_SET_REFCOUNT_P(object, 1);
01208        Z_SET_ISREF_P(object);
01209 
01210        if (context) {
01211               add_property_resource(object, "context", context->rsrc_id);
01212               zend_list_addref(context->rsrc_id);
01213        } else {
01214               add_property_null(object, "context");
01215        }
01216 
01217        /* call the mkdir method */
01218        MAKE_STD_ZVAL(zfilename);
01219        ZVAL_STRING(zfilename, url, 1);
01220        args[0] = &zfilename;
01221 
01222        MAKE_STD_ZVAL(zmode);
01223        ZVAL_LONG(zmode, mode);
01224        args[1] = &zmode;
01225 
01226        MAKE_STD_ZVAL(zoptions);
01227        ZVAL_LONG(zoptions, options);
01228        args[2] = &zoptions;
01229 
01230        MAKE_STD_ZVAL(zfuncname);
01231        ZVAL_STRING(zfuncname, USERSTREAM_MKDIR, 1);
01232        
01233        call_result = call_user_function_ex(NULL,
01234                      &object,
01235                      zfuncname,
01236                      &zretval,
01237                      3, args,
01238                      0, NULL       TSRMLS_CC);
01239 
01240        if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
01241               ret = Z_LVAL_P(zretval);
01242        } else if (call_result == FAILURE) {
01243               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_MKDIR " is not implemented!", uwrap->classname);
01244        }
01245 
01246        /* clean up */
01247        zval_ptr_dtor(&object);
01248        if (zretval) {
01249               zval_ptr_dtor(&zretval);
01250        }
01251        
01252        zval_ptr_dtor(&zfuncname);
01253        zval_ptr_dtor(&zfilename);
01254        zval_ptr_dtor(&zmode);
01255        zval_ptr_dtor(&zoptions);
01256 
01257        return ret;
01258 }
01259 
01260 static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC)
01261 {
01262        struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
01263        zval *zfilename, *zoptions, *zfuncname, *zretval;
01264        zval **args[3];
01265        int call_result;
01266        zval *object;
01267        int ret = 0;
01268 
01269        /* create an instance of our class */
01270        ALLOC_ZVAL(object);
01271        object_init_ex(object, uwrap->ce);
01272        Z_SET_REFCOUNT_P(object, 1);
01273        Z_SET_ISREF_P(object);
01274 
01275        if (context) {
01276               add_property_resource(object, "context", context->rsrc_id);
01277               zend_list_addref(context->rsrc_id);
01278        } else {
01279               add_property_null(object, "context");
01280        }
01281 
01282        /* call the rmdir method */
01283        MAKE_STD_ZVAL(zfilename);
01284        ZVAL_STRING(zfilename, url, 1);
01285        args[0] = &zfilename;
01286 
01287        MAKE_STD_ZVAL(zoptions);
01288        ZVAL_LONG(zoptions, options);
01289        args[1] = &zoptions;
01290 
01291        MAKE_STD_ZVAL(zfuncname);
01292        ZVAL_STRING(zfuncname, USERSTREAM_RMDIR, 1);
01293        
01294        call_result = call_user_function_ex(NULL,
01295                      &object,
01296                      zfuncname,
01297                      &zretval,
01298                      2, args,
01299                      0, NULL       TSRMLS_CC);
01300 
01301        if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
01302               ret = Z_LVAL_P(zretval);
01303        } else if (call_result == FAILURE) {
01304               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_RMDIR " is not implemented!", uwrap->classname);
01305        }
01306 
01307        /* clean up */
01308        zval_ptr_dtor(&object);
01309        if (zretval) {
01310               zval_ptr_dtor(&zretval);
01311        }
01312        
01313        zval_ptr_dtor(&zfuncname);
01314        zval_ptr_dtor(&zfilename);
01315        zval_ptr_dtor(&zoptions);
01316 
01317        return ret;
01318 }
01319 
01320 static int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC)
01321 {
01322        struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
01323        zval *zfilename, *zfuncname, *zretval, *zflags;
01324        zval **args[2];      
01325        int call_result;
01326        zval *object;
01327        int ret = -1;
01328 
01329        /* create an instance of our class */
01330        ALLOC_ZVAL(object);
01331        object_init_ex(object, uwrap->ce);
01332        Z_SET_REFCOUNT_P(object, 1);
01333        Z_SET_ISREF_P(object);
01334 
01335        if (context) {
01336               add_property_resource(object, "context", context->rsrc_id);
01337               zend_list_addref(context->rsrc_id);
01338        } else {
01339               add_property_null(object, "context");
01340        }
01341 
01342        /* call it's stat_url method - set up params first */
01343        MAKE_STD_ZVAL(zfilename);
01344        ZVAL_STRING(zfilename, url, 1);
01345        args[0] = &zfilename;
01346 
01347        MAKE_STD_ZVAL(zflags);
01348        ZVAL_LONG(zflags, flags);
01349        args[1] = &zflags;
01350 
01351        MAKE_STD_ZVAL(zfuncname);
01352        ZVAL_STRING(zfuncname, USERSTREAM_STATURL, 1);
01353        
01354        call_result = call_user_function_ex(NULL,
01355                      &object,
01356                      zfuncname,
01357                      &zretval,
01358                      2, args,
01359                      0, NULL       TSRMLS_CC);
01360        
01361        if (call_result == SUCCESS && zretval != NULL && Z_TYPE_P(zretval) == IS_ARRAY) {
01362               /* We got the info we needed */
01363               if (SUCCESS == statbuf_from_array(zretval, ssb TSRMLS_CC))
01364                      ret = 0;
01365        } else {
01366               if (call_result == FAILURE) {
01367                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_STATURL " is not implemented!",
01368                                    uwrap->classname);
01369               }
01370        }
01371        
01372        /* clean up */
01373        zval_ptr_dtor(&object);
01374        if (zretval)
01375               zval_ptr_dtor(&zretval);
01376        
01377        zval_ptr_dtor(&zfuncname);
01378        zval_ptr_dtor(&zfilename);
01379        zval_ptr_dtor(&zflags);
01380               
01381        return ret;
01382 
01383 }
01384 
01385 static size_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t count TSRMLS_DC)
01386 {
01387        zval func_name;
01388        zval *retval = NULL;
01389        int call_result;
01390        size_t didread = 0;
01391        php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
01392        php_stream_dirent *ent = (php_stream_dirent*)buf;
01393 
01394        /* avoid problems if someone mis-uses the stream */
01395        if (count != sizeof(php_stream_dirent))
01396               return 0;
01397 
01398        ZVAL_STRINGL(&func_name, USERSTREAM_DIR_READ, sizeof(USERSTREAM_DIR_READ)-1, 0);
01399 
01400        call_result = call_user_function_ex(NULL,
01401                      &us->object,
01402                      &func_name,
01403                      &retval,
01404                      0, NULL,
01405                      0, NULL TSRMLS_CC);
01406 
01407        if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) != IS_BOOL) {
01408               convert_to_string(retval);
01409               PHP_STRLCPY(ent->d_name, Z_STRVAL_P(retval), sizeof(ent->d_name), Z_STRLEN_P(retval));
01410 
01411               didread = sizeof(php_stream_dirent);
01412        } else if (call_result == FAILURE) {
01413               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_DIR_READ " is not implemented!",
01414                             us->wrapper->classname);
01415        }
01416 
01417        if (retval)
01418               zval_ptr_dtor(&retval);
01419 
01420        return didread;
01421 }
01422 
01423 static int php_userstreamop_closedir(php_stream *stream, int close_handle TSRMLS_DC)
01424 {
01425        zval func_name;
01426        zval *retval = NULL;
01427        php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
01428 
01429        assert(us != NULL);
01430        
01431        ZVAL_STRINGL(&func_name, USERSTREAM_DIR_CLOSE, sizeof(USERSTREAM_DIR_CLOSE)-1, 0);
01432        
01433        call_user_function_ex(NULL,
01434                      &us->object,
01435                      &func_name,
01436                      &retval,
01437                      0, NULL, 0, NULL TSRMLS_CC);
01438 
01439        if (retval)
01440               zval_ptr_dtor(&retval);
01441        
01442        zval_ptr_dtor(&us->object);
01443 
01444        efree(us);
01445        
01446        return 0;
01447 }
01448 
01449 static int php_userstreamop_rewinddir(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
01450 {
01451        zval func_name;
01452        zval *retval = NULL;
01453        php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
01454 
01455        ZVAL_STRINGL(&func_name, USERSTREAM_DIR_REWIND, sizeof(USERSTREAM_DIR_REWIND)-1, 0);
01456        
01457        call_user_function_ex(NULL,
01458                      &us->object,
01459                      &func_name,
01460                      &retval,
01461                      0, NULL, 0, NULL TSRMLS_CC);
01462 
01463        if (retval)
01464               zval_ptr_dtor(&retval);
01465        
01466        return 0;
01467 
01468 }
01469 
01470 static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr TSRMLS_DC)
01471 {
01472        php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
01473        zval func_name;
01474        zval *retval = NULL;
01475        zval *zcastas = NULL;
01476        zval **args[1];
01477        php_stream * intstream = NULL;
01478        int call_result;
01479        int ret = FAILURE;
01480 
01481        ZVAL_STRINGL(&func_name, USERSTREAM_CAST, sizeof(USERSTREAM_CAST)-1, 0);
01482 
01483        ALLOC_INIT_ZVAL(zcastas);
01484        switch(castas) {
01485        case PHP_STREAM_AS_FD_FOR_SELECT:
01486               ZVAL_LONG(zcastas, PHP_STREAM_AS_FD_FOR_SELECT);
01487               break;
01488        default:
01489               ZVAL_LONG(zcastas, PHP_STREAM_AS_STDIO);
01490               break;
01491        }
01492        args[0] = &zcastas;
01493 
01494        call_result = call_user_function_ex(NULL,
01495                      &us->object,
01496                      &func_name,
01497                      &retval,
01498                      1, args, 0, NULL TSRMLS_CC);
01499 
01500        do {
01501               if (call_result == FAILURE) {
01502                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_CAST " is not implemented!",
01503                                    us->wrapper->classname);
01504                      break;
01505               }
01506               if (retval == NULL || !zend_is_true(retval)) {
01507                      break;
01508               }
01509               php_stream_from_zval_no_verify(intstream, &retval);
01510               if (!intstream) {
01511                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_CAST " must return a stream resource",
01512                                    us->wrapper->classname);
01513                      break;
01514               }
01515               if (intstream == stream) {
01516                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_CAST " must not return itself",
01517                                    us->wrapper->classname);
01518                      intstream = NULL;
01519                      break;
01520               }
01521               ret = php_stream_cast(intstream, castas, retptr, 1);
01522        } while (0);
01523 
01524        if (retval) {
01525               zval_ptr_dtor(&retval);
01526        }
01527        if (zcastas) {
01528               zval_ptr_dtor(&zcastas);
01529        }
01530 
01531        return ret;
01532 }
01533 
01534 php_stream_ops php_stream_userspace_ops = {
01535        php_userstreamop_write, php_userstreamop_read,
01536        php_userstreamop_close, php_userstreamop_flush,
01537        "user-space",
01538        php_userstreamop_seek,
01539        php_userstreamop_cast,
01540        php_userstreamop_stat, 
01541        php_userstreamop_set_option,
01542 };
01543 
01544 php_stream_ops php_stream_userspace_dir_ops = {
01545        NULL, /* write */
01546        php_userstreamop_readdir,
01547        php_userstreamop_closedir,
01548        NULL, /* flush */
01549        "user-space-dir",
01550        php_userstreamop_rewinddir,
01551        NULL, /* cast */
01552        NULL, /* stat */
01553        NULL  /* set_option */
01554 };
01555 
01556