Annotation of embedaddon/php/ext/phar/phar.c, revision 1.1.1.4
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | phar php single-file executable PHP extension |
4: +----------------------------------------------------------------------+
1.1.1.3 misho 5: | Copyright (c) 2005-2013 The PHP Group |
1.1 misho 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:
1.1.1.4 ! misho 20: /* $Id: c85687ef5cec51a63c41cffb9ec3972cb8c3f7f3 $ */
1.1 misho 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;
1.1.1.2 misho 1741: char *realpath;
1742: char *filename = estrndup(fname, (ext - fname) + ext_len);
1.1 misho 1743:
1.1.1.2 misho 1744: if ((realpath = expand_filepath(filename, NULL TSRMLS_CC))) {
1.1 misho 1745: #ifdef PHP_WIN32
1746: phar_unixify_path_separators(realpath, strlen(realpath));
1747: #endif
1748: if (zend_hash_exists(&(PHAR_GLOBALS->phar_fname_map), realpath, strlen(realpath))) {
1749: efree(realpath);
1.1.1.2 misho 1750: efree(filename);
1.1 misho 1751: return SUCCESS;
1752: }
1753:
1754: if (PHAR_G(manifest_cached) && zend_hash_exists(&cached_phars, realpath, strlen(realpath))) {
1755: efree(realpath);
1.1.1.2 misho 1756: efree(filename);
1.1 misho 1757: return SUCCESS;
1758: }
1759: efree(realpath);
1760: }
1761:
1.1.1.2 misho 1762: if (SUCCESS == php_stream_stat_path((char *) filename, &ssb)) {
1763:
1764: efree(filename);
1.1 misho 1765:
1766: if (ssb.sb.st_mode & S_IFDIR) {
1767: return FAILURE;
1768: }
1769:
1770: if (for_create == 1) {
1771: return FAILURE;
1772: }
1773:
1774: return SUCCESS;
1775: } else {
1776: char *slash;
1777:
1778: if (!for_create) {
1.1.1.2 misho 1779: efree(filename);
1.1 misho 1780: return FAILURE;
1781: }
1782:
1.1.1.2 misho 1783: slash = (char *) strrchr(filename, '/');
1.1 misho 1784:
1785: if (slash) {
1786: *slash = '\0';
1787: }
1788:
1.1.1.2 misho 1789: if (SUCCESS != php_stream_stat_path((char *) filename, &ssb)) {
1790: if (!slash) {
1791: if (!(realpath = expand_filepath(filename, NULL TSRMLS_CC))) {
1792: efree(filename);
1.1 misho 1793: return FAILURE;
1794: }
1795: #ifdef PHP_WIN32
1796: phar_unixify_path_separators(realpath, strlen(realpath));
1797: #endif
1.1.1.2 misho 1798: slash = strstr(realpath, filename) + ((ext - fname) + ext_len);
1799: *slash = '\0';
1.1 misho 1800: slash = strrchr(realpath, '/');
1801:
1802: if (slash) {
1803: *slash = '\0';
1804: } else {
1805: efree(realpath);
1.1.1.2 misho 1806: efree(filename);
1.1 misho 1807: return FAILURE;
1808: }
1809:
1810: if (SUCCESS != php_stream_stat_path(realpath, &ssb)) {
1811: efree(realpath);
1.1.1.2 misho 1812: efree(filename);
1.1 misho 1813: return FAILURE;
1814: }
1815:
1816: efree(realpath);
1817:
1818: if (ssb.sb.st_mode & S_IFDIR) {
1.1.1.2 misho 1819: efree(filename);
1.1 misho 1820: return SUCCESS;
1821: }
1822: }
1823:
1.1.1.2 misho 1824: efree(filename);
1.1 misho 1825: return FAILURE;
1826: }
1827:
1.1.1.2 misho 1828: efree(filename);
1.1 misho 1829:
1830: if (ssb.sb.st_mode & S_IFDIR) {
1831: return SUCCESS;
1832: }
1833:
1834: return FAILURE;
1835: }
1836: }
1837: /* }}} */
1838:
1839: /* check for ".phar" in extension */
1840: static int phar_check_str(const char *fname, const char *ext_str, int ext_len, int executable, int for_create TSRMLS_DC) /* {{{ */
1841: {
1842: char test[51];
1843: const char *pos;
1844:
1845: if (ext_len >= 50) {
1846: return FAILURE;
1847: }
1848:
1849: if (executable == 1) {
1850: /* copy "." as well */
1851: memcpy(test, ext_str - 1, ext_len + 1);
1852: test[ext_len + 1] = '\0';
1853: /* executable phars must contain ".phar" as a valid extension (phar://.pharmy/oops is invalid) */
1854: /* (phar://hi/there/.phar/oops is also invalid) */
1855: pos = strstr(test, ".phar");
1856:
1857: if (pos && (*(pos - 1) != '/')
1858: && (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) {
1859: return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC);
1860: } else {
1861: return FAILURE;
1862: }
1863: }
1864:
1865: /* data phars need only contain a single non-"." to be valid */
1866: if (!executable) {
1867: pos = strstr(ext_str, ".phar");
1868: if (!(pos && (*(pos - 1) != '/')
1869: && (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) && *(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
1870: return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC);
1871: }
1872: } else {
1873: if (*(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
1874: return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC);
1875: }
1876: }
1877:
1878: return FAILURE;
1879: }
1880: /* }}} */
1881:
1882: /*
1883: * if executable is 1, only returns SUCCESS if the extension is one of the tar/zip .phar extensions
1884: * if executable is 0, it returns SUCCESS only if the filename does *not* contain ".phar" anywhere, and treats
1885: * the first extension as the filename extension
1886: *
1887: * if an extension is found, it sets ext_str to the location of the file extension in filename,
1888: * and ext_len to the length of the extension.
1889: * for urls like "phar://alias/oops" it instead sets ext_len to -1 and returns FAILURE, which tells
1890: * the calling function to use "alias" as the phar alias
1891: *
1892: * the last parameter should be set to tell the thing to assume that filename is the full path, and only to check the
1893: * extension rules, not to iterate.
1894: */
1895: 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) /* {{{ */
1896: {
1897: const char *pos, *slash;
1898:
1899: *ext_str = NULL;
1900: *ext_len = 0;
1901:
1902: if (!filename_len || filename_len == 1) {
1903: return FAILURE;
1904: }
1905:
1906: phar_request_initialize(TSRMLS_C);
1907: /* first check for alias in first segment */
1908: pos = memchr(filename, '/', filename_len);
1909:
1910: if (pos && pos != filename) {
1911: /* check for url like http:// or phar:// */
1912: if (*(pos - 1) == ':' && (pos - filename) < filename_len - 1 && *(pos + 1) == '/') {
1913: *ext_len = -2;
1914: *ext_str = NULL;
1915: return FAILURE;
1916: }
1917: if (zend_hash_exists(&(PHAR_GLOBALS->phar_alias_map), (char *) filename, pos - filename)) {
1918: *ext_str = pos;
1919: *ext_len = -1;
1920: return FAILURE;
1921: }
1922:
1923: if (PHAR_G(manifest_cached) && zend_hash_exists(&cached_alias, (char *) filename, pos - filename)) {
1924: *ext_str = pos;
1925: *ext_len = -1;
1926: return FAILURE;
1927: }
1928: }
1929:
1930: if (zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map)) || PHAR_G(manifest_cached)) {
1931: phar_archive_data **pphar;
1932:
1933: if (is_complete) {
1934: if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), (char *) filename, filename_len, (void **)&pphar)) {
1935: *ext_str = filename + (filename_len - (*pphar)->ext_len);
1936: woohoo:
1937: *ext_len = (*pphar)->ext_len;
1938:
1939: if (executable == 2) {
1940: return SUCCESS;
1941: }
1942:
1943: if (executable == 1 && !(*pphar)->is_data) {
1944: return SUCCESS;
1945: }
1946:
1947: if (!executable && (*pphar)->is_data) {
1948: return SUCCESS;
1949: }
1950:
1951: return FAILURE;
1952: }
1953:
1954: if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_find(&cached_phars, (char *) filename, filename_len, (void **)&pphar)) {
1955: *ext_str = filename + (filename_len - (*pphar)->ext_len);
1956: goto woohoo;
1957: }
1958: } else {
1959: phar_zstr key;
1960: char *str_key;
1961: uint keylen;
1962: ulong unused;
1963:
1964: zend_hash_internal_pointer_reset(&(PHAR_GLOBALS->phar_fname_map));
1965:
1966: while (FAILURE != zend_hash_has_more_elements(&(PHAR_GLOBALS->phar_fname_map))) {
1967: if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(&(PHAR_GLOBALS->phar_fname_map), &key, &keylen, &unused, 0, NULL)) {
1968: break;
1969: }
1970:
1971: PHAR_STR(key, str_key);
1972:
1973: if (keylen > (uint) filename_len) {
1974: zend_hash_move_forward(&(PHAR_GLOBALS->phar_fname_map));
1975: PHAR_STR_FREE(str_key);
1976: continue;
1977: }
1978:
1979: if (!memcmp(filename, str_key, keylen) && ((uint)filename_len == keylen
1980: || filename[keylen] == '/' || filename[keylen] == '\0')) {
1981: PHAR_STR_FREE(str_key);
1982: if (FAILURE == zend_hash_get_current_data(&(PHAR_GLOBALS->phar_fname_map), (void **) &pphar)) {
1983: break;
1984: }
1985: *ext_str = filename + (keylen - (*pphar)->ext_len);
1986: goto woohoo;
1987: }
1988:
1989: PHAR_STR_FREE(str_key);
1990: zend_hash_move_forward(&(PHAR_GLOBALS->phar_fname_map));
1991: }
1992:
1993: if (PHAR_G(manifest_cached)) {
1994: zend_hash_internal_pointer_reset(&cached_phars);
1995:
1996: while (FAILURE != zend_hash_has_more_elements(&cached_phars)) {
1997: if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(&cached_phars, &key, &keylen, &unused, 0, NULL)) {
1998: break;
1999: }
2000:
2001: PHAR_STR(key, str_key);
2002:
2003: if (keylen > (uint) filename_len) {
2004: zend_hash_move_forward(&cached_phars);
2005: PHAR_STR_FREE(str_key);
2006: continue;
2007: }
2008:
2009: if (!memcmp(filename, str_key, keylen) && ((uint)filename_len == keylen
2010: || filename[keylen] == '/' || filename[keylen] == '\0')) {
2011: PHAR_STR_FREE(str_key);
2012: if (FAILURE == zend_hash_get_current_data(&cached_phars, (void **) &pphar)) {
2013: break;
2014: }
2015: *ext_str = filename + (keylen - (*pphar)->ext_len);
2016: goto woohoo;
2017: }
2018: PHAR_STR_FREE(str_key);
2019: zend_hash_move_forward(&cached_phars);
2020: }
2021: }
2022: }
2023: }
2024:
2025: pos = memchr(filename + 1, '.', filename_len);
2026: next_extension:
2027: if (!pos) {
2028: return FAILURE;
2029: }
2030:
2031: while (pos != filename && (*(pos - 1) == '/' || *(pos - 1) == '\0')) {
2032: pos = memchr(pos + 1, '.', filename_len - (pos - filename) + 1);
2033: if (!pos) {
2034: return FAILURE;
2035: }
2036: }
2037:
2038: slash = memchr(pos, '/', filename_len - (pos - filename));
2039:
2040: if (!slash) {
2041: /* this is a url like "phar://blah.phar" with no directory */
2042: *ext_str = pos;
2043: *ext_len = strlen(pos);
2044:
2045: /* file extension must contain "phar" */
2046: switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create TSRMLS_CC)) {
2047: case SUCCESS:
2048: return SUCCESS;
2049: case FAILURE:
2050: /* we are at the end of the string, so we fail */
2051: return FAILURE;
2052: }
2053: }
2054:
2055: /* we've found an extension that ends at a directory separator */
2056: *ext_str = pos;
2057: *ext_len = slash - pos;
2058:
2059: switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create TSRMLS_CC)) {
2060: case SUCCESS:
2061: return SUCCESS;
2062: case FAILURE:
2063: /* look for more extensions */
2064: pos = strchr(pos + 1, '.');
2065: if (pos) {
2066: *ext_str = NULL;
2067: *ext_len = 0;
2068: }
2069: goto next_extension;
2070: }
2071:
2072: return FAILURE;
2073: }
2074: /* }}} */
2075:
2076: static int php_check_dots(const char *element, int n) /* {{{ */
2077: {
2078: for(n--; n >= 0; --n) {
2079: if (element[n] != '.') {
2080: return 1;
2081: }
2082: }
2083: return 0;
2084: }
2085: /* }}} */
2086:
2087: #define IS_DIRECTORY_UP(element, len) \
2088: (len >= 2 && !php_check_dots(element, len))
2089:
2090: #define IS_DIRECTORY_CURRENT(element, len) \
2091: (len == 1 && element[0] == '.')
2092:
2093: #define IS_BACKSLASH(c) ((c) == '/')
2094:
2095: #ifdef COMPILE_DL_PHAR
2096: /* stupid-ass non-extern declaration in tsrm_strtok.h breaks dumbass MS compiler */
2097: static inline int in_character_class(char ch, const char *delim) /* {{{ */
2098: {
2099: while (*delim) {
2100: if (*delim == ch) {
2101: return 1;
2102: }
2103: ++delim;
2104: }
2105: return 0;
2106: }
2107: /* }}} */
2108:
2109: char *tsrm_strtok_r(char *s, const char *delim, char **last) /* {{{ */
2110: {
2111: char *token;
2112:
2113: if (s == NULL) {
2114: s = *last;
2115: }
2116:
2117: while (*s && in_character_class(*s, delim)) {
2118: ++s;
2119: }
2120:
2121: if (!*s) {
2122: return NULL;
2123: }
2124:
2125: token = s;
2126:
2127: while (*s && !in_character_class(*s, delim)) {
2128: ++s;
2129: }
2130:
2131: if (!*s) {
2132: *last = s;
2133: } else {
2134: *s = '\0';
2135: *last = s + 1;
2136: }
2137:
2138: return token;
2139: }
2140: /* }}} */
2141: #endif
2142:
2143: /**
2144: * Remove .. and . references within a phar filename
2145: */
2146: char *phar_fix_filepath(char *path, int *new_len, int use_cwd TSRMLS_DC) /* {{{ */
2147: {
2148: char newpath[MAXPATHLEN];
2149: int newpath_len;
2150: char *ptr;
2151: char *tok;
2152: int ptr_length, path_length = *new_len;
2153:
2154: if (PHAR_G(cwd_len) && use_cwd && path_length > 2 && path[0] == '.' && path[1] == '/') {
2155: newpath_len = PHAR_G(cwd_len);
2156: memcpy(newpath, PHAR_G(cwd), newpath_len);
2157: } else {
2158: newpath[0] = '/';
2159: newpath_len = 1;
2160: }
2161:
2162: ptr = path;
2163:
2164: if (*ptr == '/') {
2165: ++ptr;
2166: }
2167:
2168: tok = ptr;
2169:
2170: do {
2171: ptr = memchr(ptr, '/', path_length - (ptr - path));
2172: } while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
2173:
2174: if (!ptr && (path_length - (tok - path))) {
2175: switch (path_length - (tok - path)) {
2176: case 1:
2177: if (*tok == '.') {
2178: efree(path);
2179: *new_len = 1;
2180: return estrndup("/", 1);
2181: }
2182: break;
2183: case 2:
2184: if (tok[0] == '.' && tok[1] == '.') {
2185: efree(path);
2186: *new_len = 1;
2187: return estrndup("/", 1);
2188: }
2189: }
2190: return path;
2191: }
2192:
2193: while (ptr) {
2194: ptr_length = ptr - tok;
2195: last_time:
2196: if (IS_DIRECTORY_UP(tok, ptr_length)) {
2197: #define PREVIOUS newpath[newpath_len - 1]
2198:
2199: while (newpath_len > 1 && !IS_BACKSLASH(PREVIOUS)) {
2200: newpath_len--;
2201: }
2202:
2203: if (newpath[0] != '/') {
2204: newpath[newpath_len] = '\0';
2205: } else if (newpath_len > 1) {
2206: --newpath_len;
2207: }
2208: } else if (!IS_DIRECTORY_CURRENT(tok, ptr_length)) {
2209: if (newpath_len > 1) {
2210: newpath[newpath_len++] = '/';
2211: memcpy(newpath + newpath_len, tok, ptr_length+1);
2212: } else {
2213: memcpy(newpath + newpath_len, tok, ptr_length+1);
2214: }
2215:
2216: newpath_len += ptr_length;
2217: }
2218:
2219: if (ptr == path + path_length) {
2220: break;
2221: }
2222:
2223: tok = ++ptr;
2224:
2225: do {
2226: ptr = memchr(ptr, '/', path_length - (ptr - path));
2227: } while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
2228:
2229: if (!ptr && (path_length - (tok - path))) {
2230: ptr_length = path_length - (tok - path);
2231: ptr = path + path_length;
2232: goto last_time;
2233: }
2234: }
2235:
2236: efree(path);
2237: *new_len = newpath_len;
2238: return estrndup(newpath, newpath_len);
2239: }
2240: /* }}} */
2241:
2242: /**
2243: * Process a phar stream name, ensuring we can handle any of:
2244: *
2245: * - whatever.phar
2246: * - whatever.phar.gz
2247: * - whatever.phar.bz2
2248: * - whatever.phar.php
2249: *
2250: * Optionally the name might start with 'phar://'
2251: *
2252: * This is used by phar_parse_url()
2253: */
2254: 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) /* {{{ */
2255: {
2256: const char *ext_str;
2257: #ifdef PHP_WIN32
2258: char *save;
2259: #endif
2260: int ext_len, free_filename = 0;
2261:
2262: if (!strncasecmp(filename, "phar://", 7)) {
2263: filename += 7;
2264: filename_len -= 7;
2265: }
2266:
2267: ext_len = 0;
2268: #ifdef PHP_WIN32
2269: free_filename = 1;
2270: save = filename;
2271: filename = estrndup(filename, filename_len);
2272: phar_unixify_path_separators(filename, filename_len);
2273: #endif
2274: if (phar_detect_phar_fname_ext(filename, filename_len, &ext_str, &ext_len, executable, for_create, 0 TSRMLS_CC) == FAILURE) {
2275: if (ext_len != -1) {
2276: if (!ext_str) {
2277: /* no / detected, restore arch for error message */
2278: #ifdef PHP_WIN32
2279: *arch = save;
2280: #else
2281: *arch = filename;
2282: #endif
2283: }
2284:
2285: if (free_filename) {
2286: efree(filename);
2287: }
2288:
2289: return FAILURE;
2290: }
2291:
2292: ext_len = 0;
2293: /* no extension detected - instead we are dealing with an alias */
2294: }
2295:
2296: *arch_len = ext_str - filename + ext_len;
2297: *arch = estrndup(filename, *arch_len);
2298:
2299: if (ext_str[ext_len]) {
2300: *entry_len = filename_len - *arch_len;
2301: *entry = estrndup(ext_str+ext_len, *entry_len);
2302: #ifdef PHP_WIN32
2303: phar_unixify_path_separators(*entry, *entry_len);
2304: #endif
2305: *entry = phar_fix_filepath(*entry, entry_len, 0 TSRMLS_CC);
2306: } else {
2307: *entry_len = 1;
2308: *entry = estrndup("/", 1);
2309: }
2310:
2311: if (free_filename) {
2312: efree(filename);
2313: }
2314:
2315: return SUCCESS;
2316: }
2317: /* }}} */
2318:
2319: /**
2320: * Invoked when a user calls Phar::mapPhar() from within an executing .phar
2321: * to set up its manifest directly
2322: */
2323: int phar_open_executed_filename(char *alias, int alias_len, char **error TSRMLS_DC) /* {{{ */
2324: {
2325: char *fname;
2326: zval *halt_constant;
2327: php_stream *fp;
2328: int fname_len;
2329: char *actual = NULL;
2330: int ret;
2331:
2332: if (error) {
2333: *error = NULL;
2334: }
2335:
1.1.1.2 misho 2336: fname = (char*)zend_get_executed_filename(TSRMLS_C);
1.1 misho 2337: fname_len = strlen(fname);
2338:
2339: if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, 0, REPORT_ERRORS, NULL, 0 TSRMLS_CC) == SUCCESS) {
2340: return SUCCESS;
2341: }
2342:
2343: if (!strcmp(fname, "[no active file]")) {
2344: if (error) {
2345: spprintf(error, 0, "cannot initialize a phar outside of PHP execution");
2346: }
2347: return FAILURE;
2348: }
2349:
2350: MAKE_STD_ZVAL(halt_constant);
2351:
2352: if (0 == zend_get_constant("__COMPILER_HALT_OFFSET__", 24, halt_constant TSRMLS_CC)) {
2353: FREE_ZVAL(halt_constant);
2354: if (error) {
2355: spprintf(error, 0, "__HALT_COMPILER(); must be declared in a phar");
2356: }
2357: return FAILURE;
2358: }
2359:
2360: FREE_ZVAL(halt_constant);
2361:
2362: #if PHP_API_VERSION < 20100412
2363: if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
2364: return FAILURE;
2365: }
2366: #endif
2367:
2368: if (php_check_open_basedir(fname TSRMLS_CC)) {
2369: return FAILURE;
2370: }
2371:
2372: fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, &actual);
2373:
2374: if (!fp) {
2375: if (error) {
2376: spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
2377: }
2378: if (actual) {
2379: efree(actual);
2380: }
2381: return FAILURE;
2382: }
2383:
2384: if (actual) {
2385: fname = actual;
2386: fname_len = strlen(actual);
2387: }
2388:
2389: ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, REPORT_ERRORS, NULL, 0, error TSRMLS_CC);
2390:
2391: if (actual) {
2392: efree(actual);
2393: }
2394:
2395: return ret;
2396: }
2397: /* }}} */
2398:
2399: /**
2400: * Validate the CRC32 of a file opened from within the phar
2401: */
2402: int phar_postprocess_file(phar_entry_data *idata, php_uint32 crc32, char **error, int process_zip TSRMLS_DC) /* {{{ */
2403: {
2404: php_uint32 crc = ~0;
2405: int len = idata->internal_file->uncompressed_filesize;
2406: php_stream *fp = idata->fp;
2407: phar_entry_info *entry = idata->internal_file;
2408:
2409: if (error) {
2410: *error = NULL;
2411: }
2412:
2413: if (entry->is_zip && process_zip > 0) {
2414: /* verify local file header */
2415: phar_zip_file_header local;
2416: phar_zip_data_desc desc;
2417:
2418: if (SUCCESS != phar_open_archive_fp(idata->phar TSRMLS_CC)) {
2419: 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);
2420: return FAILURE;
2421: }
2422: php_stream_seek(phar_get_entrypfp(idata->internal_file TSRMLS_CC), entry->header_offset, SEEK_SET);
2423:
2424: if (sizeof(local) != php_stream_read(phar_get_entrypfp(idata->internal_file TSRMLS_CC), (char *) &local, sizeof(local))) {
2425:
2426: 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);
2427: return FAILURE;
2428: }
2429:
2430: /* check for data descriptor */
2431: if (((PHAR_ZIP_16(local.flags)) & 0x8) == 0x8) {
2432: php_stream_seek(phar_get_entrypfp(idata->internal_file TSRMLS_CC),
2433: entry->header_offset + sizeof(local) +
2434: PHAR_ZIP_16(local.filename_len) +
2435: PHAR_ZIP_16(local.extra_len) +
2436: entry->compressed_filesize, SEEK_SET);
2437: if (sizeof(desc) != php_stream_read(phar_get_entrypfp(idata->internal_file TSRMLS_CC),
2438: (char *) &desc, sizeof(desc))) {
2439: 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);
2440: return FAILURE;
2441: }
2442: if (desc.signature[0] == 'P' && desc.signature[1] == 'K') {
2443: memcpy(&(local.crc32), &(desc.crc32), 12);
2444: } else {
2445: /* old data descriptors have no signature */
2446: memcpy(&(local.crc32), &desc, 12);
2447: }
2448: }
2449: /* verify local header */
2450: 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)) {
2451: 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);
2452: return FAILURE;
2453: }
2454:
2455: /* construct actual offset to file start - local extra_len can be different from central extra_len */
2456: entry->offset = entry->offset_abs =
2457: sizeof(local) + entry->header_offset + PHAR_ZIP_16(local.filename_len) + PHAR_ZIP_16(local.extra_len);
2458:
2459: if (idata->zero && idata->zero != entry->offset_abs) {
2460: idata->zero = entry->offset_abs;
2461: }
2462: }
2463:
2464: if (process_zip == 1) {
2465: return SUCCESS;
2466: }
2467:
2468: php_stream_seek(fp, idata->zero, SEEK_SET);
2469:
2470: while (len--) {
2471: CRC32(crc, php_stream_getc(fp));
2472: }
2473:
2474: php_stream_seek(fp, idata->zero, SEEK_SET);
2475:
2476: if (~crc == crc32) {
2477: entry->is_crc_checked = 1;
2478: return SUCCESS;
2479: } else {
2480: spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (crc32 mismatch on file \"%s\")", idata->phar->fname, entry->filename);
2481: return FAILURE;
2482: }
2483: }
2484: /* }}} */
2485:
2486: static inline void phar_set_32(char *buffer, int var) /* {{{ */
2487: {
2488: #ifdef WORDS_BIGENDIAN
2489: *((buffer) + 3) = (unsigned char) (((var) >> 24) & 0xFF);
2490: *((buffer) + 2) = (unsigned char) (((var) >> 16) & 0xFF);
2491: *((buffer) + 1) = (unsigned char) (((var) >> 8) & 0xFF);
2492: *((buffer) + 0) = (unsigned char) ((var) & 0xFF);
2493: #else
2494: memcpy(buffer, &var, sizeof(var));
2495: #endif
2496: } /* }}} */
2497:
2498: static int phar_flush_clean_deleted_apply(void *data TSRMLS_DC) /* {{{ */
2499: {
2500: phar_entry_info *entry = (phar_entry_info *)data;
2501:
2502: if (entry->fp_refcount <= 0 && entry->is_deleted) {
2503: return ZEND_HASH_APPLY_REMOVE;
2504: } else {
2505: return ZEND_HASH_APPLY_KEEP;
2506: }
2507: }
2508: /* }}} */
2509:
2510: #include "stub.h"
2511:
2512: char *phar_create_default_stub(const char *index_php, const char *web_index, size_t *len, char **error TSRMLS_DC) /* {{{ */
2513: {
2514: char *stub = NULL;
2515: int index_len, web_len;
2516: size_t dummy;
2517:
2518: if (!len) {
2519: len = &dummy;
2520: }
2521:
2522: if (error) {
2523: *error = NULL;
2524: }
2525:
2526: if (!index_php) {
2527: index_php = "index.php";
2528: }
2529:
2530: if (!web_index) {
2531: web_index = "index.php";
2532: }
2533:
2534: index_len = strlen(index_php);
2535: web_len = strlen(web_index);
2536:
2537: if (index_len > 400) {
2538: /* ridiculous size not allowed for index.php startup filename */
2539: if (error) {
2540: spprintf(error, 0, "Illegal filename passed in for stub creation, was %d characters long, and only 400 or less is allowed", index_len);
2541: return NULL;
2542: }
2543: }
2544:
2545: if (web_len > 400) {
2546: /* ridiculous size not allowed for index.php startup filename */
2547: if (error) {
2548: spprintf(error, 0, "Illegal web filename passed in for stub creation, was %d characters long, and only 400 or less is allowed", web_len);
2549: return NULL;
2550: }
2551: }
2552:
2553: phar_get_stub(index_php, web_index, len, &stub, index_len+1, web_len+1 TSRMLS_CC);
2554: return stub;
2555: }
2556: /* }}} */
2557:
2558: /**
2559: * Save phar contents to disk
2560: *
2561: * user_stub contains either a string, or a resource pointer, if len is a negative length.
2562: * user_stub and len should be both 0 if the default or existing stub should be used
2563: */
2564: int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, char **error TSRMLS_DC) /* {{{ */
2565: {
2566: char halt_stub[] = "__HALT_COMPILER();";
2567: char *newstub, *tmp;
2568: phar_entry_info *entry, *newentry;
2569: int halt_offset, restore_alias_len, global_flags = 0, closeoldfile;
2570: char *pos, has_dirs = 0;
2571: char manifest[18], entry_buffer[24];
2572: off_t manifest_ftell;
2573: long offset;
2574: size_t wrote;
2575: php_uint32 manifest_len, mytime, loc, new_manifest_count;
2576: php_uint32 newcrc32;
2577: php_stream *file, *oldfile, *newfile, *stubfile;
2578: php_stream_filter *filter;
2579: php_serialize_data_t metadata_hash;
2580: smart_str main_metadata_str = {0};
2581: int free_user_stub, free_fp = 1, free_ufp = 1;
1.1.1.4 ! misho 2582: int manifest_hack = 0;
1.1 misho 2583:
2584: if (phar->is_persistent) {
2585: if (error) {
2586: spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
2587: }
2588: return EOF;
2589: }
2590:
2591: if (error) {
2592: *error = NULL;
2593: }
2594:
2595: if (!zend_hash_num_elements(&phar->manifest) && !user_stub) {
2596: return EOF;
2597: }
2598:
2599: zend_hash_clean(&phar->virtual_dirs);
2600:
2601: if (phar->is_zip) {
2602: return phar_zip_flush(phar, user_stub, len, convert, error TSRMLS_CC);
2603: }
2604:
2605: if (phar->is_tar) {
2606: return phar_tar_flush(phar, user_stub, len, convert, error TSRMLS_CC);
2607: }
2608:
2609: if (PHAR_G(readonly)) {
2610: return EOF;
2611: }
2612:
2613: if (phar->fp && !phar->is_brandnew) {
2614: oldfile = phar->fp;
2615: closeoldfile = 0;
2616: php_stream_rewind(oldfile);
2617: } else {
2618: oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
2619: closeoldfile = oldfile != NULL;
2620: }
2621: newfile = php_stream_fopen_tmpfile();
2622: if (!newfile) {
2623: if (error) {
2624: spprintf(error, 0, "unable to create temporary file");
2625: }
2626: if (closeoldfile) {
2627: php_stream_close(oldfile);
2628: }
2629: return EOF;
2630: }
2631:
2632: if (user_stub) {
2633: if (len < 0) {
2634: /* resource passed in */
2635: if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) {
2636: if (closeoldfile) {
2637: php_stream_close(oldfile);
2638: }
2639: php_stream_close(newfile);
2640: if (error) {
2641: spprintf(error, 0, "unable to access resource to copy stub to new phar \"%s\"", phar->fname);
2642: }
2643: return EOF;
2644: }
2645: if (len == -1) {
2646: len = PHP_STREAM_COPY_ALL;
2647: } else {
2648: len = -len;
2649: }
2650: user_stub = 0;
1.1.1.3 misho 2651:
1.1 misho 2652: if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) {
2653: if (closeoldfile) {
2654: php_stream_close(oldfile);
2655: }
2656: php_stream_close(newfile);
2657: if (error) {
2658: spprintf(error, 0, "unable to read resource to copy stub to new phar \"%s\"", phar->fname);
2659: }
2660: return EOF;
2661: }
2662: free_user_stub = 1;
2663: } else {
2664: free_user_stub = 0;
2665: }
2666: tmp = estrndup(user_stub, len);
2667: if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
2668: efree(tmp);
2669: if (closeoldfile) {
2670: php_stream_close(oldfile);
2671: }
2672: php_stream_close(newfile);
2673: if (error) {
2674: spprintf(error, 0, "illegal stub for phar \"%s\"", phar->fname);
2675: }
2676: if (free_user_stub) {
2677: efree(user_stub);
2678: }
2679: return EOF;
2680: }
2681: pos = user_stub + (pos - tmp);
2682: efree(tmp);
2683: len = pos - user_stub + 18;
2684: if ((size_t)len != php_stream_write(newfile, user_stub, len)
2685: || 5 != php_stream_write(newfile, " ?>\r\n", 5)) {
2686: if (closeoldfile) {
2687: php_stream_close(oldfile);
2688: }
2689: php_stream_close(newfile);
2690: if (error) {
2691: spprintf(error, 0, "unable to create stub from string in new phar \"%s\"", phar->fname);
2692: }
2693: if (free_user_stub) {
2694: efree(user_stub);
2695: }
2696: return EOF;
2697: }
2698: phar->halt_offset = len + 5;
2699: if (free_user_stub) {
2700: efree(user_stub);
2701: }
2702: } else {
2703: size_t written;
2704:
2705: if (!user_stub && phar->halt_offset && oldfile && !phar->is_brandnew) {
2706: phar_stream_copy_to_stream(oldfile, newfile, phar->halt_offset, &written);
2707: newstub = NULL;
2708: } else {
2709: /* this is either a brand new phar or a default stub overwrite */
2710: newstub = phar_create_default_stub(NULL, NULL, &(phar->halt_offset), NULL TSRMLS_CC);
2711: written = php_stream_write(newfile, newstub, phar->halt_offset);
2712: }
2713: if (phar->halt_offset != written) {
2714: if (closeoldfile) {
2715: php_stream_close(oldfile);
2716: }
2717: php_stream_close(newfile);
2718: if (error) {
2719: if (newstub) {
2720: spprintf(error, 0, "unable to create stub in new phar \"%s\"", phar->fname);
2721: } else {
2722: spprintf(error, 0, "unable to copy stub of old phar to new phar \"%s\"", phar->fname);
2723: }
2724: }
2725: if (newstub) {
2726: efree(newstub);
2727: }
2728: return EOF;
2729: }
2730: if (newstub) {
2731: efree(newstub);
2732: }
2733: }
2734: manifest_ftell = php_stream_tell(newfile);
2735: halt_offset = manifest_ftell;
2736:
2737: /* Check whether we can get rid of some of the deleted entries which are
2738: * unused. However some might still be in use so even after this clean-up
2739: * we need to skip entries marked is_deleted. */
2740: zend_hash_apply(&phar->manifest, phar_flush_clean_deleted_apply TSRMLS_CC);
2741:
2742: /* compress as necessary, calculate crcs, serialize meta-data, manifest size, and file sizes */
2743: main_metadata_str.c = 0;
2744: if (phar->metadata) {
2745: PHP_VAR_SERIALIZE_INIT(metadata_hash);
2746: php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash TSRMLS_CC);
2747: PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2748: } else {
2749: main_metadata_str.len = 0;
2750: }
2751: new_manifest_count = 0;
2752: offset = 0;
2753: for (zend_hash_internal_pointer_reset(&phar->manifest);
2754: zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
2755: zend_hash_move_forward(&phar->manifest)) {
2756: if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) {
2757: continue;
2758: }
2759: if (entry->cfp) {
2760: /* did we forget to get rid of cfp last time? */
2761: php_stream_close(entry->cfp);
2762: entry->cfp = 0;
2763: }
2764: if (entry->is_deleted || entry->is_mounted) {
2765: /* remove this from the new phar */
2766: continue;
2767: }
2768: if (!entry->is_modified && entry->fp_refcount) {
2769: /* open file pointers refer to this fp, do not free the stream */
2770: switch (entry->fp_type) {
2771: case PHAR_FP:
2772: free_fp = 0;
2773: break;
2774: case PHAR_UFP:
2775: free_ufp = 0;
2776: default:
2777: break;
2778: }
2779: }
2780: /* after excluding deleted files, calculate manifest size in bytes and number of entries */
2781: ++new_manifest_count;
2782: phar_add_virtual_dirs(phar, entry->filename, entry->filename_len TSRMLS_CC);
2783:
2784: if (entry->is_dir) {
2785: /* we use this to calculate API version, 1.1.1 is used for phars with directories */
2786: has_dirs = 1;
2787: }
2788: if (entry->metadata) {
2789: if (entry->metadata_str.c) {
2790: smart_str_free(&entry->metadata_str);
2791: }
2792: entry->metadata_str.c = 0;
2793: entry->metadata_str.len = 0;
2794: PHP_VAR_SERIALIZE_INIT(metadata_hash);
2795: php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash TSRMLS_CC);
2796: PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2797: } else {
2798: if (entry->metadata_str.c) {
2799: smart_str_free(&entry->metadata_str);
2800: }
2801: entry->metadata_str.c = 0;
2802: entry->metadata_str.len = 0;
2803: }
2804:
2805: /* 32 bits for filename length, length of filename, manifest + metadata, and add 1 for trailing / if a directory */
2806: offset += 4 + entry->filename_len + sizeof(entry_buffer) + entry->metadata_str.len + (entry->is_dir ? 1 : 0);
2807:
2808: /* compress and rehash as necessary */
2809: if ((oldfile && !entry->is_modified) || entry->is_dir) {
2810: if (entry->fp_type == PHAR_UFP) {
2811: /* reset so we can copy the compressed data over */
2812: entry->fp_type = PHAR_FP;
2813: }
2814: continue;
2815: }
2816: if (!phar_get_efp(entry, 0 TSRMLS_CC)) {
2817: /* re-open internal file pointer just-in-time */
2818: newentry = phar_open_jit(phar, entry, error TSRMLS_CC);
2819: if (!newentry) {
2820: /* major problem re-opening, so we ignore this file and the error */
2821: efree(*error);
2822: *error = NULL;
2823: continue;
2824: }
2825: entry = newentry;
2826: }
2827: file = phar_get_efp(entry, 0 TSRMLS_CC);
2828: if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 1 TSRMLS_CC)) {
2829: if (closeoldfile) {
2830: php_stream_close(oldfile);
2831: }
2832: php_stream_close(newfile);
2833: if (error) {
2834: spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2835: }
2836: return EOF;
2837: }
2838: newcrc32 = ~0;
2839: mytime = entry->uncompressed_filesize;
2840: for (loc = 0;loc < mytime; ++loc) {
2841: CRC32(newcrc32, php_stream_getc(file));
2842: }
2843: entry->crc32 = ~newcrc32;
2844: entry->is_crc_checked = 1;
2845: if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
2846: /* not compressed */
2847: entry->compressed_filesize = entry->uncompressed_filesize;
2848: continue;
2849: }
2850: filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0 TSRMLS_CC);
2851: if (!filter) {
2852: if (closeoldfile) {
2853: php_stream_close(oldfile);
2854: }
2855: php_stream_close(newfile);
2856: if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
2857: if (error) {
2858: spprintf(error, 0, "unable to gzip compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2859: }
2860: } else {
2861: if (error) {
2862: spprintf(error, 0, "unable to bzip2 compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2863: }
2864: }
2865: return EOF;
2866: }
2867:
2868: /* create new file that holds the compressed version */
2869: /* work around inability to specify freedom in write and strictness
2870: in read count */
2871: entry->cfp = php_stream_fopen_tmpfile();
2872: if (!entry->cfp) {
2873: if (error) {
2874: spprintf(error, 0, "unable to create temporary file");
2875: }
2876: if (closeoldfile) {
2877: php_stream_close(oldfile);
2878: }
2879: php_stream_close(newfile);
2880: return EOF;
2881: }
2882: php_stream_flush(file);
2883: if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
2884: if (closeoldfile) {
2885: php_stream_close(oldfile);
2886: }
2887: php_stream_close(newfile);
2888: if (error) {
2889: spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2890: }
2891: return EOF;
2892: }
2893: php_stream_filter_append((&entry->cfp->writefilters), filter);
2894: if (SUCCESS != phar_stream_copy_to_stream(file, entry->cfp, entry->uncompressed_filesize, NULL)) {
2895: if (closeoldfile) {
2896: php_stream_close(oldfile);
2897: }
2898: php_stream_close(newfile);
2899: if (error) {
2900: spprintf(error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2901: }
2902: return EOF;
2903: }
2904: php_stream_filter_flush(filter, 1);
2905: php_stream_flush(entry->cfp);
2906: php_stream_filter_remove(filter, 1 TSRMLS_CC);
2907: php_stream_seek(entry->cfp, 0, SEEK_END);
2908: entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
2909: /* generate crc on compressed file */
2910: php_stream_rewind(entry->cfp);
2911: entry->old_flags = entry->flags;
2912: entry->is_modified = 1;
2913: global_flags |= (entry->flags & PHAR_ENT_COMPRESSION_MASK);
2914: }
2915: global_flags |= PHAR_HDR_SIGNATURE;
2916:
2917: /* write out manifest pre-header */
2918: /* 4: manifest length
2919: * 4: manifest entry count
2920: * 2: phar version
2921: * 4: phar global flags
2922: * 4: alias length
2923: * ?: the alias itself
2924: * 4: phar metadata length
2925: * ?: phar metadata
2926: */
2927: restore_alias_len = phar->alias_len;
2928: if (phar->is_temporary_alias) {
2929: phar->alias_len = 0;
2930: }
2931:
2932: manifest_len = offset + phar->alias_len + sizeof(manifest) + main_metadata_str.len;
2933: phar_set_32(manifest, manifest_len);
1.1.1.4 ! misho 2934: /* Hack - see bug #65028, add padding byte to the end of the manifest */
! 2935: if(manifest[0] == '\r' || manifest[0] == '\n') {
! 2936: manifest_len++;
! 2937: phar_set_32(manifest, manifest_len);
! 2938: manifest_hack = 1;
! 2939: }
1.1 misho 2940: phar_set_32(manifest+4, new_manifest_count);
2941: if (has_dirs) {
2942: *(manifest + 8) = (unsigned char) (((PHAR_API_VERSION) >> 8) & 0xFF);
2943: *(manifest + 9) = (unsigned char) (((PHAR_API_VERSION) & 0xF0));
2944: } else {
2945: *(manifest + 8) = (unsigned char) (((PHAR_API_VERSION_NODIR) >> 8) & 0xFF);
2946: *(manifest + 9) = (unsigned char) (((PHAR_API_VERSION_NODIR) & 0xF0));
2947: }
2948: phar_set_32(manifest+10, global_flags);
2949: phar_set_32(manifest+14, phar->alias_len);
2950:
2951: /* write the manifest header */
2952: if (sizeof(manifest) != php_stream_write(newfile, manifest, sizeof(manifest))
2953: || (size_t)phar->alias_len != php_stream_write(newfile, phar->alias, phar->alias_len)) {
2954:
2955: if (closeoldfile) {
2956: php_stream_close(oldfile);
2957: }
2958:
2959: php_stream_close(newfile);
2960: phar->alias_len = restore_alias_len;
2961:
2962: if (error) {
2963: spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", phar->fname);
2964: }
2965:
2966: return EOF;
2967: }
2968:
2969: phar->alias_len = restore_alias_len;
2970:
2971: phar_set_32(manifest, main_metadata_str.len);
2972: if (4 != php_stream_write(newfile, manifest, 4) || (main_metadata_str.len
2973: && main_metadata_str.len != php_stream_write(newfile, main_metadata_str.c, main_metadata_str.len))) {
2974: smart_str_free(&main_metadata_str);
2975:
2976: if (closeoldfile) {
2977: php_stream_close(oldfile);
2978: }
2979:
2980: php_stream_close(newfile);
2981: phar->alias_len = restore_alias_len;
2982:
2983: if (error) {
2984: spprintf(error, 0, "unable to write manifest meta-data of new phar \"%s\"", phar->fname);
2985: }
2986:
2987: return EOF;
2988: }
2989: smart_str_free(&main_metadata_str);
2990:
2991: /* re-calculate the manifest location to simplify later code */
2992: manifest_ftell = php_stream_tell(newfile);
2993:
2994: /* now write the manifest */
2995: for (zend_hash_internal_pointer_reset(&phar->manifest);
2996: zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
2997: zend_hash_move_forward(&phar->manifest)) {
2998:
2999: if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) {
3000: continue;
3001: }
3002:
3003: if (entry->is_deleted || entry->is_mounted) {
3004: /* remove this from the new phar if deleted, ignore if mounted */
3005: continue;
3006: }
3007:
3008: if (entry->is_dir) {
3009: /* add 1 for trailing slash */
3010: phar_set_32(entry_buffer, entry->filename_len + 1);
3011: } else {
3012: phar_set_32(entry_buffer, entry->filename_len);
3013: }
3014:
3015: if (4 != php_stream_write(newfile, entry_buffer, 4)
3016: || entry->filename_len != php_stream_write(newfile, entry->filename, entry->filename_len)
3017: || (entry->is_dir && 1 != php_stream_write(newfile, "/", 1))) {
3018: if (closeoldfile) {
3019: php_stream_close(oldfile);
3020: }
3021: php_stream_close(newfile);
3022: if (error) {
3023: if (entry->is_dir) {
3024: spprintf(error, 0, "unable to write filename of directory \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
3025: } else {
3026: spprintf(error, 0, "unable to write filename of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
3027: }
3028: }
3029: return EOF;
3030: }
3031:
3032: /* set the manifest meta-data:
3033: 4: uncompressed filesize
3034: 4: creation timestamp
3035: 4: compressed filesize
3036: 4: crc32
3037: 4: flags
3038: 4: metadata-len
3039: +: metadata
3040: */
3041: mytime = time(NULL);
3042: phar_set_32(entry_buffer, entry->uncompressed_filesize);
3043: phar_set_32(entry_buffer+4, mytime);
3044: phar_set_32(entry_buffer+8, entry->compressed_filesize);
3045: phar_set_32(entry_buffer+12, entry->crc32);
3046: phar_set_32(entry_buffer+16, entry->flags);
3047: phar_set_32(entry_buffer+20, entry->metadata_str.len);
3048:
3049: if (sizeof(entry_buffer) != php_stream_write(newfile, entry_buffer, sizeof(entry_buffer))
3050: || entry->metadata_str.len != php_stream_write(newfile, entry->metadata_str.c, entry->metadata_str.len)) {
3051: if (closeoldfile) {
3052: php_stream_close(oldfile);
3053: }
3054:
3055: php_stream_close(newfile);
3056:
3057: if (error) {
3058: spprintf(error, 0, "unable to write temporary manifest of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
3059: }
3060:
3061: return EOF;
3062: }
3063: }
1.1.1.4 ! misho 3064: /* Hack - see bug #65028, add padding byte to the end of the manifest */
! 3065: if(manifest_hack) {
! 3066: if(1 != php_stream_write(newfile, manifest, 1)) {
! 3067: if (closeoldfile) {
! 3068: php_stream_close(oldfile);
! 3069: }
! 3070:
! 3071: php_stream_close(newfile);
! 3072:
! 3073: if (error) {
! 3074: spprintf(error, 0, "unable to write manifest padding byte");
! 3075: }
! 3076:
! 3077: return EOF;
! 3078: }
! 3079: }
1.1 misho 3080:
3081: /* now copy the actual file data to the new phar */
3082: offset = php_stream_tell(newfile);
3083: for (zend_hash_internal_pointer_reset(&phar->manifest);
3084: zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
3085: zend_hash_move_forward(&phar->manifest)) {
3086:
3087: if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) {
3088: continue;
3089: }
3090:
3091: if (entry->is_deleted || entry->is_dir || entry->is_mounted) {
3092: continue;
3093: }
3094:
3095: if (entry->cfp) {
3096: file = entry->cfp;
3097: php_stream_rewind(file);
3098: } else {
3099: file = phar_get_efp(entry, 0 TSRMLS_CC);
3100: if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
3101: if (closeoldfile) {
3102: php_stream_close(oldfile);
3103: }
3104: php_stream_close(newfile);
3105: if (error) {
3106: spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3107: }
3108: return EOF;
3109: }
3110: }
3111:
3112: if (!file) {
3113: if (closeoldfile) {
3114: php_stream_close(oldfile);
3115: }
3116: php_stream_close(newfile);
3117: if (error) {
3118: spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3119: }
3120: return EOF;
3121: }
3122:
3123: /* this will have changed for all files that have either changed compression or been modified */
3124: entry->offset = entry->offset_abs = offset;
3125: offset += entry->compressed_filesize;
3126: if (phar_stream_copy_to_stream(file, newfile, entry->compressed_filesize, &wrote) == FAILURE) {
3127: if (closeoldfile) {
3128: php_stream_close(oldfile);
3129: }
3130:
3131: php_stream_close(newfile);
3132:
3133: if (error) {
3134: spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
3135: }
3136:
3137: return EOF;
3138: }
3139:
3140: entry->is_modified = 0;
3141:
3142: if (entry->cfp) {
3143: php_stream_close(entry->cfp);
3144: entry->cfp = NULL;
3145: }
3146:
3147: if (entry->fp_type == PHAR_MOD) {
3148: /* 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 */
3149: if (entry->fp_refcount == 0 && entry->fp != phar->fp && entry->fp != phar->ufp) {
3150: php_stream_close(entry->fp);
3151: }
3152:
3153: entry->fp = NULL;
3154: entry->fp_type = PHAR_FP;
3155: } else if (entry->fp_type == PHAR_UFP) {
3156: entry->fp_type = PHAR_FP;
3157: }
3158: }
3159:
3160: /* append signature */
3161: if (global_flags & PHAR_HDR_SIGNATURE) {
3162: char sig_buf[4];
3163:
3164: php_stream_rewind(newfile);
3165:
3166: if (phar->signature) {
3167: efree(phar->signature);
3168: phar->signature = NULL;
3169: }
3170:
3171: switch(phar->sig_flags) {
3172: #ifndef PHAR_HASH_OK
3173: case PHAR_SIG_SHA512:
3174: case PHAR_SIG_SHA256:
3175: if (closeoldfile) {
3176: php_stream_close(oldfile);
3177: }
3178: php_stream_close(newfile);
3179: if (error) {
3180: spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\" with requested hash type", entry->filename, phar->fname);
3181: }
3182: return EOF;
3183: #endif
3184: default: {
3185: char *digest = NULL;
3186: int digest_len;
3187:
3188: if (FAILURE == phar_create_signature(phar, newfile, &digest, &digest_len, error TSRMLS_CC)) {
3189: if (error) {
3190: char *save = *error;
3191: spprintf(error, 0, "phar error: unable to write signature: %s", save);
3192: efree(save);
3193: }
3194: if (digest) {
3195: efree(digest);
3196: }
3197: if (closeoldfile) {
3198: php_stream_close(oldfile);
3199: }
3200: php_stream_close(newfile);
3201: return EOF;
3202: }
3203:
3204: php_stream_write(newfile, digest, digest_len);
3205: efree(digest);
3206: if (phar->sig_flags == PHAR_SIG_OPENSSL) {
3207: phar_set_32(sig_buf, digest_len);
3208: php_stream_write(newfile, sig_buf, 4);
3209: }
3210: break;
3211: }
3212: }
3213: phar_set_32(sig_buf, phar->sig_flags);
3214: php_stream_write(newfile, sig_buf, 4);
3215: php_stream_write(newfile, "GBMB", 4);
3216: }
3217:
3218: /* finally, close the temp file, rename the original phar,
3219: move the temp to the old phar, unlink the old phar, and reload it into memory
3220: */
3221: if (phar->fp && free_fp) {
3222: php_stream_close(phar->fp);
3223: }
3224:
3225: if (phar->ufp) {
3226: if (free_ufp) {
3227: php_stream_close(phar->ufp);
3228: }
3229: phar->ufp = NULL;
3230: }
3231:
3232: if (closeoldfile) {
3233: php_stream_close(oldfile);
3234: }
3235:
3236: phar->internal_file_start = halt_offset + manifest_len + 4;
3237: phar->halt_offset = halt_offset;
3238: phar->is_brandnew = 0;
3239:
3240: php_stream_rewind(newfile);
3241:
3242: if (phar->donotflush) {
3243: /* deferred flush */
3244: phar->fp = newfile;
3245: } else {
3246: phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
3247: if (!phar->fp) {
3248: phar->fp = newfile;
3249: if (error) {
3250: spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
3251: }
3252: return EOF;
3253: }
3254:
3255: if (phar->flags & PHAR_FILE_COMPRESSED_GZ) {
3256: /* to properly compress, we have to tell zlib to add a zlib header */
3257: zval filterparams;
3258:
3259: array_init(&filterparams);
3260: add_assoc_long(&filterparams, "window", MAX_WBITS+16);
3261: filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp) TSRMLS_CC);
3262: zval_dtor(&filterparams);
3263:
3264: if (!filter) {
3265: if (error) {
3266: 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);
3267: }
3268: return EOF;
3269: }
3270:
3271: php_stream_filter_append(&phar->fp->writefilters, filter);
3272: phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3273: php_stream_filter_flush(filter, 1);
3274: php_stream_filter_remove(filter, 1 TSRMLS_CC);
3275: php_stream_close(phar->fp);
3276: /* use the temp stream as our base */
3277: phar->fp = newfile;
3278: } else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) {
3279: filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp) TSRMLS_CC);
3280: php_stream_filter_append(&phar->fp->writefilters, filter);
3281: phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3282: php_stream_filter_flush(filter, 1);
3283: php_stream_filter_remove(filter, 1 TSRMLS_CC);
3284: php_stream_close(phar->fp);
3285: /* use the temp stream as our base */
3286: phar->fp = newfile;
3287: } else {
3288: phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3289: /* we could also reopen the file in "rb" mode but there is no need for that */
3290: php_stream_close(newfile);
3291: }
3292: }
3293:
3294: if (-1 == php_stream_seek(phar->fp, phar->halt_offset, SEEK_SET)) {
3295: if (error) {
3296: spprintf(error, 0, "unable to seek to __HALT_COMPILER(); in new phar \"%s\"", phar->fname);
3297: }
3298: return EOF;
3299: }
3300:
3301: return EOF;
3302: }
3303: /* }}} */
3304:
3305: #ifdef COMPILE_DL_PHAR
3306: ZEND_GET_MODULE(phar)
3307: #endif
3308:
3309: /* {{{ phar_functions[]
3310: *
3311: * Every user visible function must have an entry in phar_functions[].
3312: */
3313: zend_function_entry phar_functions[] = {
3314: PHP_FE_END
3315: };
3316: /* }}}*/
3317:
3318: static size_t phar_zend_stream_reader(void *handle, char *buf, size_t len TSRMLS_DC) /* {{{ */
3319: {
3320: return php_stream_read(phar_get_pharfp((phar_archive_data*)handle TSRMLS_CC), buf, len);
3321: }
3322: /* }}} */
3323:
3324: #if PHP_VERSION_ID >= 50300
3325: static size_t phar_zend_stream_fsizer(void *handle TSRMLS_DC) /* {{{ */
3326: {
3327: return ((phar_archive_data*)handle)->halt_offset + 32;
3328: } /* }}} */
3329:
3330: #else /* PHP_VERSION_ID */
3331:
3332: static long phar_stream_fteller_for_zend(void *handle TSRMLS_DC) /* {{{ */
3333: {
3334: return (long)php_stream_tell(phar_get_pharfp((phar_archive_data*)handle TSRMLS_CC));
3335: }
3336: /* }}} */
3337: #endif
3338:
3339: zend_op_array *(*phar_orig_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);
3340: #if PHP_VERSION_ID >= 50300
3341: #define phar_orig_zend_open zend_stream_open_function
3342: static char *phar_resolve_path(const char *filename, int filename_len TSRMLS_DC)
3343: {
3344: return phar_find_in_include_path((char *) filename, filename_len, NULL TSRMLS_CC);
3345: }
3346: #else
3347: int (*phar_orig_zend_open)(const char *filename, zend_file_handle *handle TSRMLS_DC);
3348: #endif
3349:
3350: static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type TSRMLS_DC) /* {{{ */
3351: {
3352: zend_op_array *res;
3353: char *name = NULL;
3354: int failed;
3355: phar_archive_data *phar;
3356:
3357: if (!file_handle || !file_handle->filename) {
3358: return phar_orig_compile_file(file_handle, type TSRMLS_CC);
3359: }
3360: if (strstr(file_handle->filename, ".phar") && !strstr(file_handle->filename, "://")) {
1.1.1.2 misho 3361: if (SUCCESS == phar_open_from_filename((char*)file_handle->filename, strlen(file_handle->filename), NULL, 0, 0, &phar, NULL TSRMLS_CC)) {
1.1 misho 3362: if (phar->is_zip || phar->is_tar) {
3363: zend_file_handle f = *file_handle;
3364:
3365: /* zip or tar-based phar */
3366: spprintf(&name, 4096, "phar://%s/%s", file_handle->filename, ".phar/stub.php");
3367: if (SUCCESS == phar_orig_zend_open((const char *)name, file_handle TSRMLS_CC)) {
3368: efree(name);
3369: name = NULL;
3370: file_handle->filename = f.filename;
3371: if (file_handle->opened_path) {
3372: efree(file_handle->opened_path);
3373: }
3374: file_handle->opened_path = f.opened_path;
3375: file_handle->free_filename = f.free_filename;
3376: } else {
3377: *file_handle = f;
3378: }
3379: } else if (phar->flags & PHAR_FILE_COMPRESSION_MASK) {
3380: /* compressed phar */
3381: #if PHP_VERSION_ID >= 50300
3382: file_handle->type = ZEND_HANDLE_STREAM;
3383: /* we do our own reading directly from the phar, don't change the next line */
3384: file_handle->handle.stream.handle = phar;
3385: file_handle->handle.stream.reader = phar_zend_stream_reader;
3386: file_handle->handle.stream.closer = NULL;
3387: file_handle->handle.stream.fsizer = phar_zend_stream_fsizer;
3388: file_handle->handle.stream.isatty = 0;
3389: phar->is_persistent ?
3390: php_stream_rewind(PHAR_GLOBALS->cached_fp[phar->phar_pos].fp) :
3391: php_stream_rewind(phar->fp);
3392: memset(&file_handle->handle.stream.mmap, 0, sizeof(file_handle->handle.stream.mmap));
3393: #else /* PHP_VERSION_ID */
3394: file_handle->type = ZEND_HANDLE_STREAM;
3395: /* we do our own reading directly from the phar, don't change the next line */
3396: file_handle->handle.stream.handle = phar;
3397: file_handle->handle.stream.reader = phar_zend_stream_reader;
3398: file_handle->handle.stream.closer = NULL; /* don't close - let phar handle this one */
3399: file_handle->handle.stream.fteller = phar_stream_fteller_for_zend;
3400: file_handle->handle.stream.interactive = 0;
3401: phar->is_persistent ?
3402: php_stream_rewind(PHAR_GLOBALS->cached_fp[phar->phar_pos].fp) :
3403: php_stream_rewind(phar->fp);
3404: #endif
3405: }
3406: }
3407: }
3408:
3409: zend_try {
3410: failed = 0;
3411: res = phar_orig_compile_file(file_handle, type TSRMLS_CC);
3412: } zend_catch {
3413: failed = 1;
3414: res = NULL;
3415: } zend_end_try();
3416:
3417: if (name) {
3418: efree(name);
3419: }
3420:
3421: if (failed) {
3422: zend_bailout();
3423: }
3424:
3425: return res;
3426: }
3427: /* }}} */
3428:
3429: #if PHP_VERSION_ID < 50300
3430: int phar_zend_open(const char *filename, zend_file_handle *handle TSRMLS_DC) /* {{{ */
3431: {
3432: char *arch, *entry;
3433: int arch_len, entry_len;
3434:
3435: /* this code is obsoleted in php 5.3 */
3436: entry = (char *) filename;
3437: if (!IS_ABSOLUTE_PATH(entry, strlen(entry)) && !strstr(entry, "://")) {
3438: phar_archive_data **pphar = NULL;
3439: char *fname;
3440: int fname_len;
3441:
1.1.1.2 misho 3442: fname = (char*)zend_get_executed_filename(TSRMLS_C);
1.1 misho 3443: fname_len = strlen(fname);
3444:
3445: if (fname_len > 7 && !strncasecmp(fname, "phar://", 7)) {
3446: if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 1, 0 TSRMLS_CC)) {
3447: zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar);
3448: if (!pphar && PHAR_G(manifest_cached)) {
3449: zend_hash_find(&cached_phars, arch, arch_len, (void **) &pphar);
3450: }
3451: efree(arch);
3452: efree(entry);
3453: }
3454: }
3455:
3456: /* retrieving an include within the current directory, so use this if possible */
3457: if (!(entry = phar_find_in_include_path((char *) filename, strlen(filename), NULL TSRMLS_CC))) {
3458: /* this file is not in the phar, use the original path */
3459: goto skip_phar;
3460: }
3461:
3462: if (SUCCESS == phar_orig_zend_open(entry, handle TSRMLS_CC)) {
3463: if (!handle->opened_path) {
3464: handle->opened_path = entry;
3465: }
3466: if (entry != filename) {
3467: handle->free_filename = 1;
3468: }
3469: return SUCCESS;
3470: }
3471:
3472: if (entry != filename) {
3473: efree(entry);
3474: }
3475:
3476: return FAILURE;
3477: }
3478: skip_phar:
3479: return phar_orig_zend_open(filename, handle TSRMLS_CC);
3480: }
3481: /* }}} */
3482: #endif
3483: typedef zend_op_array* (zend_compile_t)(zend_file_handle*, int TSRMLS_DC);
3484: typedef zend_compile_t* (compile_hook)(zend_compile_t *ptr);
3485:
3486: PHP_GINIT_FUNCTION(phar) /* {{{ */
3487: {
3488: phar_mime_type mime;
3489:
3490: memset(phar_globals, 0, sizeof(zend_phar_globals));
3491: phar_globals->readonly = 1;
3492:
3493: zend_hash_init(&phar_globals->mime_types, 0, NULL, NULL, 1);
3494:
3495: #define PHAR_SET_MIME(mimetype, ret, fileext) \
3496: mime.mime = mimetype; \
3497: mime.len = sizeof((mimetype))+1; \
3498: mime.type = ret; \
3499: zend_hash_add(&phar_globals->mime_types, fileext, sizeof(fileext)-1, (void *)&mime, sizeof(phar_mime_type), NULL); \
3500:
3501: PHAR_SET_MIME("text/html", PHAR_MIME_PHPS, "phps")
3502: PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c")
3503: PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cc")
3504: PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cpp")
3505: PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c++")
3506: PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "dtd")
3507: PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "h")
3508: PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "log")
3509: PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "rng")
3510: PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "txt")
3511: PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "xsd")
3512: PHAR_SET_MIME("", PHAR_MIME_PHP, "php")
3513: PHAR_SET_MIME("", PHAR_MIME_PHP, "inc")
3514: PHAR_SET_MIME("video/avi", PHAR_MIME_OTHER, "avi")
3515: PHAR_SET_MIME("image/bmp", PHAR_MIME_OTHER, "bmp")
3516: PHAR_SET_MIME("text/css", PHAR_MIME_OTHER, "css")
3517: PHAR_SET_MIME("image/gif", PHAR_MIME_OTHER, "gif")
3518: PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htm")
3519: PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "html")
3520: PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htmls")
3521: PHAR_SET_MIME("image/x-ico", PHAR_MIME_OTHER, "ico")
3522: PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpe")
3523: PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpg")
3524: PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpeg")
3525: PHAR_SET_MIME("application/x-javascript", PHAR_MIME_OTHER, "js")
3526: PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "midi")
3527: PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "mid")
3528: PHAR_SET_MIME("audio/mod", PHAR_MIME_OTHER, "mod")
3529: PHAR_SET_MIME("movie/quicktime", PHAR_MIME_OTHER, "mov")
3530: PHAR_SET_MIME("audio/mp3", PHAR_MIME_OTHER, "mp3")
3531: PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpg")
3532: PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpeg")
3533: PHAR_SET_MIME("application/pdf", PHAR_MIME_OTHER, "pdf")
3534: PHAR_SET_MIME("image/png", PHAR_MIME_OTHER, "png")
3535: PHAR_SET_MIME("application/shockwave-flash", PHAR_MIME_OTHER, "swf")
3536: PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tif")
3537: PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tiff")
3538: PHAR_SET_MIME("audio/wav", PHAR_MIME_OTHER, "wav")
3539: PHAR_SET_MIME("image/xbm", PHAR_MIME_OTHER, "xbm")
3540: PHAR_SET_MIME("text/xml", PHAR_MIME_OTHER, "xml")
3541:
3542: phar_restore_orig_functions(TSRMLS_C);
3543: }
3544: /* }}} */
3545:
3546: PHP_GSHUTDOWN_FUNCTION(phar) /* {{{ */
3547: {
3548: zend_hash_destroy(&phar_globals->mime_types);
3549: }
3550: /* }}} */
3551:
3552: PHP_MINIT_FUNCTION(phar) /* {{{ */
3553: {
3554: REGISTER_INI_ENTRIES();
3555:
3556: phar_orig_compile_file = zend_compile_file;
3557: zend_compile_file = phar_compile_file;
3558:
3559: #if PHP_VERSION_ID >= 50300
3560: phar_save_resolve_path = zend_resolve_path;
3561: zend_resolve_path = phar_resolve_path;
3562: #else
3563: phar_orig_zend_open = zend_stream_open_function;
3564: zend_stream_open_function = phar_zend_open;
3565: #endif
3566:
3567: phar_object_init(TSRMLS_C);
3568:
3569: phar_intercept_functions_init(TSRMLS_C);
3570: phar_save_orig_functions(TSRMLS_C);
3571:
3572: return php_register_url_stream_wrapper("phar", &php_stream_phar_wrapper TSRMLS_CC);
3573: }
3574: /* }}} */
3575:
3576: PHP_MSHUTDOWN_FUNCTION(phar) /* {{{ */
3577: {
3578: php_unregister_url_stream_wrapper("phar" TSRMLS_CC);
3579:
3580: phar_intercept_functions_shutdown(TSRMLS_C);
3581:
3582: if (zend_compile_file == phar_compile_file) {
3583: zend_compile_file = phar_orig_compile_file;
3584: }
3585:
3586: #if PHP_VERSION_ID < 50300
3587: if (zend_stream_open_function == phar_zend_open) {
3588: zend_stream_open_function = phar_orig_zend_open;
3589: }
3590: #endif
3591: if (PHAR_G(manifest_cached)) {
3592: zend_hash_destroy(&(cached_phars));
3593: zend_hash_destroy(&(cached_alias));
3594: }
3595:
3596: return SUCCESS;
3597: }
3598: /* }}} */
3599:
3600: void phar_request_initialize(TSRMLS_D) /* {{{ */
3601: {
3602: if (!PHAR_GLOBALS->request_init)
3603: {
3604: PHAR_G(last_phar) = NULL;
3605: PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
3606: PHAR_G(has_bz2) = zend_hash_exists(&module_registry, "bz2", sizeof("bz2"));
3607: PHAR_G(has_zlib) = zend_hash_exists(&module_registry, "zlib", sizeof("zlib"));
3608: PHAR_GLOBALS->request_init = 1;
3609: PHAR_GLOBALS->request_ends = 0;
3610: PHAR_GLOBALS->request_done = 0;
3611: zend_hash_init(&(PHAR_GLOBALS->phar_fname_map), 5, zend_get_hash_value, destroy_phar_data, 0);
3612: zend_hash_init(&(PHAR_GLOBALS->phar_persist_map), 5, zend_get_hash_value, NULL, 0);
3613: zend_hash_init(&(PHAR_GLOBALS->phar_alias_map), 5, zend_get_hash_value, NULL, 0);
3614:
3615: if (PHAR_G(manifest_cached)) {
3616: phar_archive_data **pphar;
3617: phar_entry_fp *stuff = (phar_entry_fp *) ecalloc(zend_hash_num_elements(&cached_phars), sizeof(phar_entry_fp));
3618:
3619: for (zend_hash_internal_pointer_reset(&cached_phars);
3620: zend_hash_get_current_data(&cached_phars, (void **)&pphar) == SUCCESS;
3621: zend_hash_move_forward(&cached_phars)) {
3622: stuff[pphar[0]->phar_pos].manifest = (phar_entry_fp_info *) ecalloc( zend_hash_num_elements(&(pphar[0]->manifest)), sizeof(phar_entry_fp_info));
3623: }
3624:
3625: PHAR_GLOBALS->cached_fp = stuff;
3626: }
3627:
3628: PHAR_GLOBALS->phar_SERVER_mung_list = 0;
3629: PHAR_G(cwd) = NULL;
3630: PHAR_G(cwd_len) = 0;
3631: PHAR_G(cwd_init) = 0;
3632: }
3633: }
3634: /* }}} */
3635:
3636: PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */
3637: {
3638: int i;
3639:
3640: PHAR_GLOBALS->request_ends = 1;
3641:
3642: if (PHAR_GLOBALS->request_init)
3643: {
3644: phar_release_functions(TSRMLS_C);
3645: zend_hash_destroy(&(PHAR_GLOBALS->phar_alias_map));
3646: PHAR_GLOBALS->phar_alias_map.arBuckets = NULL;
3647: zend_hash_destroy(&(PHAR_GLOBALS->phar_fname_map));
3648: PHAR_GLOBALS->phar_fname_map.arBuckets = NULL;
3649: zend_hash_destroy(&(PHAR_GLOBALS->phar_persist_map));
3650: PHAR_GLOBALS->phar_persist_map.arBuckets = NULL;
3651: PHAR_GLOBALS->phar_SERVER_mung_list = 0;
3652:
3653: if (PHAR_GLOBALS->cached_fp) {
3654: for (i = 0; i < zend_hash_num_elements(&cached_phars); ++i) {
3655: if (PHAR_GLOBALS->cached_fp[i].fp) {
3656: php_stream_close(PHAR_GLOBALS->cached_fp[i].fp);
3657: }
3658: if (PHAR_GLOBALS->cached_fp[i].ufp) {
3659: php_stream_close(PHAR_GLOBALS->cached_fp[i].ufp);
3660: }
3661: efree(PHAR_GLOBALS->cached_fp[i].manifest);
3662: }
3663: efree(PHAR_GLOBALS->cached_fp);
3664: PHAR_GLOBALS->cached_fp = 0;
3665: }
3666:
3667: PHAR_GLOBALS->request_init = 0;
3668:
3669: if (PHAR_G(cwd)) {
3670: efree(PHAR_G(cwd));
3671: }
3672:
3673: PHAR_G(cwd) = NULL;
3674: PHAR_G(cwd_len) = 0;
3675: PHAR_G(cwd_init) = 0;
3676: }
3677:
3678: PHAR_GLOBALS->request_done = 1;
3679: return SUCCESS;
3680: }
3681: /* }}} */
3682:
3683: PHP_MINFO_FUNCTION(phar) /* {{{ */
3684: {
3685: phar_request_initialize(TSRMLS_C);
3686: php_info_print_table_start();
3687: php_info_print_table_header(2, "Phar: PHP Archive support", "enabled");
3688: php_info_print_table_row(2, "Phar EXT version", PHP_PHAR_VERSION);
3689: php_info_print_table_row(2, "Phar API version", PHP_PHAR_API_VERSION);
1.1.1.4 ! misho 3690: php_info_print_table_row(2, "SVN revision", "$Id: c85687ef5cec51a63c41cffb9ec3972cb8c3f7f3 $");
1.1 misho 3691: php_info_print_table_row(2, "Phar-based phar archives", "enabled");
3692: php_info_print_table_row(2, "Tar-based phar archives", "enabled");
3693: php_info_print_table_row(2, "ZIP-based phar archives", "enabled");
3694:
3695: if (PHAR_G(has_zlib)) {
3696: php_info_print_table_row(2, "gzip compression", "enabled");
3697: } else {
3698: php_info_print_table_row(2, "gzip compression", "disabled (install ext/zlib)");
3699: }
3700:
3701: if (PHAR_G(has_bz2)) {
3702: php_info_print_table_row(2, "bzip2 compression", "enabled");
3703: } else {
3704: php_info_print_table_row(2, "bzip2 compression", "disabled (install pecl/bz2)");
3705: }
3706: #ifdef PHAR_HAVE_OPENSSL
3707: php_info_print_table_row(2, "Native OpenSSL support", "enabled");
3708: #else
3709: if (zend_hash_exists(&module_registry, "openssl", sizeof("openssl"))) {
3710: php_info_print_table_row(2, "OpenSSL support", "enabled");
3711: } else {
3712: php_info_print_table_row(2, "OpenSSL support", "disabled (install ext/openssl)");
3713: }
3714: #endif
3715: php_info_print_table_end();
3716:
3717: php_info_print_box_start(0);
3718: PUTS("Phar based on pear/PHP_Archive, original concept by Davey Shafik.");
3719: PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3720: PUTS("Phar fully realized by Gregory Beaver and Marcus Boerger.");
3721: PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3722: PUTS("Portions of tar implementation Copyright (c) 2003-2009 Tim Kientzle.");
3723: php_info_print_box_end();
3724:
3725: DISPLAY_INI_ENTRIES();
3726: }
3727: /* }}} */
3728:
3729: /* {{{ phar_module_entry
3730: */
3731: static const zend_module_dep phar_deps[] = {
3732: ZEND_MOD_OPTIONAL("apc")
3733: ZEND_MOD_OPTIONAL("bz2")
3734: ZEND_MOD_OPTIONAL("openssl")
3735: ZEND_MOD_OPTIONAL("zlib")
3736: ZEND_MOD_OPTIONAL("standard")
3737: #if defined(HAVE_HASH) && !defined(COMPILE_DL_HASH)
3738: ZEND_MOD_REQUIRED("hash")
3739: #endif
3740: #if HAVE_SPL
3741: ZEND_MOD_REQUIRED("spl")
3742: #endif
3743: ZEND_MOD_END
3744: };
3745:
3746: zend_module_entry phar_module_entry = {
3747: STANDARD_MODULE_HEADER_EX, NULL,
3748: phar_deps,
3749: "Phar",
3750: phar_functions,
3751: PHP_MINIT(phar),
3752: PHP_MSHUTDOWN(phar),
3753: NULL,
3754: PHP_RSHUTDOWN(phar),
3755: PHP_MINFO(phar),
3756: PHP_PHAR_VERSION,
3757: PHP_MODULE_GLOBALS(phar), /* globals descriptor */
3758: PHP_GINIT(phar), /* globals ctor */
3759: PHP_GSHUTDOWN(phar), /* globals dtor */
3760: NULL, /* post deactivate */
3761: STANDARD_MODULE_PROPERTIES_EX
3762: };
3763: /* }}} */
3764:
3765: /*
3766: * Local variables:
3767: * tab-width: 4
3768: * c-basic-offset: 4
3769: * End:
3770: * vim600: noet sw=4 ts=4 fdm=marker
3771: * vim<600: noet sw=4 ts=4
3772: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>