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