Back to index

php5  5.3.10
phar.c
Go to the documentation of this file.
00001 /*
00002   +----------------------------------------------------------------------+
00003   | phar php single-file executable PHP extension                        |
00004   +----------------------------------------------------------------------+
00005   | Copyright (c) 2005-2012 The PHP Group                                |
00006   +----------------------------------------------------------------------+
00007   | This source file is subject to version 3.01 of the PHP license,      |
00008   | that is bundled with this package in the file LICENSE, and is        |
00009   | available through the world-wide-web at the following url:           |
00010   | http://www.php.net/license/3_01.txt.                                 |
00011   | If you did not receive a copy of the PHP license and are unable to   |
00012   | obtain it through the world-wide-web, please send a note to          |
00013   | license@php.net so we can mail you a copy immediately.               |
00014   +----------------------------------------------------------------------+
00015   | Authors: Gregory Beaver <cellog@php.net>                             |
00016   |          Marcus Boerger <helly@php.net>                              |
00017   +----------------------------------------------------------------------+
00018 */
00019 
00020 /* $Id: phar.c 321634 2012-01-01 13:15:04Z felipe $ */
00021 
00022 #define PHAR_MAIN 1
00023 #include "phar_internal.h"
00024 #include "SAPI.h"
00025 #include "func_interceptors.h"
00026 
00027 static void destroy_phar_data(void *pDest);
00028 
00029 ZEND_DECLARE_MODULE_GLOBALS(phar)
00030 #if PHP_VERSION_ID >= 50300
00031 char *(*phar_save_resolve_path)(const char *filename, int filename_len TSRMLS_DC);
00032 #endif
00033 
00037 static int phar_set_writeable_bit(void *pDest, void *argument TSRMLS_DC) /* {{{ */
00038 {
00039        zend_bool keep = *(zend_bool *)argument;
00040        phar_archive_data *phar = *(phar_archive_data **)pDest;
00041 
00042        if (!phar->is_data) {
00043               phar->is_writeable = !keep;
00044        }
00045 
00046        return ZEND_HASH_APPLY_KEEP;
00047 }
00048 /* }}} */
00049 
00050 /* if the original value is 0 (disabled), then allow setting/unsetting at will. Otherwise only allow 1 (enabled), and error on disabling */
00051 ZEND_INI_MH(phar_ini_modify_handler) /* {{{ */
00052 {
00053        zend_bool old, ini;
00054 
00055        if (entry->name_length == 14) {
00056               old = PHAR_G(readonly_orig);
00057        } else {
00058               old = PHAR_G(require_hash_orig);
00059        }
00060 
00061        if (new_value_length == 2 && !strcasecmp("on", new_value)) {
00062               ini = (zend_bool) 1;
00063        }
00064        else if (new_value_length == 3 && !strcasecmp("yes", new_value)) {
00065               ini = (zend_bool) 1;
00066        }
00067        else if (new_value_length == 4 && !strcasecmp("true", new_value)) {
00068               ini = (zend_bool) 1;
00069        }
00070        else {
00071               ini = (zend_bool) atoi(new_value);
00072        }
00073 
00074        /* do not allow unsetting in runtime */
00075        if (stage == ZEND_INI_STAGE_STARTUP) {
00076               if (entry->name_length == 14) {
00077                      PHAR_G(readonly_orig) = ini;
00078               } else {
00079                      PHAR_G(require_hash_orig) = ini;
00080               }
00081        } else if (old && !ini) {
00082               return FAILURE;
00083        }
00084 
00085        if (entry->name_length == 14) {
00086               PHAR_G(readonly) = ini;
00087               if (PHAR_GLOBALS->request_init && PHAR_GLOBALS->phar_fname_map.arBuckets) {
00088                      zend_hash_apply_with_argument(&(PHAR_GLOBALS->phar_fname_map), phar_set_writeable_bit, (void *)&ini TSRMLS_CC);
00089               }
00090        } else {
00091               PHAR_G(require_hash) = ini;
00092        }
00093 
00094        return SUCCESS;
00095 }
00096 /* }}}*/
00097 
00098 /* this global stores the global cached pre-parsed manifests */
00099 HashTable cached_phars;
00100 HashTable cached_alias;
00101 
00102 static void phar_split_cache_list(TSRMLS_D) /* {{{ */
00103 {
00104        char *tmp;
00105        char *key, *lasts, *end;
00106        char ds[2];
00107        phar_archive_data *phar;
00108        uint i = 0;
00109 
00110        if (!PHAR_GLOBALS->cache_list || !(PHAR_GLOBALS->cache_list[0])) {
00111               return;
00112        }
00113 
00114        ds[0] = DEFAULT_DIR_SEPARATOR;
00115        ds[1] = '\0';
00116        tmp = estrdup(PHAR_GLOBALS->cache_list);
00117 
00118        /* fake request startup */
00119        PHAR_GLOBALS->request_init = 1;
00120        if (zend_hash_init(&EG(regular_list), 0, NULL, NULL, 0) == SUCCESS) {
00121               EG(regular_list).nNextFreeElement=1;      /* we don't want resource id 0 */
00122        }
00123 
00124        PHAR_G(has_bz2) = zend_hash_exists(&module_registry, "bz2", sizeof("bz2"));
00125        PHAR_G(has_zlib) = zend_hash_exists(&module_registry, "zlib", sizeof("zlib"));
00126        /* these two are dummies and will be destroyed later */
00127        zend_hash_init(&cached_phars, sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data,  1);
00128        zend_hash_init(&cached_alias, sizeof(phar_archive_data*), zend_get_hash_value, NULL, 1);
00129        /* these two are real and will be copied over cached_phars/cached_alias later */
00130        zend_hash_init(&(PHAR_GLOBALS->phar_fname_map), sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data,  1);
00131        zend_hash_init(&(PHAR_GLOBALS->phar_alias_map), sizeof(phar_archive_data*), zend_get_hash_value, NULL, 1);
00132        PHAR_GLOBALS->manifest_cached = 1;
00133        PHAR_GLOBALS->persist = 1;
00134 
00135        for (key = php_strtok_r(tmp, ds, &lasts);
00136                      key;
00137                      key = php_strtok_r(NULL, ds, &lasts)) {
00138               end = strchr(key, DEFAULT_DIR_SEPARATOR);
00139 
00140               if (end) {
00141                      if (SUCCESS == phar_open_from_filename(key, end - key, NULL, 0, 0, &phar, NULL TSRMLS_CC)) {
00142 finish_up:
00143                             phar->phar_pos = i++;
00144                             php_stream_close(phar->fp);
00145                             phar->fp = NULL;
00146                      } else {
00147 finish_error:
00148                             PHAR_GLOBALS->persist = 0;
00149                             PHAR_GLOBALS->manifest_cached = 0;
00150                             efree(tmp);
00151                             zend_hash_destroy(&(PHAR_G(phar_fname_map)));
00152                             PHAR_GLOBALS->phar_fname_map.arBuckets = 0;
00153                             zend_hash_destroy(&(PHAR_G(phar_alias_map)));
00154                             PHAR_GLOBALS->phar_alias_map.arBuckets = 0;
00155                             zend_hash_destroy(&cached_phars);
00156                             zend_hash_destroy(&cached_alias);
00157                             zend_hash_graceful_reverse_destroy(&EG(regular_list));
00158                             memset(&EG(regular_list), 0, sizeof(HashTable));
00159                             /* free cached manifests */
00160                             PHAR_GLOBALS->request_init = 0;
00161                             return;
00162                      }
00163               } else {
00164                      if (SUCCESS == phar_open_from_filename(key, strlen(key), NULL, 0, 0, &phar, NULL TSRMLS_CC)) {
00165                             goto finish_up;
00166                      } else {
00167                             goto finish_error;
00168                      }
00169               }
00170        }
00171 
00172        PHAR_GLOBALS->persist = 0;
00173        PHAR_GLOBALS->request_init = 0;
00174        /* destroy dummy values from before */
00175        zend_hash_destroy(&cached_phars);
00176        zend_hash_destroy(&cached_alias);
00177        cached_phars = PHAR_GLOBALS->phar_fname_map;
00178        cached_alias = PHAR_GLOBALS->phar_alias_map;
00179        PHAR_GLOBALS->phar_fname_map.arBuckets = 0;
00180        PHAR_GLOBALS->phar_alias_map.arBuckets = 0;
00181        zend_hash_graceful_reverse_destroy(&EG(regular_list));
00182        memset(&EG(regular_list), 0, sizeof(HashTable));
00183        efree(tmp);
00184 }
00185 /* }}} */
00186 
00187 ZEND_INI_MH(phar_ini_cache_list) /* {{{ */
00188 {
00189        PHAR_G(cache_list) = new_value;
00190 
00191        if (stage == ZEND_INI_STAGE_STARTUP) {
00192               phar_split_cache_list(TSRMLS_C);
00193        }
00194 
00195        return SUCCESS;
00196 }
00197 /* }}} */
00198 
00199 PHP_INI_BEGIN()
00200        STD_PHP_INI_BOOLEAN( "phar.readonly", "1", PHP_INI_ALL, phar_ini_modify_handler, readonly, zend_phar_globals, phar_globals)
00201        STD_PHP_INI_BOOLEAN( "phar.require_hash", "1", PHP_INI_ALL, phar_ini_modify_handler, require_hash, zend_phar_globals, phar_globals)
00202        STD_PHP_INI_ENTRY("phar.cache_list", "", PHP_INI_SYSTEM, phar_ini_cache_list, cache_list, zend_phar_globals, phar_globals)
00203 PHP_INI_END()
00204 
00209 void phar_destroy_phar_data(phar_archive_data *phar TSRMLS_DC) /* {{{ */
00210 {
00211        if (phar->alias && phar->alias != phar->fname) {
00212               pefree(phar->alias, phar->is_persistent);
00213               phar->alias = NULL;
00214        }
00215 
00216        if (phar->fname) {
00217               pefree(phar->fname, phar->is_persistent);
00218               phar->fname = NULL;
00219        }
00220 
00221        if (phar->signature) {
00222               pefree(phar->signature, phar->is_persistent);
00223               phar->signature = NULL;
00224        }
00225 
00226        if (phar->manifest.arBuckets) {
00227               zend_hash_destroy(&phar->manifest);
00228               phar->manifest.arBuckets = NULL;
00229        }
00230 
00231        if (phar->mounted_dirs.arBuckets) {
00232               zend_hash_destroy(&phar->mounted_dirs);
00233               phar->mounted_dirs.arBuckets = NULL;
00234        }
00235 
00236        if (phar->virtual_dirs.arBuckets) {
00237               zend_hash_destroy(&phar->virtual_dirs);
00238               phar->virtual_dirs.arBuckets = NULL;
00239        }
00240 
00241        if (phar->metadata) {
00242               if (phar->is_persistent) {
00243                      if (phar->metadata_len) {
00244                             /* for zip comments that are strings */
00245                             free(phar->metadata);
00246                      } else {
00247                             zval_internal_ptr_dtor(&phar->metadata);
00248                      }
00249               } else {
00250                      zval_ptr_dtor(&phar->metadata);
00251               }
00252               phar->metadata_len = 0;
00253               phar->metadata = 0;
00254        }
00255 
00256        if (phar->fp) {
00257               php_stream_close(phar->fp);
00258               phar->fp = 0;
00259        }
00260 
00261        if (phar->ufp) {
00262               php_stream_close(phar->ufp);
00263               phar->ufp = 0;
00264        }
00265 
00266        pefree(phar, phar->is_persistent);
00267 }
00268 /* }}}*/
00269 
00273 int phar_archive_delref(phar_archive_data *phar TSRMLS_DC) /* {{{ */
00274 {
00275        if (phar->is_persistent) {
00276               return 0;
00277        }
00278 
00279        if (--phar->refcount < 0) {
00280               if (PHAR_GLOBALS->request_done
00281               || zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), phar->fname, phar->fname_len) != SUCCESS) {
00282                      phar_destroy_phar_data(phar TSRMLS_CC);
00283               }
00284               return 1;
00285        } else if (!phar->refcount) {
00286               /* invalidate phar cache */
00287               PHAR_G(last_phar) = NULL;
00288               PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
00289 
00290               if (phar->fp && !(phar->flags & PHAR_FILE_COMPRESSION_MASK)) {
00291                      /* close open file handle - allows removal or rename of
00292                      the file on windows, which has greedy locking
00293                      only close if the archive was not already compressed.  If it
00294                      was compressed, then the fp does not refer to the original file */
00295                      php_stream_close(phar->fp);
00296                      phar->fp = NULL;
00297               }
00298 
00299               if (!zend_hash_num_elements(&phar->manifest)) {
00300                      /* this is a new phar that has perhaps had an alias/metadata set, but has never
00301                      been flushed */
00302                      if (zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), phar->fname, phar->fname_len) != SUCCESS) {
00303                             phar_destroy_phar_data(phar TSRMLS_CC);
00304                      }
00305                      return 1;
00306               }
00307        }
00308        return 0;
00309 }
00310 /* }}}*/
00311 
00315 static void destroy_phar_data_only(void *pDest) /* {{{ */
00316 {
00317        phar_archive_data *phar_data = *(phar_archive_data **) pDest;
00318        TSRMLS_FETCH();
00319 
00320        if (EG(exception) || --phar_data->refcount < 0) {
00321               phar_destroy_phar_data(phar_data TSRMLS_CC);
00322        }
00323 }
00324 /* }}}*/
00325 
00329 static int phar_unalias_apply(void *pDest, void *argument TSRMLS_DC) /* {{{ */
00330 {
00331        return *(void**)pDest == argument ? ZEND_HASH_APPLY_REMOVE : ZEND_HASH_APPLY_KEEP;
00332 }
00333 /* }}} */
00334 
00338 static int phar_tmpclose_apply(void *pDest TSRMLS_DC) /* {{{ */
00339 {
00340        phar_entry_info *entry = (phar_entry_info *) pDest;
00341 
00342        if (entry->fp_type != PHAR_TMP) {
00343               return ZEND_HASH_APPLY_KEEP;
00344        }
00345 
00346        if (entry->fp && !entry->fp_refcount) {
00347               php_stream_close(entry->fp);
00348               entry->fp = NULL;
00349        }
00350 
00351        return ZEND_HASH_APPLY_KEEP;
00352 }
00353 /* }}} */
00354 
00358 static void destroy_phar_data(void *pDest) /* {{{ */
00359 {
00360        phar_archive_data *phar_data = *(phar_archive_data **) pDest;
00361        TSRMLS_FETCH();
00362 
00363        if (PHAR_GLOBALS->request_ends) {
00364               /* first, iterate over the manifest and close all PHAR_TMP entry fp handles,
00365               this prevents unnecessary unfreed stream resources */
00366               zend_hash_apply(&(phar_data->manifest), phar_tmpclose_apply TSRMLS_CC);
00367               destroy_phar_data_only(pDest);
00368               return;
00369        }
00370 
00371        zend_hash_apply_with_argument(&(PHAR_GLOBALS->phar_alias_map), phar_unalias_apply, phar_data TSRMLS_CC);
00372 
00373        if (--phar_data->refcount < 0) {
00374               phar_destroy_phar_data(phar_data TSRMLS_CC);
00375        }
00376 }
00377 /* }}}*/
00378 
00382 void destroy_phar_manifest_entry(void *pDest) /* {{{ */
00383 {
00384        phar_entry_info *entry = (phar_entry_info *)pDest;
00385        TSRMLS_FETCH();
00386 
00387        if (entry->cfp) {
00388               php_stream_close(entry->cfp);
00389               entry->cfp = 0;
00390        }
00391 
00392        if (entry->fp) {
00393               php_stream_close(entry->fp);
00394               entry->fp = 0;
00395        }
00396 
00397        if (entry->metadata) {
00398               if (entry->is_persistent) {
00399                      if (entry->metadata_len) {
00400                             /* for zip comments that are strings */
00401                             free(entry->metadata);
00402                      } else {
00403                             zval_internal_ptr_dtor(&entry->metadata);
00404                      }
00405               } else {
00406                      zval_ptr_dtor(&entry->metadata);
00407               }
00408               entry->metadata_len = 0;
00409               entry->metadata = 0;
00410        }
00411 
00412        if (entry->metadata_str.c) {
00413               smart_str_free(&entry->metadata_str);
00414               entry->metadata_str.c = 0;
00415        }
00416 
00417        pefree(entry->filename, entry->is_persistent);
00418 
00419        if (entry->link) {
00420               pefree(entry->link, entry->is_persistent);
00421               entry->link = 0;
00422        }
00423 
00424        if (entry->tmp) {
00425               pefree(entry->tmp, entry->is_persistent);
00426               entry->tmp = 0;
00427        }
00428 }
00429 /* }}} */
00430 
00431 int phar_entry_delref(phar_entry_data *idata TSRMLS_DC) /* {{{ */
00432 {
00433        int ret = 0;
00434 
00435        if (idata->internal_file && !idata->internal_file->is_persistent) {
00436               if (--idata->internal_file->fp_refcount < 0) {
00437                      idata->internal_file->fp_refcount = 0;
00438               }
00439 
00440               if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) {
00441                      php_stream_close(idata->fp);
00442               }
00443               /* if phar_get_or_create_entry_data returns a sub-directory, we have to free it */
00444               if (idata->internal_file->is_temp_dir) {
00445                      destroy_phar_manifest_entry((void *)idata->internal_file);
00446                      efree(idata->internal_file);
00447               }
00448        }
00449 
00450        phar_archive_delref(idata->phar TSRMLS_CC);
00451        efree(idata);
00452        return ret;
00453 }
00454 /* }}} */
00455 
00459 void phar_entry_remove(phar_entry_data *idata, char **error TSRMLS_DC) /* {{{ */
00460 {
00461        phar_archive_data *phar;
00462 
00463        phar = idata->phar;
00464 
00465        if (idata->internal_file->fp_refcount < 2) {
00466               if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) {
00467                      php_stream_close(idata->fp);
00468               }
00469               zend_hash_del(&idata->phar->manifest, idata->internal_file->filename, idata->internal_file->filename_len);
00470               idata->phar->refcount--;
00471               efree(idata);
00472        } else {
00473               idata->internal_file->is_deleted = 1;
00474               phar_entry_delref(idata TSRMLS_CC);
00475        }
00476 
00477        if (!phar->donotflush) {
00478               phar_flush(phar, 0, 0, 0, error TSRMLS_CC);
00479        }
00480 }
00481 /* }}} */
00482 
00483 #define MAPPHAR_ALLOC_FAIL(msg) \
00484        if (fp) {\
00485               php_stream_close(fp);\
00486        }\
00487        if (error) {\
00488               spprintf(error, 0, msg, fname);\
00489        }\
00490        return FAILURE;
00491 
00492 #define MAPPHAR_FAIL(msg) \
00493        efree(savebuf);\
00494        if (mydata) {\
00495               phar_destroy_phar_data(mydata TSRMLS_CC);\
00496        }\
00497        if (signature) {\
00498               pefree(signature, PHAR_G(persist));\
00499        }\
00500        MAPPHAR_ALLOC_FAIL(msg)
00501 
00502 #ifdef WORDS_BIGENDIAN
00503 # define PHAR_GET_32(buffer, var) \
00504        var = ((((unsigned char*)(buffer))[3]) << 24) \
00505               | ((((unsigned char*)(buffer))[2]) << 16) \
00506               | ((((unsigned char*)(buffer))[1]) <<  8) \
00507               | (((unsigned char*)(buffer))[0]); \
00508        (buffer) += 4
00509 # define PHAR_GET_16(buffer, var) \
00510        var = ((((unsigned char*)(buffer))[1]) <<  8) \
00511               | (((unsigned char*)(buffer))[0]); \
00512        (buffer) += 2
00513 #else
00514 # define PHAR_GET_32(buffer, var) \
00515        memcpy(&var, buffer, sizeof(var)); \
00516        buffer += 4
00517 # define PHAR_GET_16(buffer, var) \
00518        var = *(php_uint16*)(buffer); \
00519        buffer += 2
00520 #endif
00521 #define PHAR_ZIP_16(var) ((php_uint16)((((php_uint16)var[0]) & 0xff) | \
00522        (((php_uint16)var[1]) & 0xff) << 8))
00523 #define PHAR_ZIP_32(var) ((php_uint32)((((php_uint32)var[0]) & 0xff) | \
00524        (((php_uint32)var[1]) & 0xff) << 8 | \
00525        (((php_uint32)var[2]) & 0xff) << 16 | \
00526        (((php_uint32)var[3]) & 0xff) << 24))
00527 
00531 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) /* {{{ */
00532 {
00533        phar_archive_data *phar;
00534 #ifdef PHP_WIN32
00535        char *unixfname;
00536 #endif
00537 
00538        if (error) {
00539               *error = NULL;
00540        }
00541 #ifdef PHP_WIN32
00542        unixfname = estrndup(fname, fname_len);
00543        phar_unixify_path_separators(unixfname, fname_len);
00544 
00545        if (SUCCESS == phar_get_archive(&phar, unixfname, fname_len, alias, alias_len, error TSRMLS_CC)
00546               && ((alias && fname_len == phar->fname_len
00547               && !strncmp(unixfname, phar->fname, fname_len)) || !alias)
00548        ) {
00549               phar_entry_info *stub;
00550               efree(unixfname);
00551 #else
00552        if (SUCCESS == phar_get_archive(&phar, fname, fname_len, alias, alias_len, error TSRMLS_CC)
00553               && ((alias && fname_len == phar->fname_len
00554               && !strncmp(fname, phar->fname, fname_len)) || !alias)
00555        ) {
00556               phar_entry_info *stub;
00557 #endif
00558               /* logic above is as follows:
00559                  If an explicit alias was requested, ensure the filename passed in
00560                  matches the phar's filename.
00561                  If no alias was passed in, then it can match either and be valid
00562                */
00563 
00564               if (!is_data) {
00565                      /* prevent any ".phar" without a stub getting through */
00566                      if (!phar->halt_offset && !phar->is_brandnew && (phar->is_tar || phar->is_zip)) {
00567                             if (PHAR_G(readonly) && FAILURE == zend_hash_find(&(phar->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&stub)) {
00568                                    if (error) {
00569                                           spprintf(error, 0, "'%s' is not a phar archive. Use PharData::__construct() for a standard zip or tar archive", fname);
00570                                    }
00571                                    return FAILURE;
00572                             }
00573                      }
00574               }
00575 
00576               if (pphar) {
00577                      *pphar = phar;
00578               }
00579 
00580               return SUCCESS;
00581        } else {
00582 #ifdef PHP_WIN32
00583               efree(unixfname);
00584 #endif
00585               if (pphar) {
00586                      *pphar = NULL;
00587               }
00588 
00589               if (phar && error && !(options & REPORT_ERRORS)) {
00590                      efree(error);
00591               }
00592 
00593               return FAILURE;
00594        }
00595 }
00596 /* }}}*/
00597 
00606 int phar_parse_metadata(char **buffer, zval **metadata, int zip_metadata_len TSRMLS_DC) /* {{{ */
00607 {
00608        const unsigned char *p;
00609        php_uint32 buf_len;
00610        php_unserialize_data_t var_hash;
00611 
00612        if (!zip_metadata_len) {
00613               PHAR_GET_32(*buffer, buf_len);
00614        } else {
00615               buf_len = zip_metadata_len;
00616        }
00617 
00618        if (buf_len) {
00619               ALLOC_ZVAL(*metadata);
00620               INIT_ZVAL(**metadata);
00621               p = (const unsigned char*) *buffer;
00622               PHP_VAR_UNSERIALIZE_INIT(var_hash);
00623 
00624               if (!php_var_unserialize(metadata, &p, p + buf_len, &var_hash TSRMLS_CC)) {
00625                      PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
00626                      zval_ptr_dtor(metadata);
00627                      *metadata = NULL;
00628                      return FAILURE;
00629               }
00630 
00631               PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
00632 
00633               if (PHAR_G(persist)) {
00634                      /* lazy init metadata */
00635                      zval_ptr_dtor(metadata);
00636                      *metadata = (zval *) pemalloc(buf_len, 1);
00637                      memcpy(*metadata, *buffer, buf_len);
00638                      *buffer += buf_len;
00639                      return SUCCESS;
00640               }
00641        } else {
00642               *metadata = NULL;
00643        }
00644 
00645        if (!zip_metadata_len) {
00646               *buffer += buf_len;
00647        }
00648 
00649        return SUCCESS;
00650 }
00651 /* }}}*/
00652 
00662 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) /* {{{ */
00663 {
00664        char b32[4], *buffer, *endbuffer, *savebuf;
00665        phar_archive_data *mydata = NULL;
00666        phar_entry_info entry;
00667        php_uint32 manifest_len, manifest_count, manifest_flags, manifest_index, tmp_len, sig_flags;
00668        php_uint16 manifest_ver;
00669        long offset;
00670        int sig_len, register_alias = 0, temp_alias = 0;
00671        char *signature = NULL;
00672 
00673        if (pphar) {
00674               *pphar = NULL;
00675        }
00676 
00677        if (error) {
00678               *error = NULL;
00679        }
00680 
00681        /* check for ?>\n and increment accordingly */
00682        if (-1 == php_stream_seek(fp, halt_offset, SEEK_SET)) {
00683               MAPPHAR_ALLOC_FAIL("cannot seek to __HALT_COMPILER(); location in phar \"%s\"")
00684        }
00685 
00686        buffer = b32;
00687 
00688        if (3 != php_stream_read(fp, buffer, 3)) {
00689               MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
00690        }
00691 
00692        if ((*buffer == ' ' || *buffer == '\n') && *(buffer + 1) == '?' && *(buffer + 2) == '>') {
00693               int nextchar;
00694               halt_offset += 3;
00695               if (EOF == (nextchar = php_stream_getc(fp))) {
00696                      MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
00697               }
00698 
00699               if ((char) nextchar == '\r') {
00700                      /* if we have an \r we require an \n as well */
00701                      if (EOF == (nextchar = php_stream_getc(fp)) || (char)nextchar != '\n') {
00702                             MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
00703                      }
00704                      ++halt_offset;
00705               }
00706 
00707               if ((char) nextchar == '\n') {
00708                      ++halt_offset;
00709               }
00710        }
00711 
00712        /* make sure we are at the right location to read the manifest */
00713        if (-1 == php_stream_seek(fp, halt_offset, SEEK_SET)) {
00714               MAPPHAR_ALLOC_FAIL("cannot seek to __HALT_COMPILER(); location in phar \"%s\"")
00715        }
00716 
00717        /* read in manifest */
00718        buffer = b32;
00719 
00720        if (4 != php_stream_read(fp, buffer, 4)) {
00721               MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at manifest length)")
00722        }
00723 
00724        PHAR_GET_32(buffer, manifest_len);
00725 
00726        if (manifest_len > 1048576 * 100) {
00727               /* prevent serious memory issues by limiting manifest to at most 100 MB in length */
00728               MAPPHAR_ALLOC_FAIL("manifest cannot be larger than 100 MB in phar \"%s\"")
00729        }
00730 
00731        buffer = (char *)emalloc(manifest_len);
00732        savebuf = buffer;
00733        endbuffer = buffer + manifest_len;
00734 
00735        if (manifest_len < 10 || manifest_len != php_stream_read(fp, buffer, manifest_len)) {
00736               MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)")
00737        }
00738 
00739        /* extract the number of entries */
00740        PHAR_GET_32(buffer, manifest_count);
00741 
00742        if (manifest_count == 0) {
00743               MAPPHAR_FAIL("in phar \"%s\", manifest claims to have zero entries.  Phars must have at least 1 entry");
00744        }
00745 
00746        /* extract API version, lowest nibble currently unused */
00747        manifest_ver = (((unsigned char)buffer[0]) << 8)
00748                              + ((unsigned char)buffer[1]);
00749        buffer += 2;
00750 
00751        if ((manifest_ver & PHAR_API_VER_MASK) < PHAR_API_MIN_READ) {
00752               efree(savebuf);
00753               php_stream_close(fp);
00754               if (error) {
00755                      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);
00756               }
00757               return FAILURE;
00758        }
00759 
00760        PHAR_GET_32(buffer, manifest_flags);
00761 
00762        manifest_flags &= ~PHAR_HDR_COMPRESSION_MASK;
00763        manifest_flags &= ~PHAR_FILE_COMPRESSION_MASK;
00764        /* remember whether this entire phar was compressed with gz/bzip2 */
00765        manifest_flags |= compression;
00766 
00767        /* The lowest nibble contains the phar wide flags. The compression flags can */
00768        /* be ignored on reading because it is being generated anyways. */
00769        if (manifest_flags & PHAR_HDR_SIGNATURE) {
00770               char sig_buf[8], *sig_ptr = sig_buf;
00771               off_t read_len;
00772               size_t end_of_phar;
00773 
00774               if (-1 == php_stream_seek(fp, -8, SEEK_END)
00775               || (read_len = php_stream_tell(fp)) < 20
00776               || 8 != php_stream_read(fp, sig_buf, 8)
00777               || memcmp(sig_buf+4, "GBMB", 4)) {
00778                      efree(savebuf);
00779                      php_stream_close(fp);
00780                      if (error) {
00781                             spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
00782                      }
00783                      return FAILURE;
00784               }
00785 
00786               PHAR_GET_32(sig_ptr, sig_flags);
00787 
00788               switch(sig_flags) {
00789                      case PHAR_SIG_OPENSSL: {
00790                             php_uint32 signature_len;
00791                             char *sig;
00792                             off_t whence;
00793 
00794                             /* we store the signature followed by the signature length */
00795                             if (-1 == php_stream_seek(fp, -12, SEEK_CUR)
00796                             || 4 != php_stream_read(fp, sig_buf, 4)) {
00797                                    efree(savebuf);
00798                                    php_stream_close(fp);
00799                                    if (error) {
00800                                           spprintf(error, 0, "phar \"%s\" openssl signature length could not be read", fname);
00801                                    }
00802                                    return FAILURE;
00803                             }
00804 
00805                             sig_ptr = sig_buf;
00806                             PHAR_GET_32(sig_ptr, signature_len);
00807                             sig = (char *) emalloc(signature_len);
00808                             whence = signature_len + 4;
00809                             whence = -whence;
00810 
00811                             if (-1 == php_stream_seek(fp, whence, SEEK_CUR)
00812                             || !(end_of_phar = php_stream_tell(fp))
00813                             || signature_len != php_stream_read(fp, sig, signature_len)) {
00814                                    efree(savebuf);
00815                                    efree(sig);
00816                                    php_stream_close(fp);
00817                                    if (error) {
00818                                           spprintf(error, 0, "phar \"%s\" openssl signature could not be read", fname);
00819                                    }
00820                                    return FAILURE;
00821                             }
00822 
00823                             if (FAILURE == phar_verify_signature(fp, end_of_phar, PHAR_SIG_OPENSSL, sig, signature_len, fname, &signature, &sig_len, error TSRMLS_CC)) {
00824                                    efree(savebuf);
00825                                    efree(sig);
00826                                    php_stream_close(fp);
00827                                    if (error) {
00828                                           char *save = *error;
00829                                           spprintf(error, 0, "phar \"%s\" openssl signature could not be verified: %s", fname, *error);
00830                                           efree(save);
00831                                    }
00832                                    return FAILURE;
00833                             }
00834                             efree(sig);
00835                      }
00836                      break;
00837 #if PHAR_HASH_OK
00838                      case PHAR_SIG_SHA512: {
00839                             unsigned char digest[64];
00840 
00841                             php_stream_seek(fp, -(8 + 64), SEEK_END);
00842                             read_len = php_stream_tell(fp);
00843 
00844                             if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
00845                                    efree(savebuf);
00846                                    php_stream_close(fp);
00847                                    if (error) {
00848                                           spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
00849                                    }
00850                                    return FAILURE;
00851                             }
00852 
00853                             if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA512, (char *)digest, 64, fname, &signature, &sig_len, error TSRMLS_CC)) {
00854                                    efree(savebuf);
00855                                    php_stream_close(fp);
00856                                    if (error) {
00857                                           char *save = *error;
00858                                           spprintf(error, 0, "phar \"%s\" SHA512 signature could not be verified: %s", fname, *error);
00859                                           efree(save);
00860                                    }
00861                                    return FAILURE;
00862                             }
00863                             break;
00864                      }
00865                      case PHAR_SIG_SHA256: {
00866                             unsigned char digest[32];
00867 
00868                             php_stream_seek(fp, -(8 + 32), SEEK_END);
00869                             read_len = php_stream_tell(fp);
00870 
00871                             if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
00872                                    efree(savebuf);
00873                                    php_stream_close(fp);
00874                                    if (error) {
00875                                           spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
00876                                    }
00877                                    return FAILURE;
00878                             }
00879 
00880                             if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA256, (char *)digest, 32, fname, &signature, &sig_len, error TSRMLS_CC)) {
00881                                    efree(savebuf);
00882                                    php_stream_close(fp);
00883                                    if (error) {
00884                                           char *save = *error;
00885                                           spprintf(error, 0, "phar \"%s\" SHA256 signature could not be verified: %s", fname, *error);
00886                                           efree(save);
00887                                    }
00888                                    return FAILURE;
00889                             }
00890                             break;
00891                      }
00892 #else
00893                      case PHAR_SIG_SHA512:
00894                      case PHAR_SIG_SHA256:
00895                             efree(savebuf);
00896                             php_stream_close(fp);
00897 
00898                             if (error) {
00899                                    spprintf(error, 0, "phar \"%s\" has a unsupported signature", fname);
00900                             }
00901                             return FAILURE;
00902 #endif
00903                      case PHAR_SIG_SHA1: {
00904                             unsigned char digest[20];
00905 
00906                             php_stream_seek(fp, -(8 + 20), SEEK_END);
00907                             read_len = php_stream_tell(fp);
00908 
00909                             if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
00910                                    efree(savebuf);
00911                                    php_stream_close(fp);
00912                                    if (error) {
00913                                           spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
00914                                    }
00915                                    return FAILURE;
00916                             }
00917 
00918                             if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA1, (char *)digest, 20, fname, &signature, &sig_len, error TSRMLS_CC)) {
00919                                    efree(savebuf);
00920                                    php_stream_close(fp);
00921                                    if (error) {
00922                                           char *save = *error;
00923                                           spprintf(error, 0, "phar \"%s\" SHA1 signature could not be verified: %s", fname, *error);
00924                                           efree(save);
00925                                    }
00926                                    return FAILURE;
00927                             }
00928                             break;
00929                      }
00930                      case PHAR_SIG_MD5: {
00931                             unsigned char digest[16];
00932 
00933                             php_stream_seek(fp, -(8 + 16), SEEK_END);
00934                             read_len = php_stream_tell(fp);
00935 
00936                             if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
00937                                    efree(savebuf);
00938                                    php_stream_close(fp);
00939                                    if (error) {
00940                                           spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
00941                                    }
00942                                    return FAILURE;
00943                             }
00944 
00945                             if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_MD5, (char *)digest, 16, fname, &signature, &sig_len, error TSRMLS_CC)) {
00946                                    efree(savebuf);
00947                                    php_stream_close(fp);
00948                                    if (error) {
00949                                           char *save = *error;
00950                                           spprintf(error, 0, "phar \"%s\" MD5 signature could not be verified: %s", fname, *error);
00951                                           efree(save);
00952                                    }
00953                                    return FAILURE;
00954                             }
00955                             break;
00956                      }
00957                      default:
00958                             efree(savebuf);
00959                             php_stream_close(fp);
00960 
00961                             if (error) {
00962                                    spprintf(error, 0, "phar \"%s\" has a broken or unsupported signature", fname);
00963                             }
00964                             return FAILURE;
00965               }
00966        } else if (PHAR_G(require_hash)) {
00967               efree(savebuf);
00968               php_stream_close(fp);
00969 
00970               if (error) {
00971                      spprintf(error, 0, "phar \"%s\" does not have a signature", fname);
00972               }
00973               return FAILURE;
00974        } else {
00975               sig_flags = 0;
00976               sig_len = 0;
00977        }
00978 
00979        /* extract alias */
00980        PHAR_GET_32(buffer, tmp_len);
00981 
00982        if (buffer + tmp_len > endbuffer) {
00983               MAPPHAR_FAIL("internal corruption of phar \"%s\" (buffer overrun)");
00984        }
00985 
00986        if (manifest_len < 10 + tmp_len) {
00987               MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)")
00988        }
00989 
00990        /* tmp_len = 0 says alias length is 0, which means the alias is not stored in the phar */
00991        if (tmp_len) {
00992               /* if the alias is stored we enforce it (implicit overrides explicit) */
00993               if (alias && alias_len && (alias_len != (int)tmp_len || strncmp(alias, buffer, tmp_len)))
00994               {
00995                      buffer[tmp_len] = '\0';
00996                      php_stream_close(fp);
00997 
00998                      if (signature) {
00999                             efree(signature);
01000                      }
01001 
01002                      if (error) {
01003                             spprintf(error, 0, "cannot load phar \"%s\" with implicit alias \"%s\" under different alias \"%s\"", fname, buffer, alias);
01004                      }
01005 
01006                      efree(savebuf);
01007                      return FAILURE;
01008               }
01009 
01010               alias_len = tmp_len;
01011               alias = buffer;
01012               buffer += tmp_len;
01013               register_alias = 1;
01014        } else if (!alias_len || !alias) {
01015               /* if we neither have an explicit nor an implicit alias, we use the filename */
01016               alias = NULL;
01017               alias_len = 0;
01018               register_alias = 0;
01019        } else if (alias_len) {
01020               register_alias = 1;
01021               temp_alias = 1;
01022        }
01023 
01024        /* we have 5 32-bit items plus 1 byte at least */
01025        if (manifest_count > ((manifest_len - 10 - tmp_len) / (5 * 4 + 1))) {
01026               /* prevent serious memory issues */
01027               MAPPHAR_FAIL("internal corruption of phar \"%s\" (too many manifest entries for size of manifest)")
01028        }
01029 
01030        mydata = pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist));
01031        mydata->is_persistent = PHAR_G(persist);
01032 
01033        /* check whether we have meta data, zero check works regardless of byte order */
01034        if (mydata->is_persistent) {
01035               PHAR_GET_32(buffer, mydata->metadata_len);
01036               if (phar_parse_metadata(&buffer, &mydata->metadata, mydata->metadata_len TSRMLS_CC) == FAILURE) {
01037                      MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\"");
01038               }
01039        } else {
01040               if (phar_parse_metadata(&buffer, &mydata->metadata, 0 TSRMLS_CC) == FAILURE) {
01041                      MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\"");
01042               }
01043        }
01044 
01045        /* set up our manifest */
01046        zend_hash_init(&mydata->manifest, manifest_count,
01047               zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)mydata->is_persistent);
01048        zend_hash_init(&mydata->mounted_dirs, 5,
01049               zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
01050        zend_hash_init(&mydata->virtual_dirs, manifest_count * 2,
01051               zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
01052        mydata->fname = pestrndup(fname, fname_len, mydata->is_persistent);
01053 #ifdef PHP_WIN32
01054        phar_unixify_path_separators(mydata->fname, fname_len);
01055 #endif
01056        mydata->fname_len = fname_len;
01057        offset = halt_offset + manifest_len + 4;
01058        memset(&entry, 0, sizeof(phar_entry_info));
01059        entry.phar = mydata;
01060        entry.fp_type = PHAR_FP;
01061        entry.is_persistent = mydata->is_persistent;
01062 
01063        for (manifest_index = 0; manifest_index < manifest_count; ++manifest_index) {
01064               if (buffer + 4 > endbuffer) {
01065                      MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)")
01066               }
01067 
01068               PHAR_GET_32(buffer, entry.filename_len);
01069 
01070               if (entry.filename_len == 0) {
01071                      MAPPHAR_FAIL("zero-length filename encountered in phar \"%s\"");
01072               }
01073 
01074               if (entry.is_persistent) {
01075                      entry.manifest_pos = manifest_index;
01076               }
01077 
01078               if (buffer + entry.filename_len + 20 > endbuffer) {
01079                      MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)");
01080               }
01081 
01082               if ((manifest_ver & PHAR_API_VER_MASK) >= PHAR_API_MIN_DIR && buffer[entry.filename_len - 1] == '/') {
01083                      entry.is_dir = 1;
01084               } else {
01085                      entry.is_dir = 0;
01086               }
01087 
01088               phar_add_virtual_dirs(mydata, buffer, entry.filename_len TSRMLS_CC);
01089               entry.filename = pestrndup(buffer, entry.filename_len, entry.is_persistent);
01090               buffer += entry.filename_len;
01091               PHAR_GET_32(buffer, entry.uncompressed_filesize);
01092               PHAR_GET_32(buffer, entry.timestamp);
01093 
01094               if (offset == halt_offset + (int)manifest_len + 4) {
01095                      mydata->min_timestamp = entry.timestamp;
01096                      mydata->max_timestamp = entry.timestamp;
01097               } else {
01098                      if (mydata->min_timestamp > entry.timestamp) {
01099                             mydata->min_timestamp = entry.timestamp;
01100                      } else if (mydata->max_timestamp < entry.timestamp) {
01101                             mydata->max_timestamp = entry.timestamp;
01102                      }
01103               }
01104 
01105               PHAR_GET_32(buffer, entry.compressed_filesize);
01106               PHAR_GET_32(buffer, entry.crc32);
01107               PHAR_GET_32(buffer, entry.flags);
01108 
01109               if (entry.is_dir) {
01110                      entry.filename_len--;
01111                      entry.flags |= PHAR_ENT_PERM_DEF_DIR;
01112               }
01113 
01114               if (entry.is_persistent) {
01115                      PHAR_GET_32(buffer, entry.metadata_len);
01116                      if (!entry.metadata_len) buffer -= 4;
01117                      if (phar_parse_metadata(&buffer, &entry.metadata, entry.metadata_len TSRMLS_CC) == FAILURE) {
01118                             pefree(entry.filename, entry.is_persistent);
01119                             MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\"");
01120                      }
01121               } else {
01122                      if (phar_parse_metadata(&buffer, &entry.metadata, 0 TSRMLS_CC) == FAILURE) {
01123                             pefree(entry.filename, entry.is_persistent);
01124                             MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\"");
01125                      }
01126               }
01127 
01128               entry.offset = entry.offset_abs = offset;
01129               offset += entry.compressed_filesize;
01130 
01131               switch (entry.flags & PHAR_ENT_COMPRESSION_MASK) {
01132                      case PHAR_ENT_COMPRESSED_GZ:
01133                             if (!PHAR_G(has_zlib)) {
01134                                    if (entry.metadata) {
01135                                           if (entry.is_persistent) {
01136                                                  free(entry.metadata);
01137                                           } else {
01138                                                  zval_ptr_dtor(&entry.metadata);
01139                                           }
01140                                    }
01141                                    pefree(entry.filename, entry.is_persistent);
01142                                    MAPPHAR_FAIL("zlib extension is required for gz compressed .phar file \"%s\"");
01143                             }
01144                             break;
01145                      case PHAR_ENT_COMPRESSED_BZ2:
01146                             if (!PHAR_G(has_bz2)) {
01147                                    if (entry.metadata) {
01148                                           if (entry.is_persistent) {
01149                                                  free(entry.metadata);
01150                                           } else {
01151                                                  zval_ptr_dtor(&entry.metadata);
01152                                           }
01153                                    }
01154                                    pefree(entry.filename, entry.is_persistent);
01155                                    MAPPHAR_FAIL("bz2 extension is required for bzip2 compressed .phar file \"%s\"");
01156                             }
01157                             break;
01158                      default:
01159                             if (entry.uncompressed_filesize != entry.compressed_filesize) {
01160                                    if (entry.metadata) {
01161                                           if (entry.is_persistent) {
01162                                                  free(entry.metadata);
01163                                           } else {
01164                                                  zval_ptr_dtor(&entry.metadata);
01165                                           }
01166                                    }
01167                                    pefree(entry.filename, entry.is_persistent);
01168                                    MAPPHAR_FAIL("internal corruption of phar \"%s\" (compressed and uncompressed size does not match for uncompressed entry)");
01169                             }
01170                             break;
01171               }
01172 
01173               manifest_flags |= (entry.flags & PHAR_ENT_COMPRESSION_MASK);
01174               /* if signature matched, no need to check CRC32 for each file */
01175               entry.is_crc_checked = (manifest_flags & PHAR_HDR_SIGNATURE ? 1 : 0);
01176               phar_set_inode(&entry TSRMLS_CC);
01177               zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL);
01178        }
01179 
01180        snprintf(mydata->version, sizeof(mydata->version), "%u.%u.%u", manifest_ver >> 12, (manifest_ver >> 8) & 0xF, (manifest_ver >> 4) & 0xF);
01181        mydata->internal_file_start = halt_offset + manifest_len + 4;
01182        mydata->halt_offset = halt_offset;
01183        mydata->flags = manifest_flags;
01184        endbuffer = strrchr(mydata->fname, '/');
01185 
01186        if (endbuffer) {
01187               mydata->ext = memchr(endbuffer, '.', (mydata->fname + fname_len) - endbuffer);
01188               if (mydata->ext == endbuffer) {
01189                      mydata->ext = memchr(endbuffer + 1, '.', (mydata->fname + fname_len) - endbuffer - 1);
01190               }
01191               if (mydata->ext) {
01192                      mydata->ext_len = (mydata->fname + mydata->fname_len) - mydata->ext;
01193               }
01194        }
01195 
01196        mydata->alias = alias ?
01197               pestrndup(alias, alias_len, mydata->is_persistent) :
01198               pestrndup(mydata->fname, fname_len, mydata->is_persistent);
01199        mydata->alias_len = alias ? alias_len : fname_len;
01200        mydata->sig_flags = sig_flags;
01201        mydata->fp = fp;
01202        mydata->sig_len = sig_len;
01203        mydata->signature = signature;
01204        phar_request_initialize(TSRMLS_C);
01205 
01206        if (register_alias) {
01207               phar_archive_data **fd_ptr;
01208 
01209               mydata->is_temporary_alias = temp_alias;
01210 
01211               if (!phar_validate_alias(mydata->alias, mydata->alias_len)) {
01212                      signature = NULL;
01213                      fp = NULL;
01214                      MAPPHAR_FAIL("Cannot open archive \"%s\", invalid alias");
01215               }
01216 
01217               if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) {
01218                      if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) {
01219                             signature = NULL;
01220                             fp = NULL;
01221                             MAPPHAR_FAIL("Cannot open archive \"%s\", alias is already in use by existing archive");
01222                      }
01223               }
01224 
01225               zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
01226        } else {
01227               mydata->is_temporary_alias = 1;
01228        }
01229 
01230        zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*),  NULL);
01231        efree(savebuf);
01232 
01233        if (pphar) {
01234               *pphar = mydata;
01235        }
01236 
01237        return SUCCESS;
01238 }
01239 /* }}} */
01240 
01244 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) /* {{{ */
01245 {
01246        const char *ext_str, *z;
01247        char *my_error;
01248        int ext_len;
01249        phar_archive_data **test, *unused = NULL;
01250 
01251        test = &unused;
01252 
01253        if (error) {
01254               *error = NULL;
01255        }
01256 
01257        /* first try to open an existing file */
01258        if (phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, !is_data, 0, 1 TSRMLS_CC) == SUCCESS) {
01259               goto check_file;
01260        }
01261 
01262        /* next try to create a new file */
01263        if (FAILURE == phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, !is_data, 1, 1 TSRMLS_CC)) {
01264               if (error) {
01265                      if (ext_len == -2) {
01266                             spprintf(error, 0, "Cannot create a phar archive from a URL like \"%s\". Phar objects can only be created from local files", fname);
01267                      } else {
01268                             spprintf(error, 0, "Cannot create phar '%s', file extension (or combination) not recognised or the directory does not exist", fname);
01269                      }
01270               }
01271               return FAILURE;
01272        }
01273 check_file:
01274        if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, test, &my_error TSRMLS_CC) == SUCCESS) {
01275               if (pphar) {
01276                      *pphar = *test;
01277               }
01278 
01279               if ((*test)->is_data && !(*test)->is_tar && !(*test)->is_zip) {
01280                      if (error) {
01281                             spprintf(error, 0, "Cannot open '%s' as a PharData object. Use Phar::__construct() for executable archives", fname);
01282                      }
01283                      return FAILURE;
01284               }
01285 
01286               if (PHAR_G(readonly) && !(*test)->is_data && ((*test)->is_tar || (*test)->is_zip)) {
01287                      phar_entry_info *stub;
01288                      if (FAILURE == zend_hash_find(&((*test)->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&stub)) {
01289                             spprintf(error, 0, "'%s' is not a phar archive. Use PharData::__construct() for a standard zip or tar archive", fname);
01290                             return FAILURE;
01291                      }
01292               }
01293 
01294               if (!PHAR_G(readonly) || (*test)->is_data) {
01295                      (*test)->is_writeable = 1;
01296               }
01297               return SUCCESS;
01298        } else if (my_error) {
01299               if (error) {
01300                      *error = my_error;
01301               } else {
01302                      efree(my_error);
01303               }
01304               return FAILURE;
01305        }
01306 
01307        if (ext_len > 3 && (z = memchr(ext_str, 'z', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ip", 2)) {
01308               /* assume zip-based phar */
01309               return phar_open_or_create_zip(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC);
01310        }
01311 
01312        if (ext_len > 3 && (z = memchr(ext_str, 't', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ar", 2)) {
01313               /* assume tar-based phar */
01314               return phar_open_or_create_tar(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC);
01315        }
01316 
01317        return phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC);
01318 }
01319 /* }}} */
01320 
01321 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) /* {{{ */
01322 {
01323        phar_archive_data *mydata;
01324        php_stream *fp;
01325        char *actual = NULL, *p;
01326 
01327        if (!pphar) {
01328               pphar = &mydata;
01329        }
01330 #if PHP_API_VERSION < 20100412
01331        if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
01332               return FAILURE;
01333        }
01334 #endif
01335        if (php_check_open_basedir(fname TSRMLS_CC)) {
01336               return FAILURE;
01337        }
01338 
01339        /* first open readonly so it won't be created if not present */
01340        fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, &actual);
01341 
01342        if (actual) {
01343               fname = actual;
01344               fname_len = strlen(actual);
01345        }
01346 
01347        if (fp) {
01348               if (phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error TSRMLS_CC) == SUCCESS) {
01349                      if ((*pphar)->is_data || !PHAR_G(readonly)) {
01350                             (*pphar)->is_writeable = 1;
01351                      }
01352                      if (actual) {
01353                             efree(actual);
01354                      }
01355                      return SUCCESS;
01356               } else {
01357                      /* file exists, but is either corrupt or not a phar archive */
01358                      if (actual) {
01359                             efree(actual);
01360                      }
01361                      return FAILURE;
01362               }
01363        }
01364 
01365        if (actual) {
01366               efree(actual);
01367        }
01368 
01369        if (PHAR_G(readonly) && !is_data) {
01370               if (options & REPORT_ERRORS) {
01371                      if (error) {
01372                             spprintf(error, 0, "creating archive \"%s\" disabled by the php.ini setting phar.readonly", fname);
01373                      }
01374               }
01375               return FAILURE;
01376        }
01377 
01378        /* set up our manifest */
01379        mydata = ecalloc(1, sizeof(phar_archive_data));
01380        mydata->fname = expand_filepath(fname, NULL TSRMLS_CC);
01381        fname_len = strlen(mydata->fname);
01382 #ifdef PHP_WIN32
01383        phar_unixify_path_separators(mydata->fname, fname_len);
01384 #endif
01385        p = strrchr(mydata->fname, '/');
01386 
01387        if (p) {
01388               mydata->ext = memchr(p, '.', (mydata->fname + fname_len) - p);
01389               if (mydata->ext == p) {
01390                      mydata->ext = memchr(p + 1, '.', (mydata->fname + fname_len) - p - 1);
01391               }
01392               if (mydata->ext) {
01393                      mydata->ext_len = (mydata->fname + fname_len) - mydata->ext;
01394               }
01395        }
01396 
01397        if (pphar) {
01398               *pphar = mydata;
01399        }
01400 
01401        zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
01402               zend_get_hash_value, destroy_phar_manifest_entry, 0);
01403        zend_hash_init(&mydata->mounted_dirs, sizeof(char *),
01404               zend_get_hash_value, NULL, 0);
01405        zend_hash_init(&mydata->virtual_dirs, sizeof(char *),
01406               zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
01407        mydata->fname_len = fname_len;
01408        snprintf(mydata->version, sizeof(mydata->version), "%s", PHP_PHAR_API_VERSION);
01409        mydata->is_temporary_alias = alias ? 0 : 1;
01410        mydata->internal_file_start = -1;
01411        mydata->fp = NULL;
01412        mydata->is_writeable = 1;
01413        mydata->is_brandnew = 1;
01414        phar_request_initialize(TSRMLS_C);
01415        zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*),  NULL);
01416 
01417        if (is_data) {
01418               alias = NULL;
01419               alias_len = 0;
01420               mydata->is_data = 1;
01421               /* assume tar format, PharData can specify other */
01422               mydata->is_tar = 1;
01423        } else {
01424               phar_archive_data **fd_ptr;
01425 
01426               if (alias && SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) {
01427                      if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) {
01428                             if (error) {
01429                                    spprintf(error, 4096, "phar error: phar \"%s\" cannot set alias \"%s\", already in use by another phar archive", mydata->fname, alias);
01430                             }
01431 
01432                             zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len);
01433 
01434                             if (pphar) {
01435                                    *pphar = NULL;
01436                             }
01437 
01438                             return FAILURE;
01439                      }
01440               }
01441 
01442               mydata->alias = alias ? estrndup(alias, alias_len) : estrndup(mydata->fname, fname_len);
01443               mydata->alias_len = alias ? alias_len : fname_len;
01444        }
01445 
01446        if (alias_len && alias) {
01447               if (FAILURE == zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL)) {
01448                      if (options & REPORT_ERRORS) {
01449                             if (error) {
01450                                    spprintf(error, 0, "archive \"%s\" cannot be associated with alias \"%s\", already in use", fname, alias);
01451                             }
01452                      }
01453 
01454                      zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len);
01455 
01456                      if (pphar) {
01457                             *pphar = NULL;
01458                      }
01459 
01460                      return FAILURE;
01461               }
01462        }
01463 
01464        return SUCCESS;
01465 }
01466 /* }}}*/
01467 
01475 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) /* {{{ */
01476 {
01477        php_stream *fp;
01478        char *actual;
01479        int ret, is_data = 0;
01480 
01481        if (error) {
01482               *error = NULL;
01483        }
01484 
01485        if (!strstr(fname, ".phar")) {
01486               is_data = 1;
01487        }
01488 
01489        if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC) == SUCCESS) {
01490               return SUCCESS;
01491        } else if (error && *error) {
01492               return FAILURE;
01493        }
01494 #if PHP_API_VERSION < 20100412
01495        if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
01496               return FAILURE;
01497        }
01498 #endif
01499        if (php_check_open_basedir(fname TSRMLS_CC)) {
01500               return FAILURE;
01501        }
01502 
01503        fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, &actual);
01504 
01505        if (!fp) {
01506               if (options & REPORT_ERRORS) {
01507                      if (error) {
01508                             spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
01509                      }
01510               }
01511               if (actual) {
01512                      efree(actual);
01513               }
01514               return FAILURE;
01515        }
01516 
01517        if (actual) {
01518               fname = actual;
01519               fname_len = strlen(actual);
01520        }
01521 
01522        ret =  phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error TSRMLS_CC);
01523 
01524        if (actual) {
01525               efree(actual);
01526        }
01527 
01528        return ret;
01529 }
01530 /* }}}*/
01531 
01532 static inline char *phar_strnstr(const char *buf, int buf_len, const char *search, int search_len) /* {{{ */
01533 {
01534        const char *c;
01535        int so_far = 0;
01536 
01537        if (buf_len < search_len) {
01538               return NULL;
01539        }
01540 
01541        c = buf - 1;
01542 
01543        do {
01544               if (!(c = memchr(c + 1, search[0], buf_len - search_len - so_far))) {
01545                      return (char *) NULL;
01546               }
01547 
01548               so_far = c - buf;
01549 
01550               if (so_far >= (buf_len - search_len)) {
01551                      return (char *) NULL;
01552               }
01553 
01554               if (!memcmp(c, search, search_len)) {
01555                      return (char *) c;
01556               }
01557        } while (1);
01558 }
01559 /* }}} */
01560 
01566 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) /* {{{ */
01567 {
01568        const char token[] = "__HALT_COMPILER();";
01569        const char zip_magic[] = "PK\x03\x04";
01570        const char gz_magic[] = "\x1f\x8b\x08";
01571        const char bz_magic[] = "BZh";
01572        char *pos, test = '\0';
01573        const int window_size = 1024;
01574        char buffer[1024 + sizeof(token)]; /* a 1024 byte window + the size of the halt_compiler token (moving window) */
01575        const long readsize = sizeof(buffer) - sizeof(token);
01576        const long tokenlen = sizeof(token) - 1;
01577        long halt_offset;
01578        size_t got;
01579        php_uint32 compression = PHAR_FILE_COMPRESSED_NONE;
01580 
01581        if (error) {
01582               *error = NULL;
01583        }
01584 
01585        if (-1 == php_stream_rewind(fp)) {
01586               MAPPHAR_ALLOC_FAIL("cannot rewind phar \"%s\"")
01587        }
01588 
01589        buffer[sizeof(buffer)-1] = '\0';
01590        memset(buffer, 32, sizeof(token));
01591        halt_offset = 0;
01592 
01593        /* Maybe it's better to compile the file instead of just searching,  */
01594        /* but we only want the offset. So we want a .re scanner to find it. */
01595        while(!php_stream_eof(fp)) {
01596               if ((got = php_stream_read(fp, buffer+tokenlen, readsize)) < (size_t) tokenlen) {
01597                      MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)")
01598               }
01599 
01600               if (!test) {
01601                      test = '\1';
01602                      pos = buffer+tokenlen;
01603                      if (!memcmp(pos, gz_magic, 3)) {
01604                             char err = 0;
01605                             php_stream_filter *filter;
01606                             php_stream *temp;
01607                             /* to properly decompress, we have to tell zlib to look for a zlib or gzip header */
01608                             zval filterparams;
01609 
01610                             if (!PHAR_G(has_zlib)) {
01611                                    MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file, enable zlib extension in php.ini")
01612                             }
01613                             array_init(&filterparams);
01614 /* this is defined in zlib's zconf.h */
01615 #ifndef MAX_WBITS
01616 #define MAX_WBITS 15
01617 #endif
01618                             add_assoc_long(&filterparams, "window", MAX_WBITS + 32);
01619 
01620                             /* entire file is gzip-compressed, uncompress to temporary file */
01621                             if (!(temp = php_stream_fopen_tmpfile())) {
01622                                    MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of gzipped phar archive \"%s\"")
01623                             }
01624 
01625                             php_stream_rewind(fp);
01626                             filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC);
01627 
01628                             if (!filter) {
01629                                    err = 1;
01630                                    add_assoc_long(&filterparams, "window", MAX_WBITS);
01631                                    filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC);
01632                                    zval_dtor(&filterparams);
01633 
01634                                    if (!filter) {
01635                                           php_stream_close(temp);
01636                                           MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6")
01637                                    }
01638                             } else {
01639                                    zval_dtor(&filterparams);
01640                             }
01641 
01642                             php_stream_filter_append(&temp->writefilters, filter);
01643 
01644                             if (SUCCESS != phar_stream_copy_to_stream(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
01645                                    if (err) {
01646                                           php_stream_close(temp);
01647                                           MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6")
01648                                    }
01649                                    php_stream_close(temp);
01650                                    MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file")
01651                             }
01652 
01653                             php_stream_filter_flush(filter, 1);
01654                             php_stream_filter_remove(filter, 1 TSRMLS_CC);
01655                             php_stream_close(fp);
01656                             fp = temp;
01657                             php_stream_rewind(fp);
01658                             compression = PHAR_FILE_COMPRESSED_GZ;
01659 
01660                             /* now, start over */
01661                             test = '\0';
01662                             continue;
01663                      } else if (!memcmp(pos, bz_magic, 3)) {
01664                             php_stream_filter *filter;
01665                             php_stream *temp;
01666 
01667                             if (!PHAR_G(has_bz2)) {
01668                                    MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file, enable bz2 extension in php.ini")
01669                             }
01670 
01671                             /* entire file is bzip-compressed, uncompress to temporary file */
01672                             if (!(temp = php_stream_fopen_tmpfile())) {
01673                                    MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of bzipped phar archive \"%s\"")
01674                             }
01675 
01676                             php_stream_rewind(fp);
01677                             filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp) TSRMLS_CC);
01678 
01679                             if (!filter) {
01680                                    php_stream_close(temp);
01681                                    MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\", filter creation failed")
01682                             }
01683 
01684                             php_stream_filter_append(&temp->writefilters, filter);
01685 
01686                             if (SUCCESS != phar_stream_copy_to_stream(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
01687                                    php_stream_close(temp);
01688                                    MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file")
01689                             }
01690 
01691                             php_stream_filter_flush(filter, 1);
01692                             php_stream_filter_remove(filter, 1 TSRMLS_CC);
01693                             php_stream_close(fp);
01694                             fp = temp;
01695                             php_stream_rewind(fp);
01696                             compression = PHAR_FILE_COMPRESSED_BZ2;
01697 
01698                             /* now, start over */
01699                             test = '\0';
01700                             continue;
01701                      }
01702 
01703                      if (!memcmp(pos, zip_magic, 4)) {
01704                             php_stream_seek(fp, 0, SEEK_END);
01705                             return phar_parse_zipfile(fp, fname, fname_len, alias, alias_len, pphar, error TSRMLS_CC);
01706                      }
01707 
01708                      if (got > 512) {
01709                             if (phar_is_tar(pos, fname)) {
01710                                    php_stream_rewind(fp);
01711                                    return phar_parse_tarfile(fp, fname, fname_len, alias, alias_len, pphar, is_data, compression, error TSRMLS_CC);
01712                             }
01713                      }
01714               }
01715 
01716               if (got > 0 && (pos = phar_strnstr(buffer, got + sizeof(token), token, sizeof(token)-1)) != NULL) {
01717                      halt_offset += (pos - buffer); /* no -tokenlen+tokenlen here */
01718                      return phar_parse_pharfile(fp, fname, fname_len, alias, alias_len, halt_offset, pphar, compression, error TSRMLS_CC);
01719               }
01720 
01721               halt_offset += got;
01722               memmove(buffer, buffer + window_size, tokenlen); /* move the memory buffer by the size of the window */
01723        }
01724 
01725        MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (__HALT_COMPILER(); not found)")
01726 }
01727 /* }}} */
01728 
01729 /*
01730  * given the location of the file extension and the start of the file path,
01731  * determine the end of the portion of the path (i.e. /path/to/file.ext/blah
01732  * grabs "/path/to/file.ext" as does the straight /path/to/file.ext),
01733  * stat it to determine if it exists.
01734  * if so, check to see if it is a directory and fail if so
01735  * if not, check to see if its dirname() exists (i.e. "/path/to") and is a directory
01736  * succeed if we are creating the file, otherwise fail.
01737  */
01738 static int phar_analyze_path(const char *fname, const char *ext, int ext_len, int for_create TSRMLS_DC) /* {{{ */
01739 {
01740        php_stream_statbuf ssb;
01741        char *realpath, old, *a = (char *)(ext + ext_len);
01742 
01743        old = *a;
01744        *a = '\0';
01745 
01746        if ((realpath = expand_filepath(fname, NULL TSRMLS_CC))) {
01747 #ifdef PHP_WIN32
01748               phar_unixify_path_separators(realpath, strlen(realpath));
01749 #endif
01750               if (zend_hash_exists(&(PHAR_GLOBALS->phar_fname_map), realpath, strlen(realpath))) {
01751                      *a = old;
01752                      efree(realpath);
01753                      return SUCCESS;
01754               }
01755 
01756               if (PHAR_G(manifest_cached) && zend_hash_exists(&cached_phars, realpath, strlen(realpath))) {
01757                      *a = old;
01758                      efree(realpath);
01759                      return SUCCESS;
01760               }
01761               efree(realpath);
01762        }
01763 
01764        if (SUCCESS == php_stream_stat_path((char *) fname, &ssb)) {
01765               *a = old;
01766 
01767               if (ssb.sb.st_mode & S_IFDIR) {
01768                      return FAILURE;
01769               }
01770 
01771               if (for_create == 1) {
01772                      return FAILURE;
01773               }
01774 
01775               return SUCCESS;
01776        } else {
01777               char *slash;
01778 
01779               if (!for_create) {
01780                      *a = old;
01781                      return FAILURE;
01782               }
01783 
01784               slash = (char *) strrchr(fname, '/');
01785               *a = old;
01786 
01787               if (slash) {
01788                      old = *slash;
01789                      *slash = '\0';
01790               }
01791 
01792               if (SUCCESS != php_stream_stat_path((char *) fname, &ssb)) {
01793                      if (slash) {
01794                             *slash = old;
01795                      } else {
01796                             if (!(realpath = expand_filepath(fname, NULL TSRMLS_CC))) {
01797                                    return FAILURE;
01798                             }
01799 #ifdef PHP_WIN32
01800                             phar_unixify_path_separators(realpath, strlen(realpath));
01801 #endif
01802                             a = strstr(realpath, fname) + ((ext - fname) + ext_len);
01803                             *a = '\0';
01804                             slash = strrchr(realpath, '/');
01805 
01806                             if (slash) {
01807                                    *slash = '\0';
01808                             } else {
01809                                    efree(realpath);
01810                                    return FAILURE;
01811                             }
01812 
01813                             if (SUCCESS != php_stream_stat_path(realpath, &ssb)) {
01814                                    efree(realpath);
01815                                    return FAILURE;
01816                             }
01817 
01818                             efree(realpath);
01819 
01820                             if (ssb.sb.st_mode & S_IFDIR) {
01821                                    return SUCCESS;
01822                             }
01823                      }
01824 
01825                      return FAILURE;
01826               }
01827 
01828               if (slash) {
01829                      *slash = old;
01830               }
01831 
01832               if (ssb.sb.st_mode & S_IFDIR) {
01833                      return SUCCESS;
01834               }
01835 
01836               return FAILURE;
01837        }
01838 }
01839 /* }}} */
01840 
01841 /* check for ".phar" in extension */
01842 static int phar_check_str(const char *fname, const char *ext_str, int ext_len, int executable, int for_create TSRMLS_DC) /* {{{ */
01843 {
01844        char test[51];
01845        const char *pos;
01846 
01847        if (ext_len >= 50) {
01848               return FAILURE;
01849        }
01850 
01851        if (executable == 1) {
01852               /* copy "." as well */
01853               memcpy(test, ext_str - 1, ext_len + 1);
01854               test[ext_len + 1] = '\0';
01855               /* executable phars must contain ".phar" as a valid extension (phar://.pharmy/oops is invalid) */
01856               /* (phar://hi/there/.phar/oops is also invalid) */
01857               pos = strstr(test, ".phar");
01858 
01859               if (pos && (*(pos - 1) != '/')
01860                             && (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) {
01861                      return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC);
01862               } else {
01863                      return FAILURE;
01864               }
01865        }
01866 
01867        /* data phars need only contain a single non-"." to be valid */
01868        if (!executable) {
01869               pos = strstr(ext_str, ".phar");
01870               if (!(pos && (*(pos - 1) != '/')
01871                                    && (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) && *(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
01872                      return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC);
01873               }
01874        } else {
01875               if (*(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
01876                      return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC);
01877               }
01878        }
01879 
01880        return FAILURE;
01881 }
01882 /* }}} */
01883 
01884 /*
01885  * if executable is 1, only returns SUCCESS if the extension is one of the tar/zip .phar extensions
01886  * if executable is 0, it returns SUCCESS only if the filename does *not* contain ".phar" anywhere, and treats
01887  * the first extension as the filename extension
01888  *
01889  * if an extension is found, it sets ext_str to the location of the file extension in filename,
01890  * and ext_len to the length of the extension.
01891  * for urls like "phar://alias/oops" it instead sets ext_len to -1 and returns FAILURE, which tells
01892  * the calling function to use "alias" as the phar alias
01893  *
01894  * the last parameter should be set to tell the thing to assume that filename is the full path, and only to check the
01895  * extension rules, not to iterate.
01896  */
01897 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) /* {{{ */
01898 {
01899        const char *pos, *slash;
01900 
01901        *ext_str = NULL;
01902        *ext_len = 0;
01903 
01904        if (!filename_len || filename_len == 1) {
01905               return FAILURE;
01906        }
01907 
01908        phar_request_initialize(TSRMLS_C);
01909        /* first check for alias in first segment */
01910        pos = memchr(filename, '/', filename_len);
01911 
01912        if (pos && pos != filename) {
01913               /* check for url like http:// or phar:// */
01914               if (*(pos - 1) == ':' && (pos - filename) < filename_len - 1 && *(pos + 1) == '/') {
01915                      *ext_len = -2;
01916                      *ext_str = NULL;
01917                      return FAILURE;
01918               }
01919               if (zend_hash_exists(&(PHAR_GLOBALS->phar_alias_map), (char *) filename, pos - filename)) {
01920                      *ext_str = pos;
01921                      *ext_len = -1;
01922                      return FAILURE;
01923               }
01924 
01925               if (PHAR_G(manifest_cached) && zend_hash_exists(&cached_alias, (char *) filename, pos - filename)) {
01926                      *ext_str = pos;
01927                      *ext_len = -1;
01928                      return FAILURE;
01929               }
01930        }
01931 
01932        if (zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map)) || PHAR_G(manifest_cached)) {
01933               phar_archive_data **pphar;
01934 
01935               if (is_complete) {
01936                      if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), (char *) filename, filename_len, (void **)&pphar)) {
01937                             *ext_str = filename + (filename_len - (*pphar)->ext_len);
01938 woohoo:
01939                             *ext_len = (*pphar)->ext_len;
01940 
01941                             if (executable == 2) {
01942                                    return SUCCESS;
01943                             }
01944 
01945                             if (executable == 1 && !(*pphar)->is_data) {
01946                                    return SUCCESS;
01947                             }
01948 
01949                             if (!executable && (*pphar)->is_data) {
01950                                    return SUCCESS;
01951                             }
01952 
01953                             return FAILURE;
01954                      }
01955 
01956                      if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_find(&cached_phars, (char *) filename, filename_len, (void **)&pphar)) {
01957                             *ext_str = filename + (filename_len - (*pphar)->ext_len);
01958                             goto woohoo;
01959                      }
01960               } else {
01961                      phar_zstr key;
01962                      char *str_key;
01963                      uint keylen;
01964                      ulong unused;
01965 
01966                      zend_hash_internal_pointer_reset(&(PHAR_GLOBALS->phar_fname_map));
01967 
01968                      while (FAILURE != zend_hash_has_more_elements(&(PHAR_GLOBALS->phar_fname_map))) {
01969                             if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(&(PHAR_GLOBALS->phar_fname_map), &key, &keylen, &unused, 0, NULL)) {
01970                                    break;
01971                             }
01972 
01973                             PHAR_STR(key, str_key);
01974 
01975                             if (keylen > (uint) filename_len) {
01976                                    zend_hash_move_forward(&(PHAR_GLOBALS->phar_fname_map));
01977                                    PHAR_STR_FREE(str_key);
01978                                    continue;
01979                             }
01980 
01981                             if (!memcmp(filename, str_key, keylen) && ((uint)filename_len == keylen
01982                                    || filename[keylen] == '/' || filename[keylen] == '\0')) {
01983                                    PHAR_STR_FREE(str_key);
01984                                    if (FAILURE == zend_hash_get_current_data(&(PHAR_GLOBALS->phar_fname_map), (void **) &pphar)) {
01985                                           break;
01986                                    }
01987                                    *ext_str = filename + (keylen - (*pphar)->ext_len);
01988                                    goto woohoo;
01989                             }
01990 
01991                             PHAR_STR_FREE(str_key);
01992                             zend_hash_move_forward(&(PHAR_GLOBALS->phar_fname_map));
01993                      }
01994 
01995                      if (PHAR_G(manifest_cached)) {
01996                             zend_hash_internal_pointer_reset(&cached_phars);
01997 
01998                             while (FAILURE != zend_hash_has_more_elements(&cached_phars)) {
01999                                    if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(&cached_phars, &key, &keylen, &unused, 0, NULL)) {
02000                                           break;
02001                                    }
02002 
02003                                    PHAR_STR(key, str_key);
02004 
02005                                    if (keylen > (uint) filename_len) {
02006                                           zend_hash_move_forward(&cached_phars);
02007                                           PHAR_STR_FREE(str_key);
02008                                           continue;
02009                                    }
02010 
02011                                    if (!memcmp(filename, str_key, keylen) && ((uint)filename_len == keylen
02012                                           || filename[keylen] == '/' || filename[keylen] == '\0')) {
02013                                           PHAR_STR_FREE(str_key);
02014                                           if (FAILURE == zend_hash_get_current_data(&cached_phars, (void **) &pphar)) {
02015                                                  break;
02016                                           }
02017                                           *ext_str = filename + (keylen - (*pphar)->ext_len);
02018                                           goto woohoo;
02019                                    }
02020                                    PHAR_STR_FREE(str_key);
02021                                    zend_hash_move_forward(&cached_phars);
02022                             }
02023                      }
02024               }
02025        }
02026 
02027        pos = memchr(filename + 1, '.', filename_len);
02028 next_extension:
02029        if (!pos) {
02030               return FAILURE;
02031        }
02032 
02033        while (pos != filename && (*(pos - 1) == '/' || *(pos - 1) == '\0')) {
02034               pos = memchr(pos + 1, '.', filename_len - (pos - filename) + 1);
02035               if (!pos) {
02036                      return FAILURE;
02037               }
02038        }
02039 
02040        slash = memchr(pos, '/', filename_len - (pos - filename));
02041 
02042        if (!slash) {
02043               /* this is a url like "phar://blah.phar" with no directory */
02044               *ext_str = pos;
02045               *ext_len = strlen(pos);
02046 
02047               /* file extension must contain "phar" */
02048               switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create TSRMLS_CC)) {
02049                      case SUCCESS:
02050                             return SUCCESS;
02051                      case FAILURE:
02052                             /* we are at the end of the string, so we fail */
02053                             return FAILURE;
02054               }
02055        }
02056 
02057        /* we've found an extension that ends at a directory separator */
02058        *ext_str = pos;
02059        *ext_len = slash - pos;
02060 
02061        switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create TSRMLS_CC)) {
02062               case SUCCESS:
02063                      return SUCCESS;
02064               case FAILURE:
02065                      /* look for more extensions */
02066                      pos = strchr(pos + 1, '.');
02067                      if (pos) {
02068                             *ext_str = NULL;
02069                             *ext_len = 0;
02070                      }
02071                      goto next_extension;
02072        }
02073 
02074        return FAILURE;
02075 }
02076 /* }}} */
02077 
02078 static int php_check_dots(const char *element, int n) /* {{{ */
02079 {
02080        for(n--; n >= 0; --n) {
02081               if (element[n] != '.') {
02082                      return 1;
02083               }
02084        }
02085        return 0;
02086 }
02087 /* }}} */
02088 
02089 #define IS_DIRECTORY_UP(element, len) \
02090        (len >= 2 && !php_check_dots(element, len))
02091 
02092 #define IS_DIRECTORY_CURRENT(element, len) \
02093        (len == 1 && element[0] == '.')
02094 
02095 #define IS_BACKSLASH(c) ((c) == '/')
02096 
02097 #ifdef COMPILE_DL_PHAR
02098 /* stupid-ass non-extern declaration in tsrm_strtok.h breaks dumbass MS compiler */
02099 static inline int in_character_class(char ch, const char *delim) /* {{{ */
02100 {
02101        while (*delim) {
02102               if (*delim == ch) {
02103                      return 1;
02104               }
02105               ++delim;
02106        }
02107        return 0;
02108 }
02109 /* }}} */
02110 
02111 char *tsrm_strtok_r(char *s, const char *delim, char **last) /* {{{ */
02112 {
02113        char *token;
02114 
02115        if (s == NULL) {
02116               s = *last;
02117        }
02118 
02119        while (*s && in_character_class(*s, delim)) {
02120               ++s;
02121        }
02122 
02123        if (!*s) {
02124               return NULL;
02125        }
02126 
02127        token = s;
02128 
02129        while (*s && !in_character_class(*s, delim)) {
02130               ++s;
02131        }
02132 
02133        if (!*s) {
02134               *last = s;
02135        } else {
02136               *s = '\0';
02137               *last = s + 1;
02138        }
02139 
02140        return token;
02141 }
02142 /* }}} */
02143 #endif
02144 
02148 char *phar_fix_filepath(char *path, int *new_len, int use_cwd TSRMLS_DC) /* {{{ */
02149 {
02150        char newpath[MAXPATHLEN];
02151        int newpath_len;
02152        char *ptr;
02153        char *tok;
02154        int ptr_length, path_length = *new_len;
02155 
02156        if (PHAR_G(cwd_len) && use_cwd && path_length > 2 && path[0] == '.' && path[1] == '/') {
02157               newpath_len = PHAR_G(cwd_len);
02158               memcpy(newpath, PHAR_G(cwd), newpath_len);
02159        } else {
02160               newpath[0] = '/';
02161               newpath_len = 1;
02162        }
02163 
02164        ptr = path;
02165 
02166        if (*ptr == '/') {
02167               ++ptr;
02168        }
02169 
02170        tok = ptr;
02171 
02172        do {
02173               ptr = memchr(ptr, '/', path_length - (ptr - path));
02174        } while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
02175 
02176        if (!ptr && (path_length - (tok - path))) {
02177               switch (path_length - (tok - path)) {
02178                      case 1:
02179                             if (*tok == '.') {
02180                                    efree(path);
02181                                    *new_len = 1;
02182                                    return estrndup("/", 1);
02183                             }
02184                             break;
02185                      case 2:
02186                             if (tok[0] == '.' && tok[1] == '.') {
02187                                    efree(path);
02188                                    *new_len = 1;
02189                                    return estrndup("/", 1);
02190                             }
02191               }
02192               return path;
02193        }
02194 
02195        while (ptr) {
02196               ptr_length = ptr - tok;
02197 last_time:
02198               if (IS_DIRECTORY_UP(tok, ptr_length)) {
02199 #define PREVIOUS newpath[newpath_len - 1]
02200 
02201                      while (newpath_len > 1 && !IS_BACKSLASH(PREVIOUS)) {
02202                             newpath_len--;
02203                      }
02204 
02205                      if (newpath[0] != '/') {
02206                             newpath[newpath_len] = '\0';
02207                      } else if (newpath_len > 1) {
02208                             --newpath_len;
02209                      }
02210               } else if (!IS_DIRECTORY_CURRENT(tok, ptr_length)) {
02211                      if (newpath_len > 1) {
02212                             newpath[newpath_len++] = '/';
02213                             memcpy(newpath + newpath_len, tok, ptr_length+1);
02214                      } else {
02215                             memcpy(newpath + newpath_len, tok, ptr_length+1);
02216                      }
02217 
02218                      newpath_len += ptr_length;
02219               }
02220 
02221               if (ptr == path + path_length) {
02222                      break;
02223               }
02224 
02225               tok = ++ptr;
02226 
02227               do {
02228                      ptr = memchr(ptr, '/', path_length - (ptr - path));
02229               } while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
02230 
02231               if (!ptr && (path_length - (tok - path))) {
02232                      ptr_length = path_length - (tok - path);
02233                      ptr = path + path_length;
02234                      goto last_time;
02235               }
02236        }
02237 
02238        efree(path);
02239        *new_len = newpath_len;
02240        return estrndup(newpath, newpath_len);
02241 }
02242 /* }}} */
02243 
02256 int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_len, char **entry, int *entry_len, int executable, int for_create TSRMLS_DC) /* {{{ */
02257 {
02258        const char *ext_str;
02259 #ifdef PHP_WIN32
02260        char *save;
02261 #endif
02262        int ext_len, free_filename = 0;
02263 
02264        if (!strncasecmp(filename, "phar://", 7)) {
02265               filename += 7;
02266               filename_len -= 7;
02267        }
02268 
02269        ext_len = 0;
02270 #ifdef PHP_WIN32
02271        free_filename = 1;
02272        save = filename;
02273        filename = estrndup(filename, filename_len);
02274        phar_unixify_path_separators(filename, filename_len);
02275 #endif
02276        if (phar_detect_phar_fname_ext(filename, filename_len, &ext_str, &ext_len, executable, for_create, 0 TSRMLS_CC) == FAILURE) {
02277               if (ext_len != -1) {
02278                      if (!ext_str) {
02279                             /* no / detected, restore arch for error message */
02280 #ifdef PHP_WIN32
02281                             *arch = save;
02282 #else
02283                             *arch = filename;
02284 #endif
02285                      }
02286 
02287                      if (free_filename) {
02288                             efree(filename);
02289                      }
02290 
02291                      return FAILURE;
02292               }
02293 
02294               ext_len = 0;
02295               /* no extension detected - instead we are dealing with an alias */
02296        }
02297 
02298        *arch_len = ext_str - filename + ext_len;
02299        *arch = estrndup(filename, *arch_len);
02300 
02301        if (ext_str[ext_len]) {
02302               *entry_len = filename_len - *arch_len;
02303               *entry = estrndup(ext_str+ext_len, *entry_len);
02304 #ifdef PHP_WIN32
02305               phar_unixify_path_separators(*entry, *entry_len);
02306 #endif
02307               *entry = phar_fix_filepath(*entry, entry_len, 0 TSRMLS_CC);
02308        } else {
02309               *entry_len = 1;
02310               *entry = estrndup("/", 1);
02311        }
02312 
02313        if (free_filename) {
02314               efree(filename);
02315        }
02316 
02317        return SUCCESS;
02318 }
02319 /* }}} */
02320 
02325 int phar_open_executed_filename(char *alias, int alias_len, char **error TSRMLS_DC) /* {{{ */
02326 {
02327        char *fname;
02328        zval *halt_constant;
02329        php_stream *fp;
02330        int fname_len;
02331        char *actual = NULL;
02332        int ret;
02333 
02334        if (error) {
02335               *error = NULL;
02336        }
02337 
02338        fname = zend_get_executed_filename(TSRMLS_C);
02339        fname_len = strlen(fname);
02340 
02341        if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, 0, REPORT_ERRORS, NULL, 0 TSRMLS_CC) == SUCCESS) {
02342               return SUCCESS;
02343        }
02344 
02345        if (!strcmp(fname, "[no active file]")) {
02346               if (error) {
02347                      spprintf(error, 0, "cannot initialize a phar outside of PHP execution");
02348               }
02349               return FAILURE;
02350        }
02351 
02352        MAKE_STD_ZVAL(halt_constant);
02353 
02354        if (0 == zend_get_constant("__COMPILER_HALT_OFFSET__", 24, halt_constant TSRMLS_CC)) {
02355               FREE_ZVAL(halt_constant);
02356               if (error) {
02357                      spprintf(error, 0, "__HALT_COMPILER(); must be declared in a phar");
02358               }
02359               return FAILURE;
02360        }
02361 
02362        FREE_ZVAL(halt_constant);
02363 
02364 #if PHP_API_VERSION < 20100412
02365        if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
02366               return FAILURE;
02367        }
02368 #endif
02369 
02370        if (php_check_open_basedir(fname TSRMLS_CC)) {
02371               return FAILURE;
02372        }
02373 
02374        fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, &actual);
02375 
02376        if (!fp) {
02377               if (error) {
02378                      spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
02379               }
02380               if (actual) {
02381                      efree(actual);
02382               }
02383               return FAILURE;
02384        }
02385 
02386        if (actual) {
02387               fname = actual;
02388               fname_len = strlen(actual);
02389        }
02390 
02391        ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, REPORT_ERRORS, NULL, 0, error TSRMLS_CC);
02392 
02393        if (actual) {
02394               efree(actual);
02395        }
02396 
02397        return ret;
02398 }
02399 /* }}} */
02400 
02404 int phar_postprocess_file(phar_entry_data *idata, php_uint32 crc32, char **error, int process_zip TSRMLS_DC) /* {{{ */
02405 {
02406        php_uint32 crc = ~0;
02407        int len = idata->internal_file->uncompressed_filesize;
02408        php_stream *fp = idata->fp;
02409        phar_entry_info *entry = idata->internal_file;
02410 
02411        if (error) {
02412               *error = NULL;
02413        }
02414 
02415        if (entry->is_zip && process_zip > 0) {
02416               /* verify local file header */
02417               phar_zip_file_header local;
02418               phar_zip_data_desc desc;
02419 
02420               if (SUCCESS != phar_open_archive_fp(idata->phar TSRMLS_CC)) {
02421                      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);
02422                      return FAILURE;
02423               }
02424               php_stream_seek(phar_get_entrypfp(idata->internal_file TSRMLS_CC), entry->header_offset, SEEK_SET);
02425 
02426               if (sizeof(local) != php_stream_read(phar_get_entrypfp(idata->internal_file TSRMLS_CC), (char *) &local, sizeof(local))) {
02427 
02428                      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);
02429                      return FAILURE;
02430               }
02431 
02432               /* check for data descriptor */
02433               if (((PHAR_ZIP_16(local.flags)) & 0x8) == 0x8) {
02434                      php_stream_seek(phar_get_entrypfp(idata->internal_file TSRMLS_CC),
02435                                    entry->header_offset + sizeof(local) +
02436                                    PHAR_ZIP_16(local.filename_len) +
02437                                    PHAR_ZIP_16(local.extra_len) +
02438                                    entry->compressed_filesize, SEEK_SET);
02439                      if (sizeof(desc) != php_stream_read(phar_get_entrypfp(idata->internal_file TSRMLS_CC),
02440                                                      (char *) &desc, sizeof(desc))) {
02441                             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);
02442                             return FAILURE;
02443                      }
02444                      if (desc.signature[0] == 'P' && desc.signature[1] == 'K') {
02445                             memcpy(&(local.crc32), &(desc.crc32), 12);
02446                      } else {
02447                             /* old data descriptors have no signature */
02448                             memcpy(&(local.crc32), &desc, 12);
02449                      }
02450               }
02451               /* verify local header */
02452               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)) {
02453                      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);
02454                      return FAILURE;
02455               }
02456 
02457               /* construct actual offset to file start - local extra_len can be different from central extra_len */
02458               entry->offset = entry->offset_abs =
02459                      sizeof(local) + entry->header_offset + PHAR_ZIP_16(local.filename_len) + PHAR_ZIP_16(local.extra_len);
02460 
02461               if (idata->zero && idata->zero != entry->offset_abs) {
02462                      idata->zero = entry->offset_abs;
02463               }
02464        }
02465 
02466        if (process_zip == 1) {
02467               return SUCCESS;
02468        }
02469 
02470        php_stream_seek(fp, idata->zero, SEEK_SET);
02471 
02472        while (len--) {
02473               CRC32(crc, php_stream_getc(fp));
02474        }
02475 
02476        php_stream_seek(fp, idata->zero, SEEK_SET);
02477 
02478        if (~crc == crc32) {
02479               entry->is_crc_checked = 1;
02480               return SUCCESS;
02481        } else {
02482               spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (crc32 mismatch on file \"%s\")", idata->phar->fname, entry->filename);
02483               return FAILURE;
02484        }
02485 }
02486 /* }}} */
02487 
02488 static inline void phar_set_32(char *buffer, int var) /* {{{ */
02489 {
02490 #ifdef WORDS_BIGENDIAN
02491        *((buffer) + 3) = (unsigned char) (((var) >> 24) & 0xFF);
02492        *((buffer) + 2) = (unsigned char) (((var) >> 16) & 0xFF);
02493        *((buffer) + 1) = (unsigned char) (((var) >> 8) & 0xFF);
02494        *((buffer) + 0) = (unsigned char) ((var) & 0xFF);
02495 #else
02496         memcpy(buffer, &var, sizeof(var));
02497 #endif
02498 } /* }}} */
02499 
02500 static int phar_flush_clean_deleted_apply(void *data TSRMLS_DC) /* {{{ */
02501 {
02502        phar_entry_info *entry = (phar_entry_info *)data;
02503 
02504        if (entry->fp_refcount <= 0 && entry->is_deleted) {
02505               return ZEND_HASH_APPLY_REMOVE;
02506        } else {
02507               return ZEND_HASH_APPLY_KEEP;
02508        }
02509 }
02510 /* }}} */
02511 
02512 #include "stub.h"
02513 
02514 char *phar_create_default_stub(const char *index_php, const char *web_index, size_t *len, char **error TSRMLS_DC) /* {{{ */
02515 {
02516        char *stub = NULL;
02517        int index_len, web_len;
02518        size_t dummy;
02519 
02520        if (!len) {
02521               len = &dummy;
02522        }
02523 
02524        if (error) {
02525               *error = NULL;
02526        }
02527 
02528        if (!index_php) {
02529               index_php = "index.php";
02530        }
02531 
02532        if (!web_index) {
02533               web_index = "index.php";
02534        }
02535 
02536        index_len = strlen(index_php);
02537        web_len = strlen(web_index);
02538 
02539        if (index_len > 400) {
02540               /* ridiculous size not allowed for index.php startup filename */
02541               if (error) {
02542                      spprintf(error, 0, "Illegal filename passed in for stub creation, was %d characters long, and only 400 or less is allowed", index_len);
02543                      return NULL;
02544               }
02545        }
02546 
02547        if (web_len > 400) {
02548               /* ridiculous size not allowed for index.php startup filename */
02549               if (error) {
02550                      spprintf(error, 0, "Illegal web filename passed in for stub creation, was %d characters long, and only 400 or less is allowed", web_len);
02551                      return NULL;
02552               }
02553        }
02554 
02555        phar_get_stub(index_php, web_index, len, &stub, index_len+1, web_len+1 TSRMLS_CC);
02556        return stub;
02557 }
02558 /* }}} */
02559 
02566 int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, char **error TSRMLS_DC) /* {{{ */
02567 {
02568        char halt_stub[] = "__HALT_COMPILER();";
02569        char *newstub, *tmp;
02570        phar_entry_info *entry, *newentry;
02571        int halt_offset, restore_alias_len, global_flags = 0, closeoldfile;
02572        char *pos, has_dirs = 0;
02573        char manifest[18], entry_buffer[24];
02574        off_t manifest_ftell;
02575        long offset;
02576        size_t wrote;
02577        php_uint32 manifest_len, mytime, loc, new_manifest_count;
02578        php_uint32 newcrc32;
02579        php_stream *file, *oldfile, *newfile, *stubfile;
02580        php_stream_filter *filter;
02581        php_serialize_data_t metadata_hash;
02582        smart_str main_metadata_str = {0};
02583        int free_user_stub, free_fp = 1, free_ufp = 1;
02584 
02585        if (phar->is_persistent) {
02586               if (error) {
02587                      spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
02588               }
02589               return EOF;
02590        }
02591 
02592        if (error) {
02593               *error = NULL;
02594        }
02595 
02596        if (!zend_hash_num_elements(&phar->manifest) && !user_stub) {
02597               return EOF;
02598        }
02599 
02600        zend_hash_clean(&phar->virtual_dirs);
02601 
02602        if (phar->is_zip) {
02603               return phar_zip_flush(phar, user_stub, len, convert, error TSRMLS_CC);
02604        }
02605 
02606        if (phar->is_tar) {
02607               return phar_tar_flush(phar, user_stub, len, convert, error TSRMLS_CC);
02608        }
02609 
02610        if (PHAR_G(readonly)) {
02611               return EOF;
02612        }
02613 
02614        if (phar->fp && !phar->is_brandnew) {
02615               oldfile = phar->fp;
02616               closeoldfile = 0;
02617               php_stream_rewind(oldfile);
02618        } else {
02619               oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
02620               closeoldfile = oldfile != NULL;
02621        }
02622        newfile = php_stream_fopen_tmpfile();
02623        if (!newfile) {
02624               if (error) {
02625                      spprintf(error, 0, "unable to create temporary file");
02626               }
02627               if (closeoldfile) {
02628                      php_stream_close(oldfile);
02629               }
02630               return EOF;
02631        }
02632 
02633        if (user_stub) {
02634               if (len < 0) {
02635                      /* resource passed in */
02636                      if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) {
02637                             if (closeoldfile) {
02638                                    php_stream_close(oldfile);
02639                             }
02640                             php_stream_close(newfile);
02641                             if (error) {
02642                                    spprintf(error, 0, "unable to access resource to copy stub to new phar \"%s\"", phar->fname);
02643                             }
02644                             return EOF;
02645                      }
02646                      if (len == -1) {
02647                             len = PHP_STREAM_COPY_ALL;
02648                      } else {
02649                             len = -len;
02650                      }
02651                      user_stub = 0;
02652 #if PHP_MAJOR_VERSION >= 6
02653                      if (!(len = php_stream_copy_to_mem(stubfile, (void **) &user_stub, len, 0)) || !user_stub) {
02654 #else
02655                      if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) {
02656 #endif
02657                             if (closeoldfile) {
02658                                    php_stream_close(oldfile);
02659                             }
02660                             php_stream_close(newfile);
02661                             if (error) {
02662                                    spprintf(error, 0, "unable to read resource to copy stub to new phar \"%s\"", phar->fname);
02663                             }
02664                             return EOF;
02665                      }
02666                      free_user_stub = 1;
02667               } else {
02668                      free_user_stub = 0;
02669               }
02670               tmp = estrndup(user_stub, len);
02671               if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
02672                      efree(tmp);
02673                      if (closeoldfile) {
02674                             php_stream_close(oldfile);
02675                      }
02676                      php_stream_close(newfile);
02677                      if (error) {
02678                             spprintf(error, 0, "illegal stub for phar \"%s\"", phar->fname);
02679                      }
02680                      if (free_user_stub) {
02681                             efree(user_stub);
02682                      }
02683                      return EOF;
02684               }
02685               pos = user_stub + (pos - tmp);
02686               efree(tmp);
02687               len = pos - user_stub + 18;
02688               if ((size_t)len != php_stream_write(newfile, user_stub, len)
02689               ||                     5 != php_stream_write(newfile, " ?>\r\n", 5)) {
02690                      if (closeoldfile) {
02691                             php_stream_close(oldfile);
02692                      }
02693                      php_stream_close(newfile);
02694                      if (error) {
02695                             spprintf(error, 0, "unable to create stub from string in new phar \"%s\"", phar->fname);
02696                      }
02697                      if (free_user_stub) {
02698                             efree(user_stub);
02699                      }
02700                      return EOF;
02701               }
02702               phar->halt_offset = len + 5;
02703               if (free_user_stub) {
02704                      efree(user_stub);
02705               }
02706        } else {
02707               size_t written;
02708 
02709               if (!user_stub && phar->halt_offset && oldfile && !phar->is_brandnew) {
02710                      phar_stream_copy_to_stream(oldfile, newfile, phar->halt_offset, &written);
02711                      newstub = NULL;
02712               } else {
02713                      /* this is either a brand new phar or a default stub overwrite */
02714                      newstub = phar_create_default_stub(NULL, NULL, &(phar->halt_offset), NULL TSRMLS_CC);
02715                      written = php_stream_write(newfile, newstub, phar->halt_offset);
02716               }
02717               if (phar->halt_offset != written) {
02718                      if (closeoldfile) {
02719                             php_stream_close(oldfile);
02720                      }
02721                      php_stream_close(newfile);
02722                      if (error) {
02723                             if (newstub) {
02724                                    spprintf(error, 0, "unable to create stub in new phar \"%s\"", phar->fname);
02725                             } else {
02726                                    spprintf(error, 0, "unable to copy stub of old phar to new phar \"%s\"", phar->fname);
02727                             }
02728                      }
02729                      if (newstub) {
02730                             efree(newstub);
02731                      }
02732                      return EOF;
02733               }
02734               if (newstub) {
02735                      efree(newstub);
02736               }
02737        }
02738        manifest_ftell = php_stream_tell(newfile);
02739        halt_offset = manifest_ftell;
02740 
02741        /* Check whether we can get rid of some of the deleted entries which are
02742         * unused. However some might still be in use so even after this clean-up
02743         * we need to skip entries marked is_deleted. */
02744        zend_hash_apply(&phar->manifest, phar_flush_clean_deleted_apply TSRMLS_CC);
02745 
02746        /* compress as necessary, calculate crcs, serialize meta-data, manifest size, and file sizes */
02747        main_metadata_str.c = 0;
02748        if (phar->metadata) {
02749               PHP_VAR_SERIALIZE_INIT(metadata_hash);
02750               php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash TSRMLS_CC);
02751               PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
02752        } else {
02753               main_metadata_str.len = 0;
02754        }
02755        new_manifest_count = 0;
02756        offset = 0;
02757        for (zend_hash_internal_pointer_reset(&phar->manifest);
02758               zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
02759               zend_hash_move_forward(&phar->manifest)) {
02760               if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) {
02761                      continue;
02762               }
02763               if (entry->cfp) {
02764                      /* did we forget to get rid of cfp last time? */
02765                      php_stream_close(entry->cfp);
02766                      entry->cfp = 0;
02767               }
02768               if (entry->is_deleted || entry->is_mounted) {
02769                      /* remove this from the new phar */
02770                      continue;
02771               }
02772               if (!entry->is_modified && entry->fp_refcount) {
02773                      /* open file pointers refer to this fp, do not free the stream */
02774                      switch (entry->fp_type) {
02775                             case PHAR_FP:
02776                                    free_fp = 0;
02777                                    break;
02778                             case PHAR_UFP:
02779                                    free_ufp = 0;
02780                             default:
02781                                    break;
02782                      }
02783               }
02784               /* after excluding deleted files, calculate manifest size in bytes and number of entries */
02785               ++new_manifest_count;
02786               phar_add_virtual_dirs(phar, entry->filename, entry->filename_len TSRMLS_CC);
02787 
02788               if (entry->is_dir) {
02789                      /* we use this to calculate API version, 1.1.1 is used for phars with directories */
02790                      has_dirs = 1;
02791               }
02792               if (entry->metadata) {
02793                      if (entry->metadata_str.c) {
02794                             smart_str_free(&entry->metadata_str);
02795                      }
02796                      entry->metadata_str.c = 0;
02797                      entry->metadata_str.len = 0;
02798                      PHP_VAR_SERIALIZE_INIT(metadata_hash);
02799                      php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash TSRMLS_CC);
02800                      PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
02801               } else {
02802                      if (entry->metadata_str.c) {
02803                             smart_str_free(&entry->metadata_str);
02804                      }
02805                      entry->metadata_str.c = 0;
02806                      entry->metadata_str.len = 0;
02807               }
02808 
02809               /* 32 bits for filename length, length of filename, manifest + metadata, and add 1 for trailing / if a directory */
02810               offset += 4 + entry->filename_len + sizeof(entry_buffer) + entry->metadata_str.len + (entry->is_dir ? 1 : 0);
02811 
02812               /* compress and rehash as necessary */
02813               if ((oldfile && !entry->is_modified) || entry->is_dir) {
02814                      if (entry->fp_type == PHAR_UFP) {
02815                             /* reset so we can copy the compressed data over */
02816                             entry->fp_type = PHAR_FP;
02817                      }
02818                      continue;
02819               }
02820               if (!phar_get_efp(entry, 0 TSRMLS_CC)) {
02821                      /* re-open internal file pointer just-in-time */
02822                      newentry = phar_open_jit(phar, entry, error TSRMLS_CC);
02823                      if (!newentry) {
02824                             /* major problem re-opening, so we ignore this file and the error */
02825                             efree(*error);
02826                             *error = NULL;
02827                             continue;
02828                      }
02829                      entry = newentry;
02830               }
02831               file = phar_get_efp(entry, 0 TSRMLS_CC);
02832               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 1 TSRMLS_CC)) {
02833                      if (closeoldfile) {
02834                             php_stream_close(oldfile);
02835                      }
02836                      php_stream_close(newfile);
02837                      if (error) {
02838                             spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
02839                      }
02840                      return EOF;
02841               }
02842               newcrc32 = ~0;
02843               mytime = entry->uncompressed_filesize;
02844               for (loc = 0;loc < mytime; ++loc) {
02845                      CRC32(newcrc32, php_stream_getc(file));
02846               }
02847               entry->crc32 = ~newcrc32;
02848               entry->is_crc_checked = 1;
02849               if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
02850                      /* not compressed */
02851                      entry->compressed_filesize = entry->uncompressed_filesize;
02852                      continue;
02853               }
02854               filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0 TSRMLS_CC);
02855               if (!filter) {
02856                      if (closeoldfile) {
02857                             php_stream_close(oldfile);
02858                      }
02859                      php_stream_close(newfile);
02860                      if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
02861                             if (error) {
02862                                    spprintf(error, 0, "unable to gzip compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
02863                             }
02864                      } else {
02865                             if (error) {
02866                                    spprintf(error, 0, "unable to bzip2 compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
02867                             }
02868                      }
02869                      return EOF;
02870               }
02871 
02872               /* create new file that holds the compressed version */
02873               /* work around inability to specify freedom in write and strictness
02874               in read count */
02875               entry->cfp = php_stream_fopen_tmpfile();
02876               if (!entry->cfp) {
02877                      if (error) {
02878                             spprintf(error, 0, "unable to create temporary file");
02879                      }
02880                      if (closeoldfile) {
02881                             php_stream_close(oldfile);
02882                      }
02883                      php_stream_close(newfile);
02884                      return EOF;
02885               }
02886               php_stream_flush(file);
02887               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
02888                      if (closeoldfile) {
02889                             php_stream_close(oldfile);
02890                      }
02891                      php_stream_close(newfile);
02892                      if (error) {
02893                             spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
02894                      }
02895                      return EOF;
02896               }
02897               php_stream_filter_append((&entry->cfp->writefilters), filter);
02898               if (SUCCESS != phar_stream_copy_to_stream(file, entry->cfp, entry->uncompressed_filesize, NULL)) {
02899                      if (closeoldfile) {
02900                             php_stream_close(oldfile);
02901                      }
02902                      php_stream_close(newfile);
02903                      if (error) {
02904                             spprintf(error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
02905                      }
02906                      return EOF;
02907               }
02908               php_stream_filter_flush(filter, 1);
02909               php_stream_flush(entry->cfp);
02910               php_stream_filter_remove(filter, 1 TSRMLS_CC);
02911               php_stream_seek(entry->cfp, 0, SEEK_END);
02912               entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
02913               /* generate crc on compressed file */
02914               php_stream_rewind(entry->cfp);
02915               entry->old_flags = entry->flags;
02916               entry->is_modified = 1;
02917               global_flags |= (entry->flags & PHAR_ENT_COMPRESSION_MASK);
02918        }
02919        global_flags |= PHAR_HDR_SIGNATURE;
02920 
02921        /* write out manifest pre-header */
02922        /*  4: manifest length
02923         *  4: manifest entry count
02924         *  2: phar version
02925         *  4: phar global flags
02926         *  4: alias length
02927         *  ?: the alias itself
02928         *  4: phar metadata length
02929         *  ?: phar metadata
02930         */
02931        restore_alias_len = phar->alias_len;
02932        if (phar->is_temporary_alias) {
02933               phar->alias_len = 0;
02934        }
02935 
02936        manifest_len = offset + phar->alias_len + sizeof(manifest) + main_metadata_str.len;
02937        phar_set_32(manifest, manifest_len);
02938        phar_set_32(manifest+4, new_manifest_count);
02939        if (has_dirs) {
02940               *(manifest + 8) = (unsigned char) (((PHAR_API_VERSION) >> 8) & 0xFF);
02941               *(manifest + 9) = (unsigned char) (((PHAR_API_VERSION) & 0xF0));
02942        } else {
02943               *(manifest + 8) = (unsigned char) (((PHAR_API_VERSION_NODIR) >> 8) & 0xFF);
02944               *(manifest + 9) = (unsigned char) (((PHAR_API_VERSION_NODIR) & 0xF0));
02945        }
02946        phar_set_32(manifest+10, global_flags);
02947        phar_set_32(manifest+14, phar->alias_len);
02948 
02949        /* write the manifest header */
02950        if (sizeof(manifest) != php_stream_write(newfile, manifest, sizeof(manifest))
02951        || (size_t)phar->alias_len != php_stream_write(newfile, phar->alias, phar->alias_len)) {
02952 
02953               if (closeoldfile) {
02954                      php_stream_close(oldfile);
02955               }
02956 
02957               php_stream_close(newfile);
02958               phar->alias_len = restore_alias_len;
02959 
02960               if (error) {
02961                      spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", phar->fname);
02962               }
02963 
02964               return EOF;
02965        }
02966 
02967        phar->alias_len = restore_alias_len;
02968 
02969        phar_set_32(manifest, main_metadata_str.len);
02970        if (4 != php_stream_write(newfile, manifest, 4) || (main_metadata_str.len
02971        && main_metadata_str.len != php_stream_write(newfile, main_metadata_str.c, main_metadata_str.len))) {
02972               smart_str_free(&main_metadata_str);
02973 
02974               if (closeoldfile) {
02975                      php_stream_close(oldfile);
02976               }
02977 
02978               php_stream_close(newfile);
02979               phar->alias_len = restore_alias_len;
02980 
02981               if (error) {
02982                      spprintf(error, 0, "unable to write manifest meta-data of new phar \"%s\"", phar->fname);
02983               }
02984 
02985               return EOF;
02986        }
02987        smart_str_free(&main_metadata_str);
02988 
02989        /* re-calculate the manifest location to simplify later code */
02990        manifest_ftell = php_stream_tell(newfile);
02991 
02992        /* now write the manifest */
02993        for (zend_hash_internal_pointer_reset(&phar->manifest);
02994               zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
02995               zend_hash_move_forward(&phar->manifest)) {
02996 
02997               if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) {
02998                      continue;
02999               }
03000 
03001               if (entry->is_deleted || entry->is_mounted) {
03002                      /* remove this from the new phar if deleted, ignore if mounted */
03003                      continue;
03004               }
03005 
03006               if (entry->is_dir) {
03007                      /* add 1 for trailing slash */
03008                      phar_set_32(entry_buffer, entry->filename_len + 1);
03009               } else {
03010                      phar_set_32(entry_buffer, entry->filename_len);
03011               }
03012 
03013               if (4 != php_stream_write(newfile, entry_buffer, 4)
03014               || entry->filename_len != php_stream_write(newfile, entry->filename, entry->filename_len)
03015               || (entry->is_dir && 1 != php_stream_write(newfile, "/", 1))) {
03016                      if (closeoldfile) {
03017                             php_stream_close(oldfile);
03018                      }
03019                      php_stream_close(newfile);
03020                      if (error) {
03021                             if (entry->is_dir) {
03022                                    spprintf(error, 0, "unable to write filename of directory \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
03023                             } else {
03024                                    spprintf(error, 0, "unable to write filename of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
03025                             }
03026                      }
03027                      return EOF;
03028               }
03029 
03030               /* set the manifest meta-data:
03031                      4: uncompressed filesize
03032                      4: creation timestamp
03033                      4: compressed filesize
03034                      4: crc32
03035                      4: flags
03036                      4: metadata-len
03037                      +: metadata
03038               */
03039               mytime = time(NULL);
03040               phar_set_32(entry_buffer, entry->uncompressed_filesize);
03041               phar_set_32(entry_buffer+4, mytime);
03042               phar_set_32(entry_buffer+8, entry->compressed_filesize);
03043               phar_set_32(entry_buffer+12, entry->crc32);
03044               phar_set_32(entry_buffer+16, entry->flags);
03045               phar_set_32(entry_buffer+20, entry->metadata_str.len);
03046 
03047               if (sizeof(entry_buffer) != php_stream_write(newfile, entry_buffer, sizeof(entry_buffer))
03048               || entry->metadata_str.len != php_stream_write(newfile, entry->metadata_str.c, entry->metadata_str.len)) {
03049                      if (closeoldfile) {
03050                             php_stream_close(oldfile);
03051                      }
03052 
03053                      php_stream_close(newfile);
03054 
03055                      if (error) {
03056                             spprintf(error, 0, "unable to write temporary manifest of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
03057                      }
03058 
03059                      return EOF;
03060               }
03061        }
03062 
03063        /* now copy the actual file data to the new phar */
03064        offset = php_stream_tell(newfile);
03065        for (zend_hash_internal_pointer_reset(&phar->manifest);
03066               zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
03067               zend_hash_move_forward(&phar->manifest)) {
03068 
03069               if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) {
03070                      continue;
03071               }
03072 
03073               if (entry->is_deleted || entry->is_dir || entry->is_mounted) {
03074                      continue;
03075               }
03076 
03077               if (entry->cfp) {
03078                      file = entry->cfp;
03079                      php_stream_rewind(file);
03080               } else {
03081                      file = phar_get_efp(entry, 0 TSRMLS_CC);
03082                      if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
03083                             if (closeoldfile) {
03084                                    php_stream_close(oldfile);
03085                             }
03086                             php_stream_close(newfile);
03087                             if (error) {
03088                                    spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
03089                             }
03090                             return EOF;
03091                      }
03092               }
03093 
03094               if (!file) {
03095                      if (closeoldfile) {
03096                             php_stream_close(oldfile);
03097                      }
03098                      php_stream_close(newfile);
03099                      if (error) {
03100                             spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
03101                      }
03102                      return EOF;
03103               }
03104 
03105               /* this will have changed for all files that have either changed compression or been modified */
03106               entry->offset = entry->offset_abs = offset;
03107               offset += entry->compressed_filesize;
03108               if (phar_stream_copy_to_stream(file, newfile, entry->compressed_filesize, &wrote) == FAILURE) {
03109                      if (closeoldfile) {
03110                             php_stream_close(oldfile);
03111                      }
03112 
03113                      php_stream_close(newfile);
03114 
03115                      if (error) {
03116                             spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
03117                      }
03118 
03119                      return EOF;
03120               }
03121 
03122               entry->is_modified = 0;
03123 
03124               if (entry->cfp) {
03125                      php_stream_close(entry->cfp);
03126                      entry->cfp = NULL;
03127               }
03128 
03129               if (entry->fp_type == PHAR_MOD) {
03130                      /* this fp is in use by a phar_entry_data returned by phar_get_entry_data, it will be closed when the phar_entry_data is phar_entry_delref'ed */
03131                      if (entry->fp_refcount == 0 && entry->fp != phar->fp && entry->fp != phar->ufp) {
03132                             php_stream_close(entry->fp);
03133                      }
03134 
03135                      entry->fp = NULL;
03136                      entry->fp_type = PHAR_FP;
03137               } else if (entry->fp_type == PHAR_UFP) {
03138                      entry->fp_type = PHAR_FP;
03139               }
03140        }
03141 
03142        /* append signature */
03143        if (global_flags & PHAR_HDR_SIGNATURE) {
03144               char sig_buf[4];
03145 
03146               php_stream_rewind(newfile);
03147 
03148               if (phar->signature) {
03149                      efree(phar->signature);
03150                      phar->signature = NULL;
03151               }
03152 
03153               switch(phar->sig_flags) {
03154 #ifndef PHAR_HASH_OK
03155                      case PHAR_SIG_SHA512:
03156                      case PHAR_SIG_SHA256:
03157                             if (closeoldfile) {
03158                                    php_stream_close(oldfile);
03159                             }
03160                             php_stream_close(newfile);
03161                             if (error) {
03162                                    spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\" with requested hash type", entry->filename, phar->fname);
03163                             }
03164                             return EOF;
03165 #endif
03166                      default: {
03167                             char *digest = NULL;
03168                             int digest_len;
03169 
03170                             if (FAILURE == phar_create_signature(phar, newfile, &digest, &digest_len, error TSRMLS_CC)) {
03171                                    if (error) {
03172                                           char *save = *error;
03173                                           spprintf(error, 0, "phar error: unable to write signature: %s", save);
03174                                           efree(save);
03175                                    }
03176                                    if (digest) {
03177                                           efree(digest);
03178                                    }
03179                                    if (closeoldfile) {
03180                                           php_stream_close(oldfile);
03181                                    }
03182                                    php_stream_close(newfile);
03183                                    return EOF;
03184                             }
03185 
03186                             php_stream_write(newfile, digest, digest_len);
03187                             efree(digest);
03188                             if (phar->sig_flags == PHAR_SIG_OPENSSL) {
03189                                    phar_set_32(sig_buf, digest_len);
03190                                    php_stream_write(newfile, sig_buf, 4);
03191                             }
03192                             break;
03193                      }
03194               }
03195               phar_set_32(sig_buf, phar->sig_flags);
03196               php_stream_write(newfile, sig_buf, 4);
03197               php_stream_write(newfile, "GBMB", 4);
03198        }
03199 
03200        /* finally, close the temp file, rename the original phar,
03201           move the temp to the old phar, unlink the old phar, and reload it into memory
03202        */
03203        if (phar->fp && free_fp) {
03204               php_stream_close(phar->fp);
03205        }
03206 
03207        if (phar->ufp) {
03208               if (free_ufp) {
03209                      php_stream_close(phar->ufp);
03210               }
03211               phar->ufp = NULL;
03212        }
03213 
03214        if (closeoldfile) {
03215               php_stream_close(oldfile);
03216        }
03217 
03218        phar->internal_file_start = halt_offset + manifest_len + 4;
03219        phar->halt_offset = halt_offset;
03220        phar->is_brandnew = 0;
03221 
03222        php_stream_rewind(newfile);
03223 
03224        if (phar->donotflush) {
03225               /* deferred flush */
03226               phar->fp = newfile;
03227        } else {
03228               phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
03229               if (!phar->fp) {
03230                      phar->fp = newfile;
03231                      if (error) {
03232                             spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
03233                      }
03234                      return EOF;
03235               }
03236 
03237               if (phar->flags & PHAR_FILE_COMPRESSED_GZ) {
03238                      /* to properly compress, we have to tell zlib to add a zlib header */
03239                      zval filterparams;
03240 
03241                      array_init(&filterparams);
03242                      add_assoc_long(&filterparams, "window", MAX_WBITS+16);
03243                      filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp) TSRMLS_CC);
03244                      zval_dtor(&filterparams);
03245 
03246                      if (!filter) {
03247                             if (error) {
03248                                    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);
03249                             }
03250                             return EOF;
03251                      }
03252 
03253                      php_stream_filter_append(&phar->fp->writefilters, filter);
03254                      phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
03255                      php_stream_filter_flush(filter, 1);
03256                      php_stream_filter_remove(filter, 1 TSRMLS_CC);
03257                      php_stream_close(phar->fp);
03258                      /* use the temp stream as our base */
03259                      phar->fp = newfile;
03260               } else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) {
03261                      filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp) TSRMLS_CC);
03262                      php_stream_filter_append(&phar->fp->writefilters, filter);
03263                      phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
03264                      php_stream_filter_flush(filter, 1);
03265                      php_stream_filter_remove(filter, 1 TSRMLS_CC);
03266                      php_stream_close(phar->fp);
03267                      /* use the temp stream as our base */
03268                      phar->fp = newfile;
03269               } else {
03270                      phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
03271                      /* we could also reopen the file in "rb" mode but there is no need for that */
03272                      php_stream_close(newfile);
03273               }
03274        }
03275 
03276        if (-1 == php_stream_seek(phar->fp, phar->halt_offset, SEEK_SET)) {
03277               if (error) {
03278                      spprintf(error, 0, "unable to seek to __HALT_COMPILER(); in new phar \"%s\"", phar->fname);
03279               }
03280               return EOF;
03281        }
03282 
03283        return EOF;
03284 }
03285 /* }}} */
03286 
03287 #ifdef COMPILE_DL_PHAR
03288 ZEND_GET_MODULE(phar)
03289 #endif
03290 
03291 /* {{{ phar_functions[]
03292  *
03293  * Every user visible function must have an entry in phar_functions[].
03294  */
03295 zend_function_entry phar_functions[] = {
03296        PHP_FE_END
03297 };
03298 /* }}}*/
03299 
03300 static size_t phar_zend_stream_reader(void *handle, char *buf, size_t len TSRMLS_DC) /* {{{ */
03301 {
03302        return php_stream_read(phar_get_pharfp((phar_archive_data*)handle TSRMLS_CC), buf, len);
03303 }
03304 /* }}} */
03305 
03306 #if PHP_VERSION_ID >= 50300
03307 static size_t phar_zend_stream_fsizer(void *handle TSRMLS_DC) /* {{{ */
03308 {
03309        return ((phar_archive_data*)handle)->halt_offset + 32;
03310 } /* }}} */
03311 
03312 #else /* PHP_VERSION_ID */
03313 
03314 static long phar_stream_fteller_for_zend(void *handle TSRMLS_DC) /* {{{ */
03315 {
03316        return (long)php_stream_tell(phar_get_pharfp((phar_archive_data*)handle TSRMLS_CC));
03317 }
03318 /* }}} */
03319 #endif
03320 
03321 zend_op_array *(*phar_orig_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);
03322 #if PHP_VERSION_ID >= 50300
03323 #define phar_orig_zend_open zend_stream_open_function
03324 static char *phar_resolve_path(const char *filename, int filename_len TSRMLS_DC)
03325 {
03326        return phar_find_in_include_path((char *) filename, filename_len, NULL TSRMLS_CC);
03327 }
03328 #else
03329 int (*phar_orig_zend_open)(const char *filename, zend_file_handle *handle TSRMLS_DC);
03330 #endif
03331 
03332 static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type TSRMLS_DC) /* {{{ */
03333 {
03334        zend_op_array *res;
03335        char *name = NULL;
03336        int failed;
03337        phar_archive_data *phar;
03338 
03339        if (!file_handle || !file_handle->filename) {
03340               return phar_orig_compile_file(file_handle, type TSRMLS_CC);
03341        }
03342        if (strstr(file_handle->filename, ".phar") && !strstr(file_handle->filename, "://")) {
03343               if (SUCCESS == phar_open_from_filename(file_handle->filename, strlen(file_handle->filename), NULL, 0, 0, &phar, NULL TSRMLS_CC)) {
03344                      if (phar->is_zip || phar->is_tar) {
03345                             zend_file_handle f = *file_handle;
03346 
03347                             /* zip or tar-based phar */
03348                             spprintf(&name, 4096, "phar://%s/%s", file_handle->filename, ".phar/stub.php");
03349                             if (SUCCESS == phar_orig_zend_open((const char *)name, file_handle TSRMLS_CC)) {
03350                                    efree(name);
03351                                    name = NULL;
03352                                    file_handle->filename = f.filename;
03353                                    if (file_handle->opened_path) {
03354                                           efree(file_handle->opened_path);
03355                                    }
03356                                    file_handle->opened_path = f.opened_path;
03357                                    file_handle->free_filename = f.free_filename;
03358                             } else {
03359                                    *file_handle = f;
03360                             }
03361                      } else if (phar->flags & PHAR_FILE_COMPRESSION_MASK) {
03362                             /* compressed phar */
03363 #if PHP_VERSION_ID >= 50300
03364                             file_handle->type = ZEND_HANDLE_STREAM;
03365                             /* we do our own reading directly from the phar, don't change the next line */
03366                             file_handle->handle.stream.handle  = phar;
03367                             file_handle->handle.stream.reader  = phar_zend_stream_reader;
03368                             file_handle->handle.stream.closer  = NULL;
03369                             file_handle->handle.stream.fsizer  = phar_zend_stream_fsizer;
03370                             file_handle->handle.stream.isatty  = 0;
03371                             phar->is_persistent ?
03372                                    php_stream_rewind(PHAR_GLOBALS->cached_fp[phar->phar_pos].fp) :
03373                                    php_stream_rewind(phar->fp);
03374                             memset(&file_handle->handle.stream.mmap, 0, sizeof(file_handle->handle.stream.mmap));
03375 #else /* PHP_VERSION_ID */
03376                             file_handle->type = ZEND_HANDLE_STREAM;
03377                             /* we do our own reading directly from the phar, don't change the next line */
03378                             file_handle->handle.stream.handle = phar;
03379                             file_handle->handle.stream.reader = phar_zend_stream_reader;
03380                             file_handle->handle.stream.closer = NULL; /* don't close - let phar handle this one */
03381                             file_handle->handle.stream.fteller = phar_stream_fteller_for_zend;
03382                             file_handle->handle.stream.interactive = 0;
03383                             phar->is_persistent ?
03384                                    php_stream_rewind(PHAR_GLOBALS->cached_fp[phar->phar_pos].fp) :
03385                                    php_stream_rewind(phar->fp);
03386 #endif
03387                      }
03388               }
03389        }
03390 
03391        zend_try {
03392               failed = 0;
03393               res = phar_orig_compile_file(file_handle, type TSRMLS_CC);
03394        } zend_catch {
03395               failed = 1;
03396               res = NULL;
03397        } zend_end_try();
03398 
03399        if (name) {
03400               efree(name);
03401        }
03402 
03403        if (failed) {
03404               zend_bailout();
03405        }
03406 
03407        return res;
03408 }
03409 /* }}} */
03410 
03411 #if PHP_VERSION_ID < 50300
03412 int phar_zend_open(const char *filename, zend_file_handle *handle TSRMLS_DC) /* {{{ */
03413 {
03414        char *arch, *entry;
03415        int arch_len, entry_len;
03416 
03417        /* this code is obsoleted in php 5.3 */
03418        entry = (char *) filename;
03419        if (!IS_ABSOLUTE_PATH(entry, strlen(entry)) && !strstr(entry, "://")) {
03420               phar_archive_data **pphar = NULL;
03421               char *fname;
03422               int fname_len;
03423 
03424               fname = zend_get_executed_filename(TSRMLS_C);
03425               fname_len = strlen(fname);
03426 
03427               if (fname_len > 7 && !strncasecmp(fname, "phar://", 7)) {
03428                      if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 1, 0 TSRMLS_CC)) {
03429                             zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar);
03430                             if (!pphar && PHAR_G(manifest_cached)) {
03431                                    zend_hash_find(&cached_phars, arch, arch_len, (void **) &pphar);
03432                             }
03433                             efree(arch);
03434                             efree(entry);
03435                      }
03436               }
03437 
03438               /* retrieving an include within the current directory, so use this if possible */
03439               if (!(entry = phar_find_in_include_path((char *) filename, strlen(filename), NULL TSRMLS_CC))) {
03440                      /* this file is not in the phar, use the original path */
03441                      goto skip_phar;
03442               }
03443 
03444               if (SUCCESS == phar_orig_zend_open(entry, handle TSRMLS_CC)) {
03445                      if (!handle->opened_path) {
03446                             handle->opened_path = entry;
03447                      }
03448                      if (entry != filename) {
03449                             handle->free_filename = 1;
03450                      }
03451                      return SUCCESS;
03452               }
03453 
03454               if (entry != filename) {
03455                      efree(entry);
03456               }
03457 
03458               return FAILURE;
03459        }
03460 skip_phar:
03461        return phar_orig_zend_open(filename, handle TSRMLS_CC);
03462 }
03463 /* }}} */
03464 #endif
03465 typedef zend_op_array* (zend_compile_t)(zend_file_handle*, int TSRMLS_DC);
03466 typedef zend_compile_t* (compile_hook)(zend_compile_t *ptr);
03467 
03468 PHP_GINIT_FUNCTION(phar) /* {{{ */
03469 {
03470        phar_mime_type mime;
03471 
03472        memset(phar_globals, 0, sizeof(zend_phar_globals));
03473        phar_globals->readonly = 1;
03474 
03475        zend_hash_init(&phar_globals->mime_types, 0, NULL, NULL, 1);
03476 
03477 #define PHAR_SET_MIME(mimetype, ret, fileext) \
03478               mime.mime = mimetype; \
03479               mime.len = sizeof((mimetype))+1; \
03480               mime.type = ret; \
03481               zend_hash_add(&phar_globals->mime_types, fileext, sizeof(fileext)-1, (void *)&mime, sizeof(phar_mime_type), NULL); \
03482 
03483        PHAR_SET_MIME("text/html", PHAR_MIME_PHPS, "phps")
03484        PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c")
03485        PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cc")
03486        PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cpp")
03487        PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c++")
03488        PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "dtd")
03489        PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "h")
03490        PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "log")
03491        PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "rng")
03492        PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "txt")
03493        PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "xsd")
03494        PHAR_SET_MIME("", PHAR_MIME_PHP, "php")
03495        PHAR_SET_MIME("", PHAR_MIME_PHP, "inc")
03496        PHAR_SET_MIME("video/avi", PHAR_MIME_OTHER, "avi")
03497        PHAR_SET_MIME("image/bmp", PHAR_MIME_OTHER, "bmp")
03498        PHAR_SET_MIME("text/css", PHAR_MIME_OTHER, "css")
03499        PHAR_SET_MIME("image/gif", PHAR_MIME_OTHER, "gif")
03500        PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htm")
03501        PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "html")
03502        PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htmls")
03503        PHAR_SET_MIME("image/x-ico", PHAR_MIME_OTHER, "ico")
03504        PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpe")
03505        PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpg")
03506        PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpeg")
03507        PHAR_SET_MIME("application/x-javascript", PHAR_MIME_OTHER, "js")
03508        PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "midi")
03509        PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "mid")
03510        PHAR_SET_MIME("audio/mod", PHAR_MIME_OTHER, "mod")
03511        PHAR_SET_MIME("movie/quicktime", PHAR_MIME_OTHER, "mov")
03512        PHAR_SET_MIME("audio/mp3", PHAR_MIME_OTHER, "mp3")
03513        PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpg")
03514        PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpeg")
03515        PHAR_SET_MIME("application/pdf", PHAR_MIME_OTHER, "pdf")
03516        PHAR_SET_MIME("image/png", PHAR_MIME_OTHER, "png")
03517        PHAR_SET_MIME("application/shockwave-flash", PHAR_MIME_OTHER, "swf")
03518        PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tif")
03519        PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tiff")
03520        PHAR_SET_MIME("audio/wav", PHAR_MIME_OTHER, "wav")
03521        PHAR_SET_MIME("image/xbm", PHAR_MIME_OTHER, "xbm")
03522        PHAR_SET_MIME("text/xml", PHAR_MIME_OTHER, "xml")
03523 
03524        phar_restore_orig_functions(TSRMLS_C);
03525 }
03526 /* }}} */
03527 
03528 PHP_GSHUTDOWN_FUNCTION(phar) /* {{{ */
03529 {
03530        zend_hash_destroy(&phar_globals->mime_types);
03531 }
03532 /* }}} */
03533 
03534 PHP_MINIT_FUNCTION(phar) /* {{{ */
03535 {
03536        REGISTER_INI_ENTRIES();
03537 
03538        phar_orig_compile_file = zend_compile_file;
03539        zend_compile_file = phar_compile_file;
03540 
03541 #if PHP_VERSION_ID >= 50300
03542        phar_save_resolve_path = zend_resolve_path;
03543        zend_resolve_path = phar_resolve_path;
03544 #else
03545        phar_orig_zend_open = zend_stream_open_function;
03546        zend_stream_open_function = phar_zend_open;
03547 #endif
03548 
03549        phar_object_init(TSRMLS_C);
03550 
03551        phar_intercept_functions_init(TSRMLS_C);
03552        phar_save_orig_functions(TSRMLS_C);
03553 
03554        return php_register_url_stream_wrapper("phar", &php_stream_phar_wrapper TSRMLS_CC);
03555 }
03556 /* }}} */
03557 
03558 PHP_MSHUTDOWN_FUNCTION(phar) /* {{{ */
03559 {
03560        php_unregister_url_stream_wrapper("phar" TSRMLS_CC);
03561 
03562        phar_intercept_functions_shutdown(TSRMLS_C);
03563 
03564        if (zend_compile_file == phar_compile_file) {
03565               zend_compile_file = phar_orig_compile_file;
03566        }
03567 
03568 #if PHP_VERSION_ID < 50300
03569        if (zend_stream_open_function == phar_zend_open) {
03570               zend_stream_open_function = phar_orig_zend_open;
03571        }
03572 #endif
03573        if (PHAR_G(manifest_cached)) {
03574               zend_hash_destroy(&(cached_phars));
03575               zend_hash_destroy(&(cached_alias));
03576        }
03577 
03578        return SUCCESS;
03579 }
03580 /* }}} */
03581 
03582 void phar_request_initialize(TSRMLS_D) /* {{{ */
03583 {
03584        if (!PHAR_GLOBALS->request_init)
03585        {
03586               PHAR_G(last_phar) = NULL;
03587               PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
03588               PHAR_G(has_bz2) = zend_hash_exists(&module_registry, "bz2", sizeof("bz2"));
03589               PHAR_G(has_zlib) = zend_hash_exists(&module_registry, "zlib", sizeof("zlib"));
03590               PHAR_GLOBALS->request_init = 1;
03591               PHAR_GLOBALS->request_ends = 0;
03592               PHAR_GLOBALS->request_done = 0;
03593               zend_hash_init(&(PHAR_GLOBALS->phar_fname_map), 5, zend_get_hash_value, destroy_phar_data,  0);
03594               zend_hash_init(&(PHAR_GLOBALS->phar_persist_map), 5, zend_get_hash_value, NULL,  0);
03595               zend_hash_init(&(PHAR_GLOBALS->phar_alias_map), 5, zend_get_hash_value, NULL, 0);
03596 
03597               if (PHAR_G(manifest_cached)) {
03598                      phar_archive_data **pphar;
03599                      phar_entry_fp *stuff = (phar_entry_fp *) ecalloc(zend_hash_num_elements(&cached_phars), sizeof(phar_entry_fp));
03600 
03601                      for (zend_hash_internal_pointer_reset(&cached_phars);
03602                      zend_hash_get_current_data(&cached_phars, (void **)&pphar) == SUCCESS;
03603                      zend_hash_move_forward(&cached_phars)) {
03604                             stuff[pphar[0]->phar_pos].manifest = (phar_entry_fp_info *) ecalloc( zend_hash_num_elements(&(pphar[0]->manifest)), sizeof(phar_entry_fp_info));
03605                      }
03606 
03607                      PHAR_GLOBALS->cached_fp = stuff;
03608               }
03609 
03610               PHAR_GLOBALS->phar_SERVER_mung_list = 0;
03611               PHAR_G(cwd) = NULL;
03612               PHAR_G(cwd_len) = 0;
03613               PHAR_G(cwd_init) = 0;
03614        }
03615 }
03616 /* }}} */
03617 
03618 PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */
03619 {
03620        int i;
03621 
03622        PHAR_GLOBALS->request_ends = 1;
03623 
03624        if (PHAR_GLOBALS->request_init)
03625        {
03626               phar_release_functions(TSRMLS_C);
03627               zend_hash_destroy(&(PHAR_GLOBALS->phar_alias_map));
03628               PHAR_GLOBALS->phar_alias_map.arBuckets = NULL;
03629               zend_hash_destroy(&(PHAR_GLOBALS->phar_fname_map));
03630               PHAR_GLOBALS->phar_fname_map.arBuckets = NULL;
03631               zend_hash_destroy(&(PHAR_GLOBALS->phar_persist_map));
03632               PHAR_GLOBALS->phar_persist_map.arBuckets = NULL;
03633               PHAR_GLOBALS->phar_SERVER_mung_list = 0;
03634 
03635               if (PHAR_GLOBALS->cached_fp) {
03636                      for (i = 0; i < zend_hash_num_elements(&cached_phars); ++i) {
03637                             if (PHAR_GLOBALS->cached_fp[i].fp) {
03638                                    php_stream_close(PHAR_GLOBALS->cached_fp[i].fp);
03639                             }
03640                             if (PHAR_GLOBALS->cached_fp[i].ufp) {
03641                                    php_stream_close(PHAR_GLOBALS->cached_fp[i].ufp);
03642                             }
03643                             efree(PHAR_GLOBALS->cached_fp[i].manifest);
03644                      }
03645                      efree(PHAR_GLOBALS->cached_fp);
03646                      PHAR_GLOBALS->cached_fp = 0;
03647               }
03648 
03649               PHAR_GLOBALS->request_init = 0;
03650 
03651               if (PHAR_G(cwd)) {
03652                      efree(PHAR_G(cwd));
03653               }
03654 
03655               PHAR_G(cwd) = NULL;
03656               PHAR_G(cwd_len) = 0;
03657               PHAR_G(cwd_init) = 0;
03658        }
03659 
03660        PHAR_GLOBALS->request_done = 1;
03661        return SUCCESS;
03662 }
03663 /* }}} */
03664 
03665 PHP_MINFO_FUNCTION(phar) /* {{{ */
03666 {
03667        phar_request_initialize(TSRMLS_C);
03668        php_info_print_table_start();
03669        php_info_print_table_header(2, "Phar: PHP Archive support", "enabled");
03670        php_info_print_table_row(2, "Phar EXT version", PHP_PHAR_VERSION);
03671        php_info_print_table_row(2, "Phar API version", PHP_PHAR_API_VERSION);
03672        php_info_print_table_row(2, "SVN revision", "$Revision: 321634 $");
03673        php_info_print_table_row(2, "Phar-based phar archives", "enabled");
03674        php_info_print_table_row(2, "Tar-based phar archives", "enabled");
03675        php_info_print_table_row(2, "ZIP-based phar archives", "enabled");
03676 
03677        if (PHAR_G(has_zlib)) {
03678               php_info_print_table_row(2, "gzip compression", "enabled");
03679        } else {
03680               php_info_print_table_row(2, "gzip compression", "disabled (install ext/zlib)");
03681        }
03682 
03683        if (PHAR_G(has_bz2)) {
03684               php_info_print_table_row(2, "bzip2 compression", "enabled");
03685        } else {
03686               php_info_print_table_row(2, "bzip2 compression", "disabled (install pecl/bz2)");
03687        }
03688 #ifdef PHAR_HAVE_OPENSSL
03689        php_info_print_table_row(2, "Native OpenSSL support", "enabled");
03690 #else
03691        if (zend_hash_exists(&module_registry, "openssl", sizeof("openssl"))) {
03692               php_info_print_table_row(2, "OpenSSL support", "enabled");
03693        } else {
03694               php_info_print_table_row(2, "OpenSSL support", "disabled (install ext/openssl)");
03695        }
03696 #endif
03697        php_info_print_table_end();
03698 
03699        php_info_print_box_start(0);
03700        PUTS("Phar based on pear/PHP_Archive, original concept by Davey Shafik.");
03701        PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
03702        PUTS("Phar fully realized by Gregory Beaver and Marcus Boerger.");
03703        PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
03704        PUTS("Portions of tar implementation Copyright (c) 2003-2009 Tim Kientzle.");
03705        php_info_print_box_end();
03706 
03707        DISPLAY_INI_ENTRIES();
03708 }
03709 /* }}} */
03710 
03711 /* {{{ phar_module_entry
03712  */
03713 static const zend_module_dep phar_deps[] = {
03714        ZEND_MOD_OPTIONAL("apc")
03715        ZEND_MOD_OPTIONAL("bz2")
03716        ZEND_MOD_OPTIONAL("openssl")
03717        ZEND_MOD_OPTIONAL("zlib")
03718        ZEND_MOD_OPTIONAL("standard")
03719 #if defined(HAVE_HASH) && !defined(COMPILE_DL_HASH)
03720        ZEND_MOD_REQUIRED("hash")
03721 #endif
03722 #if HAVE_SPL
03723        ZEND_MOD_REQUIRED("spl")
03724 #endif
03725        ZEND_MOD_END
03726 };
03727 
03728 zend_module_entry phar_module_entry = {
03729        STANDARD_MODULE_HEADER_EX, NULL,
03730        phar_deps,
03731        "Phar",
03732        phar_functions,
03733        PHP_MINIT(phar),
03734        PHP_MSHUTDOWN(phar),
03735        NULL,
03736        PHP_RSHUTDOWN(phar),
03737        PHP_MINFO(phar),
03738        PHP_PHAR_VERSION,
03739        PHP_MODULE_GLOBALS(phar),   /* globals descriptor */
03740        PHP_GINIT(phar),            /* globals ctor */
03741        PHP_GSHUTDOWN(phar),        /* globals dtor */
03742        NULL,                       /* post deactivate */
03743        STANDARD_MODULE_PROPERTIES_EX
03744 };
03745 /* }}} */
03746 
03747 /*
03748  * Local variables:
03749  * tab-width: 4
03750  * c-basic-offset: 4
03751  * End:
03752  * vim600: noet sw=4 ts=4 fdm=marker
03753  * vim<600: noet sw=4 ts=4
03754  */