Annotation of embedaddon/php/ext/phar/phar.c, revision 1.1
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: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>