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