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