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