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