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