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