#define PHAR_MAIN 1
#include "phar_internal.h"
#include "SAPI.h"
#include "func_interceptors.h"
static void destroy_phar_data(void *pDest);
ZEND_DECLARE_MODULE_GLOBALS(phar)
char *(*phar_save_resolve_path)(const char *filename, int filename_len TSRMLS_DC);
static int phar_set_writeable_bit(void *pDest, void *argument TSRMLS_DC)
{
zend_bool keep = *(zend_bool *)argument;
phar_archive_data *phar = *(phar_archive_data **)pDest;
if (!phar->is_data) {
phar->is_writeable = !keep;
}
return ZEND_HASH_APPLY_KEEP;
}
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);
}
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;
if (PHAR_GLOBALS->request_init && PHAR_GLOBALS->phar_fname_map.arBuckets) {
zend_hash_apply_with_argument(&(PHAR_GLOBALS->phar_fname_map), phar_set_writeable_bit, (void *)&ini TSRMLS_CC);
}
} else {
PHAR_G(require_hash) = ini;
}
return SUCCESS;
}
HashTable cached_phars;
HashTable cached_alias;
static void phar_split_cache_list(TSRMLS_D)
{
char *tmp;
char *key, *lasts, *end;
char ds[2];
phar_archive_data *phar;
uint i = 0;
if (!PHAR_GLOBALS->cache_list || !(PHAR_GLOBALS->cache_list[0])) {
return;
}
ds[0] = DEFAULT_DIR_SEPARATOR;
ds[1] = '\0';
tmp = estrdup(PHAR_GLOBALS->cache_list);
PHAR_GLOBALS->request_init = 1;
if (zend_hash_init(&EG(regular_list), 0, NULL, NULL, 0) == SUCCESS) {
EG(regular_list).nNextFreeElement=1;
}
PHAR_G(has_bz2) = zend_hash_exists(&module_registry, "bz2", sizeof("bz2"));
PHAR_G(has_zlib) = zend_hash_exists(&module_registry, "zlib", sizeof("zlib"));
zend_hash_init(&cached_phars, sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data, 1);
zend_hash_init(&cached_alias, sizeof(phar_archive_data*), zend_get_hash_value, NULL, 1);
zend_hash_init(&(PHAR_GLOBALS->phar_fname_map), sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data, 1);
zend_hash_init(&(PHAR_GLOBALS->phar_alias_map), sizeof(phar_archive_data*), zend_get_hash_value, NULL, 1);
PHAR_GLOBALS->manifest_cached = 1;
PHAR_GLOBALS->persist = 1;
for (key = php_strtok_r(tmp, ds, &lasts);
key;
key = php_strtok_r(NULL, ds, &lasts)) {
end = strchr(key, DEFAULT_DIR_SEPARATOR);
if (end) {
if (SUCCESS == phar_open_from_filename(key, end - key, NULL, 0, 0, &phar, NULL TSRMLS_CC)) {
finish_up:
phar->phar_pos = i++;
php_stream_close(phar->fp);
phar->fp = NULL;
} else {
finish_error:
PHAR_GLOBALS->persist = 0;
PHAR_GLOBALS->manifest_cached = 0;
efree(tmp);
zend_hash_destroy(&(PHAR_G(phar_fname_map)));
PHAR_GLOBALS->phar_fname_map.arBuckets = 0;
zend_hash_destroy(&(PHAR_G(phar_alias_map)));
PHAR_GLOBALS->phar_alias_map.arBuckets = 0;
zend_hash_destroy(&cached_phars);
zend_hash_destroy(&cached_alias);
zend_hash_graceful_reverse_destroy(&EG(regular_list));
memset(&EG(regular_list), 0, sizeof(HashTable));
PHAR_GLOBALS->request_init = 0;
return;
}
} else {
if (SUCCESS == phar_open_from_filename(key, strlen(key), NULL, 0, 0, &phar, NULL TSRMLS_CC)) {
goto finish_up;
} else {
goto finish_error;
}
}
}
PHAR_GLOBALS->persist = 0;
PHAR_GLOBALS->request_init = 0;
zend_hash_destroy(&cached_phars);
zend_hash_destroy(&cached_alias);
cached_phars = PHAR_GLOBALS->phar_fname_map;
cached_alias = PHAR_GLOBALS->phar_alias_map;
PHAR_GLOBALS->phar_fname_map.arBuckets = 0;
PHAR_GLOBALS->phar_alias_map.arBuckets = 0;
zend_hash_graceful_reverse_destroy(&EG(regular_list));
memset(&EG(regular_list), 0, sizeof(HashTable));
efree(tmp);
}
ZEND_INI_MH(phar_ini_cache_list)
{
PHAR_G(cache_list) = new_value;
if (stage == ZEND_INI_STAGE_STARTUP) {
phar_split_cache_list(TSRMLS_C);
}
return SUCCESS;
}
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("phar.cache_list", "", PHP_INI_SYSTEM, phar_ini_cache_list, cache_list, zend_phar_globals, phar_globals)
PHP_INI_END()
void phar_destroy_phar_data(phar_archive_data *phar TSRMLS_DC)
{
if (phar->alias && phar->alias != phar->fname) {
pefree(phar->alias, phar->is_persistent);
phar->alias = NULL;
}
if (phar->fname) {
pefree(phar->fname, phar->is_persistent);
phar->fname = NULL;
}
if (phar->signature) {
pefree(phar->signature, phar->is_persistent);
phar->signature = NULL;
}
if (phar->manifest.arBuckets) {
zend_hash_destroy(&phar->manifest);
phar->manifest.arBuckets = NULL;
}
if (phar->mounted_dirs.arBuckets) {
zend_hash_destroy(&phar->mounted_dirs);
phar->mounted_dirs.arBuckets = NULL;
}
if (phar->virtual_dirs.arBuckets) {
zend_hash_destroy(&phar->virtual_dirs);
phar->virtual_dirs.arBuckets = NULL;
}
if (phar->metadata) {
if (phar->is_persistent) {
if (phar->metadata_len) {
free(phar->metadata);
} else {
zval_internal_ptr_dtor(&phar->metadata);
}
} else {
zval_ptr_dtor(&phar->metadata);
}
phar->metadata_len = 0;
phar->metadata = 0;
}
if (phar->fp) {
php_stream_close(phar->fp);
phar->fp = 0;
}
if (phar->ufp) {
php_stream_close(phar->ufp);
phar->ufp = 0;
}
pefree(phar, phar->is_persistent);
}
int phar_archive_delref(phar_archive_data *phar TSRMLS_DC)
{
if (phar->is_persistent) {
return 0;
}
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;
} else if (!phar->refcount) {
PHAR_G(last_phar) = NULL;
PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
if (phar->fp && !(phar->flags & PHAR_FILE_COMPRESSION_MASK)) {
php_stream_close(phar->fp);
phar->fp = NULL;
}
if (!zend_hash_num_elements(&phar->manifest)) {
if (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;
}
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);
}
}
static int phar_unalias_apply(void *pDest, void *argument TSRMLS_DC)
{
return *(void**)pDest == argument ? ZEND_HASH_APPLY_REMOVE : ZEND_HASH_APPLY_KEEP;
}
static int phar_tmpclose_apply(void *pDest TSRMLS_DC)
{
phar_entry_info *entry = (phar_entry_info *) pDest;
if (entry->fp_type != PHAR_TMP) {
return ZEND_HASH_APPLY_KEEP;
}
if (entry->fp && !entry->fp_refcount) {
php_stream_close(entry->fp);
entry->fp = NULL;
}
return ZEND_HASH_APPLY_KEEP;
}
static void destroy_phar_data(void *pDest)
{
phar_archive_data *phar_data = *(phar_archive_data **) pDest;
TSRMLS_FETCH();
if (PHAR_GLOBALS->request_ends) {
zend_hash_apply(&(phar_data->manifest), phar_tmpclose_apply TSRMLS_CC);
destroy_phar_data_only(pDest);
return;
}
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);
}
}
void destroy_phar_manifest_entry(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) {
if (entry->is_persistent) {
if (entry->metadata_len) {
free(entry->metadata);
} else {
zval_internal_ptr_dtor(&entry->metadata);
}
} else {
zval_ptr_dtor(&entry->metadata);
}
entry->metadata_len = 0;
entry->metadata = 0;
}
if (entry->metadata_str.c) {
smart_str_free(&entry->metadata_str);
entry->metadata_str.c = 0;
}
pefree(entry->filename, entry->is_persistent);
if (entry->link) {
pefree(entry->link, entry->is_persistent);
entry->link = 0;
}
if (entry->tmp) {
pefree(entry->tmp, entry->is_persistent);
entry->tmp = 0;
}
}
int phar_entry_delref(phar_entry_data *idata TSRMLS_DC)
{
int ret = 0;
if (idata->internal_file && !idata->internal_file->is_persistent) {
if (--idata->internal_file->fp_refcount < 0) {
idata->internal_file->fp_refcount = 0;
}
if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) {
php_stream_close(idata->fp);
}
if (idata->internal_file->is_temp_dir) {
destroy_phar_manifest_entry((void *)idata->internal_file);
efree(idata->internal_file);
}
}
phar_archive_delref(idata->phar TSRMLS_CC);
efree(idata);
return ret;
}
void phar_entry_remove(phar_entry_data *idata, char **error TSRMLS_DC)
{
phar_archive_data *phar;
phar = idata->phar;
if (idata->internal_file->fp_refcount < 2) {
if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && 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);
}
if (!phar->donotflush) {
phar_flush(phar, 0, 0, 0, error TSRMLS_CC);
}
}
#define MAPPHAR_ALLOC_FAIL(msg) \
if (fp) {\
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) {\
pefree(signature, PHAR_G(persist));\
}\
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) \
memcpy(&var, buffer, sizeof(var)); \
buffer += 4
# define PHAR_GET_16(buffer, var) \
var = *(php_uint16*)(buffer); \
buffer += 2
#endif
#define PHAR_ZIP_16(var) ((php_uint16)((((php_uint16)var[0]) & 0xff) | \
(((php_uint16)var[1]) & 0xff) << 8))
#define PHAR_ZIP_32(var) ((php_uint32)((((php_uint32)var[0]) & 0xff) | \
(((php_uint32)var[1]) & 0xff) << 8 | \
(((php_uint32)var[2]) & 0xff) << 16 | \
(((php_uint32)var[3]) & 0xff) << 24))
int phar_open_parsed_phar(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC)
{
phar_archive_data *phar;
#ifdef PHP_WIN32
char *unixfname;
#endif
if (error) {
*error = NULL;
}
#ifdef PHP_WIN32
unixfname = estrndup(fname, fname_len);
phar_unixify_path_separators(unixfname, fname_len);
if (SUCCESS == phar_get_archive(&phar, unixfname, fname_len, alias, alias_len, error TSRMLS_CC)
&& ((alias && fname_len == phar->fname_len
&& !strncmp(unixfname, phar->fname, fname_len)) || !alias)
) {
phar_entry_info *stub;
efree(unixfname);
#else
if (SUCCESS == phar_get_archive(&phar, fname, fname_len, alias, alias_len, error TSRMLS_CC)
&& ((alias && fname_len == phar->fname_len
&& !strncmp(fname, phar->fname, fname_len)) || !alias)
) {
phar_entry_info *stub;
#endif
if (!is_data) {
if (!phar->halt_offset && !phar->is_brandnew && (phar->is_tar || phar->is_zip)) {
if (PHAR_G(readonly) && FAILURE == zend_hash_find(&(phar->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&stub)) {
if (error) {
spprintf(error, 0, "'%s' is not a phar archive. Use PharData::__construct() for a standard zip or tar archive", fname);
}
return FAILURE;
}
}
}
if (pphar) {
*pphar = phar;
}
return SUCCESS;
} else {
#ifdef PHP_WIN32
efree(unixfname);
#endif
if (pphar) {
*pphar = NULL;
}
if (phar && error && !(options & REPORT_ERRORS)) {
efree(error);
}
return FAILURE;
}
}
int phar_parse_metadata(char **buffer, zval **metadata, php_uint32 zip_metadata_len TSRMLS_DC)
{
php_unserialize_data_t var_hash;
if (zip_metadata_len) {
const unsigned char *p;
unsigned char *p_buff = (unsigned char *)estrndup(*buffer, zip_metadata_len);
p = p_buff;
ALLOC_ZVAL(*metadata);
INIT_ZVAL(**metadata);
PHP_VAR_UNSERIALIZE_INIT(var_hash);
if (!php_var_unserialize(metadata, &p, p + zip_metadata_len, &var_hash TSRMLS_CC)) {
efree(p_buff);
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
zval_ptr_dtor(metadata);
*metadata = NULL;
return FAILURE;
}
efree(p_buff);
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
if (PHAR_G(persist)) {
zval_ptr_dtor(metadata);
*metadata = (zval *) pemalloc(zip_metadata_len, 1);
memcpy(*metadata, *buffer, zip_metadata_len);
return SUCCESS;
}
} else {
*metadata = NULL;
}
return SUCCESS;
}
static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, long halt_offset, phar_archive_data** pphar, php_uint32 compression, 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;
php_uint32 len;
long offset;
int sig_len, register_alias = 0, temp_alias = 0;
char *signature = NULL;
if (pphar) {
*pphar = NULL;
}
if (error) {
*error = NULL;
}
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 (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;
}
}
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 (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 * 100) {
MAPPHAR_ALLOC_FAIL("manifest cannot be larger than 100 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)")
}
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");
}
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;
manifest_flags &= ~PHAR_FILE_COMPRESSION_MASK;
manifest_flags |= compression;
if (manifest_flags & PHAR_HDR_SIGNATURE) {
char sig_buf[8], *sig_ptr = sig_buf;
off_t read_len;
size_t end_of_phar;
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) {
case PHAR_SIG_OPENSSL: {
php_uint32 signature_len;
char *sig;
off_t whence;
if (-1 == php_stream_seek(fp, -12, SEEK_CUR)
|| 4 != php_stream_read(fp, sig_buf, 4)) {
efree(savebuf);
php_stream_close(fp);
if (error) {
spprintf(error, 0, "phar \"%s\" openssl signature length could not be read", fname);
}
return FAILURE;
}
sig_ptr = sig_buf;
PHAR_GET_32(sig_ptr, signature_len);
sig = (char *) emalloc(signature_len);
whence = signature_len + 4;
whence = -whence;
if (-1 == php_stream_seek(fp, whence, SEEK_CUR)
|| !(end_of_phar = php_stream_tell(fp))
|| signature_len != php_stream_read(fp, sig, signature_len)) {
efree(savebuf);
efree(sig);
php_stream_close(fp);
if (error) {
spprintf(error, 0, "phar \"%s\" openssl signature could not be read", fname);
}
return FAILURE;
}
if (FAILURE == phar_verify_signature(fp, end_of_phar, PHAR_SIG_OPENSSL, sig, signature_len, fname, &signature, &sig_len, error TSRMLS_CC)) {
efree(savebuf);
efree(sig);
php_stream_close(fp);
if (error) {
char *save = *error;
spprintf(error, 0, "phar \"%s\" openssl signature could not be verified: %s", fname, *error);
efree(save);
}
return FAILURE;
}
efree(sig);
}
break;
#if PHAR_HASH_OK
case PHAR_SIG_SHA512: {
unsigned char digest[64];
php_stream_seek(fp, -(8 + 64), SEEK_END);
read_len = php_stream_tell(fp);
if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
efree(savebuf);
php_stream_close(fp);
if (error) {
spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
}
return FAILURE;
}
if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA512, (char *)digest, 64, fname, &signature, &sig_len, error TSRMLS_CC)) {
efree(savebuf);
php_stream_close(fp);
if (error) {
char *save = *error;
spprintf(error, 0, "phar \"%s\" SHA512 signature could not be verified: %s", fname, *error);
efree(save);
}
return FAILURE;
}
break;
}
case PHAR_SIG_SHA256: {
unsigned char digest[32];
php_stream_seek(fp, -(8 + 32), SEEK_END);
read_len = php_stream_tell(fp);
if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
efree(savebuf);
php_stream_close(fp);
if (error) {
spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
}
return FAILURE;
}
if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA256, (char *)digest, 32, fname, &signature, &sig_len, error TSRMLS_CC)) {
efree(savebuf);
php_stream_close(fp);
if (error) {
char *save = *error;
spprintf(error, 0, "phar \"%s\" SHA256 signature could not be verified: %s", fname, *error);
efree(save);
}
return FAILURE;
}
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];
php_stream_seek(fp, -(8 + 20), SEEK_END);
read_len = php_stream_tell(fp);
if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
efree(savebuf);
php_stream_close(fp);
if (error) {
spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
}
return FAILURE;
}
if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA1, (char *)digest, 20, fname, &signature, &sig_len, error TSRMLS_CC)) {
efree(savebuf);
php_stream_close(fp);
if (error) {
char *save = *error;
spprintf(error, 0, "phar \"%s\" SHA1 signature could not be verified: %s", fname, *error);
efree(save);
}
return FAILURE;
}
break;
}
case PHAR_SIG_MD5: {
unsigned char digest[16];
php_stream_seek(fp, -(8 + 16), SEEK_END);
read_len = php_stream_tell(fp);
if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
efree(savebuf);
php_stream_close(fp);
if (error) {
spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
}
return FAILURE;
}
if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_MD5, (char *)digest, 16, fname, &signature, &sig_len, error TSRMLS_CC)) {
efree(savebuf);
php_stream_close(fp);
if (error) {
char *save = *error;
spprintf(error, 0, "phar \"%s\" MD5 signature could not be verified: %s", fname, *error);
efree(save);
}
return FAILURE;
}
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;
}
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)")
}
if (tmp_len) {
if (alias && alias_len && (alias_len != (int)tmp_len || strncmp(alias, buffer, tmp_len)))
{
buffer[tmp_len] = '\0';
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);
}
efree(savebuf);
return FAILURE;
}
alias_len = tmp_len;
alias = buffer;
buffer += tmp_len;
register_alias = 1;
} else if (!alias_len || !alias) {
alias = NULL;
alias_len = 0;
register_alias = 0;
} else if (alias_len) {
register_alias = 1;
temp_alias = 1;
}
if (manifest_count > ((manifest_len - 10 - tmp_len) / (5 * 4 + 1))) {
MAPPHAR_FAIL("internal corruption of phar \"%s\" (too many manifest entries for size of manifest)")
}
mydata = pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist));
mydata->is_persistent = PHAR_G(persist);
PHAR_GET_32(buffer, len);
if (mydata->is_persistent) {
mydata->metadata_len = len;
if(!len) {
PHAR_GET_32(buffer, len);
}
}
if(len > endbuffer - buffer) {
MAPPHAR_FAIL("internal corruption of phar \"%s\" (trying to read past buffer end)");
}
if (phar_parse_metadata(&buffer, &mydata->metadata, len TSRMLS_CC) == FAILURE) {
MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\"");
}
buffer += len;
zend_hash_init(&mydata->manifest, manifest_count,
zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)mydata->is_persistent);
zend_hash_init(&mydata->mounted_dirs, 5,
zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
zend_hash_init(&mydata->virtual_dirs, manifest_count * 2,
zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
mydata->fname = pestrndup(fname, fname_len, mydata->is_persistent);
#ifdef PHP_WIN32
phar_unixify_path_separators(mydata->fname, fname_len);
#endif
mydata->fname_len = fname_len;
offset = halt_offset + manifest_len + 4;
memset(&entry, 0, sizeof(phar_entry_info));
entry.phar = mydata;
entry.fp_type = PHAR_FP;
entry.is_persistent = mydata->is_persistent;
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 (entry.is_persistent) {
entry.manifest_pos = manifest_index;
}
if (entry.filename_len + 20 > endbuffer - buffer) {
MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)");
}
if ((manifest_ver & PHAR_API_VER_MASK) >= PHAR_API_MIN_DIR && buffer[entry.filename_len - 1] == '/') {
entry.is_dir = 1;
} else {
entry.is_dir = 0;
}
phar_add_virtual_dirs(mydata, buffer, entry.filename_len TSRMLS_CC);
entry.filename = pestrndup(buffer, entry.filename_len, entry.is_persistent);
buffer += entry.filename_len;
PHAR_GET_32(buffer, entry.uncompressed_filesize);
PHAR_GET_32(buffer, entry.timestamp);
if (offset == halt_offset + (int)manifest_len + 4) {
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 (entry.is_dir) {
entry.filename_len--;
entry.flags |= PHAR_ENT_PERM_DEF_DIR;
}
PHAR_GET_32(buffer, len);
if (entry.is_persistent) {
entry.metadata_len = len;
} else {
entry.metadata_len = 0;
}
if (len > endbuffer - buffer) {
pefree(entry.filename, entry.is_persistent);
MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)");
}
if (phar_parse_metadata(&buffer, &entry.metadata, len TSRMLS_CC) == FAILURE) {
pefree(entry.filename, entry.is_persistent);
MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\"");
}
buffer += len;
entry.offset = entry.offset_abs = offset;
offset += entry.compressed_filesize;
switch (entry.flags & PHAR_ENT_COMPRESSION_MASK) {
case PHAR_ENT_COMPRESSED_GZ:
if (!PHAR_G(has_zlib)) {
if (entry.metadata) {
if (entry.is_persistent) {
free(entry.metadata);
} else {
zval_ptr_dtor(&entry.metadata);
}
}
pefree(entry.filename, entry.is_persistent);
MAPPHAR_FAIL("zlib extension is required for gz compressed .phar file \"%s\"");
}
break;
case PHAR_ENT_COMPRESSED_BZ2:
if (!PHAR_G(has_bz2)) {
if (entry.metadata) {
if (entry.is_persistent) {
free(entry.metadata);
} else {
zval_ptr_dtor(&entry.metadata);
}
}
pefree(entry.filename, entry.is_persistent);
MAPPHAR_FAIL("bz2 extension is required for bzip2 compressed .phar file \"%s\"");
}
break;
default:
if (entry.uncompressed_filesize != entry.compressed_filesize) {
if (entry.metadata) {
if (entry.is_persistent) {
free(entry.metadata);
} else {
zval_ptr_dtor(&entry.metadata);
}
}
pefree(entry.filename, entry.is_persistent);
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 = (manifest_flags & PHAR_HDR_SIGNATURE ? 1 : 0);
phar_set_inode(&entry TSRMLS_CC);
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;
endbuffer = strrchr(mydata->fname, '/');
if (endbuffer) {
mydata->ext = memchr(endbuffer, '.', (mydata->fname + fname_len) - endbuffer);
if (mydata->ext == endbuffer) {
mydata->ext = memchr(endbuffer + 1, '.', (mydata->fname + fname_len) - endbuffer - 1);
}
if (mydata->ext) {
mydata->ext_len = (mydata->fname + mydata->fname_len) - mydata->ext;
}
}
mydata->alias = alias ?
pestrndup(alias, alias_len, mydata->is_persistent) :
pestrndup(mydata->fname, fname_len, mydata->is_persistent);
mydata->alias_len = alias ? alias_len : fname_len;
mydata->sig_flags = sig_flags;
mydata->fp = fp;
mydata->sig_len = sig_len;
mydata->signature = signature;
phar_request_initialize(TSRMLS_C);
if (register_alias) {
phar_archive_data **fd_ptr;
mydata->is_temporary_alias = temp_alias;
if (!phar_validate_alias(mydata->alias, mydata->alias_len)) {
signature = NULL;
fp = NULL;
MAPPHAR_FAIL("Cannot open archive \"%s\", invalid alias");
}
if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) {
if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) {
signature = NULL;
fp = NULL;
MAPPHAR_FAIL("Cannot open archive \"%s\", alias is already in use by existing archive");
}
}
zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
} else {
mydata->is_temporary_alias = 1;
}
zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
efree(savebuf);
if (pphar) {
*pphar = mydata;
}
return SUCCESS;
}
int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC)
{
const char *ext_str, *z;
char *my_error;
int ext_len;
phar_archive_data **test, *unused = NULL;
test = &unused;
if (error) {
*error = NULL;
}
if (phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, !is_data, 0, 1 TSRMLS_CC) == SUCCESS) {
goto check_file;
}
if (FAILURE == phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, !is_data, 1, 1 TSRMLS_CC)) {
if (error) {
if (ext_len == -2) {
spprintf(error, 0, "Cannot create a phar archive from a URL like \"%s\". Phar objects can only be created from local files", fname);
} else {
spprintf(error, 0, "Cannot create phar '%s', file extension (or combination) not recognised or the directory does not exist", fname);
}
}
return FAILURE;
}
check_file:
if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, test, &my_error TSRMLS_CC) == SUCCESS) {
if (pphar) {
*pphar = *test;
}
if ((*test)->is_data && !(*test)->is_tar && !(*test)->is_zip) {
if (error) {
spprintf(error, 0, "Cannot open '%s' as a PharData object. Use Phar::__construct() for executable archives", fname);
}
return FAILURE;
}
if (PHAR_G(readonly) && !(*test)->is_data && ((*test)->is_tar || (*test)->is_zip)) {
phar_entry_info *stub;
if (FAILURE == zend_hash_find(&((*test)->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&stub)) {
spprintf(error, 0, "'%s' is not a phar archive. Use PharData::__construct() for a standard zip or tar archive", fname);
return FAILURE;
}
}
if (!PHAR_G(readonly) || (*test)->is_data) {
(*test)->is_writeable = 1;
}
return SUCCESS;
} else if (my_error) {
if (error) {
*error = my_error;
} else {
efree(my_error);
}
return FAILURE;
}
if (ext_len > 3 && (z = memchr(ext_str, 'z', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ip", 2)) {
return phar_open_or_create_zip(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC);
}
if (ext_len > 3 && (z = memchr(ext_str, 't', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ar", 2)) {
return phar_open_or_create_tar(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC);
}
return phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC);
}
int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC)
{
phar_archive_data *mydata;
php_stream *fp;
char *actual = NULL, *p;
if (!pphar) {
pphar = &mydata;
}
#if PHP_API_VERSION < 20100412
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|0, &actual);
if (actual) {
fname = actual;
fname_len = strlen(actual);
}
if (fp) {
if (phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error TSRMLS_CC) == SUCCESS) {
if ((*pphar)->is_data || !PHAR_G(readonly)) {
(*pphar)->is_writeable = 1;
}
if (actual) {
efree(actual);
}
return SUCCESS;
} else {
if (actual) {
efree(actual);
}
return FAILURE;
}
}
if (actual) {
efree(actual);
}
if (PHAR_G(readonly) && !is_data) {
if (options & REPORT_ERRORS) {
if (error) {
spprintf(error, 0, "creating archive \"%s\" disabled by the php.ini setting phar.readonly", fname);
}
}
return FAILURE;
}
mydata = ecalloc(1, sizeof(phar_archive_data));
mydata->fname = expand_filepath(fname, NULL TSRMLS_CC);
fname_len = strlen(mydata->fname);
#ifdef PHP_WIN32
phar_unixify_path_separators(mydata->fname, fname_len);
#endif
p = strrchr(mydata->fname, '/');
if (p) {
mydata->ext = memchr(p, '.', (mydata->fname + fname_len) - p);
if (mydata->ext == p) {
mydata->ext = memchr(p + 1, '.', (mydata->fname + fname_len) - p - 1);
}
if (mydata->ext) {
mydata->ext_len = (mydata->fname + fname_len) - mydata->ext;
}
}
if (pphar) {
*pphar = mydata;
}
zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
zend_get_hash_value, destroy_phar_manifest_entry, 0);
zend_hash_init(&mydata->mounted_dirs, sizeof(char *),
zend_get_hash_value, NULL, 0);
zend_hash_init(&mydata->virtual_dirs, sizeof(char *),
zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
mydata->fname_len = fname_len;
snprintf(mydata->version, sizeof(mydata->version), "%s", PHP_PHAR_API_VERSION);
mydata->is_temporary_alias = alias ? 0 : 1;
mydata->internal_file_start = -1;
mydata->fp = NULL;
mydata->is_writeable = 1;
mydata->is_brandnew = 1;
phar_request_initialize(TSRMLS_C);
zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
if (is_data) {
alias = NULL;
alias_len = 0;
mydata->is_data = 1;
mydata->is_tar = 1;
} else {
phar_archive_data **fd_ptr;
if (alias && SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) {
if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) {
if (error) {
spprintf(error, 4096, "phar error: phar \"%s\" cannot set alias \"%s\", already in use by another phar archive", mydata->fname, alias);
}
zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len);
if (pphar) {
*pphar = NULL;
}
return FAILURE;
}
}
mydata->alias = alias ? estrndup(alias, alias_len) : estrndup(mydata->fname, fname_len);
mydata->alias_len = alias ? alias_len : fname_len;
}
if (alias_len && alias) {
if (FAILURE == zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL)) {
if (options & REPORT_ERRORS) {
if (error) {
spprintf(error, 0, "archive \"%s\" cannot be associated with alias \"%s\", already in use", fname, alias);
}
}
zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len);
if (pphar) {
*pphar = NULL;
}
return FAILURE;
}
}
return SUCCESS;
}
int phar_open_from_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC)
{
php_stream *fp;
char *actual;
int ret, is_data = 0;
if (error) {
*error = NULL;
}
if (!strstr(fname, ".phar")) {
is_data = 1;
}
if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC) == SUCCESS) {
return SUCCESS;
} else if (error && *error) {
return FAILURE;
}
#if PHP_API_VERSION < 20100412
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, &actual);
if (!fp) {
if (options & REPORT_ERRORS) {
if (error) {
spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
}
}
if (actual) {
efree(actual);
}
return FAILURE;
}
if (actual) {
fname = actual;
fname_len = strlen(actual);
}
ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error TSRMLS_CC);
if (actual) {
efree(actual);
}
return ret;
}
static inline char *phar_strnstr(const char *buf, int buf_len, const char *search, int search_len)
{
const char *c;
int so_far = 0;
if (buf_len < search_len) {
return NULL;
}
c = buf - 1;
do {
if (!(c = memchr(c + 1, search[0], buf_len - search_len - so_far))) {
return (char *) NULL;
}
so_far = c - buf;
if (so_far >= (buf_len - search_len)) {
return (char *) NULL;
}
if (!memcmp(c, search, search_len)) {
return (char *) c;
}
} while (1);
}
static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, int is_data, char **error TSRMLS_DC)
{
const char token[] = "__HALT_COMPILER();";
const char zip_magic[] = "PK\x03\x04";
const char gz_magic[] = "\x1f\x8b\x08";
const char bz_magic[] = "BZh";
char *pos, test = '\0';
const int window_size = 1024;
char buffer[1024 + sizeof(token)];
const long readsize = sizeof(buffer) - sizeof(token);
const long tokenlen = sizeof(token) - 1;
long halt_offset;
size_t got;
php_uint32 compression = PHAR_FILE_COMPRESSED_NONE;
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)) < (size_t) tokenlen) {
MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)")
}
if (!test) {
test = '\1';
pos = buffer+tokenlen;
if (!memcmp(pos, gz_magic, 3)) {
char err = 0;
php_stream_filter *filter;
php_stream *temp;
zval filterparams;
if (!PHAR_G(has_zlib)) {
MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file, enable zlib extension in php.ini")
}
array_init(&filterparams);
#ifndef MAX_WBITS
#define MAX_WBITS 15
#endif
add_assoc_long(&filterparams, "window", MAX_WBITS + 32);
if (!(temp = php_stream_fopen_tmpfile())) {
MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of gzipped phar archive \"%s\"")
}
php_stream_rewind(fp);
filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC);
if (!filter) {
err = 1;
add_assoc_long(&filterparams, "window", MAX_WBITS);
filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC);
zval_dtor(&filterparams);
if (!filter) {
php_stream_close(temp);
MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6")
}
} else {
zval_dtor(&filterparams);
}
php_stream_filter_append(&temp->writefilters, filter);
if (SUCCESS != php_stream_copy_to_stream_ex(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
if (err) {
php_stream_close(temp);
MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6")
}
php_stream_close(temp);
MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file")
}
php_stream_filter_flush(filter, 1);
php_stream_filter_remove(filter, 1 TSRMLS_CC);
php_stream_close(fp);
fp = temp;
php_stream_rewind(fp);
compression = PHAR_FILE_COMPRESSED_GZ;
test = '\0';
continue;
} else if (!memcmp(pos, bz_magic, 3)) {
php_stream_filter *filter;
php_stream *temp;
if (!PHAR_G(has_bz2)) {
MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file, enable bz2 extension in php.ini")
}
if (!(temp = php_stream_fopen_tmpfile())) {
MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of bzipped phar archive \"%s\"")
}
php_stream_rewind(fp);
filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp) TSRMLS_CC);
if (!filter) {
php_stream_close(temp);
MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\", filter creation failed")
}
php_stream_filter_append(&temp->writefilters, filter);
if (SUCCESS != php_stream_copy_to_stream_ex(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
php_stream_close(temp);
MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file")
}
php_stream_filter_flush(filter, 1);
php_stream_filter_remove(filter, 1 TSRMLS_CC);
php_stream_close(fp);
fp = temp;
php_stream_rewind(fp);
compression = PHAR_FILE_COMPRESSED_BZ2;
test = '\0';
continue;
}
if (!memcmp(pos, zip_magic, 4)) {
php_stream_seek(fp, 0, SEEK_END);
return phar_parse_zipfile(fp, fname, fname_len, alias, alias_len, pphar, error TSRMLS_CC);
}
if (got > 512) {
if (phar_is_tar(pos, fname)) {
php_stream_rewind(fp);
return phar_parse_tarfile(fp, fname, fname_len, alias, alias_len, pphar, is_data, compression, error TSRMLS_CC);
}
}
}
if (got > 0 && (pos = phar_strnstr(buffer, got + sizeof(token), token, sizeof(token)-1)) != NULL) {
halt_offset += (pos - buffer);
return phar_parse_pharfile(fp, fname, fname_len, alias, alias_len, halt_offset, pphar, compression, error TSRMLS_CC);
}
halt_offset += got;
memmove(buffer, buffer + window_size, tokenlen);
}
MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (__HALT_COMPILER(); not found)")
}
static int phar_analyze_path(const char *fname, const char *ext, int ext_len, int for_create TSRMLS_DC)
{
php_stream_statbuf ssb;
char *realpath;
char *filename = estrndup(fname, (ext - fname) + ext_len);
if ((realpath = expand_filepath(filename, NULL TSRMLS_CC))) {
#ifdef PHP_WIN32
phar_unixify_path_separators(realpath, strlen(realpath));
#endif
if (zend_hash_exists(&(PHAR_GLOBALS->phar_fname_map), realpath, strlen(realpath))) {
efree(realpath);
efree(filename);
return SUCCESS;
}
if (PHAR_G(manifest_cached) && zend_hash_exists(&cached_phars, realpath, strlen(realpath))) {
efree(realpath);
efree(filename);
return SUCCESS;
}
efree(realpath);
}
if (SUCCESS == php_stream_stat_path((char *) filename, &ssb)) {
efree(filename);
if (ssb.sb.st_mode & S_IFDIR) {
return FAILURE;
}
if (for_create == 1) {
return FAILURE;
}
return SUCCESS;
} else {
char *slash;
if (!for_create) {
efree(filename);
return FAILURE;
}
slash = (char *) strrchr(filename, '/');
if (slash) {
*slash = '\0';
}
if (SUCCESS != php_stream_stat_path((char *) filename, &ssb)) {
if (!slash) {
if (!(realpath = expand_filepath(filename, NULL TSRMLS_CC))) {
efree(filename);
return FAILURE;
}
#ifdef PHP_WIN32
phar_unixify_path_separators(realpath, strlen(realpath));
#endif
slash = strstr(realpath, filename);
if (slash) {
slash += ((ext - fname) + ext_len);
*slash = '\0';
}
slash = strrchr(realpath, '/');
if (slash) {
*slash = '\0';
} else {
efree(realpath);
efree(filename);
return FAILURE;
}
if (SUCCESS != php_stream_stat_path(realpath, &ssb)) {
efree(realpath);
efree(filename);
return FAILURE;
}
efree(realpath);
if (ssb.sb.st_mode & S_IFDIR) {
efree(filename);
return SUCCESS;
}
}
efree(filename);
return FAILURE;
}
efree(filename);
if (ssb.sb.st_mode & S_IFDIR) {
return SUCCESS;
}
return FAILURE;
}
}
static int phar_check_str(const char *fname, const char *ext_str, int ext_len, int executable, int for_create TSRMLS_DC)
{
char test[51];
const char *pos;
if (ext_len >= 50) {
return FAILURE;
}
if (executable == 1) {
memcpy(test, ext_str - 1, ext_len + 1);
test[ext_len + 1] = '\0';
pos = strstr(test, ".phar");
if (pos && (*(pos - 1) != '/')
&& (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) {
return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC);
} else {
return FAILURE;
}
}
if (!executable) {
pos = strstr(ext_str, ".phar");
if (!(pos && (*(pos - 1) != '/')
&& (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) && *(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC);
}
} else {
if (*(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC);
}
}
return FAILURE;
}
int phar_detect_phar_fname_ext(const char *filename, int filename_len, const char **ext_str, int *ext_len, int executable, int for_create, int is_complete TSRMLS_DC)
{
const char *pos, *slash;
*ext_str = NULL;
*ext_len = 0;
if (!filename_len || filename_len == 1) {
return FAILURE;
}
phar_request_initialize(TSRMLS_C);
pos = memchr(filename, '/', filename_len);
if (pos && pos != filename) {
if (*(pos - 1) == ':' && (pos - filename) < filename_len - 1 && *(pos + 1) == '/') {
*ext_len = -2;
*ext_str = NULL;
return FAILURE;
}
if (zend_hash_exists(&(PHAR_GLOBALS->phar_alias_map), (char *) filename, pos - filename)) {
*ext_str = pos;
*ext_len = -1;
return FAILURE;
}
if (PHAR_G(manifest_cached) && zend_hash_exists(&cached_alias, (char *) filename, pos - filename)) {
*ext_str = pos;
*ext_len = -1;
return FAILURE;
}
}
if (zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map)) || PHAR_G(manifest_cached)) {
phar_archive_data **pphar;
if (is_complete) {
if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), (char *) filename, filename_len, (void **)&pphar)) {
*ext_str = filename + (filename_len - (*pphar)->ext_len);
woohoo:
*ext_len = (*pphar)->ext_len;
if (executable == 2) {
return SUCCESS;
}
if (executable == 1 && !(*pphar)->is_data) {
return SUCCESS;
}
if (!executable && (*pphar)->is_data) {
return SUCCESS;
}
return FAILURE;
}
if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_find(&cached_phars, (char *) filename, filename_len, (void **)&pphar)) {
*ext_str = filename + (filename_len - (*pphar)->ext_len);
goto woohoo;
}
} else {
char *str_key;
uint keylen;
ulong unused;
for (zend_hash_internal_pointer_reset(&(PHAR_GLOBALS->phar_fname_map));
HASH_KEY_NON_EXISTENT != zend_hash_get_current_key_ex(&(PHAR_GLOBALS->phar_fname_map), &str_key, &keylen, &unused, 0, NULL);
zend_hash_move_forward(&(PHAR_GLOBALS->phar_fname_map))
) {
if (keylen > (uint) filename_len) {
continue;
}
if (!memcmp(filename, str_key, keylen) && ((uint)filename_len == keylen
|| filename[keylen] == '/' || filename[keylen] == '\0')) {
if (FAILURE == zend_hash_get_current_data(&(PHAR_GLOBALS->phar_fname_map), (void **) &pphar)) {
break;
}
*ext_str = filename + (keylen - (*pphar)->ext_len);
goto woohoo;
}
}
if (PHAR_G(manifest_cached)) {
for (zend_hash_internal_pointer_reset(&cached_phars);
HASH_KEY_NON_EXISTENT != zend_hash_get_current_key_ex(&cached_phars, &str_key, &keylen, &unused, 0, NULL);
zend_hash_move_forward(&cached_phars)
) {
if (keylen > (uint) filename_len) {
continue;
}
if (!memcmp(filename, str_key, keylen) && ((uint)filename_len == keylen
|| filename[keylen] == '/' || filename[keylen] == '\0')) {
if (FAILURE == zend_hash_get_current_data(&cached_phars, (void **) &pphar)) {
break;
}
*ext_str = filename + (keylen - (*pphar)->ext_len);
goto woohoo;
}
}
}
}
}
pos = memchr(filename + 1, '.', filename_len);
next_extension:
if (!pos) {
return FAILURE;
}
while (pos != filename && (*(pos - 1) == '/' || *(pos - 1) == '\0')) {
pos = memchr(pos + 1, '.', filename_len - (pos - filename) + 1);
if (!pos) {
return FAILURE;
}
}
slash = memchr(pos, '/', filename_len - (pos - filename));
if (!slash) {
*ext_str = pos;
*ext_len = strlen(pos);
switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create TSRMLS_CC)) {
case SUCCESS:
return SUCCESS;
case FAILURE:
return FAILURE;
}
}
*ext_str = pos;
*ext_len = slash - pos;
switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create TSRMLS_CC)) {
case SUCCESS:
return SUCCESS;
case FAILURE:
pos = strchr(pos + 1, '.');
if (pos) {
*ext_str = NULL;
*ext_len = 0;
}
goto next_extension;
}
return FAILURE;
}
static int php_check_dots(const char *element, int n)
{
for(n--; n >= 0; --n) {
if (element[n] != '.') {
return 1;
}
}
return 0;
}
#define IS_DIRECTORY_UP(element, len) \
(len >= 2 && !php_check_dots(element, len))
#define IS_DIRECTORY_CURRENT(element, len) \
(len == 1 && element[0] == '.')
#define IS_BACKSLASH(c) ((c) == '/')
#ifdef COMPILE_DL_PHAR
static inline int in_character_class(char ch, const char *delim)
{
while (*delim) {
if (*delim == ch) {
return 1;
}
++delim;
}
return 0;
}
char *tsrm_strtok_r(char *s, const char *delim, char **last)
{
char *token;
if (s == NULL) {
s = *last;
}
while (*s && in_character_class(*s, delim)) {
++s;
}
if (!*s) {
return NULL;
}
token = s;
while (*s && !in_character_class(*s, delim)) {
++s;
}
if (!*s) {
*last = s;
} else {
*s = '\0';
*last = s + 1;
}
return token;
}
#endif
char *phar_fix_filepath(char *path, int *new_len, int use_cwd TSRMLS_DC)
{
char *newpath;
int newpath_len;
char *ptr;
char *tok;
int ptr_length, path_length = *new_len;
if (PHAR_G(cwd_len) && use_cwd && path_length > 2 && path[0] == '.' && path[1] == '/') {
newpath_len = PHAR_G(cwd_len);
newpath = emalloc(strlen(path) + newpath_len + 1);
memcpy(newpath, PHAR_G(cwd), newpath_len);
} else {
newpath = emalloc(strlen(path) + 2);
newpath[0] = '/';
newpath_len = 1;
}
ptr = path;
if (*ptr == '/') {
++ptr;
}
tok = ptr;
do {
ptr = memchr(ptr, '/', path_length - (ptr - path));
} while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
if (!ptr && (path_length - (tok - path))) {
switch (path_length - (tok - path)) {
case 1:
if (*tok == '.') {
efree(path);
*new_len = 1;
efree(newpath);
return estrndup("/", 1);
}
break;
case 2:
if (tok[0] == '.' && tok[1] == '.') {
efree(path);
*new_len = 1;
efree(newpath);
return estrndup("/", 1);
}
}
efree(newpath);
return path;
}
while (ptr) {
ptr_length = ptr - tok;
last_time:
if (IS_DIRECTORY_UP(tok, ptr_length)) {
#define PREVIOUS newpath[newpath_len - 1]
while (newpath_len > 1 && !IS_BACKSLASH(PREVIOUS)) {
newpath_len--;
}
if (newpath[0] != '/') {
newpath[newpath_len] = '\0';
} else if (newpath_len > 1) {
--newpath_len;
}
} else if (!IS_DIRECTORY_CURRENT(tok, ptr_length)) {
if (newpath_len > 1) {
newpath[newpath_len++] = '/';
memcpy(newpath + newpath_len, tok, ptr_length+1);
} else {
memcpy(newpath + newpath_len, tok, ptr_length+1);
}
newpath_len += ptr_length;
}
if (ptr == path + path_length) {
break;
}
tok = ++ptr;
do {
ptr = memchr(ptr, '/', path_length - (ptr - path));
} while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
if (!ptr && (path_length - (tok - path))) {
ptr_length = path_length - (tok - path);
ptr = path + path_length;
goto last_time;
}
}
efree(path);
*new_len = newpath_len;
newpath[newpath_len] = '\0';
return erealloc(newpath, newpath_len + 1);
}
int phar_split_fname(const char *filename, int filename_len, char **arch, int *arch_len, char **entry, int *entry_len, int executable, int for_create TSRMLS_DC)
{
const char *ext_str;
#ifdef PHP_WIN32
char *save;
#endif
int ext_len;
if (CHECK_NULL_PATH(filename, filename_len)) {
return FAILURE;
}
if (!strncasecmp(filename, "phar://", 7)) {
filename += 7;
filename_len -= 7;
}
ext_len = 0;
#ifdef PHP_WIN32
save = filename;
filename = estrndup(filename, filename_len);
phar_unixify_path_separators(filename, filename_len);
#endif
if (phar_detect_phar_fname_ext(filename, filename_len, &ext_str, &ext_len, executable, for_create, 0 TSRMLS_CC) == FAILURE) {
if (ext_len != -1) {
if (!ext_str) {
#ifdef PHP_WIN32
*arch = save;
#else
*arch = filename;
#endif
}
#ifdef PHP_WIN32
efree(filename);
#endif
return FAILURE;
}
ext_len = 0;
}
*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);
#ifdef PHP_WIN32
phar_unixify_path_separators(*entry, *entry_len);
#endif
*entry = phar_fix_filepath(*entry, entry_len, 0 TSRMLS_CC);
} else {
*entry_len = 1;
*entry = estrndup("/", 1);
}
#ifdef PHP_WIN32
efree(filename);
#endif
return SUCCESS;
}
int phar_open_executed_filename(char *alias, int alias_len, char **error TSRMLS_DC)
{
char *fname;
zval *halt_constant;
php_stream *fp;
int fname_len;
char *actual = NULL;
int ret;
if (error) {
*error = NULL;
}
fname = (char*)zend_get_executed_filename(TSRMLS_C);
fname_len = strlen(fname);
if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, 0, 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;
}
FREE_ZVAL(halt_constant);
#if PHP_API_VERSION < 20100412
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, &actual);
if (!fp) {
if (error) {
spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
}
if (actual) {
efree(actual);
}
return FAILURE;
}
if (actual) {
fname = actual;
fname_len = strlen(actual);
}
ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, REPORT_ERRORS, NULL, 0, error TSRMLS_CC);
if (actual) {
efree(actual);
}
return ret;
}
int phar_postprocess_file(phar_entry_data *idata, php_uint32 crc32, char **error, int process_zip TSRMLS_DC)
{
php_uint32 crc = ~0;
int len = idata->internal_file->uncompressed_filesize;
php_stream *fp = idata->fp;
phar_entry_info *entry = idata->internal_file;
if (error) {
*error = NULL;
}
if (entry->is_zip && process_zip > 0) {
phar_zip_file_header local;
phar_zip_data_desc desc;
if (SUCCESS != phar_open_archive_fp(idata->phar TSRMLS_CC)) {
spprintf(error, 0, "phar error: unable to open zip-based phar archive \"%s\" to verify local file header for file \"%s\"", idata->phar->fname, entry->filename);
return FAILURE;
}
php_stream_seek(phar_get_entrypfp(idata->internal_file TSRMLS_CC), entry->header_offset, SEEK_SET);
if (sizeof(local) != php_stream_read(phar_get_entrypfp(idata->internal_file TSRMLS_CC), (char *) &local, sizeof(local))) {
spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot read local file header for file \"%s\")", idata->phar->fname, entry->filename);
return FAILURE;
}
if (((PHAR_ZIP_16(local.flags)) & 0x8) == 0x8) {
php_stream_seek(phar_get_entrypfp(idata->internal_file TSRMLS_CC),
entry->header_offset + sizeof(local) +
PHAR_ZIP_16(local.filename_len) +
PHAR_ZIP_16(local.extra_len) +
entry->compressed_filesize, SEEK_SET);
if (sizeof(desc) != php_stream_read(phar_get_entrypfp(idata->internal_file TSRMLS_CC),
(char *) &desc, sizeof(desc))) {
spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot read local data descriptor for file \"%s\")", idata->phar->fname, entry->filename);
return FAILURE;
}
if (desc.signature[0] == 'P' && desc.signature[1] == 'K') {
memcpy(&(local.crc32), &(desc.crc32), 12);
} else {
memcpy(&(local.crc32), &desc, 12);
}
}
if (entry->filename_len != PHAR_ZIP_16(local.filename_len) || entry->crc32 != PHAR_ZIP_32(local.crc32) || entry->uncompressed_filesize != PHAR_ZIP_32(local.uncompsize) || entry->compressed_filesize != PHAR_ZIP_32(local.compsize)) {
spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (local header of file \"%s\" does not match central directory)", idata->phar->fname, entry->filename);
return FAILURE;
}
entry->offset = entry->offset_abs =
sizeof(local) + entry->header_offset + PHAR_ZIP_16(local.filename_len) + PHAR_ZIP_16(local.extra_len);
if (idata->zero && idata->zero != entry->offset_abs) {
idata->zero = entry->offset_abs;
}
}
if (process_zip == 1) {
return SUCCESS;
}
php_stream_seek(fp, idata->zero, SEEK_SET);
while (len--) {
CRC32(crc, php_stream_getc(fp));
}
php_stream_seek(fp, idata->zero, SEEK_SET);
if (~crc == crc32) {
entry->is_crc_checked = 1;
return SUCCESS;
} else {
spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (crc32 mismatch on file \"%s\")", idata->phar->fname, entry->filename);
return FAILURE;
}
}
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
memcpy(buffer, &var, sizeof(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;
}
}
#include "stub.h"
char *phar_create_default_stub(const char *index_php, const char *web_index, size_t *len, char **error TSRMLS_DC)
{
char *stub = NULL;
int index_len, web_len;
size_t dummy;
if (!len) {
len = &dummy;
}
if (error) {
*error = NULL;
}
if (!index_php) {
index_php = "index.php";
}
if (!web_index) {
web_index = "index.php";
}
index_len = strlen(index_php);
web_len = strlen(web_index);
if (index_len > 400) {
if (error) {
spprintf(error, 0, "Illegal filename passed in for stub creation, was %d characters long, and only 400 or less is allowed", index_len);
return NULL;
}
}
if (web_len > 400) {
if (error) {
spprintf(error, 0, "Illegal web filename passed in for stub creation, was %d characters long, and only 400 or less is allowed", web_len);
return NULL;
}
}
phar_get_stub(index_php, web_index, len, &stub, index_len+1, web_len+1 TSRMLS_CC);
return stub;
}
int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, char **error TSRMLS_DC)
{
char halt_stub[] = "__HALT_COMPILER();";
char *newstub, *tmp;
phar_entry_info *entry, *newentry;
int halt_offset, restore_alias_len, global_flags = 0, closeoldfile;
char *pos, has_dirs = 0;
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, free_fp = 1, free_ufp = 1;
int manifest_hack = 0;
if (phar->is_persistent) {
if (error) {
spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
}
return EOF;
}
if (error) {
*error = NULL;
}
if (!zend_hash_num_elements(&phar->manifest) && !user_stub) {
return EOF;
}
zend_hash_clean(&phar->virtual_dirs);
if (phar->is_zip) {
return phar_zip_flush(phar, user_stub, len, convert, error TSRMLS_CC);
}
if (phar->is_tar) {
return phar_tar_flush(phar, user_stub, len, convert, error TSRMLS_CC);
}
if (PHAR_G(readonly)) {
return EOF;
}
if (phar->fp && !phar->is_brandnew) {
oldfile = phar->fp;
closeoldfile = 0;
php_stream_rewind(oldfile);
} else {
oldfile = php_stream_open_wrapper(phar->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) {
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\"", phar->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\"", phar->fname);
}
return EOF;
}
free_user_stub = 1;
} else {
free_user_stub = 0;
}
tmp = estrndup(user_stub, len);
if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
efree(tmp);
if (closeoldfile) {
php_stream_close(oldfile);
}
php_stream_close(newfile);
if (error) {
spprintf(error, 0, "illegal stub for phar \"%s\"", phar->fname);
}
if (free_user_stub) {
efree(user_stub);
}
return EOF;
}
pos = user_stub + (pos - tmp);
efree(tmp);
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\"", phar->fname);
}
if (free_user_stub) {
efree(user_stub);
}
return EOF;
}
phar->halt_offset = len + 5;
if (free_user_stub) {
efree(user_stub);
}
} else {
size_t written;
if (!user_stub && phar->halt_offset && oldfile && !phar->is_brandnew) {
php_stream_copy_to_stream_ex(oldfile, newfile, phar->halt_offset, &written);
newstub = NULL;
} else {
newstub = phar_create_default_stub(NULL, NULL, &(phar->halt_offset), NULL TSRMLS_CC);
written = php_stream_write(newfile, newstub, phar->halt_offset);
}
if (phar->halt_offset != written) {
if (closeoldfile) {
php_stream_close(oldfile);
}
php_stream_close(newfile);
if (error) {
if (newstub) {
spprintf(error, 0, "unable to create stub in new phar \"%s\"", phar->fname);
} else {
spprintf(error, 0, "unable to copy stub of old phar to new phar \"%s\"", phar->fname);
}
}
if (newstub) {
efree(newstub);
}
return EOF;
}
if (newstub) {
efree(newstub);
}
}
manifest_ftell = php_stream_tell(newfile);
halt_offset = manifest_ftell;
zend_hash_apply(&phar->manifest, phar_flush_clean_deleted_apply TSRMLS_CC);
main_metadata_str.c = 0;
if (phar->metadata) {
PHP_VAR_SERIALIZE_INIT(metadata_hash);
php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash TSRMLS_CC);
PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
} else {
main_metadata_str.len = 0;
}
new_manifest_count = 0;
offset = 0;
for (zend_hash_internal_pointer_reset(&phar->manifest);
zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
zend_hash_move_forward(&phar->manifest)) {
if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) {
continue;
}
if (entry->cfp) {
php_stream_close(entry->cfp);
entry->cfp = 0;
}
if (entry->is_deleted || entry->is_mounted) {
continue;
}
if (!entry->is_modified && entry->fp_refcount) {
switch (entry->fp_type) {
case PHAR_FP:
free_fp = 0;
break;
case PHAR_UFP:
free_ufp = 0;
default:
break;
}
}
++new_manifest_count;
phar_add_virtual_dirs(phar, entry->filename, entry->filename_len TSRMLS_CC);
if (entry->is_dir) {
has_dirs = 1;
}
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 {
if (entry->metadata_str.c) {
smart_str_free(&entry->metadata_str);
}
entry->metadata_str.c = 0;
entry->metadata_str.len = 0;
}
offset += 4 + entry->filename_len + sizeof(entry_buffer) + entry->metadata_str.len + (entry->is_dir ? 1 : 0);
if ((oldfile && !entry->is_modified) || entry->is_dir) {
if (entry->fp_type == PHAR_UFP) {
entry->fp_type = PHAR_FP;
}
continue;
}
if (!phar_get_efp(entry, 0 TSRMLS_CC)) {
newentry = phar_open_jit(phar, entry, error TSRMLS_CC);
if (!newentry) {
efree(*error);
*error = NULL;
continue;
}
entry = newentry;
}
file = phar_get_efp(entry, 0 TSRMLS_CC);
if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 1 TSRMLS_CC)) {
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, phar->fname);
}
return EOF;
}
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)) {
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, phar->fname);
}
} else {
if (error) {
spprintf(error, 0, "unable to bzip2 compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
}
}
return EOF;
}
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);
return EOF;
}
php_stream_flush(file);
if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
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, phar->fname);
}
return EOF;
}
php_stream_filter_append((&entry->cfp->writefilters), filter);
if (SUCCESS != php_stream_copy_to_stream_ex(file, entry->cfp, entry->uncompressed_filesize, NULL)) {
if (closeoldfile) {
php_stream_close(oldfile);
}
php_stream_close(newfile);
if (error) {
spprintf(error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
}
return EOF;
}
php_stream_filter_flush(filter, 1);
php_stream_flush(entry->cfp);
php_stream_filter_remove(filter, 1 TSRMLS_CC);
php_stream_seek(entry->cfp, 0, SEEK_END);
entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
php_stream_rewind(entry->cfp);
entry->old_flags = entry->flags;
entry->is_modified = 1;
global_flags |= (entry->flags & PHAR_ENT_COMPRESSION_MASK);
}
global_flags |= PHAR_HDR_SIGNATURE;
restore_alias_len = phar->alias_len;
if (phar->is_temporary_alias) {
phar->alias_len = 0;
}
manifest_len = offset + phar->alias_len + sizeof(manifest) + main_metadata_str.len;
phar_set_32(manifest, manifest_len);
if(manifest[0] == '\r' || manifest[0] == '\n') {
manifest_len++;
phar_set_32(manifest, manifest_len);
manifest_hack = 1;
}
phar_set_32(manifest+4, new_manifest_count);
if (has_dirs) {
*(manifest + 8) = (unsigned char) (((PHAR_API_VERSION) >> 8) & 0xFF);
*(manifest + 9) = (unsigned char) (((PHAR_API_VERSION) & 0xF0));
} else {
*(manifest + 8) = (unsigned char) (((PHAR_API_VERSION_NODIR) >> 8) & 0xFF);
*(manifest + 9) = (unsigned char) (((PHAR_API_VERSION_NODIR) & 0xF0));
}
phar_set_32(manifest+10, global_flags);
phar_set_32(manifest+14, phar->alias_len);
if (sizeof(manifest) != php_stream_write(newfile, manifest, sizeof(manifest))
|| (size_t)phar->alias_len != php_stream_write(newfile, phar->alias, phar->alias_len)) {
if (closeoldfile) {
php_stream_close(oldfile);
}
php_stream_close(newfile);
phar->alias_len = restore_alias_len;
if (error) {
spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", phar->fname);
}
return EOF;
}
phar->alias_len = restore_alias_len;
phar_set_32(manifest, main_metadata_str.len);
if (4 != php_stream_write(newfile, manifest, 4) || (main_metadata_str.len
&& 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);
phar->alias_len = restore_alias_len;
if (error) {
spprintf(error, 0, "unable to write manifest meta-data of new phar \"%s\"", phar->fname);
}
return EOF;
}
smart_str_free(&main_metadata_str);
manifest_ftell = php_stream_tell(newfile);
for (zend_hash_internal_pointer_reset(&phar->manifest);
zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
zend_hash_move_forward(&phar->manifest)) {
if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) {
continue;
}
if (entry->is_deleted || entry->is_mounted) {
continue;
}
if (entry->is_dir) {
phar_set_32(entry_buffer, entry->filename_len + 1);
} else {
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)
|| (entry->is_dir && 1 != php_stream_write(newfile, "/", 1))) {
if (closeoldfile) {
php_stream_close(oldfile);
}
php_stream_close(newfile);
if (error) {
if (entry->is_dir) {
spprintf(error, 0, "unable to write filename of directory \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
} else {
spprintf(error, 0, "unable to write filename of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
}
}
return EOF;
}
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, phar->fname);
}
return EOF;
}
}
if(manifest_hack) {
if(1 != php_stream_write(newfile, manifest, 1)) {
if (closeoldfile) {
php_stream_close(oldfile);
}
php_stream_close(newfile);
if (error) {
spprintf(error, 0, "unable to write manifest padding byte");
}
return EOF;
}
}
offset = php_stream_tell(newfile);
for (zend_hash_internal_pointer_reset(&phar->manifest);
zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
zend_hash_move_forward(&phar->manifest)) {
if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) {
continue;
}
if (entry->is_deleted || entry->is_dir || entry->is_mounted) {
continue;
}
if (entry->cfp) {
file = entry->cfp;
php_stream_rewind(file);
} else {
file = phar_get_efp(entry, 0 TSRMLS_CC);
if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
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, phar->fname);
}
return EOF;
}
}
if (!file) {
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, phar->fname);
}
return EOF;
}
entry->offset = entry->offset_abs = offset;
offset += entry->compressed_filesize;
if (php_stream_copy_to_stream_ex(file, newfile, entry->compressed_filesize, &wrote) == FAILURE) {
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, phar->fname);
}
return EOF;
}
entry->is_modified = 0;
if (entry->cfp) {
php_stream_close(entry->cfp);
entry->cfp = NULL;
}
if (entry->fp_type == PHAR_MOD) {
if (entry->fp_refcount == 0 && entry->fp != phar->fp && entry->fp != phar->ufp) {
php_stream_close(entry->fp);
}
entry->fp = NULL;
entry->fp_type = PHAR_FP;
} else if (entry->fp_type == PHAR_UFP) {
entry->fp_type = PHAR_FP;
}
}
if (global_flags & PHAR_HDR_SIGNATURE) {
char sig_buf[4];
php_stream_rewind(newfile);
if (phar->signature) {
efree(phar->signature);
phar->signature = NULL;
}
switch(phar->sig_flags) {
#ifndef PHAR_HASH_OK
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, phar->fname);
}
return EOF;
#endif
default: {
char *digest = NULL;
int digest_len;
if (FAILURE == phar_create_signature(phar, newfile, &digest, &digest_len, error TSRMLS_CC)) {
if (error) {
char *save = *error;
spprintf(error, 0, "phar error: unable to write signature: %s", save);
efree(save);
}
if (digest) {
efree(digest);
}
if (closeoldfile) {
php_stream_close(oldfile);
}
php_stream_close(newfile);
return EOF;
}
php_stream_write(newfile, digest, digest_len);
efree(digest);
if (phar->sig_flags == PHAR_SIG_OPENSSL) {
phar_set_32(sig_buf, digest_len);
php_stream_write(newfile, sig_buf, 4);
}
break;
}
}
phar_set_32(sig_buf, phar->sig_flags);
php_stream_write(newfile, sig_buf, 4);
php_stream_write(newfile, "GBMB", 4);
}
if (phar->fp && free_fp) {
php_stream_close(phar->fp);
}
if (phar->ufp) {
if (free_ufp) {
php_stream_close(phar->ufp);
}
phar->ufp = NULL;
}
if (closeoldfile) {
php_stream_close(oldfile);
}
phar->internal_file_start = halt_offset + manifest_len + 4;
phar->halt_offset = halt_offset;
phar->is_brandnew = 0;
php_stream_rewind(newfile);
if (phar->donotflush) {
phar->fp = newfile;
} else {
phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
if (!phar->fp) {
phar->fp = newfile;
if (error) {
spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
}
return EOF;
}
if (phar->flags & PHAR_FILE_COMPRESSED_GZ) {
zval filterparams;
array_init(&filterparams);
add_assoc_long(&filterparams, "window", MAX_WBITS+16);
filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp) TSRMLS_CC);
zval_dtor(&filterparams);
if (!filter) {
if (error) {
spprintf(error, 4096, "unable to compress all contents of phar \"%s\" using zlib, PHP versions older than 5.2.6 have a buggy zlib", phar->fname);
}
return EOF;
}
php_stream_filter_append(&phar->fp->writefilters, filter);
php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
php_stream_filter_flush(filter, 1);
php_stream_filter_remove(filter, 1 TSRMLS_CC);
php_stream_close(phar->fp);
phar->fp = newfile;
} else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) {
filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp) TSRMLS_CC);
php_stream_filter_append(&phar->fp->writefilters, filter);
php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
php_stream_filter_flush(filter, 1);
php_stream_filter_remove(filter, 1 TSRMLS_CC);
php_stream_close(phar->fp);
phar->fp = newfile;
} else {
php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
php_stream_close(newfile);
}
}
if (-1 == php_stream_seek(phar->fp, phar->halt_offset, SEEK_SET)) {
if (error) {
spprintf(error, 0, "unable to seek to __HALT_COMPILER(); in new phar \"%s\"", phar->fname);
}
return EOF;
}
return EOF;
}
#ifdef COMPILE_DL_PHAR
ZEND_GET_MODULE(phar)
#endif
zend_function_entry phar_functions[] = {
PHP_FE_END
};
static size_t phar_zend_stream_reader(void *handle, char *buf, size_t len TSRMLS_DC)
{
return php_stream_read(phar_get_pharfp((phar_archive_data*)handle TSRMLS_CC), buf, len);
}
static size_t phar_zend_stream_fsizer(void *handle TSRMLS_DC)
{
return ((phar_archive_data*)handle)->halt_offset + 32;
}
zend_op_array *(*phar_orig_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);
#define phar_orig_zend_open zend_stream_open_function
static char *phar_resolve_path(const char *filename, int filename_len TSRMLS_DC)
{
return phar_find_in_include_path((char *) filename, filename_len, NULL TSRMLS_CC);
}
static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type TSRMLS_DC)
{
zend_op_array *res;
char *name = NULL;
int failed;
phar_archive_data *phar;
if (!file_handle || !file_handle->filename) {
return phar_orig_compile_file(file_handle, type TSRMLS_CC);
}
if (strstr(file_handle->filename, ".phar") && !strstr(file_handle->filename, "://")) {
if (SUCCESS == phar_open_from_filename((char*)file_handle->filename, strlen(file_handle->filename), NULL, 0, 0, &phar, NULL TSRMLS_CC)) {
if (phar->is_zip || phar->is_tar) {
zend_file_handle f = *file_handle;
spprintf(&name, 4096, "phar://%s/%s", file_handle->filename, ".phar/stub.php");
if (SUCCESS == phar_orig_zend_open((const char *)name, file_handle TSRMLS_CC)) {
efree(name);
name = NULL;
file_handle->filename = f.filename;
if (file_handle->opened_path) {
efree(file_handle->opened_path);
}
file_handle->opened_path = f.opened_path;
file_handle->free_filename = f.free_filename;
} else {
*file_handle = f;
}
} else if (phar->flags & PHAR_FILE_COMPRESSION_MASK) {
file_handle->type = ZEND_HANDLE_STREAM;
file_handle->handle.stream.handle = phar;
file_handle->handle.stream.reader = phar_zend_stream_reader;
file_handle->handle.stream.closer = NULL;
file_handle->handle.stream.fsizer = phar_zend_stream_fsizer;
file_handle->handle.stream.isatty = 0;
phar->is_persistent ?
php_stream_rewind(PHAR_GLOBALS->cached_fp[phar->phar_pos].fp) :
php_stream_rewind(phar->fp);
memset(&file_handle->handle.stream.mmap, 0, sizeof(file_handle->handle.stream.mmap));
}
}
}
zend_try {
failed = 0;
CG(zend_lineno) = 0;
res = phar_orig_compile_file(file_handle, type TSRMLS_CC);
} zend_catch {
failed = 1;
res = NULL;
} zend_end_try();
if (name) {
efree(name);
}
if (failed) {
zend_bailout();
}
return res;
}
typedef zend_op_array* (zend_compile_t)(zend_file_handle*, int TSRMLS_DC);
typedef zend_compile_t* (compile_hook)(zend_compile_t *ptr);
PHP_GINIT_FUNCTION(phar)
{
phar_mime_type mime;
memset(phar_globals, 0, sizeof(zend_phar_globals));
phar_globals->readonly = 1;
zend_hash_init(&phar_globals->mime_types, 0, NULL, NULL, 1);
#define PHAR_SET_MIME(mimetype, ret, fileext) \
mime.mime = mimetype; \
mime.len = sizeof((mimetype))+1; \
mime.type = ret; \
zend_hash_add(&phar_globals->mime_types, fileext, sizeof(fileext)-1, (void *)&mime, sizeof(phar_mime_type), NULL); \
PHAR_SET_MIME("text/html", PHAR_MIME_PHPS, "phps")
PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c")
PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cc")
PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cpp")
PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c++")
PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "dtd")
PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "h")
PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "log")
PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "rng")
PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "txt")
PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "xsd")
PHAR_SET_MIME("", PHAR_MIME_PHP, "php")
PHAR_SET_MIME("", PHAR_MIME_PHP, "inc")
PHAR_SET_MIME("video/avi", PHAR_MIME_OTHER, "avi")
PHAR_SET_MIME("image/bmp", PHAR_MIME_OTHER, "bmp")
PHAR_SET_MIME("text/css", PHAR_MIME_OTHER, "css")
PHAR_SET_MIME("image/gif", PHAR_MIME_OTHER, "gif")
PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htm")
PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "html")
PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htmls")
PHAR_SET_MIME("image/x-ico", PHAR_MIME_OTHER, "ico")
PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpe")
PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpg")
PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpeg")
PHAR_SET_MIME("application/x-javascript", PHAR_MIME_OTHER, "js")
PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "midi")
PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "mid")
PHAR_SET_MIME("audio/mod", PHAR_MIME_OTHER, "mod")
PHAR_SET_MIME("movie/quicktime", PHAR_MIME_OTHER, "mov")
PHAR_SET_MIME("audio/mp3", PHAR_MIME_OTHER, "mp3")
PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpg")
PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpeg")
PHAR_SET_MIME("application/pdf", PHAR_MIME_OTHER, "pdf")
PHAR_SET_MIME("image/png", PHAR_MIME_OTHER, "png")
PHAR_SET_MIME("application/shockwave-flash", PHAR_MIME_OTHER, "swf")
PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tif")
PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tiff")
PHAR_SET_MIME("audio/wav", PHAR_MIME_OTHER, "wav")
PHAR_SET_MIME("image/xbm", PHAR_MIME_OTHER, "xbm")
PHAR_SET_MIME("text/xml", PHAR_MIME_OTHER, "xml")
phar_restore_orig_functions(TSRMLS_C);
}
PHP_GSHUTDOWN_FUNCTION(phar)
{
zend_hash_destroy(&phar_globals->mime_types);
}
PHP_MINIT_FUNCTION(phar)
{
REGISTER_INI_ENTRIES();
phar_orig_compile_file = zend_compile_file;
zend_compile_file = phar_compile_file;
phar_save_resolve_path = zend_resolve_path;
zend_resolve_path = phar_resolve_path;
phar_object_init(TSRMLS_C);
phar_intercept_functions_init(TSRMLS_C);
phar_save_orig_functions(TSRMLS_C);
return php_register_url_stream_wrapper("phar", &php_stream_phar_wrapper TSRMLS_CC);
}
PHP_MSHUTDOWN_FUNCTION(phar)
{
php_unregister_url_stream_wrapper("phar" TSRMLS_CC);
phar_intercept_functions_shutdown(TSRMLS_C);
if (zend_compile_file == phar_compile_file) {
zend_compile_file = phar_orig_compile_file;
}
if (PHAR_G(manifest_cached)) {
zend_hash_destroy(&(cached_phars));
zend_hash_destroy(&(cached_alias));
}
return SUCCESS;
}
void phar_request_initialize(TSRMLS_D)
{
if (!PHAR_GLOBALS->request_init)
{
PHAR_G(last_phar) = NULL;
PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
PHAR_G(has_bz2) = zend_hash_exists(&module_registry, "bz2", sizeof("bz2"));
PHAR_G(has_zlib) = zend_hash_exists(&module_registry, "zlib", sizeof("zlib"));
PHAR_GLOBALS->request_init = 1;
PHAR_GLOBALS->request_ends = 0;
PHAR_GLOBALS->request_done = 0;
zend_hash_init(&(PHAR_GLOBALS->phar_fname_map), 5, zend_get_hash_value, destroy_phar_data, 0);
zend_hash_init(&(PHAR_GLOBALS->phar_persist_map), 5, zend_get_hash_value, NULL, 0);
zend_hash_init(&(PHAR_GLOBALS->phar_alias_map), 5, zend_get_hash_value, NULL, 0);
if (PHAR_G(manifest_cached)) {
phar_archive_data **pphar;
phar_entry_fp *stuff = (phar_entry_fp *) ecalloc(zend_hash_num_elements(&cached_phars), sizeof(phar_entry_fp));
for (zend_hash_internal_pointer_reset(&cached_phars);
zend_hash_get_current_data(&cached_phars, (void **)&pphar) == SUCCESS;
zend_hash_move_forward(&cached_phars)) {
stuff[pphar[0]->phar_pos].manifest = (phar_entry_fp_info *) ecalloc( zend_hash_num_elements(&(pphar[0]->manifest)), sizeof(phar_entry_fp_info));
}
PHAR_GLOBALS->cached_fp = stuff;
}
PHAR_GLOBALS->phar_SERVER_mung_list = 0;
PHAR_G(cwd) = NULL;
PHAR_G(cwd_len) = 0;
PHAR_G(cwd_init) = 0;
}
}
PHP_RSHUTDOWN_FUNCTION(phar)
{
int i;
PHAR_GLOBALS->request_ends = 1;
if (PHAR_GLOBALS->request_init)
{
phar_release_functions(TSRMLS_C);
zend_hash_destroy(&(PHAR_GLOBALS->phar_alias_map));
PHAR_GLOBALS->phar_alias_map.arBuckets = NULL;
zend_hash_destroy(&(PHAR_GLOBALS->phar_fname_map));
PHAR_GLOBALS->phar_fname_map.arBuckets = NULL;
zend_hash_destroy(&(PHAR_GLOBALS->phar_persist_map));
PHAR_GLOBALS->phar_persist_map.arBuckets = NULL;
PHAR_GLOBALS->phar_SERVER_mung_list = 0;
if (PHAR_GLOBALS->cached_fp) {
for (i = 0; i < zend_hash_num_elements(&cached_phars); ++i) {
if (PHAR_GLOBALS->cached_fp[i].fp) {
php_stream_close(PHAR_GLOBALS->cached_fp[i].fp);
}
if (PHAR_GLOBALS->cached_fp[i].ufp) {
php_stream_close(PHAR_GLOBALS->cached_fp[i].ufp);
}
efree(PHAR_GLOBALS->cached_fp[i].manifest);
}
efree(PHAR_GLOBALS->cached_fp);
PHAR_GLOBALS->cached_fp = 0;
}
PHAR_GLOBALS->request_init = 0;
if (PHAR_G(cwd)) {
efree(PHAR_G(cwd));
}
PHAR_G(cwd) = NULL;
PHAR_G(cwd_len) = 0;
PHAR_G(cwd_init) = 0;
}
PHAR_GLOBALS->request_done = 1;
return SUCCESS;
}
PHP_MINFO_FUNCTION(phar)
{
phar_request_initialize(TSRMLS_C);
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", PHP_PHAR_VERSION);
php_info_print_table_row(2, "Phar API version", PHP_PHAR_API_VERSION);
php_info_print_table_row(2, "SVN revision", "$Id$");
php_info_print_table_row(2, "Phar-based phar archives", "enabled");
php_info_print_table_row(2, "Tar-based phar archives", "enabled");
php_info_print_table_row(2, "ZIP-based phar archives", "enabled");
if (PHAR_G(has_zlib)) {
php_info_print_table_row(2, "gzip compression", "enabled");
} else {
php_info_print_table_row(2, "gzip compression", "disabled (install ext/zlib)");
}
if (PHAR_G(has_bz2)) {
php_info_print_table_row(2, "bzip2 compression", "enabled");
} else {
php_info_print_table_row(2, "bzip2 compression", "disabled (install pecl/bz2)");
}
#ifdef PHAR_HAVE_OPENSSL
php_info_print_table_row(2, "Native OpenSSL support", "enabled");
#else
if (zend_hash_exists(&module_registry, "openssl", sizeof("openssl"))) {
php_info_print_table_row(2, "OpenSSL support", "enabled");
} else {
php_info_print_table_row(2, "OpenSSL support", "disabled (install ext/openssl)");
}
#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.");
PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
PUTS("Portions of tar implementation Copyright (c) 2003-2009 Tim Kientzle.");
php_info_print_box_end();
DISPLAY_INI_ENTRIES();
}
static const zend_module_dep phar_deps[] = {
ZEND_MOD_OPTIONAL("apc")
ZEND_MOD_OPTIONAL("bz2")
ZEND_MOD_OPTIONAL("openssl")
ZEND_MOD_OPTIONAL("zlib")
ZEND_MOD_OPTIONAL("standard")
#if defined(HAVE_HASH) && !defined(COMPILE_DL_HASH)
ZEND_MOD_REQUIRED("hash")
#endif
#if HAVE_SPL
ZEND_MOD_REQUIRED("spl")
#endif
ZEND_MOD_END
};
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),
PHP_PHAR_VERSION,
PHP_MODULE_GLOBALS(phar),
PHP_GINIT(phar),
PHP_GSHUTDOWN(phar),
NULL,
STANDARD_MODULE_PROPERTIES_EX
};