xref: /PHP-7.1/ext/phar/phar.c (revision 7f0ab7c2)
1 /*
2   +----------------------------------------------------------------------+
3   | phar php single-file executable PHP extension                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2005-2018 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: e117ab0dc068703c55b505e78a0d3b3752e9c0b7 $ */
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 = *(uint16_t*)(buffer); \
522 	buffer += 2
523 #endif
524 #define PHAR_ZIP_16(var) ((uint16_t)((((uint16_t)var[0]) & 0xff) | \
525 	(((uint16_t)var[1]) & 0xff) << 8))
526 #define PHAR_ZIP_32(var) ((uint32_t)((((uint32_t)var[0]) & 0xff) | \
527 	(((uint32_t)var[1]) & 0xff) << 8 | \
528 	(((uint32_t)var[2]) & 0xff) << 16 | \
529 	(((uint32_t)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, uint32_t 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, uint32_t compression, char **error) /* {{{ */
667 {
668 	char b32[4], *buffer, *endbuffer, *savebuf;
669 	phar_archive_data *mydata = NULL;
670 	phar_entry_info entry;
671 	uint32_t manifest_len, manifest_count, manifest_flags, manifest_index, tmp_len, sig_flags;
672 	uint16_t manifest_ver;
673 	uint32_t 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 				uint32_t 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 > (size_t)(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 > (size_t)(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 > (size_t)(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_check_open_basedir(fname)) {
1343 		return FAILURE;
1344 	}
1345 
1346 	/* first open readonly so it won't be created if not present */
1347 	fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, &actual);
1348 
1349 	if (actual) {
1350 		fname = ZSTR_VAL(actual);
1351 		fname_len = ZSTR_LEN(actual);
1352 	}
1353 
1354 	if (fp) {
1355 		if (phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error) == SUCCESS) {
1356 			if ((*pphar)->is_data || !PHAR_G(readonly)) {
1357 				(*pphar)->is_writeable = 1;
1358 			}
1359 			if (actual) {
1360 				zend_string_release(actual);
1361 			}
1362 			return SUCCESS;
1363 		} else {
1364 			/* file exists, but is either corrupt or not a phar archive */
1365 			if (actual) {
1366 				zend_string_release(actual);
1367 			}
1368 			return FAILURE;
1369 		}
1370 	}
1371 
1372 	if (actual) {
1373 		zend_string_release(actual);
1374 	}
1375 
1376 	if (PHAR_G(readonly) && !is_data) {
1377 		if (options & REPORT_ERRORS) {
1378 			if (error) {
1379 				spprintf(error, 0, "creating archive \"%s\" disabled by the php.ini setting phar.readonly", fname);
1380 			}
1381 		}
1382 		return FAILURE;
1383 	}
1384 
1385 	/* set up our manifest */
1386 	mydata = ecalloc(1, sizeof(phar_archive_data));
1387 	mydata->fname = expand_filepath(fname, NULL);
1388 	if (mydata->fname == NULL) {
1389 		return FAILURE;
1390 	}
1391 	fname_len = strlen(mydata->fname);
1392 #ifdef PHP_WIN32
1393 	phar_unixify_path_separators(mydata->fname, fname_len);
1394 #endif
1395 	p = strrchr(mydata->fname, '/');
1396 
1397 	if (p) {
1398 		mydata->ext = memchr(p, '.', (mydata->fname + fname_len) - p);
1399 		if (mydata->ext == p) {
1400 			mydata->ext = memchr(p + 1, '.', (mydata->fname + fname_len) - p - 1);
1401 		}
1402 		if (mydata->ext) {
1403 			mydata->ext_len = (mydata->fname + fname_len) - mydata->ext;
1404 		}
1405 	}
1406 
1407 	if (pphar) {
1408 		*pphar = mydata;
1409 	}
1410 
1411 	zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
1412 		zend_get_hash_value, destroy_phar_manifest_entry, 0);
1413 	zend_hash_init(&mydata->mounted_dirs, sizeof(char *),
1414 		zend_get_hash_value, NULL, 0);
1415 	zend_hash_init(&mydata->virtual_dirs, sizeof(char *),
1416 		zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
1417 	mydata->fname_len = fname_len;
1418 	snprintf(mydata->version, sizeof(mydata->version), "%s", PHP_PHAR_API_VERSION);
1419 	mydata->is_temporary_alias = alias ? 0 : 1;
1420 	mydata->internal_file_start = -1;
1421 	mydata->fp = NULL;
1422 	mydata->is_writeable = 1;
1423 	mydata->is_brandnew = 1;
1424 	phar_request_initialize();
1425 	zend_hash_str_add_ptr(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len, mydata);
1426 
1427 	if (is_data) {
1428 		alias = NULL;
1429 		alias_len = 0;
1430 		mydata->is_data = 1;
1431 		/* assume tar format, PharData can specify other */
1432 		mydata->is_tar = 1;
1433 	} else {
1434 		phar_archive_data *fd_ptr;
1435 
1436 		if (alias && NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len))) {
1437 			if (SUCCESS != phar_free_alias(fd_ptr, alias, alias_len)) {
1438 				if (error) {
1439 					spprintf(error, 4096, "phar error: phar \"%s\" cannot set alias \"%s\", already in use by another phar archive", mydata->fname, alias);
1440 				}
1441 
1442 				zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
1443 
1444 				if (pphar) {
1445 					*pphar = NULL;
1446 				}
1447 
1448 				return FAILURE;
1449 			}
1450 		}
1451 
1452 		mydata->alias = alias ? estrndup(alias, alias_len) : estrndup(mydata->fname, fname_len);
1453 		mydata->alias_len = alias ? alias_len : fname_len;
1454 	}
1455 
1456 	if (alias_len && alias) {
1457 		if (NULL == zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len, mydata)) {
1458 			if (options & REPORT_ERRORS) {
1459 				if (error) {
1460 					spprintf(error, 0, "archive \"%s\" cannot be associated with alias \"%s\", already in use", fname, alias);
1461 				}
1462 			}
1463 
1464 			zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
1465 
1466 			if (pphar) {
1467 				*pphar = NULL;
1468 			}
1469 
1470 			return FAILURE;
1471 		}
1472 	}
1473 
1474 	return SUCCESS;
1475 }
1476 /* }}}*/
1477 
1478 /**
1479  * Return an already opened filename.
1480  *
1481  * Or scan a phar file for the required __HALT_COMPILER(); ?> token and verify
1482  * that the manifest is proper, then pass it to phar_parse_pharfile().  SUCCESS
1483  * or FAILURE is returned and pphar is set to a pointer to the phar's manifest
1484  */
1485 int phar_open_from_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error) /* {{{ */
1486 {
1487 	php_stream *fp;
1488 	zend_string *actual;
1489 	int ret, is_data = 0;
1490 
1491 	if (error) {
1492 		*error = NULL;
1493 	}
1494 
1495 	if (!strstr(fname, ".phar")) {
1496 		is_data = 1;
1497 	}
1498 
1499 	if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, pphar, error) == SUCCESS) {
1500 		return SUCCESS;
1501 	} else if (error && *error) {
1502 		return FAILURE;
1503 	}
1504 	if (php_check_open_basedir(fname)) {
1505 		return FAILURE;
1506 	}
1507 
1508 	fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, &actual);
1509 
1510 	if (!fp) {
1511 		if (options & REPORT_ERRORS) {
1512 			if (error) {
1513 				spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
1514 			}
1515 		}
1516 		if (actual) {
1517 			zend_string_release(actual);
1518 		}
1519 		return FAILURE;
1520 	}
1521 
1522 	if (actual) {
1523 		fname = ZSTR_VAL(actual);
1524 		fname_len = ZSTR_LEN(actual);
1525 	}
1526 
1527 	ret =  phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error);
1528 
1529 	if (actual) {
1530 		zend_string_release(actual);
1531 	}
1532 
1533 	return ret;
1534 }
1535 /* }}}*/
1536 
1537 static inline char *phar_strnstr(const char *buf, int buf_len, const char *search, int search_len) /* {{{ */
1538 {
1539 	const char *c;
1540 	ptrdiff_t so_far = 0;
1541 
1542 	if (buf_len < search_len) {
1543 		return NULL;
1544 	}
1545 
1546 	c = buf - 1;
1547 
1548 	do {
1549 		if (!(c = memchr(c + 1, search[0], buf_len - search_len - so_far))) {
1550 			return (char *) NULL;
1551 		}
1552 
1553 		so_far = c - buf;
1554 
1555 		if (so_far >= (buf_len - search_len)) {
1556 			return (char *) NULL;
1557 		}
1558 
1559 		if (!memcmp(c, search, search_len)) {
1560 			return (char *) c;
1561 		}
1562 	} while (1);
1563 }
1564 /* }}} */
1565 
1566 /**
1567  * Scan an open fp for the required __HALT_COMPILER(); ?> token and verify
1568  * that the manifest is proper, then pass it to phar_parse_pharfile().  SUCCESS
1569  * or FAILURE is returned and pphar is set to a pointer to the phar's manifest
1570  */
1571 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) /* {{{ */
1572 {
1573 	const char token[] = "__HALT_COMPILER();";
1574 	const char zip_magic[] = "PK\x03\x04";
1575 	const char gz_magic[] = "\x1f\x8b\x08";
1576 	const char bz_magic[] = "BZh";
1577 	char *pos, test = '\0';
1578 	const int window_size = 1024;
1579 	char buffer[1024 + sizeof(token)]; /* a 1024 byte window + the size of the halt_compiler token (moving window) */
1580 	const zend_long readsize = sizeof(buffer) - sizeof(token);
1581 	const zend_long tokenlen = sizeof(token) - 1;
1582 	zend_long halt_offset;
1583 	size_t got;
1584 	uint32_t compression = PHAR_FILE_COMPRESSED_NONE;
1585 
1586 	if (error) {
1587 		*error = NULL;
1588 	}
1589 
1590 	if (-1 == php_stream_rewind(fp)) {
1591 		MAPPHAR_ALLOC_FAIL("cannot rewind phar \"%s\"")
1592 	}
1593 
1594 	buffer[sizeof(buffer)-1] = '\0';
1595 	memset(buffer, 32, sizeof(token));
1596 	halt_offset = 0;
1597 
1598 	/* Maybe it's better to compile the file instead of just searching,  */
1599 	/* but we only want the offset. So we want a .re scanner to find it. */
1600 	while(!php_stream_eof(fp)) {
1601 		if ((got = php_stream_read(fp, buffer+tokenlen, readsize)) < (size_t) tokenlen) {
1602 			MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)")
1603 		}
1604 
1605 		if (!test) {
1606 			test = '\1';
1607 			pos = buffer+tokenlen;
1608 			if (!memcmp(pos, gz_magic, 3)) {
1609 				char err = 0;
1610 				php_stream_filter *filter;
1611 				php_stream *temp;
1612 				/* to properly decompress, we have to tell zlib to look for a zlib or gzip header */
1613 				zval filterparams;
1614 
1615 				if (!PHAR_G(has_zlib)) {
1616 					MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file, enable zlib extension in php.ini")
1617 				}
1618 				array_init(&filterparams);
1619 /* this is defined in zlib's zconf.h */
1620 #ifndef MAX_WBITS
1621 #define MAX_WBITS 15
1622 #endif
1623 				add_assoc_long_ex(&filterparams, "window", sizeof("window") - 1, MAX_WBITS + 32);
1624 
1625 				/* entire file is gzip-compressed, uncompress to temporary file */
1626 				if (!(temp = php_stream_fopen_tmpfile())) {
1627 					MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of gzipped phar archive \"%s\"")
1628 				}
1629 
1630 				php_stream_rewind(fp);
1631 				filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp));
1632 
1633 				if (!filter) {
1634 					err = 1;
1635 					add_assoc_long_ex(&filterparams, "window", sizeof("window") - 1, MAX_WBITS);
1636 					filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp));
1637 					zval_dtor(&filterparams);
1638 
1639 					if (!filter) {
1640 						php_stream_close(temp);
1641 						MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6")
1642 					}
1643 				} else {
1644 					zval_dtor(&filterparams);
1645 				}
1646 
1647 				php_stream_filter_append(&temp->writefilters, filter);
1648 
1649 				if (SUCCESS != php_stream_copy_to_stream_ex(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
1650 					if (err) {
1651 						php_stream_close(temp);
1652 						MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6")
1653 					}
1654 					php_stream_close(temp);
1655 					MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file")
1656 				}
1657 
1658 				php_stream_filter_flush(filter, 1);
1659 				php_stream_filter_remove(filter, 1);
1660 				php_stream_close(fp);
1661 				fp = temp;
1662 				php_stream_rewind(fp);
1663 				compression = PHAR_FILE_COMPRESSED_GZ;
1664 
1665 				/* now, start over */
1666 				test = '\0';
1667 				continue;
1668 			} else if (!memcmp(pos, bz_magic, 3)) {
1669 				php_stream_filter *filter;
1670 				php_stream *temp;
1671 
1672 				if (!PHAR_G(has_bz2)) {
1673 					MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file, enable bz2 extension in php.ini")
1674 				}
1675 
1676 				/* entire file is bzip-compressed, uncompress to temporary file */
1677 				if (!(temp = php_stream_fopen_tmpfile())) {
1678 					MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of bzipped phar archive \"%s\"")
1679 				}
1680 
1681 				php_stream_rewind(fp);
1682 				filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp));
1683 
1684 				if (!filter) {
1685 					php_stream_close(temp);
1686 					MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\", filter creation failed")
1687 				}
1688 
1689 				php_stream_filter_append(&temp->writefilters, filter);
1690 
1691 				if (SUCCESS != php_stream_copy_to_stream_ex(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
1692 					php_stream_close(temp);
1693 					MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file")
1694 				}
1695 
1696 				php_stream_filter_flush(filter, 1);
1697 				php_stream_filter_remove(filter, 1);
1698 				php_stream_close(fp);
1699 				fp = temp;
1700 				php_stream_rewind(fp);
1701 				compression = PHAR_FILE_COMPRESSED_BZ2;
1702 
1703 				/* now, start over */
1704 				test = '\0';
1705 				continue;
1706 			}
1707 
1708 			if (!memcmp(pos, zip_magic, 4)) {
1709 				php_stream_seek(fp, 0, SEEK_END);
1710 				return phar_parse_zipfile(fp, fname, fname_len, alias, alias_len, pphar, error);
1711 			}
1712 
1713 			if (got > 512) {
1714 				if (phar_is_tar(pos, fname)) {
1715 					php_stream_rewind(fp);
1716 					return phar_parse_tarfile(fp, fname, fname_len, alias, alias_len, pphar, is_data, compression, error);
1717 				}
1718 			}
1719 		}
1720 
1721 		if (got > 0 && (pos = phar_strnstr(buffer, got + sizeof(token), token, sizeof(token)-1)) != NULL) {
1722 			halt_offset += (pos - buffer); /* no -tokenlen+tokenlen here */
1723 			return phar_parse_pharfile(fp, fname, fname_len, alias, alias_len, halt_offset, pphar, compression, error);
1724 		}
1725 
1726 		halt_offset += got;
1727 		memmove(buffer, buffer + window_size, tokenlen); /* move the memory buffer by the size of the window */
1728 	}
1729 
1730 	MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (__HALT_COMPILER(); not found)")
1731 }
1732 /* }}} */
1733 
1734 /*
1735  * given the location of the file extension and the start of the file path,
1736  * determine the end of the portion of the path (i.e. /path/to/file.ext/blah
1737  * grabs "/path/to/file.ext" as does the straight /path/to/file.ext),
1738  * stat it to determine if it exists.
1739  * if so, check to see if it is a directory and fail if so
1740  * if not, check to see if its dirname() exists (i.e. "/path/to") and is a directory
1741  * succeed if we are creating the file, otherwise fail.
1742  */
1743 static int phar_analyze_path(const char *fname, const char *ext, int ext_len, int for_create) /* {{{ */
1744 {
1745 	php_stream_statbuf ssb;
1746 	char *realpath;
1747 	char *filename = estrndup(fname, (ext - fname) + ext_len);
1748 
1749 	if ((realpath = expand_filepath(filename, NULL))) {
1750 #ifdef PHP_WIN32
1751 		phar_unixify_path_separators(realpath, strlen(realpath));
1752 #endif
1753 		if (zend_hash_str_exists(&(PHAR_G(phar_fname_map)), realpath, strlen(realpath))) {
1754 			efree(realpath);
1755 			efree(filename);
1756 			return SUCCESS;
1757 		}
1758 
1759 		if (PHAR_G(manifest_cached) && zend_hash_str_exists(&cached_phars, realpath, strlen(realpath))) {
1760 			efree(realpath);
1761 			efree(filename);
1762 			return SUCCESS;
1763 		}
1764 		efree(realpath);
1765 	}
1766 
1767 	if (SUCCESS == php_stream_stat_path((char *) filename, &ssb)) {
1768 
1769 		efree(filename);
1770 
1771 		if (ssb.sb.st_mode & S_IFDIR) {
1772 			return FAILURE;
1773 		}
1774 
1775 		if (for_create == 1) {
1776 			return FAILURE;
1777 		}
1778 
1779 		return SUCCESS;
1780 	} else {
1781 		char *slash;
1782 
1783 		if (!for_create) {
1784 			efree(filename);
1785 			return FAILURE;
1786 		}
1787 
1788 		slash = (char *) strrchr(filename, '/');
1789 
1790 		if (slash) {
1791 			*slash = '\0';
1792 		}
1793 
1794 		if (SUCCESS != php_stream_stat_path((char *) filename, &ssb)) {
1795 			if (!slash) {
1796 				if (!(realpath = expand_filepath(filename, NULL))) {
1797 					efree(filename);
1798 					return FAILURE;
1799 				}
1800 #ifdef PHP_WIN32
1801 				phar_unixify_path_separators(realpath, strlen(realpath));
1802 #endif
1803 				slash = strstr(realpath, filename);
1804 				if (slash) {
1805 					slash += ((ext - fname) + ext_len);
1806 					*slash = '\0';
1807 				}
1808 				slash = strrchr(realpath, '/');
1809 
1810 				if (slash) {
1811 					*slash = '\0';
1812 				} else {
1813 					efree(realpath);
1814 					efree(filename);
1815 					return FAILURE;
1816 				}
1817 
1818 				if (SUCCESS != php_stream_stat_path(realpath, &ssb)) {
1819 					efree(realpath);
1820 					efree(filename);
1821 					return FAILURE;
1822 				}
1823 
1824 				efree(realpath);
1825 
1826 				if (ssb.sb.st_mode & S_IFDIR) {
1827 					efree(filename);
1828 					return SUCCESS;
1829 				}
1830 			}
1831 
1832 			efree(filename);
1833 			return FAILURE;
1834 		}
1835 
1836 		efree(filename);
1837 
1838 		if (ssb.sb.st_mode & S_IFDIR) {
1839 			return SUCCESS;
1840 		}
1841 
1842 		return FAILURE;
1843 	}
1844 }
1845 /* }}} */
1846 
1847 /* check for ".phar" in extension */
1848 static int phar_check_str(const char *fname, const char *ext_str, int ext_len, int executable, int for_create) /* {{{ */
1849 {
1850 	const char *pos;
1851 
1852 	if (ext_len < 0 || ext_len >= 50) {
1853 		return FAILURE;
1854 	}
1855 	if (executable == 1) {
1856 		/* executable phars must contain ".phar" as a valid extension (phar://.pharmy/oops is invalid) */
1857 		/* (phar://hi/there/.phar/oops is also invalid) */
1858 		pos = strstr(ext_str, ".phar");
1859 
1860 		if (!pos
1861 			|| (pos != ext_str && (*(pos - 1) == '/'))
1862 			|| (ext_len - (pos - ext_str)) < 5
1863 			|| !(pos += 5)
1864 			|| !(*pos == '\0' || *pos == '/' || *pos == '.')) {
1865 			return FAILURE;
1866 		}
1867 		return phar_analyze_path(fname, ext_str, ext_len, for_create);
1868 	}
1869 
1870 	/* data phars need only contain a single non-"." to be valid */
1871 	if (!executable) {
1872 		pos = strstr(ext_str, ".phar");
1873 		if (!(pos && (*(pos - 1) != '/')
1874 					&& (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) && *(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
1875 			return phar_analyze_path(fname, ext_str, ext_len, for_create);
1876 		}
1877 	} else {
1878 		if (*(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
1879 			return phar_analyze_path(fname, ext_str, ext_len, for_create);
1880 		}
1881 	}
1882 
1883 	return FAILURE;
1884 }
1885 /* }}} */
1886 
1887 /*
1888  * if executable is 1, only returns SUCCESS if the extension is one of the tar/zip .phar extensions
1889  * if executable is 0, it returns SUCCESS only if the filename does *not* contain ".phar" anywhere, and treats
1890  * the first extension as the filename extension
1891  *
1892  * if an extension is found, it sets ext_str to the location of the file extension in filename,
1893  * and ext_len to the length of the extension.
1894  * for urls like "phar://alias/oops" it instead sets ext_len to -1 and returns FAILURE, which tells
1895  * the calling function to use "alias" as the phar alias
1896  *
1897  * the last parameter should be set to tell the thing to assume that filename is the full path, and only to check the
1898  * extension rules, not to iterate.
1899  */
1900 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) /* {{{ */
1901 {
1902 	const char *pos, *slash;
1903 
1904 	*ext_str = NULL;
1905 	*ext_len = 0;
1906 
1907 	if (!filename_len || filename_len == 1) {
1908 		return FAILURE;
1909 	}
1910 
1911 	phar_request_initialize();
1912 	/* first check for alias in first segment */
1913 	pos = memchr(filename, '/', filename_len);
1914 
1915 	if (pos && pos != filename) {
1916 		/* check for url like http:// or phar:// */
1917 		if (*(pos - 1) == ':' && (pos - filename) < filename_len - 1 && *(pos + 1) == '/') {
1918 			*ext_len = -2;
1919 			*ext_str = NULL;
1920 			return FAILURE;
1921 		}
1922 		if (zend_hash_str_exists(&(PHAR_G(phar_alias_map)), (char *) filename, pos - filename)) {
1923 			*ext_str = pos;
1924 			*ext_len = -1;
1925 			return FAILURE;
1926 		}
1927 
1928 		if (PHAR_G(manifest_cached) && zend_hash_str_exists(&cached_alias, (char *) filename, pos - filename)) {
1929 			*ext_str = pos;
1930 			*ext_len = -1;
1931 			return FAILURE;
1932 		}
1933 	}
1934 
1935 	if (zend_hash_num_elements(&(PHAR_G(phar_fname_map))) || PHAR_G(manifest_cached)) {
1936 		phar_archive_data *pphar;
1937 
1938 		if (is_complete) {
1939 			if (NULL != (pphar = zend_hash_str_find_ptr(&(PHAR_G(phar_fname_map)), (char *) filename, filename_len))) {
1940 				*ext_str = filename + (filename_len - pphar->ext_len);
1941 woohoo:
1942 				*ext_len = pphar->ext_len;
1943 
1944 				if (executable == 2) {
1945 					return SUCCESS;
1946 				}
1947 
1948 				if (executable == 1 && !pphar->is_data) {
1949 					return SUCCESS;
1950 				}
1951 
1952 				if (!executable && pphar->is_data) {
1953 					return SUCCESS;
1954 				}
1955 
1956 				return FAILURE;
1957 			}
1958 
1959 			if (PHAR_G(manifest_cached) && NULL != (pphar = zend_hash_str_find_ptr(&cached_phars, (char *) filename, filename_len))) {
1960 				*ext_str = filename + (filename_len - pphar->ext_len);
1961 				goto woohoo;
1962 			}
1963 		} else {
1964 			zend_string *str_key;
1965 			zend_ulong unused;
1966 
1967 			for (zend_hash_internal_pointer_reset(&(PHAR_G(phar_fname_map)));
1968 				HASH_KEY_NON_EXISTENT != zend_hash_get_current_key(&(PHAR_G(phar_fname_map)), &str_key, &unused);
1969 				zend_hash_move_forward(&(PHAR_G(phar_fname_map)))
1970 			) {
1971 				if (ZSTR_LEN(str_key) > (uint) filename_len) {
1972 					continue;
1973 				}
1974 
1975 				if (!memcmp(filename, ZSTR_VAL(str_key), ZSTR_LEN(str_key)) && ((uint)filename_len == ZSTR_LEN(str_key)
1976 					|| filename[ZSTR_LEN(str_key)] == '/' || filename[ZSTR_LEN(str_key)] == '\0')) {
1977 					if (NULL == (pphar = zend_hash_get_current_data_ptr(&(PHAR_G(phar_fname_map))))) {
1978 						break;
1979 					}
1980 					*ext_str = filename + (ZSTR_LEN(str_key) - pphar->ext_len);
1981 					goto woohoo;
1982 				}
1983 			}
1984 
1985 			if (PHAR_G(manifest_cached)) {
1986 				for (zend_hash_internal_pointer_reset(&cached_phars);
1987 					HASH_KEY_NON_EXISTENT != zend_hash_get_current_key(&cached_phars, &str_key, &unused);
1988 					zend_hash_move_forward(&cached_phars)
1989 				) {
1990 					if (ZSTR_LEN(str_key) > (uint) filename_len) {
1991 						continue;
1992 					}
1993 
1994 					if (!memcmp(filename, ZSTR_VAL(str_key), ZSTR_LEN(str_key)) && ((uint)filename_len == ZSTR_LEN(str_key)
1995 						|| filename[ZSTR_LEN(str_key)] == '/' || filename[ZSTR_LEN(str_key)] == '\0')) {
1996 						if (NULL == (pphar = zend_hash_get_current_data_ptr(&cached_phars))) {
1997 							break;
1998 						}
1999 						*ext_str = filename + (ZSTR_LEN(str_key) - pphar->ext_len);
2000 						goto woohoo;
2001 					}
2002 				}
2003 			}
2004 		}
2005 	}
2006 
2007 	pos = memchr(filename + 1, '.', filename_len);
2008 next_extension:
2009 	if (!pos) {
2010 		return FAILURE;
2011 	}
2012 
2013 	while (pos != filename && (*(pos - 1) == '/' || *(pos - 1) == '\0')) {
2014 		pos = memchr(pos + 1, '.', filename_len - (pos - filename) - 1);
2015 		if (!pos) {
2016 			return FAILURE;
2017 		}
2018 	}
2019 
2020 	slash = memchr(pos, '/', filename_len - (pos - filename));
2021 
2022 	if (!slash) {
2023 		/* this is a url like "phar://blah.phar" with no directory */
2024 		*ext_str = pos;
2025 		*ext_len = strlen(pos);
2026 
2027 		/* file extension must contain "phar" */
2028 		switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create)) {
2029 			case SUCCESS:
2030 				return SUCCESS;
2031 			case FAILURE:
2032 				/* we are at the end of the string, so we fail */
2033 				return FAILURE;
2034 		}
2035 	}
2036 
2037 	/* we've found an extension that ends at a directory separator */
2038 	*ext_str = pos;
2039 	*ext_len = slash - pos;
2040 
2041 	switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create)) {
2042 		case SUCCESS:
2043 			return SUCCESS;
2044 		case FAILURE:
2045 			/* look for more extensions */
2046 			pos = strchr(pos + 1, '.');
2047 			if (pos) {
2048 				*ext_str = NULL;
2049 				*ext_len = 0;
2050 			}
2051 			goto next_extension;
2052 	}
2053 
2054 	return FAILURE;
2055 }
2056 /* }}} */
2057 
2058 static int php_check_dots(const char *element, int n) /* {{{ */
2059 {
2060 	for(n--; n >= 0; --n) {
2061 		if (element[n] != '.') {
2062 			return 1;
2063 		}
2064 	}
2065 	return 0;
2066 }
2067 /* }}} */
2068 
2069 #define IS_DIRECTORY_UP(element, len) \
2070 	(len >= 2 && !php_check_dots(element, len))
2071 
2072 #define IS_DIRECTORY_CURRENT(element, len) \
2073 	(len == 1 && element[0] == '.')
2074 
2075 #define IS_BACKSLASH(c) ((c) == '/')
2076 
2077 /**
2078  * Remove .. and . references within a phar filename
2079  */
2080 char *phar_fix_filepath(char *path, int *new_len, int use_cwd) /* {{{ */
2081 {
2082 	char *newpath;
2083 	int newpath_len;
2084 	char *ptr;
2085 	char *tok;
2086 	int ptr_length, path_length = *new_len;
2087 
2088 	if (PHAR_G(cwd_len) && use_cwd && path_length > 2 && path[0] == '.' && path[1] == '/') {
2089 		newpath_len = PHAR_G(cwd_len);
2090 		newpath = emalloc(strlen(path) + newpath_len + 1);
2091 		memcpy(newpath, PHAR_G(cwd), newpath_len);
2092 	} else {
2093 		newpath = emalloc(strlen(path) + 2);
2094 		newpath[0] = '/';
2095 		newpath_len = 1;
2096 	}
2097 
2098 	ptr = path;
2099 
2100 	if (*ptr == '/') {
2101 		++ptr;
2102 	}
2103 
2104 	tok = ptr;
2105 
2106 	do {
2107 		ptr = memchr(ptr, '/', path_length - (ptr - path));
2108 	} while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
2109 
2110 	if (!ptr && (path_length - (tok - path))) {
2111 		switch (path_length - (tok - path)) {
2112 			case 1:
2113 				if (*tok == '.') {
2114 					efree(path);
2115 					*new_len = 1;
2116 					efree(newpath);
2117 					return estrndup("/", 1);
2118 				}
2119 				break;
2120 			case 2:
2121 				if (tok[0] == '.' && tok[1] == '.') {
2122 					efree(path);
2123 					*new_len = 1;
2124 					efree(newpath);
2125 					return estrndup("/", 1);
2126 				}
2127 		}
2128 		efree(newpath);
2129 		return path;
2130 	}
2131 
2132 	while (ptr) {
2133 		ptr_length = ptr - tok;
2134 last_time:
2135 		if (IS_DIRECTORY_UP(tok, ptr_length)) {
2136 #define PREVIOUS newpath[newpath_len - 1]
2137 
2138 			while (newpath_len > 1 && !IS_BACKSLASH(PREVIOUS)) {
2139 				newpath_len--;
2140 			}
2141 
2142 			if (newpath[0] != '/') {
2143 				newpath[newpath_len] = '\0';
2144 			} else if (newpath_len > 1) {
2145 				--newpath_len;
2146 			}
2147 		} else if (!IS_DIRECTORY_CURRENT(tok, ptr_length)) {
2148 			if (newpath_len > 1) {
2149 				newpath[newpath_len++] = '/';
2150 				memcpy(newpath + newpath_len, tok, ptr_length+1);
2151 			} else {
2152 				memcpy(newpath + newpath_len, tok, ptr_length+1);
2153 			}
2154 
2155 			newpath_len += ptr_length;
2156 		}
2157 
2158 		if (ptr == path + path_length) {
2159 			break;
2160 		}
2161 
2162 		tok = ++ptr;
2163 
2164 		do {
2165 			ptr = memchr(ptr, '/', path_length - (ptr - path));
2166 		} while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
2167 
2168 		if (!ptr && (path_length - (tok - path))) {
2169 			ptr_length = path_length - (tok - path);
2170 			ptr = path + path_length;
2171 			goto last_time;
2172 		}
2173 	}
2174 
2175 	efree(path);
2176 	*new_len = newpath_len;
2177 	newpath[newpath_len] = '\0';
2178 	return erealloc(newpath, newpath_len + 1);
2179 }
2180 /* }}} */
2181 
2182 /**
2183  * Process a phar stream name, ensuring we can handle any of:
2184  *
2185  * - whatever.phar
2186  * - whatever.phar.gz
2187  * - whatever.phar.bz2
2188  * - whatever.phar.php
2189  *
2190  * Optionally the name might start with 'phar://'
2191  *
2192  * This is used by phar_parse_url()
2193  */
2194 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) /* {{{ */
2195 {
2196 	const char *ext_str;
2197 #ifdef PHP_WIN32
2198 	char *save;
2199 #endif
2200 	int ext_len;
2201 
2202 	if (CHECK_NULL_PATH(filename, filename_len)) {
2203 		return FAILURE;
2204 	}
2205 
2206 	if (CHECK_NULL_PATH(filename, filename_len)) {
2207 		return FAILURE;
2208 	}
2209 
2210 	if (!strncasecmp(filename, "phar://", 7)) {
2211 		filename += 7;
2212 		filename_len -= 7;
2213 	}
2214 
2215 	ext_len = 0;
2216 #ifdef PHP_WIN32
2217 	save = filename;
2218 	filename = estrndup(filename, filename_len);
2219 	phar_unixify_path_separators(filename, filename_len);
2220 #endif
2221 	if (phar_detect_phar_fname_ext(filename, filename_len, &ext_str, &ext_len, executable, for_create, 0) == FAILURE) {
2222 		if (ext_len != -1) {
2223 			if (!ext_str) {
2224 				/* no / detected, restore arch for error message */
2225 #ifdef PHP_WIN32
2226 				*arch = save;
2227 #else
2228 				*arch = (char*)filename;
2229 #endif
2230 			}
2231 
2232 #ifdef PHP_WIN32
2233 			efree(filename);
2234 #endif
2235 			return FAILURE;
2236 		}
2237 
2238 		ext_len = 0;
2239 		/* no extension detected - instead we are dealing with an alias */
2240 	}
2241 
2242 	*arch_len = ext_str - filename + ext_len;
2243 	*arch = estrndup(filename, *arch_len);
2244 
2245 	if (ext_str[ext_len]) {
2246 		*entry_len = filename_len - *arch_len;
2247 		*entry = estrndup(ext_str+ext_len, *entry_len);
2248 #ifdef PHP_WIN32
2249 		phar_unixify_path_separators(*entry, *entry_len);
2250 #endif
2251 		*entry = phar_fix_filepath(*entry, entry_len, 0);
2252 	} else {
2253 		*entry_len = 1;
2254 		*entry = estrndup("/", 1);
2255 	}
2256 
2257 #ifdef PHP_WIN32
2258 	efree(filename);
2259 #endif
2260 
2261 	return SUCCESS;
2262 }
2263 /* }}} */
2264 
2265 /**
2266  * Invoked when a user calls Phar::mapPhar() from within an executing .phar
2267  * to set up its manifest directly
2268  */
2269 int phar_open_executed_filename(char *alias, int alias_len, char **error) /* {{{ */
2270 {
2271 	char *fname;
2272 	php_stream *fp;
2273 	int fname_len;
2274 	zend_string *actual = NULL;
2275 	int ret;
2276 
2277 	if (error) {
2278 		*error = NULL;
2279 	}
2280 
2281 	fname = (char*)zend_get_executed_filename();
2282 	fname_len = strlen(fname);
2283 
2284 	if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, 0, REPORT_ERRORS, NULL, 0) == SUCCESS) {
2285 		return SUCCESS;
2286 	}
2287 
2288 	if (!strcmp(fname, "[no active file]")) {
2289 		if (error) {
2290 			spprintf(error, 0, "cannot initialize a phar outside of PHP execution");
2291 		}
2292 		return FAILURE;
2293 	}
2294 
2295 	if (0 == zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1)) {
2296 		if (error) {
2297 			spprintf(error, 0, "__HALT_COMPILER(); must be declared in a phar");
2298 		}
2299 		return FAILURE;
2300 	}
2301 
2302 	if (php_check_open_basedir(fname)) {
2303 		return FAILURE;
2304 	}
2305 
2306 	fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, &actual);
2307 
2308 	if (!fp) {
2309 		if (error) {
2310 			spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
2311 		}
2312 		if (actual) {
2313 			zend_string_release(actual);
2314 		}
2315 		return FAILURE;
2316 	}
2317 
2318 	if (actual) {
2319 		fname = ZSTR_VAL(actual);
2320 		fname_len = ZSTR_LEN(actual);
2321 	}
2322 
2323 	ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, REPORT_ERRORS, NULL, 0, error);
2324 
2325 	if (actual) {
2326 		zend_string_release(actual);
2327 	}
2328 
2329 	return ret;
2330 }
2331 /* }}} */
2332 
2333 /**
2334  * Validate the CRC32 of a file opened from within the phar
2335  */
2336 int phar_postprocess_file(phar_entry_data *idata, uint32_t crc32, char **error, int process_zip) /* {{{ */
2337 {
2338 	uint32_t crc = ~0;
2339 	int len = idata->internal_file->uncompressed_filesize;
2340 	php_stream *fp = idata->fp;
2341 	phar_entry_info *entry = idata->internal_file;
2342 
2343 	if (error) {
2344 		*error = NULL;
2345 	}
2346 
2347 	if (entry->is_zip && process_zip > 0) {
2348 		/* verify local file header */
2349 		phar_zip_file_header local;
2350 		phar_zip_data_desc desc;
2351 
2352 		if (SUCCESS != phar_open_archive_fp(idata->phar)) {
2353 			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);
2354 			return FAILURE;
2355 		}
2356 		php_stream_seek(phar_get_entrypfp(idata->internal_file), entry->header_offset, SEEK_SET);
2357 
2358 		if (sizeof(local) != php_stream_read(phar_get_entrypfp(idata->internal_file), (char *) &local, sizeof(local))) {
2359 
2360 			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);
2361 			return FAILURE;
2362 		}
2363 
2364 		/* check for data descriptor */
2365 		if (((PHAR_ZIP_16(local.flags)) & 0x8) == 0x8) {
2366 			php_stream_seek(phar_get_entrypfp(idata->internal_file),
2367 					entry->header_offset + sizeof(local) +
2368 					PHAR_ZIP_16(local.filename_len) +
2369 					PHAR_ZIP_16(local.extra_len) +
2370 					entry->compressed_filesize, SEEK_SET);
2371 			if (sizeof(desc) != php_stream_read(phar_get_entrypfp(idata->internal_file),
2372 							    (char *) &desc, sizeof(desc))) {
2373 				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);
2374 				return FAILURE;
2375 			}
2376 			if (desc.signature[0] == 'P' && desc.signature[1] == 'K') {
2377 				memcpy(&(local.crc32), &(desc.crc32), 12);
2378 			} else {
2379 				/* old data descriptors have no signature */
2380 				memcpy(&(local.crc32), &desc, 12);
2381 			}
2382 		}
2383 		/* verify local header */
2384 		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)) {
2385 			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);
2386 			return FAILURE;
2387 		}
2388 
2389 		/* construct actual offset to file start - local extra_len can be different from central extra_len */
2390 		entry->offset = entry->offset_abs =
2391 			sizeof(local) + entry->header_offset + PHAR_ZIP_16(local.filename_len) + PHAR_ZIP_16(local.extra_len);
2392 
2393 		if (idata->zero && idata->zero != entry->offset_abs) {
2394 			idata->zero = entry->offset_abs;
2395 		}
2396 	}
2397 
2398 	if (process_zip == 1) {
2399 		return SUCCESS;
2400 	}
2401 
2402 	php_stream_seek(fp, idata->zero, SEEK_SET);
2403 
2404 	while (len--) {
2405 		CRC32(crc, php_stream_getc(fp));
2406 	}
2407 
2408 	php_stream_seek(fp, idata->zero, SEEK_SET);
2409 
2410 	if (~crc == crc32) {
2411 		entry->is_crc_checked = 1;
2412 		return SUCCESS;
2413 	} else {
2414 		spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (crc32 mismatch on file \"%s\")", idata->phar->fname, entry->filename);
2415 		return FAILURE;
2416 	}
2417 }
2418 /* }}} */
2419 
2420 static inline void phar_set_32(char *buffer, int var) /* {{{ */
2421 {
2422 #ifdef WORDS_BIGENDIAN
2423 	*((buffer) + 3) = (unsigned char) (((var) >> 24) & 0xFF);
2424 	*((buffer) + 2) = (unsigned char) (((var) >> 16) & 0xFF);
2425 	*((buffer) + 1) = (unsigned char) (((var) >> 8) & 0xFF);
2426 	*((buffer) + 0) = (unsigned char) ((var) & 0xFF);
2427 #else
2428 	 memcpy(buffer, &var, sizeof(var));
2429 #endif
2430 } /* }}} */
2431 
2432 static int phar_flush_clean_deleted_apply(zval *zv) /* {{{ */
2433 {
2434 	phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv);
2435 
2436 	if (entry->fp_refcount <= 0 && entry->is_deleted) {
2437 		return ZEND_HASH_APPLY_REMOVE;
2438 	} else {
2439 		return ZEND_HASH_APPLY_KEEP;
2440 	}
2441 }
2442 /* }}} */
2443 
2444 #include "stub.h"
2445 
2446 zend_string *phar_create_default_stub(const char *index_php, const char *web_index, char **error) /* {{{ */
2447 {
2448 	int index_len, web_len;
2449 
2450 	if (error) {
2451 		*error = NULL;
2452 	}
2453 
2454 	if (!index_php) {
2455 		index_php = "index.php";
2456 	}
2457 
2458 	if (!web_index) {
2459 		web_index = "index.php";
2460 	}
2461 
2462 	index_len = strlen(index_php);
2463 	web_len = strlen(web_index);
2464 
2465 	if (index_len > 400) {
2466 		/* ridiculous size not allowed for index.php startup filename */
2467 		if (error) {
2468 			spprintf(error, 0, "Illegal filename passed in for stub creation, was %d characters long, and only 400 or less is allowed", index_len);
2469 			return NULL;
2470 		}
2471 	}
2472 
2473 	if (web_len > 400) {
2474 		/* ridiculous size not allowed for index.php startup filename */
2475 		if (error) {
2476 			spprintf(error, 0, "Illegal web filename passed in for stub creation, was %d characters long, and only 400 or less is allowed", web_len);
2477 			return NULL;
2478 		}
2479 	}
2480 
2481 	return phar_get_stub(index_php, web_index, index_len+1, web_len+1);
2482 }
2483 /* }}} */
2484 
2485 /**
2486  * Save phar contents to disk
2487  *
2488  * user_stub contains either a string, or a resource pointer, if len is a negative length.
2489  * user_stub and len should be both 0 if the default or existing stub should be used
2490  */
2491 int phar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int convert, char **error) /* {{{ */
2492 {
2493 	char halt_stub[] = "__HALT_COMPILER();";
2494 	zend_string *newstub;
2495 	char *tmp;
2496 	phar_entry_info *entry, *newentry;
2497 	int halt_offset, restore_alias_len, global_flags = 0, closeoldfile;
2498 	char *pos, has_dirs = 0;
2499 	char manifest[18], entry_buffer[24];
2500 	zend_off_t manifest_ftell;
2501 	zend_long offset;
2502 	size_t wrote;
2503 	uint32_t manifest_len, mytime, loc, new_manifest_count;
2504 	uint32_t newcrc32;
2505 	php_stream *file, *oldfile, *newfile, *stubfile;
2506 	php_stream_filter *filter;
2507 	php_serialize_data_t metadata_hash;
2508 	smart_str main_metadata_str = {0};
2509 	int free_user_stub, free_fp = 1, free_ufp = 1;
2510 	int manifest_hack = 0;
2511 
2512 	if (phar->is_persistent) {
2513 		if (error) {
2514 			spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
2515 		}
2516 		return EOF;
2517 	}
2518 
2519 	if (error) {
2520 		*error = NULL;
2521 	}
2522 
2523 	if (!zend_hash_num_elements(&phar->manifest) && !user_stub) {
2524 		return EOF;
2525 	}
2526 
2527 	zend_hash_clean(&phar->virtual_dirs);
2528 
2529 	if (phar->is_zip) {
2530 		return phar_zip_flush(phar, user_stub, len, convert, error);
2531 	}
2532 
2533 	if (phar->is_tar) {
2534 		return phar_tar_flush(phar, user_stub, len, convert, error);
2535 	}
2536 
2537 	if (PHAR_G(readonly)) {
2538 		return EOF;
2539 	}
2540 
2541 	if (phar->fp && !phar->is_brandnew) {
2542 		oldfile = phar->fp;
2543 		closeoldfile = 0;
2544 		php_stream_rewind(oldfile);
2545 	} else {
2546 		oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
2547 		closeoldfile = oldfile != NULL;
2548 	}
2549 	newfile = php_stream_fopen_tmpfile();
2550 	if (!newfile) {
2551 		if (error) {
2552 			spprintf(error, 0, "unable to create temporary file");
2553 		}
2554 		if (closeoldfile) {
2555 			php_stream_close(oldfile);
2556 		}
2557 		return EOF;
2558 	}
2559 
2560 	if (user_stub) {
2561 		zend_string *suser_stub;
2562 		if (len < 0) {
2563 			/* resource passed in */
2564 			if (!(php_stream_from_zval_no_verify(stubfile, (zval *)user_stub))) {
2565 				if (closeoldfile) {
2566 					php_stream_close(oldfile);
2567 				}
2568 				php_stream_close(newfile);
2569 				if (error) {
2570 					spprintf(error, 0, "unable to access resource to copy stub to new phar \"%s\"", phar->fname);
2571 				}
2572 				return EOF;
2573 			}
2574 			if (len == -1) {
2575 				len = PHP_STREAM_COPY_ALL;
2576 			} else {
2577 				len = -len;
2578 			}
2579 			user_stub = 0;
2580 
2581 			if (!(suser_stub = php_stream_copy_to_mem(stubfile, len, 0))) {
2582 				if (closeoldfile) {
2583 					php_stream_close(oldfile);
2584 				}
2585 				php_stream_close(newfile);
2586 				if (error) {
2587 					spprintf(error, 0, "unable to read resource to copy stub to new phar \"%s\"", phar->fname);
2588 				}
2589 				return EOF;
2590 			}
2591 			free_user_stub = 1;
2592 			user_stub = ZSTR_VAL(suser_stub);
2593 			len = ZSTR_LEN(suser_stub);
2594 		} else {
2595 			free_user_stub = 0;
2596 		}
2597 		tmp = estrndup(user_stub, len);
2598 		if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
2599 			efree(tmp);
2600 			if (closeoldfile) {
2601 				php_stream_close(oldfile);
2602 			}
2603 			php_stream_close(newfile);
2604 			if (error) {
2605 				spprintf(error, 0, "illegal stub for phar \"%s\"", phar->fname);
2606 			}
2607 			if (free_user_stub) {
2608 				zend_string_free(suser_stub);
2609 			}
2610 			return EOF;
2611 		}
2612 		pos = user_stub + (pos - tmp);
2613 		efree(tmp);
2614 		len = pos - user_stub + 18;
2615 		if ((size_t)len != php_stream_write(newfile, user_stub, len)
2616 		||			  5 != php_stream_write(newfile, " ?>\r\n", 5)) {
2617 			if (closeoldfile) {
2618 				php_stream_close(oldfile);
2619 			}
2620 			php_stream_close(newfile);
2621 			if (error) {
2622 				spprintf(error, 0, "unable to create stub from string in new phar \"%s\"", phar->fname);
2623 			}
2624 			if (free_user_stub) {
2625 				zend_string_free(suser_stub);
2626 			}
2627 			return EOF;
2628 		}
2629 		phar->halt_offset = len + 5;
2630 		if (free_user_stub) {
2631 			zend_string_free(suser_stub);
2632 		}
2633 	} else {
2634 		size_t written;
2635 
2636 		if (!user_stub && phar->halt_offset && oldfile && !phar->is_brandnew) {
2637 			php_stream_copy_to_stream_ex(oldfile, newfile, phar->halt_offset, &written);
2638 			newstub = NULL;
2639 		} else {
2640 			/* this is either a brand new phar or a default stub overwrite */
2641 			newstub = phar_create_default_stub(NULL, NULL, NULL);
2642 			phar->halt_offset = ZSTR_LEN(newstub);
2643 			written = php_stream_write(newfile, ZSTR_VAL(newstub), phar->halt_offset);
2644 		}
2645 		if (phar->halt_offset != written) {
2646 			if (closeoldfile) {
2647 				php_stream_close(oldfile);
2648 			}
2649 			php_stream_close(newfile);
2650 			if (error) {
2651 				if (newstub) {
2652 					spprintf(error, 0, "unable to create stub in new phar \"%s\"", phar->fname);
2653 				} else {
2654 					spprintf(error, 0, "unable to copy stub of old phar to new phar \"%s\"", phar->fname);
2655 				}
2656 			}
2657 			if (newstub) {
2658 				zend_string_free(newstub);
2659 			}
2660 			return EOF;
2661 		}
2662 		if (newstub) {
2663 			zend_string_free(newstub);
2664 		}
2665 	}
2666 	manifest_ftell = php_stream_tell(newfile);
2667 	halt_offset = manifest_ftell;
2668 
2669 	/* Check whether we can get rid of some of the deleted entries which are
2670 	 * unused. However some might still be in use so even after this clean-up
2671 	 * we need to skip entries marked is_deleted. */
2672 	zend_hash_apply(&phar->manifest, phar_flush_clean_deleted_apply);
2673 
2674 	/* compress as necessary, calculate crcs, serialize meta-data, manifest size, and file sizes */
2675 	main_metadata_str.s = NULL;
2676 	if (Z_TYPE(phar->metadata) != IS_UNDEF) {
2677 		PHP_VAR_SERIALIZE_INIT(metadata_hash);
2678 		php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash);
2679 		PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2680 	}
2681 	new_manifest_count = 0;
2682 	offset = 0;
2683 	for (zend_hash_internal_pointer_reset(&phar->manifest);
2684 		zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
2685 		zend_hash_move_forward(&phar->manifest)) {
2686 		if ((entry = zend_hash_get_current_data_ptr(&phar->manifest)) == NULL) {
2687 			continue;
2688 		}
2689 		if (entry->cfp) {
2690 			/* did we forget to get rid of cfp last time? */
2691 			php_stream_close(entry->cfp);
2692 			entry->cfp = 0;
2693 		}
2694 		if (entry->is_deleted || entry->is_mounted) {
2695 			/* remove this from the new phar */
2696 			continue;
2697 		}
2698 		if (!entry->is_modified && entry->fp_refcount) {
2699 			/* open file pointers refer to this fp, do not free the stream */
2700 			switch (entry->fp_type) {
2701 				case PHAR_FP:
2702 					free_fp = 0;
2703 					break;
2704 				case PHAR_UFP:
2705 					free_ufp = 0;
2706 				default:
2707 					break;
2708 			}
2709 		}
2710 		/* after excluding deleted files, calculate manifest size in bytes and number of entries */
2711 		++new_manifest_count;
2712 		phar_add_virtual_dirs(phar, entry->filename, entry->filename_len);
2713 
2714 		if (entry->is_dir) {
2715 			/* we use this to calculate API version, 1.1.1 is used for phars with directories */
2716 			has_dirs = 1;
2717 		}
2718 		if (Z_TYPE(entry->metadata) != IS_UNDEF) {
2719 			if (entry->metadata_str.s) {
2720 				smart_str_free(&entry->metadata_str);
2721 			}
2722 			entry->metadata_str.s = NULL;
2723 			PHP_VAR_SERIALIZE_INIT(metadata_hash);
2724 			php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash);
2725 			PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2726 		} else {
2727 			if (entry->metadata_str.s) {
2728 				smart_str_free(&entry->metadata_str);
2729 			}
2730 			entry->metadata_str.s = NULL;
2731 		}
2732 
2733 		/* 32 bits for filename length, length of filename, manifest + metadata, and add 1 for trailing / if a directory */
2734 		offset += 4 + entry->filename_len + sizeof(entry_buffer) + (entry->metadata_str.s ? ZSTR_LEN(entry->metadata_str.s) : 0) + (entry->is_dir ? 1 : 0);
2735 
2736 		/* compress and rehash as necessary */
2737 		if ((oldfile && !entry->is_modified) || entry->is_dir) {
2738 			if (entry->fp_type == PHAR_UFP) {
2739 				/* reset so we can copy the compressed data over */
2740 				entry->fp_type = PHAR_FP;
2741 			}
2742 			continue;
2743 		}
2744 		if (!phar_get_efp(entry, 0)) {
2745 			/* re-open internal file pointer just-in-time */
2746 			newentry = phar_open_jit(phar, entry, error);
2747 			if (!newentry) {
2748 				/* major problem re-opening, so we ignore this file and the error */
2749 				efree(*error);
2750 				*error = NULL;
2751 				continue;
2752 			}
2753 			entry = newentry;
2754 		}
2755 		file = phar_get_efp(entry, 0);
2756 		if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 1)) {
2757 			if (closeoldfile) {
2758 				php_stream_close(oldfile);
2759 			}
2760 			php_stream_close(newfile);
2761 			if (error) {
2762 				spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2763 			}
2764 			return EOF;
2765 		}
2766 		newcrc32 = ~0;
2767 		mytime = entry->uncompressed_filesize;
2768 		for (loc = 0;loc < mytime; ++loc) {
2769 			CRC32(newcrc32, php_stream_getc(file));
2770 		}
2771 		entry->crc32 = ~newcrc32;
2772 		entry->is_crc_checked = 1;
2773 		if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
2774 			/* not compressed */
2775 			entry->compressed_filesize = entry->uncompressed_filesize;
2776 			continue;
2777 		}
2778 		filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0);
2779 		if (!filter) {
2780 			if (closeoldfile) {
2781 				php_stream_close(oldfile);
2782 			}
2783 			php_stream_close(newfile);
2784 			if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
2785 				if (error) {
2786 					spprintf(error, 0, "unable to gzip compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2787 				}
2788 			} else {
2789 				if (error) {
2790 					spprintf(error, 0, "unable to bzip2 compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2791 				}
2792 			}
2793 			return EOF;
2794 		}
2795 
2796 		/* create new file that holds the compressed version */
2797 		/* work around inability to specify freedom in write and strictness
2798 		in read count */
2799 		entry->cfp = php_stream_fopen_tmpfile();
2800 		if (!entry->cfp) {
2801 			if (error) {
2802 				spprintf(error, 0, "unable to create temporary file");
2803 			}
2804 			if (closeoldfile) {
2805 				php_stream_close(oldfile);
2806 			}
2807 			php_stream_close(newfile);
2808 			return EOF;
2809 		}
2810 		php_stream_flush(file);
2811 		if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
2812 			if (closeoldfile) {
2813 				php_stream_close(oldfile);
2814 			}
2815 			php_stream_close(newfile);
2816 			if (error) {
2817 				spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2818 			}
2819 			return EOF;
2820 		}
2821 		php_stream_filter_append((&entry->cfp->writefilters), filter);
2822 		if (SUCCESS != php_stream_copy_to_stream_ex(file, entry->cfp, entry->uncompressed_filesize, NULL)) {
2823 			if (closeoldfile) {
2824 				php_stream_close(oldfile);
2825 			}
2826 			php_stream_close(newfile);
2827 			if (error) {
2828 				spprintf(error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2829 			}
2830 			return EOF;
2831 		}
2832 		php_stream_filter_flush(filter, 1);
2833 		php_stream_flush(entry->cfp);
2834 		php_stream_filter_remove(filter, 1);
2835 		php_stream_seek(entry->cfp, 0, SEEK_END);
2836 		entry->compressed_filesize = (uint32_t) php_stream_tell(entry->cfp);
2837 		/* generate crc on compressed file */
2838 		php_stream_rewind(entry->cfp);
2839 		entry->old_flags = entry->flags;
2840 		entry->is_modified = 1;
2841 		global_flags |= (entry->flags & PHAR_ENT_COMPRESSION_MASK);
2842 	}
2843 	global_flags |= PHAR_HDR_SIGNATURE;
2844 
2845 	/* write out manifest pre-header */
2846 	/*  4: manifest length
2847 	 *  4: manifest entry count
2848 	 *  2: phar version
2849 	 *  4: phar global flags
2850 	 *  4: alias length
2851 	 *  ?: the alias itself
2852 	 *  4: phar metadata length
2853 	 *  ?: phar metadata
2854 	 */
2855 	restore_alias_len = phar->alias_len;
2856 	if (phar->is_temporary_alias) {
2857 		phar->alias_len = 0;
2858 	}
2859 
2860 	manifest_len = offset + phar->alias_len + sizeof(manifest) + (main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0);
2861 	phar_set_32(manifest, manifest_len);
2862 	/* Hack - see bug #65028, add padding byte to the end of the manifest */
2863 	if(manifest[0] == '\r' || manifest[0] == '\n') {
2864 		manifest_len++;
2865 		phar_set_32(manifest, manifest_len);
2866 		manifest_hack = 1;
2867 	}
2868 	phar_set_32(manifest+4, new_manifest_count);
2869 	if (has_dirs) {
2870 		*(manifest + 8) = (unsigned char) (((PHAR_API_VERSION) >> 8) & 0xFF);
2871 		*(manifest + 9) = (unsigned char) (((PHAR_API_VERSION) & 0xF0));
2872 	} else {
2873 		*(manifest + 8) = (unsigned char) (((PHAR_API_VERSION_NODIR) >> 8) & 0xFF);
2874 		*(manifest + 9) = (unsigned char) (((PHAR_API_VERSION_NODIR) & 0xF0));
2875 	}
2876 	phar_set_32(manifest+10, global_flags);
2877 	phar_set_32(manifest+14, phar->alias_len);
2878 
2879 	/* write the manifest header */
2880 	if (sizeof(manifest) != php_stream_write(newfile, manifest, sizeof(manifest))
2881 	|| (size_t)phar->alias_len != php_stream_write(newfile, phar->alias, phar->alias_len)) {
2882 
2883 		if (closeoldfile) {
2884 			php_stream_close(oldfile);
2885 		}
2886 
2887 		php_stream_close(newfile);
2888 		phar->alias_len = restore_alias_len;
2889 
2890 		if (error) {
2891 			spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", phar->fname);
2892 		}
2893 
2894 		return EOF;
2895 	}
2896 
2897 	phar->alias_len = restore_alias_len;
2898 
2899 	phar_set_32(manifest, main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0);
2900 	if (4 != php_stream_write(newfile, manifest, 4) || ((main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0)
2901 	&& ZSTR_LEN(main_metadata_str.s) != php_stream_write(newfile, ZSTR_VAL(main_metadata_str.s), ZSTR_LEN(main_metadata_str.s)))) {
2902 		smart_str_free(&main_metadata_str);
2903 
2904 		if (closeoldfile) {
2905 			php_stream_close(oldfile);
2906 		}
2907 
2908 		php_stream_close(newfile);
2909 		phar->alias_len = restore_alias_len;
2910 
2911 		if (error) {
2912 			spprintf(error, 0, "unable to write manifest meta-data of new phar \"%s\"", phar->fname);
2913 		}
2914 
2915 		return EOF;
2916 	}
2917 	smart_str_free(&main_metadata_str);
2918 
2919 	/* re-calculate the manifest location to simplify later code */
2920 	manifest_ftell = php_stream_tell(newfile);
2921 
2922 	/* now write the manifest */
2923 	for (zend_hash_internal_pointer_reset(&phar->manifest);
2924 		zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
2925 		zend_hash_move_forward(&phar->manifest)) {
2926 
2927 		if ((entry = zend_hash_get_current_data_ptr(&phar->manifest)) == NULL) {
2928 			continue;
2929 		}
2930 
2931 		if (entry->is_deleted || entry->is_mounted) {
2932 			/* remove this from the new phar if deleted, ignore if mounted */
2933 			continue;
2934 		}
2935 
2936 		if (entry->is_dir) {
2937 			/* add 1 for trailing slash */
2938 			phar_set_32(entry_buffer, entry->filename_len + 1);
2939 		} else {
2940 			phar_set_32(entry_buffer, entry->filename_len);
2941 		}
2942 
2943 		if (4 != php_stream_write(newfile, entry_buffer, 4)
2944 		|| entry->filename_len != php_stream_write(newfile, entry->filename, entry->filename_len)
2945 		|| (entry->is_dir && 1 != php_stream_write(newfile, "/", 1))) {
2946 			if (closeoldfile) {
2947 				php_stream_close(oldfile);
2948 			}
2949 			php_stream_close(newfile);
2950 			if (error) {
2951 				if (entry->is_dir) {
2952 					spprintf(error, 0, "unable to write filename of directory \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
2953 				} else {
2954 					spprintf(error, 0, "unable to write filename of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
2955 				}
2956 			}
2957 			return EOF;
2958 		}
2959 
2960 		/* set the manifest meta-data:
2961 			4: uncompressed filesize
2962 			4: creation timestamp
2963 			4: compressed filesize
2964 			4: crc32
2965 			4: flags
2966 			4: metadata-len
2967 			+: metadata
2968 		*/
2969 		mytime = time(NULL);
2970 		phar_set_32(entry_buffer, entry->uncompressed_filesize);
2971 		phar_set_32(entry_buffer+4, mytime);
2972 		phar_set_32(entry_buffer+8, entry->compressed_filesize);
2973 		phar_set_32(entry_buffer+12, entry->crc32);
2974 		phar_set_32(entry_buffer+16, entry->flags);
2975 		phar_set_32(entry_buffer+20, entry->metadata_str.s ? ZSTR_LEN(entry->metadata_str.s) : 0);
2976 
2977 		if (sizeof(entry_buffer) != php_stream_write(newfile, entry_buffer, sizeof(entry_buffer))
2978 		|| (entry->metadata_str.s &&
2979 		    ZSTR_LEN(entry->metadata_str.s) != php_stream_write(newfile, ZSTR_VAL(entry->metadata_str.s), ZSTR_LEN(entry->metadata_str.s)))) {
2980 			if (closeoldfile) {
2981 				php_stream_close(oldfile);
2982 			}
2983 
2984 			php_stream_close(newfile);
2985 
2986 			if (error) {
2987 				spprintf(error, 0, "unable to write temporary manifest of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
2988 			}
2989 
2990 			return EOF;
2991 		}
2992 	}
2993 	/* Hack - see bug #65028, add padding byte to the end of the manifest */
2994 	if(manifest_hack) {
2995 		if(1 != php_stream_write(newfile, manifest, 1)) {
2996 			if (closeoldfile) {
2997 				php_stream_close(oldfile);
2998 			}
2999 
3000 			php_stream_close(newfile);
3001 
3002 			if (error) {
3003 				spprintf(error, 0, "unable to write manifest padding byte");
3004 			}
3005 
3006 			return EOF;
3007 		}
3008 	}
3009 
3010 	/* now copy the actual file data to the new phar */
3011 	offset = php_stream_tell(newfile);
3012 	for (zend_hash_internal_pointer_reset(&phar->manifest);
3013 		zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
3014 		zend_hash_move_forward(&phar->manifest)) {
3015 
3016 		if ((entry = zend_hash_get_current_data_ptr(&phar->manifest)) == NULL) {
3017 			continue;
3018 		}
3019 
3020 		if (entry->is_deleted || entry->is_dir || entry->is_mounted) {
3021 			continue;
3022 		}
3023 
3024 		if (entry->cfp) {
3025 			file = entry->cfp;
3026 			php_stream_rewind(file);
3027 		} else {
3028 			file = phar_get_efp(entry, 0);
3029 			if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
3030 				if (closeoldfile) {
3031 					php_stream_close(oldfile);
3032 				}
3033 				php_stream_close(newfile);
3034 				if (error) {
3035 					spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3036 				}
3037 				return EOF;
3038 			}
3039 		}
3040 
3041 		if (!file) {
3042 			if (closeoldfile) {
3043 				php_stream_close(oldfile);
3044 			}
3045 			php_stream_close(newfile);
3046 			if (error) {
3047 				spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3048 			}
3049 			return EOF;
3050 		}
3051 
3052 		/* this will have changed for all files that have either changed compression or been modified */
3053 		entry->offset = entry->offset_abs = offset;
3054 		offset += entry->compressed_filesize;
3055 		if (php_stream_copy_to_stream_ex(file, newfile, entry->compressed_filesize, &wrote) == FAILURE) {
3056 			if (closeoldfile) {
3057 				php_stream_close(oldfile);
3058 			}
3059 
3060 			php_stream_close(newfile);
3061 
3062 			if (error) {
3063 				spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
3064 			}
3065 
3066 			return EOF;
3067 		}
3068 
3069 		entry->is_modified = 0;
3070 
3071 		if (entry->cfp) {
3072 			php_stream_close(entry->cfp);
3073 			entry->cfp = NULL;
3074 		}
3075 
3076 		if (entry->fp_type == PHAR_MOD) {
3077 			/* 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 */
3078 			if (entry->fp_refcount == 0 && entry->fp != phar->fp && entry->fp != phar->ufp) {
3079 				php_stream_close(entry->fp);
3080 			}
3081 
3082 			entry->fp = NULL;
3083 			entry->fp_type = PHAR_FP;
3084 		} else if (entry->fp_type == PHAR_UFP) {
3085 			entry->fp_type = PHAR_FP;
3086 		}
3087 	}
3088 
3089 	/* append signature */
3090 	if (global_flags & PHAR_HDR_SIGNATURE) {
3091 		char sig_buf[4];
3092 
3093 		php_stream_rewind(newfile);
3094 
3095 		if (phar->signature) {
3096 			efree(phar->signature);
3097 			phar->signature = NULL;
3098 		}
3099 
3100 		switch(phar->sig_flags) {
3101 #ifndef PHAR_HASH_OK
3102 			case PHAR_SIG_SHA512:
3103 			case PHAR_SIG_SHA256:
3104 				if (closeoldfile) {
3105 					php_stream_close(oldfile);
3106 				}
3107 				php_stream_close(newfile);
3108 				if (error) {
3109 					spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\" with requested hash type", entry->filename, phar->fname);
3110 				}
3111 				return EOF;
3112 #endif
3113 			default: {
3114 				char *digest = NULL;
3115 				int digest_len;
3116 
3117 				if (FAILURE == phar_create_signature(phar, newfile, &digest, &digest_len, error)) {
3118 					if (error) {
3119 						char *save = *error;
3120 						spprintf(error, 0, "phar error: unable to write signature: %s", save);
3121 						efree(save);
3122 					}
3123 					if (digest) {
3124 						efree(digest);
3125 					}
3126 					if (closeoldfile) {
3127 						php_stream_close(oldfile);
3128 					}
3129 					php_stream_close(newfile);
3130 					return EOF;
3131 				}
3132 
3133 				php_stream_write(newfile, digest, digest_len);
3134 				efree(digest);
3135 				if (phar->sig_flags == PHAR_SIG_OPENSSL) {
3136 					phar_set_32(sig_buf, digest_len);
3137 					php_stream_write(newfile, sig_buf, 4);
3138 				}
3139 				break;
3140 			}
3141 		}
3142 		phar_set_32(sig_buf, phar->sig_flags);
3143 		php_stream_write(newfile, sig_buf, 4);
3144 		php_stream_write(newfile, "GBMB", 4);
3145 	}
3146 
3147 	/* finally, close the temp file, rename the original phar,
3148 	   move the temp to the old phar, unlink the old phar, and reload it into memory
3149 	*/
3150 	if (phar->fp && free_fp) {
3151 		php_stream_close(phar->fp);
3152 	}
3153 
3154 	if (phar->ufp) {
3155 		if (free_ufp) {
3156 			php_stream_close(phar->ufp);
3157 		}
3158 		phar->ufp = NULL;
3159 	}
3160 
3161 	if (closeoldfile) {
3162 		php_stream_close(oldfile);
3163 	}
3164 
3165 	phar->internal_file_start = halt_offset + manifest_len + 4;
3166 	phar->halt_offset = halt_offset;
3167 	phar->is_brandnew = 0;
3168 
3169 	php_stream_rewind(newfile);
3170 
3171 	if (phar->donotflush) {
3172 		/* deferred flush */
3173 		phar->fp = newfile;
3174 	} else {
3175 		phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
3176 		if (!phar->fp) {
3177 			phar->fp = newfile;
3178 			if (error) {
3179 				spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
3180 			}
3181 			return EOF;
3182 		}
3183 
3184 		if (phar->flags & PHAR_FILE_COMPRESSED_GZ) {
3185 			/* to properly compress, we have to tell zlib to add a zlib header */
3186 			zval filterparams;
3187 
3188 			array_init(&filterparams);
3189 			add_assoc_long(&filterparams, "window", MAX_WBITS+16);
3190 			filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp));
3191 			zval_dtor(&filterparams);
3192 
3193 			if (!filter) {
3194 				if (error) {
3195 					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);
3196 				}
3197 				return EOF;
3198 			}
3199 
3200 			php_stream_filter_append(&phar->fp->writefilters, filter);
3201 			php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3202 			php_stream_filter_flush(filter, 1);
3203 			php_stream_filter_remove(filter, 1);
3204 			php_stream_close(phar->fp);
3205 			/* use the temp stream as our base */
3206 			phar->fp = newfile;
3207 		} else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) {
3208 			filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp));
3209 			php_stream_filter_append(&phar->fp->writefilters, filter);
3210 			php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3211 			php_stream_filter_flush(filter, 1);
3212 			php_stream_filter_remove(filter, 1);
3213 			php_stream_close(phar->fp);
3214 			/* use the temp stream as our base */
3215 			phar->fp = newfile;
3216 		} else {
3217 			php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3218 			/* we could also reopen the file in "rb" mode but there is no need for that */
3219 			php_stream_close(newfile);
3220 		}
3221 	}
3222 
3223 	if (-1 == php_stream_seek(phar->fp, phar->halt_offset, SEEK_SET)) {
3224 		if (error) {
3225 			spprintf(error, 0, "unable to seek to __HALT_COMPILER(); in new phar \"%s\"", phar->fname);
3226 		}
3227 		return EOF;
3228 	}
3229 
3230 	return EOF;
3231 }
3232 /* }}} */
3233 
3234 #ifdef COMPILE_DL_PHAR
3235 #ifdef ZTS
3236 ZEND_TSRMLS_CACHE_DEFINE()
3237 #endif
3238 ZEND_GET_MODULE(phar)
3239 #endif
3240 
3241 /* {{{ phar_functions[]
3242  *
3243  * Every user visible function must have an entry in phar_functions[].
3244  */
3245 zend_function_entry phar_functions[] = {
3246 	PHP_FE_END
3247 };
3248 /* }}}*/
3249 
3250 static size_t phar_zend_stream_reader(void *handle, char *buf, size_t len) /* {{{ */
3251 {
3252 	return php_stream_read(phar_get_pharfp((phar_archive_data*)handle), buf, len);
3253 }
3254 /* }}} */
3255 
3256 static size_t phar_zend_stream_fsizer(void *handle) /* {{{ */
3257 {
3258 	return ((phar_archive_data*)handle)->halt_offset + 32;
3259 } /* }}} */
3260 
3261 zend_op_array *(*phar_orig_compile_file)(zend_file_handle *file_handle, int type);
3262 #define phar_orig_zend_open zend_stream_open_function
3263 
3264 static zend_string *phar_resolve_path(const char *filename, int filename_len)
3265 {
3266 	return phar_find_in_include_path((char *) filename, filename_len, NULL);
3267 }
3268 
3269 static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type) /* {{{ */
3270 {
3271 	zend_op_array *res;
3272 	char *name = NULL;
3273 	int failed;
3274 	phar_archive_data *phar;
3275 
3276 	if (!file_handle || !file_handle->filename) {
3277 		return phar_orig_compile_file(file_handle, type);
3278 	}
3279 	if (strstr(file_handle->filename, ".phar") && !strstr(file_handle->filename, "://")) {
3280 		if (SUCCESS == phar_open_from_filename((char*)file_handle->filename, strlen(file_handle->filename), NULL, 0, 0, &phar, NULL)) {
3281 			if (phar->is_zip || phar->is_tar) {
3282 				zend_file_handle f = *file_handle;
3283 
3284 				/* zip or tar-based phar */
3285 				spprintf(&name, 4096, "phar://%s/%s", file_handle->filename, ".phar/stub.php");
3286 				if (SUCCESS == phar_orig_zend_open((const char *)name, &f)) {
3287 
3288 					efree(name);
3289 					name = NULL;
3290 
3291 					f.filename = file_handle->filename;
3292 					if (f.opened_path) {
3293 						efree(f.opened_path);
3294 					}
3295 					f.opened_path = file_handle->opened_path;
3296 					f.free_filename = file_handle->free_filename;
3297 
3298 					switch (file_handle->type) {
3299 						case ZEND_HANDLE_STREAM:
3300 						case ZEND_HANDLE_MAPPED:
3301 							if (file_handle->handle.stream.closer && file_handle->handle.stream.handle) {
3302 								file_handle->handle.stream.closer(file_handle->handle.stream.handle);
3303 							}
3304 							file_handle->handle.stream.handle = NULL;
3305 							break;
3306 						default:
3307 							break;
3308 					}
3309 					*file_handle = f;
3310 				}
3311 			} else if (phar->flags & PHAR_FILE_COMPRESSION_MASK) {
3312 				zend_file_handle_dtor(file_handle);
3313 				/* compressed phar */
3314 				file_handle->type = ZEND_HANDLE_STREAM;
3315 				/* we do our own reading directly from the phar, don't change the next line */
3316 				file_handle->handle.stream.handle  = phar;
3317 				file_handle->handle.stream.reader  = phar_zend_stream_reader;
3318 				file_handle->handle.stream.closer  = NULL;
3319 				file_handle->handle.stream.fsizer  = phar_zend_stream_fsizer;
3320 				file_handle->handle.stream.isatty  = 0;
3321 				phar->is_persistent ?
3322 					php_stream_rewind(PHAR_G(cached_fp)[phar->phar_pos].fp) :
3323 					php_stream_rewind(phar->fp);
3324 				memset(&file_handle->handle.stream.mmap, 0, sizeof(file_handle->handle.stream.mmap));
3325 			}
3326 		}
3327 	}
3328 
3329 	zend_try {
3330 		failed = 0;
3331 		CG(zend_lineno) = 0;
3332 		res = phar_orig_compile_file(file_handle, type);
3333 	} zend_catch {
3334 		failed = 1;
3335 		res = NULL;
3336 	} zend_end_try();
3337 
3338 	if (name) {
3339 		efree(name);
3340 	}
3341 
3342 	if (failed) {
3343 		zend_bailout();
3344 	}
3345 
3346 	return res;
3347 }
3348 /* }}} */
3349 
3350 typedef zend_op_array* (zend_compile_t)(zend_file_handle*, int);
3351 typedef zend_compile_t* (compile_hook)(zend_compile_t *ptr);
3352 
3353 static void mime_type_dtor(zval *zv)
3354 {
3355 	free(Z_PTR_P(zv));
3356 }
3357 
3358 PHP_GINIT_FUNCTION(phar) /* {{{ */
3359 {
3360 #if defined(COMPILE_DL_PHAR) && defined(ZTS)
3361 	ZEND_TSRMLS_CACHE_UPDATE();
3362 #endif
3363 	phar_mime_type mime;
3364 
3365 	memset(phar_globals, 0, sizeof(zend_phar_globals));
3366 	phar_globals->readonly = 1;
3367 
3368 	zend_hash_init(&phar_globals->mime_types, 0, NULL, mime_type_dtor, 1);
3369 
3370 #define PHAR_SET_MIME(mimetype, ret, fileext) \
3371 		mime.mime = mimetype; \
3372 		mime.len = sizeof((mimetype))+1; \
3373 		mime.type = ret; \
3374 		zend_hash_str_add_mem(&phar_globals->mime_types, fileext, sizeof(fileext)-1, (void *)&mime, sizeof(phar_mime_type)); \
3375 
3376 	PHAR_SET_MIME("text/html", PHAR_MIME_PHPS, "phps")
3377 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c")
3378 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cc")
3379 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cpp")
3380 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c++")
3381 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "dtd")
3382 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "h")
3383 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "log")
3384 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "rng")
3385 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "txt")
3386 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "xsd")
3387 	PHAR_SET_MIME("", PHAR_MIME_PHP, "php")
3388 	PHAR_SET_MIME("", PHAR_MIME_PHP, "inc")
3389 	PHAR_SET_MIME("video/avi", PHAR_MIME_OTHER, "avi")
3390 	PHAR_SET_MIME("image/bmp", PHAR_MIME_OTHER, "bmp")
3391 	PHAR_SET_MIME("text/css", PHAR_MIME_OTHER, "css")
3392 	PHAR_SET_MIME("image/gif", PHAR_MIME_OTHER, "gif")
3393 	PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htm")
3394 	PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "html")
3395 	PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htmls")
3396 	PHAR_SET_MIME("image/x-ico", PHAR_MIME_OTHER, "ico")
3397 	PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpe")
3398 	PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpg")
3399 	PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpeg")
3400 	PHAR_SET_MIME("application/x-javascript", PHAR_MIME_OTHER, "js")
3401 	PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "midi")
3402 	PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "mid")
3403 	PHAR_SET_MIME("audio/mod", PHAR_MIME_OTHER, "mod")
3404 	PHAR_SET_MIME("movie/quicktime", PHAR_MIME_OTHER, "mov")
3405 	PHAR_SET_MIME("audio/mp3", PHAR_MIME_OTHER, "mp3")
3406 	PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpg")
3407 	PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpeg")
3408 	PHAR_SET_MIME("application/pdf", PHAR_MIME_OTHER, "pdf")
3409 	PHAR_SET_MIME("image/png", PHAR_MIME_OTHER, "png")
3410 	PHAR_SET_MIME("application/shockwave-flash", PHAR_MIME_OTHER, "swf")
3411 	PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tif")
3412 	PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tiff")
3413 	PHAR_SET_MIME("audio/wav", PHAR_MIME_OTHER, "wav")
3414 	PHAR_SET_MIME("image/xbm", PHAR_MIME_OTHER, "xbm")
3415 	PHAR_SET_MIME("text/xml", PHAR_MIME_OTHER, "xml")
3416 
3417 	phar_restore_orig_functions();
3418 }
3419 /* }}} */
3420 
3421 PHP_GSHUTDOWN_FUNCTION(phar) /* {{{ */
3422 {
3423 	zend_hash_destroy(&phar_globals->mime_types);
3424 }
3425 /* }}} */
3426 
3427 PHP_MINIT_FUNCTION(phar) /* {{{ */
3428 {
3429 	REGISTER_INI_ENTRIES();
3430 
3431 	phar_orig_compile_file = zend_compile_file;
3432 	zend_compile_file = phar_compile_file;
3433 
3434 	phar_save_resolve_path = zend_resolve_path;
3435 	zend_resolve_path = phar_resolve_path;
3436 
3437 	phar_object_init();
3438 
3439 	phar_intercept_functions_init();
3440 	phar_save_orig_functions();
3441 
3442 	return php_register_url_stream_wrapper("phar", &php_stream_phar_wrapper);
3443 }
3444 /* }}} */
3445 
3446 PHP_MSHUTDOWN_FUNCTION(phar) /* {{{ */
3447 {
3448 	php_unregister_url_stream_wrapper("phar");
3449 
3450 	phar_intercept_functions_shutdown();
3451 
3452 	if (zend_compile_file == phar_compile_file) {
3453 		zend_compile_file = phar_orig_compile_file;
3454 	}
3455 
3456 	if (PHAR_G(manifest_cached)) {
3457 		zend_hash_destroy(&(cached_phars));
3458 		zend_hash_destroy(&(cached_alias));
3459 	}
3460 
3461 	return SUCCESS;
3462 }
3463 /* }}} */
3464 
3465 void phar_request_initialize(void) /* {{{ */
3466 {
3467 	if (!PHAR_G(request_init))
3468 	{
3469 		PHAR_G(last_phar) = NULL;
3470 		PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
3471 		PHAR_G(has_bz2) = zend_hash_str_exists(&module_registry, "bz2", sizeof("bz2")-1);
3472 		PHAR_G(has_zlib) = zend_hash_str_exists(&module_registry, "zlib", sizeof("zlib")-1);
3473 		PHAR_G(request_init) = 1;
3474 		PHAR_G(request_ends) = 0;
3475 		PHAR_G(request_done) = 0;
3476 		zend_hash_init(&(PHAR_G(phar_fname_map)), 5, zend_get_hash_value, destroy_phar_data,  0);
3477 		zend_hash_init(&(PHAR_G(phar_persist_map)), 5, zend_get_hash_value, NULL,  0);
3478 		zend_hash_init(&(PHAR_G(phar_alias_map)), 5, zend_get_hash_value, NULL, 0);
3479 
3480 		if (PHAR_G(manifest_cached)) {
3481 			phar_archive_data *pphar;
3482 			phar_entry_fp *stuff = (phar_entry_fp *) ecalloc(zend_hash_num_elements(&cached_phars), sizeof(phar_entry_fp));
3483 
3484 			for (zend_hash_internal_pointer_reset(&cached_phars);
3485 			(pphar = zend_hash_get_current_data_ptr(&cached_phars)) != NULL;
3486 			zend_hash_move_forward(&cached_phars)) {
3487 				stuff[pphar->phar_pos].manifest = (phar_entry_fp_info *) ecalloc( zend_hash_num_elements(&(pphar->manifest)), sizeof(phar_entry_fp_info));
3488 			}
3489 
3490 			PHAR_G(cached_fp) = stuff;
3491 		}
3492 
3493 		PHAR_G(phar_SERVER_mung_list) = 0;
3494 		PHAR_G(cwd) = NULL;
3495 		PHAR_G(cwd_len) = 0;
3496 		PHAR_G(cwd_init) = 0;
3497 	}
3498 }
3499 /* }}} */
3500 
3501 PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */
3502 {
3503 	uint32_t i;
3504 
3505 	PHAR_G(request_ends) = 1;
3506 
3507 	if (PHAR_G(request_init))
3508 	{
3509 		phar_release_functions();
3510 		zend_hash_destroy(&(PHAR_G(phar_alias_map)));
3511 		PHAR_G(phar_alias_map.u.flags) = 0;
3512 		zend_hash_destroy(&(PHAR_G(phar_fname_map)));
3513 		PHAR_G(phar_fname_map.u.flags) = 0;
3514 		zend_hash_destroy(&(PHAR_G(phar_persist_map)));
3515 		PHAR_G(phar_persist_map.u.flags) = 0;
3516 		PHAR_G(phar_SERVER_mung_list) = 0;
3517 
3518 		if (PHAR_G(cached_fp)) {
3519 			for (i = 0; i < zend_hash_num_elements(&cached_phars); ++i) {
3520 				if (PHAR_G(cached_fp)[i].fp) {
3521 					php_stream_close(PHAR_G(cached_fp)[i].fp);
3522 				}
3523 				if (PHAR_G(cached_fp)[i].ufp) {
3524 					php_stream_close(PHAR_G(cached_fp)[i].ufp);
3525 				}
3526 				efree(PHAR_G(cached_fp)[i].manifest);
3527 			}
3528 			efree(PHAR_G(cached_fp));
3529 			PHAR_G(cached_fp) = 0;
3530 		}
3531 
3532 		PHAR_G(request_init) = 0;
3533 
3534 		if (PHAR_G(cwd)) {
3535 			efree(PHAR_G(cwd));
3536 		}
3537 
3538 		PHAR_G(cwd) = NULL;
3539 		PHAR_G(cwd_len) = 0;
3540 		PHAR_G(cwd_init) = 0;
3541 	}
3542 
3543 	PHAR_G(request_done) = 1;
3544 	return SUCCESS;
3545 }
3546 /* }}} */
3547 
3548 PHP_MINFO_FUNCTION(phar) /* {{{ */
3549 {
3550 	phar_request_initialize();
3551 	php_info_print_table_start();
3552 	php_info_print_table_header(2, "Phar: PHP Archive support", "enabled");
3553 	php_info_print_table_row(2, "Phar EXT version", PHP_PHAR_VERSION);
3554 	php_info_print_table_row(2, "Phar API version", PHP_PHAR_API_VERSION);
3555 	php_info_print_table_row(2, "SVN revision", "$Id: e117ab0dc068703c55b505e78a0d3b3752e9c0b7 $");
3556 	php_info_print_table_row(2, "Phar-based phar archives", "enabled");
3557 	php_info_print_table_row(2, "Tar-based phar archives", "enabled");
3558 	php_info_print_table_row(2, "ZIP-based phar archives", "enabled");
3559 
3560 	if (PHAR_G(has_zlib)) {
3561 		php_info_print_table_row(2, "gzip compression", "enabled");
3562 	} else {
3563 		php_info_print_table_row(2, "gzip compression", "disabled (install ext/zlib)");
3564 	}
3565 
3566 	if (PHAR_G(has_bz2)) {
3567 		php_info_print_table_row(2, "bzip2 compression", "enabled");
3568 	} else {
3569 		php_info_print_table_row(2, "bzip2 compression", "disabled (install pecl/bz2)");
3570 	}
3571 #ifdef PHAR_HAVE_OPENSSL
3572 	php_info_print_table_row(2, "Native OpenSSL support", "enabled");
3573 #else
3574 	if (zend_hash_str_exists(&module_registry, "openssl", sizeof("openssl")-1)) {
3575 		php_info_print_table_row(2, "OpenSSL support", "enabled");
3576 	} else {
3577 		php_info_print_table_row(2, "OpenSSL support", "disabled (install ext/openssl)");
3578 	}
3579 #endif
3580 	php_info_print_table_end();
3581 
3582 	php_info_print_box_start(0);
3583 	PUTS("Phar based on pear/PHP_Archive, original concept by Davey Shafik.");
3584 	PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3585 	PUTS("Phar fully realized by Gregory Beaver and Marcus Boerger.");
3586 	PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3587 	PUTS("Portions of tar implementation Copyright (c) 2003-2009 Tim Kientzle.");
3588 	php_info_print_box_end();
3589 
3590 	DISPLAY_INI_ENTRIES();
3591 }
3592 /* }}} */
3593 
3594 /* {{{ phar_module_entry
3595  */
3596 static const zend_module_dep phar_deps[] = {
3597 	ZEND_MOD_OPTIONAL("apc")
3598 	ZEND_MOD_OPTIONAL("bz2")
3599 	ZEND_MOD_OPTIONAL("openssl")
3600 	ZEND_MOD_OPTIONAL("zlib")
3601 	ZEND_MOD_OPTIONAL("standard")
3602 #if defined(HAVE_HASH) && !defined(COMPILE_DL_HASH)
3603 	ZEND_MOD_REQUIRED("hash")
3604 #endif
3605 	ZEND_MOD_REQUIRED("spl")
3606 	ZEND_MOD_END
3607 };
3608 
3609 zend_module_entry phar_module_entry = {
3610 	STANDARD_MODULE_HEADER_EX, NULL,
3611 	phar_deps,
3612 	"Phar",
3613 	phar_functions,
3614 	PHP_MINIT(phar),
3615 	PHP_MSHUTDOWN(phar),
3616 	NULL,
3617 	PHP_RSHUTDOWN(phar),
3618 	PHP_MINFO(phar),
3619 	PHP_PHAR_VERSION,
3620 	PHP_MODULE_GLOBALS(phar),   /* globals descriptor */
3621 	PHP_GINIT(phar),            /* globals ctor */
3622 	PHP_GSHUTDOWN(phar),        /* globals dtor */
3623 	NULL,                       /* post deactivate */
3624 	STANDARD_MODULE_PROPERTIES_EX
3625 };
3626 /* }}} */
3627 
3628 /*
3629  * Local variables:
3630  * tab-width: 4
3631  * c-basic-offset: 4
3632  * End:
3633  * vim600: noet sw=4 ts=4 fdm=marker
3634  * vim<600: noet sw=4 ts=4
3635  */
3636