root/ext/phar/phar.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ZEND_DECLARE_MODULE_GLOBALS
  2. ZEND_INI_MH
  3. phar_split_cache_list
  4. ZEND_INI_MH
  5. PHP_INI_BEGIN
  6. phar_archive_delref
  7. destroy_phar_data_only
  8. phar_unalias_apply
  9. phar_tmpclose_apply
  10. destroy_phar_data
  11. destroy_phar_manifest_entry
  12. phar_entry_delref
  13. phar_entry_remove
  14. phar_open_parsed_phar
  15. phar_parse_metadata
  16. phar_parse_pharfile
  17. phar_open_or_create_filename
  18. phar_create_or_parse_filename
  19. phar_open_from_filename
  20. phar_strnstr
  21. phar_open_from_fp
  22. phar_analyze_path
  23. phar_check_str
  24. phar_detect_phar_fname_ext
  25. php_check_dots
  26. in_character_class
  27. tsrm_strtok_r
  28. phar_fix_filepath
  29. phar_split_fname
  30. phar_open_executed_filename
  31. phar_postprocess_file
  32. phar_set_32
  33. phar_flush_clean_deleted_apply
  34. phar_create_default_stub
  35. phar_flush
  36. phar_zend_stream_reader
  37. phar_zend_stream_fsizer
  38. phar_resolve_path
  39. phar_compile_file
  40. PHP_GINIT_FUNCTION
  41. PHP_GSHUTDOWN_FUNCTION
  42. PHP_MINIT_FUNCTION
  43. PHP_MSHUTDOWN_FUNCTION
  44. phar_request_initialize
  45. PHP_RSHUTDOWN_FUNCTION
  46. PHP_MINFO_FUNCTION

   1 /*
   2   +----------------------------------------------------------------------+
   3   | phar php single-file executable PHP extension                        |
   4   +----------------------------------------------------------------------+
   5   | Copyright (c) 2005-2016 The PHP Group                                |
   6   +----------------------------------------------------------------------+
   7   | This source file is subject to version 3.01 of the PHP license,      |
   8   | that is bundled with this package in the file LICENSE, and is        |
   9   | available through the world-wide-web at the following url:           |
  10   | http://www.php.net/license/3_01.txt.                                 |
  11   | If you did not receive a copy of the PHP license and are unable to   |
  12   | obtain it through the world-wide-web, please send a note to          |
  13   | license@php.net so we can mail you a copy immediately.               |
  14   +----------------------------------------------------------------------+
  15   | Authors: Gregory Beaver <cellog@php.net>                             |
  16   |          Marcus Boerger <helly@php.net>                              |
  17   +----------------------------------------------------------------------+
  18 */
  19 
  20 /* $Id: 14b80e175ed4d5bf11c890e1f30ea8225935b16c $ */
  21 
  22 #define PHAR_MAIN 1
  23 #include "phar_internal.h"
  24 #include "SAPI.h"
  25 #include "func_interceptors.h"
  26 
  27 static void destroy_phar_data(void *pDest);
  28 
  29 ZEND_DECLARE_MODULE_GLOBALS(phar)
  30 char *(*phar_save_resolve_path)(const char *filename, int filename_len TSRMLS_DC);
  31 
  32 /**
  33  * set's phar->is_writeable based on the current INI value
  34  */
  35 static int phar_set_writeable_bit(void *pDest, void *argument TSRMLS_DC) /* {{{ */
  36 {
  37         zend_bool keep = *(zend_bool *)argument;
  38         phar_archive_data *phar = *(phar_archive_data **)pDest;
  39 
  40         if (!phar->is_data) {
  41                 phar->is_writeable = !keep;
  42         }
  43 
  44         return ZEND_HASH_APPLY_KEEP;
  45 }
  46 /* }}} */
  47 
  48 /* if the original value is 0 (disabled), then allow setting/unsetting at will. Otherwise only allow 1 (enabled), and error on disabling */
  49 ZEND_INI_MH(phar_ini_modify_handler) /* {{{ */
  50 {
  51         zend_bool old, ini;
  52 
  53         if (entry->name_length == 14) {
  54                 old = PHAR_G(readonly_orig);
  55         } else {
  56                 old = PHAR_G(require_hash_orig);
  57         }
  58 
  59         if (new_value_length == 2 && !strcasecmp("on", new_value)) {
  60                 ini = (zend_bool) 1;
  61         }
  62         else if (new_value_length == 3 && !strcasecmp("yes", new_value)) {
  63                 ini = (zend_bool) 1;
  64         }
  65         else if (new_value_length == 4 && !strcasecmp("true", new_value)) {
  66                 ini = (zend_bool) 1;
  67         }
  68         else {
  69                 ini = (zend_bool) atoi(new_value);
  70         }
  71 
  72         /* do not allow unsetting in runtime */
  73         if (stage == ZEND_INI_STAGE_STARTUP) {
  74                 if (entry->name_length == 14) {
  75                         PHAR_G(readonly_orig) = ini;
  76                 } else {
  77                         PHAR_G(require_hash_orig) = ini;
  78                 }
  79         } else if (old && !ini) {
  80                 return FAILURE;
  81         }
  82 
  83         if (entry->name_length == 14) {
  84                 PHAR_G(readonly) = ini;
  85                 if (PHAR_GLOBALS->request_init && PHAR_GLOBALS->phar_fname_map.arBuckets) {
  86                         zend_hash_apply_with_argument(&(PHAR_GLOBALS->phar_fname_map), phar_set_writeable_bit, (void *)&ini TSRMLS_CC);
  87                 }
  88         } else {
  89                 PHAR_G(require_hash) = ini;
  90         }
  91 
  92         return SUCCESS;
  93 }
  94 /* }}}*/
  95 
  96 /* this global stores the global cached pre-parsed manifests */
  97 HashTable cached_phars;
  98 HashTable cached_alias;
  99 
 100 static void phar_split_cache_list(TSRMLS_D) /* {{{ */
 101 {
 102         char *tmp;
 103         char *key, *lasts, *end;
 104         char ds[2];
 105         phar_archive_data *phar;
 106         uint i = 0;
 107 
 108         if (!PHAR_GLOBALS->cache_list || !(PHAR_GLOBALS->cache_list[0])) {
 109                 return;
 110         }
 111 
 112         ds[0] = DEFAULT_DIR_SEPARATOR;
 113         ds[1] = '\0';
 114         tmp = estrdup(PHAR_GLOBALS->cache_list);
 115 
 116         /* fake request startup */
 117         PHAR_GLOBALS->request_init = 1;
 118         if (zend_hash_init(&EG(regular_list), 0, NULL, NULL, 0) == SUCCESS) {
 119                 EG(regular_list).nNextFreeElement=1;    /* we don't want resource id 0 */
 120         }
 121 
 122         PHAR_G(has_bz2) = zend_hash_exists(&module_registry, "bz2", sizeof("bz2"));
 123         PHAR_G(has_zlib) = zend_hash_exists(&module_registry, "zlib", sizeof("zlib"));
 124         /* these two are dummies and will be destroyed later */
 125         zend_hash_init(&cached_phars, sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data,  1);
 126         zend_hash_init(&cached_alias, sizeof(phar_archive_data*), zend_get_hash_value, NULL, 1);
 127         /* these two are real and will be copied over cached_phars/cached_alias later */
 128         zend_hash_init(&(PHAR_GLOBALS->phar_fname_map), sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data,  1);
 129         zend_hash_init(&(PHAR_GLOBALS->phar_alias_map), sizeof(phar_archive_data*), zend_get_hash_value, NULL, 1);
 130         PHAR_GLOBALS->manifest_cached = 1;
 131         PHAR_GLOBALS->persist = 1;
 132 
 133         for (key = php_strtok_r(tmp, ds, &lasts);
 134                         key;
 135                         key = php_strtok_r(NULL, ds, &lasts)) {
 136                 end = strchr(key, DEFAULT_DIR_SEPARATOR);
 137 
 138                 if (end) {
 139                         if (SUCCESS == phar_open_from_filename(key, end - key, NULL, 0, 0, &phar, NULL TSRMLS_CC)) {
 140 finish_up:
 141                                 phar->phar_pos = i++;
 142                                 php_stream_close(phar->fp);
 143                                 phar->fp = NULL;
 144                         } else {
 145 finish_error:
 146                                 PHAR_GLOBALS->persist = 0;
 147                                 PHAR_GLOBALS->manifest_cached = 0;
 148                                 efree(tmp);
 149                                 zend_hash_destroy(&(PHAR_G(phar_fname_map)));
 150                                 PHAR_GLOBALS->phar_fname_map.arBuckets = 0;
 151                                 zend_hash_destroy(&(PHAR_G(phar_alias_map)));
 152                                 PHAR_GLOBALS->phar_alias_map.arBuckets = 0;
 153                                 zend_hash_destroy(&cached_phars);
 154                                 zend_hash_destroy(&cached_alias);
 155                                 zend_hash_graceful_reverse_destroy(&EG(regular_list));
 156                                 memset(&EG(regular_list), 0, sizeof(HashTable));
 157                                 /* free cached manifests */
 158                                 PHAR_GLOBALS->request_init = 0;
 159                                 return;
 160                         }
 161                 } else {
 162                         if (SUCCESS == phar_open_from_filename(key, strlen(key), NULL, 0, 0, &phar, NULL TSRMLS_CC)) {
 163                                 goto finish_up;
 164                         } else {
 165                                 goto finish_error;
 166                         }
 167                 }
 168         }
 169 
 170         PHAR_GLOBALS->persist = 0;
 171         PHAR_GLOBALS->request_init = 0;
 172         /* destroy dummy values from before */
 173         zend_hash_destroy(&cached_phars);
 174         zend_hash_destroy(&cached_alias);
 175         cached_phars = PHAR_GLOBALS->phar_fname_map;
 176         cached_alias = PHAR_GLOBALS->phar_alias_map;
 177         PHAR_GLOBALS->phar_fname_map.arBuckets = 0;
 178         PHAR_GLOBALS->phar_alias_map.arBuckets = 0;
 179         zend_hash_graceful_reverse_destroy(&EG(regular_list));
 180         memset(&EG(regular_list), 0, sizeof(HashTable));
 181         efree(tmp);
 182 }
 183 /* }}} */
 184 
 185 ZEND_INI_MH(phar_ini_cache_list) /* {{{ */
 186 {
 187         PHAR_G(cache_list) = new_value;
 188 
 189         if (stage == ZEND_INI_STAGE_STARTUP) {
 190                 phar_split_cache_list(TSRMLS_C);
 191         }
 192 
 193         return SUCCESS;
 194 }
 195 /* }}} */
 196 
 197 PHP_INI_BEGIN()
 198         STD_PHP_INI_BOOLEAN("phar.readonly", "1", PHP_INI_ALL, phar_ini_modify_handler, readonly, zend_phar_globals, phar_globals)
 199         STD_PHP_INI_BOOLEAN("phar.require_hash", "1", PHP_INI_ALL, phar_ini_modify_handler, require_hash, zend_phar_globals, phar_globals)
 200         STD_PHP_INI_ENTRY("phar.cache_list", "", PHP_INI_SYSTEM, phar_ini_cache_list, cache_list, zend_phar_globals, phar_globals)
 201 PHP_INI_END()
 202 
 203 /**
 204  * When all uses of a phar have been concluded, this frees the manifest
 205  * and the phar slot
 206  */
 207 void phar_destroy_phar_data(phar_archive_data *phar TSRMLS_DC) /* {{{ */
 208 {
 209         if (phar->alias && phar->alias != phar->fname) {
 210                 pefree(phar->alias, phar->is_persistent);
 211                 phar->alias = NULL;
 212         }
 213 
 214         if (phar->fname) {
 215                 pefree(phar->fname, phar->is_persistent);
 216                 phar->fname = NULL;
 217         }
 218 
 219         if (phar->signature) {
 220                 pefree(phar->signature, phar->is_persistent);
 221                 phar->signature = NULL;
 222         }
 223 
 224         if (phar->manifest.arBuckets) {
 225                 zend_hash_destroy(&phar->manifest);
 226                 phar->manifest.arBuckets = NULL;
 227         }
 228 
 229         if (phar->mounted_dirs.arBuckets) {
 230                 zend_hash_destroy(&phar->mounted_dirs);
 231                 phar->mounted_dirs.arBuckets = NULL;
 232         }
 233 
 234         if (phar->virtual_dirs.arBuckets) {
 235                 zend_hash_destroy(&phar->virtual_dirs);
 236                 phar->virtual_dirs.arBuckets = NULL;
 237         }
 238 
 239         if (phar->metadata) {
 240                 if (phar->is_persistent) {
 241                         if (phar->metadata_len) {
 242                                 /* for zip comments that are strings */
 243                                 free(phar->metadata);
 244                         } else {
 245                                 zval_internal_ptr_dtor(&phar->metadata);
 246                         }
 247                 } else {
 248                         zval_ptr_dtor(&phar->metadata);
 249                 }
 250                 phar->metadata_len = 0;
 251                 phar->metadata = 0;
 252         }
 253 
 254         if (phar->fp) {
 255                 php_stream_close(phar->fp);
 256                 phar->fp = 0;
 257         }
 258 
 259         if (phar->ufp) {
 260                 php_stream_close(phar->ufp);
 261                 phar->ufp = 0;
 262         }
 263 
 264         pefree(phar, phar->is_persistent);
 265 }
 266 /* }}}*/
 267 
 268 /**
 269  * Delete refcount and destruct if needed. On destruct return 1 else 0.
 270  */
 271 int phar_archive_delref(phar_archive_data *phar TSRMLS_DC) /* {{{ */
 272 {
 273         if (phar->is_persistent) {
 274                 return 0;
 275         }
 276 
 277         if (--phar->refcount < 0) {
 278                 if (PHAR_GLOBALS->request_done
 279                 || zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), phar->fname, phar->fname_len) != SUCCESS) {
 280                         phar_destroy_phar_data(phar TSRMLS_CC);
 281                 }
 282                 return 1;
 283         } else if (!phar->refcount) {
 284                 /* invalidate phar cache */
 285                 PHAR_G(last_phar) = NULL;
 286                 PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
 287 
 288                 if (phar->fp && !(phar->flags & PHAR_FILE_COMPRESSION_MASK)) {
 289                         /* close open file handle - allows removal or rename of
 290                         the file on windows, which has greedy locking
 291                         only close if the archive was not already compressed.  If it
 292                         was compressed, then the fp does not refer to the original file */
 293                         php_stream_close(phar->fp);
 294                         phar->fp = NULL;
 295                 }
 296 
 297                 if (!zend_hash_num_elements(&phar->manifest)) {
 298                         /* this is a new phar that has perhaps had an alias/metadata set, but has never
 299                         been flushed */
 300                         if (zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), phar->fname, phar->fname_len) != SUCCESS) {
 301                                 phar_destroy_phar_data(phar TSRMLS_CC);
 302                         }
 303                         return 1;
 304                 }
 305         }
 306         return 0;
 307 }
 308 /* }}}*/
 309 
 310 /**
 311  * Destroy phar's in shutdown, here we don't care about aliases
 312  */
 313 static void destroy_phar_data_only(void *pDest) /* {{{ */
 314 {
 315         phar_archive_data *phar_data = *(phar_archive_data **) pDest;
 316         TSRMLS_FETCH();
 317 
 318         if (EG(exception) || --phar_data->refcount < 0) {
 319                 phar_destroy_phar_data(phar_data TSRMLS_CC);
 320         }
 321 }
 322 /* }}}*/
 323 
 324 /**
 325  * Delete aliases to phar's that got kicked out of the global table
 326  */
 327 static int phar_unalias_apply(void *pDest, void *argument TSRMLS_DC) /* {{{ */
 328 {
 329         return *(void**)pDest == argument ? ZEND_HASH_APPLY_REMOVE : ZEND_HASH_APPLY_KEEP;
 330 }
 331 /* }}} */
 332 
 333 /**
 334  * Delete aliases to phar's that got kicked out of the global table
 335  */
 336 static int phar_tmpclose_apply(void *pDest TSRMLS_DC) /* {{{ */
 337 {
 338         phar_entry_info *entry = (phar_entry_info *) pDest;
 339 
 340         if (entry->fp_type != PHAR_TMP) {
 341                 return ZEND_HASH_APPLY_KEEP;
 342         }
 343 
 344         if (entry->fp && !entry->fp_refcount) {
 345                 php_stream_close(entry->fp);
 346                 entry->fp = NULL;
 347         }
 348 
 349         return ZEND_HASH_APPLY_KEEP;
 350 }
 351 /* }}} */
 352 
 353 /**
 354  * Filename map destructor
 355  */
 356 static void destroy_phar_data(void *pDest) /* {{{ */
 357 {
 358         phar_archive_data *phar_data = *(phar_archive_data **) pDest;
 359         TSRMLS_FETCH();
 360 
 361         if (PHAR_GLOBALS->request_ends) {
 362                 /* first, iterate over the manifest and close all PHAR_TMP entry fp handles,
 363                 this prevents unnecessary unfreed stream resources */
 364                 zend_hash_apply(&(phar_data->manifest), phar_tmpclose_apply TSRMLS_CC);
 365                 destroy_phar_data_only(pDest);
 366                 return;
 367         }
 368 
 369         zend_hash_apply_with_argument(&(PHAR_GLOBALS->phar_alias_map), phar_unalias_apply, phar_data TSRMLS_CC);
 370 
 371         if (--phar_data->refcount < 0) {
 372                 phar_destroy_phar_data(phar_data TSRMLS_CC);
 373         }
 374 }
 375 /* }}}*/
 376 
 377 /**
 378  * destructor for the manifest hash, frees each file's entry
 379  */
 380 void destroy_phar_manifest_entry(void *pDest) /* {{{ */
 381 {
 382         phar_entry_info *entry = (phar_entry_info *)pDest;
 383         TSRMLS_FETCH();
 384 
 385         if (entry->cfp) {
 386                 php_stream_close(entry->cfp);
 387                 entry->cfp = 0;
 388         }
 389 
 390         if (entry->fp) {
 391                 php_stream_close(entry->fp);
 392                 entry->fp = 0;
 393         }
 394 
 395         if (entry->metadata) {
 396                 if (entry->is_persistent) {
 397                         if (entry->metadata_len) {
 398                                 /* for zip comments that are strings */
 399                                 free(entry->metadata);
 400                         } else {
 401                                 zval_internal_ptr_dtor(&entry->metadata);
 402                         }
 403                 } else {
 404                         zval_ptr_dtor(&entry->metadata);
 405                 }
 406                 entry->metadata_len = 0;
 407                 entry->metadata = 0;
 408         }
 409 
 410         if (entry->metadata_str.c) {
 411                 smart_str_free(&entry->metadata_str);
 412                 entry->metadata_str.c = 0;
 413         }
 414 
 415         pefree(entry->filename, entry->is_persistent);
 416 
 417         if (entry->link) {
 418                 pefree(entry->link, entry->is_persistent);
 419                 entry->link = 0;
 420         }
 421 
 422         if (entry->tmp) {
 423                 pefree(entry->tmp, entry->is_persistent);
 424                 entry->tmp = 0;
 425         }
 426 }
 427 /* }}} */
 428 
 429 int phar_entry_delref(phar_entry_data *idata TSRMLS_DC) /* {{{ */
 430 {
 431         int ret = 0;
 432 
 433         if (idata->internal_file && !idata->internal_file->is_persistent) {
 434                 if (--idata->internal_file->fp_refcount < 0) {
 435                         idata->internal_file->fp_refcount = 0;
 436                 }
 437 
 438                 if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) {
 439                         php_stream_close(idata->fp);
 440                 }
 441                 /* if phar_get_or_create_entry_data returns a sub-directory, we have to free it */
 442                 if (idata->internal_file->is_temp_dir) {
 443                         destroy_phar_manifest_entry((void *)idata->internal_file);
 444                         efree(idata->internal_file);
 445                 }
 446         }
 447 
 448         phar_archive_delref(idata->phar TSRMLS_CC);
 449         efree(idata);
 450         return ret;
 451 }
 452 /* }}} */
 453 
 454 /**
 455  * Removes an entry, either by actually removing it or by marking it.
 456  */
 457 void phar_entry_remove(phar_entry_data *idata, char **error TSRMLS_DC) /* {{{ */
 458 {
 459         phar_archive_data *phar;
 460 
 461         phar = idata->phar;
 462 
 463         if (idata->internal_file->fp_refcount < 2) {
 464                 if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) {
 465                         php_stream_close(idata->fp);
 466                 }
 467                 zend_hash_del(&idata->phar->manifest, idata->internal_file->filename, idata->internal_file->filename_len);
 468                 idata->phar->refcount--;
 469                 efree(idata);
 470         } else {
 471                 idata->internal_file->is_deleted = 1;
 472                 phar_entry_delref(idata TSRMLS_CC);
 473         }
 474 
 475         if (!phar->donotflush) {
 476                 phar_flush(phar, 0, 0, 0, error TSRMLS_CC);
 477         }
 478 }
 479 /* }}} */
 480 
 481 #define MAPPHAR_ALLOC_FAIL(msg) \
 482         if (fp) {\
 483                 php_stream_close(fp);\
 484         }\
 485         if (error) {\
 486                 spprintf(error, 0, msg, fname);\
 487         }\
 488         return FAILURE;
 489 
 490 #define MAPPHAR_FAIL(msg) \
 491         efree(savebuf);\
 492         if (mydata) {\
 493                 phar_destroy_phar_data(mydata TSRMLS_CC);\
 494         }\
 495         if (signature) {\
 496                 pefree(signature, PHAR_G(persist));\
 497         }\
 498         MAPPHAR_ALLOC_FAIL(msg)
 499 
 500 #ifdef WORDS_BIGENDIAN
 501 # define PHAR_GET_32(buffer, var) \
 502         var = ((((unsigned char*)(buffer))[3]) << 24) \
 503                 | ((((unsigned char*)(buffer))[2]) << 16) \
 504                 | ((((unsigned char*)(buffer))[1]) <<  8) \
 505                 | (((unsigned char*)(buffer))[0]); \
 506         (buffer) += 4
 507 # define PHAR_GET_16(buffer, var) \
 508         var = ((((unsigned char*)(buffer))[1]) <<  8) \
 509                 | (((unsigned char*)(buffer))[0]); \
 510         (buffer) += 2
 511 #else
 512 # define PHAR_GET_32(buffer, var) \
 513         memcpy(&var, buffer, sizeof(var)); \
 514         buffer += 4
 515 # define PHAR_GET_16(buffer, var) \
 516         var = *(php_uint16*)(buffer); \
 517         buffer += 2
 518 #endif
 519 #define PHAR_ZIP_16(var) ((php_uint16)((((php_uint16)var[0]) & 0xff) | \
 520         (((php_uint16)var[1]) & 0xff) << 8))
 521 #define PHAR_ZIP_32(var) ((php_uint32)((((php_uint32)var[0]) & 0xff) | \
 522         (((php_uint32)var[1]) & 0xff) << 8 | \
 523         (((php_uint32)var[2]) & 0xff) << 16 | \
 524         (((php_uint32)var[3]) & 0xff) << 24))
 525 
 526 /**
 527  * Open an already loaded phar
 528  */
 529 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) /* {{{ */
 530 {
 531         phar_archive_data *phar;
 532 #ifdef PHP_WIN32
 533         char *unixfname;
 534 #endif
 535 
 536         if (error) {
 537                 *error = NULL;
 538         }
 539 #ifdef PHP_WIN32
 540         unixfname = estrndup(fname, fname_len);
 541         phar_unixify_path_separators(unixfname, fname_len);
 542 
 543         if (SUCCESS == phar_get_archive(&phar, unixfname, fname_len, alias, alias_len, error TSRMLS_CC)
 544                 && ((alias && fname_len == phar->fname_len
 545                 && !strncmp(unixfname, phar->fname, fname_len)) || !alias)
 546         ) {
 547                 phar_entry_info *stub;
 548                 efree(unixfname);
 549 #else
 550         if (SUCCESS == phar_get_archive(&phar, fname, fname_len, alias, alias_len, error TSRMLS_CC)
 551                 && ((alias && fname_len == phar->fname_len
 552                 && !strncmp(fname, phar->fname, fname_len)) || !alias)
 553         ) {
 554                 phar_entry_info *stub;
 555 #endif
 556                 /* logic above is as follows:
 557                    If an explicit alias was requested, ensure the filename passed in
 558                    matches the phar's filename.
 559                    If no alias was passed in, then it can match either and be valid
 560                  */
 561 
 562                 if (!is_data) {
 563                         /* prevent any ".phar" without a stub getting through */
 564                         if (!phar->halt_offset && !phar->is_brandnew && (phar->is_tar || phar->is_zip)) {
 565                                 if (PHAR_G(readonly) && FAILURE == zend_hash_find(&(phar->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&stub)) {
 566                                         if (error) {
 567                                                 spprintf(error, 0, "'%s' is not a phar archive. Use PharData::__construct() for a standard zip or tar archive", fname);
 568                                         }
 569                                         return FAILURE;
 570                                 }
 571                         }
 572                 }
 573 
 574                 if (pphar) {
 575                         *pphar = phar;
 576                 }
 577 
 578                 return SUCCESS;
 579         } else {
 580 #ifdef PHP_WIN32
 581                 efree(unixfname);
 582 #endif
 583                 if (pphar) {
 584                         *pphar = NULL;
 585                 }
 586 
 587                 if (phar && error && !(options & REPORT_ERRORS)) {
 588                         efree(error);
 589                 }
 590 
 591                 return FAILURE;
 592         }
 593 }
 594 /* }}}*/
 595 
 596 /**
 597  * Parse out metadata from the manifest for a single file
 598  *
 599  * Meta-data is in this format:
 600  * [len32][data...]
 601  *
 602  * data is the serialized zval
 603  */
 604 int phar_parse_metadata(char **buffer, zval **metadata, php_uint32 zip_metadata_len TSRMLS_DC) /* {{{ */
 605 {
 606         php_unserialize_data_t var_hash;
 607 
 608         if (zip_metadata_len) {
 609                 const unsigned char *p;
 610                 unsigned char *p_buff = (unsigned char *)estrndup(*buffer, zip_metadata_len);
 611                 p = p_buff;
 612                 ALLOC_ZVAL(*metadata);
 613                 INIT_ZVAL(**metadata);
 614                 PHP_VAR_UNSERIALIZE_INIT(var_hash);
 615 
 616                 if (!php_var_unserialize(metadata, &p, p + zip_metadata_len, &var_hash TSRMLS_CC)) {
 617                         efree(p_buff);
 618                         PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
 619                         zval_ptr_dtor(metadata);
 620                         *metadata = NULL;
 621                         return FAILURE;
 622                 }
 623                 efree(p_buff);
 624                 PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
 625 
 626                 if (PHAR_G(persist)) {
 627                         /* lazy init metadata */
 628                         zval_ptr_dtor(metadata);
 629                         *metadata = (zval *) pemalloc(zip_metadata_len, 1);
 630                         memcpy(*metadata, *buffer, zip_metadata_len);
 631                         return SUCCESS;
 632                 }
 633         } else {
 634                 *metadata = NULL;
 635         }
 636 
 637         return SUCCESS;
 638 }
 639 /* }}}*/
 640 
 641 /**
 642  * Does not check for a previously opened phar in the cache.
 643  *
 644  * Parse a new one and add it to the cache, returning either SUCCESS or
 645  * FAILURE, and setting pphar to the pointer to the manifest entry
 646  *
 647  * This is used by phar_open_from_filename to process the manifest, but can be called
 648  * directly.
 649  */
 650 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) /* {{{ */
 651 {
 652         char b32[4], *buffer, *endbuffer, *savebuf;
 653         phar_archive_data *mydata = NULL;
 654         phar_entry_info entry;
 655         php_uint32 manifest_len, manifest_count, manifest_flags, manifest_index, tmp_len, sig_flags;
 656         php_uint16 manifest_ver;
 657         php_uint32 len;
 658         long offset;
 659         int sig_len, register_alias = 0, temp_alias = 0;
 660         char *signature = NULL;
 661 
 662         if (pphar) {
 663                 *pphar = NULL;
 664         }
 665 
 666         if (error) {
 667                 *error = NULL;
 668         }
 669 
 670         /* check for ?>\n and increment accordingly */
 671         if (-1 == php_stream_seek(fp, halt_offset, SEEK_SET)) {
 672                 MAPPHAR_ALLOC_FAIL("cannot seek to __HALT_COMPILER(); location in phar \"%s\"")
 673         }
 674 
 675         buffer = b32;
 676 
 677         if (3 != php_stream_read(fp, buffer, 3)) {
 678                 MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
 679         }
 680 
 681         if ((*buffer == ' ' || *buffer == '\n') && *(buffer + 1) == '?' && *(buffer + 2) == '>') {
 682                 int nextchar;
 683                 halt_offset += 3;
 684                 if (EOF == (nextchar = php_stream_getc(fp))) {
 685                         MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
 686                 }
 687 
 688                 if ((char) nextchar == '\r') {
 689                         /* if we have an \r we require an \n as well */
 690                         if (EOF == (nextchar = php_stream_getc(fp)) || (char)nextchar != '\n') {
 691                                 MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
 692                         }
 693                         ++halt_offset;
 694                 }
 695 
 696                 if ((char) nextchar == '\n') {
 697                         ++halt_offset;
 698                 }
 699         }
 700 
 701         /* make sure we are at the right location to read the manifest */
 702         if (-1 == php_stream_seek(fp, halt_offset, SEEK_SET)) {
 703                 MAPPHAR_ALLOC_FAIL("cannot seek to __HALT_COMPILER(); location in phar \"%s\"")
 704         }
 705 
 706         /* read in manifest */
 707         buffer = b32;
 708 
 709         if (4 != php_stream_read(fp, buffer, 4)) {
 710                 MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at manifest length)")
 711         }
 712 
 713         PHAR_GET_32(buffer, manifest_len);
 714 
 715         if (manifest_len > 1048576 * 100) {
 716                 /* prevent serious memory issues by limiting manifest to at most 100 MB in length */
 717                 MAPPHAR_ALLOC_FAIL("manifest cannot be larger than 100 MB in phar \"%s\"")
 718         }
 719 
 720         buffer = (char *)emalloc(manifest_len);
 721         savebuf = buffer;
 722         endbuffer = buffer + manifest_len;
 723 
 724         if (manifest_len < 10 || manifest_len != php_stream_read(fp, buffer, manifest_len)) {
 725                 MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)")
 726         }
 727 
 728         /* extract the number of entries */
 729         PHAR_GET_32(buffer, manifest_count);
 730 
 731         if (manifest_count == 0) {
 732                 MAPPHAR_FAIL("in phar \"%s\", manifest claims to have zero entries.  Phars must have at least 1 entry");
 733         }
 734 
 735         /* extract API version, lowest nibble currently unused */
 736         manifest_ver = (((unsigned char)buffer[0]) << 8)
 737                                  + ((unsigned char)buffer[1]);
 738         buffer += 2;
 739 
 740         if ((manifest_ver & PHAR_API_VER_MASK) < PHAR_API_MIN_READ) {
 741                 efree(savebuf);
 742                 php_stream_close(fp);
 743                 if (error) {
 744                         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);
 745                 }
 746                 return FAILURE;
 747         }
 748 
 749         PHAR_GET_32(buffer, manifest_flags);
 750 
 751         manifest_flags &= ~PHAR_HDR_COMPRESSION_MASK;
 752         manifest_flags &= ~PHAR_FILE_COMPRESSION_MASK;
 753         /* remember whether this entire phar was compressed with gz/bzip2 */
 754         manifest_flags |= compression;
 755 
 756         /* The lowest nibble contains the phar wide flags. The compression flags can */
 757         /* be ignored on reading because it is being generated anyways. */
 758         if (manifest_flags & PHAR_HDR_SIGNATURE) {
 759                 char sig_buf[8], *sig_ptr = sig_buf;
 760                 off_t read_len;
 761                 size_t end_of_phar;
 762 
 763                 if (-1 == php_stream_seek(fp, -8, SEEK_END)
 764                 || (read_len = php_stream_tell(fp)) < 20
 765                 || 8 != php_stream_read(fp, sig_buf, 8)
 766                 || memcmp(sig_buf+4, "GBMB", 4)) {
 767                         efree(savebuf);
 768                         php_stream_close(fp);
 769                         if (error) {
 770                                 spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
 771                         }
 772                         return FAILURE;
 773                 }
 774 
 775                 PHAR_GET_32(sig_ptr, sig_flags);
 776 
 777                 switch(sig_flags) {
 778                         case PHAR_SIG_OPENSSL: {
 779                                 php_uint32 signature_len;
 780                                 char *sig;
 781                                 off_t whence;
 782 
 783                                 /* we store the signature followed by the signature length */
 784                                 if (-1 == php_stream_seek(fp, -12, SEEK_CUR)
 785                                 || 4 != php_stream_read(fp, sig_buf, 4)) {
 786                                         efree(savebuf);
 787                                         php_stream_close(fp);
 788                                         if (error) {
 789                                                 spprintf(error, 0, "phar \"%s\" openssl signature length could not be read", fname);
 790                                         }
 791                                         return FAILURE;
 792                                 }
 793 
 794                                 sig_ptr = sig_buf;
 795                                 PHAR_GET_32(sig_ptr, signature_len);
 796                                 sig = (char *) emalloc(signature_len);
 797                                 whence = signature_len + 4;
 798                                 whence = -whence;
 799 
 800                                 if (-1 == php_stream_seek(fp, whence, SEEK_CUR)
 801                                 || !(end_of_phar = php_stream_tell(fp))
 802                                 || signature_len != php_stream_read(fp, sig, signature_len)) {
 803                                         efree(savebuf);
 804                                         efree(sig);
 805                                         php_stream_close(fp);
 806                                         if (error) {
 807                                                 spprintf(error, 0, "phar \"%s\" openssl signature could not be read", fname);
 808                                         }
 809                                         return FAILURE;
 810                                 }
 811 
 812                                 if (FAILURE == phar_verify_signature(fp, end_of_phar, PHAR_SIG_OPENSSL, sig, signature_len, fname, &signature, &sig_len, error TSRMLS_CC)) {
 813                                         efree(savebuf);
 814                                         efree(sig);
 815                                         php_stream_close(fp);
 816                                         if (error) {
 817                                                 char *save = *error;
 818                                                 spprintf(error, 0, "phar \"%s\" openssl signature could not be verified: %s", fname, *error);
 819                                                 efree(save);
 820                                         }
 821                                         return FAILURE;
 822                                 }
 823                                 efree(sig);
 824                         }
 825                         break;
 826 #if PHAR_HASH_OK
 827                         case PHAR_SIG_SHA512: {
 828                                 unsigned char digest[64];
 829 
 830                                 php_stream_seek(fp, -(8 + 64), SEEK_END);
 831                                 read_len = php_stream_tell(fp);
 832 
 833                                 if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
 834                                         efree(savebuf);
 835                                         php_stream_close(fp);
 836                                         if (error) {
 837                                                 spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
 838                                         }
 839                                         return FAILURE;
 840                                 }
 841 
 842                                 if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA512, (char *)digest, 64, fname, &signature, &sig_len, error TSRMLS_CC)) {
 843                                         efree(savebuf);
 844                                         php_stream_close(fp);
 845                                         if (error) {
 846                                                 char *save = *error;
 847                                                 spprintf(error, 0, "phar \"%s\" SHA512 signature could not be verified: %s", fname, *error);
 848                                                 efree(save);
 849                                         }
 850                                         return FAILURE;
 851                                 }
 852                                 break;
 853                         }
 854                         case PHAR_SIG_SHA256: {
 855                                 unsigned char digest[32];
 856 
 857                                 php_stream_seek(fp, -(8 + 32), SEEK_END);
 858                                 read_len = php_stream_tell(fp);
 859 
 860                                 if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
 861                                         efree(savebuf);
 862                                         php_stream_close(fp);
 863                                         if (error) {
 864                                                 spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
 865                                         }
 866                                         return FAILURE;
 867                                 }
 868 
 869                                 if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA256, (char *)digest, 32, fname, &signature, &sig_len, error TSRMLS_CC)) {
 870                                         efree(savebuf);
 871                                         php_stream_close(fp);
 872                                         if (error) {
 873                                                 char *save = *error;
 874                                                 spprintf(error, 0, "phar \"%s\" SHA256 signature could not be verified: %s", fname, *error);
 875                                                 efree(save);
 876                                         }
 877                                         return FAILURE;
 878                                 }
 879                                 break;
 880                         }
 881 #else
 882                         case PHAR_SIG_SHA512:
 883                         case PHAR_SIG_SHA256:
 884                                 efree(savebuf);
 885                                 php_stream_close(fp);
 886 
 887                                 if (error) {
 888                                         spprintf(error, 0, "phar \"%s\" has a unsupported signature", fname);
 889                                 }
 890                                 return FAILURE;
 891 #endif
 892                         case PHAR_SIG_SHA1: {
 893                                 unsigned char digest[20];
 894 
 895                                 php_stream_seek(fp, -(8 + 20), SEEK_END);
 896                                 read_len = php_stream_tell(fp);
 897 
 898                                 if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
 899                                         efree(savebuf);
 900                                         php_stream_close(fp);
 901                                         if (error) {
 902                                                 spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
 903                                         }
 904                                         return FAILURE;
 905                                 }
 906 
 907                                 if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA1, (char *)digest, 20, fname, &signature, &sig_len, error TSRMLS_CC)) {
 908                                         efree(savebuf);
 909                                         php_stream_close(fp);
 910                                         if (error) {
 911                                                 char *save = *error;
 912                                                 spprintf(error, 0, "phar \"%s\" SHA1 signature could not be verified: %s", fname, *error);
 913                                                 efree(save);
 914                                         }
 915                                         return FAILURE;
 916                                 }
 917                                 break;
 918                         }
 919                         case PHAR_SIG_MD5: {
 920                                 unsigned char digest[16];
 921 
 922                                 php_stream_seek(fp, -(8 + 16), SEEK_END);
 923                                 read_len = php_stream_tell(fp);
 924 
 925                                 if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
 926                                         efree(savebuf);
 927                                         php_stream_close(fp);
 928                                         if (error) {
 929                                                 spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
 930                                         }
 931                                         return FAILURE;
 932                                 }
 933 
 934                                 if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_MD5, (char *)digest, 16, fname, &signature, &sig_len, error TSRMLS_CC)) {
 935                                         efree(savebuf);
 936                                         php_stream_close(fp);
 937                                         if (error) {
 938                                                 char *save = *error;
 939                                                 spprintf(error, 0, "phar \"%s\" MD5 signature could not be verified: %s", fname, *error);
 940                                                 efree(save);
 941                                         }
 942                                         return FAILURE;
 943                                 }
 944                                 break;
 945                         }
 946                         default:
 947                                 efree(savebuf);
 948                                 php_stream_close(fp);
 949 
 950                                 if (error) {
 951                                         spprintf(error, 0, "phar \"%s\" has a broken or unsupported signature", fname);
 952                                 }
 953                                 return FAILURE;
 954                 }
 955         } else if (PHAR_G(require_hash)) {
 956                 efree(savebuf);
 957                 php_stream_close(fp);
 958 
 959                 if (error) {
 960                         spprintf(error, 0, "phar \"%s\" does not have a signature", fname);
 961                 }
 962                 return FAILURE;
 963         } else {
 964                 sig_flags = 0;
 965                 sig_len = 0;
 966         }
 967 
 968         /* extract alias */
 969         PHAR_GET_32(buffer, tmp_len);
 970 
 971         if (buffer + tmp_len > endbuffer) {
 972                 MAPPHAR_FAIL("internal corruption of phar \"%s\" (buffer overrun)");
 973         }
 974 
 975         if (manifest_len < 10 + tmp_len) {
 976                 MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)")
 977         }
 978 
 979         /* tmp_len = 0 says alias length is 0, which means the alias is not stored in the phar */
 980         if (tmp_len) {
 981                 /* if the alias is stored we enforce it (implicit overrides explicit) */
 982                 if (alias && alias_len && (alias_len != (int)tmp_len || strncmp(alias, buffer, tmp_len)))
 983                 {
 984                         buffer[tmp_len] = '\0';
 985                         php_stream_close(fp);
 986 
 987                         if (signature) {
 988                                 efree(signature);
 989                         }
 990 
 991                         if (error) {
 992                                 spprintf(error, 0, "cannot load phar \"%s\" with implicit alias \"%s\" under different alias \"%s\"", fname, buffer, alias);
 993                         }
 994 
 995                         efree(savebuf);
 996                         return FAILURE;
 997                 }
 998 
 999                 alias_len = tmp_len;
1000                 alias = buffer;
1001                 buffer += tmp_len;
1002                 register_alias = 1;
1003         } else if (!alias_len || !alias) {
1004                 /* if we neither have an explicit nor an implicit alias, we use the filename */
1005                 alias = NULL;
1006                 alias_len = 0;
1007                 register_alias = 0;
1008         } else if (alias_len) {
1009                 register_alias = 1;
1010                 temp_alias = 1;
1011         }
1012 
1013         /* we have 5 32-bit items plus 1 byte at least */
1014         if (manifest_count > ((manifest_len - 10 - tmp_len) / (5 * 4 + 1))) {
1015                 /* prevent serious memory issues */
1016                 MAPPHAR_FAIL("internal corruption of phar \"%s\" (too many manifest entries for size of manifest)")
1017         }
1018 
1019         mydata = pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist));
1020         mydata->is_persistent = PHAR_G(persist);
1021 
1022         /* check whether we have meta data, zero check works regardless of byte order */
1023         PHAR_GET_32(buffer, len);
1024         if (mydata->is_persistent) {
1025                 mydata->metadata_len = len;
1026                 if(!len) {
1027                         /* FIXME: not sure why this is needed but removing it breaks tests */
1028                         PHAR_GET_32(buffer, len);
1029                 }
1030         }
1031         if(len > endbuffer - buffer) {
1032                 MAPPHAR_FAIL("internal corruption of phar \"%s\" (trying to read past buffer end)");
1033         }
1034         if (phar_parse_metadata(&buffer, &mydata->metadata, len TSRMLS_CC) == FAILURE) {
1035                 MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\"");
1036         }
1037         buffer += len;
1038 
1039         /* set up our manifest */
1040         zend_hash_init(&mydata->manifest, manifest_count,
1041                 zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)mydata->is_persistent);
1042         zend_hash_init(&mydata->mounted_dirs, 5,
1043                 zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
1044         zend_hash_init(&mydata->virtual_dirs, manifest_count * 2,
1045                 zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
1046         mydata->fname = pestrndup(fname, fname_len, mydata->is_persistent);
1047 #ifdef PHP_WIN32
1048         phar_unixify_path_separators(mydata->fname, fname_len);
1049 #endif
1050         mydata->fname_len = fname_len;
1051         offset = halt_offset + manifest_len + 4;
1052         memset(&entry, 0, sizeof(phar_entry_info));
1053         entry.phar = mydata;
1054         entry.fp_type = PHAR_FP;
1055         entry.is_persistent = mydata->is_persistent;
1056 
1057         for (manifest_index = 0; manifest_index < manifest_count; ++manifest_index) {
1058                 if (buffer + 4 > endbuffer) {
1059                         MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)")
1060                 }
1061 
1062                 PHAR_GET_32(buffer, entry.filename_len);
1063 
1064                 if (entry.filename_len == 0) {
1065                         MAPPHAR_FAIL("zero-length filename encountered in phar \"%s\"");
1066                 }
1067 
1068                 if (entry.is_persistent) {
1069                         entry.manifest_pos = manifest_index;
1070                 }
1071 
1072                 if (entry.filename_len + 20 > endbuffer - buffer) {
1073                         MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)");
1074                 }
1075 
1076                 if ((manifest_ver & PHAR_API_VER_MASK) >= PHAR_API_MIN_DIR && buffer[entry.filename_len - 1] == '/') {
1077                         entry.is_dir = 1;
1078                 } else {
1079                         entry.is_dir = 0;
1080                 }
1081 
1082                 phar_add_virtual_dirs(mydata, buffer, entry.filename_len TSRMLS_CC);
1083                 entry.filename = pestrndup(buffer, entry.filename_len, entry.is_persistent);
1084                 buffer += entry.filename_len;
1085                 PHAR_GET_32(buffer, entry.uncompressed_filesize);
1086                 PHAR_GET_32(buffer, entry.timestamp);
1087 
1088                 if (offset == halt_offset + (int)manifest_len + 4) {
1089                         mydata->min_timestamp = entry.timestamp;
1090                         mydata->max_timestamp = entry.timestamp;
1091                 } else {
1092                         if (mydata->min_timestamp > entry.timestamp) {
1093                                 mydata->min_timestamp = entry.timestamp;
1094                         } else if (mydata->max_timestamp < entry.timestamp) {
1095                                 mydata->max_timestamp = entry.timestamp;
1096                         }
1097                 }
1098 
1099                 PHAR_GET_32(buffer, entry.compressed_filesize);
1100                 PHAR_GET_32(buffer, entry.crc32);
1101                 PHAR_GET_32(buffer, entry.flags);
1102 
1103                 if (entry.is_dir) {
1104                         entry.filename_len--;
1105                         entry.flags |= PHAR_ENT_PERM_DEF_DIR;
1106                 }
1107 
1108                 PHAR_GET_32(buffer, len);
1109                 if (entry.is_persistent) {
1110                         entry.metadata_len = len;
1111                 } else {
1112                         entry.metadata_len = 0;
1113                 }
1114                 if (len > endbuffer - buffer) {
1115                         pefree(entry.filename, entry.is_persistent);
1116                         MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)");
1117                 }
1118                 if (phar_parse_metadata(&buffer, &entry.metadata, len TSRMLS_CC) == FAILURE) {
1119                         pefree(entry.filename, entry.is_persistent);
1120                         MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\"");
1121                 }
1122                 buffer += len;
1123 
1124                 entry.offset = entry.offset_abs = offset;
1125                 offset += entry.compressed_filesize;
1126 
1127                 switch (entry.flags & PHAR_ENT_COMPRESSION_MASK) {
1128                         case PHAR_ENT_COMPRESSED_GZ:
1129                                 if (!PHAR_G(has_zlib)) {
1130                                         if (entry.metadata) {
1131                                                 if (entry.is_persistent) {
1132                                                         free(entry.metadata);
1133                                                 } else {
1134                                                         zval_ptr_dtor(&entry.metadata);
1135                                                 }
1136                                         }
1137                                         pefree(entry.filename, entry.is_persistent);
1138                                         MAPPHAR_FAIL("zlib extension is required for gz compressed .phar file \"%s\"");
1139                                 }
1140                                 break;
1141                         case PHAR_ENT_COMPRESSED_BZ2:
1142                                 if (!PHAR_G(has_bz2)) {
1143                                         if (entry.metadata) {
1144                                                 if (entry.is_persistent) {
1145                                                         free(entry.metadata);
1146                                                 } else {
1147                                                         zval_ptr_dtor(&entry.metadata);
1148                                                 }
1149                                         }
1150                                         pefree(entry.filename, entry.is_persistent);
1151                                         MAPPHAR_FAIL("bz2 extension is required for bzip2 compressed .phar file \"%s\"");
1152                                 }
1153                                 break;
1154                         default:
1155                                 if (entry.uncompressed_filesize != entry.compressed_filesize) {
1156                                         if (entry.metadata) {
1157                                                 if (entry.is_persistent) {
1158                                                         free(entry.metadata);
1159                                                 } else {
1160                                                         zval_ptr_dtor(&entry.metadata);
1161                                                 }
1162                                         }
1163                                         pefree(entry.filename, entry.is_persistent);
1164                                         MAPPHAR_FAIL("internal corruption of phar \"%s\" (compressed and uncompressed size does not match for uncompressed entry)");
1165                                 }
1166                                 break;
1167                 }
1168 
1169                 manifest_flags |= (entry.flags & PHAR_ENT_COMPRESSION_MASK);
1170                 /* if signature matched, no need to check CRC32 for each file */
1171                 entry.is_crc_checked = (manifest_flags & PHAR_HDR_SIGNATURE ? 1 : 0);
1172                 phar_set_inode(&entry TSRMLS_CC);
1173                 zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL);
1174         }
1175 
1176         snprintf(mydata->version, sizeof(mydata->version), "%u.%u.%u", manifest_ver >> 12, (manifest_ver >> 8) & 0xF, (manifest_ver >> 4) & 0xF);
1177         mydata->internal_file_start = halt_offset + manifest_len + 4;
1178         mydata->halt_offset = halt_offset;
1179         mydata->flags = manifest_flags;
1180         endbuffer = strrchr(mydata->fname, '/');
1181 
1182         if (endbuffer) {
1183                 mydata->ext = memchr(endbuffer, '.', (mydata->fname + fname_len) - endbuffer);
1184                 if (mydata->ext == endbuffer) {
1185                         mydata->ext = memchr(endbuffer + 1, '.', (mydata->fname + fname_len) - endbuffer - 1);
1186                 }
1187                 if (mydata->ext) {
1188                         mydata->ext_len = (mydata->fname + mydata->fname_len) - mydata->ext;
1189                 }
1190         }
1191 
1192         mydata->alias = alias ?
1193                 pestrndup(alias, alias_len, mydata->is_persistent) :
1194                 pestrndup(mydata->fname, fname_len, mydata->is_persistent);
1195         mydata->alias_len = alias ? alias_len : fname_len;
1196         mydata->sig_flags = sig_flags;
1197         mydata->fp = fp;
1198         mydata->sig_len = sig_len;
1199         mydata->signature = signature;
1200         phar_request_initialize(TSRMLS_C);
1201 
1202         if (register_alias) {
1203                 phar_archive_data **fd_ptr;
1204 
1205                 mydata->is_temporary_alias = temp_alias;
1206 
1207                 if (!phar_validate_alias(mydata->alias, mydata->alias_len)) {
1208                         signature = NULL;
1209                         fp = NULL;
1210                         MAPPHAR_FAIL("Cannot open archive \"%s\", invalid alias");
1211                 }
1212 
1213                 if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) {
1214                         if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) {
1215                                 signature = NULL;
1216                                 fp = NULL;
1217                                 MAPPHAR_FAIL("Cannot open archive \"%s\", alias is already in use by existing archive");
1218                         }
1219                 }
1220 
1221                 zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
1222         } else {
1223                 mydata->is_temporary_alias = 1;
1224         }
1225 
1226         zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*),  NULL);
1227         efree(savebuf);
1228 
1229         if (pphar) {
1230                 *pphar = mydata;
1231         }
1232 
1233         return SUCCESS;
1234 }
1235 /* }}} */
1236 
1237 /**
1238  * Create or open a phar for writing
1239  */
1240 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) /* {{{ */
1241 {
1242         const char *ext_str, *z;
1243         char *my_error;
1244         int ext_len;
1245         phar_archive_data **test, *unused = NULL;
1246 
1247         test = &unused;
1248 
1249         if (error) {
1250                 *error = NULL;
1251         }
1252 
1253         /* first try to open an existing file */
1254         if (phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, !is_data, 0, 1 TSRMLS_CC) == SUCCESS) {
1255                 goto check_file;
1256         }
1257 
1258         /* next try to create a new file */
1259         if (FAILURE == phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, !is_data, 1, 1 TSRMLS_CC)) {
1260                 if (error) {
1261                         if (ext_len == -2) {
1262                                 spprintf(error, 0, "Cannot create a phar archive from a URL like \"%s\". Phar objects can only be created from local files", fname);
1263                         } else {
1264                                 spprintf(error, 0, "Cannot create phar '%s', file extension (or combination) not recognised or the directory does not exist", fname);
1265                         }
1266                 }
1267                 return FAILURE;
1268         }
1269 check_file:
1270         if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, test, &my_error TSRMLS_CC) == SUCCESS) {
1271                 if (pphar) {
1272                         *pphar = *test;
1273                 }
1274 
1275                 if ((*test)->is_data && !(*test)->is_tar && !(*test)->is_zip) {
1276                         if (error) {
1277                                 spprintf(error, 0, "Cannot open '%s' as a PharData object. Use Phar::__construct() for executable archives", fname);
1278                         }
1279                         return FAILURE;
1280                 }
1281 
1282                 if (PHAR_G(readonly) && !(*test)->is_data && ((*test)->is_tar || (*test)->is_zip)) {
1283                         phar_entry_info *stub;
1284                         if (FAILURE == zend_hash_find(&((*test)->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&stub)) {
1285                                 spprintf(error, 0, "'%s' is not a phar archive. Use PharData::__construct() for a standard zip or tar archive", fname);
1286                                 return FAILURE;
1287                         }
1288                 }
1289 
1290                 if (!PHAR_G(readonly) || (*test)->is_data) {
1291                         (*test)->is_writeable = 1;
1292                 }
1293                 return SUCCESS;
1294         } else if (my_error) {
1295                 if (error) {
1296                         *error = my_error;
1297                 } else {
1298                         efree(my_error);
1299                 }
1300                 return FAILURE;
1301         }
1302 
1303         if (ext_len > 3 && (z = memchr(ext_str, 'z', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ip", 2)) {
1304                 /* assume zip-based phar */
1305                 return phar_open_or_create_zip(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC);
1306         }
1307 
1308         if (ext_len > 3 && (z = memchr(ext_str, 't', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ar", 2)) {
1309                 /* assume tar-based phar */
1310                 return phar_open_or_create_tar(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC);
1311         }
1312 
1313         return phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC);
1314 }
1315 /* }}} */
1316 
1317 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) /* {{{ */
1318 {
1319         phar_archive_data *mydata;
1320         php_stream *fp;
1321         char *actual = NULL, *p;
1322 
1323         if (!pphar) {
1324                 pphar = &mydata;
1325         }
1326 #if PHP_API_VERSION < 20100412
1327         if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
1328                 return FAILURE;
1329         }
1330 #endif
1331         if (php_check_open_basedir(fname TSRMLS_CC)) {
1332                 return FAILURE;
1333         }
1334 
1335         /* first open readonly so it won't be created if not present */
1336         fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, &actual);
1337 
1338         if (actual) {
1339                 fname = actual;
1340                 fname_len = strlen(actual);
1341         }
1342 
1343         if (fp) {
1344                 if (phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error TSRMLS_CC) == SUCCESS) {
1345                         if ((*pphar)->is_data || !PHAR_G(readonly)) {
1346                                 (*pphar)->is_writeable = 1;
1347                         }
1348                         if (actual) {
1349                                 efree(actual);
1350                         }
1351                         return SUCCESS;
1352                 } else {
1353                         /* file exists, but is either corrupt or not a phar archive */
1354                         if (actual) {
1355                                 efree(actual);
1356                         }
1357                         return FAILURE;
1358                 }
1359         }
1360 
1361         if (actual) {
1362                 efree(actual);
1363         }
1364 
1365         if (PHAR_G(readonly) && !is_data) {
1366                 if (options & REPORT_ERRORS) {
1367                         if (error) {
1368                                 spprintf(error, 0, "creating archive \"%s\" disabled by the php.ini setting phar.readonly", fname);
1369                         }
1370                 }
1371                 return FAILURE;
1372         }
1373 
1374         /* set up our manifest */
1375         mydata = ecalloc(1, sizeof(phar_archive_data));
1376         mydata->fname = expand_filepath(fname, NULL TSRMLS_CC);
1377         fname_len = strlen(mydata->fname);
1378 #ifdef PHP_WIN32
1379         phar_unixify_path_separators(mydata->fname, fname_len);
1380 #endif
1381         p = strrchr(mydata->fname, '/');
1382 
1383         if (p) {
1384                 mydata->ext = memchr(p, '.', (mydata->fname + fname_len) - p);
1385                 if (mydata->ext == p) {
1386                         mydata->ext = memchr(p + 1, '.', (mydata->fname + fname_len) - p - 1);
1387                 }
1388                 if (mydata->ext) {
1389                         mydata->ext_len = (mydata->fname + fname_len) - mydata->ext;
1390                 }
1391         }
1392 
1393         if (pphar) {
1394                 *pphar = mydata;
1395         }
1396 
1397         zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
1398                 zend_get_hash_value, destroy_phar_manifest_entry, 0);
1399         zend_hash_init(&mydata->mounted_dirs, sizeof(char *),
1400                 zend_get_hash_value, NULL, 0);
1401         zend_hash_init(&mydata->virtual_dirs, sizeof(char *),
1402                 zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
1403         mydata->fname_len = fname_len;
1404         snprintf(mydata->version, sizeof(mydata->version), "%s", PHP_PHAR_API_VERSION);
1405         mydata->is_temporary_alias = alias ? 0 : 1;
1406         mydata->internal_file_start = -1;
1407         mydata->fp = NULL;
1408         mydata->is_writeable = 1;
1409         mydata->is_brandnew = 1;
1410         phar_request_initialize(TSRMLS_C);
1411         zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*),  NULL);
1412 
1413         if (is_data) {
1414                 alias = NULL;
1415                 alias_len = 0;
1416                 mydata->is_data = 1;
1417                 /* assume tar format, PharData can specify other */
1418                 mydata->is_tar = 1;
1419         } else {
1420                 phar_archive_data **fd_ptr;
1421 
1422                 if (alias && SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) {
1423                         if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) {
1424                                 if (error) {
1425                                         spprintf(error, 4096, "phar error: phar \"%s\" cannot set alias \"%s\", already in use by another phar archive", mydata->fname, alias);
1426                                 }
1427 
1428                                 zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len);
1429 
1430                                 if (pphar) {
1431                                         *pphar = NULL;
1432                                 }
1433 
1434                                 return FAILURE;
1435                         }
1436                 }
1437 
1438                 mydata->alias = alias ? estrndup(alias, alias_len) : estrndup(mydata->fname, fname_len);
1439                 mydata->alias_len = alias ? alias_len : fname_len;
1440         }
1441 
1442         if (alias_len && alias) {
1443                 if (FAILURE == zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL)) {
1444                         if (options & REPORT_ERRORS) {
1445                                 if (error) {
1446                                         spprintf(error, 0, "archive \"%s\" cannot be associated with alias \"%s\", already in use", fname, alias);
1447                                 }
1448                         }
1449 
1450                         zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len);
1451 
1452                         if (pphar) {
1453                                 *pphar = NULL;
1454                         }
1455 
1456                         return FAILURE;
1457                 }
1458         }
1459 
1460         return SUCCESS;
1461 }
1462 /* }}}*/
1463 
1464 /**
1465  * Return an already opened filename.
1466  *
1467  * Or scan a phar file for the required __HALT_COMPILER(); ?> token and verify
1468  * that the manifest is proper, then pass it to phar_parse_pharfile().  SUCCESS
1469  * or FAILURE is returned and pphar is set to a pointer to the phar's manifest
1470  */
1471 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) /* {{{ */
1472 {
1473         php_stream *fp;
1474         char *actual;
1475         int ret, is_data = 0;
1476 
1477         if (error) {
1478                 *error = NULL;
1479         }
1480 
1481         if (!strstr(fname, ".phar")) {
1482                 is_data = 1;
1483         }
1484 
1485         if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC) == SUCCESS) {
1486                 return SUCCESS;
1487         } else if (error && *error) {
1488                 return FAILURE;
1489         }
1490 #if PHP_API_VERSION < 20100412
1491         if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
1492                 return FAILURE;
1493         }
1494 #endif
1495         if (php_check_open_basedir(fname TSRMLS_CC)) {
1496                 return FAILURE;
1497         }
1498 
1499         fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, &actual);
1500 
1501         if (!fp) {
1502                 if (options & REPORT_ERRORS) {
1503                         if (error) {
1504                                 spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
1505                         }
1506                 }
1507                 if (actual) {
1508                         efree(actual);
1509                 }
1510                 return FAILURE;
1511         }
1512 
1513         if (actual) {
1514                 fname = actual;
1515                 fname_len = strlen(actual);
1516         }
1517 
1518         ret =  phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error TSRMLS_CC);
1519 
1520         if (actual) {
1521                 efree(actual);
1522         }
1523 
1524         return ret;
1525 }
1526 /* }}}*/
1527 
1528 static inline char *phar_strnstr(const char *buf, int buf_len, const char *search, int search_len) /* {{{ */
1529 {
1530         const char *c;
1531         int so_far = 0;
1532 
1533         if (buf_len < search_len) {
1534                 return NULL;
1535         }
1536 
1537         c = buf - 1;
1538 
1539         do {
1540                 if (!(c = memchr(c + 1, search[0], buf_len - search_len - so_far))) {
1541                         return (char *) NULL;
1542                 }
1543 
1544                 so_far = c - buf;
1545 
1546                 if (so_far >= (buf_len - search_len)) {
1547                         return (char *) NULL;
1548                 }
1549 
1550                 if (!memcmp(c, search, search_len)) {
1551                         return (char *) c;
1552                 }
1553         } while (1);
1554 }
1555 /* }}} */
1556 
1557 /**
1558  * Scan an open fp for the required __HALT_COMPILER(); ?> token and verify
1559  * that the manifest is proper, then pass it to phar_parse_pharfile().  SUCCESS
1560  * or FAILURE is returned and pphar is set to a pointer to the phar's manifest
1561  */
1562 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) /* {{{ */
1563 {
1564         const char token[] = "__HALT_COMPILER();";
1565         const char zip_magic[] = "PK\x03\x04";
1566         const char gz_magic[] = "\x1f\x8b\x08";
1567         const char bz_magic[] = "BZh";
1568         char *pos, test = '\0';
1569         const int window_size = 1024;
1570         char buffer[1024 + sizeof(token)]; /* a 1024 byte window + the size of the halt_compiler token (moving window) */
1571         const long readsize = sizeof(buffer) - sizeof(token);
1572         const long tokenlen = sizeof(token) - 1;
1573         long halt_offset;
1574         size_t got;
1575         php_uint32 compression = PHAR_FILE_COMPRESSED_NONE;
1576 
1577         if (error) {
1578                 *error = NULL;
1579         }
1580 
1581         if (-1 == php_stream_rewind(fp)) {
1582                 MAPPHAR_ALLOC_FAIL("cannot rewind phar \"%s\"")
1583         }
1584 
1585         buffer[sizeof(buffer)-1] = '\0';
1586         memset(buffer, 32, sizeof(token));
1587         halt_offset = 0;
1588 
1589         /* Maybe it's better to compile the file instead of just searching,  */
1590         /* but we only want the offset. So we want a .re scanner to find it. */
1591         while(!php_stream_eof(fp)) {
1592                 if ((got = php_stream_read(fp, buffer+tokenlen, readsize)) < (size_t) tokenlen) {
1593                         MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)")
1594                 }
1595 
1596                 if (!test) {
1597                         test = '\1';
1598                         pos = buffer+tokenlen;
1599                         if (!memcmp(pos, gz_magic, 3)) {
1600                                 char err = 0;
1601                                 php_stream_filter *filter;
1602                                 php_stream *temp;
1603                                 /* to properly decompress, we have to tell zlib to look for a zlib or gzip header */
1604                                 zval filterparams;
1605 
1606                                 if (!PHAR_G(has_zlib)) {
1607                                         MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file, enable zlib extension in php.ini")
1608                                 }
1609                                 array_init(&filterparams);
1610 /* this is defined in zlib's zconf.h */
1611 #ifndef MAX_WBITS
1612 #define MAX_WBITS 15
1613 #endif
1614                                 add_assoc_long(&filterparams, "window", MAX_WBITS + 32);
1615 
1616                                 /* entire file is gzip-compressed, uncompress to temporary file */
1617                                 if (!(temp = php_stream_fopen_tmpfile())) {
1618                                         MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of gzipped phar archive \"%s\"")
1619                                 }
1620 
1621                                 php_stream_rewind(fp);
1622                                 filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC);
1623 
1624                                 if (!filter) {
1625                                         err = 1;
1626                                         add_assoc_long(&filterparams, "window", MAX_WBITS);
1627                                         filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC);
1628                                         zval_dtor(&filterparams);
1629 
1630                                         if (!filter) {
1631                                                 php_stream_close(temp);
1632                                                 MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6")
1633                                         }
1634                                 } else {
1635                                         zval_dtor(&filterparams);
1636                                 }
1637 
1638                                 php_stream_filter_append(&temp->writefilters, filter);
1639 
1640                                 if (SUCCESS != php_stream_copy_to_stream_ex(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
1641                                         if (err) {
1642                                                 php_stream_close(temp);
1643                                                 MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6")
1644                                         }
1645                                         php_stream_close(temp);
1646                                         MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file")
1647                                 }
1648 
1649                                 php_stream_filter_flush(filter, 1);
1650                                 php_stream_filter_remove(filter, 1 TSRMLS_CC);
1651                                 php_stream_close(fp);
1652                                 fp = temp;
1653                                 php_stream_rewind(fp);
1654                                 compression = PHAR_FILE_COMPRESSED_GZ;
1655 
1656                                 /* now, start over */
1657                                 test = '\0';
1658                                 continue;
1659                         } else if (!memcmp(pos, bz_magic, 3)) {
1660                                 php_stream_filter *filter;
1661                                 php_stream *temp;
1662 
1663                                 if (!PHAR_G(has_bz2)) {
1664                                         MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file, enable bz2 extension in php.ini")
1665                                 }
1666 
1667                                 /* entire file is bzip-compressed, uncompress to temporary file */
1668                                 if (!(temp = php_stream_fopen_tmpfile())) {
1669                                         MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of bzipped phar archive \"%s\"")
1670                                 }
1671 
1672                                 php_stream_rewind(fp);
1673                                 filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp) TSRMLS_CC);
1674 
1675                                 if (!filter) {
1676                                         php_stream_close(temp);
1677                                         MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\", filter creation failed")
1678                                 }
1679 
1680                                 php_stream_filter_append(&temp->writefilters, filter);
1681 
1682                                 if (SUCCESS != php_stream_copy_to_stream_ex(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
1683                                         php_stream_close(temp);
1684                                         MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file")
1685                                 }
1686 
1687                                 php_stream_filter_flush(filter, 1);
1688                                 php_stream_filter_remove(filter, 1 TSRMLS_CC);
1689                                 php_stream_close(fp);
1690                                 fp = temp;
1691                                 php_stream_rewind(fp);
1692                                 compression = PHAR_FILE_COMPRESSED_BZ2;
1693 
1694                                 /* now, start over */
1695                                 test = '\0';
1696                                 continue;
1697                         }
1698 
1699                         if (!memcmp(pos, zip_magic, 4)) {
1700                                 php_stream_seek(fp, 0, SEEK_END);
1701                                 return phar_parse_zipfile(fp, fname, fname_len, alias, alias_len, pphar, error TSRMLS_CC);
1702                         }
1703 
1704                         if (got > 512) {
1705                                 if (phar_is_tar(pos, fname)) {
1706                                         php_stream_rewind(fp);
1707                                         return phar_parse_tarfile(fp, fname, fname_len, alias, alias_len, pphar, is_data, compression, error TSRMLS_CC);
1708                                 }
1709                         }
1710                 }
1711 
1712                 if (got > 0 && (pos = phar_strnstr(buffer, got + sizeof(token), token, sizeof(token)-1)) != NULL) {
1713                         halt_offset += (pos - buffer); /* no -tokenlen+tokenlen here */
1714                         return phar_parse_pharfile(fp, fname, fname_len, alias, alias_len, halt_offset, pphar, compression, error TSRMLS_CC);
1715                 }
1716 
1717                 halt_offset += got;
1718                 memmove(buffer, buffer + window_size, tokenlen); /* move the memory buffer by the size of the window */
1719         }
1720 
1721         MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (__HALT_COMPILER(); not found)")
1722 }
1723 /* }}} */
1724 
1725 /*
1726  * given the location of the file extension and the start of the file path,
1727  * determine the end of the portion of the path (i.e. /path/to/file.ext/blah
1728  * grabs "/path/to/file.ext" as does the straight /path/to/file.ext),
1729  * stat it to determine if it exists.
1730  * if so, check to see if it is a directory and fail if so
1731  * if not, check to see if its dirname() exists (i.e. "/path/to") and is a directory
1732  * succeed if we are creating the file, otherwise fail.
1733  */
1734 static int phar_analyze_path(const char *fname, const char *ext, int ext_len, int for_create TSRMLS_DC) /* {{{ */
1735 {
1736         php_stream_statbuf ssb;
1737         char *realpath;
1738         char *filename = estrndup(fname, (ext - fname) + ext_len);
1739 
1740         if ((realpath = expand_filepath(filename, NULL TSRMLS_CC))) {
1741 #ifdef PHP_WIN32
1742                 phar_unixify_path_separators(realpath, strlen(realpath));
1743 #endif
1744                 if (zend_hash_exists(&(PHAR_GLOBALS->phar_fname_map), realpath, strlen(realpath))) {
1745                         efree(realpath);
1746                         efree(filename);
1747                         return SUCCESS;
1748                 }
1749 
1750                 if (PHAR_G(manifest_cached) && zend_hash_exists(&cached_phars, realpath, strlen(realpath))) {
1751                         efree(realpath);
1752                         efree(filename);
1753                         return SUCCESS;
1754                 }
1755                 efree(realpath);
1756         }
1757 
1758         if (SUCCESS == php_stream_stat_path((char *) filename, &ssb)) {
1759 
1760                 efree(filename);
1761 
1762                 if (ssb.sb.st_mode & S_IFDIR) {
1763                         return FAILURE;
1764                 }
1765 
1766                 if (for_create == 1) {
1767                         return FAILURE;
1768                 }
1769 
1770                 return SUCCESS;
1771         } else {
1772                 char *slash;
1773 
1774                 if (!for_create) {
1775                         efree(filename);
1776                         return FAILURE;
1777                 }
1778 
1779                 slash = (char *) strrchr(filename, '/');
1780 
1781                 if (slash) {
1782                         *slash = '\0';
1783                 }
1784 
1785                 if (SUCCESS != php_stream_stat_path((char *) filename, &ssb)) {
1786                         if (!slash) {
1787                                 if (!(realpath = expand_filepath(filename, NULL TSRMLS_CC))) {
1788                                         efree(filename);
1789                                         return FAILURE;
1790                                 }
1791 #ifdef PHP_WIN32
1792                                 phar_unixify_path_separators(realpath, strlen(realpath));
1793 #endif
1794                                 slash = strstr(realpath, filename);
1795                                 if (slash) {
1796                                         slash += ((ext - fname) + ext_len);
1797                                         *slash = '\0';
1798                                 }
1799                                 slash = strrchr(realpath, '/');
1800 
1801                                 if (slash) {
1802                                         *slash = '\0';
1803                                 } else {
1804                                         efree(realpath);
1805                                         efree(filename);
1806                                         return FAILURE;
1807                                 }
1808 
1809                                 if (SUCCESS != php_stream_stat_path(realpath, &ssb)) {
1810                                         efree(realpath);
1811                                         efree(filename);
1812                                         return FAILURE;
1813                                 }
1814 
1815                                 efree(realpath);
1816 
1817                                 if (ssb.sb.st_mode & S_IFDIR) {
1818                                         efree(filename);
1819                                         return SUCCESS;
1820                                 }
1821                         }
1822 
1823                         efree(filename);
1824                         return FAILURE;
1825                 }
1826 
1827                 efree(filename);
1828 
1829                 if (ssb.sb.st_mode & S_IFDIR) {
1830                         return SUCCESS;
1831                 }
1832 
1833                 return FAILURE;
1834         }
1835 }
1836 /* }}} */
1837 
1838 /* check for ".phar" in extension */
1839 static int phar_check_str(const char *fname, const char *ext_str, int ext_len, int executable, int for_create TSRMLS_DC) /* {{{ */
1840 {
1841         char test[51];
1842         const char *pos;
1843 
1844         if (ext_len >= 50) {
1845                 return FAILURE;
1846         }
1847 
1848         if (executable == 1) {
1849                 /* copy "." as well */
1850                 memcpy(test, ext_str - 1, ext_len + 1);
1851                 test[ext_len + 1] = '\0';
1852                 /* executable phars must contain ".phar" as a valid extension (phar://.pharmy/oops is invalid) */
1853                 /* (phar://hi/there/.phar/oops is also invalid) */
1854                 pos = strstr(test, ".phar");
1855 
1856                 if (pos && (*(pos - 1) != '/')
1857                                 && (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) {
1858                         return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC);
1859                 } else {
1860                         return FAILURE;
1861                 }
1862         }
1863 
1864         /* data phars need only contain a single non-"." to be valid */
1865         if (!executable) {
1866                 pos = strstr(ext_str, ".phar");
1867                 if (!(pos && (*(pos - 1) != '/')
1868                                         && (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) && *(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
1869                         return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC);
1870                 }
1871         } else {
1872                 if (*(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
1873                         return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC);
1874                 }
1875         }
1876 
1877         return FAILURE;
1878 }
1879 /* }}} */
1880 
1881 /*
1882  * if executable is 1, only returns SUCCESS if the extension is one of the tar/zip .phar extensions
1883  * if executable is 0, it returns SUCCESS only if the filename does *not* contain ".phar" anywhere, and treats
1884  * the first extension as the filename extension
1885  *
1886  * if an extension is found, it sets ext_str to the location of the file extension in filename,
1887  * and ext_len to the length of the extension.
1888  * for urls like "phar://alias/oops" it instead sets ext_len to -1 and returns FAILURE, which tells
1889  * the calling function to use "alias" as the phar alias
1890  *
1891  * the last parameter should be set to tell the thing to assume that filename is the full path, and only to check the
1892  * extension rules, not to iterate.
1893  */
1894 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) /* {{{ */
1895 {
1896         const char *pos, *slash;
1897 
1898         *ext_str = NULL;
1899         *ext_len = 0;
1900 
1901         if (!filename_len || filename_len == 1) {
1902                 return FAILURE;
1903         }
1904 
1905         phar_request_initialize(TSRMLS_C);
1906         /* first check for alias in first segment */
1907         pos = memchr(filename, '/', filename_len);
1908 
1909         if (pos && pos != filename) {
1910                 /* check for url like http:// or phar:// */
1911                 if (*(pos - 1) == ':' && (pos - filename) < filename_len - 1 && *(pos + 1) == '/') {
1912                         *ext_len = -2;
1913                         *ext_str = NULL;
1914                         return FAILURE;
1915                 }
1916                 if (zend_hash_exists(&(PHAR_GLOBALS->phar_alias_map), (char *) filename, pos - filename)) {
1917                         *ext_str = pos;
1918                         *ext_len = -1;
1919                         return FAILURE;
1920                 }
1921 
1922                 if (PHAR_G(manifest_cached) && zend_hash_exists(&cached_alias, (char *) filename, pos - filename)) {
1923                         *ext_str = pos;
1924                         *ext_len = -1;
1925                         return FAILURE;
1926                 }
1927         }
1928 
1929         if (zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map)) || PHAR_G(manifest_cached)) {
1930                 phar_archive_data **pphar;
1931 
1932                 if (is_complete) {
1933                         if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), (char *) filename, filename_len, (void **)&pphar)) {
1934                                 *ext_str = filename + (filename_len - (*pphar)->ext_len);
1935 woohoo:
1936                                 *ext_len = (*pphar)->ext_len;
1937 
1938                                 if (executable == 2) {
1939                                         return SUCCESS;
1940                                 }
1941 
1942                                 if (executable == 1 && !(*pphar)->is_data) {
1943                                         return SUCCESS;
1944                                 }
1945 
1946                                 if (!executable && (*pphar)->is_data) {
1947                                         return SUCCESS;
1948                                 }
1949 
1950                                 return FAILURE;
1951                         }
1952 
1953                         if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_find(&cached_phars, (char *) filename, filename_len, (void **)&pphar)) {
1954                                 *ext_str = filename + (filename_len - (*pphar)->ext_len);
1955                                 goto woohoo;
1956                         }
1957                 } else {
1958                         char *str_key;
1959                         uint keylen;
1960                         ulong unused;
1961 
1962                         for (zend_hash_internal_pointer_reset(&(PHAR_GLOBALS->phar_fname_map));
1963                                 HASH_KEY_NON_EXISTENT != zend_hash_get_current_key_ex(&(PHAR_GLOBALS->phar_fname_map), &str_key, &keylen, &unused, 0, NULL);
1964                                 zend_hash_move_forward(&(PHAR_GLOBALS->phar_fname_map))
1965                         ) {
1966                                 if (keylen > (uint) filename_len) {
1967                                         continue;
1968                                 }
1969 
1970                                 if (!memcmp(filename, str_key, keylen) && ((uint)filename_len == keylen
1971                                         || filename[keylen] == '/' || filename[keylen] == '\0')) {
1972                                         if (FAILURE == zend_hash_get_current_data(&(PHAR_GLOBALS->phar_fname_map), (void **) &pphar)) {
1973                                                 break;
1974                                         }
1975                                         *ext_str = filename + (keylen - (*pphar)->ext_len);
1976                                         goto woohoo;
1977                                 }
1978                         }
1979 
1980                         if (PHAR_G(manifest_cached)) {
1981                                 for (zend_hash_internal_pointer_reset(&cached_phars);
1982                                         HASH_KEY_NON_EXISTENT != zend_hash_get_current_key_ex(&cached_phars, &str_key, &keylen, &unused, 0, NULL);
1983                                         zend_hash_move_forward(&cached_phars)
1984                                 ) {
1985                                         if (keylen > (uint) filename_len) {
1986                                                 continue;
1987                                         }
1988 
1989                                         if (!memcmp(filename, str_key, keylen) && ((uint)filename_len == keylen
1990                                                 || filename[keylen] == '/' || filename[keylen] == '\0')) {
1991                                                 if (FAILURE == zend_hash_get_current_data(&cached_phars, (void **) &pphar)) {
1992                                                         break;
1993                                                 }
1994                                                 *ext_str = filename + (keylen - (*pphar)->ext_len);
1995                                                 goto woohoo;
1996                                         }
1997                                 }
1998                         }
1999                 }
2000         }
2001 
2002         pos = memchr(filename + 1, '.', filename_len);
2003 next_extension:
2004         if (!pos) {
2005                 return FAILURE;
2006         }
2007 
2008         while (pos != filename && (*(pos - 1) == '/' || *(pos - 1) == '\0')) {
2009                 pos = memchr(pos + 1, '.', filename_len - (pos - filename) + 1);
2010                 if (!pos) {
2011                         return FAILURE;
2012                 }
2013         }
2014 
2015         slash = memchr(pos, '/', filename_len - (pos - filename));
2016 
2017         if (!slash) {
2018                 /* this is a url like "phar://blah.phar" with no directory */
2019                 *ext_str = pos;
2020                 *ext_len = strlen(pos);
2021 
2022                 /* file extension must contain "phar" */
2023                 switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create TSRMLS_CC)) {
2024                         case SUCCESS:
2025                                 return SUCCESS;
2026                         case FAILURE:
2027                                 /* we are at the end of the string, so we fail */
2028                                 return FAILURE;
2029                 }
2030         }
2031 
2032         /* we've found an extension that ends at a directory separator */
2033         *ext_str = pos;
2034         *ext_len = slash - pos;
2035 
2036         switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create TSRMLS_CC)) {
2037                 case SUCCESS:
2038                         return SUCCESS;
2039                 case FAILURE:
2040                         /* look for more extensions */
2041                         pos = strchr(pos + 1, '.');
2042                         if (pos) {
2043                                 *ext_str = NULL;
2044                                 *ext_len = 0;
2045                         }
2046                         goto next_extension;
2047         }
2048 
2049         return FAILURE;
2050 }
2051 /* }}} */
2052 
2053 static int php_check_dots(const char *element, int n) /* {{{ */
2054 {
2055         for(n--; n >= 0; --n) {
2056                 if (element[n] != '.') {
2057                         return 1;
2058                 }
2059         }
2060         return 0;
2061 }
2062 /* }}} */
2063 
2064 #define IS_DIRECTORY_UP(element, len) \
2065         (len >= 2 && !php_check_dots(element, len))
2066 
2067 #define IS_DIRECTORY_CURRENT(element, len) \
2068         (len == 1 && element[0] == '.')
2069 
2070 #define IS_BACKSLASH(c) ((c) == '/')
2071 
2072 #ifdef COMPILE_DL_PHAR
2073 /* stupid-ass non-extern declaration in tsrm_strtok.h breaks dumbass MS compiler */
2074 static inline int in_character_class(char ch, const char *delim) /* {{{ */
2075 {
2076         while (*delim) {
2077                 if (*delim == ch) {
2078                         return 1;
2079                 }
2080                 ++delim;
2081         }
2082         return 0;
2083 }
2084 /* }}} */
2085 
2086 char *tsrm_strtok_r(char *s, const char *delim, char **last) /* {{{ */
2087 {
2088         char *token;
2089 
2090         if (s == NULL) {
2091                 s = *last;
2092         }
2093 
2094         while (*s && in_character_class(*s, delim)) {
2095                 ++s;
2096         }
2097 
2098         if (!*s) {
2099                 return NULL;
2100         }
2101 
2102         token = s;
2103 
2104         while (*s && !in_character_class(*s, delim)) {
2105                 ++s;
2106         }
2107 
2108         if (!*s) {
2109                 *last = s;
2110         } else {
2111                 *s = '\0';
2112                 *last = s + 1;
2113         }
2114 
2115         return token;
2116 }
2117 /* }}} */
2118 #endif
2119 
2120 /**
2121  * Remove .. and . references within a phar filename
2122  */
2123 char *phar_fix_filepath(char *path, int *new_len, int use_cwd TSRMLS_DC) /* {{{ */
2124 {
2125         char *newpath;
2126         int newpath_len;
2127         char *ptr;
2128         char *tok;
2129         int ptr_length, path_length = *new_len;
2130 
2131         if (PHAR_G(cwd_len) && use_cwd && path_length > 2 && path[0] == '.' && path[1] == '/') {
2132                 newpath_len = PHAR_G(cwd_len);
2133                 newpath = emalloc(strlen(path) + newpath_len + 1);
2134                 memcpy(newpath, PHAR_G(cwd), newpath_len);
2135         } else {
2136                 newpath = emalloc(strlen(path) + 2);
2137                 newpath[0] = '/';
2138                 newpath_len = 1;
2139         }
2140 
2141         ptr = path;
2142 
2143         if (*ptr == '/') {
2144                 ++ptr;
2145         }
2146 
2147         tok = ptr;
2148 
2149         do {
2150                 ptr = memchr(ptr, '/', path_length - (ptr - path));
2151         } while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
2152 
2153         if (!ptr && (path_length - (tok - path))) {
2154                 switch (path_length - (tok - path)) {
2155                         case 1:
2156                                 if (*tok == '.') {
2157                                         efree(path);
2158                                         *new_len = 1;
2159                                         efree(newpath);
2160                                         return estrndup("/", 1);
2161                                 }
2162                                 break;
2163                         case 2:
2164                                 if (tok[0] == '.' && tok[1] == '.') {
2165                                         efree(path);
2166                                         *new_len = 1;
2167                                         efree(newpath);
2168                                         return estrndup("/", 1);
2169                                 }
2170                 }
2171                 efree(newpath);
2172                 return path;
2173         }
2174 
2175         while (ptr) {
2176                 ptr_length = ptr - tok;
2177 last_time:
2178                 if (IS_DIRECTORY_UP(tok, ptr_length)) {
2179 #define PREVIOUS newpath[newpath_len - 1]
2180 
2181                         while (newpath_len > 1 && !IS_BACKSLASH(PREVIOUS)) {
2182                                 newpath_len--;
2183                         }
2184 
2185                         if (newpath[0] != '/') {
2186                                 newpath[newpath_len] = '\0';
2187                         } else if (newpath_len > 1) {
2188                                 --newpath_len;
2189                         }
2190                 } else if (!IS_DIRECTORY_CURRENT(tok, ptr_length)) {
2191                         if (newpath_len > 1) {
2192                                 newpath[newpath_len++] = '/';
2193                                 memcpy(newpath + newpath_len, tok, ptr_length+1);
2194                         } else {
2195                                 memcpy(newpath + newpath_len, tok, ptr_length+1);
2196                         }
2197 
2198                         newpath_len += ptr_length;
2199                 }
2200 
2201                 if (ptr == path + path_length) {
2202                         break;
2203                 }
2204 
2205                 tok = ++ptr;
2206 
2207                 do {
2208                         ptr = memchr(ptr, '/', path_length - (ptr - path));
2209                 } while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
2210 
2211                 if (!ptr && (path_length - (tok - path))) {
2212                         ptr_length = path_length - (tok - path);
2213                         ptr = path + path_length;
2214                         goto last_time;
2215                 }
2216         }
2217 
2218         efree(path);
2219         *new_len = newpath_len;
2220         newpath[newpath_len] = '\0';
2221         return erealloc(newpath, newpath_len + 1);
2222 }
2223 /* }}} */
2224 
2225 /**
2226  * Process a phar stream name, ensuring we can handle any of:
2227  *
2228  * - whatever.phar
2229  * - whatever.phar.gz
2230  * - whatever.phar.bz2
2231  * - whatever.phar.php
2232  *
2233  * Optionally the name might start with 'phar://'
2234  *
2235  * This is used by phar_parse_url()
2236  */
2237 int phar_split_fname(const char *filename, int filename_len, char **arch, int *arch_len, char **entry, int *entry_len, int executable, int for_create TSRMLS_DC) /* {{{ */
2238 {
2239         const char *ext_str;
2240 #ifdef PHP_WIN32
2241         char *save;
2242 #endif
2243         int ext_len;
2244 
2245         if (CHECK_NULL_PATH(filename, filename_len)) {
2246                 return FAILURE;
2247         }
2248 
2249         if (!strncasecmp(filename, "phar://", 7)) {
2250                 filename += 7;
2251                 filename_len -= 7;
2252         }
2253 
2254         ext_len = 0;
2255 #ifdef PHP_WIN32
2256         save = filename;
2257         filename = estrndup(filename, filename_len);
2258         phar_unixify_path_separators(filename, filename_len);
2259 #endif
2260         if (phar_detect_phar_fname_ext(filename, filename_len, &ext_str, &ext_len, executable, for_create, 0 TSRMLS_CC) == FAILURE) {
2261                 if (ext_len != -1) {
2262                         if (!ext_str) {
2263                                 /* no / detected, restore arch for error message */
2264 #ifdef PHP_WIN32
2265                                 *arch = save;
2266 #else
2267                                 *arch = filename;
2268 #endif
2269                         }
2270 
2271 #ifdef PHP_WIN32
2272                         efree(filename);
2273 #endif
2274                         return FAILURE;
2275                 }
2276 
2277                 ext_len = 0;
2278                 /* no extension detected - instead we are dealing with an alias */
2279         }
2280 
2281         *arch_len = ext_str - filename + ext_len;
2282         *arch = estrndup(filename, *arch_len);
2283 
2284         if (ext_str[ext_len]) {
2285                 *entry_len = filename_len - *arch_len;
2286                 *entry = estrndup(ext_str+ext_len, *entry_len);
2287 #ifdef PHP_WIN32
2288                 phar_unixify_path_separators(*entry, *entry_len);
2289 #endif
2290                 *entry = phar_fix_filepath(*entry, entry_len, 0 TSRMLS_CC);
2291         } else {
2292                 *entry_len = 1;
2293                 *entry = estrndup("/", 1);
2294         }
2295 
2296 #ifdef PHP_WIN32
2297         efree(filename);
2298 #endif
2299 
2300         return SUCCESS;
2301 }
2302 /* }}} */
2303 
2304 /**
2305  * Invoked when a user calls Phar::mapPhar() from within an executing .phar
2306  * to set up its manifest directly
2307  */
2308 int phar_open_executed_filename(char *alias, int alias_len, char **error TSRMLS_DC) /* {{{ */
2309 {
2310         char *fname;
2311         zval *halt_constant;
2312         php_stream *fp;
2313         int fname_len;
2314         char *actual = NULL;
2315         int ret;
2316 
2317         if (error) {
2318                 *error = NULL;
2319         }
2320 
2321         fname = (char*)zend_get_executed_filename(TSRMLS_C);
2322         fname_len = strlen(fname);
2323 
2324         if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, 0, REPORT_ERRORS, NULL, 0 TSRMLS_CC) == SUCCESS) {
2325                 return SUCCESS;
2326         }
2327 
2328         if (!strcmp(fname, "[no active file]")) {
2329                 if (error) {
2330                         spprintf(error, 0, "cannot initialize a phar outside of PHP execution");
2331                 }
2332                 return FAILURE;
2333         }
2334 
2335         MAKE_STD_ZVAL(halt_constant);
2336 
2337         if (0 == zend_get_constant("__COMPILER_HALT_OFFSET__", 24, halt_constant TSRMLS_CC)) {
2338                 FREE_ZVAL(halt_constant);
2339                 if (error) {
2340                         spprintf(error, 0, "__HALT_COMPILER(); must be declared in a phar");
2341                 }
2342                 return FAILURE;
2343         }
2344 
2345         FREE_ZVAL(halt_constant);
2346 
2347 #if PHP_API_VERSION < 20100412
2348         if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
2349                 return FAILURE;
2350         }
2351 #endif
2352 
2353         if (php_check_open_basedir(fname TSRMLS_CC)) {
2354                 return FAILURE;
2355         }
2356 
2357         fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, &actual);
2358 
2359         if (!fp) {
2360                 if (error) {
2361                         spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
2362                 }
2363                 if (actual) {
2364                         efree(actual);
2365                 }
2366                 return FAILURE;
2367         }
2368 
2369         if (actual) {
2370                 fname = actual;
2371                 fname_len = strlen(actual);
2372         }
2373 
2374         ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, REPORT_ERRORS, NULL, 0, error TSRMLS_CC);
2375 
2376         if (actual) {
2377                 efree(actual);
2378         }
2379 
2380         return ret;
2381 }
2382 /* }}} */
2383 
2384 /**
2385  * Validate the CRC32 of a file opened from within the phar
2386  */
2387 int phar_postprocess_file(phar_entry_data *idata, php_uint32 crc32, char **error, int process_zip TSRMLS_DC) /* {{{ */
2388 {
2389         php_uint32 crc = ~0;
2390         int len = idata->internal_file->uncompressed_filesize;
2391         php_stream *fp = idata->fp;
2392         phar_entry_info *entry = idata->internal_file;
2393 
2394         if (error) {
2395                 *error = NULL;
2396         }
2397 
2398         if (entry->is_zip && process_zip > 0) {
2399                 /* verify local file header */
2400                 phar_zip_file_header local;
2401                 phar_zip_data_desc desc;
2402 
2403                 if (SUCCESS != phar_open_archive_fp(idata->phar TSRMLS_CC)) {
2404                         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);
2405                         return FAILURE;
2406                 }
2407                 php_stream_seek(phar_get_entrypfp(idata->internal_file TSRMLS_CC), entry->header_offset, SEEK_SET);
2408 
2409                 if (sizeof(local) != php_stream_read(phar_get_entrypfp(idata->internal_file TSRMLS_CC), (char *) &local, sizeof(local))) {
2410 
2411                         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);
2412                         return FAILURE;
2413                 }
2414 
2415                 /* check for data descriptor */
2416                 if (((PHAR_ZIP_16(local.flags)) & 0x8) == 0x8) {
2417                         php_stream_seek(phar_get_entrypfp(idata->internal_file TSRMLS_CC),
2418                                         entry->header_offset + sizeof(local) +
2419                                         PHAR_ZIP_16(local.filename_len) +
2420                                         PHAR_ZIP_16(local.extra_len) +
2421                                         entry->compressed_filesize, SEEK_SET);
2422                         if (sizeof(desc) != php_stream_read(phar_get_entrypfp(idata->internal_file TSRMLS_CC),
2423                                                             (char *) &desc, sizeof(desc))) {
2424                                 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);
2425                                 return FAILURE;
2426                         }
2427                         if (desc.signature[0] == 'P' && desc.signature[1] == 'K') {
2428                                 memcpy(&(local.crc32), &(desc.crc32), 12);
2429                         } else {
2430                                 /* old data descriptors have no signature */
2431                                 memcpy(&(local.crc32), &desc, 12);
2432                         }
2433                 }
2434                 /* verify local header */
2435                 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)) {
2436                         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);
2437                         return FAILURE;
2438                 }
2439 
2440                 /* construct actual offset to file start - local extra_len can be different from central extra_len */
2441                 entry->offset = entry->offset_abs =
2442                         sizeof(local) + entry->header_offset + PHAR_ZIP_16(local.filename_len) + PHAR_ZIP_16(local.extra_len);
2443 
2444                 if (idata->zero && idata->zero != entry->offset_abs) {
2445                         idata->zero = entry->offset_abs;
2446                 }
2447         }
2448 
2449         if (process_zip == 1) {
2450                 return SUCCESS;
2451         }
2452 
2453         php_stream_seek(fp, idata->zero, SEEK_SET);
2454 
2455         while (len--) {
2456                 CRC32(crc, php_stream_getc(fp));
2457         }
2458 
2459         php_stream_seek(fp, idata->zero, SEEK_SET);
2460 
2461         if (~crc == crc32) {
2462                 entry->is_crc_checked = 1;
2463                 return SUCCESS;
2464         } else {
2465                 spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (crc32 mismatch on file \"%s\")", idata->phar->fname, entry->filename);
2466                 return FAILURE;
2467         }
2468 }
2469 /* }}} */
2470 
2471 static inline void phar_set_32(char *buffer, int var) /* {{{ */
2472 {
2473 #ifdef WORDS_BIGENDIAN
2474         *((buffer) + 3) = (unsigned char) (((var) >> 24) & 0xFF);
2475         *((buffer) + 2) = (unsigned char) (((var) >> 16) & 0xFF);
2476         *((buffer) + 1) = (unsigned char) (((var) >> 8) & 0xFF);
2477         *((buffer) + 0) = (unsigned char) ((var) & 0xFF);
2478 #else
2479          memcpy(buffer, &var, sizeof(var));
2480 #endif
2481 } /* }}} */
2482 
2483 static int phar_flush_clean_deleted_apply(void *data TSRMLS_DC) /* {{{ */
2484 {
2485         phar_entry_info *entry = (phar_entry_info *)data;
2486 
2487         if (entry->fp_refcount <= 0 && entry->is_deleted) {
2488                 return ZEND_HASH_APPLY_REMOVE;
2489         } else {
2490                 return ZEND_HASH_APPLY_KEEP;
2491         }
2492 }
2493 /* }}} */
2494 
2495 #include "stub.h"
2496 
2497 char *phar_create_default_stub(const char *index_php, const char *web_index, size_t *len, char **error TSRMLS_DC) /* {{{ */
2498 {
2499         char *stub = NULL;
2500         int index_len, web_len;
2501         size_t dummy;
2502 
2503         if (!len) {
2504                 len = &dummy;
2505         }
2506 
2507         if (error) {
2508                 *error = NULL;
2509         }
2510 
2511         if (!index_php) {
2512                 index_php = "index.php";
2513         }
2514 
2515         if (!web_index) {
2516                 web_index = "index.php";
2517         }
2518 
2519         index_len = strlen(index_php);
2520         web_len = strlen(web_index);
2521 
2522         if (index_len > 400) {
2523                 /* ridiculous size not allowed for index.php startup filename */
2524                 if (error) {
2525                         spprintf(error, 0, "Illegal filename passed in for stub creation, was %d characters long, and only 400 or less is allowed", index_len);
2526                         return NULL;
2527                 }
2528         }
2529 
2530         if (web_len > 400) {
2531                 /* ridiculous size not allowed for index.php startup filename */
2532                 if (error) {
2533                         spprintf(error, 0, "Illegal web filename passed in for stub creation, was %d characters long, and only 400 or less is allowed", web_len);
2534                         return NULL;
2535                 }
2536         }
2537 
2538         phar_get_stub(index_php, web_index, len, &stub, index_len+1, web_len+1 TSRMLS_CC);
2539         return stub;
2540 }
2541 /* }}} */
2542 
2543 /**
2544  * Save phar contents to disk
2545  *
2546  * user_stub contains either a string, or a resource pointer, if len is a negative length.
2547  * user_stub and len should be both 0 if the default or existing stub should be used
2548  */
2549 int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, char **error TSRMLS_DC) /* {{{ */
2550 {
2551         char halt_stub[] = "__HALT_COMPILER();";
2552         char *newstub, *tmp;
2553         phar_entry_info *entry, *newentry;
2554         int halt_offset, restore_alias_len, global_flags = 0, closeoldfile;
2555         char *pos, has_dirs = 0;
2556         char manifest[18], entry_buffer[24];
2557         off_t manifest_ftell;
2558         long offset;
2559         size_t wrote;
2560         php_uint32 manifest_len, mytime, loc, new_manifest_count;
2561         php_uint32 newcrc32;
2562         php_stream *file, *oldfile, *newfile, *stubfile;
2563         php_stream_filter *filter;
2564         php_serialize_data_t metadata_hash;
2565         smart_str main_metadata_str = {0};
2566         int free_user_stub, free_fp = 1, free_ufp = 1;
2567         int manifest_hack = 0;
2568 
2569         if (phar->is_persistent) {
2570                 if (error) {
2571                         spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
2572                 }
2573                 return EOF;
2574         }
2575 
2576         if (error) {
2577                 *error = NULL;
2578         }
2579 
2580         if (!zend_hash_num_elements(&phar->manifest) && !user_stub) {
2581                 return EOF;
2582         }
2583 
2584         zend_hash_clean(&phar->virtual_dirs);
2585 
2586         if (phar->is_zip) {
2587                 return phar_zip_flush(phar, user_stub, len, convert, error TSRMLS_CC);
2588         }
2589 
2590         if (phar->is_tar) {
2591                 return phar_tar_flush(phar, user_stub, len, convert, error TSRMLS_CC);
2592         }
2593 
2594         if (PHAR_G(readonly)) {
2595                 return EOF;
2596         }
2597 
2598         if (phar->fp && !phar->is_brandnew) {
2599                 oldfile = phar->fp;
2600                 closeoldfile = 0;
2601                 php_stream_rewind(oldfile);
2602         } else {
2603                 oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
2604                 closeoldfile = oldfile != NULL;
2605         }
2606         newfile = php_stream_fopen_tmpfile();
2607         if (!newfile) {
2608                 if (error) {
2609                         spprintf(error, 0, "unable to create temporary file");
2610                 }
2611                 if (closeoldfile) {
2612                         php_stream_close(oldfile);
2613                 }
2614                 return EOF;
2615         }
2616 
2617         if (user_stub) {
2618                 if (len < 0) {
2619                         /* resource passed in */
2620                         if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) {
2621                                 if (closeoldfile) {
2622                                         php_stream_close(oldfile);
2623                                 }
2624                                 php_stream_close(newfile);
2625                                 if (error) {
2626                                         spprintf(error, 0, "unable to access resource to copy stub to new phar \"%s\"", phar->fname);
2627                                 }
2628                                 return EOF;
2629                         }
2630                         if (len == -1) {
2631                                 len = PHP_STREAM_COPY_ALL;
2632                         } else {
2633                                 len = -len;
2634                         }
2635                         user_stub = 0;
2636 
2637                         if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) {
2638                                 if (closeoldfile) {
2639                                         php_stream_close(oldfile);
2640                                 }
2641                                 php_stream_close(newfile);
2642                                 if (error) {
2643                                         spprintf(error, 0, "unable to read resource to copy stub to new phar \"%s\"", phar->fname);
2644                                 }
2645                                 return EOF;
2646                         }
2647                         free_user_stub = 1;
2648                 } else {
2649                         free_user_stub = 0;
2650                 }
2651                 tmp = estrndup(user_stub, len);
2652                 if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
2653                         efree(tmp);
2654                         if (closeoldfile) {
2655                                 php_stream_close(oldfile);
2656                         }
2657                         php_stream_close(newfile);
2658                         if (error) {
2659                                 spprintf(error, 0, "illegal stub for phar \"%s\"", phar->fname);
2660                         }
2661                         if (free_user_stub) {
2662                                 efree(user_stub);
2663                         }
2664                         return EOF;
2665                 }
2666                 pos = user_stub + (pos - tmp);
2667                 efree(tmp);
2668                 len = pos - user_stub + 18;
2669                 if ((size_t)len != php_stream_write(newfile, user_stub, len)
2670                 ||                        5 != php_stream_write(newfile, " ?>\r\n", 5)) {
2671                         if (closeoldfile) {
2672                                 php_stream_close(oldfile);
2673                         }
2674                         php_stream_close(newfile);
2675                         if (error) {
2676                                 spprintf(error, 0, "unable to create stub from string in new phar \"%s\"", phar->fname);
2677                         }
2678                         if (free_user_stub) {
2679                                 efree(user_stub);
2680                         }
2681                         return EOF;
2682                 }
2683                 phar->halt_offset = len + 5;
2684                 if (free_user_stub) {
2685                         efree(user_stub);
2686                 }
2687         } else {
2688                 size_t written;
2689 
2690                 if (!user_stub && phar->halt_offset && oldfile && !phar->is_brandnew) {
2691                         php_stream_copy_to_stream_ex(oldfile, newfile, phar->halt_offset, &written);
2692                         newstub = NULL;
2693                 } else {
2694                         /* this is either a brand new phar or a default stub overwrite */
2695                         newstub = phar_create_default_stub(NULL, NULL, &(phar->halt_offset), NULL TSRMLS_CC);
2696                         written = php_stream_write(newfile, newstub, phar->halt_offset);
2697                 }
2698                 if (phar->halt_offset != written) {
2699                         if (closeoldfile) {
2700                                 php_stream_close(oldfile);
2701                         }
2702                         php_stream_close(newfile);
2703                         if (error) {
2704                                 if (newstub) {
2705                                         spprintf(error, 0, "unable to create stub in new phar \"%s\"", phar->fname);
2706                                 } else {
2707                                         spprintf(error, 0, "unable to copy stub of old phar to new phar \"%s\"", phar->fname);
2708                                 }
2709                         }
2710                         if (newstub) {
2711                                 efree(newstub);
2712                         }
2713                         return EOF;
2714                 }
2715                 if (newstub) {
2716                         efree(newstub);
2717                 }
2718         }
2719         manifest_ftell = php_stream_tell(newfile);
2720         halt_offset = manifest_ftell;
2721 
2722         /* Check whether we can get rid of some of the deleted entries which are
2723          * unused. However some might still be in use so even after this clean-up
2724          * we need to skip entries marked is_deleted. */
2725         zend_hash_apply(&phar->manifest, phar_flush_clean_deleted_apply TSRMLS_CC);
2726 
2727         /* compress as necessary, calculate crcs, serialize meta-data, manifest size, and file sizes */
2728         main_metadata_str.c = 0;
2729         if (phar->metadata) {
2730                 PHP_VAR_SERIALIZE_INIT(metadata_hash);
2731                 php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash TSRMLS_CC);
2732                 PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2733         } else {
2734                 main_metadata_str.len = 0;
2735         }
2736         new_manifest_count = 0;
2737         offset = 0;
2738         for (zend_hash_internal_pointer_reset(&phar->manifest);
2739                 zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
2740                 zend_hash_move_forward(&phar->manifest)) {
2741                 if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) {
2742                         continue;
2743                 }
2744                 if (entry->cfp) {
2745                         /* did we forget to get rid of cfp last time? */
2746                         php_stream_close(entry->cfp);
2747                         entry->cfp = 0;
2748                 }
2749                 if (entry->is_deleted || entry->is_mounted) {
2750                         /* remove this from the new phar */
2751                         continue;
2752                 }
2753                 if (!entry->is_modified && entry->fp_refcount) {
2754                         /* open file pointers refer to this fp, do not free the stream */
2755                         switch (entry->fp_type) {
2756                                 case PHAR_FP:
2757                                         free_fp = 0;
2758                                         break;
2759                                 case PHAR_UFP:
2760                                         free_ufp = 0;
2761                                 default:
2762                                         break;
2763                         }
2764                 }
2765                 /* after excluding deleted files, calculate manifest size in bytes and number of entries */
2766                 ++new_manifest_count;
2767                 phar_add_virtual_dirs(phar, entry->filename, entry->filename_len TSRMLS_CC);
2768 
2769                 if (entry->is_dir) {
2770                         /* we use this to calculate API version, 1.1.1 is used for phars with directories */
2771                         has_dirs = 1;
2772                 }
2773                 if (entry->metadata) {
2774                         if (entry->metadata_str.c) {
2775                                 smart_str_free(&entry->metadata_str);
2776                         }
2777                         entry->metadata_str.c = 0;
2778                         entry->metadata_str.len = 0;
2779                         PHP_VAR_SERIALIZE_INIT(metadata_hash);
2780                         php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash TSRMLS_CC);
2781                         PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2782                 } else {
2783                         if (entry->metadata_str.c) {
2784                                 smart_str_free(&entry->metadata_str);
2785                         }
2786                         entry->metadata_str.c = 0;
2787                         entry->metadata_str.len = 0;
2788                 }
2789 
2790                 /* 32 bits for filename length, length of filename, manifest + metadata, and add 1 for trailing / if a directory */
2791                 offset += 4 + entry->filename_len + sizeof(entry_buffer) + entry->metadata_str.len + (entry->is_dir ? 1 : 0);
2792 
2793                 /* compress and rehash as necessary */
2794                 if ((oldfile && !entry->is_modified) || entry->is_dir) {
2795                         if (entry->fp_type == PHAR_UFP) {
2796                                 /* reset so we can copy the compressed data over */
2797                                 entry->fp_type = PHAR_FP;
2798                         }
2799                         continue;
2800                 }
2801                 if (!phar_get_efp(entry, 0 TSRMLS_CC)) {
2802                         /* re-open internal file pointer just-in-time */
2803                         newentry = phar_open_jit(phar, entry, error TSRMLS_CC);
2804                         if (!newentry) {
2805                                 /* major problem re-opening, so we ignore this file and the error */
2806                                 efree(*error);
2807                                 *error = NULL;
2808                                 continue;
2809                         }
2810                         entry = newentry;
2811                 }
2812                 file = phar_get_efp(entry, 0 TSRMLS_CC);
2813                 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 1 TSRMLS_CC)) {
2814                         if (closeoldfile) {
2815                                 php_stream_close(oldfile);
2816                         }
2817                         php_stream_close(newfile);
2818                         if (error) {
2819                                 spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2820                         }
2821                         return EOF;
2822                 }
2823                 newcrc32 = ~0;
2824                 mytime = entry->uncompressed_filesize;
2825                 for (loc = 0;loc < mytime; ++loc) {
2826                         CRC32(newcrc32, php_stream_getc(file));
2827                 }
2828                 entry->crc32 = ~newcrc32;
2829                 entry->is_crc_checked = 1;
2830                 if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
2831                         /* not compressed */
2832                         entry->compressed_filesize = entry->uncompressed_filesize;
2833                         continue;
2834                 }
2835                 filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0 TSRMLS_CC);
2836                 if (!filter) {
2837                         if (closeoldfile) {
2838                                 php_stream_close(oldfile);
2839                         }
2840                         php_stream_close(newfile);
2841                         if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
2842                                 if (error) {
2843                                         spprintf(error, 0, "unable to gzip compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2844                                 }
2845                         } else {
2846                                 if (error) {
2847                                         spprintf(error, 0, "unable to bzip2 compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2848                                 }
2849                         }
2850                         return EOF;
2851                 }
2852 
2853                 /* create new file that holds the compressed version */
2854                 /* work around inability to specify freedom in write and strictness
2855                 in read count */
2856                 entry->cfp = php_stream_fopen_tmpfile();
2857                 if (!entry->cfp) {
2858                         if (error) {
2859                                 spprintf(error, 0, "unable to create temporary file");
2860                         }
2861                         if (closeoldfile) {
2862                                 php_stream_close(oldfile);
2863                         }
2864                         php_stream_close(newfile);
2865                         return EOF;
2866                 }
2867                 php_stream_flush(file);
2868                 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
2869                         if (closeoldfile) {
2870                                 php_stream_close(oldfile);
2871                         }
2872                         php_stream_close(newfile);
2873                         if (error) {
2874                                 spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2875                         }
2876                         return EOF;
2877                 }
2878                 php_stream_filter_append((&entry->cfp->writefilters), filter);
2879                 if (SUCCESS != php_stream_copy_to_stream_ex(file, entry->cfp, entry->uncompressed_filesize, NULL)) {
2880                         if (closeoldfile) {
2881                                 php_stream_close(oldfile);
2882                         }
2883                         php_stream_close(newfile);
2884                         if (error) {
2885                                 spprintf(error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2886                         }
2887                         return EOF;
2888                 }
2889                 php_stream_filter_flush(filter, 1);
2890                 php_stream_flush(entry->cfp);
2891                 php_stream_filter_remove(filter, 1 TSRMLS_CC);
2892                 php_stream_seek(entry->cfp, 0, SEEK_END);
2893                 entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
2894                 /* generate crc on compressed file */
2895                 php_stream_rewind(entry->cfp);
2896                 entry->old_flags = entry->flags;
2897                 entry->is_modified = 1;
2898                 global_flags |= (entry->flags & PHAR_ENT_COMPRESSION_MASK);
2899         }
2900         global_flags |= PHAR_HDR_SIGNATURE;
2901 
2902         /* write out manifest pre-header */
2903         /*  4: manifest length
2904          *  4: manifest entry count
2905          *  2: phar version
2906          *  4: phar global flags
2907          *  4: alias length
2908          *  ?: the alias itself
2909          *  4: phar metadata length
2910          *  ?: phar metadata
2911          */
2912         restore_alias_len = phar->alias_len;
2913         if (phar->is_temporary_alias) {
2914                 phar->alias_len = 0;
2915         }
2916 
2917         manifest_len = offset + phar->alias_len + sizeof(manifest) + main_metadata_str.len;
2918         phar_set_32(manifest, manifest_len);
2919         /* Hack - see bug #65028, add padding byte to the end of the manifest */
2920         if(manifest[0] == '\r' || manifest[0] == '\n') {
2921                 manifest_len++;
2922                 phar_set_32(manifest, manifest_len);
2923                 manifest_hack = 1;
2924         }
2925         phar_set_32(manifest+4, new_manifest_count);
2926         if (has_dirs) {
2927                 *(manifest + 8) = (unsigned char) (((PHAR_API_VERSION) >> 8) & 0xFF);
2928                 *(manifest + 9) = (unsigned char) (((PHAR_API_VERSION) & 0xF0));
2929         } else {
2930                 *(manifest + 8) = (unsigned char) (((PHAR_API_VERSION_NODIR) >> 8) & 0xFF);
2931                 *(manifest + 9) = (unsigned char) (((PHAR_API_VERSION_NODIR) & 0xF0));
2932         }
2933         phar_set_32(manifest+10, global_flags);
2934         phar_set_32(manifest+14, phar->alias_len);
2935 
2936         /* write the manifest header */
2937         if (sizeof(manifest) != php_stream_write(newfile, manifest, sizeof(manifest))
2938         || (size_t)phar->alias_len != php_stream_write(newfile, phar->alias, phar->alias_len)) {
2939 
2940                 if (closeoldfile) {
2941                         php_stream_close(oldfile);
2942                 }
2943 
2944                 php_stream_close(newfile);
2945                 phar->alias_len = restore_alias_len;
2946 
2947                 if (error) {
2948                         spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", phar->fname);
2949                 }
2950 
2951                 return EOF;
2952         }
2953 
2954         phar->alias_len = restore_alias_len;
2955 
2956         phar_set_32(manifest, main_metadata_str.len);
2957         if (4 != php_stream_write(newfile, manifest, 4) || (main_metadata_str.len
2958         && main_metadata_str.len != php_stream_write(newfile, main_metadata_str.c, main_metadata_str.len))) {
2959                 smart_str_free(&main_metadata_str);
2960 
2961                 if (closeoldfile) {
2962                         php_stream_close(oldfile);
2963                 }
2964 
2965                 php_stream_close(newfile);
2966                 phar->alias_len = restore_alias_len;
2967 
2968                 if (error) {
2969                         spprintf(error, 0, "unable to write manifest meta-data of new phar \"%s\"", phar->fname);
2970                 }
2971 
2972                 return EOF;
2973         }
2974         smart_str_free(&main_metadata_str);
2975 
2976         /* re-calculate the manifest location to simplify later code */
2977         manifest_ftell = php_stream_tell(newfile);
2978 
2979         /* now write the manifest */
2980         for (zend_hash_internal_pointer_reset(&phar->manifest);
2981                 zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
2982                 zend_hash_move_forward(&phar->manifest)) {
2983 
2984                 if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) {
2985                         continue;
2986                 }
2987 
2988                 if (entry->is_deleted || entry->is_mounted) {
2989                         /* remove this from the new phar if deleted, ignore if mounted */
2990                         continue;
2991                 }
2992 
2993                 if (entry->is_dir) {
2994                         /* add 1 for trailing slash */
2995                         phar_set_32(entry_buffer, entry->filename_len + 1);
2996                 } else {
2997                         phar_set_32(entry_buffer, entry->filename_len);
2998                 }
2999 
3000                 if (4 != php_stream_write(newfile, entry_buffer, 4)
3001                 || entry->filename_len != php_stream_write(newfile, entry->filename, entry->filename_len)
3002                 || (entry->is_dir && 1 != php_stream_write(newfile, "/", 1))) {
3003                         if (closeoldfile) {
3004                                 php_stream_close(oldfile);
3005                         }
3006                         php_stream_close(newfile);
3007                         if (error) {
3008                                 if (entry->is_dir) {
3009                                         spprintf(error, 0, "unable to write filename of directory \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
3010                                 } else {
3011                                         spprintf(error, 0, "unable to write filename of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
3012                                 }
3013                         }
3014                         return EOF;
3015                 }
3016 
3017                 /* set the manifest meta-data:
3018                         4: uncompressed filesize
3019                         4: creation timestamp
3020                         4: compressed filesize
3021                         4: crc32
3022                         4: flags
3023                         4: metadata-len
3024                         +: metadata
3025                 */
3026                 mytime = time(NULL);
3027                 phar_set_32(entry_buffer, entry->uncompressed_filesize);
3028                 phar_set_32(entry_buffer+4, mytime);
3029                 phar_set_32(entry_buffer+8, entry->compressed_filesize);
3030                 phar_set_32(entry_buffer+12, entry->crc32);
3031                 phar_set_32(entry_buffer+16, entry->flags);
3032                 phar_set_32(entry_buffer+20, entry->metadata_str.len);
3033 
3034                 if (sizeof(entry_buffer) != php_stream_write(newfile, entry_buffer, sizeof(entry_buffer))
3035                 || entry->metadata_str.len != php_stream_write(newfile, entry->metadata_str.c, entry->metadata_str.len)) {
3036                         if (closeoldfile) {
3037                                 php_stream_close(oldfile);
3038                         }
3039 
3040                         php_stream_close(newfile);
3041 
3042                         if (error) {
3043                                 spprintf(error, 0, "unable to write temporary manifest of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
3044                         }
3045 
3046                         return EOF;
3047                 }
3048         }
3049         /* Hack - see bug #65028, add padding byte to the end of the manifest */
3050         if(manifest_hack) {
3051                 if(1 != php_stream_write(newfile, manifest, 1)) {
3052                         if (closeoldfile) {
3053                                 php_stream_close(oldfile);
3054                         }
3055 
3056                         php_stream_close(newfile);
3057 
3058                         if (error) {
3059                                 spprintf(error, 0, "unable to write manifest padding byte");
3060                         }
3061 
3062                         return EOF;
3063                 }
3064         }
3065 
3066         /* now copy the actual file data to the new phar */
3067         offset = php_stream_tell(newfile);
3068         for (zend_hash_internal_pointer_reset(&phar->manifest);
3069                 zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
3070                 zend_hash_move_forward(&phar->manifest)) {
3071 
3072                 if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) {
3073                         continue;
3074                 }
3075 
3076                 if (entry->is_deleted || entry->is_dir || entry->is_mounted) {
3077                         continue;
3078                 }
3079 
3080                 if (entry->cfp) {
3081                         file = entry->cfp;
3082                         php_stream_rewind(file);
3083                 } else {
3084                         file = phar_get_efp(entry, 0 TSRMLS_CC);
3085                         if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
3086                                 if (closeoldfile) {
3087                                         php_stream_close(oldfile);
3088                                 }
3089                                 php_stream_close(newfile);
3090                                 if (error) {
3091                                         spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3092                                 }
3093                                 return EOF;
3094                         }
3095                 }
3096 
3097                 if (!file) {
3098                         if (closeoldfile) {
3099                                 php_stream_close(oldfile);
3100                         }
3101                         php_stream_close(newfile);
3102                         if (error) {
3103                                 spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3104                         }
3105                         return EOF;
3106                 }
3107 
3108                 /* this will have changed for all files that have either changed compression or been modified */
3109                 entry->offset = entry->offset_abs = offset;
3110                 offset += entry->compressed_filesize;
3111                 if (php_stream_copy_to_stream_ex(file, newfile, entry->compressed_filesize, &wrote) == FAILURE) {
3112                         if (closeoldfile) {
3113                                 php_stream_close(oldfile);
3114                         }
3115 
3116                         php_stream_close(newfile);
3117 
3118                         if (error) {
3119                                 spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
3120                         }
3121 
3122                         return EOF;
3123                 }
3124 
3125                 entry->is_modified = 0;
3126 
3127                 if (entry->cfp) {
3128                         php_stream_close(entry->cfp);
3129                         entry->cfp = NULL;
3130                 }
3131 
3132                 if (entry->fp_type == PHAR_MOD) {
3133                         /* 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 */
3134                         if (entry->fp_refcount == 0 && entry->fp != phar->fp && entry->fp != phar->ufp) {
3135                                 php_stream_close(entry->fp);
3136                         }
3137 
3138                         entry->fp = NULL;
3139                         entry->fp_type = PHAR_FP;
3140                 } else if (entry->fp_type == PHAR_UFP) {
3141                         entry->fp_type = PHAR_FP;
3142                 }
3143         }
3144 
3145         /* append signature */
3146         if (global_flags & PHAR_HDR_SIGNATURE) {
3147                 char sig_buf[4];
3148 
3149                 php_stream_rewind(newfile);
3150 
3151                 if (phar->signature) {
3152                         efree(phar->signature);
3153                         phar->signature = NULL;
3154                 }
3155 
3156                 switch(phar->sig_flags) {
3157 #ifndef PHAR_HASH_OK
3158                         case PHAR_SIG_SHA512:
3159                         case PHAR_SIG_SHA256:
3160                                 if (closeoldfile) {
3161                                         php_stream_close(oldfile);
3162                                 }
3163                                 php_stream_close(newfile);
3164                                 if (error) {
3165                                         spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\" with requested hash type", entry->filename, phar->fname);
3166                                 }
3167                                 return EOF;
3168 #endif
3169                         default: {
3170                                 char *digest = NULL;
3171                                 int digest_len;
3172 
3173                                 if (FAILURE == phar_create_signature(phar, newfile, &digest, &digest_len, error TSRMLS_CC)) {
3174                                         if (error) {
3175                                                 char *save = *error;
3176                                                 spprintf(error, 0, "phar error: unable to write signature: %s", save);
3177                                                 efree(save);
3178                                         }
3179                                         if (digest) {
3180                                                 efree(digest);
3181                                         }
3182                                         if (closeoldfile) {
3183                                                 php_stream_close(oldfile);
3184                                         }
3185                                         php_stream_close(newfile);
3186                                         return EOF;
3187                                 }
3188 
3189                                 php_stream_write(newfile, digest, digest_len);
3190                                 efree(digest);
3191                                 if (phar->sig_flags == PHAR_SIG_OPENSSL) {
3192                                         phar_set_32(sig_buf, digest_len);
3193                                         php_stream_write(newfile, sig_buf, 4);
3194                                 }
3195                                 break;
3196                         }
3197                 }
3198                 phar_set_32(sig_buf, phar->sig_flags);
3199                 php_stream_write(newfile, sig_buf, 4);
3200                 php_stream_write(newfile, "GBMB", 4);
3201         }
3202 
3203         /* finally, close the temp file, rename the original phar,
3204            move the temp to the old phar, unlink the old phar, and reload it into memory
3205         */
3206         if (phar->fp && free_fp) {
3207                 php_stream_close(phar->fp);
3208         }
3209 
3210         if (phar->ufp) {
3211                 if (free_ufp) {
3212                         php_stream_close(phar->ufp);
3213                 }
3214                 phar->ufp = NULL;
3215         }
3216 
3217         if (closeoldfile) {
3218                 php_stream_close(oldfile);
3219         }
3220 
3221         phar->internal_file_start = halt_offset + manifest_len + 4;
3222         phar->halt_offset = halt_offset;
3223         phar->is_brandnew = 0;
3224 
3225         php_stream_rewind(newfile);
3226 
3227         if (phar->donotflush) {
3228                 /* deferred flush */
3229                 phar->fp = newfile;
3230         } else {
3231                 phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
3232                 if (!phar->fp) {
3233                         phar->fp = newfile;
3234                         if (error) {
3235                                 spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
3236                         }
3237                         return EOF;
3238                 }
3239 
3240                 if (phar->flags & PHAR_FILE_COMPRESSED_GZ) {
3241                         /* to properly compress, we have to tell zlib to add a zlib header */
3242                         zval filterparams;
3243 
3244                         array_init(&filterparams);
3245                         add_assoc_long(&filterparams, "window", MAX_WBITS+16);
3246                         filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp) TSRMLS_CC);
3247                         zval_dtor(&filterparams);
3248 
3249                         if (!filter) {
3250                                 if (error) {
3251                                         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);
3252                                 }
3253                                 return EOF;
3254                         }
3255 
3256                         php_stream_filter_append(&phar->fp->writefilters, filter);
3257                         php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3258                         php_stream_filter_flush(filter, 1);
3259                         php_stream_filter_remove(filter, 1 TSRMLS_CC);
3260                         php_stream_close(phar->fp);
3261                         /* use the temp stream as our base */
3262                         phar->fp = newfile;
3263                 } else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) {
3264                         filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp) TSRMLS_CC);
3265                         php_stream_filter_append(&phar->fp->writefilters, filter);
3266                         php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3267                         php_stream_filter_flush(filter, 1);
3268                         php_stream_filter_remove(filter, 1 TSRMLS_CC);
3269                         php_stream_close(phar->fp);
3270                         /* use the temp stream as our base */
3271                         phar->fp = newfile;
3272                 } else {
3273                         php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3274                         /* we could also reopen the file in "rb" mode but there is no need for that */
3275                         php_stream_close(newfile);
3276                 }
3277         }
3278 
3279         if (-1 == php_stream_seek(phar->fp, phar->halt_offset, SEEK_SET)) {
3280                 if (error) {
3281                         spprintf(error, 0, "unable to seek to __HALT_COMPILER(); in new phar \"%s\"", phar->fname);
3282                 }
3283                 return EOF;
3284         }
3285 
3286         return EOF;
3287 }
3288 /* }}} */
3289 
3290 #ifdef COMPILE_DL_PHAR
3291 ZEND_GET_MODULE(phar)
3292 #endif
3293 
3294 /* {{{ phar_functions[]
3295  *
3296  * Every user visible function must have an entry in phar_functions[].
3297  */
3298 zend_function_entry phar_functions[] = {
3299         PHP_FE_END
3300 };
3301 /* }}}*/
3302 
3303 static size_t phar_zend_stream_reader(void *handle, char *buf, size_t len TSRMLS_DC) /* {{{ */
3304 {
3305         return php_stream_read(phar_get_pharfp((phar_archive_data*)handle TSRMLS_CC), buf, len);
3306 }
3307 /* }}} */
3308 
3309 static size_t phar_zend_stream_fsizer(void *handle TSRMLS_DC) /* {{{ */
3310 {
3311         return ((phar_archive_data*)handle)->halt_offset + 32;
3312 } /* }}} */
3313 
3314 zend_op_array *(*phar_orig_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);
3315 #define phar_orig_zend_open zend_stream_open_function
3316 
3317 static char *phar_resolve_path(const char *filename, int filename_len TSRMLS_DC)
3318 {
3319         return phar_find_in_include_path((char *) filename, filename_len, NULL TSRMLS_CC);
3320 }
3321 
3322 static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type TSRMLS_DC) /* {{{ */
3323 {
3324         zend_op_array *res;
3325         char *name = NULL;
3326         int failed;
3327         phar_archive_data *phar;
3328 
3329         if (!file_handle || !file_handle->filename) {
3330                 return phar_orig_compile_file(file_handle, type TSRMLS_CC);
3331         }
3332         if (strstr(file_handle->filename, ".phar") && !strstr(file_handle->filename, "://")) {
3333                 if (SUCCESS == phar_open_from_filename((char*)file_handle->filename, strlen(file_handle->filename), NULL, 0, 0, &phar, NULL TSRMLS_CC)) {
3334                         if (phar->is_zip || phar->is_tar) {
3335                                 zend_file_handle f = *file_handle;
3336 
3337                                 /* zip or tar-based phar */
3338                                 spprintf(&name, 4096, "phar://%s/%s", file_handle->filename, ".phar/stub.php");
3339                                 if (SUCCESS == phar_orig_zend_open((const char *)name, file_handle TSRMLS_CC)) {
3340                                         efree(name);
3341                                         name = NULL;
3342                                         file_handle->filename = f.filename;
3343                                         if (file_handle->opened_path) {
3344                                                 efree(file_handle->opened_path);
3345                                         }
3346                                         file_handle->opened_path = f.opened_path;
3347                                         file_handle->free_filename = f.free_filename;
3348                                 } else {
3349                                         *file_handle = f;
3350                                 }
3351                         } else if (phar->flags & PHAR_FILE_COMPRESSION_MASK) {
3352                                 /* compressed phar */
3353                                 file_handle->type = ZEND_HANDLE_STREAM;
3354                                 /* we do our own reading directly from the phar, don't change the next line */
3355                                 file_handle->handle.stream.handle  = phar;
3356                                 file_handle->handle.stream.reader  = phar_zend_stream_reader;
3357                                 file_handle->handle.stream.closer  = NULL;
3358                                 file_handle->handle.stream.fsizer  = phar_zend_stream_fsizer;
3359                                 file_handle->handle.stream.isatty  = 0;
3360                                 phar->is_persistent ?
3361                                         php_stream_rewind(PHAR_GLOBALS->cached_fp[phar->phar_pos].fp) :
3362                                         php_stream_rewind(phar->fp);
3363                                 memset(&file_handle->handle.stream.mmap, 0, sizeof(file_handle->handle.stream.mmap));
3364                         }
3365                 }
3366         }
3367 
3368         zend_try {
3369                 failed = 0;
3370                 CG(zend_lineno) = 0;
3371                 res = phar_orig_compile_file(file_handle, type TSRMLS_CC);
3372         } zend_catch {
3373                 failed = 1;
3374                 res = NULL;
3375         } zend_end_try();
3376 
3377         if (name) {
3378                 efree(name);
3379         }
3380 
3381         if (failed) {
3382                 zend_bailout();
3383         }
3384 
3385         return res;
3386 }
3387 /* }}} */
3388 
3389 typedef zend_op_array* (zend_compile_t)(zend_file_handle*, int TSRMLS_DC);
3390 typedef zend_compile_t* (compile_hook)(zend_compile_t *ptr);
3391 
3392 PHP_GINIT_FUNCTION(phar) /* {{{ */
3393 {
3394         phar_mime_type mime;
3395 
3396         memset(phar_globals, 0, sizeof(zend_phar_globals));
3397         phar_globals->readonly = 1;
3398 
3399         zend_hash_init(&phar_globals->mime_types, 0, NULL, NULL, 1);
3400 
3401 #define PHAR_SET_MIME(mimetype, ret, fileext) \
3402                 mime.mime = mimetype; \
3403                 mime.len = sizeof((mimetype))+1; \
3404                 mime.type = ret; \
3405                 zend_hash_add(&phar_globals->mime_types, fileext, sizeof(fileext)-1, (void *)&mime, sizeof(phar_mime_type), NULL); \
3406 
3407         PHAR_SET_MIME("text/html", PHAR_MIME_PHPS, "phps")
3408         PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c")
3409         PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cc")
3410         PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cpp")
3411         PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c++")
3412         PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "dtd")
3413         PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "h")
3414         PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "log")
3415         PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "rng")
3416         PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "txt")
3417         PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "xsd")
3418         PHAR_SET_MIME("", PHAR_MIME_PHP, "php")
3419         PHAR_SET_MIME("", PHAR_MIME_PHP, "inc")
3420         PHAR_SET_MIME("video/avi", PHAR_MIME_OTHER, "avi")
3421         PHAR_SET_MIME("image/bmp", PHAR_MIME_OTHER, "bmp")
3422         PHAR_SET_MIME("text/css", PHAR_MIME_OTHER, "css")
3423         PHAR_SET_MIME("image/gif", PHAR_MIME_OTHER, "gif")
3424         PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htm")
3425         PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "html")
3426         PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htmls")
3427         PHAR_SET_MIME("image/x-ico", PHAR_MIME_OTHER, "ico")
3428         PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpe")
3429         PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpg")
3430         PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpeg")
3431         PHAR_SET_MIME("application/x-javascript", PHAR_MIME_OTHER, "js")
3432         PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "midi")
3433         PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "mid")
3434         PHAR_SET_MIME("audio/mod", PHAR_MIME_OTHER, "mod")
3435         PHAR_SET_MIME("movie/quicktime", PHAR_MIME_OTHER, "mov")
3436         PHAR_SET_MIME("audio/mp3", PHAR_MIME_OTHER, "mp3")
3437         PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpg")
3438         PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpeg")
3439         PHAR_SET_MIME("application/pdf", PHAR_MIME_OTHER, "pdf")
3440         PHAR_SET_MIME("image/png", PHAR_MIME_OTHER, "png")
3441         PHAR_SET_MIME("application/shockwave-flash", PHAR_MIME_OTHER, "swf")
3442         PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tif")
3443         PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tiff")
3444         PHAR_SET_MIME("audio/wav", PHAR_MIME_OTHER, "wav")
3445         PHAR_SET_MIME("image/xbm", PHAR_MIME_OTHER, "xbm")
3446         PHAR_SET_MIME("text/xml", PHAR_MIME_OTHER, "xml")
3447 
3448         phar_restore_orig_functions(TSRMLS_C);
3449 }
3450 /* }}} */
3451 
3452 PHP_GSHUTDOWN_FUNCTION(phar) /* {{{ */
3453 {
3454         zend_hash_destroy(&phar_globals->mime_types);
3455 }
3456 /* }}} */
3457 
3458 PHP_MINIT_FUNCTION(phar) /* {{{ */
3459 {
3460         REGISTER_INI_ENTRIES();
3461 
3462         phar_orig_compile_file = zend_compile_file;
3463         zend_compile_file = phar_compile_file;
3464 
3465         phar_save_resolve_path = zend_resolve_path;
3466         zend_resolve_path = phar_resolve_path;
3467 
3468         phar_object_init(TSRMLS_C);
3469 
3470         phar_intercept_functions_init(TSRMLS_C);
3471         phar_save_orig_functions(TSRMLS_C);
3472 
3473         return php_register_url_stream_wrapper("phar", &php_stream_phar_wrapper TSRMLS_CC);
3474 }
3475 /* }}} */
3476 
3477 PHP_MSHUTDOWN_FUNCTION(phar) /* {{{ */
3478 {
3479         php_unregister_url_stream_wrapper("phar" TSRMLS_CC);
3480 
3481         phar_intercept_functions_shutdown(TSRMLS_C);
3482 
3483         if (zend_compile_file == phar_compile_file) {
3484                 zend_compile_file = phar_orig_compile_file;
3485         }
3486 
3487         if (PHAR_G(manifest_cached)) {
3488                 zend_hash_destroy(&(cached_phars));
3489                 zend_hash_destroy(&(cached_alias));
3490         }
3491 
3492         return SUCCESS;
3493 }
3494 /* }}} */
3495 
3496 void phar_request_initialize(TSRMLS_D) /* {{{ */
3497 {
3498         if (!PHAR_GLOBALS->request_init)
3499         {
3500                 PHAR_G(last_phar) = NULL;
3501                 PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
3502                 PHAR_G(has_bz2) = zend_hash_exists(&module_registry, "bz2", sizeof("bz2"));
3503                 PHAR_G(has_zlib) = zend_hash_exists(&module_registry, "zlib", sizeof("zlib"));
3504                 PHAR_GLOBALS->request_init = 1;
3505                 PHAR_GLOBALS->request_ends = 0;
3506                 PHAR_GLOBALS->request_done = 0;
3507                 zend_hash_init(&(PHAR_GLOBALS->phar_fname_map), 5, zend_get_hash_value, destroy_phar_data,  0);
3508                 zend_hash_init(&(PHAR_GLOBALS->phar_persist_map), 5, zend_get_hash_value, NULL,  0);
3509                 zend_hash_init(&(PHAR_GLOBALS->phar_alias_map), 5, zend_get_hash_value, NULL, 0);
3510 
3511                 if (PHAR_G(manifest_cached)) {
3512                         phar_archive_data **pphar;
3513                         phar_entry_fp *stuff = (phar_entry_fp *) ecalloc(zend_hash_num_elements(&cached_phars), sizeof(phar_entry_fp));
3514 
3515                         for (zend_hash_internal_pointer_reset(&cached_phars);
3516                         zend_hash_get_current_data(&cached_phars, (void **)&pphar) == SUCCESS;
3517                         zend_hash_move_forward(&cached_phars)) {
3518                                 stuff[pphar[0]->phar_pos].manifest = (phar_entry_fp_info *) ecalloc( zend_hash_num_elements(&(pphar[0]->manifest)), sizeof(phar_entry_fp_info));
3519                         }
3520 
3521                         PHAR_GLOBALS->cached_fp = stuff;
3522                 }
3523 
3524                 PHAR_GLOBALS->phar_SERVER_mung_list = 0;
3525                 PHAR_G(cwd) = NULL;
3526                 PHAR_G(cwd_len) = 0;
3527                 PHAR_G(cwd_init) = 0;
3528         }
3529 }
3530 /* }}} */
3531 
3532 PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */
3533 {
3534         int i;
3535 
3536         PHAR_GLOBALS->request_ends = 1;
3537 
3538         if (PHAR_GLOBALS->request_init)
3539         {
3540                 phar_release_functions(TSRMLS_C);
3541                 zend_hash_destroy(&(PHAR_GLOBALS->phar_alias_map));
3542                 PHAR_GLOBALS->phar_alias_map.arBuckets = NULL;
3543                 zend_hash_destroy(&(PHAR_GLOBALS->phar_fname_map));
3544                 PHAR_GLOBALS->phar_fname_map.arBuckets = NULL;
3545                 zend_hash_destroy(&(PHAR_GLOBALS->phar_persist_map));
3546                 PHAR_GLOBALS->phar_persist_map.arBuckets = NULL;
3547                 PHAR_GLOBALS->phar_SERVER_mung_list = 0;
3548 
3549                 if (PHAR_GLOBALS->cached_fp) {
3550                         for (i = 0; i < zend_hash_num_elements(&cached_phars); ++i) {
3551                                 if (PHAR_GLOBALS->cached_fp[i].fp) {
3552                                         php_stream_close(PHAR_GLOBALS->cached_fp[i].fp);
3553                                 }
3554                                 if (PHAR_GLOBALS->cached_fp[i].ufp) {
3555                                         php_stream_close(PHAR_GLOBALS->cached_fp[i].ufp);
3556                                 }
3557                                 efree(PHAR_GLOBALS->cached_fp[i].manifest);
3558                         }
3559                         efree(PHAR_GLOBALS->cached_fp);
3560                         PHAR_GLOBALS->cached_fp = 0;
3561                 }
3562 
3563                 PHAR_GLOBALS->request_init = 0;
3564 
3565                 if (PHAR_G(cwd)) {
3566                         efree(PHAR_G(cwd));
3567                 }
3568 
3569                 PHAR_G(cwd) = NULL;
3570                 PHAR_G(cwd_len) = 0;
3571                 PHAR_G(cwd_init) = 0;
3572         }
3573 
3574         PHAR_GLOBALS->request_done = 1;
3575         return SUCCESS;
3576 }
3577 /* }}} */
3578 
3579 PHP_MINFO_FUNCTION(phar) /* {{{ */
3580 {
3581         phar_request_initialize(TSRMLS_C);
3582         php_info_print_table_start();
3583         php_info_print_table_header(2, "Phar: PHP Archive support", "enabled");
3584         php_info_print_table_row(2, "Phar EXT version", PHP_PHAR_VERSION);
3585         php_info_print_table_row(2, "Phar API version", PHP_PHAR_API_VERSION);
3586         php_info_print_table_row(2, "SVN revision", "$Id: 14b80e175ed4d5bf11c890e1f30ea8225935b16c $");
3587         php_info_print_table_row(2, "Phar-based phar archives", "enabled");
3588         php_info_print_table_row(2, "Tar-based phar archives", "enabled");
3589         php_info_print_table_row(2, "ZIP-based phar archives", "enabled");
3590 
3591         if (PHAR_G(has_zlib)) {
3592                 php_info_print_table_row(2, "gzip compression", "enabled");
3593         } else {
3594                 php_info_print_table_row(2, "gzip compression", "disabled (install ext/zlib)");
3595         }
3596 
3597         if (PHAR_G(has_bz2)) {
3598                 php_info_print_table_row(2, "bzip2 compression", "enabled");
3599         } else {
3600                 php_info_print_table_row(2, "bzip2 compression", "disabled (install pecl/bz2)");
3601         }
3602 #ifdef PHAR_HAVE_OPENSSL
3603         php_info_print_table_row(2, "Native OpenSSL support", "enabled");
3604 #else
3605         if (zend_hash_exists(&module_registry, "openssl", sizeof("openssl"))) {
3606                 php_info_print_table_row(2, "OpenSSL support", "enabled");
3607         } else {
3608                 php_info_print_table_row(2, "OpenSSL support", "disabled (install ext/openssl)");
3609         }
3610 #endif
3611         php_info_print_table_end();
3612 
3613         php_info_print_box_start(0);
3614         PUTS("Phar based on pear/PHP_Archive, original concept by Davey Shafik.");
3615         PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3616         PUTS("Phar fully realized by Gregory Beaver and Marcus Boerger.");
3617         PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3618         PUTS("Portions of tar implementation Copyright (c) 2003-2009 Tim Kientzle.");
3619         php_info_print_box_end();
3620 
3621         DISPLAY_INI_ENTRIES();
3622 }
3623 /* }}} */
3624 
3625 /* {{{ phar_module_entry
3626  */
3627 static const zend_module_dep phar_deps[] = {
3628         ZEND_MOD_OPTIONAL("apc")
3629         ZEND_MOD_OPTIONAL("bz2")
3630         ZEND_MOD_OPTIONAL("openssl")
3631         ZEND_MOD_OPTIONAL("zlib")
3632         ZEND_MOD_OPTIONAL("standard")
3633 #if defined(HAVE_HASH) && !defined(COMPILE_DL_HASH)
3634         ZEND_MOD_REQUIRED("hash")
3635 #endif
3636 #if HAVE_SPL
3637         ZEND_MOD_REQUIRED("spl")
3638 #endif
3639         ZEND_MOD_END
3640 };
3641 
3642 zend_module_entry phar_module_entry = {
3643         STANDARD_MODULE_HEADER_EX, NULL,
3644         phar_deps,
3645         "Phar",
3646         phar_functions,
3647         PHP_MINIT(phar),
3648         PHP_MSHUTDOWN(phar),
3649         NULL,
3650         PHP_RSHUTDOWN(phar),
3651         PHP_MINFO(phar),
3652         PHP_PHAR_VERSION,
3653         PHP_MODULE_GLOBALS(phar),   /* globals descriptor */
3654         PHP_GINIT(phar),            /* globals ctor */
3655         PHP_GSHUTDOWN(phar),        /* globals dtor */
3656         NULL,                       /* post deactivate */
3657         STANDARD_MODULE_PROPERTIES_EX
3658 };
3659 /* }}} */
3660 
3661 /*
3662  * Local variables:
3663  * tab-width: 4
3664  * c-basic-offset: 4
3665  * End:
3666  * vim600: noet sw=4 ts=4 fdm=marker
3667  * vim<600: noet sw=4 ts=4
3668  */

/* [<][>][^][v][top][bottom][index][help] */