summaryrefslogtreecommitdiff
path: root/ext/phar/phar.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/phar/phar.c')
-rw-r--r--ext/phar/phar.c3561
1 files changed, 0 insertions, 3561 deletions
diff --git a/ext/phar/phar.c b/ext/phar/phar.c
deleted file mode 100644
index c2b651bb49..0000000000
--- a/ext/phar/phar.c
+++ /dev/null
@@ -1,3561 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | phar php single-file executable PHP extension |
- +----------------------------------------------------------------------+
- | Copyright (c) 2005-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$ */
-
-#define PHAR_MAIN
-#include "phar_internal.h"
-#include "SAPI.h"
-
-ZEND_DECLARE_MODULE_GLOBALS(phar)
-
-/* 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) /* {{{ */
-{
- zend_bool old, ini;
-
- if (entry->name_length == 14) {
- old = PHAR_G(readonly_orig);
- } else {
- old = PHAR_G(require_hash_orig);
- }
-
- if (new_value_length == 2 && !strcasecmp("on", new_value)) {
- ini = (zend_bool) 1;
- }
- else if (new_value_length == 3 && !strcasecmp("yes", new_value)) {
- ini = (zend_bool) 1;
- }
- else if (new_value_length == 4 && !strcasecmp("true", new_value)) {
- ini = (zend_bool) 1;
- }
- else {
- ini = (zend_bool) atoi(new_value);
- }
-
- /* do not allow unsetting in runtime */
- if (stage == ZEND_INI_STAGE_STARTUP) {
- if (entry->name_length == 14) {
- PHAR_G(readonly_orig) = ini;
- } else {
- PHAR_G(require_hash_orig) = ini;
- }
- } else if (old && !ini) {
- return FAILURE;
- }
-
- if (entry->name_length == 14) {
- PHAR_G(readonly) = ini;
- } else {
- PHAR_G(require_hash) = ini;
- }
- return SUCCESS;
-}
-/* }}}*/
-
-static void phar_split_extract_list(TSRMLS_D)
-{
- char *tmp = estrdup(PHAR_GLOBALS->extract_list);
- char *key;
- char *lasts;
- char *q;
- int keylen;
-
- zend_hash_clean(&(PHAR_GLOBALS->phar_plain_map));
-
- for (key = php_strtok_r(tmp, ",", &lasts);
- key;
- key = php_strtok_r(NULL, ",", &lasts))
- {
- char *val = strchr(key, '=');
-
- if (val) {
- *val++ = '\0';
- for (q = key; *q; q++) {
- *q = tolower(*q);
- }
- keylen = q - key + 1;
- zend_hash_add(&(PHAR_GLOBALS->phar_plain_map), key, keylen, val, strlen(val)+1, NULL);
- }
- }
- efree(tmp);
-}
-/* }}} */
-
-ZEND_INI_MH(phar_ini_extract_list) /* {{{ */
-{
- PHAR_G(extract_list) = new_value;
-
- if (stage == ZEND_INI_STAGE_RUNTIME) {
- phar_split_extract_list(TSRMLS_C);
- }
-
- return SUCCESS;
-}
-/* }}} */
-
-ZEND_INI_DISP(phar_ini_extract_list_disp) /*void name(zend_ini_entry *ini_entry, int type) {{{ */
-{
- char *value;
-
- if (type==ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
- value = ini_entry->orig_value;
- } else if (ini_entry->value) {
- value = ini_entry->value;
- } else {
- value = NULL;
- }
-
- if (value) {
- char *tmp = strdup(value);
- char *key;
- char *lasts;
- char *q;
-
- if (!sapi_module.phpinfo_as_text) {
- php_printf("<ul>");
- }
- for (key = php_strtok_r(tmp, ",", &lasts);
- key;
- key = php_strtok_r(NULL, ",", &lasts))
- {
- char *val = strchr(key, '=');
-
- if (val) {
- *val++ = '\0';
- for (q = key; *q; q++) {
- *q = tolower(*q);
- }
- if (sapi_module.phpinfo_as_text) {
- php_printf("%s => %s", key, val);
- } else {
- php_printf("<li>%s => %s</li>", key, val);
- }
- }
- }
- if (!sapi_module.phpinfo_as_text) {
- php_printf("</ul>");
- }
- free(tmp);
- }
-}
-/* }}} */
-
-PHP_INI_BEGIN()
- STD_PHP_INI_BOOLEAN( "phar.readonly", "1", PHP_INI_ALL, phar_ini_modify_handler, readonly, zend_phar_globals, phar_globals)
- STD_PHP_INI_BOOLEAN( "phar.require_hash", "1", PHP_INI_ALL, phar_ini_modify_handler, require_hash, zend_phar_globals, phar_globals)
- STD_PHP_INI_ENTRY_EX("phar.extract_list", "", PHP_INI_ALL, phar_ini_extract_list, extract_list, zend_phar_globals, phar_globals, phar_ini_extract_list_disp)
-PHP_INI_END()
-
-/**
- * When all uses of a phar have been concluded, this frees the manifest
- * and the phar slot
- */
-static void phar_destroy_phar_data(phar_archive_data *data TSRMLS_DC) /* {{{ */
-{
- if (data->alias && data->alias != data->fname) {
- efree(data->alias);
- data->alias = NULL;
- }
- if (data->fname) {
- efree(data->fname);
- }
- if (data->signature) {
- efree(data->signature);
- }
- if (data->manifest.arBuckets) {
- zend_hash_destroy(&data->manifest);
- }
- if (data->metadata) {
- zval_ptr_dtor(&data->metadata);
- data->metadata = 0;
- }
- if (data->fp) {
- php_stream_close(data->fp);
- data->fp = 0;
- }
-
- efree(data);
-}
-/* }}}*/
-
-/**
- * Delete refcount and destruct if needed. On destruct return 1 else 0.
- */
-int phar_archive_delref(phar_archive_data *phar TSRMLS_DC) /* {{{ */
-{
- if (--phar->refcount < 0) {
- if (PHAR_GLOBALS->request_done
- || zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), phar->fname, phar->fname_len) != SUCCESS) {
- phar_destroy_phar_data(phar TSRMLS_CC);
- }
- return 1;
- }
- return 0;
-}
-/* }}}*/
-
-/**
- * Destroy phar's in shutdown, here we don't care about aliases
- */
-static void destroy_phar_data_only(void *pDest) /* {{{ */
-{
- phar_archive_data *phar_data = *(phar_archive_data **) pDest;
- TSRMLS_FETCH();
-
- if (EG(exception) || --phar_data->refcount < 0) {
- phar_destroy_phar_data(phar_data TSRMLS_CC);
- }
-}
-/* }}}*/
-
-/**
- * Delete aliases to phar's that got kicked out of the global table
- */
-static int phar_unalias_apply(void *pDest, void *argument TSRMLS_DC) /* {{{ */
-{
- return *(void**)pDest == argument ? ZEND_HASH_APPLY_REMOVE : ZEND_HASH_APPLY_KEEP;
-}
-/* }}} */
-
-/**
- * Filename map destructor
- */
-static void destroy_phar_data(void *pDest) /* {{{ */
-{
- phar_archive_data *phar_data = *(phar_archive_data **) pDest;
- TSRMLS_FETCH();
-
- if (PHAR_GLOBALS->request_ends) {
- return destroy_phar_data_only(pDest);
- }
- zend_hash_apply_with_argument(&(PHAR_GLOBALS->phar_alias_map), phar_unalias_apply, phar_data TSRMLS_CC);
- if (--phar_data->refcount < 0) {
- phar_destroy_phar_data(phar_data TSRMLS_CC);
- }
-}
-/* }}}*/
-
-/**
- * destructor for the manifest hash, frees each file's entry
- */
-static void destroy_phar_manifest(void *pDest) /* {{{ */
-{
- phar_entry_info *entry = (phar_entry_info *)pDest;
- TSRMLS_FETCH();
-
- if (entry->cfp) {
- php_stream_close(entry->cfp);
- entry->cfp = 0;
- }
- if (entry->fp) {
- php_stream_close(entry->fp);
- entry->fp = 0;
- }
- if (entry->metadata) {
- zval_ptr_dtor(&entry->metadata);
- entry->metadata = 0;
- }
- if (entry->metadata_str.c) {
- smart_str_free(&entry->metadata_str);
- entry->metadata_str.c = 0;
- }
- efree(entry->filename);
-}
-/* }}} */
-
-/**
- * Looks up a phar archive in the filename map, connecting it to the alias
- * (if any) or returns null
- */
-static int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, char *alias, int alias_len, char **error TSRMLS_DC) /* {{{ */
-{
- phar_archive_data *fd, **fd_ptr;
-
- phar_request_initialize(TSRMLS_C);
-
- if (error) {
- *error = NULL;
- }
- *archive = NULL;
- if (alias && alias_len) {
- if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void**)&fd_ptr)) {
- if (fname && (fname_len != (*fd_ptr)->fname_len || strncmp(fname, (*fd_ptr)->fname, fname_len))) {
- if (error) {
- spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, (*fd_ptr)->fname, fname);
- }
- return FAILURE;
- }
- *archive = *fd_ptr;
- return SUCCESS;
- }
- }
- if (fname && fname_len) {
- if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void**)&fd_ptr)) {
- *archive = *fd_ptr;
- fd = *fd_ptr;
- if (alias && alias_len) {
- zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&fd, sizeof(phar_archive_data*), NULL);
- }
- return SUCCESS;
- }
- if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), fname, fname_len, (void**)&fd_ptr)) {
- *archive = *fd_ptr;
- return SUCCESS;
- }
- }
- return FAILURE;
-}
-/* }}} */
-
-/**
- * retrieve information on a file contained within a phar, or null if it ain't there
- */
-phar_entry_info *phar_get_entry_info(phar_archive_data *phar, char *path, int path_len, char **error TSRMLS_DC) /* {{{ */
-{
- const char *pcr_error;
- phar_entry_info *entry;
-
- if (error) {
- *error = NULL;
- }
-
- if (phar_path_check(&path, &path_len, &pcr_error) > pcr_is_ok) {
- if (error) {
- spprintf(error, 0, "phar error: invalid path \"%s\" contains %s", path, pcr_error);
- }
- return NULL;
- }
-
- if (!&phar->manifest.arBuckets) {
- return NULL;
- }
- if (SUCCESS == zend_hash_find(&phar->manifest, path, path_len, (void**)&entry)) {
- if (entry->is_deleted) {
- /* entry is deleted, but has not been flushed to disk yet */
- return NULL;
- }
- return entry;
- }
- return NULL;
-}
-/* }}} */
-
-#if defined(PHP_VERSION_ID) && PHP_VERSION_ID < 50202
-typedef struct {
- char *data;
- size_t fpos;
- size_t fsize;
- size_t smax;
- int mode;
- php_stream **owner_ptr;
-} php_stream_memory_data;
-#endif
-
-/**
- * Retrieve a copy of the file information on a single file within a phar, or null.
- * This also transfers the open file pointer, if any, to the entry.
- */
-static int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char *path, int path_len, char *mode, char **error TSRMLS_DC) /* {{{ */
-{
- phar_archive_data *phar;
- phar_entry_info *entry;
- int for_write = mode[0] != 'r' || mode[1] == '+';
- int for_append = mode[0] == 'a';
- int for_create = mode[0] != 'r';
- int for_trunc = mode[0] == 'w';
-
- if (!ret) {
- return FAILURE;
- }
- *ret = NULL;
- if (error) {
- *error = NULL;
- }
- if (for_write && PHAR_G(readonly)) {
- if (error) {
- spprintf(error, 0, "phar error: file \"%s\" cannot opened for writing, disabled by ini setting", fname);
- }
- return FAILURE;
- }
- if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, error TSRMLS_CC)) {
- return FAILURE;
- }
- if ((entry = phar_get_entry_info(phar, path, path_len, for_create && !PHAR_G(readonly) ? NULL : error TSRMLS_CC)) == NULL) {
- if (for_create && !PHAR_G(readonly)) {
- return SUCCESS;
- }
- return FAILURE;
- }
- if (entry->is_modified && !for_write) {
- if (error) {
- spprintf(error, 0, "phar error: file \"%s\" cannot opened for reading, writable file pointers are open", fname);
- }
- return FAILURE;
- }
- if (entry->fp_refcount && for_write) {
- if (error) {
- spprintf(error, 0, "phar error: file \"%s\" cannot opened for writing, readable file pointers are open", fname);
- }
- return FAILURE;
- }
- if (entry->is_deleted) {
- if (!for_create) {
- return FAILURE;
- }
- entry->is_deleted = 0;
- }
- *ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data));
- (*ret)->position = 0;
- (*ret)->phar = phar;
- (*ret)->internal_file = entry;
- if (entry->fp) {
- /* make a copy */
- if (for_trunc) {
-#if PHP_VERSION_ID >= 50202
- php_stream_truncate_set_size(entry->fp, 0);
-#else
- if (php_stream_is(entry->fp, PHP_STREAM_IS_TEMP)) {
- if (php_stream_is(*(php_stream**)entry->fp->abstract, PHP_STREAM_IS_MEMORY)) {
- php_stream *inner = *(php_stream**)entry->fp->abstract;
- php_stream_memory_data *memfp = (php_stream_memory_data*)inner->abstract;
- memfp->fpos = 0;
- memfp->fsize = 0;
- } else if (php_stream_is(*(php_stream**)entry->fp->abstract, PHP_STREAM_IS_STDIO)) {
- php_stream_truncate_set_size(*(php_stream**)entry->fp->abstract, 0);
- } else {
- efree(*ret);
- *ret = NULL;
- if (error) {
- spprintf(error, 0, "phar error: file \"%s\" cannot opened for writing, no truncate support", fname);
- }
- return FAILURE;
- }
- } else if (php_stream_is(entry->fp, PHP_STREAM_IS_STDIO)) {
- php_stream_truncate_set_size(entry->fp, 0);
- } else {
- efree(*ret);
- *ret = NULL;
- if (error) {
- spprintf(error, 0, "phar error: file \"%s\" cannot opened for writing, no truncate support", fname);
- }
- return FAILURE;
- }
-#endif
- entry->is_modified = 1;
- phar->is_modified = 1;
- /* reset file size */
- entry->uncompressed_filesize = 0;
- entry->compressed_filesize = 0;
- entry->crc32 = 0;
- } else if (for_append) {
- php_stream_seek(entry->fp, 0, SEEK_END);
- }
- (*ret)->fp = entry->fp;
- } else {
- (*ret)->fp = 0;
- if (for_write) {
- /* open a new temp file for writing */
- entry->fp = php_stream_fopen_tmpfile();
- if (!entry->fp) {
- if (error) {
- spprintf(error, 0, "phar error: unable to create temporary file");
- }
- return FAILURE;
- }
- (*ret)->fp = entry->fp;
- entry->is_modified = 1;
- phar->is_modified = 1;
- /* reset file size */
- entry->uncompressed_filesize = 0;
- entry->compressed_filesize = 0;
- entry->crc32 = 0;
- entry->flags = PHAR_ENT_PERM_DEF_FILE;
- }
- }
- entry->fp_refcount++;
- entry->phar->refcount++;
- return SUCCESS;
-}
-/* }}} */
-
-int phar_entry_delref(phar_entry_data *idata TSRMLS_DC) /* {{{ */
-{
- int ret = 0;
-
- if (--idata->internal_file->fp_refcount <= 0) {
- idata->internal_file->fp_refcount = 0;
- }
- if (idata->fp && idata->fp != idata->internal_file->fp) {
- php_stream_close(idata->fp);
- }
- phar_archive_delref(idata->internal_file->phar TSRMLS_CC);
- efree(idata);
- return ret;
-}
-/* }}} */
-
-/**
- * Removes an entry, either by actually removing it or by marking it.
- */
-void phar_entry_remove(phar_entry_data *idata, char **error TSRMLS_DC) /* {{{ */
-{
- if (!idata->phar->donotflush) {
- phar_flush(idata->internal_file->phar, 0, 0, error TSRMLS_CC);
- }
- if (idata->internal_file->fp_refcount < 2) {
- if (idata->fp && idata->fp != idata->internal_file->fp) {
- php_stream_close(idata->fp);
- }
- zend_hash_del(&idata->phar->manifest, idata->internal_file->filename, idata->internal_file->filename_len);
- idata->phar->refcount--;
- efree(idata);
- } else {
- idata->internal_file->is_deleted = 1;
- phar_entry_delref(idata TSRMLS_CC);
- }
-}
-/* }}} */
-
-/**
- * Create a new dummy file slot within a writeable phar for a newly created file
- */
-phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char *path, int path_len, char *mode, char **error TSRMLS_DC) /* {{{ */
-{
- phar_archive_data *phar;
- phar_entry_info *entry, etemp;
- phar_entry_data *ret;
- const char *pcr_error;
-
- if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, error TSRMLS_CC)) {
- return NULL;
- }
-
- if (FAILURE == phar_get_entry_data(&ret, fname, fname_len, path, path_len, mode, error TSRMLS_CC)) {
- return NULL;
- } else if (ret) {
- return ret;
- }
-
- if (phar_path_check(&path, &path_len, &pcr_error) > pcr_is_ok) {
- if (error) {
- spprintf(error, 0, "phar error: invalid path \"%s\" contains %s", path, pcr_error);
- }
- return NULL;
- }
-
- /* create a new phar data holder */
- ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data));
-
- /* create an entry, this is a new file */
- memset(&etemp, 0, sizeof(phar_entry_info));
- etemp.filename_len = path_len;
- etemp.fp = php_stream_fopen_tmpfile();
- if (!etemp.fp) {
- if (error) {
- spprintf(error, 0, "phar error: unable to create temporary file");
- }
- return NULL;
- }
- etemp.fp_refcount = 1;
- etemp.is_modified = 1;
- etemp.filename = estrndup(path, path_len);
- etemp.timestamp = time(0);
- etemp.offset_within_phar = -1;
- etemp.is_crc_checked = 1;
- etemp.flags = PHAR_ENT_PERM_DEF_FILE;
- etemp.phar = phar;
- zend_hash_add(&phar->manifest, etemp.filename, path_len, (void*)&etemp, sizeof(phar_entry_info), NULL);
- /* retrieve the phar manifest copy */
- entry = phar_get_entry_info(phar, path, path_len, error TSRMLS_CC);
-
- if (!entry) {
- return NULL;
- }
-
- phar->refcount++;
- ret->phar = phar;
- ret->fp = entry->fp;
- ret->position = 0;
- ret->internal_file = entry;
- return ret;
-}
-/* }}} */
-
-#define MAPPHAR_ALLOC_FAIL(msg) \
- php_stream_close(fp);\
- if (error) {\
- spprintf(error, 0, msg, fname);\
- }\
- return FAILURE;
-
-#define MAPPHAR_FAIL(msg) \
- efree(savebuf);\
- if (mydata) {\
- phar_destroy_phar_data(mydata TSRMLS_CC);\
- }\
- if (signature) {\
- efree(signature);\
- }\
- MAPPHAR_ALLOC_FAIL(msg)
-
-#ifdef WORDS_BIGENDIAN
-# define PHAR_GET_32(buffer, var) \
- var = ((((unsigned char*)(buffer))[3]) << 24) \
- | ((((unsigned char*)(buffer))[2]) << 16) \
- | ((((unsigned char*)(buffer))[1]) << 8) \
- | (((unsigned char*)(buffer))[0]); \
- (buffer) += 4
-# define PHAR_GET_16(buffer, var) \
- var = ((((unsigned char*)(buffer))[1]) << 8) \
- | (((unsigned char*)(buffer))[0]); \
- (buffer) += 2
-#else
-# define PHAR_GET_32(buffer, var) \
- var = *(php_uint32*)(buffer); \
- buffer += 4
-# define PHAR_GET_16(buffer, var) \
- var = *(php_uint16*)(buffer); \
- buffer += 2
-#endif
-
-/**
- * Open an already loaded phar
- */
-static int phar_open_loaded(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
-{
- phar_archive_data *phar;
-
- if (error) {
- *error = NULL;
- }
- if (SUCCESS == phar_get_archive(&phar, fname, fname_len, alias, alias_len, error TSRMLS_CC)
- && fname_len == phar->fname_len
- && !strncmp(fname, phar->fname, fname_len)
- ) {
- if (pphar) {
- *pphar = phar;
- }
- return SUCCESS;
- } else {
- if (pphar) {
- *pphar = NULL;
- }
- if (phar && alias && (options & REPORT_ERRORS)) {
- if (error) {
- spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, phar->fname, fname);
- }
- }
- return FAILURE;
- }
-}
-/* }}}*/
-
-/**
- * Parse out metadata from the manifest for a single file
- *
- * Meta-data is in this format:
- * [len32][data...]
- *
- * data is the serialized zval
- */
-static int phar_parse_metadata(php_stream *fp, char **buffer, char *endbuffer, zval **metadata TSRMLS_DC) /* {{{ */
-{
- const unsigned char *p;
- php_uint32 buf_len;
- php_unserialize_data_t var_hash;
-
- PHAR_GET_32(*buffer, buf_len);
-
- if (buf_len) {
- ALLOC_INIT_ZVAL(*metadata);
- p = (const unsigned char*) *buffer;
- PHP_VAR_UNSERIALIZE_INIT(var_hash);
- if (!php_var_unserialize(metadata, &p, p + buf_len, &var_hash TSRMLS_CC)) {
- PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- zval_ptr_dtor(metadata);
- *metadata = NULL;
- return FAILURE;
- }
- PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- } else {
- *metadata = NULL;
- }
- *buffer += buf_len;
- return SUCCESS;
-}
-/* }}}*/
-
-static const char hexChars[] = "0123456789ABCDEF";
-
-static int phar_hex_str(const char *digest, size_t digest_len, char ** signature)
-{
- int pos = -1;
- size_t len;
-
- *signature = (char*)safe_emalloc(digest_len, 2, 1);
-
- for(len = 0; len < digest_len; ++len) {
- (*signature)[++pos] = hexChars[((const unsigned char *)digest)[len] >> 4];
- (*signature)[++pos] = hexChars[((const unsigned char *)digest)[len] & 0x0F];
- }
- (*signature)[++pos] = '\0';
- return pos;
-}
-
-/**
- * Does not check for a previously opened phar in the cache.
- *
- * Parse a new one and add it to the cache, returning either SUCCESS or
- * FAILURE, and setting pphar to the pointer to the manifest entry
- *
- * This is used by phar_open_filename to process the manifest, but can be called
- * directly.
- */
-int phar_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, long halt_offset, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
-{
- char b32[4], *buffer, *endbuffer, *savebuf;
- phar_archive_data *mydata = NULL;
- phar_entry_info entry;
- php_uint32 manifest_len, manifest_count, manifest_flags, manifest_index, tmp_len, sig_flags;
- php_uint16 manifest_ver;
- long offset;
- int register_alias, sig_len;
- char *signature = NULL;
-
- if (pphar) {
- *pphar = NULL;
- }
- if (error) {
- *error = NULL;
- }
-
- /* check for ?>\n and increment accordingly */
- if (-1 == php_stream_seek(fp, halt_offset, SEEK_SET)) {
- MAPPHAR_ALLOC_FAIL("cannot seek to __HALT_COMPILER(); location in phar \"%s\"")
- }
-
- buffer = b32;
- if (3 != php_stream_read(fp, buffer, 3)) {
- MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
- }
- if ((*buffer == ' ' || *buffer == '\n') && *(buffer + 1) == '?' && *(buffer + 2) == '>') {
- int nextchar;
- halt_offset += 3;
- if (EOF == (nextchar = php_stream_getc(fp))) {
- MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
- }
- if ((char) nextchar == '\r') {
- /* if we have an \r we require an \n as well */
- if (EOF == (nextchar = php_stream_getc(fp)) || (char)nextchar != '\n') {
- MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
- }
- halt_offset++;
- }
- if ((char) nextchar == '\n') {
- halt_offset++;
- }
- }
- /* make sure we are at the right location to read the manifest */
- if (-1 == php_stream_seek(fp, halt_offset, SEEK_SET)) {
- MAPPHAR_ALLOC_FAIL("cannot seek to __HALT_COMPILER(); location in phar \"%s\"")
- }
-
- /* read in manifest */
- buffer = b32;
- if (4 != php_stream_read(fp, buffer, 4)) {
- MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at manifest length)")
- }
- PHAR_GET_32(buffer, manifest_len);
- if (manifest_len > 1048576) {
- /* prevent serious memory issues by limiting manifest to at most 1 MB in length */
- MAPPHAR_ALLOC_FAIL("manifest cannot be larger than 1 MB in phar \"%s\"")
- }
- buffer = (char *)emalloc(manifest_len);
- savebuf = buffer;
- endbuffer = buffer + manifest_len;
- if (manifest_len < 10 || manifest_len != php_stream_read(fp, buffer, manifest_len)) {
- MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)")
- }
-
- /* extract the number of entries */
- PHAR_GET_32(buffer, manifest_count);
- if (manifest_count == 0) {
- MAPPHAR_FAIL("in phar \"%s\", manifest claims to have zero entries. Phars must have at least 1 entry");
- }
-
- /* extract API version, lowest nibble currently unused */
- manifest_ver = (((unsigned char)buffer[0]) << 8)
- + ((unsigned char)buffer[1]);
- buffer += 2;
- if ((manifest_ver & PHAR_API_VER_MASK) < PHAR_API_MIN_READ) {
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- spprintf(error, 0, "phar \"%s\" is API version %1.u.%1.u.%1.u, and cannot be processed", fname, manifest_ver >> 12, (manifest_ver >> 8) & 0xF, (manifest_ver >> 4) & 0x0F);
- }
- return FAILURE;
- }
-
- PHAR_GET_32(buffer, manifest_flags);
-
- manifest_flags &= ~PHAR_HDR_COMPRESSION_MASK;
-
- /* The lowest nibble contains the phar wide flags. The compression flags can */
- /* be ignored on reading because it is being generated anyways. */
- if (manifest_flags & PHAR_HDR_SIGNATURE) {
- unsigned char buf[1024];
- int read_size, len;
- char sig_buf[8], *sig_ptr = sig_buf;
- off_t read_len;
-
- if (-1 == php_stream_seek(fp, -8, SEEK_END)
- || (read_len = php_stream_tell(fp)) < 20
- || 8 != php_stream_read(fp, sig_buf, 8)
- || memcmp(sig_buf+4, "GBMB", 4)) {
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
- }
- return FAILURE;
- }
- PHAR_GET_32(sig_ptr, sig_flags);
- switch(sig_flags) {
-#if HAVE_HASH_EXT
- case PHAR_SIG_SHA512: {
- unsigned char digest[64], saved[64];
- PHP_SHA512_CTX context;
-
- php_stream_rewind(fp);
- PHP_SHA512Init(&context);
- read_len -= sizeof(digest);
- if (read_len > sizeof(buf)) {
- read_size = sizeof(buf);
- } else {
- read_size = (int)read_len;
- }
- while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) {
- PHP_SHA512Update(&context, buf, len);
- read_len -= (off_t)len;
- if (read_len < read_size) {
- read_size = (int)read_len;
- }
- }
- PHP_SHA512Final(digest, &context);
-
- if (read_len > 0
- || php_stream_read(fp, (char*)saved, sizeof(saved)) != sizeof(saved)
- || memcmp(digest, saved, sizeof(digest))) {
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
- }
- return FAILURE;
- }
-
- sig_len = phar_hex_str((const char*)digest, sizeof(digest), &signature);
- break;
- }
- case PHAR_SIG_SHA256: {
- unsigned char digest[32], saved[32];
- PHP_SHA256_CTX context;
-
- php_stream_rewind(fp);
- PHP_SHA256Init(&context);
- read_len -= sizeof(digest);
- if (read_len > sizeof(buf)) {
- read_size = sizeof(buf);
- } else {
- read_size = (int)read_len;
- }
- while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) {
- PHP_SHA256Update(&context, buf, len);
- read_len -= (off_t)len;
- if (read_len < read_size) {
- read_size = (int)read_len;
- }
- }
- PHP_SHA256Final(digest, &context);
-
- if (read_len > 0
- || php_stream_read(fp, (char*)saved, sizeof(saved)) != sizeof(saved)
- || memcmp(digest, saved, sizeof(digest))) {
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
- }
- return FAILURE;
- }
-
- sig_len = phar_hex_str((const char*)digest, sizeof(digest), &signature);
- break;
- }
-#else
- case PHAR_SIG_SHA512:
- case PHAR_SIG_SHA256:
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- spprintf(error, 0, "phar \"%s\" has a unsupported signature", fname);
- }
- return FAILURE;
-#endif
- case PHAR_SIG_SHA1: {
- unsigned char digest[20], saved[20];
- PHP_SHA1_CTX context;
-
- php_stream_rewind(fp);
- PHP_SHA1Init(&context);
- read_len -= sizeof(digest);
- if (read_len > sizeof(buf)) {
- read_size = sizeof(buf);
- } else {
- read_size = (int)read_len;
- }
- while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) {
- PHP_SHA1Update(&context, buf, len);
- read_len -= (off_t)len;
- if (read_len < read_size) {
- read_size = (int)read_len;
- }
- }
- PHP_SHA1Final(digest, &context);
-
- if (read_len > 0
- || php_stream_read(fp, (char*)saved, sizeof(saved)) != sizeof(saved)
- || memcmp(digest, saved, sizeof(digest))) {
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
- }
- return FAILURE;
- }
-
- sig_len = phar_hex_str((const char*)digest, sizeof(digest), &signature);
- break;
- }
- case PHAR_SIG_MD5: {
- unsigned char digest[16], saved[16];
- PHP_MD5_CTX context;
-
- php_stream_rewind(fp);
- PHP_MD5Init(&context);
- read_len -= sizeof(digest);
- if (read_len > sizeof(buf)) {
- read_size = sizeof(buf);
- } else {
- read_size = (int)read_len;
- }
- while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) {
- PHP_MD5Update(&context, buf, len);
- read_len -= (off_t)len;
- if (read_len < read_size) {
- read_size = (int)read_len;
- }
- }
- PHP_MD5Final(digest, &context);
-
- if (read_len > 0
- || php_stream_read(fp, (char*)saved, sizeof(saved)) != sizeof(saved)
- || memcmp(digest, saved, sizeof(digest))) {
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
- }
- return FAILURE;
- }
-
- sig_len = phar_hex_str((const char*)digest, sizeof(digest), &signature);
- break;
- }
- default:
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- spprintf(error, 0, "phar \"%s\" has a broken or unsupported signature", fname);
- }
- return FAILURE;
- }
- } else if (PHAR_G(require_hash)) {
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- spprintf(error, 0, "phar \"%s\" does not have a signature", fname);
- }
- return FAILURE;
- } else {
- sig_flags = 0;
- sig_len = 0;
- }
-
- /* extract alias */
- PHAR_GET_32(buffer, tmp_len);
- if (buffer + tmp_len > endbuffer) {
- MAPPHAR_FAIL("internal corruption of phar \"%s\" (buffer overrun)");
- }
- if (manifest_len < 10 + tmp_len) {
- MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)")
- }
- /* tmp_len = 0 says alias length is 0, which means the alias is not stored in the phar */
- if (tmp_len) {
- /* if the alias is stored we enforce it (implicit overrides explicit) */
- if (alias && alias_len && (alias_len != (int)tmp_len || strncmp(alias, buffer, tmp_len)))
- {
- buffer[tmp_len] = '\0';
- efree(savebuf);
- php_stream_close(fp);
- if (signature) {
- efree(signature);
- }
- if (error) {
- spprintf(error, 0, "cannot load phar \"%s\" with implicit alias \"%s\" under different alias \"%s\"", fname, buffer, alias);
- }
- return FAILURE;
- }
- alias_len = tmp_len;
- alias = buffer;
- buffer += tmp_len;
- register_alias = 1;
- } else if (!alias_len || !alias) {
- /* if we neither have an explicit nor an implicit alias, we use the filename */
- alias = NULL;
- alias_len = 0;
- register_alias = 0;
- } else {
- register_alias = 1;
- }
-
- /* we have 5 32-bit items plus 1 byte at least */
- if (manifest_count > ((manifest_len - 10 - tmp_len) / (5 * 4 + 1))) {
- /* prevent serious memory issues */
- MAPPHAR_FAIL("internal corruption of phar \"%s\" (too many manifest entries for size of manifest)")
- }
-
- mydata = ecalloc(sizeof(phar_archive_data), 1);
-
- /* check whether we have meta data, zero check works regardless of byte order */
- if (phar_parse_metadata(fp, &buffer, endbuffer, &mydata->metadata TSRMLS_CC) == FAILURE) {
- MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\"");
- }
-
- /* set up our manifest */
- zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
- zend_get_hash_value, destroy_phar_manifest, 0);
- offset = 0;
- for (manifest_index = 0; manifest_index < manifest_count; manifest_index++) {
- if (buffer + 4 > endbuffer) {
- MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)")
- }
- memset(&entry, 0, sizeof(phar_entry_info));
- PHAR_GET_32(buffer, entry.filename_len);
- if (entry.filename_len == 0) {
- MAPPHAR_FAIL("zero-length filename encountered in phar \"%s\"");
- }
- if (buffer + entry.filename_len + 20 > endbuffer) {
- MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)");
- }
- entry.filename = estrndup(buffer, entry.filename_len);
- buffer += entry.filename_len;
- PHAR_GET_32(buffer, entry.uncompressed_filesize);
- PHAR_GET_32(buffer, entry.timestamp);
- if (offset == 0) {
- mydata->min_timestamp = entry.timestamp;
- mydata->max_timestamp = entry.timestamp;
- } else {
- if (mydata->min_timestamp > entry.timestamp) {
- mydata->min_timestamp = entry.timestamp;
- } else if (mydata->max_timestamp < entry.timestamp) {
- mydata->max_timestamp = entry.timestamp;
- }
- }
- PHAR_GET_32(buffer, entry.compressed_filesize);
- PHAR_GET_32(buffer, entry.crc32);
- PHAR_GET_32(buffer, entry.flags);
- if (phar_parse_metadata(fp, &buffer, endbuffer, &entry.metadata TSRMLS_CC) == FAILURE) {
- efree(entry.filename);
- MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\"");
- }
- entry.offset_within_phar = offset;
- offset += entry.compressed_filesize;
- switch (entry.flags & PHAR_ENT_COMPRESSION_MASK) {
- case PHAR_ENT_COMPRESSED_GZ:
-#if !HAVE_ZLIB
- if (entry.metadata) {
- zval_ptr_dtor(&entry.metadata);
- }
- efree(entry.filename);
- MAPPHAR_FAIL("zlib extension is required for gz compressed .phar file \"%s\"");
-#endif
- break;
- case PHAR_ENT_COMPRESSED_BZ2:
-#if !HAVE_BZ2
- if (entry.metadata) {
- zval_ptr_dtor(&entry.metadata);
- }
- efree(entry.filename);
- MAPPHAR_FAIL("bz2 extension is required for bzip2 compressed .phar file \"%s\"");
-#endif
- break;
- default:
- if (entry.uncompressed_filesize != entry.compressed_filesize) {
- if (entry.metadata) {
- zval_ptr_dtor(&entry.metadata);
- }
- efree(entry.filename);
- MAPPHAR_FAIL("internal corruption of phar \"%s\" (compressed and uncompressed size does not match for uncompressed entry)");
- }
- break;
- }
- manifest_flags |= (entry.flags & PHAR_ENT_COMPRESSION_MASK);
- entry.is_crc_checked = 0;
- entry.fp = NULL;
- entry.phar = mydata;
- zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL);
- }
-
- snprintf(mydata->version, sizeof(mydata->version), "%u.%u.%u", manifest_ver >> 12, (manifest_ver >> 8) & 0xF, (manifest_ver >> 4) & 0xF);
- mydata->internal_file_start = halt_offset + manifest_len + 4;
- mydata->halt_offset = halt_offset;
- mydata->flags = manifest_flags;
- mydata->fp = fp;
- mydata->fname = estrndup(fname, fname_len);
- mydata->fname_len = fname_len;
- mydata->alias = alias ? estrndup(alias, alias_len) : mydata->fname;
- mydata->alias_len = alias ? alias_len : fname_len;
- mydata->sig_flags = sig_flags;
- mydata->sig_len = sig_len;
- mydata->signature = signature;
- phar_request_initialize(TSRMLS_C);
- zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
- if (register_alias) {
- mydata->is_explicit_alias = 1;
- zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
- } else {
- mydata->is_explicit_alias = 0;
- }
- efree(savebuf);
-
- if (pphar) {
- *pphar = mydata;
- }
-
- return SUCCESS;
-}
-/* }}} */
-
-/**
- * Create or open a phar for writing
- */
-int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
-{
- phar_archive_data *mydata;
- int register_alias;
- php_stream *fp;
-
- if (!pphar) {
- pphar = &mydata;
- }
- if (error) {
- *error = NULL;
- }
-
- if (phar_open_loaded(fname, fname_len, alias, alias_len, options, pphar, 0 TSRMLS_CC) == SUCCESS) {
- if (!PHAR_G(readonly)) {
- (*pphar)->is_writeable = 1;
- }
- return SUCCESS;
- }
-
-#if PHP_MAJOR_VERSION < 6
- if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
- return FAILURE;
- }
-#endif
-
- if (php_check_open_basedir(fname TSRMLS_CC)) {
- return FAILURE;
- }
-
- fp = php_stream_open_wrapper(fname, PHAR_G(readonly)?"rb":"r+b", IGNORE_URL|STREAM_MUST_SEEK|0, NULL);
-
- if (fp && phar_open_fp(fp, fname, fname_len, alias, alias_len, options, pphar, 0 TSRMLS_CC) == SUCCESS) {
- if (!PHAR_G(readonly)) {
- (*pphar)->is_writeable = 1;
- }
- return SUCCESS;
- }
-
- if (PHAR_G(readonly)) {
- if (options & REPORT_ERRORS) {
- if (error) {
- spprintf(error, 0, "creating archive \"%s\" disabled by INI setting", fname);
- }
- }
- return FAILURE;
- }
-
- /* set up our manifest */
- mydata = ecalloc(sizeof(phar_archive_data), 1);
- if (pphar) {
- *pphar = mydata;
- }
- zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
- zend_get_hash_value, destroy_phar_manifest, 0);
- mydata->fname = estrndup(fname, fname_len);
- mydata->fname_len = fname_len;
- mydata->alias = alias ? estrndup(alias, alias_len) : mydata->fname;
- mydata->alias_len = alias ? alias_len : fname_len;
- snprintf(mydata->version, sizeof(mydata->version), "%s", PHAR_API_VERSION_STR);
- mydata->is_explicit_alias = alias ? 1 : 0;
- mydata->internal_file_start = -1;
- mydata->fp = fp;
- mydata->is_writeable = 1;
- mydata->is_brandnew = 1;
- if (!alias_len || !alias) {
- /* if we neither have an explicit nor an implicit alias, we use the filename */
- alias = NULL;
- alias_len = 0;
- register_alias = 0;
- } else {
- register_alias = 1;
- }
- phar_request_initialize(TSRMLS_C);
- zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
- if (register_alias) {
- zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
- }
- return SUCCESS;
-}
-/* }}}*/
-
-/**
- * Return an already opened filename.
- *
- * Or scan a phar file for the required __HALT_COMPILER(); ?> token and verify
- * that the manifest is proper, then pass it to phar_open_file(). SUCCESS
- * or FAILURE is returned and pphar is set to a pointer to the phar's manifest
- */
-int phar_open_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
-{
- php_stream *fp;
-
- if (error) {
- *error = NULL;
- }
-
- if (phar_open_loaded(fname, fname_len, alias, alias_len, options, pphar, error TSRMLS_CC) == SUCCESS) {
- return SUCCESS;
- } else if (*error) {
- return FAILURE;
- }
-
-#if PHP_MAJOR_VERSION < 6
- if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
- return FAILURE;
- }
-#endif
-
- if (php_check_open_basedir(fname TSRMLS_CC)) {
- return FAILURE;
- }
-
- fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
-
- if (!fp) {
- if (options & REPORT_ERRORS) {
- if (error) {
- spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
- }
- }
- return FAILURE;
- }
-
- return phar_open_fp(fp, fname, fname_len, alias, alias_len, options, pphar, error TSRMLS_CC);
-}
-/* }}}*/
-
-/**
- * Scan an open fp for the required __HALT_COMPILER(); ?> token and verify
- * that the manifest is proper, then pass it to phar_open_file(). SUCCESS
- * or FAILURE is returned and pphar is set to a pointer to the phar's manifest
- */
-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) /* {{{ */
-{
- const char token[] = "__HALT_COMPILER();";
- char *pos, buffer[1024 + sizeof(token)];
- const long readsize = sizeof(buffer) - sizeof(token);
- const long tokenlen = sizeof(token) - 1;
- long halt_offset;
- size_t got;
-
- /* Maybe it's better to compile the file instead of just searching, */
- /* but we only want the offset. So we want a .re scanner to find it. */
-
- if (error) {
- *error = NULL;
- }
- if (-1 == php_stream_rewind(fp)) {
- MAPPHAR_ALLOC_FAIL("cannot rewind phar \"%s\"")
- }
-
- buffer[sizeof(buffer)-1] = '\0';
- memset(buffer, 32, sizeof(token));
- halt_offset = 0;
- while(!php_stream_eof(fp)) {
- if ((got = php_stream_read(fp, buffer+tokenlen, readsize)) < tokenlen) {
- MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)")
- }
- if ((pos = strstr(buffer, token)) != NULL) {
- halt_offset += (pos - buffer); /* no -tokenlen+tokenlen here */
- return phar_open_file(fp, fname, fname_len, alias, alias_len, halt_offset, pphar, error TSRMLS_CC);
- }
-
- halt_offset += got;
- memmove(buffer, buffer + tokenlen, got + 1);
- }
-
- MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (__HALT_COMPILER(); not found)")
-}
-/* }}} */
-
-int phar_detect_phar_fname_ext(const char *filename, int check_length, char **ext_str, int *ext_len) /* {{{ */
-{
- char end;
- char *pos_p = strstr(filename, ".phar.php");
- char *pos_z = strstr(filename, ".phar.gz");
- char *pos_b = strstr(filename, ".phar.bz2");
-
- if (pos_p) {
- if (pos_z) {
- return FAILURE;
- }
- *ext_str = pos_p;
- *ext_len = 9;
- } else if (pos_z) {
- *ext_str = pos_z;
- *ext_len = 8;
- } else if (pos_b) {
- *ext_str = pos_b;
- *ext_len = 9;
- } else if ((pos_p = strstr(filename, ".phar")) != NULL) {
- *ext_str = pos_p;
- *ext_len = 5;
- } else {
- return FAILURE;
- }
- if (check_length && (*ext_str)[*ext_len] != '\0') {
- return FAILURE;
- }
- end = (*ext_str)[*ext_len];
- if (end != '\0' && end != '/' && end != '\\') {
- return FAILURE;
- }
- return SUCCESS;
-}
-/* }}} */
-
-/**
- * Process a phar stream name, ensuring we can handle any of:
- *
- * - whatever.phar
- * - whatever.phar.gz
- * - whatever.phar.bz2
- * - whatever.phar.php
- *
- * Optionally the name might start with 'phar://'
- *
- * This is used by phar_open_url()
- */
-int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_len, char **entry, int *entry_len TSRMLS_DC) /* {{{ */
-{
- char *ext_str;
- int ext_len;
-
- if (!strncasecmp(filename, "phar://", 7)) {
- filename += 7;
- filename_len -= 7;
- }
-
- if (phar_detect_phar_fname_ext(filename, 0, &ext_str, &ext_len) == FAILURE) {
- return FAILURE;
- }
-
- *arch_len = ext_str - filename + ext_len;
- *arch = estrndup(filename, *arch_len);
- if (ext_str[ext_len]) {
- *entry_len = filename_len - *arch_len;
- *entry = estrndup(ext_str+ext_len, *entry_len);
- } else {
- *entry_len = 1;
- *entry = estrndup("/", 1);
- }
- 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) {
- 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 (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
- * to set up its manifest directly
- */
-int phar_open_compiled_file(char *alias, int alias_len, char **error TSRMLS_DC) /* {{{ */
-{
- char *fname;
- long halt_offset;
- zval *halt_constant;
- php_stream *fp;
- int fname_len;
-
- if (error) {
- *error = NULL;
- }
- fname = zend_get_executed_filename(TSRMLS_C);
-
- fname_len = strlen(fname);
-
- if (alias && phar_open_loaded(fname, fname_len, alias, alias_len, REPORT_ERRORS, NULL, 0 TSRMLS_CC) == SUCCESS) {
- return SUCCESS;
- }
-
- if (!strcmp(fname, "[no active file]")) {
- if (error) {
- spprintf(error, 0, "cannot initialize a phar outside of PHP execution");
- }
- return FAILURE;
- }
-
- MAKE_STD_ZVAL(halt_constant);
- if (0 == zend_get_constant("__COMPILER_HALT_OFFSET__", 24, halt_constant TSRMLS_CC)) {
- FREE_ZVAL(halt_constant);
- if (error) {
- spprintf(error, 0, "__HALT_COMPILER(); must be declared in a phar");
- }
- return FAILURE;
- }
- halt_offset = Z_LVAL(*halt_constant);
- FREE_ZVAL(halt_constant);
-
- fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
-
- if (!fp) {
- if (error) {
- spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
- }
- return FAILURE;
- }
-
- return phar_open_file(fp, fname, fname_len, alias, alias_len, halt_offset, NULL, error TSRMLS_CC);
-}
-/* }}} */
-
-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) /* {{{ */
-{
- php_uint32 crc = ~0;
- int len = idata->internal_file->uncompressed_filesize;
- php_stream *fp = idata->fp;
-
- php_stream_seek(fp, 0, SEEK_SET);
- while (len--) {
- CRC32(crc, php_stream_getc(fp));
- }
- php_stream_seek(fp, 0, SEEK_SET);
- if (~crc == crc32) {
- return SUCCESS;
- } else {
- php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: internal corruption of phar \"%s\" (crc32 mismatch on file \"%s\")", idata->phar->fname, idata->internal_file->filename);
- return FAILURE;
- }
-}
-/* }}} */
-
-/**
- * Determine which stream compression filter (if any) we need to read this file
- */
-static char * phar_compress_filter(phar_entry_info * entry, int return_unknown) /* {{{ */
-{
- switch (entry->flags & PHAR_ENT_COMPRESSION_MASK) {
- case PHAR_ENT_COMPRESSED_GZ:
- return "zlib.deflate";
- case PHAR_ENT_COMPRESSED_BZ2:
- return "bzip2.compress";
- default:
- return return_unknown ? "unknown" : NULL;
- }
-}
-/* }}} */
-
-/**
- * Determine which stream decompression filter (if any) we need to read this file
- */
-static char * phar_decompress_filter(phar_entry_info * entry, int return_unknown) /* {{{ */
-{
- switch (entry->flags & PHAR_ENT_COMPRESSION_MASK) {
- case PHAR_ENT_COMPRESSED_GZ:
- return "zlib.inflate";
- case PHAR_ENT_COMPRESSED_BZ2:
- return "bzip2.decompress";
- default:
- return return_unknown ? "unknown" : 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 *buffer;
- char *error;
- char *filter_name;
- char *plain_map;
- char tmpbuf[8];
- HashTable *pharcontext;
- php_url *resource = NULL;
- php_stream *fp, *fpf;
- php_stream_filter *filter/*, *consumed */;
- php_uint32 offset, read, total, toread;
- zval **pzoption, *metadata;
- uint host_len;
-
- resource = php_url_parse(path);
-
- if (!resource && (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 (!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;
- }
-
- /* seek to start of internal file and read it */
- offset = idata->phar->internal_file_start + idata->internal_file->offset_within_phar;
- if (-1 == php_stream_seek(fp, offset, SEEK_SET)) {
- php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: internal corruption of phar \"%s\" (cannot seek to start of file \"%s\" at offset \"%d\")",
- idata->phar->fname, internal_file, offset);
- phar_entry_delref(idata TSRMLS_CC);
- efree(internal_file);
- return NULL;
- }
-
- if ((idata->internal_file->flags & PHAR_ENT_COMPRESSION_MASK) != 0) {
- if ((filter_name = phar_decompress_filter(idata->internal_file, 0)) != NULL) {
- filter = php_stream_filter_create(phar_decompress_filter(idata->internal_file, 0), NULL, php_stream_is_persistent(fp) TSRMLS_CC);
- } else {
- filter = NULL;
- }
- if (!filter) {
- php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: unable to read phar \"%s\" (cannot create %s filter while decompressing file \"%s\")", idata->phar->fname, phar_decompress_filter(idata->internal_file, 1), internal_file);
- phar_entry_delref(idata TSRMLS_CC);
- efree(internal_file);
- return NULL;
- }
- buffer = (char *) emalloc(8192);
- read = 0;
- total = 0;
-
- idata->fp = php_stream_temp_new();
- idata->internal_file->fp = idata->fp;
- php_stream_filter_append(&idata->fp->writefilters, filter);
- do {
- if ((total + 8192) > idata->internal_file->compressed_filesize) {
- toread = idata->internal_file->compressed_filesize - total;
- } else {
- toread = 8192;
- }
- read = php_stream_read(fp, buffer, toread);
- if (read) {
- total += read;
- if (read != php_stream_write(idata->fp, buffer, read)) {
- efree(buffer);
- php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", idata->phar->fname, internal_file);
- phar_entry_delref(idata TSRMLS_CC);
- php_stream_filter_remove(filter, 1 TSRMLS_CC);
- efree(internal_file);
- return NULL;
- }
- if (total == idata->internal_file->compressed_filesize) {
- read = 0;
- }
- }
- } while (read);
- efree(buffer);
- php_stream_filter_flush(filter, 1);
- php_stream_filter_remove(filter, 1 TSRMLS_CC);
- if (php_stream_tell(fp) != (off_t)(offset + idata->internal_file->compressed_filesize)) {
- php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", idata->phar->fname, internal_file);
- phar_entry_delref(idata TSRMLS_CC);
- efree(internal_file);
- return NULL;
- }
- if (php_stream_tell(idata->fp) != (off_t)idata->internal_file->uncompressed_filesize) {
- php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", idata->phar->fname, internal_file);
- phar_entry_delref(idata TSRMLS_CC);
- efree(internal_file);
- return NULL;
- }
- php_stream_seek(fp, offset + idata->internal_file->compressed_filesize, SEEK_SET);
- } else { /* from here is for non-compressed */
- buffer = &tmpbuf[0];
- /* bypass to temp stream */
- idata->fp = php_stream_temp_new();
- idata->internal_file->fp = idata->fp;
- if (php_stream_copy_to_stream(fp, idata->fp, idata->internal_file->uncompressed_filesize) != idata->internal_file->uncompressed_filesize) {
- php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", idata->phar->fname, internal_file);
- phar_entry_delref(idata TSRMLS_CC);
- efree(internal_file);
- return NULL;
- }
- }
-
- /* check length, crc32 */
- if (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, SEEK_SET);
-
- 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);
-
- 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 (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->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
- *((buffer) + 3) = (unsigned char) (((var) >> 24) & 0xFF);
- *((buffer) + 2) = (unsigned char) (((var) >> 16) & 0xFF);
- *((buffer) + 1) = (unsigned char) (((var) >> 8) & 0xFF);
- *((buffer) + 0) = (unsigned char) ((var) & 0xFF);
-#else
- *(php_uint32 *)(buffer) = (php_uint32)(var);
-#endif
-} /* }}} */
-
-/**
- * The only purpose of this is to store the API version, which was stored bigendian for some reason
- * in the original PHP_Archive, so we will do the same
- */
-static inline void phar_set_16(char *buffer, int var) /* {{{ */
-{
-#ifdef WORDS_BIGENDIAN
- *((buffer) + 1) = (unsigned char) (((var) >> 8) & 0xFF); \
- *(buffer) = (unsigned char) ((var) & 0xFF);
-#else
- *(php_uint16 *)(buffer) = (php_uint16)(var);
-#endif
-} /* }}} */
-
-static int phar_flush_clean_deleted_apply(void *data TSRMLS_DC) /* {{{ */
-{
- phar_entry_info *entry = (phar_entry_info *)data;
-
- if (entry->fp_refcount <= 0 && entry->is_deleted) {
- return ZEND_HASH_APPLY_REMOVE;
- } else {
- return ZEND_HASH_APPLY_KEEP;
- }
-}
-/* }}} */
-
-/**
- * Save phar contents to disk
- *
- * user_stub contains either a string, or a resource pointer, if len is a negative length.
- * user_stub and len should be both 0 if the default or existing stub should be used
- */
-int phar_flush(phar_archive_data *archive, char *user_stub, long len, char **error TSRMLS_DC) /* {{{ */
-{
- static const char newstub[] = "<?php __HALT_COMPILER(); ?>\r\n";
- phar_entry_info *entry;
- int halt_offset, restore_alias_len, global_flags = 0, closeoldfile;
- char *buf, *pos;
- char manifest[18], entry_buffer[24];
- off_t manifest_ftell;
- long offset;
- size_t wrote;
- php_uint32 manifest_len, mytime, loc, new_manifest_count;
- php_uint32 newcrc32;
- php_stream *file, *oldfile, *newfile, *stubfile;
- php_stream_filter *filter;
- php_serialize_data_t metadata_hash;
- smart_str main_metadata_str = {0};
- int free_user_stub;
-
- if (error) {
- *error = NULL;
- }
-
- if (PHAR_G(readonly)) {
- return EOF;
- }
-
- if (archive->fp && !archive->is_brandnew) {
- oldfile = archive->fp;
- closeoldfile = 0;
- php_stream_rewind(oldfile);
- } else {
- oldfile = php_stream_open_wrapper(archive->fname, "rb", 0, NULL);
- closeoldfile = oldfile != NULL;
- }
- newfile = php_stream_fopen_tmpfile();
- if (!newfile) {
- if (error) {
- spprintf(error, 0, "unable to create temporary file");
- }
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- return EOF;
- }
-
- if (user_stub) {
- if (len < 0) {
- /* resource passed in */
- if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) {
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- if (error) {
- spprintf(error, 0, "unable to access resource to copy stub to new phar \"%s\"", archive->fname);
- }
- return EOF;
- }
- if (len == -1) {
- len = PHP_STREAM_COPY_ALL;
- } else {
- len = -len;
- }
- user_stub = 0;
- if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) {
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- if (error) {
- spprintf(error, 0, "unable to read resource to copy stub to new phar \"%s\"", archive->fname);
- }
- return EOF;
- }
- free_user_stub = 1;
- } else {
- free_user_stub = 0;
- }
- if ((pos = strstr(user_stub, "__HALT_COMPILER();")) == NULL)
- {
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- if (error) {
- spprintf(error, 0, "illegal stub for phar \"%s\"", archive->fname);
- }
- if (free_user_stub) {
- efree(user_stub);
- }
- return EOF;
- }
- len = pos - user_stub + 18;
- if ((size_t)len != php_stream_write(newfile, user_stub, len)
- || 5 != php_stream_write(newfile, " ?>\r\n", 5)) {
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- if (error) {
- spprintf(error, 0, "unable to create stub from string in new phar \"%s\"", archive->fname);
- }
- if (free_user_stub) {
- efree(user_stub);
- }
- return EOF;
- }
- archive->halt_offset = len + 5;
- if (free_user_stub) {
- efree(user_stub);
- }
- } else {
- if (archive->halt_offset && oldfile && !archive->is_brandnew) {
- if (archive->halt_offset != php_stream_copy_to_stream(oldfile, newfile, archive->halt_offset)) {
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- if (error) {
- spprintf(error, 0, "unable to copy stub of old phar to new phar \"%s\"", archive->fname);
- }
- return EOF;
- }
- } else {
- /* this is a brand new phar */
- archive->halt_offset = sizeof(newstub)-1;
- if (sizeof(newstub)-1 != php_stream_write(newfile, newstub, sizeof(newstub)-1)) {
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- if (error) {
- spprintf(error, 0, "unable to create stub in new phar \"%s\"", archive->fname);
- }
- return EOF;
- }
- }
- }
- manifest_ftell = php_stream_tell(newfile);
- halt_offset = manifest_ftell;
-
- /* Check whether we can get rid of some of the deleted entries which are
- * unused. However some might still be in use so even after this clean-up
- * we need to skip entries marked is_deleted. */
- zend_hash_apply(&archive->manifest, phar_flush_clean_deleted_apply TSRMLS_CC);
-
- /* compress as necessary, calculate crcs, serialize meta-data, manifest size, and file sizes */
- main_metadata_str.c = 0;
- if (archive->metadata) {
- PHP_VAR_SERIALIZE_INIT(metadata_hash);
- php_var_serialize(&main_metadata_str, &archive->metadata, &metadata_hash TSRMLS_CC);
- PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
- } else {
- main_metadata_str.len = 0;
- }
- new_manifest_count = 0;
- offset = 0;
- buf = (char *) emalloc(8192);
- for (zend_hash_internal_pointer_reset(&archive->manifest);
- zend_hash_has_more_elements(&archive->manifest) == SUCCESS;
- zend_hash_move_forward(&archive->manifest)) {
- if (zend_hash_get_current_data(&archive->manifest, (void **)&entry) == FAILURE) {
- continue;
- }
- if (entry->cfp) {
- /* did we forget to get rid of cfp last time? */
- php_stream_close(entry->cfp);
- entry->cfp = 0;
- }
- if (entry->is_deleted) {
- /* remove this from the new phar */
- continue;
- }
- /* after excluding deleted files, calculate manifest size in bytes and number of entries */
- ++new_manifest_count;
-
- if (entry->metadata) {
- if (entry->metadata_str.c) {
- smart_str_free(&entry->metadata_str);
- }
- entry->metadata_str.c = 0;
- entry->metadata_str.len = 0;
- PHP_VAR_SERIALIZE_INIT(metadata_hash);
- php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash TSRMLS_CC);
- PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
- } else {
- entry->metadata_str.c = 0;
- entry->metadata_str.len = 0;
- }
-
- offset += 4 + entry->filename_len + sizeof(entry_buffer) + entry->metadata_str.len;
-
- /* compress and rehash as necessary */
- if (oldfile && !entry->is_modified) {
- continue;
- }
- if (!entry->fp) {
- /* this should never happen */
- continue;
- }
- file = entry->fp;
- php_stream_rewind(file);
- newcrc32 = ~0;
- mytime = entry->uncompressed_filesize;
- for (loc = 0;loc < mytime; loc++) {
- CRC32(newcrc32, php_stream_getc(file));
- }
- entry->crc32 = ~newcrc32;
- entry->is_crc_checked = 1;
- if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
- /* not compressed */
- entry->compressed_filesize = entry->uncompressed_filesize;
- continue;
- }
- filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0 TSRMLS_CC);
- if (!filter) {
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
- if (error) {
- spprintf(error, 0, "unable to gzip compress file \"%s\" to new phar \"%s\"", entry->filename, archive->fname);
- }
- } else {
- if (error) {
- spprintf(error, 0, "unable to bzip2 compress file \"%s\" to new phar \"%s\"", entry->filename, archive->fname);
- }
- }
- efree(buf);
- return EOF;
- }
-
- /* create new file that holds the compressed version */
- /* work around inability to specify freedom in write and strictness
- in read count */
- entry->cfp = php_stream_fopen_tmpfile();
- if (!entry->cfp) {
- if (error) {
- spprintf(error, 0, "unable to create temporary file");
- }
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- efree(buf);
- return EOF;
- }
- php_stream_flush(file);
- php_stream_rewind(file);
- php_stream_filter_append(&file->readfilters, filter);
- entry->compressed_filesize = php_stream_copy_to_stream(file, entry->cfp, entry->uncompressed_filesize+8192 TSRMLS_CC);
- php_stream_filter_flush(filter, 1 TSRMLS_CC);
- entry->compressed_filesize += php_stream_copy_to_stream(file, entry->cfp, entry->uncompressed_filesize+8192 TSRMLS_CC);
- php_stream_filter_remove(filter, 1 TSRMLS_CC);
- /* generate crc on compressed file */
- php_stream_rewind(entry->cfp);
- entry->is_modified = 1;
- global_flags |= (entry->flags & PHAR_ENT_COMPRESSION_MASK);
- }
- efree(buf);
- global_flags |= PHAR_HDR_SIGNATURE;
-
- /* write out manifest pre-header */
- /* 4: manifest length
- * 4: manifest entry count
- * 2: phar version
- * 4: phar global flags
- * 4: alias length
- * ?: the alias itself
- * 4: phar metadata length
- * ?: phar metadata
- */
- restore_alias_len = archive->alias_len;
- if (!archive->is_explicit_alias) {
- archive->alias_len = 0;
- }
-
- manifest_len = offset + archive->alias_len + sizeof(manifest) + main_metadata_str.len;
- phar_set_32(manifest, manifest_len);
- phar_set_32(manifest+4, new_manifest_count);
- *(manifest + 8) = (unsigned char) (((PHAR_API_VERSION) >> 8) & 0xFF);
- *(manifest + 9) = (unsigned char) (((PHAR_API_VERSION) & 0xF0));
- phar_set_32(manifest+10, global_flags);
- phar_set_32(manifest+14, archive->alias_len);
-
- /* write the manifest header */
- if (sizeof(manifest) != php_stream_write(newfile, manifest, sizeof(manifest))
- || (size_t)archive->alias_len != php_stream_write(newfile, archive->alias, archive->alias_len)) {
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- archive->alias_len = restore_alias_len;
- if (error) {
- spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", archive->fname);
- }
- return EOF;
- }
-
- archive->alias_len = restore_alias_len;
-
- phar_set_32(manifest, main_metadata_str.len);
- if (main_metadata_str.len) {
- if (4 != php_stream_write(newfile, manifest, 4) ||
- main_metadata_str.len != php_stream_write(newfile, main_metadata_str.c, main_metadata_str.len)) {
- smart_str_free(&main_metadata_str);
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- archive->alias_len = restore_alias_len;
- if (error) {
- spprintf(error, 0, "unable to write manifest meta-data of new phar \"%s\"", archive->fname);
- }
- return EOF;
- }
- } else {
- if (4 != php_stream_write(newfile, manifest, 4)) {
- smart_str_free(&main_metadata_str);
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- archive->alias_len = restore_alias_len;
- if (error) {
- spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", archive->fname);
- }
- return EOF;
- }
- }
- smart_str_free(&main_metadata_str);
-
- /* re-calculate the manifest location to simplify later code */
- manifest_ftell = php_stream_tell(newfile);
-
- /* now write the manifest */
- for (zend_hash_internal_pointer_reset(&archive->manifest);
- zend_hash_has_more_elements(&archive->manifest) == SUCCESS;
- zend_hash_move_forward(&archive->manifest)) {
- if (zend_hash_get_current_data(&archive->manifest, (void **)&entry) == FAILURE) {
- continue;
- }
- if (entry->is_deleted) {
- /* remove this from the new phar */
- continue;
- }
- phar_set_32(entry_buffer, entry->filename_len);
- if (4 != php_stream_write(newfile, entry_buffer, 4)
- || entry->filename_len != php_stream_write(newfile, entry->filename, entry->filename_len)) {
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- if (error) {
- spprintf(error, 0, "unable to write filename of file \"%s\" to manifest of new phar \"%s\"", entry->filename, archive->fname);
- }
- return EOF;
- }
- /* set the manifest meta-data:
- 4: uncompressed filesize
- 4: creation timestamp
- 4: compressed filesize
- 4: crc32
- 4: flags
- 4: metadata-len
- +: metadata
- */
- mytime = time(NULL);
- phar_set_32(entry_buffer, entry->uncompressed_filesize);
- phar_set_32(entry_buffer+4, mytime);
- phar_set_32(entry_buffer+8, entry->compressed_filesize);
- phar_set_32(entry_buffer+12, entry->crc32);
- phar_set_32(entry_buffer+16, entry->flags);
- phar_set_32(entry_buffer+20, entry->metadata_str.len);
- if (sizeof(entry_buffer) != php_stream_write(newfile, entry_buffer, sizeof(entry_buffer))
- || entry->metadata_str.len != php_stream_write(newfile, entry->metadata_str.c, entry->metadata_str.len)) {
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- if (error) {
- spprintf(error, 0, "unable to write temporary manifest of file \"%s\" to manifest of new phar \"%s\"", entry->filename, archive->fname);
- }
- return EOF;
- }
- }
-
- /* now copy the actual file data to the new phar */
- offset = 0;
- for (zend_hash_internal_pointer_reset(&archive->manifest);
- zend_hash_has_more_elements(&archive->manifest) == SUCCESS;
- zend_hash_move_forward(&archive->manifest)) {
- if (zend_hash_get_current_data(&archive->manifest, (void **)&entry) == FAILURE) {
- continue;
- }
- if (entry->is_deleted) {
- continue;
- }
- if (entry->cfp) {
- file = entry->cfp;
- php_stream_rewind(file);
- } else if (entry->fp && (entry->is_modified || !oldfile)) {
- file = entry->fp;
- php_stream_rewind(file);
- } else {
- if (!oldfile) {
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- if (error) {
- spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, archive->fname);
- }
- return EOF;
- }
- if (-1 == php_stream_seek(oldfile, entry->offset_within_phar + archive->internal_file_start, SEEK_SET)) {
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- if (error) {
- spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, archive->fname);
- }
- return EOF;
- }
- file = oldfile;
- }
- /* this will have changed for all files that have either
- changed compression or been modified */
- entry->offset_within_phar = offset;
- offset += entry->compressed_filesize;
- wrote = php_stream_copy_to_stream(file, newfile, entry->compressed_filesize);
- if (entry->compressed_filesize != wrote) {
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- if (error) {
- spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\"", entry->filename, archive->fname);
- }
- return EOF;
- }
- entry->is_modified = 0;
- if (entry->cfp) {
- php_stream_close(entry->cfp);
- entry->cfp = 0;
- }
- }
-
- /* append signature */
- if (global_flags & PHAR_HDR_SIGNATURE) {
- unsigned char buf[1024];
- int sig_flags = 0, sig_len;
- char sig_buf[4];
-
- php_stream_rewind(newfile);
-
- if (archive->signature) {
- efree(archive->signature);
- }
-
- switch(archive->sig_flags) {
-#if HAVE_HASH_EXT
- case PHAR_SIG_SHA512: {
- unsigned char digest[64];
- PHP_SHA512_CTX context;
-
- PHP_SHA512Init(&context);
- while ((sig_len = php_stream_read(newfile, (char*)buf, sizeof(buf))) > 0) {
- PHP_SHA512Update(&context, buf, sig_len);
- }
- PHP_SHA512Final(digest, &context);
- php_stream_write(newfile, (char *) digest, sizeof(digest));
- sig_flags |= PHAR_SIG_SHA512;
- archive->sig_len = phar_hex_str((const char*)digest, sizeof(digest), &archive->signature);
- break;
- }
- case PHAR_SIG_SHA256: {
- unsigned char digest[32];
- PHP_SHA256_CTX context;
-
- PHP_SHA256Init(&context);
- while ((sig_len = php_stream_read(newfile, (char*)buf, sizeof(buf))) > 0) {
- PHP_SHA256Update(&context, buf, sig_len);
- }
- PHP_SHA256Final(digest, &context);
- php_stream_write(newfile, (char *) digest, sizeof(digest));
- sig_flags |= PHAR_SIG_SHA256;
- archive->sig_len = phar_hex_str((const char*)digest, sizeof(digest), &archive->signature);
- break;
- }
-#else
- case PHAR_SIG_SHA512:
- case PHAR_SIG_SHA256:
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- if (error) {
- spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\" with requested hash type", entry->filename, archive->fname);
- }
- return EOF;
-#endif
- case PHAR_SIG_PGP:
- /* TODO: currently fall back to sha1,later do both */
- default:
- case PHAR_SIG_SHA1: {
- unsigned char digest[20];
- PHP_SHA1_CTX context;
-
- PHP_SHA1Init(&context);
- while ((sig_len = php_stream_read(newfile, (char*)buf, sizeof(buf))) > 0) {
- PHP_SHA1Update(&context, buf, sig_len);
- }
- PHP_SHA1Final(digest, &context);
- php_stream_write(newfile, (char *) digest, sizeof(digest));
- sig_flags |= PHAR_SIG_SHA1;
- archive->sig_len = phar_hex_str((const char*)digest, sizeof(digest), &archive->signature);
- break;
- }
- case PHAR_SIG_MD5: {
- unsigned char digest[16];
- PHP_MD5_CTX context;
-
- PHP_MD5Init(&context);
- while ((sig_len = php_stream_read(newfile, (char*)buf, sizeof(buf))) > 0) {
- PHP_MD5Update(&context, buf, sig_len);
- }
- PHP_MD5Final(digest, &context);
- php_stream_write(newfile, (char *) digest, sizeof(digest));
- sig_flags |= PHAR_SIG_MD5;
- archive->sig_len = phar_hex_str((const char*)digest, sizeof(digest), &archive->signature);
- break;
- }
- }
- phar_set_32(sig_buf, sig_flags);
- php_stream_write(newfile, sig_buf, 4);
- php_stream_write(newfile, "GBMB", 4);
- archive->sig_flags = sig_flags;
- }
-
- /* finally, close the temp file, rename the original phar,
- move the temp to the old phar, unlink the old phar, and reload it into memory
- */
- if (archive->fp) {
- php_stream_close(archive->fp);
- }
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
-
- archive->internal_file_start = halt_offset + manifest_len + 4;
- archive->is_brandnew = 0;
-
- php_stream_rewind(newfile);
-
- if (archive->donotflush) {
- /* deferred flush */
- archive->fp = newfile;
- } else {
- archive->fp = php_stream_open_wrapper(archive->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
- if (!archive->fp) {
- archive->fp = newfile;
- if (error) {
- spprintf(error, 0, "unable to open new phar \"%s\" for writing", archive->fname);
- }
- return EOF;
- }
- php_stream_copy_to_stream(newfile, archive->fp, PHP_STREAM_COPY_ALL);
- php_stream_close(newfile);
- /* we could also reopen the file in "rb" mode but there is no need for that */
- }
-
- if (-1 == php_stream_seek(archive->fp, archive->halt_offset, SEEK_SET)) {
- if (error) {
- spprintf(error, 0, "unable to seek to __HALT_COMPILER(); in new phar \"%s\"", archive->fname);
- }
- return EOF;
- }
-
- return EOF;
-}
-/* }}} */
-
-/**
- * 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)->internal_file->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) {
- 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 {
- 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;
-
- resource = php_url_parse(url);
-
- if (!resource && (resource = phar_open_url(wrapper, url, "r", 0 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;
- }
-}
-/* }}}*/
-
-#if defined(PHP_VERSION_ID) && (PHP_VERSION_ID < 50300 || 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
-
-/**
- * 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;
-
- resource = php_url_parse(url);
-
- if (!resource && (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 *from_file, *to_file, *error, *plain_map;
- phar_entry_data *fromdata, *todata;
- uint host_len;
-
- if (PHAR_G(readonly)) {
- php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by INI setting");
- return 0;
- }
-
- resource_from = php_url_parse(url_from);
- resource_to = php_url_parse(url_from);
-
- if (!resource_from && (resource_from = phar_open_url(wrapper, url_from, "r+b", options TSRMLS_CC)) == NULL) {
- return 0;
- }
-
- if (!resource_to && (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_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;
- }
-
- /* need to copy to strip leading "/", will get touched again */
- from_file = estrdup(resource_from->path + 1);
- to_file = estrdup(resource_to->path + 1);
- if (FAILURE == phar_get_entry_data(&fromdata, resource_from->host, strlen(resource_from->host), from_file, strlen(from_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(from_file);
- efree(to_file);
- php_url_free(resource_from);
- php_url_free(resource_to);
- return 0;
- }
- if (error) {
- efree(error);
- }
- if (!fromdata) {
- php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" is not a file in phar \"%s\", cannot rename", from_file, resource_from->host);
- efree(from_file);
- efree(to_file);
- php_url_free(resource_from);
- php_url_free(resource_to);
- return 0;
- }
- if (!(todata = phar_get_or_create_entry_data(resource_to->host, strlen(resource_to->host), to_file, strlen(to_file), "w", &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(from_file);
- efree(to_file);
- php_url_free(resource_from);
- php_url_free(resource_to);
- return 0;
- }
- if (error) {
- efree(error);
- }
- if (fromdata->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 rename", from_file, resource_from->host);
- efree(from_file);
- efree(to_file);
- php_url_free(resource_from);
- php_url_free(resource_to);
- phar_entry_delref(fromdata TSRMLS_CC);
- phar_entry_delref(todata TSRMLS_CC);
- return 0;
- }
- if (todata->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 rename", to_file, resource_to->host);
- efree(from_file);
- efree(to_file);
- php_url_free(resource_from);
- php_url_free(resource_to);
- phar_entry_delref(fromdata TSRMLS_CC);
- phar_entry_delref(todata TSRMLS_CC);
- return 0;
- }
-
- php_stream_seek(fromdata->internal_file->fp, 0, SEEK_SET);
- if (fromdata->internal_file->uncompressed_filesize != php_stream_copy_to_stream(fromdata->internal_file->fp, todata->internal_file->fp, PHP_STREAM_COPY_ALL)) {
- php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: rename failed \"%s\" to \"%s\"", url_from, url_to);
- efree(from_file);
- efree(to_file);
- php_url_free(resource_from);
- php_url_free(resource_to);
- phar_entry_delref(fromdata TSRMLS_CC);
- phar_entry_delref(todata TSRMLS_CC);
- return 0;
- }
- phar_entry_delref(fromdata TSRMLS_CC);
- phar_entry_delref(todata TSRMLS_CC);
- efree(from_file);
- efree(to_file);
- php_url_free(resource_from);
- php_url_free(resource_to);
- phar_wrapper_unlink(wrapper, url_from, 0, 0 TSRMLS_CC);
- 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;
-
- resource = php_url_parse(path);
-
- if (!resource && (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
-
-/* {{{ phar_functions[]
- *
- * Every user visible function must have an entry in phar_functions[].
- */
-function_entry phar_functions[] = {
- {NULL, NULL, NULL} /* Must be the last line in phar_functions[] */
-};
-/* }}}*/
-
-/* {{{ php_phar_init_globals
- */
-static void php_phar_init_globals_module(zend_phar_globals *phar_globals)
-{
- memset(phar_globals, 0, sizeof(zend_phar_globals));
- phar_globals->readonly = 1;
-}
-/* }}} */
-
-PHP_MINIT_FUNCTION(phar) /* {{{ */
-{
- ZEND_INIT_MODULE_GLOBALS(phar, php_phar_init_globals_module, NULL);
- REGISTER_INI_ENTRIES();
-
- phar_object_init(TSRMLS_C);
-
- return php_register_url_stream_wrapper("phar", &php_stream_phar_wrapper TSRMLS_CC);
-}
-/* }}} */
-
-PHP_MSHUTDOWN_FUNCTION(phar) /* {{{ */
-{
- return php_unregister_url_stream_wrapper("phar" TSRMLS_CC);
-}
-/* }}} */
-
-void phar_request_initialize(TSRMLS_D) /* {{{ */
-{
- if (!PHAR_GLOBALS->request_init)
- {
- PHAR_GLOBALS->request_init = 1;
- PHAR_GLOBALS->request_ends = 0;
- PHAR_GLOBALS->request_done = 0;
- zend_hash_init(&(PHAR_GLOBALS->phar_fname_map), sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data, 0);
- zend_hash_init(&(PHAR_GLOBALS->phar_alias_map), sizeof(phar_archive_data*), zend_get_hash_value, NULL, 0);
- zend_hash_init(&(PHAR_GLOBALS->phar_plain_map), sizeof(const char *), zend_get_hash_value, NULL, 0);
- phar_split_extract_list(TSRMLS_C);
- }
-}
-/* }}} */
-
-PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */
-{
- PHAR_GLOBALS->request_ends = 1;
- if (PHAR_GLOBALS->request_init)
- {
- zend_hash_destroy(&(PHAR_GLOBALS->phar_alias_map));
- zend_hash_destroy(&(PHAR_GLOBALS->phar_fname_map));
- zend_hash_destroy(&(PHAR_GLOBALS->phar_plain_map));
- PHAR_GLOBALS->request_init = 0;
- }
- PHAR_GLOBALS->request_done = 1;
- return SUCCESS;
-}
-/* }}} */
-
-PHP_MINFO_FUNCTION(phar) /* {{{ */
-{
- php_info_print_table_start();
- php_info_print_table_header(2, "Phar: PHP Archive support", "enabled");
- php_info_print_table_row(2, "Phar EXT version", PHAR_EXT_VERSION_STR);
- php_info_print_table_row(2, "Phar API version", PHAR_API_VERSION_STR);
- php_info_print_table_row(2, "CVS revision", "$Revision$");
- php_info_print_table_row(2, "gzip compression",
-#if HAVE_ZLIB
- "enabled");
-#else
- "disabled");
-#endif
- php_info_print_table_row(2, "bzip2 compression",
-#if HAVE_BZ2
- "enabled");
-#else
- "disabled");
-#endif
- php_info_print_table_end();
-
- php_info_print_box_start(0);
- PUTS("Phar based on pear/PHP_Archive, original concept by Davey Shafik.");
- PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
- PUTS("Phar fully realized by Gregory Beaver and Marcus Boerger.");
- php_info_print_box_end();
-
- DISPLAY_INI_ENTRIES();
-}
-/* }}} */
-
-/* {{{ phar_module_entry
- */
-static zend_module_dep phar_deps[] = {
-#if HAVE_ZLIB
- ZEND_MOD_REQUIRED("zlib")
-#endif
-#if HAVE_BZ2
- ZEND_MOD_REQUIRED("bz2")
-#endif
-#if HAVE_SPL
- ZEND_MOD_REQUIRED("spl")
-#endif
- {NULL, NULL, NULL}
-};
-
-zend_module_entry phar_module_entry = {
- STANDARD_MODULE_HEADER_EX, NULL,
- phar_deps,
- "Phar",
- phar_functions,
- PHP_MINIT(phar),
- PHP_MSHUTDOWN(phar),
- NULL,
- PHP_RSHUTDOWN(phar),
- PHP_MINFO(phar),
- PHAR_EXT_VERSION_STR,
- STANDARD_MODULE_PROPERTIES
-};
-/* }}} */
-
-/*
- * 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
- */