diff options
author | SVN Migration <svn@php.net> | 2006-06-02 16:15:59 +0000 |
---|---|---|
committer | SVN Migration <svn@php.net> | 2006-06-02 16:15:59 +0000 |
commit | 5b1558c01fa9c4c1ba4c918a51a59f2e670e954c (patch) | |
tree | 8351842d1fd00eaf3d22b13ad23a2b02cf07d766 /ext/phar/phar.c | |
parent | 653007cea0bb87ccd39a746c7867eb10d907fd81 (diff) | |
download | php-git-BEFORE_NEW_OUTPUT_API.tar.gz |
This commit was manufactured by cvs2svn to create tagBEFORE_NEW_OUTPUT_API
'BEFORE_NEW_OUTPUT_API'.
Diffstat (limited to 'ext/phar/phar.c')
-rw-r--r-- | ext/phar/phar.c | 1922 |
1 files changed, 0 insertions, 1922 deletions
diff --git a/ext/phar/phar.c b/ext/phar/phar.c deleted file mode 100644 index 3f63e139e4..0000000000 --- a/ext/phar/phar.c +++ /dev/null @@ -1,1922 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | phar php single-file executable PHP extension | - +----------------------------------------------------------------------+ - | Copyright (c) 2005-2006 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$ */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <time.h> -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "ext/standard/url.h" -#include "ext/standard/crc32.h" -#include "ext/spl/spl_array.h" -#include "ext/spl/spl_directory.h" -#include "ext/spl/spl_engine.h" -#include "ext/spl/spl_exceptions.h" -#include "zend_constants.h" -#include "zend_execute.h" -#include "zend_exceptions.h" -#include "zend_hash.h" -#include "zend_interfaces.h" -#include "zend_operators.h" -#include "zend_qsort.h" -#include "php_phar.h" -#include "main/php_streams.h" -#ifdef HAVE_STDINT_H -#include <stdint.h> -#endif - -#ifndef TRUE - # define TRUE 1 - # define FALSE 0 -#endif - -#ifndef E_RECOVERABLE_ERROR -#define E_RECOVERABLE_ERROR E_ERROR -#endif - -#define PHAR_VERSION_STR "0.8.0" -/* x.y.z maps to 0xyz0 */ -#define PHAR_API_VERSION 0x0800 -#define PHAR_API_MAJORVERSION 0x0000 -#define PHAR_API_MAJORVER_MASK 0xF000 -#define PHAR_API_VER_MASK 0xFFF0 - -#define PHAR_HDR_ANY_COMPRESSED 0x0001 -#define PHAR_HDR_SIGNATURE 0x0008 - -/* flags byte for each file adheres to these bitmasks. - All unused values are reserved */ -#define PHAR_ENT_COMPRESSION_MASK 0x0F -#define PHAR_ENT_COMPRESSED_NONE 0x00 -#define PHAR_ENT_COMPRESSED_GZ 0x01 -#define PHAR_ENT_COMPRESSED_BZ2 0x02 - - -ZEND_BEGIN_MODULE_GLOBALS(phar) - HashTable phar_fname_map; - HashTable phar_alias_map; -ZEND_END_MODULE_GLOBALS(phar) - -ZEND_DECLARE_MODULE_GLOBALS(phar) - -#ifndef php_uint16 -# if SIZEOF_SHORT == 2 -# define php_uint16 unsigned short -# else -# define php_uint16 uint16_t -# endif -#endif - -typedef union _phar_archive_object phar_archive_object; -typedef union _phar_entry_object phar_entry_object; - -/* entry for one file in a phar file */ -typedef struct _phar_manifest_entry { - php_uint32 filename_len; - char *filename; - php_uint32 uncompressed_filesize; - php_uint32 timestamp; - long offset_within_phar; - php_uint32 compressed_filesize; - php_uint32 crc32; - char flags; - zend_bool crc_checked; - php_stream *fp; -} phar_entry_info; - -/* information about a phar file (the archive itself) */ -typedef struct _phar_archive_data { - char *fname; - int fname_len; - char *alias; - int alias_len; - char version[12]; - size_t internal_file_start; - zend_bool has_compressed_files; - HashTable manifest; - php_uint32 min_timestamp; - php_uint32 max_timestamp; - php_stream *fp; - int refcount; -} phar_archive_data; - -/* stream access data for one file entry in a phar file */ -typedef struct _phar_entry_data { - phar_archive_data *phar; - php_stream *fp; - phar_entry_info *internal_file; -} phar_entry_data; - -/* archive php object */ -union _phar_archive_object { - zend_object std; - spl_filesystem_object spl; - struct { - zend_object std; - phar_archive_data *archive; - } arc; -}; - -/* entry php object */ -union _phar_entry_object { - zend_object std; - spl_filesystem_object spl; - struct { - zend_object std; - phar_entry_info *entry; - } ent; -}; - -/* {{{ forward declarations */ -static php_stream *php_stream_phar_url_wrapper(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC); -static int phar_close(php_stream *stream, int close_handle TSRMLS_DC); -static int phar_closedir(php_stream *stream, int close_handle TSRMLS_DC); -static int phar_seekdir(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC); -static size_t phar_read(php_stream *stream, char *buf, size_t count TSRMLS_DC); -static size_t phar_readdir(php_stream *stream, char *buf, size_t count TSRMLS_DC); -static int phar_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC); - -static size_t phar_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC); -static int phar_flush(php_stream *stream TSRMLS_DC); -static int phar_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC); - -static int phar_stream_stat(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC); -static php_stream *phar_opendir(php_stream_wrapper *wrapper, char *filename, char *mode, - int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC); -/* }}} */ - -static zend_class_entry *phar_ce_archive; -static zend_class_entry *phar_ce_entry; - -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; - } - efree(data->fname); - zend_hash_destroy(&data->manifest); - if (data->fp) { - php_stream_close(data->fp); - } - data->fp = 0; - efree(data); -} -/* }}}*/ - -static void destroy_phar_data(void *pDest) /* {{{ */ -{ - phar_archive_data *phar_data = *(phar_archive_data **) pDest; - TSRMLS_FETCH(); - - if (--phar_data->refcount < 0) { - phar_destroy_phar_data(phar_data TSRMLS_CC); - } -} -/* }}}*/ - -static void phar_spl_foreign_dtor(spl_filesystem_object *object TSRMLS_DC) /* {{{ */ -{ - phar_archive_data *phar_data = (phar_archive_data *) object->oth; - - if (--phar_data->refcount < 0) { - phar_destroy_phar_data(phar_data TSRMLS_CC); - } -} -/* }}} */ - -static void phar_spl_foreign_clone(spl_filesystem_object *src, spl_filesystem_object *dst TSRMLS_DC) /* {{{ */ -{ - phar_archive_data *phar_data = (phar_archive_data *) dst->oth; - - phar_data->refcount++; -} -/* }}} */ - -static spl_other_handler phar_spl_foreign_handler = { - phar_spl_foreign_dtor, - phar_spl_foreign_clone -}; - -static void destroy_phar_manifest(void *pDest) /* {{{ */ -{ - phar_entry_info *entry = (phar_entry_info *)pDest; - - if (entry->fp) { - TSRMLS_FETCH(); - - php_stream_close(entry->fp); - } - entry->fp = 0; - efree(entry->filename); -} -/* }}} */ - -static phar_archive_data * phar_get_archive(char *fname, int fname_len, char *alias, int alias_len TSRMLS_DC) /* {{{ */ -{ - phar_archive_data *fd, **fd_ptr; - - 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))) { - php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, (*fd_ptr)->fname, fname); - return NULL; - } - return *fd_ptr; - } - } - if (fname && fname_len) { - if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void**)&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 fd; - } - if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), fname, fname_len, (void**)&fd_ptr)) { - return *fd_ptr; - } - } - return NULL; -} -/* }}} */ - -static phar_entry_info *phar_get_entry_info(phar_archive_data *phar, char *path, int path_len TSRMLS_DC) /* {{{ */ -{ - phar_entry_info *entry; - - if (path && *path == '/') { - path++; - path_len--; - } - if (SUCCESS == zend_hash_find(&phar->manifest, path, path_len, (void**)&entry)) { - return entry; - } - return NULL; -} -/* }}} */ - -static phar_entry_data *phar_get_entry_data(char *fname, int fname_len, char *path, int path_len TSRMLS_DC) /* {{{ */ -{ - phar_archive_data *phar; - phar_entry_info *entry; - phar_entry_data *ret; - - ret = NULL; - if ((phar = phar_get_archive(fname, fname_len, NULL, 0 TSRMLS_CC)) != NULL) { - if ((entry = phar_get_entry_info(phar, path, path_len TSRMLS_CC)) != NULL) { - ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data)); - ret->phar = phar; - ret->internal_file = entry; - if (entry->fp) { - /* transfer ownership */ - ret->fp = entry->fp; - php_stream_seek(ret->fp, 0, SEEK_SET); - entry->fp = 0; - } else { - ret->fp = 0; - } - } - } - return ret; -} -/* }}} */ - -/* {{{ proto string apiVersion() - * Returns the api version */ -PHP_METHOD(Phar, apiVersion) -{ - RETURN_STRINGL(PHAR_VERSION_STR, sizeof(PHAR_VERSION_STR)-1, 1); -} -/* }}}*/ - -/* {{{ proto bool canCompress() - * Returns whether phar extension supports compression using zlib */ -PHP_METHOD(Phar, canCompress) -{ -#if HAVE_ZLIB || HAVE_BZ2 - RETURN_TRUE; -#else - RETURN_FALSE; -#endif -} -/* }}} */ - -#define MAPPHAR_ALLOC_FAIL(msg) \ - php_stream_close(fp);\ - php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, msg, fname);\ - return FAILURE; - -#define MAPPHAR_FAIL(msg) \ - efree(savebuf);\ - 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 -#else -# define PHAR_GET_32(buffer, var) \ - var = *(php_uint32*)(buffer); \ - buffer += 4 -#endif - -static int phar_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, long halt_offset, phar_archive_data** pphar TSRMLS_DC) /* {{{ */ -{ - char b32[4], *buffer, *endbuffer, *savebuf; - phar_archive_data *mydata; - phar_entry_info entry; - php_uint32 manifest_len, manifest_count, manifest_index, tmp_len; - php_uint16 manifest_tag; - long offset; - int compressed = 0; - int register_alias; - - if (pphar) { - *pphar = NULL; - } - - if ((mydata = phar_get_archive(fname, fname_len, alias, alias_len TSRMLS_CC)) != NULL) { - /* Overloading or reloading an archive would only be possible if we */ - /* refcount everything to be sure no stream for any file in the */ - /* archive is open. */ - if (fname_len != mydata->fname_len || strncmp(fname, mydata->fname, fname_len)) { - php_stream_close(fp); - php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, mydata->fname, fname); - return FAILURE; - } else { - if (pphar) { - *pphar = mydata; - } - php_stream_close(fp); - return SUCCESS; - } - } - - /* 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)") - } - if (*buffer == ' ' && *(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)") - } - if ((char) nextchar == '\r') { - if (EOF == (nextchar = php_stream_getc(fp))) { - MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest)") - } - 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)") - } - 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 != php_stream_read(fp, buffer, manifest_len)) { - MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest)") - } - if (manifest_len < 10) { - 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 and global compressed flag */ - manifest_tag = (((unsigned char)buffer[0]) << 8) - + ((unsigned char)buffer[1]); - buffer += 2; - if ((manifest_tag & PHAR_API_VER_MASK) < PHAR_API_VERSION || - (manifest_tag & PHAR_API_MAJORVER_MASK) != PHAR_API_MAJORVERSION) - { - php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "phar \"%s\" is API version %1.u.%1.u.%1.u, and cannot be processed", fname, manifest_tag >> 12, (manifest_tag >> 8) & 0xF, (manifest_tag >> 4) & 0x0F); - efree(savebuf); - return FAILURE; - } - /* The lowest nibble contains the phar wide flags. The any compressed can */ - /* be ignored on reading because it is being generated anyways. */ - - /* 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 != tmp_len || strncmp(alias, buffer, tmp_len))) - { - buffer[tmp_len] = '\0'; - php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "cannot load phar \"%s\" with implicit alias \"%s\" under different alias \"%s\"", fname, buffer, alias); - efree(savebuf); - 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)") - } - - /* set up our manifest */ - mydata = emalloc(sizeof(phar_archive_data)); - zend_hash_init(&mydata->manifest, sizeof(phar_entry_info), - zend_get_hash_value, destroy_phar_manifest, 0); - offset = 0; - mydata->min_timestamp = 0; - mydata->max_timestamp = 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)") - } - 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 + 16 + 1 > 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); - entry.offset_within_phar = offset; - offset += entry.compressed_filesize; - entry.flags = *buffer++; - switch (entry.flags & PHAR_ENT_COMPRESSION_MASK) { - case PHAR_ENT_COMPRESSED_GZ: -#if !HAVE_ZLIB - MAPPHAR_FAIL("zlib extension is required for gz compressed .phar file \"%s\""); -#endif - compressed = 1; - break; - case PHAR_ENT_COMPRESSED_BZ2: -#if !HAVE_BZ2 - MAPPHAR_FAIL("bz2 extension is required for bzip2 compressed .phar file \"%s\""); -#endif - compressed = 1; - break; - default: - if (entry.uncompressed_filesize != entry.compressed_filesize) { - MAPPHAR_FAIL("internal corruption of phar \"%s\" (compressed and uncompressed size does not match for uncompressed entry)"); - } - break; - } - entry.crc_checked = 0; - entry.fp = NULL; - zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL); - } - - 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), "%u.%u.%u", manifest_tag >> 12, (manifest_tag >> 8) & 0xF, (manifest_tag >> 4) & 0xF); - mydata->internal_file_start = halt_offset + manifest_len + 4; - mydata->has_compressed_files = compressed; - mydata->fp = fp; - mydata->refcount = 0; - 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); - } - efree(savebuf); - - if (pphar) { - *pphar = mydata; - } - - return SUCCESS; -} -/* }}} */ - -static int phar_open_filename(char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar 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; - php_stream *fp; - phar_archive_data *phar; - - if ((phar = phar_get_archive(fname, fname_len, alias, alias_len TSRMLS_CC)) != NULL) { - if (fname_len != phar->fname_len || strncmp(fname, phar->fname, fname_len)) { - php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, phar->fname, fname); - return FAILURE; - } else { - if (pphar) { - *pphar = phar; - } - 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, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL); - - if (!fp) { - php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "unable to open phar for reading \"%s\"", fname); - return FAILURE; - } - - /* 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 (-1 == php_stream_seek(fp, 0, SEEK_SET)) { - 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 (php_stream_read(fp, buffer+tokenlen, readsize) < 0) { - MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest)") - } - 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 TSRMLS_CC); - } - - halt_offset += readsize; - memmove(buffer, buffer + tokenlen, readsize + 1); - } - - MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (__HALT_COMPILER(); not found)") - php_stream_close(fp); - return FAILURE; -} -/* }}} */ - -static int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_len, char **entry, int *entry_len TSRMLS_DC) /* {{{ */ -{ - char *pos_p, *pos_z, *ext_str; - int ext_len; - - if (!strncasecmp(filename, "phar://", 7)) { - filename += 7; - filename_len -= 7; - } - - pos_p = strstr(filename, ".phar.php"); - pos_z = strstr(filename, ".phar.gz"); - 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_p = strstr(filename, ".phar")) != NULL) { - ext_str = pos_p; - ext_len = 5; - } else { - 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; -} -/* }}} */ - -static php_url* phar_open_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options TSRMLS_DC) /* {{{ */ -{ - php_url *resource; - char *arch, *entry; - int arch_len, entry_len; - - if (!strncasecmp(filename, "phar://", 7)) { - - 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)", filename); - return NULL; - } - resource = ecalloc(1, sizeof(php_url)); - resource->scheme = estrndup("phar", 4); - resource->host = arch; - resource->path = entry; -#if MBO_0 - if (resource) { - fprintf(stderr, "Alias: %s\n", alias); - fprintf(stderr, "Scheme: %s\n", resource->scheme); -/* fprintf(stderr, "User: %s\n", resource->user);*/ -/* fprintf(stderr, "Pass: %s\n", resource->pass ? "***" : NULL);*/ - fprintf(stderr, "Host: %s\n", resource->host); -/* fprintf(stderr, "Port: %d\n", resource->port);*/ - fprintf(stderr, "Path: %s\n", resource->path); -/* fprintf(stderr, "Query: %s\n", resource->query);*/ -/* fprintf(stderr, "Fragment: %s\n", resource->fragment);*/ - } -#endif - if (phar_open_filename(resource->host, arch_len, NULL, 0, NULL TSRMLS_CC) == FAILURE) - { - php_url_free(resource); - return NULL; - } - - return resource; - } - - return NULL; -} -/* }}} */ - -static int phar_open_compiled_file(char *alias, int alias_len TSRMLS_DC) /* {{{ */ -{ - char *fname; - long halt_offset; - zval *halt_constant; - php_stream *fp; - - fname = zend_get_executed_filename(TSRMLS_C); - - if (!strcmp(fname, "[no active file]")) { - php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "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); - php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "__HALT_COMPILER(); must be declared in a phar"); - return FAILURE; - } - halt_offset = Z_LVAL(*halt_constant); - zval_ptr_dtor(&halt_constant); - - fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL); - - if (!fp) { - php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "unable to open phar for reading \"%s\"", fname); - return FAILURE; - } - - return phar_open_file(fp, fname, strlen(fname), alias, alias_len, halt_offset, NULL TSRMLS_CC); -} -/* }}} */ - -/* {{{ proto mixed Phar::mapPhar([string alias]) - * Reads the currently executed file (a phar) and registers its manifest */ -PHP_METHOD(Phar, mapPhar) -{ - char * alias = NULL; - int alias_len = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &alias, &alias_len) == FAILURE) { - return; - } - - RETURN_BOOL(phar_open_compiled_file(alias, alias_len TSRMLS_CC) == SUCCESS); -} /* }}} */ - -/* {{{ proto mixed Phar::loadPhar(string url [, string alias]) - * Loads a phar archive with an alias */ -PHP_METHOD(Phar, loadPhar) -{ - char *fname, *alias = NULL; - int fname_len, alias_len = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &fname, &fname_len, &alias, &alias_len) == FAILURE) { - return; - } - RETURN_BOOL(phar_open_filename(fname, fname_len, alias, alias_len, NULL TSRMLS_CC) == SUCCESS); -} /* }}} */ - -static php_stream_ops phar_ops = { - phar_write, /* write (does nothing) */ - phar_read, /* read */ - phar_close, /* close */ - phar_flush, /* flush (does nothing) */ - "phar stream", - phar_seek, /* seek */ - NULL, /* cast */ - phar_stat, /* stat */ - NULL, /* set option */ -}; - -static php_stream_ops phar_dir_ops = { - phar_write, /* write (does nothing) */ - phar_readdir, /* read */ - phar_closedir, /* close */ - phar_flush, /* flush (does nothing) */ - "phar stream", - phar_seekdir, /* seek */ - NULL, /* cast */ - phar_stat, /* stat */ - NULL, /* set option */ -}; - -static php_stream_wrapper_ops phar_stream_wops = { - php_stream_phar_url_wrapper, - NULL, /* stream_close */ - NULL, /* php_stream_phar_stat, */ - phar_stream_stat, /* stat_url */ - phar_opendir, /* opendir */ - "phar", - NULL, /* unlink */ - NULL, /* rename */ - NULL, /* create directory */ - NULL /* remove directory */ -}; - -php_stream_wrapper php_stream_phar_wrapper = { - &phar_stream_wops, - NULL, - 0 /* is_url */ -}; - -static int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_data *idata, php_uint32 crc32 TSRMLS_DC) /* {{{ */ -{ - unsigned int crc = ~0; - int len = idata->internal_file->uncompressed_filesize; - char c; - - php_stream_seek(idata->fp, 0, SEEK_SET); - - while (len--) { - php_stream_read(idata->fp, &c, 1); - CRC32(crc, c); - } - php_stream_seek(idata->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; - } -} -/* }}} */ - -static char * phar_decompress_filter(phar_entry_info * entry, int return_unknow) /* {{{ */ -{ - 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_unknow ? "unknown" : NULL; - } -} -/* }}} */ - -static php_stream * php_stream_phar_url_wrapper(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 *filter_name; - char tmpbuf[8]; - php_url *resource = NULL; - php_stream *fp, *fpf; - php_stream_filter *filter, *consumed; - php_uint32 offset; - - 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; - } - - /* strip leading "/" */ - internal_file = estrdup(resource->path + 1); - if (NULL == (idata = phar_get_entry_data(resource->host, strlen(resource->host), internal_file, strlen(internal_file) TSRMLS_CC))) { - 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); - idata->phar->refcount++; - efree(internal_file); - return fpf; - } - -#if PHP_MAJOR_VERSION < 6 - if (PG(safe_mode) && (!php_checkuid(idata->phar->fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) { - efree(idata); - efree(internal_file); - return NULL; - } -#endif - - if (php_check_open_basedir(idata->phar->fname TSRMLS_CC)) { - efree(idata); - 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); - efree(idata); - 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); - efree(idata); - 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); - efree(idata); - efree(internal_file); - return NULL; - } - /* Unfortunatley we cannot check the read position of fp after getting */ - /* uncompressed data because the new stream posiition is being changed */ - /* by the number of bytes read throughthe filter not by the raw number */ - /* bytes being consumed on the stream. Therefor use a consumed filter. */ - consumed = php_stream_filter_create("consumed", NULL, php_stream_is_persistent(fp) TSRMLS_CC); - php_stream_filter_append(&fp->readfilters, consumed); - php_stream_filter_append(&fp->readfilters, filter); - - idata->fp = php_stream_temp_new(); - 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); - php_stream_close(idata->fp); - efree(idata); - efree(internal_file); - return NULL; - } - php_stream_filter_flush(filter, 1); - php_stream_filter_remove(filter, 1 TSRMLS_CC); - php_stream_filter_flush(consumed, 1); - php_stream_filter_remove(consumed, 1 TSRMLS_CC); - if (offset + idata->internal_file->compressed_filesize != php_stream_tell(fp)) { - 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); - php_stream_close(idata->fp); - efree(idata); - 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(); - 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); - php_stream_close(idata->fp); - efree(idata); - efree(internal_file); - return NULL; - } - } - - /* check length, crc32 */ - if (phar_postprocess_file(wrapper, options, idata, idata->internal_file->crc32 TSRMLS_CC) != SUCCESS) { - php_stream_close(idata->fp); - efree(idata); - efree(internal_file); - return NULL; - } - idata->internal_file->crc_checked = 1; - - fpf = php_stream_alloc(&phar_ops, idata, NULL, mode); - idata->phar->refcount++; - efree(internal_file); - return fpf; -} -/* }}} */ - -static int phar_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */ -{ - phar_entry_data *data = (phar_entry_data *)stream->abstract; - - /* data->fp is the temporary memory stream containing this file's data */ - if (data->fp) { - /* only close if we have a cached temp memory stream */ - if (data->internal_file->fp) { - php_stream_close(data->fp); - } else { - data->internal_file->fp = data->fp; - } - } - if (--data->phar->refcount < 0) { - phar_destroy_phar_data(data->phar TSRMLS_CC); - } - - efree(data); - return 0; -} -/* }}} */ - -static int phar_closedir(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */ -{ - HashTable *data = (HashTable *)stream->abstract; - - if (data) - { - zend_hash_destroy(data); - FREE_HASHTABLE(data); - stream->abstract = NULL; - } - return 0; -} -/* }}} */ - -static int phar_seekdir(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; -} -/* }}} */ - -static size_t phar_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */ -{ - phar_entry_data *data = (phar_entry_data *)stream->abstract; - - size_t got = php_stream_read(data->fp, buf, count); - - if (data->fp->eof) { - stream->eof = 1; - } - - return got; -} -/* }}} */ - -static size_t phar_readdir(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); -} -/* }}} */ - -static int phar_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 = php_stream_seek(data->fp, offset, whence); - *newoffset = php_stream_tell(data->fp); - return res; -} -/* }}} */ - -static size_t phar_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */ -{ - return 0; -} -/* }}} */ - -static int phar_flush(php_stream *stream TSRMLS_DC) /* {{{ */ -{ - return EOF; -} -/* }}} */ - - /* {{{ phar_dostat */ -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)); - /* read-only across the board */ - ssb->sb.st_mode = 0444; - - if (!is_dir) { - ssb->sb.st_size = data->uncompressed_filesize; - 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 |= 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 - } - - 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 = zend_get_hash_value(tmp, tmp_len); - efree(tmp); -#ifndef PHP_WIN32 - ssb->sb.st_blksize = -1; - ssb->sb.st_blocks = -1; -#endif -} -/* }}}*/ - -static int phar_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */ -{ - phar_entry_data *data; - /* If ssb is NULL then someone is misbehaving */ - if (!ssb) return -1; - - data = (phar_entry_data *)stream->abstract; - phar_dostat(data->phar, data->internal_file, ssb, 0, data->phar->alias, data->phar->alias_len TSRMLS_CC); - return 0; -} -/* }}} */ - -static int phar_stream_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; - uint keylen; - ulong unused; - phar_archive_data *phar; - phar_entry_info *entry; - - 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; - } - - 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 ((phar = phar_get_archive(resource->host, strlen(resource->host), NULL, 0 TSRMLS_CC)) != NULL) { - 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; - } - /* 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 */ -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); -} -/* }}} */ - -static int 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; - } -} -/* }}} */ - -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); - - 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 *) emalloc (found - key + 1); - memcpy(entry, key, found - key); - keylen = found - key; - entry[keylen] = '\0'; - } else { - entry = (char *) emalloc (keylen + 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 *) emalloc (found - save + dirlen + 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 *) emalloc (keylen - dirlen + 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, 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; - } -} -/* }}}*/ - -static php_stream *phar_opendir(php_stream_wrapper *wrapper, char *filename, 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; - uint keylen; - ulong unused; - phar_archive_data *phar; - phar_entry_info *entry; - - resource = php_url_parse(filename); - - if (!resource && (resource = phar_open_url(wrapper, filename, 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", filename, 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/", filename, filename); - 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\"", filename); - return NULL; - } - - internal_file = resource->path + 1; /* strip leading "/" */ - if ((phar = phar_get_archive(resource->host, strlen(resource->host), NULL, 0 TSRMLS_CC)) != NULL) { - 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 (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; -} -/* }}} */ - -/* {{{ proto void Phar::__construct(string fname [, int flags [, string alias]]) - * Construct a Phar archive object - */ -PHP_METHOD(Phar, __construct) -{ - char *fname, *alias = NULL; - int fname_len, alias_len = 0; - long flags = 0; - phar_archive_object *phar_obj; - phar_archive_data *phar_data; - zval *zobj = getThis(), arg1, arg2; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls", &fname, &fname_len, &flags, &alias, &alias_len) == FAILURE) { - return; - } - - phar_obj = (phar_archive_object*)zend_object_store_get_object(getThis() TSRMLS_CC); - - if (phar_obj->arc.archive) { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot call constructor twice"); - return; - } - - if (phar_open_filename(fname, fname_len, alias, alias_len, &phar_data TSRMLS_CC) == FAILURE) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, - "Cannot open phar file '%s' with alias '%s'", fname, alias); - return; - } - - phar_data->refcount++; - phar_obj->arc.archive = phar_data; - phar_obj->spl.oth_handler = &phar_spl_foreign_handler; - - fname_len = spprintf(&fname, 0, "phar://%s", fname); - - INIT_PZVAL(&arg1); - ZVAL_STRINGL(&arg1, fname, fname_len, 0); - - if (ZEND_NUM_ARGS() > 1) { - INIT_PZVAL(&arg2); - ZVAL_LONG(&arg2, flags); - zend_call_method_with_2_params(&zobj, Z_OBJCE_P(zobj), - &spl_ce_RecursiveDirectoryIterator->constructor, "__construct", NULL, &arg1, &arg2); - } else { - zend_call_method_with_1_params(&zobj, Z_OBJCE_P(zobj), - &spl_ce_RecursiveDirectoryIterator->constructor, "__construct", NULL, &arg1); - } - - phar_obj->spl.info_class = phar_ce_entry; - - efree(fname); -} -/* }}} */ - -#define PHAR_ARCHIVE_OBJECT() \ - phar_archive_object *phar_obj = (phar_archive_object*)zend_object_store_get_object(getThis() TSRMLS_CC); \ - if (!phar_obj->arc.archive) { \ - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \ - "Cannot call method on an uninitialzed Phar object"); \ - return; \ - } - -/* {{{ proto int Phar::count() - * Returns the number of entries in the Phar archive - */ -PHP_METHOD(Phar, count) -{ - PHAR_ARCHIVE_OBJECT(); - - RETURN_LONG(zend_hash_num_elements(&phar_obj->arc.archive->manifest)); -} -/* }}} */ - -/* {{{ proto string Phar::getVersion() - * Return version info of Phar archive - */ -PHP_METHOD(Phar, getVersion) -{ - PHAR_ARCHIVE_OBJECT(); - - RETURN_STRING(phar_obj->arc.archive->version, 1); -} -/* }}} */ - -/* {{{ proto int Phar::offsetExists(string offset) - * determines whether a file exists in the phar - */ -PHP_METHOD(Phar, offsetExists) -{ - char *fname; - int fname_len; - PHAR_ARCHIVE_OBJECT(); - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &fname, &fname_len) == FAILURE) { - return; - } - - if (zend_hash_exists(&phar_obj->arc.archive->manifest, fname, (uint) fname_len)) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } -} -/* }}} */ - -/* {{{ proto int Phar::offsetGet(string offset) - * get a PharFileInfo object for a specific file - */ -PHP_METHOD(Phar, offsetGet) -{ - char *fname; - int fname_len; - zval *zfname; - PHAR_ARCHIVE_OBJECT(); - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &fname, &fname_len) == FAILURE) { - return; - } - - if (!phar_get_entry_info(phar_obj->arc.archive, fname, fname_len TSRMLS_CC)) { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s does not exist", fname); - } else { - fname_len = spprintf(&fname, 0, "phar://%s/%s", phar_obj->arc.archive->fname, fname); - MAKE_STD_ZVAL(zfname); - ZVAL_STRINGL(zfname, fname, fname_len, 0); - spl_instantiate_arg_ex1(phar_obj->spl.file_class, &return_value, 0, zfname TSRMLS_CC); - zval_ptr_dtor(&zfname); - } - -} -/* }}} */ - -/* {{{ proto int Phar::offsetSet(string offset, string value) - * set the contents of an internal file to those of an external file - */ -PHP_METHOD(Phar, offsetSet) -{ - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Operation currently not supported"); -} -/* }}} */ - -/* {{{ proto int Phar::offsetUnset() - * remove a file from a phar - */ -PHP_METHOD(Phar, offsetUnset) -{ - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Operation currently not supported"); -} -/* }}} */ - -/* {{{ proto void PharFileInfo::__construct(string entry) - * Construct a Phar entry object - */ -PHP_METHOD(PharFileInfo, __construct) -{ - char *fname, *arch, *entry; - int fname_len, arch_len, entry_len; - phar_entry_object *entry_obj; - phar_entry_info *entry_info; - phar_archive_data *phar_data; - zval *zobj = getThis(), arg1; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &fname, &fname_len) == FAILURE) { - return; - } - - entry_obj = (phar_entry_object*)zend_object_store_get_object(getThis() TSRMLS_CC); - - if (entry_obj->ent.entry) { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot call constructor twice"); - return; - } - - if (phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC) == FAILURE) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, - "Cannot access phar file entry '%s'", fname); - return; - } - - if (phar_open_filename(arch, arch_len, NULL, 0, &phar_data TSRMLS_CC) == FAILURE) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, - "Cannot open phar file '%s'", fname); - return; - } - - if ((entry_info = phar_get_entry_info(phar_data, entry, entry_len TSRMLS_CC)) == NULL) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, - "Cannot access phar file entry '%s' in archive '%s'", entry, arch); - return; - } - - entry_obj->ent.entry = entry_info; - - INIT_PZVAL(&arg1); - ZVAL_STRINGL(&arg1, fname, fname_len, 0); - - zend_call_method_with_1_params(&zobj, Z_OBJCE_P(zobj), - &spl_ce_SplFileInfo->constructor, "__construct", NULL, &arg1); -} -/* }}} */ - -#define PHAR_ENTRY_OBJECT() \ - phar_entry_object *entry_obj = (phar_entry_object*)zend_object_store_get_object(getThis() TSRMLS_CC); \ - if (!entry_obj->ent.entry) { \ - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \ - "Cannot call method on an uninitialzed PharFileInfo object"); \ - return; \ - } - -/* {{{ proto int PharFileInfo::getCompressedSize() - * Returns the compressed size - */ -PHP_METHOD(PharFileInfo, getCompressedSize) -{ - PHAR_ENTRY_OBJECT(); - - RETURN_LONG(entry_obj->ent.entry->compressed_filesize); -} -/* }}} */ - -/* {{{ proto int PharFileInfo::getCRC32() - * Returns CRC32 code or throws an exception if not CRC checked - */ -PHP_METHOD(PharFileInfo, getCRC32) -{ - PHAR_ENTRY_OBJECT(); - - if (entry_obj->ent.entry->crc_checked) { - RETURN_LONG(entry_obj->ent.entry->crc32); - } else { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \ - "Phar entry was not CRC checked"); \ - } -} -/* }}} */ - -/* {{{ proto int PharFileInfo::getPharFlags() - * Returns the Phar file entry flags - */ -PHP_METHOD(PharFileInfo, getPharFlags) -{ - PHAR_ENTRY_OBJECT(); - - RETURN_LONG(entry_obj->ent.entry->flags); -} -/* }}} */ - -/* {{{ proto int PharFileInfo::isCRCChecked() - * Returns whether file entry is CRC checked - */ -PHP_METHOD(PharFileInfo, isCRCChecked) -{ - PHAR_ENTRY_OBJECT(); - - RETURN_BOOL(entry_obj->ent.entry->crc_checked); -} -/* }}} */ - -#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[] */ -}; - -static -ZEND_BEGIN_ARG_INFO_EX(arginfo_phar___construct, 0, 0, 1) - ZEND_ARG_INFO(0, fname) - ZEND_ARG_INFO(0, flags) - ZEND_ARG_INFO(0, alias) -ZEND_END_ARG_INFO(); - -static -ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_mapPhar, 0, 0, 0) - ZEND_ARG_INFO(0, alias) -ZEND_END_ARG_INFO(); - -static -ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_loadPhar, 0, 0, 1) - ZEND_ARG_INFO(0, fname) - ZEND_ARG_INFO(0, alias) -ZEND_END_ARG_INFO(); - -static -ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_offsetExists, 0, 0, 1) - ZEND_ARG_INFO(0, entry) -ZEND_END_ARG_INFO(); - -static -ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_offsetSet, 0, 0, 2) - ZEND_ARG_INFO(0, entry) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO(); - -zend_function_entry php_archive_methods[] = { - PHP_ME(Phar, __construct, arginfo_phar___construct, 0) - PHP_ME(Phar, count, NULL, 0) - PHP_ME(Phar, getVersion, NULL, 0) - PHP_ME(Phar, offsetGet, arginfo_phar_offsetExists, ZEND_ACC_PUBLIC) - PHP_ME(Phar, offsetSet, arginfo_phar_offsetSet, ZEND_ACC_PUBLIC) - PHP_ME(Phar, offsetUnset, arginfo_phar_offsetExists, ZEND_ACC_PUBLIC) - PHP_ME(Phar, offsetExists, arginfo_phar_offsetExists, ZEND_ACC_PUBLIC) - /* static member functions */ - PHP_ME(Phar, apiVersion, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) - PHP_ME(Phar, canCompress, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) - PHP_ME(Phar, mapPhar, arginfo_phar_mapPhar, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) - PHP_ME(Phar, loadPhar, arginfo_phar_loadPhar, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) - {NULL, NULL, NULL} -}; - -static -ZEND_BEGIN_ARG_INFO_EX(arginfo_entry___construct, 0, 0, 1) - ZEND_ARG_INFO(0, fname) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO(); - -zend_function_entry php_entry_methods[] = { - PHP_ME(PharFileInfo, __construct, arginfo_entry___construct, 0) - PHP_ME(PharFileInfo, getCompressedSize, NULL, 0) - PHP_ME(PharFileInfo, getCRC32, NULL, 0) - PHP_ME(PharFileInfo, getPharFlags, NULL, 0) - PHP_ME(PharFileInfo, isCRCChecked, NULL, 0) - {NULL, NULL, NULL} -}; -/* }}} */ - -/* {{{ php_phar_init_globals - */ -static void php_phar_init_globals_module(zend_phar_globals *phar_globals) -{ - memset(phar_globals, 0, sizeof(zend_phar_globals)); -} -/* }}} */ - -PHP_MINIT_FUNCTION(phar) /* {{{ */ -{ - zend_class_entry ce; - - ZEND_INIT_MODULE_GLOBALS(phar, php_phar_init_globals_module, NULL); - - INIT_CLASS_ENTRY(ce, "Phar", php_archive_methods); - phar_ce_archive = zend_register_internal_class_ex(&ce, spl_ce_RecursiveDirectoryIterator, NULL TSRMLS_CC); - - zend_class_implements(phar_ce_archive TSRMLS_CC, 2, spl_ce_Countable, zend_ce_arrayaccess); - - INIT_CLASS_ENTRY(ce, "PharFileInfo", php_entry_methods); - phar_ce_entry = zend_register_internal_class_ex(&ce, spl_ce_SplFileInfo, NULL TSRMLS_CC); - - 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); -} -/* }}} */ - -PHP_RINIT_FUNCTION(phar) /* {{{ */ -{ - 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); - return SUCCESS; -} -/* }}} */ - -PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */ -{ - zend_hash_destroy(&(PHAR_GLOBALS->phar_alias_map)); - zend_hash_destroy(&(PHAR_GLOBALS->phar_fname_map)); - 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 API version", PHAR_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_row(1, "Phar based on pear/PHP_Archive, original concept by Davey Shafik and fully realized by Gregory Beaver"); - php_info_print_table_end(); -} -/* }}} */ - -/* {{{ 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 - ZEND_MOD_REQUIRED("spl") - {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), - PHP_RINIT(phar), - PHP_RSHUTDOWN(phar), - PHP_MINFO(phar), - PHAR_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 - */ |