]> granicus.if.org Git - php/commitdiff
split stream file handlers into stream.c and directory handlers into dirstream.c
authorGreg Beaver <cellog@php.net>
Tue, 8 Jan 2008 20:31:54 +0000 (20:31 +0000)
committerGreg Beaver <cellog@php.net>
Tue, 8 Jan 2008 20:31:54 +0000 (20:31 +0000)
ext/phar/config.m4
ext/phar/config.w32
ext/phar/dirstream.c [new file with mode: 0644]
ext/phar/dirstream.h [new file with mode: 0644]
ext/phar/phar.c
ext/phar/phar_internal.h
ext/phar/php_stream_unlink.h [new file with mode: 0644]
ext/phar/stream.c [new file with mode: 0644]
ext/phar/stream.h [new file with mode: 0644]
ext/phar/zip.c

index 7e35fc6180b66b045c93d529999303c465dfd471..742923994f1778906906b9a92caf5f91f5df5304 100644 (file)
@@ -32,7 +32,7 @@ if test "$PHP_PHAR" != "no"; then
        else
                AC_MSG_RESULT([no])
        fi
-  PHP_NEW_EXTENSION(phar, tar.c zip.c phar.c phar_object.c phar_path_check.c $PHP_PHAR_SOURCES, $ext_shared)
+  PHP_NEW_EXTENSION(phar, tar.c zip.c stream.c dirstream.c phar.c phar_object.c phar_path_check.c $PHP_PHAR_SOURCES, $ext_shared)
   PHP_ADD_BUILD_DIR($ext_builddir/lib, 1)
   PHP_SUBST(PHAR_SHARED_LIBADD)
   PHP_ADD_EXTENSION_DEP(phar, zlib, true)
index f91cb76b9a693eae6c7702c1202d35a77714d2bb..ac7c5460565cf8a2a1ee37b3a62389a25a3ae904 100644 (file)
@@ -4,7 +4,7 @@
 ARG_ENABLE("phar", "enable phar support", "no");
 
 if (PHP_PHAR != "no") {
-       EXTENSION("phar", "tar.c zip.c phar.c phar_object.c phar_path_check.c");
+       EXTENSION("phar", "tar.c zip.c stream.c dirstream.c phar.c phar_object.c phar_path_check.c");
                ADD_SOURCES(configure_module_dirname + "/lib", "zip_add.c zip_error.c zip_fclose.c \
                      zip_fread.c zip_open.c zip_source_filep.c  \
                      zip_strerror.c zip_close.c zip_error_get.c \
diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c
new file mode 100644 (file)
index 0000000..63d960c
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+  +----------------------------------------------------------------------+
+  | phar:// stream wrapper support                                       |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 2005-2008 The PHP Group                                |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt.                                 |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | license@php.net so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Authors: Gregory Beaver <cellog@php.net>                             |
+  |          Marcus Boerger <helly@php.net>                              |
+  +----------------------------------------------------------------------+
+*/
+
+#define PHAR_DIRSTREAM 1
+#include "phar_internal.h"
+#include "dirstream.h"
+
+BEGIN_EXTERN_C()
+void phar_dostat(phar_archive_data *phar, phar_entry_info *data, php_stream_statbuf *ssb, 
+                       zend_bool is_dir, char *alias, int alias_len TSRMLS_DC);
+END_EXTERN_C()
+
+php_stream_ops phar_dir_ops = {
+       phar_dir_write, /* write */
+       phar_dir_read,  /* read  */
+       phar_dir_close, /* close */
+       phar_dir_flush, /* flush */
+       "phar dir",
+       phar_dir_seek,  /* seek */
+       NULL,           /* cast */
+       phar_dir_stat,  /* stat */
+       NULL, /* set option */
+};
+
+/**
+ * Used for closedir($fp) where $fp is an opendir('phar://...') directory handle
+ */
+static int phar_dir_close(php_stream *stream, int close_handle TSRMLS_DC)  /* {{{ */
+{
+       HashTable *data = (HashTable *)stream->abstract;
+
+       if (data && data->arBuckets)
+       {
+               zend_hash_destroy(data);
+               data->arBuckets = 0;
+               FREE_HASHTABLE(data);
+               stream->abstract = NULL;
+       }
+       return 0;
+}
+/* }}} */
+
+/**
+ * Used for seeking on a phar directory handle
+ */
+static int phar_dir_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) /* {{{ */
+{
+       HashTable *data = (HashTable *)stream->abstract;
+
+       if (data)
+       {
+               if (whence == SEEK_END) {
+                       whence = SEEK_SET;
+                       offset = zend_hash_num_elements(data) + offset;
+               }
+               if (whence == SEEK_SET) {
+                       zend_hash_internal_pointer_reset(data);
+               }
+
+               if (offset < 0) {
+                       php_stream_wrapper_log_error(stream->wrapper, stream->flags TSRMLS_CC, "phar error: cannot seek because the resulting seek is negative");
+                       return -1;
+               } else {
+                       *newoffset = 0;
+                       while (*newoffset < offset && zend_hash_move_forward(data) == SUCCESS) {
+                               (*newoffset)++;
+                       }
+                       return 0;
+               }
+       }
+       return -1;
+}
+/* }}} */
+
+/**
+ * Used for readdir() on an opendir()ed phar directory handle
+ */
+static size_t phar_dir_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */
+{
+       size_t to_read;
+       HashTable *data = (HashTable *)stream->abstract;
+       char *key;
+       uint keylen;
+       ulong unused;
+
+       if (FAILURE == zend_hash_has_more_elements(data)) {
+               return 0;
+       }
+       if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(data, &key, &keylen, &unused, 0, NULL)) {
+               return 0;
+       }
+       zend_hash_move_forward(data);
+       to_read = MIN(keylen, count);
+       if (to_read == 0 || count < keylen) {
+               return 0;
+       }
+       memset(buf, 0, sizeof(php_stream_dirent));
+       memcpy(((php_stream_dirent *) buf)->d_name, key, to_read);
+       ((php_stream_dirent *) buf)->d_name[to_read + 1] = '\0';
+
+       return sizeof(php_stream_dirent);
+}
+/* }}} */
+
+/**
+ * Dummy: Used for writing to a phar directory (i.e. not used)
+ */
+static size_t phar_dir_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */
+{
+       return 0;
+}
+/* }}} */
+
+/**
+ * Dummy: Used for flushing writes to a phar directory (i.e. not used)
+ */
+static int phar_dir_flush(php_stream *stream TSRMLS_DC) /* {{{ */
+{
+       return EOF;
+}
+/* }}} */
+
+/**
+ * Stat a dir in a phar
+ */
+static int phar_dir_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */
+{
+       phar_entry_data *data = (phar_entry_data *)stream->abstract;
+
+       /* If ssb is NULL then someone is misbehaving */
+       if (!ssb) {
+               return -1;
+       }
+
+       phar_dostat(data->phar, data->internal_file, ssb, 0, data->phar->alias, data->phar->alias_len TSRMLS_CC);
+       return 0;
+}
+/* }}} */
+
+/**
+ * add an empty element with a char * key to a hash table, avoiding duplicates
+ *
+ * This is used to get a unique listing of virtual directories within a phar,
+ * for iterating over opendir()ed phar directories.
+ */
+static int phar_add_empty(HashTable *ht, char *arKey, uint nKeyLength)  /* {{{ */
+{
+       void *dummy = (void *) 1;
+
+       return zend_hash_update(ht, arKey, nKeyLength, &dummy, sizeof(void *), NULL);
+}
+/* }}} */
+
+/**
+ * Used for sorting directories alphabetically
+ */
+static int phar_compare_dir_name(const void *a, const void *b TSRMLS_DC)  /* {{{ */
+{
+       Bucket *f;
+       Bucket *s;
+       int result;
+       f = *((Bucket **) a);
+       s = *((Bucket **) b);
+
+#if (PHP_MAJOR_VERSION < 6)
+       result = zend_binary_strcmp(f->arKey, f->nKeyLength, s->arKey, s->nKeyLength);
+#else
+       result = zend_binary_strcmp(f->key.arKey.s, f->nKeyLength, s->key.arKey.s, s->nKeyLength);
+#endif
+
+       if (result < 0) {
+               return -1;
+       } else if (result > 0) {
+               return 1;
+       } else {
+               return 0;
+       }
+}
+/* }}} */
+
+/**
+ * Create a opendir() directory stream handle by iterating over each of the
+ * files in a phar and retrieving its relative path.  From this, construct
+ * a list of files/directories that are "in" the directory represented by dir
+ */
+static php_stream *phar_make_dirstream(char *dir, HashTable *manifest TSRMLS_DC) /* {{{ */
+{
+       HashTable *data;
+       int dirlen = strlen(dir);
+       char *save, *found, *key;
+       uint keylen;
+       ulong unused;
+       char *entry;
+       ALLOC_HASHTABLE(data);
+       zend_hash_init(data, 64, zend_get_hash_value, NULL, 0);
+
+       if (*dir == '/' && dirlen == 1 && (manifest->nNumOfElements == 0)) {
+               /* make empty root directory for empty phar */
+               efree(dir);
+               return php_stream_alloc(&phar_dir_ops, data, NULL, "r");
+       }
+       zend_hash_internal_pointer_reset(manifest);
+       while (FAILURE != zend_hash_has_more_elements(manifest)) {
+               if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(manifest, &key, &keylen, &unused, 0, NULL)) {
+                       break;
+               }
+               if (*dir == '/') {
+                       /* root directory */
+                       if (NULL != (found = (char *) memchr(key, '/', keylen))) {
+                               /* the entry has a path separator and is a subdirectory */
+                               entry = (char *) safe_emalloc(found - key, 1, 1);
+                               memcpy(entry, key, found - key);
+                               keylen = found - key;
+                               entry[keylen] = '\0';
+                       } else {
+                               entry = (char *) safe_emalloc(keylen, 1, 1);
+                               memcpy(entry, key, keylen);
+                               entry[keylen] = '\0';
+                       }
+                       goto PHAR_ADD_ENTRY;
+               } else {
+                       if (0 != memcmp(key, dir, dirlen)) {
+                               /* entry in directory not found */
+                               if (SUCCESS != zend_hash_move_forward(manifest)) {
+                                       break;
+                               }
+                               continue;
+                       } else {
+                               if (key[dirlen] != '/') {
+                                       if (SUCCESS != zend_hash_move_forward(manifest)) {
+                                               break;
+                                       }
+                                       continue;
+                               }
+                       }
+               }
+               save = key;
+               save += dirlen + 1; /* seek to just past the path separator */
+               if (NULL != (found = (char *) memchr(save, '/', keylen - dirlen - 1))) {
+                       /* is subdirectory */
+                       save -= dirlen + 1;
+                       entry = (char *) safe_emalloc(found - save + dirlen, 1, 1);
+                       memcpy(entry, save + dirlen + 1, found - save - dirlen - 1);
+                       keylen = found - save - dirlen - 1;
+                       entry[keylen] = '\0';
+               } else {
+                       /* is file */
+                       save -= dirlen + 1;
+                       entry = (char *) safe_emalloc(keylen - dirlen, 1, 1);
+                       memcpy(entry, save + dirlen + 1, keylen - dirlen - 1);
+                       entry[keylen - dirlen - 1] = '\0';
+                       keylen = keylen - dirlen - 1;
+               }
+PHAR_ADD_ENTRY:
+               phar_add_empty(data, entry, keylen);
+               efree(entry);
+               if (SUCCESS != zend_hash_move_forward(manifest)) {
+                       break;
+               }
+       }
+       if (FAILURE != zend_hash_has_more_elements(data)) {
+               efree(dir);
+               if (zend_hash_sort(data, zend_qsort, phar_compare_dir_name, 0 TSRMLS_CC) == FAILURE) {
+                       FREE_HASHTABLE(data);
+                       return NULL;
+               }
+               return php_stream_alloc(&phar_dir_ops, data, NULL, "r");
+       } else {
+               efree(dir);
+               FREE_HASHTABLE(data);
+               return NULL;
+       }
+}
+/* }}}*/
+
+/**
+ * Open a directory handle within a phar archive
+ */
+php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char *mode,
+                       int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */
+{
+       php_url *resource = NULL;
+       php_stream *ret;
+       char *internal_file, *key, *error, *plain_map;
+       uint keylen;
+       ulong unused;
+       phar_archive_data *phar;
+       phar_entry_info *entry;
+       uint host_len;
+
+       if ((resource = phar_open_url(wrapper, path, mode, options TSRMLS_CC)) == NULL) {
+               return NULL;
+       }
+
+       /* we must have at the very least phar://alias.phar/ */
+       if (!resource->scheme || !resource->host || !resource->path) {
+               if (resource->host && !resource->path) {
+                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", path, resource->host);
+                       php_url_free(resource);
+                       return NULL;
+               }
+               php_url_free(resource);
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\", must have at least phar://%s/", path, path);
+               return NULL;
+       }
+
+       if (strcasecmp("phar", resource->scheme)) {
+               php_url_free(resource);
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar url \"%s\"", path);
+               return NULL;
+       }
+
+       host_len = strlen(resource->host);
+       phar_request_initialize(TSRMLS_C);
+       if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource->host, host_len+1, (void **)&plain_map) == SUCCESS) {
+               spprintf(&internal_file, 0, "%s%s", plain_map, resource->path);
+               ret = php_stream_opendir(internal_file, options, context);
+               if (!ret) {
+                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" extracted from \"%s\" could not be opened", internal_file, resource->host);
+               }
+               php_url_free(resource);
+               efree(internal_file);
+               return ret;
+       }
+
+       internal_file = resource->path + 1; /* strip leading "/" */
+       if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) {
+               if (error) {
+                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
+                       efree(error);
+               }
+               php_url_free(resource);
+               return NULL;
+       }
+       if (error) {
+               efree(error);
+       }
+       if (*internal_file == '\0') {
+               /* root directory requested */
+               internal_file = estrndup(internal_file - 1, 1);
+               ret = phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC);
+               php_url_free(resource);
+               return ret;
+       }
+       if (!phar->manifest.arBuckets) {
+               php_url_free(resource);
+               return NULL;
+       }
+       if (SUCCESS == zend_hash_find(&phar->manifest, internal_file, strlen(internal_file), (void**)&entry)) {
+               php_url_free(resource);
+               return NULL;
+       } else {
+               /* search for directory */
+               zend_hash_internal_pointer_reset(&phar->manifest);
+               while (FAILURE != zend_hash_has_more_elements(&phar->manifest)) {
+                       if (HASH_KEY_NON_EXISTANT != 
+                                       zend_hash_get_current_key_ex(
+                                               &phar->manifest, &key, &keylen, &unused, 0, NULL)) {
+                               if (0 == memcmp(key, internal_file, strlen(internal_file))) {
+                                       /* directory found */
+                                       internal_file = estrndup(internal_file,
+                                                       strlen(internal_file));
+                                       php_url_free(resource);
+                                       return phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC);
+                               }
+                       }
+                       if (SUCCESS != zend_hash_move_forward(&phar->manifest)) {
+                               break;
+                       }
+               }
+       }
+
+       php_url_free(resource);
+       return NULL;
+}
+/* }}} */
diff --git a/ext/phar/dirstream.h b/ext/phar/dirstream.h
new file mode 100644 (file)
index 0000000..d06a45a
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+  +----------------------------------------------------------------------+
+  | phar php single-file executable PHP extension                        |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 2006-2007 The PHP Group                                |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt.                                 |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | license@php.net so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Authors: Gregory Beaver <cellog@php.net>                             |
+  |          Marcus Boerger <helly@php.net>                              |
+  +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+BEGIN_EXTERN_C()
+php_stream* phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
+
+#ifdef PHAR_DIRSTREAM
+php_url* phar_open_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options TSRMLS_DC);
+
+/* directory handlers */
+static size_t phar_dir_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC);
+static size_t phar_dir_read( php_stream *stream, char *buf, size_t count TSRMLS_DC);
+static int    phar_dir_close(php_stream *stream, int close_handle TSRMLS_DC);
+static int    phar_dir_flush(php_stream *stream TSRMLS_DC);
+static int    phar_dir_seek( php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC);
+static int    phar_dir_stat( php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC);
+#endif
+END_EXTERN_C()
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
index 8f0310c0a9373cfa62d67dd51b598b182c4563be..ef2c7d8c7e27f71f671b446032cde9d32db52047 100644 (file)
@@ -2,7 +2,7 @@
   +----------------------------------------------------------------------+
   | phar php single-file executable PHP extension                        |
   +----------------------------------------------------------------------+
-  | Copyright (c) 2005-2007 The PHP Group                                |
+  | Copyright (c) 2005-2008 The PHP Group                                |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
 
 /* $Id$ */
 
-#define PHAR_MAIN
+#define PHAR_MAIN 1
 #include "phar_internal.h"
 #include "SAPI.h"
+#include "php_stream_unlink.h"
 
 ZEND_DECLARE_MODULE_GLOBALS(phar)
 
-#if defined(PHP_VERSION_ID) && (PHP_VERSION_ID < 50400 || PHP_VERSION_ID >= 60000)
-
-static int _php_stream_unlink(char *url, int options, php_stream_context *context TSRMLS_DC)
-{
-       php_stream_wrapper *wrapper = php_stream_locate_url_wrapper(url, NULL, options TSRMLS_CC);
-
-       if (!wrapper || !wrapper->wops) {
-               return 0;
-       }
-
-       if (!wrapper->wops->unlink) {
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s does not allow unlinking", wrapper->wops->label ? wrapper->wops->label : "Wrapper");
-               return 0;
-       }
-       return wrapper->wops->unlink(wrapper, url, ENFORCE_SAFE_MODE | REPORT_ERRORS, context TSRMLS_CC);
-}
-
-#define php_stream_unlink(url, options, context) _php_stream_unlink((url), (options), (context) TSRMLS_CC)
-
-#endif
-
 /* if the original value is 0 (disabled), then allow setting/unsetting at will
    otherwise, only allow 1 (enabled), and error on disabling */
 ZEND_INI_MH(phar_ini_modify_handler) /* {{{ */
@@ -1899,91 +1879,6 @@ int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_le
        return SUCCESS;
 }
 /* }}} */
-       
-/**
- * Open a phar file for streams API
- */
-static php_url* phar_open_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options TSRMLS_DC) /* {{{ */
-{
-       php_url *resource;
-       char *arch = NULL, *entry = NULL, *error;
-       int arch_len, entry_len;
-
-       if (!strncasecmp(filename, "phar://", 7)) {
-               if (mode[0] == 'a') {
-                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: open mode append not supported");
-                       return NULL;                    
-               }               
-               if (phar_split_fname(filename, strlen(filename), &arch, &arch_len, &entry, &entry_len TSRMLS_CC) == FAILURE) {
-                       if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
-                               if (arch && !entry) {
-                                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", filename, arch);
-                                       arch = NULL;
-                               } else {
-                                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\" (cannot contain .phar.php and .phar.gz/.phar.bz2)", filename);
-                               }
-                       }
-                       if (arch) {
-                               efree(arch);
-                       }
-                       if (entry) {
-                               efree(entry);
-                       }
-                       return NULL;
-               }
-               resource = ecalloc(1, sizeof(php_url));
-               resource->scheme = estrndup("phar", 4);
-               resource->host = arch;
-
-               resource->path = entry;
-#if MBO_0
-               if (resource) {
-                       fprintf(stderr, "Alias:     %s\n", alias);
-                       fprintf(stderr, "Scheme:    %s\n", resource->scheme);
-/*                     fprintf(stderr, "User:      %s\n", resource->user);*/
-/*                     fprintf(stderr, "Pass:      %s\n", resource->pass ? "***" : NULL);*/
-                       fprintf(stderr, "Host:      %s\n", resource->host);
-/*                     fprintf(stderr, "Port:      %d\n", resource->port);*/
-                       fprintf(stderr, "Path:      %s\n", resource->path);
-/*                     fprintf(stderr, "Query:     %s\n", resource->query);*/
-/*                     fprintf(stderr, "Fragment:  %s\n", resource->fragment);*/
-               }
-#endif
-               if (PHAR_GLOBALS->request_init && PHAR_GLOBALS->phar_plain_map.arBuckets && zend_hash_exists(&(PHAR_GLOBALS->phar_plain_map), arch, arch_len+1)) {
-                       return resource;
-               }
-               if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) {
-                       if (PHAR_G(readonly)) {
-                               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by INI setting");
-                               php_url_free(resource);
-                               return NULL;
-                       }
-                       if (phar_open_or_create_filename(resource->host, arch_len, NULL, 0, options, NULL, &error TSRMLS_CC) == FAILURE)
-                       {
-                               if (error) {
-                                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
-                                       efree(error);
-                               }
-                               php_url_free(resource);
-                               return NULL;
-                       }
-               } else {
-                       if (phar_open_filename(resource->host, arch_len, NULL, 0, options, NULL, &error TSRMLS_CC) == FAILURE)
-                       {
-                               if (error) {
-                                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
-                                       efree(error);
-                               }
-                               php_url_free(resource);
-                               return NULL;
-                       }
-               }       
-               return resource;
-       }
-
-       return NULL;
-}
-/* }}} */
 
 /**
  * Invoked when a user calls Phar::mapPhar() from within an executing .phar
@@ -2038,53 +1933,10 @@ int phar_open_compiled_file(char *alias, int alias_len, char **error TSRMLS_DC)
 }
 /* }}} */
 
-static php_stream_ops phar_ops = {
-       phar_stream_write, /* write */
-       phar_stream_read,  /* read */
-       phar_stream_close, /* close */
-       phar_stream_flush, /* flush */
-       "phar stream",
-       phar_stream_seek,  /* seek */
-       NULL,              /* cast */
-       phar_stream_stat,  /* stat */
-       NULL, /* set option */
-};
-
-static php_stream_ops phar_dir_ops = {
-       phar_dir_write, /* write */
-       phar_dir_read,  /* read  */
-       phar_dir_close, /* close */
-       phar_dir_flush, /* flush */
-       "phar dir",
-       phar_dir_seek,  /* seek */
-       NULL,           /* cast */
-       phar_dir_stat,  /* stat */
-       NULL, /* set option */
-};
-
-static php_stream_wrapper_ops phar_stream_wops = {
-    phar_wrapper_open_url,
-    NULL,                  /* phar_wrapper_close */
-    NULL,                  /* phar_wrapper_stat, */
-    phar_wrapper_stat,     /* stat_url */
-    phar_wrapper_open_dir, /* opendir */
-    "phar",
-    phar_wrapper_unlink,   /* unlink */
-    phar_wrapper_rename,   /* rename */
-    NULL,                  /* create directory */
-    NULL,                  /* remove directory */
-};
-
-php_stream_wrapper php_stream_phar_wrapper =  {
-    &phar_stream_wops,
-    NULL,
-    0 /* is_url */
-};
-
 /**
  * Validate the CRC32 of a file opened from within the phar
  */
-static int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_data *idata, php_uint32 crc32 TSRMLS_DC) /* {{{ */
+int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_data *idata, php_uint32 crc32 TSRMLS_DC) /* {{{ */
 {
        php_uint32 crc = ~0;
        int len = idata->internal_file->uncompressed_filesize;
@@ -2330,379 +2182,6 @@ phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry,
        return entry;
 }
 
-/**
- * used for fopen('phar://...') and company
- */
-static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */
-{
-       phar_entry_data *idata;
-       char *internal_file;
-       char *error;
-       char *plain_map;
-       HashTable *pharcontext;
-       php_url *resource = NULL;
-       php_stream *fp, *fpf;
-       zval **pzoption, *metadata;
-       uint host_len;
-
-       if ((resource = phar_open_url(wrapper, path, mode, options TSRMLS_CC)) == NULL) {
-               return NULL;
-       }
-
-       /* we must have at the very least phar://alias.phar/internalfile.php */
-       if (!resource->scheme || !resource->host || !resource->path) {
-               php_url_free(resource);
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", path);
-               return NULL;
-       }
-
-       if (strcasecmp("phar", resource->scheme)) {
-               php_url_free(resource);
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", path);
-               return NULL;
-       }
-
-       host_len = strlen(resource->host);
-       phar_request_initialize(TSRMLS_C);
-       if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource->host, host_len+1, (void **)&plain_map) == SUCCESS) {
-               spprintf(&internal_file, 0, "%s%s", plain_map, resource->path);
-               fp = php_stream_open_wrapper_ex(internal_file, mode, options, opened_path, context);
-               if (!fp) {
-                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" extracted from \"%s\" could not be opened", internal_file, resource->host);
-               }
-               php_url_free(resource);
-               efree(internal_file);
-               return fp;
-       }
-
-       /* strip leading "/" */
-       internal_file = estrdup(resource->path + 1);
-       if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) {
-               if (NULL == (idata = phar_get_or_create_entry_data(resource->host, host_len, internal_file, strlen(internal_file), mode, &error TSRMLS_CC))) {
-                       if (error) {
-                               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
-                               efree(error);
-                       } else {
-                               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" could not be created in phar \"%s\"", internal_file, resource->host);
-                       }
-                       efree(internal_file);
-                       php_url_free(resource);
-                       return NULL;
-               }
-               if (error) {
-                       efree(error);
-               }
-               fpf = php_stream_alloc(&phar_ops, idata, NULL, mode);
-               php_url_free(resource);
-               efree(internal_file);
-               if (context && context->options && zend_hash_find(HASH_OF(context->options), "phar", sizeof("phar"), (void**)&pzoption) == SUCCESS) {
-                       pharcontext = HASH_OF(*pzoption);
-                       if (idata->internal_file->uncompressed_filesize == 0
-                               && idata->internal_file->compressed_filesize == 0
-                               && zend_hash_find(pharcontext, "compress", sizeof("compress"), (void**)&pzoption) == SUCCESS
-                               && Z_TYPE_PP(pzoption) == IS_LONG
-                               && (Z_LVAL_PP(pzoption) & ~PHAR_ENT_COMPRESSION_MASK) == 0
-                       ) {
-                               idata->internal_file->flags &= ~PHAR_ENT_COMPRESSION_MASK;
-                               idata->internal_file->flags |= Z_LVAL_PP(pzoption);
-                       }
-                       if (zend_hash_find(pharcontext, "metadata", sizeof("metadata"), (void**)&pzoption) == SUCCESS) {
-                               if (idata->internal_file->metadata) {
-                                       zval_ptr_dtor(&idata->internal_file->metadata);
-                                       idata->internal_file->metadata = NULL;
-                               }
-
-                               MAKE_STD_ZVAL(idata->internal_file->metadata);
-                               metadata = *pzoption;
-                               ZVAL_ZVAL(idata->internal_file->metadata, metadata, 1, 0);
-                               idata->phar->is_modified = 1;
-                       }
-               }
-               return fpf;
-       } else {
-               if ((FAILURE == phar_get_entry_data(&idata, resource->host, host_len, internal_file, strlen(internal_file), "r", &error TSRMLS_CC)) || !idata) {
-                       if (error) {
-                               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
-                               efree(error);
-                       }
-                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, resource->host);
-                       efree(internal_file);
-                       php_url_free(resource);
-                       return NULL;
-               }
-       }
-       php_url_free(resource);
-       
-#if MBO_0
-               fprintf(stderr, "Pharname:   %s\n", idata->phar->filename);
-               fprintf(stderr, "Filename:   %s\n", internal_file);
-               fprintf(stderr, "Entry:      %s\n", idata->internal_file->filename);
-               fprintf(stderr, "Size:       %u\n", idata->internal_file->uncompressed_filesize);
-               fprintf(stderr, "Compressed: %u\n", idata->internal_file->flags);
-               fprintf(stderr, "Offset:     %u\n", idata->internal_file->offset_within_phar);
-               fprintf(stderr, "Cached:     %s\n", idata->internal_file->filedata ? "yes" : "no");
-#endif
-
-       /* do we have the data already? */
-       if (idata->fp) {
-               fpf = php_stream_alloc(&phar_ops, idata, NULL, mode);
-               efree(internal_file);
-               return fpf;
-       }
-
-#if PHP_MAJOR_VERSION < 6
-       if (PG(safe_mode) && (!php_checkuid(idata->phar->fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
-               phar_entry_delref(idata TSRMLS_CC);
-               efree(internal_file);
-               return NULL;
-       }
-#endif
-       
-       if (php_check_open_basedir(idata->phar->fname TSRMLS_CC)) {
-               phar_entry_delref(idata TSRMLS_CC);
-               efree(internal_file);
-               return NULL;
-       }
-
-       fp = idata->phar->fp;
-
-       if (!idata->phar->is_zip && !fp) {
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot open phar \"%s\"", idata->phar->fname);
-               phar_entry_delref(idata TSRMLS_CC);
-               efree(internal_file);
-               return NULL;
-       }
-
-       if (!phar_open_jit(idata->phar, idata->internal_file, fp, &error, idata->for_write TSRMLS_CC)) {
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
-               efree(error);
-               phar_entry_delref(idata TSRMLS_CC);
-               efree(internal_file);
-               return NULL;
-       }
-       idata->fp = idata->internal_file->fp;
-       if (idata->fp == idata->phar->fp) {
-               idata->zero = idata->internal_file->offset_within_phar + idata->phar->internal_file_start;
-       }
-
-       /* check length, crc32 */
-       if (!idata->internal_file->is_crc_checked && phar_postprocess_file(wrapper, options, idata, idata->internal_file->crc32 TSRMLS_CC) != SUCCESS) {
-               /* already issued the error */
-               phar_entry_delref(idata TSRMLS_CC);
-               efree(internal_file);
-               return NULL;
-       }
-       idata->internal_file->is_crc_checked = 1;
-
-       fpf = php_stream_alloc(&phar_ops, idata, NULL, mode);
-       efree(internal_file);
-       return fpf;
-}
-/* }}} */
-
-/**
- * Used for fclose($fp) where $fp is a phar archive
- */
-static int phar_stream_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */
-{
-       phar_entry_delref((phar_entry_data *)stream->abstract TSRMLS_CC);
-
-       return 0;
-}
-/* }}} */
-
-/**
- * Used for closedir($fp) where $fp is an opendir('phar://...') directory handle
- */
-static int phar_dir_close(php_stream *stream, int close_handle TSRMLS_DC)  /* {{{ */
-{
-       HashTable *data = (HashTable *)stream->abstract;
-
-       if (data && data->arBuckets)
-       {
-               zend_hash_destroy(data);
-               data->arBuckets = 0;
-               FREE_HASHTABLE(data);
-               stream->abstract = NULL;
-       }
-       return 0;
-}
-/* }}} */
-
-/**
- * Used for seeking on a phar directory handle
- */
-static int phar_dir_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) /* {{{ */
-{
-       HashTable *data = (HashTable *)stream->abstract;
-
-       if (data)
-       {
-               if (whence == SEEK_END) {
-                       whence = SEEK_SET;
-                       offset = zend_hash_num_elements(data) + offset;
-               }
-               if (whence == SEEK_SET) {
-                       zend_hash_internal_pointer_reset(data);
-               }
-
-               if (offset < 0) {
-                       php_stream_wrapper_log_error(stream->wrapper, stream->flags TSRMLS_CC, "phar error: cannot seek because the resulting seek is negative");
-                       return -1;
-               } else {
-                       *newoffset = 0;
-                       while (*newoffset < offset && zend_hash_move_forward(data) == SUCCESS) {
-                               (*newoffset)++;
-                       }
-                       return 0;
-               }
-       }
-       return -1;
-}
-/* }}} */
-
-/**
- * used for fread($fp) and company on a fopen()ed phar file handle
- */
-static size_t phar_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */
-{
-       phar_entry_data *data = (phar_entry_data *)stream->abstract;
-       size_t got;
-       
-       if (data->internal_file->is_deleted) {
-               stream->eof = 1;
-               return 0;
-       }
-
-       /* use our proxy position */
-       php_stream_seek(data->fp, data->position + data->zero, SEEK_SET);
-
-       if (!data->zero) {
-               got = php_stream_read(data->fp, buf, count);
-               if (data->fp->eof) {
-                       stream->eof = 1;
-               }
-               /* note the position, and restore the stream for the next fp */
-               data->position = php_stream_tell(data->fp);
-       } else {
-               got = php_stream_read(data->fp, buf, MIN(count, data->internal_file->uncompressed_filesize - data->position));
-               data->position = php_stream_tell(data->fp) - data->zero;
-               stream->eof = (data->position == (off_t) data->internal_file->uncompressed_filesize);
-       }
-
-
-       return got;
-}
-/* }}} */
-
-/**
- * Used for readdir() on an opendir()ed phar directory handle
- */
-static size_t phar_dir_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */
-{
-       size_t to_read;
-       HashTable *data = (HashTable *)stream->abstract;
-       char *key;
-       uint keylen;
-       ulong unused;
-
-       if (FAILURE == zend_hash_has_more_elements(data)) {
-               return 0;
-       }
-       if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(data, &key, &keylen, &unused, 0, NULL)) {
-               return 0;
-       }
-       zend_hash_move_forward(data);
-       to_read = MIN(keylen, count);
-       if (to_read == 0 || count < keylen) {
-               return 0;
-       }
-       memset(buf, 0, sizeof(php_stream_dirent));
-       memcpy(((php_stream_dirent *) buf)->d_name, key, to_read);
-       ((php_stream_dirent *) buf)->d_name[to_read + 1] = '\0';
-
-       return sizeof(php_stream_dirent);
-}
-/* }}} */
-
-/**
- * Used for fseek($fp) on a phar file handle
- */
-static int phar_stream_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) /* {{{ */
-{
-       phar_entry_data *data = (phar_entry_data *)stream->abstract;
-
-       int res;
-       if (data->zero) {
-               off_t temp;
-               switch (whence) {
-                       case SEEK_END :
-                               temp = data->zero + data->internal_file->uncompressed_filesize + offset;
-                               break;
-                       case SEEK_CUR :
-                               temp = data->zero + data->position + offset;
-                               break;
-                       case SEEK_SET :
-                               temp = data->zero + offset;
-                               break;
-               }
-               if (temp > data->zero + (off_t) data->internal_file->uncompressed_filesize) {
-                       *newoffset = -1;
-                       return -1;
-               }
-               if (temp < data->zero) {
-                       *newoffset = -1;
-                       return -1;
-               }
-               res = php_stream_seek(data->fp, temp, SEEK_SET);
-               *newoffset = php_stream_tell(data->fp) - data->zero;
-               data->position = *newoffset;
-               return res;
-       }
-       if (whence != SEEK_SET) {
-               /* use our proxy position, so the relative stuff works */
-               php_stream_seek(data->fp, data->position, SEEK_SET);
-       }
-       /* now do the actual seek */
-       res = php_stream_seek(data->fp, offset, whence);
-       *newoffset = php_stream_tell(data->fp);
-       data->position = *newoffset;
-       return res;
-}
-/* }}} */
-
-/**
- * Used for writing to a phar file
- */
-static size_t phar_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */
-{
-       phar_entry_data *data = (phar_entry_data *) stream->abstract;
-
-       php_stream_seek(data->fp, data->position, SEEK_SET);
-       if (count != php_stream_write(data->fp, buf, count)) {
-               php_stream_wrapper_log_error(stream->wrapper, stream->flags TSRMLS_CC, "phar error: Could not write %d characters to \"%s\" in phar \"%s\"", (int) count, data->internal_file->filename, data->phar->fname);
-               return -1;
-       }
-       data->position = php_stream_tell(data->fp);
-       if (data->position > (off_t)data->internal_file->uncompressed_filesize) {
-               data->internal_file->uncompressed_filesize = data->position;
-       }
-       data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize;
-       data->internal_file->old_flags = data->internal_file->flags;
-       data->internal_file->is_modified = 1;
-       return count;
-}
-/* }}} */
-
-/**
- * Dummy: Used for writing to a phar directory (i.e. not used)
- */
-static size_t phar_dir_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */
-{
-       return 0;
-}
-/* }}} */
-
 static inline void phar_set_32(char *buffer, int var) /* {{{ */
 {
 #ifdef WORDS_BIGENDIAN
@@ -3393,702 +2872,6 @@ int phar_flush(phar_archive_data *archive, char *user_stub, long len, char **err
 }
 /* }}} */
 
-/**
- * Used to save work done on a writeable phar
- */
-static int phar_stream_flush(php_stream *stream TSRMLS_DC) /* {{{ */
-{
-       char *error;
-       int ret;
-       if (stream->mode[0] == 'w' || (stream->mode[0] == 'r' && stream->mode[1] == '+')) {
-               ret = phar_flush(((phar_entry_data *)stream->abstract)->phar, 0, 0, &error TSRMLS_CC);
-               if (error) {
-                       php_stream_wrapper_log_error(stream->wrapper, REPORT_ERRORS TSRMLS_CC, error);
-                       efree(error);
-               }
-               return ret;
-       } else {
-               return EOF;
-       }
-}
-/* }}} */
-
-/**
- * Dummy: Used for flushing writes to a phar directory (i.e. not used)
- */
-static int phar_dir_flush(php_stream *stream TSRMLS_DC) /* {{{ */
-{
-       return EOF;
-}
-/* }}} */
-
- /* {{{ phar_dostat */
-/**
- * stat an opened phar file handle stream, used by phar_stat()
- */
-static void phar_dostat(phar_archive_data *phar, phar_entry_info *data, php_stream_statbuf *ssb, 
-                       zend_bool is_dir, char *alias, int alias_len TSRMLS_DC)
-{
-       char *tmp;
-       int tmp_len;
-       memset(ssb, 0, sizeof(php_stream_statbuf));
-
-       if (!is_dir && !data->is_dir) {
-               ssb->sb.st_size = data->uncompressed_filesize;
-               ssb->sb.st_mode = data->flags & PHAR_ENT_PERM_MASK;
-               ssb->sb.st_mode |= S_IFREG; /* regular file */
-               /* timestamp is just the timestamp when this was added to the phar */
-#ifdef NETWARE
-               ssb->sb.st_mtime.tv_sec = data->timestamp;
-               ssb->sb.st_atime.tv_sec = data->timestamp;
-               ssb->sb.st_ctime.tv_sec = data->timestamp;
-#else
-               ssb->sb.st_mtime = data->timestamp;
-               ssb->sb.st_atime = data->timestamp;
-               ssb->sb.st_ctime = data->timestamp;
-#endif
-       } else if (!is_dir && data->is_dir && (data->is_tar || data->is_zip)) {
-               ssb->sb.st_size = 0;
-               ssb->sb.st_mode = data->flags & PHAR_ENT_PERM_MASK;
-               ssb->sb.st_mode |= S_IFDIR; /* regular directory */
-               /* timestamp is just the timestamp when this was added to the phar */
-#ifdef NETWARE
-               ssb->sb.st_mtime.tv_sec = data->timestamp;
-               ssb->sb.st_atime.tv_sec = data->timestamp;
-               ssb->sb.st_ctime.tv_sec = data->timestamp;
-#else
-               ssb->sb.st_mtime = data->timestamp;
-               ssb->sb.st_atime = data->timestamp;
-               ssb->sb.st_ctime = data->timestamp;
-#endif
-       } else {
-               ssb->sb.st_size = 0;
-               ssb->sb.st_mode = 0777;
-               ssb->sb.st_mode |= S_IFDIR; /* regular directory */
-#ifdef NETWARE
-               ssb->sb.st_mtime.tv_sec = phar->max_timestamp;
-               ssb->sb.st_atime.tv_sec = phar->max_timestamp;
-               ssb->sb.st_ctime.tv_sec = phar->max_timestamp;
-#else
-               ssb->sb.st_mtime = phar->max_timestamp;
-               ssb->sb.st_atime = phar->max_timestamp;
-               ssb->sb.st_ctime = phar->max_timestamp;
-#endif
-       }
-       if (!phar->is_writeable) {
-               ssb->sb.st_mode = (ssb->sb.st_mode & 0555) | (ssb->sb.st_mode & ~0777);
-       }
-
-       ssb->sb.st_nlink = 1;
-       ssb->sb.st_rdev = -1;
-       if (data) {
-               tmp_len = data->filename_len + alias_len;
-       } else {
-               tmp_len = alias_len + 1;
-       }
-       tmp = (char *) emalloc(tmp_len);
-       memcpy(tmp, alias, alias_len);
-       if (data) {
-               memcpy(tmp + alias_len, data->filename, data->filename_len);
-       } else {
-               *(tmp+alias_len) = '/';
-       }
-       /* this is only for APC, so use /dev/null device - no chance of conflict there! */
-       ssb->sb.st_dev = 0xc;
-       /* generate unique inode number for alias/filename, so no phars will conflict */
-       ssb->sb.st_ino = (unsigned short)zend_get_hash_value(tmp, tmp_len);
-       efree(tmp);
-#ifndef PHP_WIN32
-       ssb->sb.st_blksize = -1;
-       ssb->sb.st_blocks = -1;
-#endif
-}
-/* }}}*/
-
-/**
- * Stat an opened phar file handle
- */
-static int phar_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */
-{
-       phar_entry_data *data = (phar_entry_data *)stream->abstract;
-
-       /* If ssb is NULL then someone is misbehaving */
-       if (!ssb) {
-               return -1;
-       }
-
-       phar_dostat(data->phar, data->internal_file, ssb, 0, data->phar->alias, data->phar->alias_len TSRMLS_CC);
-       return 0;
-}
-/* }}} */
-
-/**
- * Stat a dir in a phar
- */
-static int phar_dir_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */
-{
-       phar_entry_data *data = (phar_entry_data *)stream->abstract;
-
-       /* If ssb is NULL then someone is misbehaving */
-       if (!ssb) {
-               return -1;
-       }
-
-       phar_dostat(data->phar, data->internal_file, ssb, 0, data->phar->alias, data->phar->alias_len TSRMLS_CC);
-       return 0;
-}
-/* }}} */
-
-/**
- * Stream wrapper stat implementation of stat()
- */
-static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags,
-                                 php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC) /* {{{ */
-{
-       php_url *resource = NULL;
-       char *internal_file, *key, *error, *plain_map;
-       uint keylen;
-       ulong unused;
-       phar_archive_data *phar;
-       phar_entry_info *entry;
-       uint host_len;
-       int retval;
-
-       if ((resource = phar_open_url(wrapper, url, "r", flags TSRMLS_CC)) == NULL) {
-               return -1;
-       }
-
-       /* we must have at the very least phar://alias.phar/internalfile.php */
-       if (!resource->scheme || !resource->host || !resource->path) {
-               php_url_free(resource);
-               php_stream_wrapper_log_error(wrapper, flags TSRMLS_CC, "phar error: invalid url \"%s\"", url);
-               return -1;
-       }
-
-       if (strcasecmp("phar", resource->scheme)) {
-               php_url_free(resource);
-               php_stream_wrapper_log_error(wrapper, flags TSRMLS_CC, "phar error: not a phar url \"%s\"", url);
-               return -1;
-       }
-
-       host_len = strlen(resource->host);
-       phar_request_initialize(TSRMLS_C);
-       if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource->host, host_len+1, (void **)&plain_map) == SUCCESS) {
-               spprintf(&internal_file, 0, "%s%s", plain_map, resource->path);
-               retval = php_stream_stat_path_ex(internal_file, flags, ssb, context);
-               if (retval == -1) {
-                       php_stream_wrapper_log_error(wrapper, 0/* TODO:options */ TSRMLS_CC, "phar error: file \"%s\" extracted from \"%s\" could not be opened", internal_file, resource->host);
-               }
-               php_url_free(resource);
-               efree(internal_file);
-               return retval;
-       }
-
-       internal_file = resource->path + 1; /* strip leading "/" */
-       /* find the phar in our trusty global hash indexed by alias (host of phar://blah.phar/file.whatever) */
-       if (FAILURE == phar_get_archive(&phar, resource->host, strlen(resource->host), NULL, 0, &error TSRMLS_CC)) {
-               php_url_free(resource);
-               if (error) {
-                       php_stream_wrapper_log_error(wrapper, flags TSRMLS_CC, error);
-                       efree(error);
-               }
-               return 0;
-       }
-       if (error) {
-               efree(error);
-       }
-       if (*internal_file == '\0') {
-               /* root directory requested */
-               phar_dostat(phar, NULL, ssb, 1, phar->alias, phar->alias_len TSRMLS_CC);
-               php_url_free(resource);
-               return 0;
-       }
-       if (!phar->manifest.arBuckets) {
-               php_url_free(resource);
-               return 0;
-       }
-       /* search through the manifest of files, and if we have an exact match, it's a file */
-       if (SUCCESS == zend_hash_find(&phar->manifest, internal_file, strlen(internal_file), (void**)&entry)) {
-               phar_dostat(phar, entry, ssb, 0, phar->alias, phar->alias_len TSRMLS_CC);
-       } else {
-               /* search for directory (partial match of a file) */
-               zend_hash_internal_pointer_reset(&phar->manifest);
-               while (FAILURE != zend_hash_has_more_elements(&phar->manifest)) {
-                       if (HASH_KEY_NON_EXISTANT !=
-                                       zend_hash_get_current_key_ex(
-                                               &phar->manifest, &key, &keylen, &unused, 0, NULL)) {
-                               if (0 == memcmp(internal_file, key, strlen(internal_file))) {
-                                       /* directory found, all dirs have the same stat */
-                                       if (key[strlen(internal_file)] == '/') {
-                                               phar_dostat(phar, NULL, ssb, 1, phar->alias, phar->alias_len TSRMLS_CC);
-                                               break;
-                                       }
-                               }
-                       }
-                       if (SUCCESS != zend_hash_move_forward(&phar->manifest)) {
-                               break;
-                       }
-               }
-       }
-
-       php_url_free(resource);
-       return 0;
-}
-/* }}} */
-
-/**
- * add an empty element with a char * key to a hash table, avoiding duplicates
- *
- * This is used to get a unique listing of virtual directories within a phar,
- * for iterating over opendir()ed phar directories.
- */
-static int phar_add_empty(HashTable *ht, char *arKey, uint nKeyLength)  /* {{{ */
-{
-       void *dummy = (void *) 1;
-
-       return zend_hash_update(ht, arKey, nKeyLength, &dummy, sizeof(void *), NULL);
-}
-/* }}} */
-
-/**
- * Used for sorting directories alphabetically
- */
-static int phar_compare_dir_name(const void *a, const void *b TSRMLS_DC)  /* {{{ */
-{
-       Bucket *f;
-       Bucket *s;
-       int result;
-       f = *((Bucket **) a);
-       s = *((Bucket **) b);
-
-#if (PHP_MAJOR_VERSION < 6)
-       result = zend_binary_strcmp(f->arKey, f->nKeyLength, s->arKey, s->nKeyLength);
-#else
-       result = zend_binary_strcmp(f->key.arKey.s, f->nKeyLength, s->key.arKey.s, s->nKeyLength);
-#endif
-
-       if (result < 0) {
-               return -1;
-       } else if (result > 0) {
-               return 1;
-       } else {
-               return 0;
-       }
-}
-/* }}} */
-
-/**
- * Create a opendir() directory stream handle by iterating over each of the
- * files in a phar and retrieving its relative path.  From this, construct
- * a list of files/directories that are "in" the directory represented by dir
- */
-static php_stream *phar_make_dirstream(char *dir, HashTable *manifest TSRMLS_DC) /* {{{ */
-{
-       HashTable *data;
-       int dirlen = strlen(dir);
-       char *save, *found, *key;
-       uint keylen;
-       ulong unused;
-       char *entry;
-       ALLOC_HASHTABLE(data);
-       zend_hash_init(data, 64, zend_get_hash_value, NULL, 0);
-
-       if (*dir == '/' && dirlen == 1 && (manifest->nNumOfElements == 0)) {
-               /* make empty root directory for empty phar */
-               efree(dir);
-               return php_stream_alloc(&phar_dir_ops, data, NULL, "r");
-       }
-       zend_hash_internal_pointer_reset(manifest);
-       while (FAILURE != zend_hash_has_more_elements(manifest)) {
-               if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(manifest, &key, &keylen, &unused, 0, NULL)) {
-                       break;
-               }
-               if (*dir == '/') {
-                       /* root directory */
-                       if (NULL != (found = (char *) memchr(key, '/', keylen))) {
-                               /* the entry has a path separator and is a subdirectory */
-                               entry = (char *) safe_emalloc(found - key, 1, 1);
-                               memcpy(entry, key, found - key);
-                               keylen = found - key;
-                               entry[keylen] = '\0';
-                       } else {
-                               entry = (char *) safe_emalloc(keylen, 1, 1);
-                               memcpy(entry, key, keylen);
-                               entry[keylen] = '\0';
-                       }
-                       goto PHAR_ADD_ENTRY;
-               } else {
-                       if (0 != memcmp(key, dir, dirlen)) {
-                               /* entry in directory not found */
-                               if (SUCCESS != zend_hash_move_forward(manifest)) {
-                                       break;
-                               }
-                               continue;
-                       } else {
-                               if (key[dirlen] != '/') {
-                                       if (SUCCESS != zend_hash_move_forward(manifest)) {
-                                               break;
-                                       }
-                                       continue;
-                               }
-                       }
-               }
-               save = key;
-               save += dirlen + 1; /* seek to just past the path separator */
-               if (NULL != (found = (char *) memchr(save, '/', keylen - dirlen - 1))) {
-                       /* is subdirectory */
-                       save -= dirlen + 1;
-                       entry = (char *) safe_emalloc(found - save + dirlen, 1, 1);
-                       memcpy(entry, save + dirlen + 1, found - save - dirlen - 1);
-                       keylen = found - save - dirlen - 1;
-                       entry[keylen] = '\0';
-               } else {
-                       /* is file */
-                       save -= dirlen + 1;
-                       entry = (char *) safe_emalloc(keylen - dirlen, 1, 1);
-                       memcpy(entry, save + dirlen + 1, keylen - dirlen - 1);
-                       entry[keylen - dirlen - 1] = '\0';
-                       keylen = keylen - dirlen - 1;
-               }
-PHAR_ADD_ENTRY:
-               phar_add_empty(data, entry, keylen);
-               efree(entry);
-               if (SUCCESS != zend_hash_move_forward(manifest)) {
-                       break;
-               }
-       }
-       if (FAILURE != zend_hash_has_more_elements(data)) {
-               efree(dir);
-               if (zend_hash_sort(data, zend_qsort, phar_compare_dir_name, 0 TSRMLS_CC) == FAILURE) {
-                       FREE_HASHTABLE(data);
-                       return NULL;
-               }
-               return php_stream_alloc(&phar_dir_ops, data, NULL, "r");
-       } else {
-               efree(dir);
-               FREE_HASHTABLE(data);
-               return NULL;
-       }
-}
-/* }}}*/
-
-/**
- * Unlink a file within a phar archive
- */
-static int phar_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) /* {{{ */
-{
-       php_url *resource;
-       char *internal_file, *error, *plain_map;
-       phar_entry_data *idata;
-       uint host_len;
-       int retval;
-
-       if ((resource = phar_open_url(wrapper, url, "rb", options TSRMLS_CC)) == NULL) {
-               return 0;
-       }
-
-       /* we must have at the very least phar://alias.phar/internalfile.php */
-       if (!resource->scheme || !resource->host || !resource->path) {
-               php_url_free(resource);
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url);
-               return 0;
-       }
-
-       if (strcasecmp("phar", resource->scheme)) {
-               php_url_free(resource);
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url);
-               return 0;
-       }
-
-       host_len = strlen(resource->host);
-       phar_request_initialize(TSRMLS_C);
-       if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource->host, host_len+1, (void **)&plain_map) == SUCCESS) {
-               spprintf(&internal_file, 0, "%s%s", plain_map, resource->path);
-               retval = php_stream_unlink(internal_file, options, context);
-               if (!retval) {
-                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" extracted from \"%s\" could not be opened", internal_file, resource->host);
-               }
-               php_url_free(resource);
-               efree(internal_file);
-               return retval;
-       }
-
-       if (PHAR_G(readonly)) {
-               php_url_free(resource);
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by INI setting");
-               return 0;
-       }
-
-       /* need to copy to strip leading "/", will get touched again */
-       internal_file = estrdup(resource->path + 1);
-       if (FAILURE == phar_get_entry_data(&idata, resource->host, strlen(resource->host), internal_file, strlen(internal_file), "r", &error TSRMLS_CC)) {
-               /* constraints of fp refcount were not met */
-               if (error) {
-                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
-                       efree(error);
-               }
-               efree(internal_file);
-               php_url_free(resource);
-               return 0;
-       }
-       if (error) {
-               efree(error);
-       }
-       if (!idata) {
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" is not a file in phar \"%s\", cannot unlink", internal_file, resource->host);
-               efree(internal_file);
-               php_url_free(resource);
-               return 0;
-       }
-       if (idata->internal_file->fp_refcount > 1) {
-               /* more than just our fp resource is open for this file */ 
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" in phar \"%s\", has open file pointers, cannot unlink", internal_file, resource->host);
-               efree(internal_file);
-               php_url_free(resource);
-               phar_entry_delref(idata TSRMLS_CC);
-               return 0;
-       }
-       php_url_free(resource);
-       efree(internal_file);
-       phar_entry_remove(idata, &error TSRMLS_CC);
-       if (error) {
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
-               efree(error);
-       }
-       return 1;
-}
-/* }}} */
-
-static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC) /* {{{ */
-{
-       php_url *resource_from, *resource_to;
-       char *error, *plain_map;
-       phar_archive_data *phar;
-       phar_entry_info *entry;
-       uint host_len;
-
-       error = NULL;
-       if (PHAR_G(readonly)) {
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by INI setting");
-               return 0;
-       }
-
-       if ((resource_from = phar_open_url(wrapper, url_from, "r+b", options TSRMLS_CC)) == NULL) {
-               return 0;
-       }
-       
-       if ((resource_to = phar_open_url(wrapper, url_to, "wb", options TSRMLS_CC)) == NULL) {
-               php_url_free(resource_from);
-               return 0;
-       }
-
-       /* we must have at the very least phar://alias.phar/internalfile.php */
-       if (!resource_from->scheme || !resource_from->host || !resource_from->path) {
-               php_url_free(resource_from);
-               php_url_free(resource_to);
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url_from);
-               return 0;
-       }
-       
-       if (!resource_to->scheme || !resource_to->host || !resource_to->path) {
-               php_url_free(resource_from);
-               php_url_free(resource_to);
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url_to);
-               return 0;
-       }
-
-       if (strcasecmp("phar", resource_from->scheme)) {
-               php_url_free(resource_from);
-               php_url_free(resource_to);
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url_from);
-               return 0;
-       }
-
-       if (strcasecmp("phar", resource_to->scheme)) {
-               php_url_free(resource_from);
-               php_url_free(resource_to);
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url_to);
-               return 0;
-       }
-
-       if (strcmp(resource_from->host, resource_to->host)) {
-               php_url_free(resource_from);
-               php_url_free(resource_to);
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\", not within the same phar archive", url_from, url_to);
-               return 0;
-       }
-
-       host_len = strlen(resource_from->host);
-       phar_request_initialize(TSRMLS_C);
-       if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource_from->host, host_len+1, (void **)&plain_map) == SUCCESS) {
-               /*TODO:use php_stream_rename() once available*/
-               php_url_free(resource_from);
-               php_url_free(resource_to);
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive", url_from, url_to);
-               return 0;
-       }
-
-       if (SUCCESS != phar_get_archive(&phar, resource_from->host, strlen(resource_from->host), NULL, 0, &error TSRMLS_CC)) {
-               php_url_free(resource_from);
-               php_url_free(resource_to);
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
-               efree(error);
-               return 0;
-       }
-
-       if (SUCCESS == zend_hash_find(&(phar->manifest), resource_from->path+1, strlen(resource_from->path)-1, (void **)&entry)) {
-               phar_entry_info new;
-
-               /* perform rename magic */
-               if (entry->is_deleted) {
-                       php_url_free(resource_from);
-                       php_url_free(resource_to);
-                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source has been deleted", url_from, url_to);
-                       return 0;
-               }
-               /* transfer all data over to the new entry */
-               memcpy((void *) &new, (void *) entry, sizeof(phar_entry_info));
-               /* mark the old one for deletion */
-               entry->is_deleted = 1;
-               entry->fp = NULL;
-               entry->metadata = 0;
-               entry->link = NULL;
-#if HAVE_PHAR_ZIP
-               entry->zip = NULL;
-#endif
-
-               zend_hash_add(&(phar->manifest), resource_to->path+1, strlen(resource_to->path)-1, (void **)&new, sizeof(phar_entry_info), (void **) &entry);
-               if (!entry->is_modified) {
-                       /* copy file contents into a new temp stream */
-                       if (!phar_open_jit(phar, entry, phar->fp, &error, 1 TSRMLS_CC)) {
-                               php_url_free(resource_from);
-                               php_url_free(resource_to);
-                               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
-                               efree(error);
-                               return 0;
-                       }
-               }
-               entry->is_modified = 1;
-               entry->filename = estrdup(resource_to->path+1);
-               entry->filename_len = strlen(entry->filename);
-               phar_flush(phar, 0, 0, &error TSRMLS_CC);
-               if (error) {
-                       php_url_free(resource_from);
-                       php_url_free(resource_to);
-                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
-                       efree(error);
-                       return 0;
-               }
-       }
-       php_url_free(resource_from);
-       php_url_free(resource_to);
-       return 1;
-}
-/* }}} */
-
-/**
- * Open a directory handle within a phar archive
- */
-static php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char *mode,
-                       int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */
-{
-       php_url *resource = NULL;
-       php_stream *ret;
-       char *internal_file, *key, *error, *plain_map;
-       uint keylen;
-       ulong unused;
-       phar_archive_data *phar;
-       phar_entry_info *entry;
-       uint host_len;
-
-       if ((resource = phar_open_url(wrapper, path, mode, options TSRMLS_CC)) == NULL) {
-               return NULL;
-       }
-
-       /* we must have at the very least phar://alias.phar/ */
-       if (!resource->scheme || !resource->host || !resource->path) {
-               if (resource->host && !resource->path) {
-                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", path, resource->host);
-                       php_url_free(resource);
-                       return NULL;
-               }
-               php_url_free(resource);
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\", must have at least phar://%s/", path, path);
-               return NULL;
-       }
-
-       if (strcasecmp("phar", resource->scheme)) {
-               php_url_free(resource);
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar url \"%s\"", path);
-               return NULL;
-       }
-
-       host_len = strlen(resource->host);
-       phar_request_initialize(TSRMLS_C);
-       if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource->host, host_len+1, (void **)&plain_map) == SUCCESS) {
-               spprintf(&internal_file, 0, "%s%s", plain_map, resource->path);
-               ret = php_stream_opendir(internal_file, options, context);
-               if (!ret) {
-                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" extracted from \"%s\" could not be opened", internal_file, resource->host);
-               }
-               php_url_free(resource);
-               efree(internal_file);
-               return ret;
-       }
-
-       internal_file = resource->path + 1; /* strip leading "/" */
-       if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) {
-               if (error) {
-                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
-                       efree(error);
-               }
-               php_url_free(resource);
-               return NULL;
-       }
-       if (error) {
-               efree(error);
-       }
-       if (*internal_file == '\0') {
-               /* root directory requested */
-               internal_file = estrndup(internal_file - 1, 1);
-               ret = phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC);
-               php_url_free(resource);
-               return ret;
-       }
-       if (!phar->manifest.arBuckets) {
-               php_url_free(resource);
-               return NULL;
-       }
-       if (SUCCESS == zend_hash_find(&phar->manifest, internal_file, strlen(internal_file), (void**)&entry)) {
-               php_url_free(resource);
-               return NULL;
-       } else {
-               /* search for directory */
-               zend_hash_internal_pointer_reset(&phar->manifest);
-               while (FAILURE != zend_hash_has_more_elements(&phar->manifest)) {
-                       if (HASH_KEY_NON_EXISTANT != 
-                                       zend_hash_get_current_key_ex(
-                                               &phar->manifest, &key, &keylen, &unused, 0, NULL)) {
-                               if (0 == memcmp(key, internal_file, strlen(internal_file))) {
-                                       /* directory found */
-                                       internal_file = estrndup(internal_file,
-                                                       strlen(internal_file));
-                                       php_url_free(resource);
-                                       return phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC);
-                               }
-                       }
-                       if (SUCCESS != zend_hash_move_forward(&phar->manifest)) {
-                               break;
-                       }
-               }
-       }
-
-       php_url_free(resource);
-       return NULL;
-}
-/* }}} */
-
 #ifdef COMPILE_DL_PHAR
 ZEND_GET_MODULE(phar)
 #endif
index ba2cca2a185e6d8525118c7bebc839de9b251be5..8b3bb966929d718092732774590cb4de0e54ecf0 100755 (executable)
@@ -317,31 +317,7 @@ int phar_zip_flush(phar_archive_data *archive, char *user_stub, long len, char *
 #ifdef PHAR_MAIN
 static void phar_fopen(INTERNAL_FUNCTION_PARAMETERS);
 static int phar_open_fp(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
-
-static php_url* phar_open_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options TSRMLS_DC);
-
-static php_stream* phar_wrapper_open_url(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
-static php_stream* phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
-static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC);
-static int phar_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC);
-static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC);
-
-/* file/stream handlers */
-static size_t phar_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC);
-static size_t phar_stream_read( php_stream *stream, char *buf, size_t count TSRMLS_DC);
-static int    phar_stream_close(php_stream *stream, int close_handle TSRMLS_DC);
-static int    phar_stream_flush(php_stream *stream TSRMLS_DC);
-static int    phar_stream_seek( php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC);
-static int    phar_stream_stat( php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC);
-
-/* directory handlers */
-static size_t phar_dir_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC);
-static size_t phar_dir_read( php_stream *stream, char *buf, size_t count TSRMLS_DC);
-static int    phar_dir_close(php_stream *stream, int close_handle TSRMLS_DC);
-static int    phar_dir_flush(php_stream *stream TSRMLS_DC);
-static int    phar_dir_seek( php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC);
-static int    phar_dir_stat( php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC);
-
+extern php_stream_wrapper php_stream_phar_wrapper;
 #endif
 
 int phar_archive_delref(phar_archive_data *phar TSRMLS_DC);
diff --git a/ext/phar/php_stream_unlink.h b/ext/phar/php_stream_unlink.h
new file mode 100644 (file)
index 0000000..31a126e
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef PHAR_UNLINK
+#define PHAR_UNLINK
+#if defined(PHP_VERSION_ID) && (PHP_VERSION_ID < 50400 || PHP_VERSION_ID >= 60000)
+
+static int _php_stream_unlink(char *url, int options, php_stream_context *context TSRMLS_DC)
+{
+       php_stream_wrapper *wrapper = php_stream_locate_url_wrapper(url, NULL, options TSRMLS_CC);
+
+       if (!wrapper || !wrapper->wops) {
+               return 0;
+       }
+
+       if (!wrapper->wops->unlink) {
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s does not allow unlinking", wrapper->wops->label ? wrapper->wops->label : "Wrapper");
+               return 0;
+       }
+       return wrapper->wops->unlink(wrapper, url, ENFORCE_SAFE_MODE | REPORT_ERRORS, context TSRMLS_CC);
+}
+
+#define php_stream_unlink(url, options, context) _php_stream_unlink((url), (options), (context) TSRMLS_CC)
+
+#endif /* defined(PHP_VERSION_ID) && (PHP_VERSION_ID < 50400 || PHP_VERSION_ID >= 60000) */
+#endif /* PHAR_UNLINK */
diff --git a/ext/phar/stream.c b/ext/phar/stream.c
new file mode 100644 (file)
index 0000000..4e59ddb
--- /dev/null
@@ -0,0 +1,855 @@
+/*
+  +----------------------------------------------------------------------+
+  | phar:// stream wrapper support                                       |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 2005-2008 The PHP Group                                |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt.                                 |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | license@php.net so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Authors: Gregory Beaver <cellog@php.net>                             |
+  |          Marcus Boerger <helly@php.net>                              |
+  +----------------------------------------------------------------------+
+*/
+
+#define PHAR_STREAM 1
+#include "phar_internal.h"
+#include "php_stream_unlink.h"
+#include "stream.h"
+#include "dirstream.h"
+
+php_stream_ops phar_ops = {
+       phar_stream_write, /* write */
+       phar_stream_read,  /* read */
+       phar_stream_close, /* close */
+       phar_stream_flush, /* flush */
+       "phar stream",
+       phar_stream_seek,  /* seek */
+       NULL,              /* cast */
+       phar_stream_stat,  /* stat */
+       NULL, /* set option */
+};
+
+php_stream_wrapper_ops phar_stream_wops = {
+    phar_wrapper_open_url,
+    NULL,                  /* phar_wrapper_close */
+    NULL,                  /* phar_wrapper_stat, */
+    phar_wrapper_stat,     /* stat_url */
+    phar_wrapper_open_dir, /* opendir */
+    "phar",
+    phar_wrapper_unlink,   /* unlink */
+    phar_wrapper_rename,   /* rename */
+    NULL,                  /* create directory */
+    NULL,                  /* remove directory */
+};
+
+php_stream_wrapper php_stream_phar_wrapper =  {
+    &phar_stream_wops,
+    NULL,
+    0 /* is_url */
+};
+
+/**
+ * Open a phar file for streams API
+ */
+php_url* phar_open_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options TSRMLS_DC) /* {{{ */
+{
+       php_url *resource;
+       char *arch = NULL, *entry = NULL, *error;
+       int arch_len, entry_len;
+
+       if (!strncasecmp(filename, "phar://", 7)) {
+               if (mode[0] == 'a') {
+                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: open mode append not supported");
+                       return NULL;                    
+               }               
+               if (phar_split_fname(filename, strlen(filename), &arch, &arch_len, &entry, &entry_len TSRMLS_CC) == FAILURE) {
+                       if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
+                               if (arch && !entry) {
+                                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", filename, arch);
+                                       arch = NULL;
+                               } else {
+                                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\" (cannot contain .phar.php and .phar.gz/.phar.bz2)", filename);
+                               }
+                       }
+                       if (arch) {
+                               efree(arch);
+                       }
+                       if (entry) {
+                               efree(entry);
+                       }
+                       return NULL;
+               }
+               resource = ecalloc(1, sizeof(php_url));
+               resource->scheme = estrndup("phar", 4);
+               resource->host = arch;
+
+               resource->path = entry;
+#if MBO_0
+               if (resource) {
+                       fprintf(stderr, "Alias:     %s\n", alias);
+                       fprintf(stderr, "Scheme:    %s\n", resource->scheme);
+/*                     fprintf(stderr, "User:      %s\n", resource->user);*/
+/*                     fprintf(stderr, "Pass:      %s\n", resource->pass ? "***" : NULL);*/
+                       fprintf(stderr, "Host:      %s\n", resource->host);
+/*                     fprintf(stderr, "Port:      %d\n", resource->port);*/
+                       fprintf(stderr, "Path:      %s\n", resource->path);
+/*                     fprintf(stderr, "Query:     %s\n", resource->query);*/
+/*                     fprintf(stderr, "Fragment:  %s\n", resource->fragment);*/
+               }
+#endif
+               if (PHAR_GLOBALS->request_init && PHAR_GLOBALS->phar_plain_map.arBuckets && zend_hash_exists(&(PHAR_GLOBALS->phar_plain_map), arch, arch_len+1)) {
+                       return resource;
+               }
+               if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) {
+                       if (PHAR_G(readonly)) {
+                               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by INI setting");
+                               php_url_free(resource);
+                               return NULL;
+                       }
+                       if (phar_open_or_create_filename(resource->host, arch_len, NULL, 0, options, NULL, &error TSRMLS_CC) == FAILURE)
+                       {
+                               if (error) {
+                                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
+                                       efree(error);
+                               }
+                               php_url_free(resource);
+                               return NULL;
+                       }
+               } else {
+                       if (phar_open_filename(resource->host, arch_len, NULL, 0, options, NULL, &error TSRMLS_CC) == FAILURE)
+                       {
+                               if (error) {
+                                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
+                                       efree(error);
+                               }
+                               php_url_free(resource);
+                               return NULL;
+                       }
+               }       
+               return resource;
+       }
+
+       return NULL;
+}
+/* }}} */
+
+/**
+ * used for fopen('phar://...') and company
+ */
+static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */
+{
+       phar_entry_data *idata;
+       char *internal_file;
+       char *error;
+       char *plain_map;
+       HashTable *pharcontext;
+       php_url *resource = NULL;
+       php_stream *fp, *fpf;
+       zval **pzoption, *metadata;
+       uint host_len;
+
+       if ((resource = phar_open_url(wrapper, path, mode, options TSRMLS_CC)) == NULL) {
+               return NULL;
+       }
+
+       /* we must have at the very least phar://alias.phar/internalfile.php */
+       if (!resource->scheme || !resource->host || !resource->path) {
+               php_url_free(resource);
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", path);
+               return NULL;
+       }
+
+       if (strcasecmp("phar", resource->scheme)) {
+               php_url_free(resource);
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", path);
+               return NULL;
+       }
+
+       host_len = strlen(resource->host);
+       phar_request_initialize(TSRMLS_C);
+       if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource->host, host_len+1, (void **)&plain_map) == SUCCESS) {
+               spprintf(&internal_file, 0, "%s%s", plain_map, resource->path);
+               fp = php_stream_open_wrapper_ex(internal_file, mode, options, opened_path, context);
+               if (!fp) {
+                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" extracted from \"%s\" could not be opened", internal_file, resource->host);
+               }
+               php_url_free(resource);
+               efree(internal_file);
+               return fp;
+       }
+
+       /* strip leading "/" */
+       internal_file = estrdup(resource->path + 1);
+       if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) {
+               if (NULL == (idata = phar_get_or_create_entry_data(resource->host, host_len, internal_file, strlen(internal_file), mode, &error TSRMLS_CC))) {
+                       if (error) {
+                               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
+                               efree(error);
+                       } else {
+                               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" could not be created in phar \"%s\"", internal_file, resource->host);
+                       }
+                       efree(internal_file);
+                       php_url_free(resource);
+                       return NULL;
+               }
+               if (error) {
+                       efree(error);
+               }
+               fpf = php_stream_alloc(&phar_ops, idata, NULL, mode);
+               php_url_free(resource);
+               efree(internal_file);
+               if (context && context->options && zend_hash_find(HASH_OF(context->options), "phar", sizeof("phar"), (void**)&pzoption) == SUCCESS) {
+                       pharcontext = HASH_OF(*pzoption);
+                       if (idata->internal_file->uncompressed_filesize == 0
+                               && idata->internal_file->compressed_filesize == 0
+                               && zend_hash_find(pharcontext, "compress", sizeof("compress"), (void**)&pzoption) == SUCCESS
+                               && Z_TYPE_PP(pzoption) == IS_LONG
+                               && (Z_LVAL_PP(pzoption) & ~PHAR_ENT_COMPRESSION_MASK) == 0
+                       ) {
+                               idata->internal_file->flags &= ~PHAR_ENT_COMPRESSION_MASK;
+                               idata->internal_file->flags |= Z_LVAL_PP(pzoption);
+                       }
+                       if (zend_hash_find(pharcontext, "metadata", sizeof("metadata"), (void**)&pzoption) == SUCCESS) {
+                               if (idata->internal_file->metadata) {
+                                       zval_ptr_dtor(&idata->internal_file->metadata);
+                                       idata->internal_file->metadata = NULL;
+                               }
+
+                               MAKE_STD_ZVAL(idata->internal_file->metadata);
+                               metadata = *pzoption;
+                               ZVAL_ZVAL(idata->internal_file->metadata, metadata, 1, 0);
+                               idata->phar->is_modified = 1;
+                       }
+               }
+               return fpf;
+       } else {
+               if ((FAILURE == phar_get_entry_data(&idata, resource->host, host_len, internal_file, strlen(internal_file), "r", &error TSRMLS_CC)) || !idata) {
+                       if (error) {
+                               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
+                               efree(error);
+                       }
+                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, resource->host);
+                       efree(internal_file);
+                       php_url_free(resource);
+                       return NULL;
+               }
+       }
+       php_url_free(resource);
+       
+#if MBO_0
+               fprintf(stderr, "Pharname:   %s\n", idata->phar->filename);
+               fprintf(stderr, "Filename:   %s\n", internal_file);
+               fprintf(stderr, "Entry:      %s\n", idata->internal_file->filename);
+               fprintf(stderr, "Size:       %u\n", idata->internal_file->uncompressed_filesize);
+               fprintf(stderr, "Compressed: %u\n", idata->internal_file->flags);
+               fprintf(stderr, "Offset:     %u\n", idata->internal_file->offset_within_phar);
+               fprintf(stderr, "Cached:     %s\n", idata->internal_file->filedata ? "yes" : "no");
+#endif
+
+       /* do we have the data already? */
+       if (idata->fp) {
+               fpf = php_stream_alloc(&phar_ops, idata, NULL, mode);
+               efree(internal_file);
+               return fpf;
+       }
+
+#if PHP_MAJOR_VERSION < 6
+       if (PG(safe_mode) && (!php_checkuid(idata->phar->fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
+               phar_entry_delref(idata TSRMLS_CC);
+               efree(internal_file);
+               return NULL;
+       }
+#endif
+       
+       if (php_check_open_basedir(idata->phar->fname TSRMLS_CC)) {
+               phar_entry_delref(idata TSRMLS_CC);
+               efree(internal_file);
+               return NULL;
+       }
+
+       fp = idata->phar->fp;
+
+       if (!idata->phar->is_zip && !fp) {
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot open phar \"%s\"", idata->phar->fname);
+               phar_entry_delref(idata TSRMLS_CC);
+               efree(internal_file);
+               return NULL;
+       }
+
+       if (!phar_open_jit(idata->phar, idata->internal_file, fp, &error, idata->for_write TSRMLS_CC)) {
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
+               efree(error);
+               phar_entry_delref(idata TSRMLS_CC);
+               efree(internal_file);
+               return NULL;
+       }
+       idata->fp = idata->internal_file->fp;
+       if (idata->fp == idata->phar->fp) {
+               idata->zero = idata->internal_file->offset_within_phar + idata->phar->internal_file_start;
+       }
+
+       /* check length, crc32 */
+       if (!idata->internal_file->is_crc_checked && phar_postprocess_file(wrapper, options, idata, idata->internal_file->crc32 TSRMLS_CC) != SUCCESS) {
+               /* already issued the error */
+               phar_entry_delref(idata TSRMLS_CC);
+               efree(internal_file);
+               return NULL;
+       }
+       idata->internal_file->is_crc_checked = 1;
+
+       fpf = php_stream_alloc(&phar_ops, idata, NULL, mode);
+       efree(internal_file);
+       return fpf;
+}
+/* }}} */
+
+/**
+ * Used for fclose($fp) where $fp is a phar archive
+ */
+static int phar_stream_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */
+{
+       phar_entry_delref((phar_entry_data *)stream->abstract TSRMLS_CC);
+
+       return 0;
+}
+/* }}} */
+
+/**
+ * used for fread($fp) and company on a fopen()ed phar file handle
+ */
+static size_t phar_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */
+{
+       phar_entry_data *data = (phar_entry_data *)stream->abstract;
+       size_t got;
+       
+       if (data->internal_file->is_deleted) {
+               stream->eof = 1;
+               return 0;
+       }
+
+       /* use our proxy position */
+       php_stream_seek(data->fp, data->position + data->zero, SEEK_SET);
+
+       if (!data->zero) {
+               got = php_stream_read(data->fp, buf, count);
+               if (data->fp->eof) {
+                       stream->eof = 1;
+               }
+               /* note the position, and restore the stream for the next fp */
+               data->position = php_stream_tell(data->fp);
+       } else {
+               got = php_stream_read(data->fp, buf, MIN(count, data->internal_file->uncompressed_filesize - data->position));
+               data->position = php_stream_tell(data->fp) - data->zero;
+               stream->eof = (data->position == (off_t) data->internal_file->uncompressed_filesize);
+       }
+
+
+       return got;
+}
+/* }}} */
+
+/**
+ * Used for fseek($fp) on a phar file handle
+ */
+static int phar_stream_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) /* {{{ */
+{
+       phar_entry_data *data = (phar_entry_data *)stream->abstract;
+
+       int res;
+       if (data->zero) {
+               off_t temp;
+               switch (whence) {
+                       case SEEK_END :
+                               temp = data->zero + data->internal_file->uncompressed_filesize + offset;
+                               break;
+                       case SEEK_CUR :
+                               temp = data->zero + data->position + offset;
+                               break;
+                       case SEEK_SET :
+                               temp = data->zero + offset;
+                               break;
+               }
+               if (temp > data->zero + (off_t) data->internal_file->uncompressed_filesize) {
+                       *newoffset = -1;
+                       return -1;
+               }
+               if (temp < data->zero) {
+                       *newoffset = -1;
+                       return -1;
+               }
+               res = php_stream_seek(data->fp, temp, SEEK_SET);
+               *newoffset = php_stream_tell(data->fp) - data->zero;
+               data->position = *newoffset;
+               return res;
+       }
+       if (whence != SEEK_SET) {
+               /* use our proxy position, so the relative stuff works */
+               php_stream_seek(data->fp, data->position, SEEK_SET);
+       }
+       /* now do the actual seek */
+       res = php_stream_seek(data->fp, offset, whence);
+       *newoffset = php_stream_tell(data->fp);
+       data->position = *newoffset;
+       return res;
+}
+/* }}} */
+
+/**
+ * Used for writing to a phar file
+ */
+static size_t phar_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */
+{
+       phar_entry_data *data = (phar_entry_data *) stream->abstract;
+
+       php_stream_seek(data->fp, data->position, SEEK_SET);
+       if (count != php_stream_write(data->fp, buf, count)) {
+               php_stream_wrapper_log_error(stream->wrapper, stream->flags TSRMLS_CC, "phar error: Could not write %d characters to \"%s\" in phar \"%s\"", (int) count, data->internal_file->filename, data->phar->fname);
+               return -1;
+       }
+       data->position = php_stream_tell(data->fp);
+       if (data->position > (off_t)data->internal_file->uncompressed_filesize) {
+               data->internal_file->uncompressed_filesize = data->position;
+       }
+       data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize;
+       data->internal_file->old_flags = data->internal_file->flags;
+       data->internal_file->is_modified = 1;
+       return count;
+}
+/* }}} */
+
+/**
+ * Used to save work done on a writeable phar
+ */
+static int phar_stream_flush(php_stream *stream TSRMLS_DC) /* {{{ */
+{
+       char *error;
+       int ret;
+       if (stream->mode[0] == 'w' || (stream->mode[0] == 'r' && stream->mode[1] == '+')) {
+               ret = phar_flush(((phar_entry_data *)stream->abstract)->phar, 0, 0, &error TSRMLS_CC);
+               if (error) {
+                       php_stream_wrapper_log_error(stream->wrapper, REPORT_ERRORS TSRMLS_CC, error);
+                       efree(error);
+               }
+               return ret;
+       } else {
+               return EOF;
+       }
+}
+/* }}} */
+
+ /* {{{ phar_dostat */
+/**
+ * stat an opened phar file handle stream, used by phar_stat()
+ */
+void phar_dostat(phar_archive_data *phar, phar_entry_info *data, php_stream_statbuf *ssb, 
+                       zend_bool is_dir, char *alias, int alias_len TSRMLS_DC)
+{
+       char *tmp;
+       int tmp_len;
+       memset(ssb, 0, sizeof(php_stream_statbuf));
+
+       if (!is_dir && !data->is_dir) {
+               ssb->sb.st_size = data->uncompressed_filesize;
+               ssb->sb.st_mode = data->flags & PHAR_ENT_PERM_MASK;
+               ssb->sb.st_mode |= S_IFREG; /* regular file */
+               /* timestamp is just the timestamp when this was added to the phar */
+#ifdef NETWARE
+               ssb->sb.st_mtime.tv_sec = data->timestamp;
+               ssb->sb.st_atime.tv_sec = data->timestamp;
+               ssb->sb.st_ctime.tv_sec = data->timestamp;
+#else
+               ssb->sb.st_mtime = data->timestamp;
+               ssb->sb.st_atime = data->timestamp;
+               ssb->sb.st_ctime = data->timestamp;
+#endif
+       } else if (!is_dir && data->is_dir && (data->is_tar || data->is_zip)) {
+               ssb->sb.st_size = 0;
+               ssb->sb.st_mode = data->flags & PHAR_ENT_PERM_MASK;
+               ssb->sb.st_mode |= S_IFDIR; /* regular directory */
+               /* timestamp is just the timestamp when this was added to the phar */
+#ifdef NETWARE
+               ssb->sb.st_mtime.tv_sec = data->timestamp;
+               ssb->sb.st_atime.tv_sec = data->timestamp;
+               ssb->sb.st_ctime.tv_sec = data->timestamp;
+#else
+               ssb->sb.st_mtime = data->timestamp;
+               ssb->sb.st_atime = data->timestamp;
+               ssb->sb.st_ctime = data->timestamp;
+#endif
+       } else {
+               ssb->sb.st_size = 0;
+               ssb->sb.st_mode = 0777;
+               ssb->sb.st_mode |= S_IFDIR; /* regular directory */
+#ifdef NETWARE
+               ssb->sb.st_mtime.tv_sec = phar->max_timestamp;
+               ssb->sb.st_atime.tv_sec = phar->max_timestamp;
+               ssb->sb.st_ctime.tv_sec = phar->max_timestamp;
+#else
+               ssb->sb.st_mtime = phar->max_timestamp;
+               ssb->sb.st_atime = phar->max_timestamp;
+               ssb->sb.st_ctime = phar->max_timestamp;
+#endif
+       }
+       if (!phar->is_writeable) {
+               ssb->sb.st_mode = (ssb->sb.st_mode & 0555) | (ssb->sb.st_mode & ~0777);
+       }
+
+       ssb->sb.st_nlink = 1;
+       ssb->sb.st_rdev = -1;
+       if (data) {
+               tmp_len = data->filename_len + alias_len;
+       } else {
+               tmp_len = alias_len + 1;
+       }
+       tmp = (char *) emalloc(tmp_len);
+       memcpy(tmp, alias, alias_len);
+       if (data) {
+               memcpy(tmp + alias_len, data->filename, data->filename_len);
+       } else {
+               *(tmp+alias_len) = '/';
+       }
+       /* this is only for APC, so use /dev/null device - no chance of conflict there! */
+       ssb->sb.st_dev = 0xc;
+       /* generate unique inode number for alias/filename, so no phars will conflict */
+       ssb->sb.st_ino = (unsigned short)zend_get_hash_value(tmp, tmp_len);
+       efree(tmp);
+#ifndef PHP_WIN32
+       ssb->sb.st_blksize = -1;
+       ssb->sb.st_blocks = -1;
+#endif
+}
+/* }}}*/
+
+/**
+ * Stat an opened phar file handle
+ */
+static int phar_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */
+{
+       phar_entry_data *data = (phar_entry_data *)stream->abstract;
+
+       /* If ssb is NULL then someone is misbehaving */
+       if (!ssb) {
+               return -1;
+       }
+
+       phar_dostat(data->phar, data->internal_file, ssb, 0, data->phar->alias, data->phar->alias_len TSRMLS_CC);
+       return 0;
+}
+/* }}} */
+
+/**
+ * Stream wrapper stat implementation of stat()
+ */
+static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags,
+                                 php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC) /* {{{ */
+{
+       php_url *resource = NULL;
+       char *internal_file, *key, *error, *plain_map;
+       uint keylen;
+       ulong unused;
+       phar_archive_data *phar;
+       phar_entry_info *entry;
+       uint host_len;
+       int retval;
+
+       if ((resource = phar_open_url(wrapper, url, "r", flags TSRMLS_CC)) == NULL) {
+               return -1;
+       }
+
+       /* we must have at the very least phar://alias.phar/internalfile.php */
+       if (!resource->scheme || !resource->host || !resource->path) {
+               php_url_free(resource);
+               php_stream_wrapper_log_error(wrapper, flags TSRMLS_CC, "phar error: invalid url \"%s\"", url);
+               return -1;
+       }
+
+       if (strcasecmp("phar", resource->scheme)) {
+               php_url_free(resource);
+               php_stream_wrapper_log_error(wrapper, flags TSRMLS_CC, "phar error: not a phar url \"%s\"", url);
+               return -1;
+       }
+
+       host_len = strlen(resource->host);
+       phar_request_initialize(TSRMLS_C);
+       if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource->host, host_len+1, (void **)&plain_map) == SUCCESS) {
+               spprintf(&internal_file, 0, "%s%s", plain_map, resource->path);
+               retval = php_stream_stat_path_ex(internal_file, flags, ssb, context);
+               if (retval == -1) {
+                       php_stream_wrapper_log_error(wrapper, 0/* TODO:options */ TSRMLS_CC, "phar error: file \"%s\" extracted from \"%s\" could not be opened", internal_file, resource->host);
+               }
+               php_url_free(resource);
+               efree(internal_file);
+               return retval;
+       }
+
+       internal_file = resource->path + 1; /* strip leading "/" */
+       /* find the phar in our trusty global hash indexed by alias (host of phar://blah.phar/file.whatever) */
+       if (FAILURE == phar_get_archive(&phar, resource->host, strlen(resource->host), NULL, 0, &error TSRMLS_CC)) {
+               php_url_free(resource);
+               if (error) {
+                       php_stream_wrapper_log_error(wrapper, flags TSRMLS_CC, error);
+                       efree(error);
+               }
+               return 0;
+       }
+       if (error) {
+               efree(error);
+       }
+       if (*internal_file == '\0') {
+               /* root directory requested */
+               phar_dostat(phar, NULL, ssb, 1, phar->alias, phar->alias_len TSRMLS_CC);
+               php_url_free(resource);
+               return 0;
+       }
+       if (!phar->manifest.arBuckets) {
+               php_url_free(resource);
+               return 0;
+       }
+       /* search through the manifest of files, and if we have an exact match, it's a file */
+       if (SUCCESS == zend_hash_find(&phar->manifest, internal_file, strlen(internal_file), (void**)&entry)) {
+               phar_dostat(phar, entry, ssb, 0, phar->alias, phar->alias_len TSRMLS_CC);
+       } else {
+               /* search for directory (partial match of a file) */
+               zend_hash_internal_pointer_reset(&phar->manifest);
+               while (FAILURE != zend_hash_has_more_elements(&phar->manifest)) {
+                       if (HASH_KEY_NON_EXISTANT !=
+                                       zend_hash_get_current_key_ex(
+                                               &phar->manifest, &key, &keylen, &unused, 0, NULL)) {
+                               if (0 == memcmp(internal_file, key, strlen(internal_file))) {
+                                       /* directory found, all dirs have the same stat */
+                                       if (key[strlen(internal_file)] == '/') {
+                                               phar_dostat(phar, NULL, ssb, 1, phar->alias, phar->alias_len TSRMLS_CC);
+                                               break;
+                                       }
+                               }
+                       }
+                       if (SUCCESS != zend_hash_move_forward(&phar->manifest)) {
+                               break;
+                       }
+               }
+       }
+
+       php_url_free(resource);
+       return 0;
+}
+/* }}} */
+
+/**
+ * Unlink a file within a phar archive
+ */
+static int phar_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) /* {{{ */
+{
+       php_url *resource;
+       char *internal_file, *error, *plain_map;
+       phar_entry_data *idata;
+       uint host_len;
+       int retval;
+
+       if ((resource = phar_open_url(wrapper, url, "rb", options TSRMLS_CC)) == NULL) {
+               return 0;
+       }
+
+       /* we must have at the very least phar://alias.phar/internalfile.php */
+       if (!resource->scheme || !resource->host || !resource->path) {
+               php_url_free(resource);
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url);
+               return 0;
+       }
+
+       if (strcasecmp("phar", resource->scheme)) {
+               php_url_free(resource);
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url);
+               return 0;
+       }
+
+       host_len = strlen(resource->host);
+       phar_request_initialize(TSRMLS_C);
+       if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource->host, host_len+1, (void **)&plain_map) == SUCCESS) {
+               spprintf(&internal_file, 0, "%s%s", plain_map, resource->path);
+               retval = php_stream_unlink(internal_file, options, context);
+               if (!retval) {
+                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" extracted from \"%s\" could not be opened", internal_file, resource->host);
+               }
+               php_url_free(resource);
+               efree(internal_file);
+               return retval;
+       }
+
+       if (PHAR_G(readonly)) {
+               php_url_free(resource);
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by INI setting");
+               return 0;
+       }
+
+       /* need to copy to strip leading "/", will get touched again */
+       internal_file = estrdup(resource->path + 1);
+       if (FAILURE == phar_get_entry_data(&idata, resource->host, strlen(resource->host), internal_file, strlen(internal_file), "r", &error TSRMLS_CC)) {
+               /* constraints of fp refcount were not met */
+               if (error) {
+                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
+                       efree(error);
+               }
+               efree(internal_file);
+               php_url_free(resource);
+               return 0;
+       }
+       if (error) {
+               efree(error);
+       }
+       if (!idata) {
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" is not a file in phar \"%s\", cannot unlink", internal_file, resource->host);
+               efree(internal_file);
+               php_url_free(resource);
+               return 0;
+       }
+       if (idata->internal_file->fp_refcount > 1) {
+               /* more than just our fp resource is open for this file */ 
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" in phar \"%s\", has open file pointers, cannot unlink", internal_file, resource->host);
+               efree(internal_file);
+               php_url_free(resource);
+               phar_entry_delref(idata TSRMLS_CC);
+               return 0;
+       }
+       php_url_free(resource);
+       efree(internal_file);
+       phar_entry_remove(idata, &error TSRMLS_CC);
+       if (error) {
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
+               efree(error);
+       }
+       return 1;
+}
+/* }}} */
+
+static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC) /* {{{ */
+{
+       php_url *resource_from, *resource_to;
+       char *error, *plain_map;
+       phar_archive_data *phar;
+       phar_entry_info *entry;
+       uint host_len;
+
+       error = NULL;
+       if (PHAR_G(readonly)) {
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by INI setting");
+               return 0;
+       }
+
+       if ((resource_from = phar_open_url(wrapper, url_from, "r+b", options TSRMLS_CC)) == NULL) {
+               return 0;
+       }
+       
+       if ((resource_to = phar_open_url(wrapper, url_to, "wb", options TSRMLS_CC)) == NULL) {
+               php_url_free(resource_from);
+               return 0;
+       }
+
+       /* we must have at the very least phar://alias.phar/internalfile.php */
+       if (!resource_from->scheme || !resource_from->host || !resource_from->path) {
+               php_url_free(resource_from);
+               php_url_free(resource_to);
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url_from);
+               return 0;
+       }
+       
+       if (!resource_to->scheme || !resource_to->host || !resource_to->path) {
+               php_url_free(resource_from);
+               php_url_free(resource_to);
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url_to);
+               return 0;
+       }
+
+       if (strcasecmp("phar", resource_from->scheme)) {
+               php_url_free(resource_from);
+               php_url_free(resource_to);
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url_from);
+               return 0;
+       }
+
+       if (strcasecmp("phar", resource_to->scheme)) {
+               php_url_free(resource_from);
+               php_url_free(resource_to);
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url_to);
+               return 0;
+       }
+
+       if (strcmp(resource_from->host, resource_to->host)) {
+               php_url_free(resource_from);
+               php_url_free(resource_to);
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\", not within the same phar archive", url_from, url_to);
+               return 0;
+       }
+
+       host_len = strlen(resource_from->host);
+       phar_request_initialize(TSRMLS_C);
+       if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource_from->host, host_len+1, (void **)&plain_map) == SUCCESS) {
+               /*TODO:use php_stream_rename() once available*/
+               php_url_free(resource_from);
+               php_url_free(resource_to);
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive", url_from, url_to);
+               return 0;
+       }
+
+       if (SUCCESS != phar_get_archive(&phar, resource_from->host, strlen(resource_from->host), NULL, 0, &error TSRMLS_CC)) {
+               php_url_free(resource_from);
+               php_url_free(resource_to);
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
+               efree(error);
+               return 0;
+       }
+
+       if (SUCCESS == zend_hash_find(&(phar->manifest), resource_from->path+1, strlen(resource_from->path)-1, (void **)&entry)) {
+               phar_entry_info new;
+
+               /* perform rename magic */
+               if (entry->is_deleted) {
+                       php_url_free(resource_from);
+                       php_url_free(resource_to);
+                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source has been deleted", url_from, url_to);
+                       return 0;
+               }
+               /* transfer all data over to the new entry */
+               memcpy((void *) &new, (void *) entry, sizeof(phar_entry_info));
+               /* mark the old one for deletion */
+               entry->is_deleted = 1;
+               entry->fp = NULL;
+               entry->metadata = 0;
+               entry->link = NULL;
+#if HAVE_PHAR_ZIP
+               entry->zip = NULL;
+#endif
+
+               zend_hash_add(&(phar->manifest), resource_to->path+1, strlen(resource_to->path)-1, (void **)&new, sizeof(phar_entry_info), (void **) &entry);
+               if (!entry->is_modified) {
+                       /* copy file contents into a new temp stream */
+                       if (!phar_open_jit(phar, entry, phar->fp, &error, 1 TSRMLS_CC)) {
+                               php_url_free(resource_from);
+                               php_url_free(resource_to);
+                               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
+                               efree(error);
+                               return 0;
+                       }
+               }
+               entry->is_modified = 1;
+               entry->filename = estrdup(resource_to->path+1);
+               entry->filename_len = strlen(entry->filename);
+               phar_flush(phar, 0, 0, &error TSRMLS_CC);
+               if (error) {
+                       php_url_free(resource_from);
+                       php_url_free(resource_to);
+                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
+                       efree(error);
+                       return 0;
+               }
+       }
+       php_url_free(resource_from);
+       php_url_free(resource_to);
+       return 1;
+}
+/* }}} */
diff --git a/ext/phar/stream.h b/ext/phar/stream.h
new file mode 100644 (file)
index 0000000..b9cf92a
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+  +----------------------------------------------------------------------+
+  | phar php single-file executable PHP extension                        |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 2006-2007 The PHP Group                                |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt.                                 |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | license@php.net so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Authors: Gregory Beaver <cellog@php.net>                             |
+  |          Marcus Boerger <helly@php.net>                              |
+  +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+BEGIN_EXTERN_C()
+int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_data *idata, php_uint32 crc32 TSRMLS_DC);
+
+php_url* phar_open_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options TSRMLS_DC);
+void phar_entry_remove(phar_entry_data *idata, char **error TSRMLS_DC);
+
+static php_stream* phar_wrapper_open_url(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
+static php_stream* phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
+static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC);
+static int phar_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC);
+static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC);
+
+/* file/stream handlers */
+static size_t phar_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC);
+static size_t phar_stream_read( php_stream *stream, char *buf, size_t count TSRMLS_DC);
+static int    phar_stream_close(php_stream *stream, int close_handle TSRMLS_DC);
+static int    phar_stream_flush(php_stream *stream TSRMLS_DC);
+static int    phar_stream_seek( php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC);
+static int    phar_stream_stat( php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC);
+END_EXTERN_C()
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
index 25859200e7bbebb492e9d3ebac61dc1d891fb897..a7e212c270e580c88e9260821637ec115824633e 100644 (file)
 */
 
 #include "phar_internal.h"
-
-#if defined(PHP_VERSION_ID) && (PHP_VERSION_ID < 50400 || PHP_VERSION_ID >= 60000)
-
-static int _php_stream_unlink(char *url, int options, php_stream_context *context TSRMLS_DC)
-{
-       php_stream_wrapper *wrapper = php_stream_locate_url_wrapper(url, NULL, options TSRMLS_CC);
-
-       if (!wrapper || !wrapper->wops) {
-               return 0;
-       }
-
-       if (!wrapper->wops->unlink) {
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s does not allow unlinking", wrapper->wops->label ? wrapper->wops->label : "Wrapper");
-               return 0;
-       }
-       return wrapper->wops->unlink(wrapper, url, ENFORCE_SAFE_MODE | REPORT_ERRORS, context TSRMLS_CC);
-}
-
-#define php_stream_unlink(url, options, context) _php_stream_unlink((url), (options), (context) TSRMLS_CC)
-
-#endif
+#include "php_stream_unlink.h"
 
 /**
  * Does not check for a previously opened phar in the cache.