xref: /PHP-8.3/ext/phar/phar_object.c (revision 6a8d0a05)
1 /*
2   +----------------------------------------------------------------------+
3   | phar php single-file executable PHP extension                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 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   | https://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 #include "phar_internal.h"
21 #include "func_interceptors.h"
22 #include "phar_object_arginfo.h"
23 
24 static zend_class_entry *phar_ce_archive;
25 static zend_class_entry *phar_ce_data;
26 static zend_class_entry *phar_ce_PharException;
27 static zend_class_entry *phar_ce_entry;
28 
phar_file_type(HashTable * mimes,char * file,char ** mime_type)29 static int phar_file_type(HashTable *mimes, char *file, char **mime_type) /* {{{ */
30 {
31 	char *ext;
32 	phar_mime_type *mime;
33 	ext = strrchr(file, '.');
34 	if (!ext) {
35 		*mime_type = "text/plain";
36 		/* no file extension = assume text/plain */
37 		return PHAR_MIME_OTHER;
38 	}
39 	++ext;
40 	if (NULL == (mime = zend_hash_str_find_ptr(mimes, ext, strlen(ext)))) {
41 		*mime_type = "application/octet-stream";
42 		return PHAR_MIME_OTHER;
43 	}
44 	*mime_type = mime->mime;
45 	return mime->type;
46 }
47 /* }}} */
48 
phar_mung_server_vars(char * fname,char * entry,size_t entry_len,char * basename,size_t request_uri_len)49 static void phar_mung_server_vars(char *fname, char *entry, size_t entry_len, char *basename, size_t request_uri_len) /* {{{ */
50 {
51 	HashTable *_SERVER;
52 	zval *stuff;
53 	char *path_info;
54 	size_t basename_len = strlen(basename);
55 	size_t code;
56 	zval temp;
57 
58 	/* "tweak" $_SERVER variables requested in earlier call to Phar::mungServer() */
59 	if (Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) == IS_UNDEF) {
60 		return;
61 	}
62 
63 	_SERVER = Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]);
64 
65 	/* PATH_INFO and PATH_TRANSLATED should always be munged */
66 	if (NULL != (stuff = zend_hash_str_find(_SERVER, "PATH_INFO", sizeof("PATH_INFO")-1))) {
67 		path_info = Z_STRVAL_P(stuff);
68 		code = Z_STRLEN_P(stuff);
69 		if (code > (size_t)entry_len && !memcmp(path_info, entry, entry_len)) {
70 			ZVAL_STR(&temp, Z_STR_P(stuff));
71 			ZVAL_STRINGL(stuff, path_info + entry_len, request_uri_len);
72 			zend_hash_str_update(_SERVER, "PHAR_PATH_INFO", sizeof("PHAR_PATH_INFO")-1, &temp);
73 		}
74 	}
75 
76 	if (NULL != (stuff = zend_hash_str_find(_SERVER, "PATH_TRANSLATED", sizeof("PATH_TRANSLATED")-1))) {
77 		zend_string *str = strpprintf(4096, "phar://%s%s", fname, entry);
78 
79 		ZVAL_STR(&temp, Z_STR_P(stuff));
80 		ZVAL_NEW_STR(stuff, str);
81 
82 		zend_hash_str_update(_SERVER, "PHAR_PATH_TRANSLATED", sizeof("PHAR_PATH_TRANSLATED")-1, &temp);
83 	}
84 
85 	if (!PHAR_G(phar_SERVER_mung_list)) {
86 		return;
87 	}
88 
89 	if (PHAR_G(phar_SERVER_mung_list) & PHAR_MUNG_REQUEST_URI) {
90 		if (NULL != (stuff = zend_hash_str_find(_SERVER, "REQUEST_URI", sizeof("REQUEST_URI")-1))) {
91 			path_info = Z_STRVAL_P(stuff);
92 			code = Z_STRLEN_P(stuff);
93 			if (code > basename_len && !memcmp(path_info, basename, basename_len)) {
94 				ZVAL_STR(&temp, Z_STR_P(stuff));
95 				ZVAL_STRINGL(stuff, path_info + basename_len, code - basename_len);
96 				zend_hash_str_update(_SERVER, "PHAR_REQUEST_URI", sizeof("PHAR_REQUEST_URI")-1, &temp);
97 			}
98 		}
99 	}
100 
101 	if (PHAR_G(phar_SERVER_mung_list) & PHAR_MUNG_PHP_SELF) {
102 		if (NULL != (stuff = zend_hash_str_find(_SERVER, "PHP_SELF", sizeof("PHP_SELF")-1))) {
103 			path_info = Z_STRVAL_P(stuff);
104 			code = Z_STRLEN_P(stuff);
105 
106 			if (code > basename_len && !memcmp(path_info, basename, basename_len)) {
107 				ZVAL_STR(&temp, Z_STR_P(stuff));
108 				ZVAL_STRINGL(stuff, path_info + basename_len, code - basename_len);
109 				zend_hash_str_update(_SERVER, "PHAR_PHP_SELF", sizeof("PHAR_PHP_SELF")-1, &temp);
110 			}
111 		}
112 	}
113 
114 	if (PHAR_G(phar_SERVER_mung_list) & PHAR_MUNG_SCRIPT_NAME) {
115 		if (NULL != (stuff = zend_hash_str_find(_SERVER, "SCRIPT_NAME", sizeof("SCRIPT_NAME")-1))) {
116 			ZVAL_STR(&temp, Z_STR_P(stuff));
117 			ZVAL_STRINGL(stuff, entry, entry_len);
118 			zend_hash_str_update(_SERVER, "PHAR_SCRIPT_NAME", sizeof("PHAR_SCRIPT_NAME")-1, &temp);
119 		}
120 	}
121 
122 	if (PHAR_G(phar_SERVER_mung_list) & PHAR_MUNG_SCRIPT_FILENAME) {
123 		if (NULL != (stuff = zend_hash_str_find(_SERVER, "SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME")-1))) {
124 			zend_string *str = strpprintf(4096, "phar://%s%s", fname, entry);
125 
126 			ZVAL_STR(&temp, Z_STR_P(stuff));
127 			ZVAL_NEW_STR(stuff, str);
128 
129 			zend_hash_str_update(_SERVER, "PHAR_SCRIPT_FILENAME", sizeof("PHAR_SCRIPT_FILENAME")-1, &temp);
130 		}
131 	}
132 }
133 /* }}} */
134 
phar_file_action(phar_archive_data * phar,phar_entry_info * info,char * mime_type,int code,char * entry,size_t entry_len,char * arch,char * basename,char * ru,size_t ru_len)135 static int phar_file_action(phar_archive_data *phar, phar_entry_info *info, char *mime_type, int code, char *entry, size_t entry_len, char *arch, char *basename, char *ru, size_t ru_len) /* {{{ */
136 {
137 	char *name = NULL, buf[8192];
138 	const char *cwd;
139 	zend_syntax_highlighter_ini syntax_highlighter_ini;
140 	sapi_header_line ctr = {0};
141 	size_t got;
142 	zval dummy;
143 	size_t name_len;
144 	zend_file_handle file_handle;
145 	zend_op_array *new_op_array;
146 	zval result;
147 	php_stream *fp;
148 	zend_off_t position;
149 
150 	switch (code) {
151 		case PHAR_MIME_PHPS:
152 			efree(basename);
153 			/* highlight source */
154 			if (entry[0] == '/') {
155 				spprintf(&name, 4096, "phar://%s%s", arch, entry);
156 			} else {
157 				spprintf(&name, 4096, "phar://%s/%s", arch, entry);
158 			}
159 			php_get_highlight_struct(&syntax_highlighter_ini);
160 
161 			highlight_file(name, &syntax_highlighter_ini);
162 
163 			efree(name);
164 #ifdef PHP_WIN32
165 			efree(arch);
166 #endif
167 			zend_bailout();
168 		case PHAR_MIME_OTHER:
169 			/* send headers, output file contents */
170 			efree(basename);
171 			ctr.line_len = spprintf((char **) &(ctr.line), 0, "Content-type: %s", mime_type);
172 			sapi_header_op(SAPI_HEADER_REPLACE, &ctr);
173 			efree((void *) ctr.line);
174 			ctr.line_len = spprintf((char **) &(ctr.line), 0, "Content-length: %u", info->uncompressed_filesize);
175 			sapi_header_op(SAPI_HEADER_REPLACE, &ctr);
176 			efree((void *) ctr.line);
177 
178 			if (FAILURE == sapi_send_headers()) {
179 				zend_bailout();
180 			}
181 
182 			/* prepare to output  */
183 			fp = phar_get_efp(info, 1);
184 
185 			if (!fp) {
186 				char *error;
187 				if (!phar_open_jit(phar, info, &error)) {
188 					if (error) {
189 						zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
190 						efree(error);
191 					}
192 					return -1;
193 				}
194 				fp = phar_get_efp(info, 1);
195 			}
196 			position = 0;
197 			phar_seek_efp(info, 0, SEEK_SET, 0, 1);
198 
199 			do {
200 				got = php_stream_read(fp, buf, MIN(8192, info->uncompressed_filesize - position));
201 				if (got > 0) {
202 					PHPWRITE(buf, got);
203 					position += got;
204 					if (position == (zend_off_t) info->uncompressed_filesize) {
205 						break;
206 					}
207 				}
208 			} while (1);
209 
210 			zend_bailout();
211 		case PHAR_MIME_PHP:
212 			if (basename) {
213 				phar_mung_server_vars(arch, entry, entry_len, basename, ru_len);
214 				efree(basename);
215 			}
216 
217 			if (entry[0] == '/') {
218 				name_len = spprintf(&name, 4096, "phar://%s%s", arch, entry);
219 			} else {
220 				name_len = spprintf(&name, 4096, "phar://%s/%s", arch, entry);
221 			}
222 
223 			zend_stream_init_filename(&file_handle, name);
224 
225 			PHAR_G(cwd) = NULL;
226 			PHAR_G(cwd_len) = 0;
227 
228 			ZVAL_NULL(&dummy);
229 			if (zend_hash_str_add(&EG(included_files), name, name_len, &dummy) != NULL) {
230 				if ((cwd = zend_memrchr(entry, '/', entry_len))) {
231 					PHAR_G(cwd_init) = 1;
232 					if (entry == cwd) {
233 						/* root directory */
234 						PHAR_G(cwd_len) = 0;
235 						PHAR_G(cwd) = NULL;
236 					} else if (entry[0] == '/') {
237 						PHAR_G(cwd_len) = (cwd - (entry + 1));
238 						PHAR_G(cwd) = estrndup(entry + 1, PHAR_G(cwd_len));
239 					} else {
240 						PHAR_G(cwd_len) = (cwd - entry);
241 						PHAR_G(cwd) = estrndup(entry, PHAR_G(cwd_len));
242 					}
243 				}
244 
245 				new_op_array = zend_compile_file(&file_handle, ZEND_REQUIRE);
246 
247 				if (!new_op_array) {
248 					zend_hash_str_del(&EG(included_files), name, name_len);
249 				}
250 			} else {
251 				efree(name);
252 				new_op_array = NULL;
253 			}
254 
255 			zend_destroy_file_handle(&file_handle);
256 #ifdef PHP_WIN32
257 			efree(arch);
258 #endif
259 			if (new_op_array) {
260 				ZVAL_UNDEF(&result);
261 
262 				zend_try {
263 					zend_execute(new_op_array, &result);
264 					if (PHAR_G(cwd)) {
265 						efree(PHAR_G(cwd));
266 						PHAR_G(cwd) = NULL;
267 						PHAR_G(cwd_len) = 0;
268 					}
269 
270 					PHAR_G(cwd_init) = 0;
271 					efree(name);
272 					destroy_op_array(new_op_array);
273 					efree(new_op_array);
274 					zval_ptr_dtor(&result);
275 				} zend_catch {
276 					if (PHAR_G(cwd)) {
277 						efree(PHAR_G(cwd));
278 						PHAR_G(cwd) = NULL;
279 						PHAR_G(cwd_len) = 0;
280 					}
281 
282 					PHAR_G(cwd_init) = 0;
283 					efree(name);
284 				} zend_end_try();
285 
286 				zend_bailout();
287 			}
288 
289 			return PHAR_MIME_PHP;
290 	}
291 	return -1;
292 }
293 /* }}} */
294 
phar_do_403(char * entry,size_t entry_len)295 static void phar_do_403(char *entry, size_t entry_len) /* {{{ */
296 {
297 	sapi_header_line ctr = {0};
298 
299 	ctr.response_code = 403;
300 	ctr.line_len = sizeof("HTTP/1.0 403 Access Denied")-1;
301 	ctr.line = "HTTP/1.0 403 Access Denied";
302 	sapi_header_op(SAPI_HEADER_REPLACE, &ctr);
303 	sapi_send_headers();
304 	PHPWRITE("<html>\n <head>\n  <title>Access Denied</title>\n </head>\n <body>\n  <h1>403 - File ", sizeof("<html>\n <head>\n  <title>Access Denied</title>\n </head>\n <body>\n  <h1>403 - File ") - 1);
305 	PHPWRITE("Access Denied</h1>\n </body>\n</html>", sizeof("Access Denied</h1>\n </body>\n</html>") - 1);
306 }
307 /* }}} */
308 
phar_do_404(phar_archive_data * phar,char * fname,size_t fname_len,char * f404,size_t f404_len,char * entry,size_t entry_len)309 static void phar_do_404(phar_archive_data *phar, char *fname, size_t fname_len, char *f404, size_t f404_len, char *entry, size_t entry_len) /* {{{ */
310 {
311 	sapi_header_line ctr = {0};
312 	phar_entry_info	*info;
313 
314 	if (phar && f404_len) {
315 		info = phar_get_entry_info(phar, f404, f404_len, NULL, 1);
316 
317 		if (info) {
318 			phar_file_action(phar, info, "text/html", PHAR_MIME_PHP, f404, f404_len, fname, NULL, NULL, 0);
319 			return;
320 		}
321 	}
322 
323 	ctr.response_code = 404;
324 	ctr.line_len = sizeof("HTTP/1.0 404 Not Found")-1;
325 	ctr.line = "HTTP/1.0 404 Not Found";
326 	sapi_header_op(SAPI_HEADER_REPLACE, &ctr);
327 	sapi_send_headers();
328 	PHPWRITE("<html>\n <head>\n  <title>File Not Found</title>\n </head>\n <body>\n  <h1>404 - File ", sizeof("<html>\n <head>\n  <title>File Not Found</title>\n </head>\n <body>\n  <h1>404 - File ") - 1);
329 	PHPWRITE("Not Found</h1>\n </body>\n</html>",  sizeof("Not Found</h1>\n </body>\n</html>") - 1);
330 }
331 /* }}} */
332 
333 /* post-process REQUEST_URI and retrieve the actual request URI.  This is for
334    cases like http://localhost/blah.phar/path/to/file.php/extra/stuff
335    which calls "blah.phar" file "path/to/file.php" with PATH_INFO "/extra/stuff" */
phar_postprocess_ru_web(char * fname,size_t fname_len,char ** entry,size_t * entry_len,char ** ru,size_t * ru_len)336 static void phar_postprocess_ru_web(char *fname, size_t fname_len, char **entry, size_t *entry_len, char **ru, size_t *ru_len) /* {{{ */
337 {
338 	char *e = *entry + 1, *u = NULL, *u1 = NULL, *saveu = NULL;
339 	size_t e_len = *entry_len - 1, u_len = 0;
340 	phar_archive_data *pphar;
341 
342 	/* we already know we can retrieve the phar if we reach here */
343 	pphar = zend_hash_str_find_ptr(&(PHAR_G(phar_fname_map)), fname, fname_len);
344 
345 	if (!pphar && PHAR_G(manifest_cached)) {
346 		pphar = zend_hash_str_find_ptr(&cached_phars, fname, fname_len);
347 	}
348 
349 	do {
350 		if (zend_hash_str_exists(&(pphar->manifest), e, e_len)) {
351 			if (u) {
352 				u[0] = '/';
353 				*ru = estrndup(u, u_len+1);
354 				++u_len;
355 				u[0] = '\0';
356 			} else {
357 				*ru = NULL;
358 			}
359 			*ru_len = u_len;
360 			*entry_len = e_len + 1;
361 			return;
362 		}
363 
364 		if (u) {
365 			u1 = strrchr(e, '/');
366 			u[0] = '/';
367 			saveu = u;
368 			e_len += u_len + 1;
369 			u = u1;
370 			if (!u) {
371 				return;
372 			}
373 		} else {
374 			u = strrchr(e, '/');
375 			if (!u) {
376 				if (saveu) {
377 					saveu[0] = '/';
378 				}
379 				return;
380 			}
381 		}
382 
383 		u[0] = '\0';
384 		u_len = strlen(u + 1);
385 		e_len -= u_len + 1;
386 	} while (1);
387 }
388 /* }}} */
389 
390 /* {{{ return the name of the currently running phar archive.  If the optional parameter
391  * is set to true, return the phar:// URL to the currently running phar
392  */
PHP_METHOD(Phar,running)393 PHP_METHOD(Phar, running)
394 {
395 	zend_string *fname;
396 	char *arch, *entry;
397 	size_t arch_len, entry_len;
398 	bool retphar = 1;
399 
400 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &retphar) == FAILURE) {
401 		RETURN_THROWS();
402 	}
403 
404 	fname = zend_get_executed_filename_ex();
405 	if (!fname) {
406 		RETURN_EMPTY_STRING();
407 	}
408 
409 	if (
410 		zend_string_starts_with_literal_ci(fname, "phar://")
411 		&& SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, &entry, &entry_len, 2, 0)
412 	) {
413 		efree(entry);
414 		if (retphar) {
415 			RETVAL_STRINGL(ZSTR_VAL(fname), arch_len + 7);
416 			efree(arch);
417 			return;
418 		} else {
419 			// TODO: avoid reallocation ???
420 			RETVAL_STRINGL(arch, arch_len);
421 			efree(arch);
422 			return;
423 		}
424 	}
425 
426 	RETURN_EMPTY_STRING();
427 }
428 /* }}} */
429 
430 /* {{{ mount an external file or path to a location within the phar.  This maps
431  * an external file or directory to a location within the phar archive, allowing
432  * reference to an external location as if it were within the phar archive.  This
433  * is useful for writable temp files like databases
434  */
PHP_METHOD(Phar,mount)435 PHP_METHOD(Phar, mount)
436 {
437 	char *fname, *arch = NULL, *entry = NULL, *path, *actual;
438 	size_t fname_len, arch_len, entry_len;
439 	size_t path_len, actual_len;
440 	phar_archive_data *pphar;
441 #ifdef PHP_WIN32
442 	char *save_fname;
443 	ALLOCA_FLAG(fname_use_heap)
444 #endif
445 
446 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "pp", &path, &path_len, &actual, &actual_len) == FAILURE) {
447 		RETURN_THROWS();
448 	}
449 
450 	zend_string *zend_file_name = zend_get_executed_filename_ex();
451 	if (UNEXPECTED(!zend_file_name)) {
452 		fname = "";
453 		fname_len = 0;
454 	} else {
455 		fname = ZSTR_VAL(zend_file_name);
456 		fname_len = ZSTR_LEN(zend_file_name);
457 	}
458 
459 #ifdef PHP_WIN32
460 	save_fname = fname;
461 	if (memchr(fname, '\\', fname_len)) {
462 		fname = do_alloca(fname_len + 1, fname_use_heap);
463 		memcpy(fname, save_fname, fname_len);
464 		fname[fname_len] = '\0';
465 		phar_unixify_path_separators(fname, fname_len);
466 	}
467 #endif
468 
469 	if (fname_len > 7 && !memcmp(fname, "phar://", 7) && SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0)) {
470 		efree(entry);
471 		entry = NULL;
472 
473 		if (path_len > 7 && !memcmp(path, "phar://", 7)) {
474 			zend_throw_exception_ex(phar_ce_PharException, 0, "Can only mount internal paths within a phar archive, use a relative path instead of \"%s\"", path);
475 			efree(arch);
476 			goto finish;
477 		}
478 carry_on2:
479 		if (NULL == (pphar = zend_hash_str_find_ptr(&(PHAR_G(phar_fname_map)), arch, arch_len))) {
480 			if (PHAR_G(manifest_cached) && NULL != (pphar = zend_hash_str_find_ptr(&cached_phars, arch, arch_len))) {
481 				if (SUCCESS == phar_copy_on_write(&pphar)) {
482 					goto carry_on;
483 				}
484 			}
485 
486 			zend_throw_exception_ex(phar_ce_PharException, 0, "%s is not a phar archive, cannot mount", arch);
487 
488 			if (arch) {
489 				efree(arch);
490 			}
491 
492 			goto finish;
493 		}
494 carry_on:
495 		if (SUCCESS != phar_mount_entry(pphar, actual, actual_len, path, path_len)) {
496 			zend_throw_exception_ex(phar_ce_PharException, 0, "Mounting of %s to %s within phar %s failed", path, actual, arch);
497 		}
498 
499 		if (entry && path == entry) {
500 			efree(entry);
501 		}
502 
503 		if (arch) {
504 			efree(arch);
505 		}
506 
507 		goto finish;
508 	} else if (HT_IS_INITIALIZED(&PHAR_G(phar_fname_map)) && NULL != (pphar = zend_hash_str_find_ptr(&(PHAR_G(phar_fname_map)), fname, fname_len))) {
509 		goto carry_on;
510 	} else if (PHAR_G(manifest_cached) && NULL != (pphar = zend_hash_str_find_ptr(&cached_phars, fname, fname_len))) {
511 		if (SUCCESS == phar_copy_on_write(&pphar)) {
512 			goto carry_on;
513 		}
514 
515 		goto carry_on;
516 	} else if (SUCCESS == phar_split_fname(path, path_len, &arch, &arch_len, &entry, &entry_len, 2, 0)) {
517 		path = entry;
518 		path_len = entry_len;
519 		goto carry_on2;
520 	}
521 
522 	zend_throw_exception_ex(phar_ce_PharException, 0, "Mounting of %s to %s failed", path, actual);
523 
524 finish: ;
525 #ifdef PHP_WIN32
526 	if (fname != save_fname) {
527 		free_alloca(fname, fname_use_heap);
528 		fname = save_fname;
529 	}
530 #endif
531 }
532 /* }}} */
533 
534 /* {{{ mapPhar for web-based phars. Reads the currently executed file (a phar)
535  * and registers its manifest. When executed in the CLI or CGI command-line sapi,
536  * this works exactly like mapPhar().  When executed by a web-based sapi, this
537  * reads $_SERVER['REQUEST_URI'] (the actual original value) and parses out the
538  * intended internal file.
539  */
PHP_METHOD(Phar,webPhar)540 PHP_METHOD(Phar, webPhar)
541 {
542 	zval *mimeoverride = NULL;
543 	zend_fcall_info rewrite_fci = {0};
544 	zend_fcall_info_cache rewrite_fcc;
545 	char *alias = NULL, *error, *index_php = NULL, *f404 = NULL, *ru = NULL;
546 	size_t alias_len = 0, f404_len = 0, free_pathinfo = 0;
547 	size_t ru_len = 0;
548 	char *fname, *path_info, *mime_type = NULL, *entry, *pt;
549 	const char *basename;
550 	size_t fname_len, index_php_len = 0;
551 	size_t entry_len;
552 	int code, not_cgi;
553 	phar_archive_data *phar = NULL;
554 	phar_entry_info *info = NULL;
555 	size_t sapi_mod_name_len = strlen(sapi_module.name);
556 
557 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!s!s!af!", &alias, &alias_len, &index_php, &index_php_len, &f404, &f404_len, &mimeoverride, &rewrite_fci, &rewrite_fcc) == FAILURE) {
558 		RETURN_THROWS();
559 	}
560 
561 	phar_request_initialize();
562 
563 	if (phar_open_executed_filename(alias, alias_len, &error) != SUCCESS) {
564 		if (error) {
565 			zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
566 			efree(error);
567 		}
568 		return;
569 	}
570 
571 	/* retrieve requested file within phar */
572 	if (!(SG(request_info).request_method
573 	      && SG(request_info).request_uri
574 	      && (!strcmp(SG(request_info).request_method, "GET")
575 	       || !strcmp(SG(request_info).request_method, "POST")
576 	       || !strcmp(SG(request_info).request_method, "DELETE")
577 	       || !strcmp(SG(request_info).request_method, "HEAD")
578 	       || !strcmp(SG(request_info).request_method, "OPTIONS")
579 	       || !strcmp(SG(request_info).request_method, "PATCH")
580 	       || !strcmp(SG(request_info).request_method, "PUT")
581 	      )
582 	     )
583 	   ) {
584 		return;
585 	}
586 
587 	zend_string *zend_file_name = zend_get_executed_filename_ex();
588 	if (UNEXPECTED(!zend_file_name)) {
589 		return;
590 	}
591 
592 	fname = ZSTR_VAL(zend_file_name);
593 	fname_len = ZSTR_LEN(zend_file_name);
594 
595 #ifdef PHP_WIN32
596 	if (memchr(fname, '\\', fname_len)) {
597 		fname = estrndup(fname, fname_len);
598 		phar_unixify_path_separators(fname, fname_len);
599 	}
600 #endif
601 	basename = zend_memrchr(fname, '/', fname_len);
602 
603 	if (!basename) {
604 		basename = fname;
605 	} else {
606 		++basename;
607 	}
608 
609 	if ((sapi_mod_name_len == sizeof("cgi-fcgi") - 1 && !strncmp(sapi_module.name, "cgi-fcgi", sizeof("cgi-fcgi") - 1))
610 		|| (sapi_mod_name_len == sizeof("fpm-fcgi") - 1 && !strncmp(sapi_module.name, "fpm-fcgi", sizeof("fpm-fcgi") - 1))
611 		|| (sapi_mod_name_len == sizeof("cgi") - 1 && !strncmp(sapi_module.name, "cgi", sizeof("cgi") - 1))
612 		|| (sapi_mod_name_len == sizeof("litespeed") - 1 && !strncmp(sapi_module.name, "litespeed", sizeof("litespeed") - 1))) {
613 
614 		if (Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) != IS_UNDEF) {
615 			HashTable *_server = Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]);
616 			zval *z_script_name, *z_path_info;
617 
618 			if (NULL == (z_script_name = zend_hash_str_find(_server, "SCRIPT_NAME", sizeof("SCRIPT_NAME")-1)) ||
619 				IS_STRING != Z_TYPE_P(z_script_name) ||
620 				!strstr(Z_STRVAL_P(z_script_name), basename)) {
621 				goto finish;
622 			}
623 
624 			if (NULL != (z_path_info = zend_hash_str_find(_server, "PATH_INFO", sizeof("PATH_INFO")-1)) &&
625 				IS_STRING == Z_TYPE_P(z_path_info)) {
626 				entry_len = Z_STRLEN_P(z_path_info);
627 				entry = estrndup(Z_STRVAL_P(z_path_info), entry_len);
628 				path_info = emalloc(Z_STRLEN_P(z_script_name) + entry_len + 1);
629 				memcpy(path_info, Z_STRVAL_P(z_script_name), Z_STRLEN_P(z_script_name));
630 				memcpy(path_info + Z_STRLEN_P(z_script_name), entry, entry_len + 1);
631 				free_pathinfo = 1;
632 			} else {
633 				entry_len = 0;
634 				entry = estrndup("", 0);
635 				path_info = Z_STRVAL_P(z_script_name);
636 			}
637 
638 			pt = estrndup(Z_STRVAL_P(z_script_name), Z_STRLEN_P(z_script_name));
639 
640 		} else {
641 			char *testit;
642 
643 			testit = sapi_getenv("SCRIPT_NAME", sizeof("SCRIPT_NAME")-1);
644 			if (!(pt = strstr(testit, basename))) {
645 				efree(testit);
646 				goto finish;
647 			}
648 
649 			path_info = sapi_getenv("PATH_INFO", sizeof("PATH_INFO")-1);
650 
651 			if (path_info) {
652 				entry = path_info;
653 				entry_len = strlen(entry);
654 				spprintf(&path_info, 0, "%s%s", testit, path_info);
655 				free_pathinfo = 1;
656 			} else {
657 				path_info = testit;
658 				free_pathinfo = 1;
659 				entry = estrndup("", 0);
660 				entry_len = 0;
661 			}
662 
663 			pt = estrndup(testit, (pt - testit) + (fname_len - (basename - fname)));
664 		}
665 		not_cgi = 0;
666 	} else {
667 		path_info = SG(request_info).request_uri;
668 
669 		if (!(pt = strstr(path_info, basename))) {
670 			/* this can happen with rewrite rules - and we have no idea what to do then, so return */
671 			goto finish;
672 		}
673 
674 		entry_len = strlen(path_info);
675 		entry_len -= (pt - path_info) + (fname_len - (basename - fname));
676 		entry = estrndup(pt + (fname_len - (basename - fname)), entry_len);
677 
678 		pt = estrndup(path_info, (pt - path_info) + (fname_len - (basename - fname)));
679 		not_cgi = 1;
680 	}
681 
682 	if (ZEND_FCI_INITIALIZED(rewrite_fci)) {
683 		zval params, retval;
684 
685 		ZVAL_STRINGL(&params, entry, entry_len);
686 
687 		rewrite_fci.param_count = 1;
688 		rewrite_fci.params = &params;
689 		rewrite_fci.retval = &retval;
690 
691 		if (FAILURE == zend_call_function(&rewrite_fci, &rewrite_fcc)) {
692 			if (!EG(exception)) {
693 				zend_throw_exception_ex(phar_ce_PharException, 0, "phar error: failed to call rewrite callback");
694 			}
695 			goto cleanup_fail;
696 		}
697 
698 		if (Z_TYPE_P(rewrite_fci.retval) == IS_UNDEF || Z_TYPE(retval) == IS_UNDEF) {
699 			zend_throw_exception_ex(phar_ce_PharException, 0, "phar error: rewrite callback must return a string or false");
700 			goto cleanup_fail;
701 		}
702 
703 		switch (Z_TYPE(retval)) {
704 			case IS_STRING:
705 				efree(entry);
706 				entry = estrndup(Z_STRVAL_P(rewrite_fci.retval), Z_STRLEN_P(rewrite_fci.retval));
707 				entry_len = Z_STRLEN_P(rewrite_fci.retval);
708 				break;
709 			case IS_TRUE:
710 			case IS_FALSE:
711 				phar_do_403(entry, entry_len);
712 
713 				if (free_pathinfo) {
714 					efree(path_info);
715 				}
716 				efree(pt);
717 
718 				zend_bailout();
719 			default:
720 				zend_throw_exception_ex(phar_ce_PharException, 0, "phar error: rewrite callback must return a string or false");
721 
722 cleanup_fail:
723 				zval_ptr_dtor(&params);
724 				if (free_pathinfo) {
725 					efree(path_info);
726 				}
727 				efree(entry);
728 				efree(pt);
729 #ifdef PHP_WIN32
730 				efree(fname);
731 #endif
732 				RETURN_THROWS();
733 		}
734 	}
735 
736 	if (entry_len) {
737 		phar_postprocess_ru_web(fname, fname_len, &entry, &entry_len, &ru, &ru_len);
738 	}
739 
740 	if (!entry_len || (entry_len == 1 && entry[0] == '/')) {
741 		efree(entry);
742 		/* direct request */
743 		if (index_php_len) {
744 			entry = index_php;
745 			entry_len = index_php_len;
746 			if (entry[0] != '/') {
747 				spprintf(&entry, 0, "/%s", index_php);
748 				++entry_len;
749 			}
750 		} else {
751 			/* assume "index.php" is starting point */
752 			entry = estrndup("/index.php", sizeof("/index.php"));
753 			entry_len = sizeof("/index.php")-1;
754 		}
755 
756 		if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, NULL) ||
757 			(info = phar_get_entry_info(phar, entry, entry_len, NULL, 0)) == NULL) {
758 			phar_do_404(phar, fname, fname_len, f404, f404_len, entry, entry_len);
759 
760 			if (free_pathinfo) {
761 				efree(path_info);
762 			}
763 
764 			zend_bailout();
765 		} else {
766 			char *tmp = NULL, sa = '\0';
767 			sapi_header_line ctr = {0};
768 			ctr.response_code = 301;
769 			ctr.line_len = sizeof("HTTP/1.1 301 Moved Permanently")-1;
770 			ctr.line = "HTTP/1.1 301 Moved Permanently";
771 			sapi_header_op(SAPI_HEADER_REPLACE, &ctr);
772 
773 			if (not_cgi) {
774 				tmp = strstr(path_info, basename) + fname_len;
775 				sa = *tmp;
776 				*tmp = '\0';
777 			}
778 
779 			ctr.response_code = 0;
780 
781 			if (path_info[strlen(path_info)-1] == '/') {
782 				ctr.line_len = spprintf((char **) &(ctr.line), 4096, "Location: %s%s", path_info, entry + 1);
783 			} else {
784 				ctr.line_len = spprintf((char **) &(ctr.line), 4096, "Location: %s%s", path_info, entry);
785 			}
786 
787 			if (not_cgi) {
788 				*tmp = sa;
789 			}
790 
791 			if (free_pathinfo) {
792 				efree(path_info);
793 			}
794 
795 			sapi_header_op(SAPI_HEADER_REPLACE, &ctr);
796 			sapi_send_headers();
797 			efree((void *) ctr.line);
798 			zend_bailout();
799 		}
800 	}
801 
802 	if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, NULL) ||
803 		(info = phar_get_entry_info(phar, entry, entry_len, NULL, 0)) == NULL) {
804 		phar_do_404(phar, fname, fname_len, f404, f404_len, entry, entry_len);
805 		zend_bailout();
806 	}
807 
808 	if (mimeoverride && zend_hash_num_elements(Z_ARRVAL_P(mimeoverride))) {
809 		const char *ext = zend_memrchr(entry, '.', entry_len);
810 		zval *val;
811 
812 		if (ext) {
813 			++ext;
814 
815 			if (NULL != (val = zend_hash_str_find(Z_ARRVAL_P(mimeoverride), ext, strlen(ext)))) {
816 				switch (Z_TYPE_P(val)) {
817 					case IS_LONG:
818 						if (Z_LVAL_P(val) == PHAR_MIME_PHP || Z_LVAL_P(val) == PHAR_MIME_PHPS) {
819 							mime_type = "";
820 							code = Z_LVAL_P(val);
821 						} else {
822 							zend_throw_exception_ex(phar_ce_PharException, 0, "Unknown mime type specifier used, only Phar::PHP, Phar::PHPS and a mime type string are allowed");
823 							if (free_pathinfo) {
824 								efree(path_info);
825 							}
826 							efree(pt);
827 							efree(entry);
828 #ifdef PHP_WIN32
829 							efree(fname);
830 #endif
831 							RETURN_THROWS();
832 						}
833 						break;
834 					case IS_STRING:
835 						mime_type = Z_STRVAL_P(val);
836 						code = PHAR_MIME_OTHER;
837 						break;
838 					default:
839 						zend_throw_exception_ex(phar_ce_PharException, 0, "Unknown mime type specifier used (not a string or int), only Phar::PHP, Phar::PHPS and a mime type string are allowed");
840 						if (free_pathinfo) {
841 							efree(path_info);
842 						}
843 						efree(pt);
844 						efree(entry);
845 #ifdef PHP_WIN32
846 						efree(fname);
847 #endif
848 						RETURN_THROWS();
849 				}
850 			}
851 		}
852 	}
853 
854 	if (!mime_type) {
855 		code = phar_file_type(&PHAR_G(mime_types), entry, &mime_type);
856 	}
857 	phar_file_action(phar, info, mime_type, code, entry, entry_len, fname, pt, ru, ru_len);
858 
859 finish: ;
860 #ifdef PHP_WIN32
861 	efree(fname);
862 #endif
863 }
864 /* }}} */
865 
866 /* {{{ Defines a list of up to 4 $_SERVER variables that should be modified for execution
867  * to mask the presence of the phar archive.  This should be used in conjunction with
868  * Phar::webPhar(), and has no effect otherwise
869  * SCRIPT_NAME, PHP_SELF, REQUEST_URI and SCRIPT_FILENAME
870  */
PHP_METHOD(Phar,mungServer)871 PHP_METHOD(Phar, mungServer)
872 {
873 	zval *mungvalues, *data;
874 
875 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &mungvalues) == FAILURE) {
876 		RETURN_THROWS();
877 	}
878 
879 	if (!zend_hash_num_elements(Z_ARRVAL_P(mungvalues))) {
880 		zend_throw_exception_ex(phar_ce_PharException, 0, "No values passed to Phar::mungServer(), expecting an array of any of these strings: PHP_SELF, REQUEST_URI, SCRIPT_FILENAME, SCRIPT_NAME");
881 		RETURN_THROWS();
882 	}
883 
884 	if (zend_hash_num_elements(Z_ARRVAL_P(mungvalues)) > 4) {
885 		zend_throw_exception_ex(phar_ce_PharException, 0, "Too many values passed to Phar::mungServer(), expecting an array of any of these strings: PHP_SELF, REQUEST_URI, SCRIPT_FILENAME, SCRIPT_NAME");
886 		RETURN_THROWS();
887 	}
888 
889 	phar_request_initialize();
890 
891 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(mungvalues), data) {
892 
893 		if (Z_TYPE_P(data) != IS_STRING) {
894 			zend_throw_exception_ex(phar_ce_PharException, 0, "Non-string value passed to Phar::mungServer(), expecting an array of any of these strings: PHP_SELF, REQUEST_URI, SCRIPT_FILENAME, SCRIPT_NAME");
895 			RETURN_THROWS();
896 		}
897 
898 		if (zend_string_equals_literal(Z_STR_P(data), "PHP_SELF")) {
899 			PHAR_G(phar_SERVER_mung_list) |= PHAR_MUNG_PHP_SELF;
900 		} else if (zend_string_equals_literal(Z_STR_P(data), "REQUEST_URI")) {
901 			PHAR_G(phar_SERVER_mung_list) |= PHAR_MUNG_REQUEST_URI;
902 		} else if (zend_string_equals_literal(Z_STR_P(data), "SCRIPT_NAME")) {
903 			PHAR_G(phar_SERVER_mung_list) |= PHAR_MUNG_SCRIPT_NAME;
904 		} else if (zend_string_equals_literal(Z_STR_P(data), "SCRIPT_FILENAME")) {
905 			PHAR_G(phar_SERVER_mung_list) |= PHAR_MUNG_SCRIPT_FILENAME;
906 		}
907 		// TODO Warning for invalid value?
908 	} ZEND_HASH_FOREACH_END();
909 }
910 /* }}} */
911 
912 /* {{{ instructs phar to intercept fopen, file_get_contents, opendir, and all of the stat-related functions
913  * and return stat on files within the phar for relative paths
914  *
915  * Once called, this cannot be reversed, and continue until the end of the request.
916  *
917  * This allows legacy scripts to be pharred unmodified
918  */
PHP_METHOD(Phar,interceptFileFuncs)919 PHP_METHOD(Phar, interceptFileFuncs)
920 {
921 	if (zend_parse_parameters_none() == FAILURE) {
922 		RETURN_THROWS();
923 	}
924 	phar_intercept_functions();
925 }
926 /* }}} */
927 
928 /* {{{ Return a stub that can be used to run a phar-based archive without the phar extension
929  * indexfile is the CLI startup filename, which defaults to "index.php", webindexfile
930  * is the web startup filename, and also defaults to "index.php"
931  */
PHP_METHOD(Phar,createDefaultStub)932 PHP_METHOD(Phar, createDefaultStub)
933 {
934 	char *index = NULL, *webindex = NULL, *error;
935 	zend_string *stub;
936 	size_t index_len = 0, webindex_len = 0;
937 
938 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|p!p!", &index, &index_len, &webindex, &webindex_len) == FAILURE) {
939 		RETURN_THROWS();
940 	}
941 
942 	stub = phar_create_default_stub(index, webindex, &error);
943 
944 	if (error) {
945 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
946 		efree(error);
947 		RETURN_THROWS();
948 	}
949 	RETURN_NEW_STR(stub);
950 }
951 /* }}} */
952 
953 /* {{{ Reads the currently executed file (a phar) and registers its manifest */
PHP_METHOD(Phar,mapPhar)954 PHP_METHOD(Phar, mapPhar)
955 {
956 	char *alias = NULL, *error;
957 	size_t alias_len = 0;
958 	zend_long dataoffset = 0;
959 
960 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!l", &alias, &alias_len, &dataoffset) == FAILURE) {
961 		RETURN_THROWS();
962 	}
963 
964 	phar_request_initialize();
965 
966 	RETVAL_BOOL(phar_open_executed_filename(alias, alias_len, &error) == SUCCESS);
967 
968 	if (error) {
969 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
970 		efree(error);
971 	}
972 } /* }}} */
973 
974 /* {{{ Loads any phar archive with an alias */
PHP_METHOD(Phar,loadPhar)975 PHP_METHOD(Phar, loadPhar)
976 {
977 	char *fname, *alias = NULL, *error;
978 	size_t fname_len, alias_len = 0;
979 
980 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|s!", &fname, &fname_len, &alias, &alias_len) == FAILURE) {
981 		RETURN_THROWS();
982 	}
983 
984 	phar_request_initialize();
985 
986 	RETVAL_BOOL(phar_open_from_filename(fname, fname_len, alias, alias_len, REPORT_ERRORS, NULL, &error) == SUCCESS);
987 
988 	if (error) {
989 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
990 		efree(error);
991 	}
992 } /* }}} */
993 
994 /* {{{ Returns the api version */
PHP_METHOD(Phar,apiVersion)995 PHP_METHOD(Phar, apiVersion)
996 {
997 	if (zend_parse_parameters_none() == FAILURE) {
998 		RETURN_THROWS();
999 	}
1000 	RETURN_STRINGL(PHP_PHAR_API_VERSION, sizeof(PHP_PHAR_API_VERSION)-1);
1001 }
1002 /* }}}*/
1003 
1004 /* {{{ Returns whether phar extension supports compression using zlib/bzip2 */
PHP_METHOD(Phar,canCompress)1005 PHP_METHOD(Phar, canCompress)
1006 {
1007 	zend_long method = 0;
1008 
1009 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &method) == FAILURE) {
1010 		RETURN_THROWS();
1011 	}
1012 
1013 	phar_request_initialize();
1014 	switch (method) {
1015 	case PHAR_ENT_COMPRESSED_GZ:
1016 		if (PHAR_G(has_zlib)) {
1017 			RETURN_TRUE;
1018 		} else {
1019 			RETURN_FALSE;
1020 		}
1021 	case PHAR_ENT_COMPRESSED_BZ2:
1022 		if (PHAR_G(has_bz2)) {
1023 			RETURN_TRUE;
1024 		} else {
1025 			RETURN_FALSE;
1026 		}
1027 	default:
1028 		if (PHAR_G(has_zlib) || PHAR_G(has_bz2)) {
1029 			RETURN_TRUE;
1030 		} else {
1031 			RETURN_FALSE;
1032 		}
1033 	}
1034 }
1035 /* }}} */
1036 
1037 /* {{{ Returns whether phar extension supports writing and creating phars */
PHP_METHOD(Phar,canWrite)1038 PHP_METHOD(Phar, canWrite)
1039 {
1040 	if (zend_parse_parameters_none() == FAILURE) {
1041 		RETURN_THROWS();
1042 	}
1043 	RETURN_BOOL(!PHAR_G(readonly));
1044 }
1045 /* }}} */
1046 
1047 /* {{{ Returns whether the given filename is a valid phar filename */
PHP_METHOD(Phar,isValidPharFilename)1048 PHP_METHOD(Phar, isValidPharFilename)
1049 {
1050 	char *fname;
1051 	const char *ext_str;
1052 	size_t fname_len;
1053 	size_t ext_len;
1054 	int is_executable;
1055 	bool executable = 1;
1056 
1057 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|b", &fname, &fname_len, &executable) == FAILURE) {
1058 		RETURN_THROWS();
1059 	}
1060 
1061 	is_executable = executable;
1062 	RETVAL_BOOL(phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, is_executable, 2, 1) == SUCCESS);
1063 }
1064 /* }}} */
1065 
1066 /**
1067  * from spl_directory
1068  */
phar_spl_foreign_dtor(spl_filesystem_object * object)1069 static void phar_spl_foreign_dtor(spl_filesystem_object *object) /* {{{ */
1070 {
1071 	phar_archive_data *phar = (phar_archive_data *) object->oth;
1072 
1073 	if (!phar->is_persistent) {
1074 		phar_archive_delref(phar);
1075 	}
1076 
1077 	object->oth = NULL;
1078 }
1079 /* }}} */
1080 
1081 /**
1082  * from spl_directory
1083  */
phar_spl_foreign_clone(spl_filesystem_object * src,spl_filesystem_object * dst)1084 static void phar_spl_foreign_clone(spl_filesystem_object *src, spl_filesystem_object *dst) /* {{{ */
1085 {
1086 	phar_archive_data *phar_data = (phar_archive_data *) dst->oth;
1087 
1088 	if (!phar_data->is_persistent) {
1089 		++(phar_data->refcount);
1090 	}
1091 }
1092 /* }}} */
1093 
1094 static const spl_other_handler phar_spl_foreign_handler = {
1095 	phar_spl_foreign_dtor,
1096 	phar_spl_foreign_clone
1097 };
1098 
1099 /* {{{ Construct a Phar archive object
1100  *
1101  * proto PharData::__construct(string fname [[, int flags [, string alias]], int file format = Phar::TAR])
1102  * Construct a PharData archive object
1103  *
1104  * This function is used as the constructor for both the Phar and PharData
1105  * classes, hence the two prototypes above.
1106  */
PHP_METHOD(Phar,__construct)1107 PHP_METHOD(Phar, __construct)
1108 {
1109 	char *fname, *alias = NULL, *error, *arch = NULL, *entry = NULL, *save_fname;
1110 	size_t fname_len, alias_len = 0;
1111 	size_t arch_len, entry_len;
1112 	bool is_data;
1113 	zend_long flags = SPL_FILE_DIR_SKIPDOTS|SPL_FILE_DIR_UNIXPATHS;
1114 	zend_long format = 0;
1115 	phar_archive_object *phar_obj;
1116 	phar_archive_data   *phar_data;
1117 	zval *zobj = ZEND_THIS, arg1, arg2;
1118 
1119 	phar_obj = (phar_archive_object*)((char*)Z_OBJ_P(zobj) - Z_OBJ_P(zobj)->handlers->offset);
1120 
1121 	is_data = instanceof_function(Z_OBJCE_P(zobj), phar_ce_data);
1122 
1123 	if (is_data) {
1124 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|ls!l", &fname, &fname_len, &flags, &alias, &alias_len, &format) == FAILURE) {
1125 			RETURN_THROWS();
1126 		}
1127 	} else {
1128 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|ls!", &fname, &fname_len, &flags, &alias, &alias_len) == FAILURE) {
1129 			RETURN_THROWS();
1130 		}
1131 	}
1132 
1133 	if (phar_obj->archive) {
1134 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot call constructor twice");
1135 		RETURN_THROWS();
1136 	}
1137 
1138 	save_fname = fname;
1139 	if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, !is_data, 2)) {
1140 		/* use arch (the basename for the archive) for fname instead of fname */
1141 		/* this allows support for RecursiveDirectoryIterator of subdirectories */
1142 #ifdef PHP_WIN32
1143 		phar_unixify_path_separators(arch, arch_len);
1144 #endif
1145 		fname = arch;
1146 		fname_len = arch_len;
1147 #ifdef PHP_WIN32
1148 	} else {
1149 		arch = estrndup(fname, fname_len);
1150 		arch_len = fname_len;
1151 		fname = arch;
1152 		phar_unixify_path_separators(arch, arch_len);
1153 #endif
1154 	}
1155 
1156 	if (phar_open_or_create_filename(fname, fname_len, alias, alias_len, is_data, REPORT_ERRORS, &phar_data, &error) == FAILURE) {
1157 
1158 		if (fname == arch && fname != save_fname) {
1159 			efree(arch);
1160 			fname = save_fname;
1161 		}
1162 
1163 		if (entry) {
1164 			efree(entry);
1165 		}
1166 
1167 		if (error) {
1168 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
1169 				"%s", error);
1170 			efree(error);
1171 		} else {
1172 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
1173 				"Phar creation or opening failed");
1174 		}
1175 
1176 		RETURN_THROWS();
1177 	}
1178 
1179 	if (is_data && phar_data->is_tar && phar_data->is_brandnew && format == PHAR_FORMAT_ZIP) {
1180 		phar_data->is_zip = 1;
1181 		phar_data->is_tar = 0;
1182 	}
1183 
1184 	if (fname == arch) {
1185 		efree(arch);
1186 		fname = save_fname;
1187 	}
1188 
1189 	if ((is_data && !phar_data->is_data) || (!is_data && phar_data->is_data)) {
1190 		if (is_data) {
1191 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
1192 				"PharData class can only be used for non-executable tar and zip archives");
1193 		} else {
1194 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
1195 				"Phar class can only be used for executable tar and zip archives");
1196 		}
1197 		efree(entry);
1198 		RETURN_THROWS();
1199 	}
1200 
1201 	is_data = phar_data->is_data;
1202 
1203 	if (!phar_data->is_persistent) {
1204 		++(phar_data->refcount);
1205 	}
1206 
1207 	phar_obj->archive = phar_data;
1208 	phar_obj->spl.oth_handler = &phar_spl_foreign_handler;
1209 
1210 	if (entry) {
1211 		fname_len = spprintf(&fname, 0, "phar://%s%s", phar_data->fname, entry);
1212 		efree(entry);
1213 	} else {
1214 		fname_len = spprintf(&fname, 0, "phar://%s", phar_data->fname);
1215 	}
1216 
1217 	ZVAL_STRINGL(&arg1, fname, fname_len);
1218 	ZVAL_LONG(&arg2, flags);
1219 
1220 	zend_call_known_instance_method_with_2_params(spl_ce_RecursiveDirectoryIterator->constructor,
1221 		Z_OBJ_P(zobj), NULL, &arg1, &arg2);
1222 
1223 	zval_ptr_dtor(&arg1);
1224 
1225 	if (!phar_data->is_persistent) {
1226 		phar_obj->archive->is_data = is_data;
1227 	} else if (!EG(exception)) {
1228 		/* register this guy so we can modify if necessary */
1229 		zend_hash_str_add_ptr(&PHAR_G(phar_persist_map), (const char *) phar_obj->archive, sizeof(phar_obj->archive), phar_obj);
1230 	}
1231 
1232 	phar_obj->spl.info_class = phar_ce_entry;
1233 	efree(fname);
1234 }
1235 /* }}} */
1236 
1237 /* {{{ Return array of supported signature types */
PHP_METHOD(Phar,getSupportedSignatures)1238 PHP_METHOD(Phar, getSupportedSignatures)
1239 {
1240 	if (zend_parse_parameters_none() == FAILURE) {
1241 		RETURN_THROWS();
1242 	}
1243 
1244 	array_init(return_value);
1245 
1246 	add_next_index_stringl(return_value, "MD5", 3);
1247 	add_next_index_stringl(return_value, "SHA-1", 5);
1248 	add_next_index_stringl(return_value, "SHA-256", 7);
1249 	add_next_index_stringl(return_value, "SHA-512", 7);
1250 #ifdef PHAR_HAVE_OPENSSL
1251 	add_next_index_stringl(return_value, "OpenSSL", 7);
1252 	add_next_index_stringl(return_value, "OpenSSL_SHA256", 14);
1253 	add_next_index_stringl(return_value, "OpenSSL_SHA512", 14);
1254 #else
1255 	if (zend_hash_str_exists(&module_registry, "openssl", sizeof("openssl")-1)) {
1256 		add_next_index_stringl(return_value, "OpenSSL", 7);
1257 		add_next_index_stringl(return_value, "OpenSSL_SHA256", 14);
1258 		add_next_index_stringl(return_value, "OpenSSL_SHA512", 14);
1259 	}
1260 #endif
1261 }
1262 /* }}} */
1263 
1264 /* {{{ Return array of supported comparession algorithms */
PHP_METHOD(Phar,getSupportedCompression)1265 PHP_METHOD(Phar, getSupportedCompression)
1266 {
1267 	if (zend_parse_parameters_none() == FAILURE) {
1268 		RETURN_THROWS();
1269 	}
1270 
1271 	array_init(return_value);
1272 	phar_request_initialize();
1273 
1274 	if (PHAR_G(has_zlib)) {
1275 		add_next_index_stringl(return_value, "GZ", 2);
1276 	}
1277 
1278 	if (PHAR_G(has_bz2)) {
1279 		add_next_index_stringl(return_value, "BZIP2", 5);
1280 	}
1281 }
1282 /* }}} */
1283 
1284 /* {{{ Completely remove a phar archive from memory and disk */
PHP_METHOD(Phar,unlinkArchive)1285 PHP_METHOD(Phar, unlinkArchive)
1286 {
1287 	char *fname, *error, *arch, *entry;
1288 	size_t fname_len;
1289 	size_t arch_len, entry_len;
1290 	phar_archive_data *phar;
1291 
1292 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &fname, &fname_len) == FAILURE) {
1293 		RETURN_THROWS();
1294 	}
1295 
1296 	if (!fname_len) {
1297 		zend_throw_exception_ex(phar_ce_PharException, 0, "Unknown phar archive \"\"");
1298 		RETURN_THROWS();
1299 	}
1300 
1301 	if (FAILURE == phar_open_from_filename(fname, fname_len, NULL, 0, REPORT_ERRORS, &phar, &error)) {
1302 		if (error) {
1303 			zend_throw_exception_ex(phar_ce_PharException, 0, "Unknown phar archive \"%s\": %s", fname, error);
1304 			efree(error);
1305 		} else {
1306 			zend_throw_exception_ex(phar_ce_PharException, 0, "Unknown phar archive \"%s\"", fname);
1307 		}
1308 		RETURN_THROWS();
1309 	}
1310 
1311 	zend_string *zend_file_name = zend_get_executed_filename_ex();
1312 
1313 	if (
1314 		zend_file_name
1315 		&& zend_string_starts_with_literal_ci(zend_file_name, "phar://")
1316 		&& SUCCESS == phar_split_fname(ZSTR_VAL(zend_file_name), ZSTR_LEN(zend_file_name), &arch, &arch_len, &entry, &entry_len, 2, 0)
1317 	) {
1318 		if (arch_len == fname_len && !memcmp(arch, fname, arch_len)) {
1319 			zend_throw_exception_ex(phar_ce_PharException, 0, "phar archive \"%s\" cannot be unlinked from within itself", fname);
1320 			efree(arch);
1321 			efree(entry);
1322 			RETURN_THROWS();
1323 		}
1324 		efree(arch);
1325 		efree(entry);
1326 	}
1327 
1328 	if (phar->is_persistent) {
1329 		zend_throw_exception_ex(phar_ce_PharException, 0, "phar archive \"%s\" is in phar.cache_list, cannot unlinkArchive()", fname);
1330 		RETURN_THROWS();
1331 	}
1332 
1333 	if (phar->refcount) {
1334 		zend_throw_exception_ex(phar_ce_PharException, 0, "phar archive \"%s\" has open file handles or objects.  fclose() all file handles, and unset() all objects prior to calling unlinkArchive()", fname);
1335 		RETURN_THROWS();
1336 	}
1337 
1338 	fname = estrndup(phar->fname, phar->fname_len);
1339 
1340 	/* invalidate phar cache */
1341 	PHAR_G(last_phar) = NULL;
1342 	PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
1343 
1344 	phar_archive_delref(phar);
1345 	unlink(fname);
1346 	efree(fname);
1347 	RETURN_TRUE;
1348 }
1349 /* }}} */
1350 
1351 #define PHAR_ARCHIVE_OBJECT() \
1352 	zval *zobj = ZEND_THIS; \
1353 	phar_archive_object *phar_obj = (phar_archive_object*)((char*)Z_OBJ_P(zobj) - Z_OBJ_P(zobj)->handlers->offset); \
1354 	if (!phar_obj->archive) { \
1355 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, \
1356 			"Cannot call method on an uninitialized Phar object"); \
1357 		RETURN_THROWS(); \
1358 	}
1359 
1360 /* {{{ if persistent, remove from the cache */
PHP_METHOD(Phar,__destruct)1361 PHP_METHOD(Phar, __destruct)
1362 {
1363 	zval *zobj = ZEND_THIS;
1364 	phar_archive_object *phar_obj = (phar_archive_object*)((char*)Z_OBJ_P(zobj) - Z_OBJ_P(zobj)->handlers->offset);
1365 
1366 	if (zend_parse_parameters_none() == FAILURE) {
1367 		RETURN_THROWS();
1368 	}
1369 
1370 	if (phar_obj->archive && phar_obj->archive->is_persistent) {
1371 		zend_hash_str_del(&PHAR_G(phar_persist_map), (const char *) phar_obj->archive, sizeof(phar_obj->archive));
1372 	}
1373 }
1374 /* }}} */
1375 
1376 struct _phar_t {
1377 	phar_archive_object *p;
1378 	zend_class_entry *c;
1379 	zend_string *base;
1380 	zval *ret;
1381 	php_stream *fp;
1382 	int count;
1383 };
1384 
phar_build(zend_object_iterator * iter,void * puser)1385 static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
1386 {
1387 	zval *value;
1388 	bool close_fp = 1;
1389 	struct _phar_t *p_obj = (struct _phar_t*) puser;
1390 	size_t str_key_len, base_len = ZSTR_LEN(p_obj->base);
1391 	phar_entry_data *data;
1392 	php_stream *fp;
1393 	size_t fname_len;
1394 	size_t contents_len;
1395 	char *fname, *error = NULL, *base = ZSTR_VAL(p_obj->base), *save = NULL, *temp = NULL;
1396 	zend_string *opened;
1397 	char *str_key;
1398 	zend_class_entry *ce = p_obj->c;
1399 	phar_archive_object *phar_obj = p_obj->p;
1400 	php_stream_statbuf ssb;
1401 	char ch;
1402 
1403 	value = iter->funcs->get_current_data(iter);
1404 
1405 	if (EG(exception)) {
1406 		return ZEND_HASH_APPLY_STOP;
1407 	}
1408 
1409 	if (!value) {
1410 		/* failure in get_current_data */
1411 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned no value", ZSTR_VAL(ce->name));
1412 		return ZEND_HASH_APPLY_STOP;
1413 	}
1414 
1415 	switch (Z_TYPE_P(value)) {
1416 		case IS_STRING:
1417 			break;
1418 		case IS_RESOURCE:
1419 			php_stream_from_zval_no_verify(fp, value);
1420 
1421 			if (!fp) {
1422 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Iterator %s returned an invalid stream handle", ZSTR_VAL(ce->name));
1423 				return ZEND_HASH_APPLY_STOP;
1424 			}
1425 
1426 			if (iter->funcs->get_current_key) {
1427 				zval key;
1428 				iter->funcs->get_current_key(iter, &key);
1429 
1430 				if (EG(exception)) {
1431 					return ZEND_HASH_APPLY_STOP;
1432 				}
1433 
1434 				if (Z_TYPE(key) != IS_STRING) {
1435 					zval_ptr_dtor(&key);
1436 					zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned an invalid key (must return a string)", ZSTR_VAL(ce->name));
1437 					return ZEND_HASH_APPLY_STOP;
1438 				}
1439 
1440 				str_key_len = Z_STRLEN(key);
1441 				str_key = estrndup(Z_STRVAL(key), str_key_len);
1442 
1443 				save = str_key;
1444 				zval_ptr_dtor_str(&key);
1445 			} else {
1446 				zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned an invalid key (must return a string)", ZSTR_VAL(ce->name));
1447 				return ZEND_HASH_APPLY_STOP;
1448 			}
1449 
1450 			close_fp = 0;
1451 			opened = ZSTR_INIT_LITERAL("[stream]", 0);
1452 			goto after_open_fp;
1453 		case IS_OBJECT:
1454 			if (instanceof_function(Z_OBJCE_P(value), spl_ce_SplFileInfo)) {
1455 				char *test = NULL;
1456 				spl_filesystem_object *intern = (spl_filesystem_object*)((char*)Z_OBJ_P(value) - Z_OBJ_P(value)->handlers->offset);
1457 
1458 				if (!base_len) {
1459 					zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Iterator %s returns an SplFileInfo object, so base directory must be specified", ZSTR_VAL(ce->name));
1460 					return ZEND_HASH_APPLY_STOP;
1461 				}
1462 
1463 				switch (intern->type) {
1464 					case SPL_FS_DIR: {
1465 						zend_string *test_str = spl_filesystem_object_get_path(intern);
1466 						fname_len = spprintf(&fname, 0, "%s%c%s", ZSTR_VAL(test_str), DEFAULT_SLASH, intern->u.dir.entry.d_name);
1467 						zend_string_release_ex(test_str, /* persistent */ false);
1468 						if (php_stream_stat_path(fname, &ssb) == 0 && S_ISDIR(ssb.sb.st_mode)) {
1469 							/* ignore directories */
1470 							efree(fname);
1471 							return ZEND_HASH_APPLY_KEEP;
1472 						}
1473 
1474 						test = expand_filepath(fname, NULL);
1475 						efree(fname);
1476 
1477 						if (test) {
1478 							fname = test;
1479 							fname_len = strlen(fname);
1480 						} else {
1481 							zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Could not resolve file path");
1482 							return ZEND_HASH_APPLY_STOP;
1483 						}
1484 
1485 						save = fname;
1486 						goto phar_spl_fileinfo;
1487 					}
1488 					case SPL_FS_INFO:
1489 					case SPL_FS_FILE:
1490 						fname = expand_filepath(ZSTR_VAL(intern->file_name), NULL);
1491 						if (!fname) {
1492 							zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Could not resolve file path");
1493 							return ZEND_HASH_APPLY_STOP;
1494 						}
1495 
1496 						fname_len = strlen(fname);
1497 						save = fname;
1498 						goto phar_spl_fileinfo;
1499 				}
1500 			}
1501 			ZEND_FALLTHROUGH;
1502 		default:
1503 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned an invalid value (must return a string)", ZSTR_VAL(ce->name));
1504 			return ZEND_HASH_APPLY_STOP;
1505 	}
1506 
1507 	fname = Z_STRVAL_P(value);
1508 	fname_len = Z_STRLEN_P(value);
1509 
1510 phar_spl_fileinfo:
1511 	if (base_len) {
1512 		temp = expand_filepath(base, NULL);
1513 		if (!temp) {
1514 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Could not resolve file path");
1515 			if (save) {
1516 				efree(save);
1517 			}
1518 			return ZEND_HASH_APPLY_STOP;
1519 		}
1520 
1521 		base = temp;
1522 		base_len = strlen(base);
1523 
1524 		if (fname_len >= base_len && strncmp(fname, base, base_len) == 0 && ((ch = fname[base_len - IS_SLASH(base[base_len - 1])]) == '\0' || IS_SLASH(ch))) {
1525 			str_key_len = fname_len - base_len;
1526 
1527 			if (str_key_len <= 0) {
1528 				if (save) {
1529 					efree(save);
1530 					efree(temp);
1531 				}
1532 				return ZEND_HASH_APPLY_KEEP;
1533 			}
1534 
1535 			str_key = fname + base_len;
1536 
1537 			if (*str_key == '/' || *str_key == '\\') {
1538 				str_key++;
1539 				str_key_len--;
1540 			}
1541 
1542 		} else {
1543 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned a path \"%s\" that is not in the base directory \"%s\"", ZSTR_VAL(ce->name), fname, base);
1544 
1545 			if (save) {
1546 				efree(save);
1547 				efree(temp);
1548 			}
1549 
1550 			return ZEND_HASH_APPLY_STOP;
1551 		}
1552 	} else {
1553 		if (iter->funcs->get_current_key) {
1554 			zval key;
1555 			iter->funcs->get_current_key(iter, &key);
1556 
1557 			if (EG(exception)) {
1558 				return ZEND_HASH_APPLY_STOP;
1559 			}
1560 
1561 			if (Z_TYPE(key) != IS_STRING) {
1562 				zval_ptr_dtor(&key);
1563 				zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned an invalid key (must return a string)", ZSTR_VAL(ce->name));
1564 				return ZEND_HASH_APPLY_STOP;
1565 			}
1566 
1567 			str_key_len = Z_STRLEN(key);
1568 			str_key = estrndup(Z_STRVAL(key), str_key_len);
1569 
1570 			save = str_key;
1571 			zval_ptr_dtor_str(&key);
1572 		} else {
1573 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned an invalid key (must return a string)", ZSTR_VAL(ce->name));
1574 			return ZEND_HASH_APPLY_STOP;
1575 		}
1576 	}
1577 
1578 	if (php_check_open_basedir(fname)) {
1579 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned a path \"%s\" that open_basedir prevents opening", ZSTR_VAL(ce->name), fname);
1580 
1581 		if (save) {
1582 			efree(save);
1583 		}
1584 
1585 		if (temp) {
1586 			efree(temp);
1587 		}
1588 
1589 		return ZEND_HASH_APPLY_STOP;
1590 	}
1591 
1592 	/* try to open source file, then create internal phar file and copy contents */
1593 	fp = php_stream_open_wrapper(fname, "rb", STREAM_MUST_SEEK|0, &opened);
1594 
1595 	if (!fp) {
1596 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned a file that could not be opened \"%s\"", ZSTR_VAL(ce->name), fname);
1597 
1598 		if (save) {
1599 			efree(save);
1600 		}
1601 
1602 		if (temp) {
1603 			efree(temp);
1604 		}
1605 
1606 		return ZEND_HASH_APPLY_STOP;
1607 	}
1608 after_open_fp:
1609 	if (str_key_len >= sizeof(".phar")-1 && !memcmp(str_key, ".phar", sizeof(".phar")-1)) {
1610 		/* silently skip any files that would be added to the magic .phar directory */
1611 		if (save) {
1612 			efree(save);
1613 		}
1614 
1615 		if (temp) {
1616 			efree(temp);
1617 		}
1618 
1619 		if (opened) {
1620 			zend_string_release_ex(opened, 0);
1621 		}
1622 
1623 		if (close_fp) {
1624 			php_stream_close(fp);
1625 		}
1626 
1627 		return ZEND_HASH_APPLY_KEEP;
1628 	}
1629 
1630 	if (!(data = phar_get_or_create_entry_data(phar_obj->archive->fname, phar_obj->archive->fname_len, str_key, str_key_len, "w+b", 0, &error, 1))) {
1631 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s cannot be created: %s", str_key, error);
1632 		efree(error);
1633 
1634 		if (save) {
1635 			efree(save);
1636 		}
1637 
1638 		if (opened) {
1639 			zend_string_release_ex(opened, 0);
1640 		}
1641 
1642 		if (temp) {
1643 			efree(temp);
1644 		}
1645 
1646 		if (close_fp) {
1647 			php_stream_close(fp);
1648 		}
1649 
1650 		return ZEND_HASH_APPLY_STOP;
1651 
1652 	} else {
1653 		if (error) {
1654 			efree(error);
1655 		}
1656 		/* convert to PHAR_UFP */
1657 		if (data->internal_file->fp_type == PHAR_MOD) {
1658 			php_stream_close(data->internal_file->fp);
1659 		}
1660 
1661 		data->internal_file->fp = NULL;
1662 		data->internal_file->fp_type = PHAR_UFP;
1663 		data->internal_file->offset_abs = data->internal_file->offset = php_stream_tell(p_obj->fp);
1664 		data->fp = NULL;
1665 		php_stream_copy_to_stream_ex(fp, p_obj->fp, PHP_STREAM_COPY_ALL, &contents_len);
1666 		data->internal_file->uncompressed_filesize = data->internal_file->compressed_filesize =
1667 			php_stream_tell(p_obj->fp) - data->internal_file->offset;
1668 		if (php_stream_stat(fp, &ssb) != -1) {
1669 			data->internal_file->flags = ssb.sb.st_mode & PHAR_ENT_PERM_MASK ;
1670 		} else {
1671 #ifndef _WIN32
1672 			mode_t mask;
1673 			mask = umask(0);
1674 			umask(mask);
1675 			data->internal_file->flags &= ~mask;
1676 #endif
1677 		}
1678 	}
1679 
1680 	if (close_fp) {
1681 		php_stream_close(fp);
1682 	}
1683 
1684 	add_assoc_str(p_obj->ret, str_key, opened);
1685 
1686 	if (save) {
1687 		efree(save);
1688 	}
1689 
1690 	if (temp) {
1691 		efree(temp);
1692 	}
1693 
1694 	data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize = contents_len;
1695 	phar_entry_delref(data);
1696 
1697 	return ZEND_HASH_APPLY_KEEP;
1698 }
1699 /* }}} */
1700 
1701 /* {{{ Construct a phar archive from an existing directory, recursively.
1702  * Optional second parameter is a regular expression for filtering directory contents.
1703  *
1704  * Return value is an array mapping phar index to actual files added.
1705  */
PHP_METHOD(Phar,buildFromDirectory)1706 PHP_METHOD(Phar, buildFromDirectory)
1707 {
1708 	char *error;
1709 	bool apply_reg = 0;
1710 	zval arg, arg2, iter, iteriter, regexiter;
1711 	struct _phar_t pass;
1712 	zend_string *dir, *regex = NULL;
1713 
1714 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|S", &dir, &regex) == FAILURE) {
1715 		RETURN_THROWS();
1716 	}
1717 
1718 	PHAR_ARCHIVE_OBJECT();
1719 
1720 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
1721 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
1722 			"Cannot write to archive - write operations restricted by INI setting");
1723 		RETURN_THROWS();
1724 	}
1725 
1726 	if (SUCCESS != object_init_ex(&iter, spl_ce_RecursiveDirectoryIterator)) {
1727 		zval_ptr_dtor(&iter);
1728 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unable to instantiate directory iterator for %s", phar_obj->archive->fname);
1729 		RETURN_THROWS();
1730 	}
1731 
1732 	ZVAL_STR(&arg, dir);
1733 	ZVAL_LONG(&arg2, SPL_FILE_DIR_SKIPDOTS|SPL_FILE_DIR_UNIXPATHS);
1734 
1735 	zend_call_known_instance_method_with_2_params(spl_ce_RecursiveDirectoryIterator->constructor,
1736 		Z_OBJ(iter), NULL, &arg, &arg2);
1737 
1738 	if (EG(exception)) {
1739 		zval_ptr_dtor(&iter);
1740 		RETURN_THROWS();
1741 	}
1742 
1743 	if (SUCCESS != object_init_ex(&iteriter, spl_ce_RecursiveIteratorIterator)) {
1744 		zval_ptr_dtor(&iter);
1745 		zval_ptr_dtor(&iteriter);
1746 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unable to instantiate directory iterator for %s", phar_obj->archive->fname);
1747 		RETURN_THROWS();
1748 	}
1749 
1750 	zend_call_known_instance_method_with_1_params(spl_ce_RecursiveIteratorIterator->constructor,
1751 		Z_OBJ(iteriter), NULL, &iter);
1752 
1753 	if (EG(exception)) {
1754 		zval_ptr_dtor(&iter);
1755 		zval_ptr_dtor(&iteriter);
1756 		RETURN_THROWS();
1757 	}
1758 
1759 	zval_ptr_dtor(&iter);
1760 
1761 	if (regex && ZSTR_LEN(regex) > 0) {
1762 		apply_reg = 1;
1763 
1764 		if (SUCCESS != object_init_ex(&regexiter, spl_ce_RegexIterator)) {
1765 			zval_ptr_dtor(&iteriter);
1766 			zval_ptr_dtor(&regexiter);
1767 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unable to instantiate regex iterator for %s", phar_obj->archive->fname);
1768 			RETURN_THROWS();
1769 		}
1770 
1771 		ZVAL_STR(&arg2, regex);
1772 		zend_call_known_instance_method_with_2_params(spl_ce_RegexIterator->constructor,
1773 			Z_OBJ(regexiter), NULL, &iteriter, &arg2);
1774 	}
1775 
1776 	array_init(return_value);
1777 
1778 	pass.c = apply_reg ? Z_OBJCE(regexiter) : Z_OBJCE(iteriter);
1779 	pass.p = phar_obj;
1780 	pass.base = dir;
1781 	pass.count = 0;
1782 	pass.ret = return_value;
1783 	pass.fp = php_stream_fopen_tmpfile();
1784 	if (pass.fp == NULL) {
1785 		zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" unable to create temporary file", phar_obj->archive->fname);
1786 		RETURN_THROWS();
1787 	}
1788 
1789 	if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
1790 		zval_ptr_dtor(&iteriter);
1791 		if (apply_reg) {
1792 			zval_ptr_dtor(&regexiter);
1793 		}
1794 		php_stream_close(pass.fp);
1795 		zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
1796 		RETURN_THROWS();
1797 	}
1798 
1799 	if (SUCCESS == spl_iterator_apply((apply_reg ? &regexiter : &iteriter), (spl_iterator_apply_func_t) phar_build, (void *) &pass)) {
1800 		zval_ptr_dtor(&iteriter);
1801 
1802 		if (apply_reg) {
1803 			zval_ptr_dtor(&regexiter);
1804 		}
1805 
1806 		phar_obj->archive->ufp = pass.fp;
1807 		phar_flush(phar_obj->archive, 0, 0, 0, &error);
1808 
1809 		if (error) {
1810 			zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
1811 			efree(error);
1812 		}
1813 
1814 	} else {
1815 		zval_ptr_dtor(&iteriter);
1816 		if (apply_reg) {
1817 			zval_ptr_dtor(&regexiter);
1818 		}
1819 		php_stream_close(pass.fp);
1820 	}
1821 }
1822 /* }}} */
1823 
1824 /* {{{ Construct a phar archive from an iterator.  The iterator must return a series of strings
1825  * that are full paths to files that should be added to the phar.  The iterator key should
1826  * be the path that the file will have within the phar archive.
1827  *
1828  * If base directory is specified, then the key will be ignored, and instead the portion of
1829  * the current value minus the base directory will be used
1830  *
1831  * Returned is an array mapping phar index to actual file added
1832  */
PHP_METHOD(Phar,buildFromIterator)1833 PHP_METHOD(Phar, buildFromIterator)
1834 {
1835 	zval *obj;
1836 	char *error;
1837 	zend_string *base = ZSTR_EMPTY_ALLOC();
1838 	struct _phar_t pass;
1839 
1840 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|S!", &obj, zend_ce_traversable, &base) == FAILURE) {
1841 		RETURN_THROWS();
1842 	}
1843 
1844 	PHAR_ARCHIVE_OBJECT();
1845 
1846 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
1847 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
1848 			"Cannot write out phar archive, phar is read-only");
1849 		RETURN_THROWS();
1850 	}
1851 
1852 	if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
1853 		zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
1854 		RETURN_THROWS();
1855 	}
1856 
1857 	array_init(return_value);
1858 
1859 	pass.c = Z_OBJCE_P(obj);
1860 	pass.p = phar_obj;
1861 	pass.base = base;
1862 	pass.ret = return_value;
1863 	pass.count = 0;
1864 	pass.fp = php_stream_fopen_tmpfile();
1865 	if (pass.fp == NULL) {
1866 		zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\": unable to create temporary file", phar_obj->archive->fname);
1867 		RETURN_THROWS();
1868 	}
1869 
1870 	if (SUCCESS == spl_iterator_apply(obj, (spl_iterator_apply_func_t) phar_build, (void *) &pass)) {
1871 		phar_obj->archive->ufp = pass.fp;
1872 		phar_flush(phar_obj->archive, 0, 0, 0, &error);
1873 		if (error) {
1874 			zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
1875 			efree(error);
1876 		}
1877 	} else {
1878 		php_stream_close(pass.fp);
1879 	}
1880 }
1881 /* }}} */
1882 
1883 /* {{{ Returns the number of entries in the Phar archive */
PHP_METHOD(Phar,count)1884 PHP_METHOD(Phar, count)
1885 {
1886 	/* mode can be ignored, maximum depth is 1 */
1887 	zend_long mode;
1888 
1889 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &mode) == FAILURE) {
1890 		RETURN_THROWS();
1891 	}
1892 
1893 	PHAR_ARCHIVE_OBJECT();
1894 
1895 	RETURN_LONG(zend_hash_num_elements(&phar_obj->archive->manifest));
1896 }
1897 /* }}} */
1898 
1899 /* {{{ Returns true if the phar archive is based on the tar/zip/phar file format depending
1900  * on whether Phar::TAR, Phar::ZIP or Phar::PHAR was passed in
1901  */
PHP_METHOD(Phar,isFileFormat)1902 PHP_METHOD(Phar, isFileFormat)
1903 {
1904 	zend_long type;
1905 
1906 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &type) == FAILURE) {
1907 		RETURN_THROWS();
1908 	}
1909 
1910 	PHAR_ARCHIVE_OBJECT();
1911 
1912 	switch (type) {
1913 		case PHAR_FORMAT_TAR:
1914 			RETURN_BOOL(phar_obj->archive->is_tar);
1915 		case PHAR_FORMAT_ZIP:
1916 			RETURN_BOOL(phar_obj->archive->is_zip);
1917 		case PHAR_FORMAT_PHAR:
1918 			RETURN_BOOL(!phar_obj->archive->is_tar && !phar_obj->archive->is_zip);
1919 		default:
1920 			zend_throw_exception_ex(phar_ce_PharException, 0, "Unknown file format specified");
1921 	}
1922 }
1923 /* }}} */
1924 
phar_copy_file_contents(phar_entry_info * entry,php_stream * fp)1925 static int phar_copy_file_contents(phar_entry_info *entry, php_stream *fp) /* {{{ */
1926 {
1927 	char *error;
1928 	zend_off_t offset;
1929 	phar_entry_info *link;
1930 
1931 	if (FAILURE == phar_open_entry_fp(entry, &error, 1)) {
1932 		if (error) {
1933 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
1934 				"Cannot convert phar archive \"%s\", unable to open entry \"%s\" contents: %s", entry->phar->fname, entry->filename, error);
1935 			efree(error);
1936 		} else {
1937 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
1938 				"Cannot convert phar archive \"%s\", unable to open entry \"%s\" contents", entry->phar->fname, entry->filename);
1939 		}
1940 		return FAILURE;
1941 	}
1942 
1943 	/* copy old contents in entirety */
1944 	phar_seek_efp(entry, 0, SEEK_SET, 0, 1);
1945 	offset = php_stream_tell(fp);
1946 	link = phar_get_link_source(entry);
1947 
1948 	if (!link) {
1949 		link = entry;
1950 	}
1951 
1952 	if (SUCCESS != php_stream_copy_to_stream_ex(phar_get_efp(link, 0), fp, link->uncompressed_filesize, NULL)) {
1953 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
1954 			"Cannot convert phar archive \"%s\", unable to copy entry \"%s\" contents", entry->phar->fname, entry->filename);
1955 		return FAILURE;
1956 	}
1957 
1958 	if (entry->fp_type == PHAR_MOD) {
1959 		/* save for potential restore on error */
1960 		entry->cfp = entry->fp;
1961 		entry->fp = NULL;
1962 	}
1963 
1964 	/* set new location of file contents */
1965 	entry->fp_type = PHAR_FP;
1966 	entry->offset = offset;
1967 	return SUCCESS;
1968 }
1969 /* }}} */
1970 
phar_rename_archive(phar_archive_data ** sphar,char * ext)1971 static zend_object *phar_rename_archive(phar_archive_data **sphar, char *ext) /* {{{ */
1972 {
1973 	const char *oldname = NULL;
1974 	phar_archive_data *phar = *sphar;
1975 	char *oldpath = NULL;
1976 	char *basename = NULL, *basepath = NULL;
1977 	char *newname = NULL, *newpath = NULL;
1978 	zval ret, arg1;
1979 	zend_class_entry *ce;
1980 	char *error = NULL;
1981 	const char *pcr_error;
1982 	size_t ext_len = ext ? strlen(ext) : 0;
1983 	size_t new_len, oldname_len, phar_ext_len;
1984 	phar_archive_data *pphar = NULL;
1985 	php_stream_statbuf ssb;
1986 
1987 	int phar_ext_list_len, i = 0;
1988 	char *ext_pos = NULL;
1989 	/* Array of PHAR extensions, Must be in order, starting with longest
1990 	 * ending with the shortest. */
1991 	static const char *const phar_ext_list[] = {
1992 		".phar.tar.bz2",
1993 		".phar.tar.gz",
1994 		".phar.php",
1995 		".phar.bz2",
1996 		".phar.zip",
1997 		".phar.tar",
1998 		".phar.gz",
1999 		".tar.bz2",
2000 		".tar.gz",
2001 		".phar",
2002 		".tar",
2003 		".zip"
2004 	};
2005 
2006 	if (!ext) {
2007 		if (phar->is_zip) {
2008 
2009 			if (phar->is_data) {
2010 				ext = "zip";
2011 			} else {
2012 				ext = "phar.zip";
2013 			}
2014 
2015 		} else if (phar->is_tar) {
2016 
2017 			switch (phar->flags) {
2018 				case PHAR_FILE_COMPRESSED_GZ:
2019 					if (phar->is_data) {
2020 						ext = "tar.gz";
2021 					} else {
2022 						ext = "phar.tar.gz";
2023 					}
2024 					break;
2025 				case PHAR_FILE_COMPRESSED_BZ2:
2026 					if (phar->is_data) {
2027 						ext = "tar.bz2";
2028 					} else {
2029 						ext = "phar.tar.bz2";
2030 					}
2031 					break;
2032 				default:
2033 					if (phar->is_data) {
2034 						ext = "tar";
2035 					} else {
2036 						ext = "phar.tar";
2037 					}
2038 			}
2039 		} else {
2040 
2041 			switch (phar->flags) {
2042 				case PHAR_FILE_COMPRESSED_GZ:
2043 					ext = "phar.gz";
2044 					break;
2045 				case PHAR_FILE_COMPRESSED_BZ2:
2046 					ext = "phar.bz2";
2047 					break;
2048 				default:
2049 					ext = "phar";
2050 			}
2051 		}
2052 	} else if (phar_path_check(&ext, &ext_len, &pcr_error) > pcr_is_ok) {
2053 
2054 		if (phar->is_data) {
2055 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "data phar converted from \"%s\" has invalid extension %s", phar->fname, ext);
2056 		} else {
2057 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "phar converted from \"%s\" has invalid extension %s", phar->fname, ext);
2058 		}
2059 		return NULL;
2060 	}
2061 
2062 
2063 	oldpath = estrndup(phar->fname, phar->fname_len);
2064 	if ((oldname = zend_memrchr(phar->fname, '/', phar->fname_len))) {
2065 		++oldname;
2066 	} else {
2067 		oldname = phar->fname;
2068 	}
2069 
2070 	oldname_len = strlen(oldname);
2071 	/* Copy the old name to create base for the new name */
2072 	basename = estrndup(oldname, oldname_len);
2073 
2074 	phar_ext_list_len = sizeof(phar_ext_list)/sizeof(phar_ext_list[0]);
2075 	/* Remove possible PHAR extensions */
2076 	/* phar_ext_list must be in order of longest extension to shortest */
2077 	for (i=0; i < phar_ext_list_len; i++) {
2078 		phar_ext_len = strlen(phar_ext_list[i]);
2079 		if (phar_ext_len && oldname_len > phar_ext_len) {
2080 			/* Check if the basename strings ends with the extension */
2081 			if (memcmp(phar_ext_list[i], basename + (oldname_len - phar_ext_len), phar_ext_len) == 0) {
2082 				ext_pos = basename + (oldname_len - phar_ext_len);
2083 				ext_pos[0] = '\0';
2084 				break;
2085 			}
2086 		}
2087 		ext_pos = NULL;
2088 	}
2089 
2090 	/* If no default PHAR extension found remove the last extension */
2091 	if (!ext_pos) {
2092 		ext_pos = strrchr(basename, '.');
2093 		if (ext_pos) {
2094 			ext_pos[0] = '\0';
2095 		}
2096 	}
2097 	ext_pos = NULL;
2098 
2099 	if (ext[0] == '.') {
2100 		++ext;
2101 	}
2102 	/* Append extension to the basename */
2103 	spprintf(&newname, 0, "%s.%s", basename, ext);
2104 	efree(basename);
2105 
2106 	basepath = estrndup(oldpath, (strlen(oldpath) - oldname_len));
2107 	new_len = spprintf(&newpath, 0, "%s%s", basepath, newname);
2108 	phar->fname_len = new_len;
2109 	phar->fname = newpath;
2110 	phar->ext = newpath + phar->fname_len - strlen(ext) - 1;
2111 	efree(basepath);
2112 	efree(newname);
2113 
2114 	if (PHAR_G(manifest_cached) && NULL != (pphar = zend_hash_str_find_ptr(&cached_phars, newpath, phar->fname_len))) {
2115 		efree(oldpath);
2116 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unable to add newly converted phar \"%s\" to the list of phars, new phar name is in phar.cache_list", phar->fname);
2117 		return NULL;
2118 	}
2119 
2120 	if (NULL != (pphar = zend_hash_str_find_ptr(&(PHAR_G(phar_fname_map)), newpath, phar->fname_len))) {
2121 		if (pphar->fname_len == phar->fname_len && !memcmp(pphar->fname, phar->fname, phar->fname_len)) {
2122 			if (!zend_hash_num_elements(&phar->manifest)) {
2123 				pphar->is_tar = phar->is_tar;
2124 				pphar->is_zip = phar->is_zip;
2125 				pphar->is_data = phar->is_data;
2126 				pphar->flags = phar->flags;
2127 				pphar->fp = phar->fp;
2128 				phar->fp = NULL;
2129 				/* FIX: GH-10755 Double-free issue caught by ASAN check */
2130 				pphar->alias = phar->alias; /* Transfer alias to pphar to */
2131 				phar->alias = NULL;         /* avoid being free'd twice   */
2132 				phar_destroy_phar_data(phar);
2133 				*sphar = NULL;
2134 				phar = pphar;
2135 				newpath = oldpath;
2136 				goto its_ok;
2137 			}
2138 		}
2139 
2140 		efree(oldpath);
2141 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unable to add newly converted phar \"%s\" to the list of phars, a phar with that name already exists", phar->fname);
2142 		return NULL;
2143 	}
2144 its_ok:
2145 	if (SUCCESS == php_stream_stat_path(newpath, &ssb)) {
2146 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "phar \"%s\" exists and must be unlinked prior to conversion", newpath);
2147 		efree(oldpath);
2148 		return NULL;
2149 	}
2150 	if (!phar->is_data) {
2151 		if (SUCCESS != phar_detect_phar_fname_ext(newpath, phar->fname_len, (const char **) &(phar->ext), &ext_len, 1, 1, 1)) {
2152 			efree(oldpath);
2153 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "phar \"%s\" has invalid extension %s", phar->fname, ext);
2154 			return NULL;
2155 		}
2156 		phar->ext_len = ext_len;
2157 
2158 		if (phar->alias) {
2159 			if (phar->is_temporary_alias) {
2160 				phar->alias = NULL;
2161 				phar->alias_len = 0;
2162 			} else {
2163 				phar->alias = estrndup(newpath, strlen(newpath));
2164 				phar->alias_len = strlen(newpath);
2165 				phar->is_temporary_alias = 1;
2166 				zend_hash_str_update_ptr(&(PHAR_G(phar_alias_map)), newpath, phar->fname_len, phar);
2167 			}
2168 		}
2169 
2170 	} else {
2171 
2172 		if (SUCCESS != phar_detect_phar_fname_ext(newpath, phar->fname_len, (const char **) &(phar->ext), &ext_len, 0, 1, 1)) {
2173 			efree(oldpath);
2174 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "data phar \"%s\" has invalid extension %s", phar->fname, ext);
2175 			return NULL;
2176 		}
2177 		phar->ext_len = ext_len;
2178 
2179 		phar->alias = NULL;
2180 		phar->alias_len = 0;
2181 	}
2182 
2183 	if ((!pphar || phar == pphar) && NULL == zend_hash_str_update_ptr(&(PHAR_G(phar_fname_map)), newpath, phar->fname_len, phar)) {
2184 		efree(oldpath);
2185 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unable to add newly converted phar \"%s\" to the list of phars", phar->fname);
2186 		return NULL;
2187 	}
2188 
2189 	phar_flush(phar, 0, 0, 1, &error);
2190 
2191 	if (error) {
2192 		zend_hash_str_del(&(PHAR_G(phar_fname_map)), newpath, phar->fname_len);
2193 		*sphar = NULL;
2194 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s", error);
2195 		efree(error);
2196 		efree(oldpath);
2197 		return NULL;
2198 	}
2199 
2200 	efree(oldpath);
2201 
2202 	if (phar->is_data) {
2203 		ce = phar_ce_data;
2204 	} else {
2205 		ce = phar_ce_archive;
2206 	}
2207 
2208 	ZVAL_NULL(&ret);
2209 	if (SUCCESS != object_init_ex(&ret, ce)) {
2210 		zval_ptr_dtor(&ret);
2211 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unable to instantiate phar object when converting archive \"%s\"", phar->fname);
2212 		return NULL;
2213 	}
2214 
2215 	ZVAL_STRINGL(&arg1, phar->fname, phar->fname_len);
2216 
2217 	zend_call_known_instance_method_with_1_params(ce->constructor, Z_OBJ(ret), NULL, &arg1);
2218 	zval_ptr_dtor(&arg1);
2219 	return Z_OBJ(ret);
2220 }
2221 /* }}} */
2222 
phar_convert_to_other(phar_archive_data * source,int convert,char * ext,uint32_t flags)2223 static zend_object *phar_convert_to_other(phar_archive_data *source, int convert, char *ext, uint32_t flags) /* {{{ */
2224 {
2225 	phar_archive_data *phar;
2226 	phar_entry_info *entry, newentry;
2227 	zend_object *ret;
2228 
2229 	/* invalidate phar cache */
2230 	PHAR_G(last_phar) = NULL;
2231 	PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
2232 
2233 	phar = (phar_archive_data *) ecalloc(1, sizeof(phar_archive_data));
2234 	/* set whole-archive compression and type from parameter */
2235 	phar->flags = flags;
2236 	phar->is_data = source->is_data;
2237 
2238 	switch (convert) {
2239 		case PHAR_FORMAT_TAR:
2240 			phar->is_tar = 1;
2241 			break;
2242 		case PHAR_FORMAT_ZIP:
2243 			phar->is_zip = 1;
2244 			break;
2245 		default:
2246 			phar->is_data = 0;
2247 			break;
2248 	}
2249 
2250 	zend_hash_init(&(phar->manifest), sizeof(phar_entry_info),
2251 		zend_get_hash_value, destroy_phar_manifest_entry, 0);
2252 	zend_hash_init(&phar->mounted_dirs, sizeof(char *),
2253 		zend_get_hash_value, NULL, 0);
2254 	zend_hash_init(&phar->virtual_dirs, sizeof(char *),
2255 		zend_get_hash_value, NULL, 0);
2256 
2257 	phar->fp = php_stream_fopen_tmpfile();
2258 	if (phar->fp == NULL) {
2259 		zend_throw_exception_ex(phar_ce_PharException, 0, "unable to create temporary file");
2260 		return NULL;
2261 	}
2262 	phar->fname = source->fname;
2263 	phar->fname_len = source->fname_len;
2264 	phar->is_temporary_alias = source->is_temporary_alias;
2265 	phar->alias = source->alias;
2266 
2267 	phar_metadata_tracker_copy(&phar->metadata_tracker, &source->metadata_tracker, phar->is_persistent);
2268 
2269 	/* first copy each file's uncompressed contents to a temporary file and set per-file flags */
2270 	ZEND_HASH_MAP_FOREACH_PTR(&source->manifest, entry) {
2271 
2272 		newentry = *entry;
2273 
2274 		if (newentry.link) {
2275 			newentry.link = estrdup(newentry.link);
2276 			goto no_copy;
2277 		}
2278 
2279 		if (newentry.tmp) {
2280 			newentry.tmp = estrdup(newentry.tmp);
2281 			goto no_copy;
2282 		}
2283 
2284 		if (FAILURE == phar_copy_file_contents(&newentry, phar->fp)) {
2285 			zend_hash_destroy(&(phar->manifest));
2286 			php_stream_close(phar->fp);
2287 			efree(phar);
2288 			/* exception already thrown */
2289 			return NULL;
2290 		}
2291 no_copy:
2292 		newentry.filename = estrndup(newentry.filename, newentry.filename_len);
2293 
2294 		phar_metadata_tracker_clone(&newentry.metadata_tracker);
2295 
2296 		newentry.is_zip = phar->is_zip;
2297 		newentry.is_tar = phar->is_tar;
2298 
2299 		if (newentry.is_tar) {
2300 			newentry.tar_type = (entry->is_dir ? TAR_DIR : TAR_FILE);
2301 		}
2302 
2303 		/* The header offset is only used for unmodified zips.
2304 		 * Once modified, phar_zip_changed_apply_int() will update the header_offset. */
2305 		newentry.header_offset = 0;
2306 		newentry.is_modified = 1;
2307 		newentry.phar = phar;
2308 		newentry.old_flags = newentry.flags & ~PHAR_ENT_COMPRESSION_MASK; /* remove compression from old_flags */
2309 		phar_set_inode(&newentry);
2310 		zend_hash_str_add_mem(&(phar->manifest), newentry.filename, newentry.filename_len, (void*)&newentry, sizeof(phar_entry_info));
2311 		phar_add_virtual_dirs(phar, newentry.filename, newentry.filename_len);
2312 	} ZEND_HASH_FOREACH_END();
2313 
2314 	if ((ret = phar_rename_archive(&phar, ext))) {
2315 		return ret;
2316 	} else {
2317 		if(phar != NULL) {
2318 			zend_hash_destroy(&(phar->manifest));
2319 			zend_hash_destroy(&(phar->mounted_dirs));
2320 			zend_hash_destroy(&(phar->virtual_dirs));
2321 			if (phar->fp) {
2322 				php_stream_close(phar->fp);
2323 			}
2324 			efree(phar->fname);
2325 			efree(phar);
2326 		}
2327 		return NULL;
2328 	}
2329 }
2330 /* }}} */
2331 
2332 /* {{{ Convert a phar.tar or phar.zip archive to the phar file format. The
2333  * optional parameter allows the user to determine the new
2334  * filename extension (default is phar).
2335  */
PHP_METHOD(Phar,convertToExecutable)2336 PHP_METHOD(Phar, convertToExecutable)
2337 {
2338 	char *ext = NULL;
2339 	int is_data;
2340 	size_t ext_len = 0;
2341 	uint32_t flags;
2342 	zend_object *ret;
2343 	zend_long format, method;
2344 	bool format_is_null = 1, method_is_null = 1;
2345 
2346 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l!l!s!", &format, &format_is_null, &method, &method_is_null, &ext, &ext_len) == FAILURE) {
2347 		RETURN_THROWS();
2348 	}
2349 
2350 	PHAR_ARCHIVE_OBJECT();
2351 
2352 	if (PHAR_G(readonly)) {
2353 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2354 			"Cannot write out executable phar archive, phar is read-only");
2355 		RETURN_THROWS();
2356 	}
2357 
2358 	if (format_is_null) {
2359 		format = PHAR_FORMAT_SAME;
2360 	}
2361 	switch (format) {
2362 		case 9021976: /* Retained for BC */
2363 		case PHAR_FORMAT_SAME:
2364 			/* by default, use the existing format */
2365 			if (phar_obj->archive->is_tar) {
2366 				format = PHAR_FORMAT_TAR;
2367 			} else if (phar_obj->archive->is_zip) {
2368 				format = PHAR_FORMAT_ZIP;
2369 			} else {
2370 				format = PHAR_FORMAT_PHAR;
2371 			}
2372 			break;
2373 		case PHAR_FORMAT_PHAR:
2374 		case PHAR_FORMAT_TAR:
2375 		case PHAR_FORMAT_ZIP:
2376 			break;
2377 		default:
2378 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2379 				"Unknown file format specified, please pass one of Phar::PHAR, Phar::TAR or Phar::ZIP");
2380 			RETURN_THROWS();
2381 	}
2382 
2383 	if (method_is_null) {
2384 		flags = phar_obj->archive->flags & PHAR_FILE_COMPRESSION_MASK;
2385 	} else {
2386 		switch (method) {
2387 		case 9021976: /* Retained for BC */
2388 			flags = phar_obj->archive->flags & PHAR_FILE_COMPRESSION_MASK;
2389 			break;
2390 		case 0:
2391 			flags = PHAR_FILE_COMPRESSED_NONE;
2392 			break;
2393 		case PHAR_ENT_COMPRESSED_GZ:
2394 			if (format == PHAR_FORMAT_ZIP) {
2395 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2396 					"Cannot compress entire archive with gzip, zip archives do not support whole-archive compression");
2397 				RETURN_THROWS();
2398 			}
2399 
2400 			if (!PHAR_G(has_zlib)) {
2401 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2402 					"Cannot compress entire archive with gzip, enable ext/zlib in php.ini");
2403 				RETURN_THROWS();
2404 			}
2405 
2406 			flags = PHAR_FILE_COMPRESSED_GZ;
2407 			break;
2408 		case PHAR_ENT_COMPRESSED_BZ2:
2409 			if (format == PHAR_FORMAT_ZIP) {
2410 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2411 					"Cannot compress entire archive with bz2, zip archives do not support whole-archive compression");
2412 				RETURN_THROWS();
2413 			}
2414 
2415 			if (!PHAR_G(has_bz2)) {
2416 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2417 					"Cannot compress entire archive with bz2, enable ext/bz2 in php.ini");
2418 				RETURN_THROWS();
2419 			}
2420 
2421 			flags = PHAR_FILE_COMPRESSED_BZ2;
2422 			break;
2423 		default:
2424 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2425 				"Unknown compression specified, please pass one of Phar::GZ or Phar::BZ2");
2426 			RETURN_THROWS();
2427 		}
2428 	}
2429 
2430 	is_data = phar_obj->archive->is_data;
2431 	phar_obj->archive->is_data = 0;
2432 	ret = phar_convert_to_other(phar_obj->archive, format, ext, flags);
2433 	phar_obj->archive->is_data = is_data;
2434 
2435 	if (ret) {
2436 		RETURN_OBJ(ret);
2437 	} else {
2438 		RETURN_NULL();
2439 	}
2440 }
2441 /* }}} */
2442 
2443 /* {{{ Convert an archive to a non-executable .tar or .zip.
2444  * The optional parameter allows the user to determine the new
2445  * filename extension (default is .zip or .tar).
2446  */
PHP_METHOD(Phar,convertToData)2447 PHP_METHOD(Phar, convertToData)
2448 {
2449 	char *ext = NULL;
2450 	int is_data;
2451 	size_t ext_len = 0;
2452 	uint32_t flags;
2453 	zend_object *ret;
2454 	zend_long format, method;
2455 	bool format_is_null = 1, method_is_null = 1;
2456 
2457 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l!l!s!", &format, &format_is_null, &method, &method_is_null, &ext, &ext_len) == FAILURE) {
2458 		RETURN_THROWS();
2459 	}
2460 
2461 	PHAR_ARCHIVE_OBJECT();
2462 
2463 	if (format_is_null) {
2464 		format = PHAR_FORMAT_SAME;
2465 	}
2466 	switch (format) {
2467 		case 9021976: /* Retained for BC */
2468 		case PHAR_FORMAT_SAME:
2469 			/* by default, use the existing format */
2470 			if (phar_obj->archive->is_tar) {
2471 				format = PHAR_FORMAT_TAR;
2472 			} else if (phar_obj->archive->is_zip) {
2473 				format = PHAR_FORMAT_ZIP;
2474 			} else {
2475 				zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2476 					"Cannot write out data phar archive, use Phar::TAR or Phar::ZIP");
2477 				RETURN_THROWS();
2478 			}
2479 			break;
2480 		case PHAR_FORMAT_PHAR:
2481 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2482 				"Cannot write out data phar archive, use Phar::TAR or Phar::ZIP");
2483 			RETURN_THROWS();
2484 		case PHAR_FORMAT_TAR:
2485 		case PHAR_FORMAT_ZIP:
2486 			break;
2487 		default:
2488 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2489 				"Unknown file format specified, please pass one of Phar::TAR or Phar::ZIP");
2490 			RETURN_THROWS();
2491 	}
2492 
2493 	if (method_is_null) {
2494 		flags = phar_obj->archive->flags & PHAR_FILE_COMPRESSION_MASK;
2495 	} else  {
2496 		switch (method) {
2497 		case 9021976: /* Retained for BC */
2498 			flags = phar_obj->archive->flags & PHAR_FILE_COMPRESSION_MASK;
2499 			break;
2500 		case 0:
2501 			flags = PHAR_FILE_COMPRESSED_NONE;
2502 			break;
2503 		case PHAR_ENT_COMPRESSED_GZ:
2504 			if (format == PHAR_FORMAT_ZIP) {
2505 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2506 					"Cannot compress entire archive with gzip, zip archives do not support whole-archive compression");
2507 				RETURN_THROWS();
2508 			}
2509 
2510 			if (!PHAR_G(has_zlib)) {
2511 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2512 					"Cannot compress entire archive with gzip, enable ext/zlib in php.ini");
2513 				RETURN_THROWS();
2514 			}
2515 
2516 			flags = PHAR_FILE_COMPRESSED_GZ;
2517 			break;
2518 		case PHAR_ENT_COMPRESSED_BZ2:
2519 			if (format == PHAR_FORMAT_ZIP) {
2520 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2521 					"Cannot compress entire archive with bz2, zip archives do not support whole-archive compression");
2522 				RETURN_THROWS();
2523 			}
2524 
2525 			if (!PHAR_G(has_bz2)) {
2526 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2527 					"Cannot compress entire archive with bz2, enable ext/bz2 in php.ini");
2528 				RETURN_THROWS();
2529 			}
2530 
2531 			flags = PHAR_FILE_COMPRESSED_BZ2;
2532 			break;
2533 		default:
2534 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2535 				"Unknown compression specified, please pass one of Phar::GZ or Phar::BZ2");
2536 			RETURN_THROWS();
2537 		}
2538 	}
2539 
2540 	is_data = phar_obj->archive->is_data;
2541 	phar_obj->archive->is_data = 1;
2542 	ret = phar_convert_to_other(phar_obj->archive, (int)format, ext, flags);
2543 	phar_obj->archive->is_data = is_data;
2544 
2545 	if (ret) {
2546 		RETURN_OBJ(ret);
2547 	} else {
2548 		RETURN_NULL();
2549 	}
2550 }
2551 /* }}} */
2552 
2553 /* {{{ Returns Phar::GZ or PHAR::BZ2 if the entire archive is compressed
2554  * (.tar.gz/tar.bz2 and so on), or FALSE otherwise.
2555  */
PHP_METHOD(Phar,isCompressed)2556 PHP_METHOD(Phar, isCompressed)
2557 {
2558 	if (zend_parse_parameters_none() == FAILURE) {
2559 		RETURN_THROWS();
2560 	}
2561 
2562 	PHAR_ARCHIVE_OBJECT();
2563 
2564 	if (phar_obj->archive->flags & PHAR_FILE_COMPRESSED_GZ) {
2565 		RETURN_LONG(PHAR_ENT_COMPRESSED_GZ);
2566 	}
2567 
2568 	if (phar_obj->archive->flags & PHAR_FILE_COMPRESSED_BZ2) {
2569 		RETURN_LONG(PHAR_ENT_COMPRESSED_BZ2);
2570 	}
2571 
2572 	RETURN_FALSE;
2573 }
2574 /* }}} */
2575 
2576 /* {{{ Returns true if phar.readonly=0 or phar is a PharData AND the actual file is writable. */
PHP_METHOD(Phar,isWritable)2577 PHP_METHOD(Phar, isWritable)
2578 {
2579 	php_stream_statbuf ssb;
2580 
2581 	if (zend_parse_parameters_none() == FAILURE) {
2582 		RETURN_THROWS();
2583 	}
2584 
2585 	PHAR_ARCHIVE_OBJECT();
2586 
2587 	if (!phar_obj->archive->is_writeable) {
2588 		RETURN_FALSE;
2589 	}
2590 
2591 	if (SUCCESS != php_stream_stat_path(phar_obj->archive->fname, &ssb)) {
2592 		if (phar_obj->archive->is_brandnew) {
2593 			/* assume it works if the file doesn't exist yet */
2594 			RETURN_TRUE;
2595 		}
2596 		RETURN_FALSE;
2597 	}
2598 
2599 	RETURN_BOOL((ssb.sb.st_mode & (S_IWOTH | S_IWGRP | S_IWUSR)) != 0);
2600 }
2601 /* }}} */
2602 
2603 /* {{{ Deletes a named file within the archive. */
PHP_METHOD(Phar,delete)2604 PHP_METHOD(Phar, delete)
2605 {
2606 	char *fname;
2607 	size_t fname_len;
2608 	char *error;
2609 	phar_entry_info *entry;
2610 
2611 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &fname, &fname_len) == FAILURE) {
2612 		RETURN_THROWS();
2613 	}
2614 
2615 	PHAR_ARCHIVE_OBJECT();
2616 
2617 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
2618 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2619 			"Cannot write out phar archive, phar is read-only");
2620 		RETURN_THROWS();
2621 	}
2622 
2623 	if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
2624 		zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
2625 		RETURN_THROWS();
2626 	}
2627 	if (NULL != (entry = zend_hash_str_find_ptr(&phar_obj->archive->manifest, fname, (uint32_t) fname_len))) {
2628 		if (entry->is_deleted) {
2629 			/* entry is deleted, but has not been flushed to disk yet */
2630 			RETURN_TRUE;
2631 		} else {
2632 			entry->is_deleted = 1;
2633 			entry->is_modified = 1;
2634 			phar_obj->archive->is_modified = 1;
2635 		}
2636 	} else {
2637 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s does not exist and cannot be deleted", fname);
2638 		RETURN_THROWS();
2639 	}
2640 
2641 	phar_flush(phar_obj->archive, NULL, 0, 0, &error);
2642 	if (error) {
2643 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
2644 		efree(error);
2645 		RETURN_THROWS();
2646 	}
2647 
2648 	RETURN_TRUE;
2649 }
2650 /* }}} */
2651 
2652 /* {{{ Returns the alias for the Phar or NULL. */
PHP_METHOD(Phar,getAlias)2653 PHP_METHOD(Phar, getAlias)
2654 {
2655 	if (zend_parse_parameters_none() == FAILURE) {
2656 		RETURN_THROWS();
2657 	}
2658 
2659 	PHAR_ARCHIVE_OBJECT();
2660 
2661 	if (phar_obj->archive->alias && phar_obj->archive->alias != phar_obj->archive->fname) {
2662 		RETURN_STRINGL(phar_obj->archive->alias, phar_obj->archive->alias_len);
2663 	}
2664 }
2665 /* }}} */
2666 
2667 /* {{{ Returns the real path to the phar archive on disk */
PHP_METHOD(Phar,getPath)2668 PHP_METHOD(Phar, getPath)
2669 {
2670 	if (zend_parse_parameters_none() == FAILURE) {
2671 		RETURN_THROWS();
2672 	}
2673 
2674 	PHAR_ARCHIVE_OBJECT();
2675 
2676 	RETURN_STRINGL(phar_obj->archive->fname, phar_obj->archive->fname_len);
2677 }
2678 /* }}} */
2679 
2680 /* {{{ Sets the alias for a Phar archive. The default value is the full path
2681  * to the archive.
2682  */
PHP_METHOD(Phar,setAlias)2683 PHP_METHOD(Phar, setAlias)
2684 {
2685 	char *alias, *error, *oldalias;
2686 	phar_archive_data *fd_ptr;
2687 	size_t alias_len, oldalias_len;
2688 	int old_temp, readd = 0;
2689 
2690 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &alias, &alias_len) == FAILURE) {
2691 		RETURN_THROWS();
2692 	}
2693 
2694 	PHAR_ARCHIVE_OBJECT();
2695 
2696 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
2697 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2698 			"Cannot write out phar archive, phar is read-only");
2699 		RETURN_THROWS();
2700 	}
2701 
2702 	/* invalidate phar cache */
2703 	PHAR_G(last_phar) = NULL;
2704 	PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
2705 
2706 	if (phar_obj->archive->is_data) {
2707 		if (phar_obj->archive->is_tar) {
2708 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2709 				"A Phar alias cannot be set in a plain tar archive");
2710 		} else {
2711 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2712 				"A Phar alias cannot be set in a plain zip archive");
2713 		}
2714 		RETURN_THROWS();
2715 	}
2716 
2717 	if (alias_len == phar_obj->archive->alias_len && memcmp(phar_obj->archive->alias, alias, alias_len) == 0) {
2718 		RETURN_TRUE;
2719 	}
2720 	if (alias_len && NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len))) {
2721 		spprintf(&error, 0, "alias \"%s\" is already used for archive \"%s\" and cannot be used for other archives", alias, fd_ptr->fname);
2722 		if (SUCCESS == phar_free_alias(fd_ptr, alias, alias_len)) {
2723 			efree(error);
2724 			goto valid_alias;
2725 		}
2726 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
2727 		efree(error);
2728 		RETURN_THROWS();
2729 	}
2730 	if (!phar_validate_alias(alias, alias_len)) {
2731 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2732 			"Invalid alias \"%s\" specified for phar \"%s\"", alias, phar_obj->archive->fname);
2733 		RETURN_THROWS();
2734 	}
2735 valid_alias:
2736 	if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
2737 		zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
2738 		RETURN_THROWS();
2739 	}
2740 	if (phar_obj->archive->alias_len && NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), phar_obj->archive->alias, phar_obj->archive->alias_len))) {
2741 		zend_hash_str_del(&(PHAR_G(phar_alias_map)), phar_obj->archive->alias, phar_obj->archive->alias_len);
2742 		readd = 1;
2743 	}
2744 
2745 	oldalias = phar_obj->archive->alias;
2746 	oldalias_len = phar_obj->archive->alias_len;
2747 	old_temp = phar_obj->archive->is_temporary_alias;
2748 
2749 	if (alias_len) {
2750 		phar_obj->archive->alias = estrndup(alias, alias_len);
2751 	} else {
2752 		phar_obj->archive->alias = NULL;
2753 	}
2754 
2755 	phar_obj->archive->alias_len = alias_len;
2756 	phar_obj->archive->is_temporary_alias = 0;
2757 	phar_flush(phar_obj->archive, NULL, 0, 0, &error);
2758 
2759 	if (error) {
2760 		phar_obj->archive->alias = oldalias;
2761 		phar_obj->archive->alias_len = oldalias_len;
2762 		phar_obj->archive->is_temporary_alias = old_temp;
2763 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
2764 		if (readd) {
2765 			zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), oldalias, oldalias_len, phar_obj->archive);
2766 		}
2767 		efree(error);
2768 		RETURN_THROWS();
2769 	}
2770 
2771 	zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len, phar_obj->archive);
2772 
2773 	if (oldalias) {
2774 		efree(oldalias);
2775 	}
2776 
2777 	RETURN_TRUE;
2778 }
2779 /* }}} */
2780 
2781 /* {{{ Return version info of Phar archive */
PHP_METHOD(Phar,getVersion)2782 PHP_METHOD(Phar, getVersion)
2783 {
2784 	if (zend_parse_parameters_none() == FAILURE) {
2785 		RETURN_THROWS();
2786 	}
2787 
2788 	PHAR_ARCHIVE_OBJECT();
2789 
2790 	RETURN_STRING(phar_obj->archive->version);
2791 }
2792 /* }}} */
2793 
2794 /* {{{ Do not flush a writeable phar (save its contents) until explicitly requested */
PHP_METHOD(Phar,startBuffering)2795 PHP_METHOD(Phar, startBuffering)
2796 {
2797 	if (zend_parse_parameters_none() == FAILURE) {
2798 		RETURN_THROWS();
2799 	}
2800 
2801 	PHAR_ARCHIVE_OBJECT();
2802 
2803 	phar_obj->archive->donotflush = 1;
2804 }
2805 /* }}} */
2806 
2807 /* {{{ Returns whether write operations are flushing to disk immediately. */
PHP_METHOD(Phar,isBuffering)2808 PHP_METHOD(Phar, isBuffering)
2809 {
2810 	if (zend_parse_parameters_none() == FAILURE) {
2811 		RETURN_THROWS();
2812 	}
2813 
2814 	PHAR_ARCHIVE_OBJECT();
2815 
2816 	RETURN_BOOL(phar_obj->archive->donotflush);
2817 }
2818 /* }}} */
2819 
2820 /* {{{ Saves the contents of a modified archive to disk. */
PHP_METHOD(Phar,stopBuffering)2821 PHP_METHOD(Phar, stopBuffering)
2822 {
2823 	char *error;
2824 
2825 	if (zend_parse_parameters_none() == FAILURE) {
2826 		RETURN_THROWS();
2827 	}
2828 
2829 	PHAR_ARCHIVE_OBJECT();
2830 
2831 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
2832 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2833 			"Cannot write out phar archive, phar is read-only");
2834 		RETURN_THROWS();
2835 	}
2836 
2837 	phar_obj->archive->donotflush = 0;
2838 	phar_flush(phar_obj->archive, 0, 0, 0, &error);
2839 
2840 	if (error) {
2841 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
2842 		efree(error);
2843 	}
2844 }
2845 /* }}} */
2846 
2847 /* {{{ Change the stub in a phar, phar.tar or phar.zip archive to something other
2848  * than the default. The stub *must* end with a call to __HALT_COMPILER().
2849  */
PHP_METHOD(Phar,setStub)2850 PHP_METHOD(Phar, setStub)
2851 {
2852 	zval *zstub;
2853 	char *stub, *error;
2854 	size_t stub_len;
2855 	zend_long len = -1;
2856 	php_stream *stream;
2857 
2858 	PHAR_ARCHIVE_OBJECT();
2859 
2860 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
2861 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2862 			"Cannot change stub, phar is read-only");
2863 		RETURN_THROWS();
2864 	}
2865 
2866 	if (phar_obj->archive->is_data) {
2867 		if (phar_obj->archive->is_tar) {
2868 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2869 				"A Phar stub cannot be set in a plain tar archive");
2870 		} else {
2871 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2872 				"A Phar stub cannot be set in a plain zip archive");
2873 		}
2874 		RETURN_THROWS();
2875 	}
2876 
2877 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r|l", &zstub, &len) == SUCCESS) {
2878 		zend_string *method_name = get_active_function_or_method_name();
2879 		zend_error(E_DEPRECATED, "Calling %s(resource $stub, int $length) is deprecated", ZSTR_VAL(method_name));
2880 		zend_string_release(method_name);
2881 		if (UNEXPECTED(EG(exception))) {
2882 			RETURN_THROWS();
2883 		}
2884 
2885 		if ((php_stream_from_zval_no_verify(stream, zstub)) != NULL) {
2886 			if (len > 0) {
2887 				len = -len;
2888 			} else {
2889 				len = -1;
2890 			}
2891 			if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
2892 				zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
2893 				RETURN_THROWS();
2894 			}
2895 			phar_flush(phar_obj->archive, (char *) zstub, len, 0, &error);
2896 			if (error) {
2897 				zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
2898 				efree(error);
2899 			}
2900 			RETURN_TRUE;
2901 		} else {
2902 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2903 				"Cannot change stub, unable to read from input stream");
2904 		}
2905 	} else if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &stub, &stub_len) == SUCCESS) {
2906 		if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
2907 			zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
2908 			RETURN_THROWS();
2909 		}
2910 		phar_flush(phar_obj->archive, stub, stub_len, 0, &error);
2911 
2912 		if (error) {
2913 			zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
2914 			efree(error);
2915 			RETURN_THROWS();
2916 		}
2917 
2918 		RETURN_TRUE;
2919 	}
2920 
2921 	RETURN_THROWS();
2922 }
2923 /* }}} */
2924 
2925 /* {{{ In a pure phar archive, sets a stub that can be used to run the archive
2926  * regardless of whether the phar extension is available. The first parameter
2927  * is the CLI startup filename, which defaults to "index.php". The second
2928  * parameter is the web startup filename and also defaults to "index.php"
2929  * (falling back to CLI behaviour).
2930  * Both parameters are optional.
2931  * In a phar.zip or phar.tar archive, the default stub is used only to
2932  * identify the archive to the extension as a Phar object. This allows the
2933  * extension to treat phar.zip and phar.tar types as honorary phars. Since
2934  * files cannot be loaded via this kind of stub, no parameters are accepted
2935  * when the Phar object is zip- or tar-based.
2936  */
PHP_METHOD(Phar,setDefaultStub)2937 PHP_METHOD(Phar, setDefaultStub)
2938 {
2939 	char *index = NULL, *webindex = NULL, *error = NULL;
2940 	zend_string *stub = NULL;
2941 	size_t index_len = 0, webindex_len = 0;
2942 	int created_stub = 0;
2943 
2944 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!s!", &index, &index_len, &webindex, &webindex_len) == FAILURE) {
2945 		RETURN_THROWS();
2946 	}
2947 
2948 	PHAR_ARCHIVE_OBJECT();
2949 
2950 	if (phar_obj->archive->is_data) {
2951 		if (phar_obj->archive->is_tar) {
2952 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2953 				"A Phar stub cannot be set in a plain tar archive");
2954 		} else {
2955 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2956 				"A Phar stub cannot be set in a plain zip archive");
2957 		}
2958 		RETURN_THROWS();
2959 	}
2960 
2961 	if ((index || webindex) && (phar_obj->archive->is_tar || phar_obj->archive->is_zip)) {
2962 		zend_argument_value_error(index ? 1 : 2, "must be null for a tar- or zip-based phar stub, string given");
2963 		RETURN_THROWS();
2964 	}
2965 
2966 	if (PHAR_G(readonly)) {
2967 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2968 			"Cannot change stub: phar.readonly=1");
2969 		RETURN_THROWS();
2970 	}
2971 
2972 	if (!phar_obj->archive->is_tar && !phar_obj->archive->is_zip) {
2973 		stub = phar_create_default_stub(index, webindex, &error);
2974 
2975 		if (error) {
2976 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "%s", error);
2977 			efree(error);
2978 			if (stub) {
2979 				zend_string_free(stub);
2980 			}
2981 			RETURN_THROWS();
2982 		}
2983 
2984 		created_stub = 1;
2985 	}
2986 
2987 	if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
2988 		zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
2989 		RETURN_THROWS();
2990 	}
2991 	phar_flush(phar_obj->archive, stub ? ZSTR_VAL(stub) : 0, stub ? ZSTR_LEN(stub) : 0, 1, &error);
2992 
2993 	if (created_stub) {
2994 		zend_string_free(stub);
2995 	}
2996 
2997 	if (error) {
2998 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
2999 		efree(error);
3000 		RETURN_THROWS();
3001 	}
3002 
3003 	RETURN_TRUE;
3004 }
3005 /* }}} */
3006 
3007 /* {{{ Sets the signature algorithm for a phar and applies it. The signature
3008  * algorithm must be one of Phar::MD5, Phar::SHA1, Phar::SHA256,
3009  * Phar::SHA512, or Phar::OPENSSL. Note that zip- based phar archives
3010  * cannot support signatures.
3011  */
PHP_METHOD(Phar,setSignatureAlgorithm)3012 PHP_METHOD(Phar, setSignatureAlgorithm)
3013 {
3014 	zend_long algo;
3015 	char *error, *key = NULL;
3016 	size_t key_len = 0;
3017 
3018 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|s!", &algo, &key, &key_len) != SUCCESS) {
3019 		RETURN_THROWS();
3020 	}
3021 
3022 	PHAR_ARCHIVE_OBJECT();
3023 
3024 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
3025 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3026 			"Cannot set signature algorithm, phar is read-only");
3027 		RETURN_THROWS();
3028 	}
3029 
3030 	switch (algo) {
3031 		case PHAR_SIG_SHA256:
3032 		case PHAR_SIG_SHA512:
3033 		case PHAR_SIG_MD5:
3034 		case PHAR_SIG_SHA1:
3035 		case PHAR_SIG_OPENSSL:
3036 		case PHAR_SIG_OPENSSL_SHA256:
3037 		case PHAR_SIG_OPENSSL_SHA512:
3038 			if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
3039 				zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
3040 				RETURN_THROWS();
3041 			}
3042 			phar_obj->archive->sig_flags = (php_uint32)algo;
3043 			phar_obj->archive->is_modified = 1;
3044 			PHAR_G(openssl_privatekey) = key;
3045 			PHAR_G(openssl_privatekey_len) = key_len;
3046 
3047 			phar_flush(phar_obj->archive, 0, 0, 0, &error);
3048 			if (error) {
3049 				zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
3050 				efree(error);
3051 			}
3052 			break;
3053 		default:
3054 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3055 				"Unknown signature algorithm specified");
3056 	}
3057 }
3058 /* }}} */
3059 
3060 /* {{{ Returns a hash signature, or FALSE if the archive is unsigned. */
PHP_METHOD(Phar,getSignature)3061 PHP_METHOD(Phar, getSignature)
3062 {
3063 	if (zend_parse_parameters_none() == FAILURE) {
3064 		RETURN_THROWS();
3065 	}
3066 
3067 	PHAR_ARCHIVE_OBJECT();
3068 
3069 	if (phar_obj->archive->signature) {
3070 		zend_string *unknown;
3071 
3072 		array_init(return_value);
3073 		add_assoc_stringl(return_value, "hash", phar_obj->archive->signature, phar_obj->archive->sig_len);
3074 		switch(phar_obj->archive->sig_flags) {
3075 			case PHAR_SIG_MD5:
3076 				add_assoc_string(return_value, "hash_type", "MD5");
3077 				break;
3078 			case PHAR_SIG_SHA1:
3079 				add_assoc_string(return_value, "hash_type", "SHA-1");
3080 				break;
3081 			case PHAR_SIG_SHA256:
3082 				add_assoc_string(return_value, "hash_type", "SHA-256");
3083 				break;
3084 			case PHAR_SIG_SHA512:
3085 				add_assoc_string(return_value, "hash_type", "SHA-512");
3086 				break;
3087 			case PHAR_SIG_OPENSSL:
3088 				add_assoc_string(return_value, "hash_type", "OpenSSL");
3089 				break;
3090 			case PHAR_SIG_OPENSSL_SHA256:
3091 				add_assoc_string(return_value, "hash_type", "OpenSSL_SHA256");
3092 				break;
3093 			case PHAR_SIG_OPENSSL_SHA512:
3094 				add_assoc_string(return_value, "hash_type", "OpenSSL_SHA512");
3095 				break;
3096 			default:
3097 				unknown = strpprintf(0, "Unknown (%u)", phar_obj->archive->sig_flags);
3098 				add_assoc_str(return_value, "hash_type", unknown);
3099 				break;
3100 		}
3101 	} else {
3102 		RETURN_FALSE;
3103 	}
3104 }
3105 /* }}} */
3106 
3107 /* {{{ Return whether phar was modified */
PHP_METHOD(Phar,getModified)3108 PHP_METHOD(Phar, getModified)
3109 {
3110 	if (zend_parse_parameters_none() == FAILURE) {
3111 		RETURN_THROWS();
3112 	}
3113 
3114 	PHAR_ARCHIVE_OBJECT();
3115 
3116 	RETURN_BOOL(phar_obj->archive->is_modified);
3117 }
3118 /* }}} */
3119 
phar_set_compression(zval * zv,void * argument)3120 static int phar_set_compression(zval *zv, void *argument) /* {{{ */
3121 {
3122 	phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv);
3123 	uint32_t compress = *(uint32_t *)argument;
3124 
3125 	if (entry->is_deleted) {
3126 		return ZEND_HASH_APPLY_KEEP;
3127 	}
3128 
3129 	entry->old_flags = entry->flags;
3130 	entry->flags &= ~PHAR_ENT_COMPRESSION_MASK;
3131 	entry->flags |= compress;
3132 	entry->is_modified = 1;
3133 	return ZEND_HASH_APPLY_KEEP;
3134 }
3135 /* }}} */
3136 
phar_test_compression(zval * zv,void * argument)3137 static int phar_test_compression(zval *zv, void *argument) /* {{{ */
3138 {
3139 	phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv);
3140 
3141 	if (entry->is_deleted) {
3142 		return ZEND_HASH_APPLY_KEEP;
3143 	}
3144 
3145 	if (!PHAR_G(has_bz2)) {
3146 		if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
3147 			*(int *) argument = 0;
3148 		}
3149 	}
3150 
3151 	if (!PHAR_G(has_zlib)) {
3152 		if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
3153 			*(int *) argument = 0;
3154 		}
3155 	}
3156 
3157 	return ZEND_HASH_APPLY_KEEP;
3158 }
3159 /* }}} */
3160 
pharobj_set_compression(HashTable * manifest,uint32_t compress)3161 static void pharobj_set_compression(HashTable *manifest, uint32_t compress) /* {{{ */
3162 {
3163 	zend_hash_apply_with_argument(manifest, phar_set_compression, &compress);
3164 }
3165 /* }}} */
3166 
pharobj_cancompress(HashTable * manifest)3167 static int pharobj_cancompress(HashTable *manifest) /* {{{ */
3168 {
3169 	int test;
3170 
3171 	test = 1;
3172 	zend_hash_apply_with_argument(manifest, phar_test_compression, &test);
3173 	return test;
3174 }
3175 /* }}} */
3176 
3177 /* {{{ Compress a .tar, or .phar.tar with whole-file compression
3178  * The parameter can be one of Phar::GZ or Phar::BZ2 to specify
3179  * the kind of compression desired
3180  */
PHP_METHOD(Phar,compress)3181 PHP_METHOD(Phar, compress)
3182 {
3183 	zend_long method;
3184 	char *ext = NULL;
3185 	size_t ext_len = 0;
3186 	uint32_t flags;
3187 	zend_object *ret;
3188 
3189 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|s!", &method, &ext, &ext_len) == FAILURE) {
3190 		RETURN_THROWS();
3191 	}
3192 
3193 	PHAR_ARCHIVE_OBJECT();
3194 
3195 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
3196 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3197 			"Cannot compress phar archive, phar is read-only");
3198 		RETURN_THROWS();
3199 	}
3200 
3201 	if (phar_obj->archive->is_zip) {
3202 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3203 			"Cannot compress zip-based archives with whole-archive compression");
3204 		RETURN_THROWS();
3205 	}
3206 
3207 	switch (method) {
3208 		case 0:
3209 			flags = PHAR_FILE_COMPRESSED_NONE;
3210 			break;
3211 		case PHAR_ENT_COMPRESSED_GZ:
3212 			if (!PHAR_G(has_zlib)) {
3213 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3214 					"Cannot compress entire archive with gzip, enable ext/zlib in php.ini");
3215 				RETURN_THROWS();
3216 			}
3217 			flags = PHAR_FILE_COMPRESSED_GZ;
3218 			break;
3219 
3220 		case PHAR_ENT_COMPRESSED_BZ2:
3221 			if (!PHAR_G(has_bz2)) {
3222 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3223 					"Cannot compress entire archive with bz2, enable ext/bz2 in php.ini");
3224 				return;
3225 			}
3226 			flags = PHAR_FILE_COMPRESSED_BZ2;
3227 			break;
3228 		default:
3229 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3230 				"Unknown compression specified, please pass one of Phar::GZ or Phar::BZ2");
3231 			RETURN_THROWS();
3232 	}
3233 
3234 	if (phar_obj->archive->is_tar) {
3235 		ret = phar_convert_to_other(phar_obj->archive, PHAR_FORMAT_TAR, ext, flags);
3236 	} else {
3237 		ret = phar_convert_to_other(phar_obj->archive, PHAR_FORMAT_PHAR, ext, flags);
3238 	}
3239 
3240 	if (ret) {
3241 		RETURN_OBJ(ret);
3242 	} else {
3243 		RETURN_NULL();
3244 	}
3245 }
3246 /* }}} */
3247 
3248 /* {{{ Decompress a .tar, or .phar.tar with whole-file compression */
PHP_METHOD(Phar,decompress)3249 PHP_METHOD(Phar, decompress)
3250 {
3251 	char *ext = NULL;
3252 	size_t ext_len = 0;
3253 	zend_object *ret;
3254 
3255 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &ext, &ext_len) == FAILURE) {
3256 		RETURN_THROWS();
3257 	}
3258 
3259 	PHAR_ARCHIVE_OBJECT();
3260 
3261 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
3262 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3263 			"Cannot decompress phar archive, phar is read-only");
3264 		RETURN_THROWS();
3265 	}
3266 
3267 	if (phar_obj->archive->is_zip) {
3268 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3269 			"Cannot decompress zip-based archives with whole-archive compression");
3270 		RETURN_THROWS();
3271 	}
3272 
3273 	if (phar_obj->archive->is_tar) {
3274 		ret = phar_convert_to_other(phar_obj->archive, PHAR_FORMAT_TAR, ext, PHAR_FILE_COMPRESSED_NONE);
3275 	} else {
3276 		ret = phar_convert_to_other(phar_obj->archive, PHAR_FORMAT_PHAR, ext, PHAR_FILE_COMPRESSED_NONE);
3277 	}
3278 
3279 	if (ret) {
3280 		RETURN_OBJ(ret);
3281 	} else {
3282 		RETURN_NULL();
3283 	}
3284 }
3285 /* }}} */
3286 
3287 /* {{{ Compress all files within a phar or zip archive using the specified compression
3288  * The parameter can be one of Phar::GZ or Phar::BZ2 to specify
3289  * the kind of compression desired
3290  */
PHP_METHOD(Phar,compressFiles)3291 PHP_METHOD(Phar, compressFiles)
3292 {
3293 	char *error;
3294 	uint32_t flags;
3295 	zend_long method;
3296 
3297 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &method) == FAILURE) {
3298 		RETURN_THROWS();
3299 	}
3300 
3301 	PHAR_ARCHIVE_OBJECT();
3302 
3303 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
3304 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3305 			"Phar is readonly, cannot change compression");
3306 		RETURN_THROWS();
3307 	}
3308 
3309 	switch (method) {
3310 		case PHAR_ENT_COMPRESSED_GZ:
3311 			if (!PHAR_G(has_zlib)) {
3312 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3313 					"Cannot compress files within archive with gzip, enable ext/zlib in php.ini");
3314 				RETURN_THROWS();
3315 			}
3316 			flags = PHAR_ENT_COMPRESSED_GZ;
3317 			break;
3318 
3319 		case PHAR_ENT_COMPRESSED_BZ2:
3320 			if (!PHAR_G(has_bz2)) {
3321 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3322 					"Cannot compress files within archive with bz2, enable ext/bz2 in php.ini");
3323 				RETURN_THROWS();
3324 			}
3325 			flags = PHAR_ENT_COMPRESSED_BZ2;
3326 			break;
3327 		default:
3328 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3329 				"Unknown compression specified, please pass one of Phar::GZ or Phar::BZ2");
3330 			RETURN_THROWS();
3331 	}
3332 
3333 	if (phar_obj->archive->is_tar) {
3334 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3335 			"Cannot compress with Gzip compression, tar archives cannot compress individual files, use compress() to compress the whole archive");
3336 		RETURN_THROWS();
3337 	}
3338 
3339 	if (!pharobj_cancompress(&phar_obj->archive->manifest)) {
3340 		if (flags == PHAR_ENT_COMPRESSED_GZ) {
3341 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3342 				"Cannot compress all files as Gzip, some are compressed as bzip2 and cannot be decompressed");
3343 		} else {
3344 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3345 				"Cannot compress all files as Bzip2, some are compressed as gzip and cannot be decompressed");
3346 		}
3347 		RETURN_THROWS();
3348 	}
3349 
3350 	if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
3351 		zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
3352 		RETURN_THROWS();
3353 	}
3354 	pharobj_set_compression(&phar_obj->archive->manifest, flags);
3355 	phar_obj->archive->is_modified = 1;
3356 	phar_flush(phar_obj->archive, 0, 0, 0, &error);
3357 
3358 	if (error) {
3359 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s", error);
3360 		efree(error);
3361 	}
3362 }
3363 /* }}} */
3364 
3365 /* {{{ decompress every file */
PHP_METHOD(Phar,decompressFiles)3366 PHP_METHOD(Phar, decompressFiles)
3367 {
3368 	char *error;
3369 
3370 	if (zend_parse_parameters_none() == FAILURE) {
3371 		RETURN_THROWS();
3372 
3373 	}
3374 
3375 	PHAR_ARCHIVE_OBJECT();
3376 
3377 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
3378 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3379 			"Phar is readonly, cannot change compression");
3380 		RETURN_THROWS();
3381 	}
3382 
3383 	if (!pharobj_cancompress(&phar_obj->archive->manifest)) {
3384 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3385 			"Cannot decompress all files, some are compressed as bzip2 or gzip and cannot be decompressed");
3386 		RETURN_THROWS();
3387 	}
3388 
3389 	if (phar_obj->archive->is_tar) {
3390 		RETURN_TRUE;
3391 	} else {
3392 		if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
3393 			zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
3394 			RETURN_THROWS();
3395 		}
3396 		pharobj_set_compression(&phar_obj->archive->manifest, PHAR_ENT_COMPRESSED_NONE);
3397 	}
3398 
3399 	phar_obj->archive->is_modified = 1;
3400 	phar_flush(phar_obj->archive, 0, 0, 0, &error);
3401 
3402 	if (error) {
3403 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s", error);
3404 		efree(error);
3405 	}
3406 
3407 	RETURN_TRUE;
3408 }
3409 /* }}} */
3410 
3411 /* {{{ copy a file internal to the phar archive to another new file within the phar */
PHP_METHOD(Phar,copy)3412 PHP_METHOD(Phar, copy)
3413 {
3414 	char *oldfile, *newfile, *error;
3415 	const char *pcr_error;
3416 	size_t oldfile_len, newfile_len;
3417 	phar_entry_info *oldentry, newentry = {0}, *temp;
3418 	size_t tmp_len = 0;
3419 
3420 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "pp", &oldfile, &oldfile_len, &newfile, &newfile_len) == FAILURE) {
3421 		RETURN_THROWS();
3422 	}
3423 
3424 	PHAR_ARCHIVE_OBJECT();
3425 
3426 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
3427 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3428 			"Cannot copy \"%s\" to \"%s\", phar is read-only", oldfile, newfile);
3429 		RETURN_THROWS();
3430 	}
3431 
3432 	if (oldfile_len >= sizeof(".phar")-1 && !memcmp(oldfile, ".phar", sizeof(".phar")-1)) {
3433 		/* can't copy a meta file */
3434 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3435 			"file \"%s\" cannot be copied to file \"%s\", cannot copy Phar meta-file in %s", oldfile, newfile, phar_obj->archive->fname);
3436 		RETURN_THROWS();
3437 	}
3438 
3439 	if (newfile_len >= sizeof(".phar")-1 && !memcmp(newfile, ".phar", sizeof(".phar")-1)) {
3440 		/* can't copy a meta file */
3441 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3442 			"file \"%s\" cannot be copied to file \"%s\", cannot copy to Phar meta-file in %s", oldfile, newfile, phar_obj->archive->fname);
3443 		RETURN_THROWS();
3444 	}
3445 
3446 	if (NULL == (oldentry = zend_hash_str_find_ptr(&phar_obj->archive->manifest, oldfile, (uint32_t) oldfile_len)) || oldentry->is_deleted) {
3447 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3448 			"file \"%s\" cannot be copied to file \"%s\", file does not exist in %s", oldfile, newfile, phar_obj->archive->fname);
3449 		RETURN_THROWS();
3450 	}
3451 
3452 	if (NULL != (temp = zend_hash_str_find_ptr(&phar_obj->archive->manifest, newfile, (uint32_t) newfile_len)) && !temp->is_deleted) {
3453 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3454 			"file \"%s\" cannot be copied to file \"%s\", file must not already exist in phar %s", oldfile, newfile, phar_obj->archive->fname);
3455 		RETURN_THROWS();
3456 	}
3457 
3458 	tmp_len = newfile_len;
3459 	if (phar_path_check(&newfile, &tmp_len, &pcr_error) > pcr_is_ok) {
3460 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3461 				"file \"%s\" contains invalid characters %s, cannot be copied from \"%s\" in phar %s", newfile, pcr_error, oldfile, phar_obj->archive->fname);
3462 		RETURN_THROWS();
3463 	}
3464 	newfile_len = tmp_len;
3465 
3466 	if (phar_obj->archive->is_persistent) {
3467 		if (FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
3468 			zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
3469 			RETURN_THROWS();
3470 		}
3471 		/* re-populate with copied-on-write entry */
3472 		oldentry = zend_hash_str_find_ptr(&phar_obj->archive->manifest, oldfile, (uint32_t) oldfile_len);
3473 	}
3474 
3475 	memcpy((void *) &newentry, oldentry, sizeof(phar_entry_info));
3476 
3477 	phar_metadata_tracker_clone(&newentry.metadata_tracker);
3478 
3479 	newentry.filename = estrndup(newfile, newfile_len);
3480 	newentry.filename_len = newfile_len;
3481 	newentry.fp_refcount = 0;
3482 
3483 	if (oldentry->fp_type != PHAR_FP) {
3484 		if (FAILURE == phar_copy_entry_fp(oldentry, &newentry, &error)) {
3485 			efree(newentry.filename);
3486 			php_stream_close(newentry.fp);
3487 			zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
3488 			efree(error);
3489 			RETURN_THROWS();
3490 		}
3491 	}
3492 
3493 	zend_hash_str_add_mem(&oldentry->phar->manifest, newfile, newfile_len, &newentry, sizeof(phar_entry_info));
3494 	phar_obj->archive->is_modified = 1;
3495 	phar_flush(phar_obj->archive, 0, 0, 0, &error);
3496 
3497 	if (error) {
3498 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
3499 		efree(error);
3500 	}
3501 
3502 	RETURN_TRUE;
3503 }
3504 /* }}} */
3505 
3506 /* {{{ determines whether a file exists in the phar */
PHP_METHOD(Phar,offsetExists)3507 PHP_METHOD(Phar, offsetExists)
3508 {
3509 	char *fname;
3510 	size_t fname_len;
3511 	phar_entry_info *entry;
3512 
3513 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &fname, &fname_len) == FAILURE) {
3514 		RETURN_THROWS();
3515 	}
3516 
3517 	PHAR_ARCHIVE_OBJECT();
3518 
3519 	if (zend_hash_str_exists(&phar_obj->archive->manifest, fname, (uint32_t) fname_len)) {
3520 		if (NULL != (entry = zend_hash_str_find_ptr(&phar_obj->archive->manifest, fname, (uint32_t) fname_len))) {
3521 			if (entry->is_deleted) {
3522 				/* entry is deleted, but has not been flushed to disk yet */
3523 				RETURN_FALSE;
3524 			}
3525 		}
3526 
3527 		if (fname_len >= sizeof(".phar")-1 && !memcmp(fname, ".phar", sizeof(".phar")-1)) {
3528 			/* none of these are real files, so they don't exist */
3529 			RETURN_FALSE;
3530 		}
3531 		RETURN_TRUE;
3532 	} else {
3533 		if (zend_hash_str_exists(&phar_obj->archive->virtual_dirs, fname, (uint32_t) fname_len)) {
3534 			RETURN_TRUE;
3535 		}
3536 		RETURN_FALSE;
3537 	}
3538 }
3539 /* }}} */
3540 
3541 /* {{{ get a PharFileInfo object for a specific file */
PHP_METHOD(Phar,offsetGet)3542 PHP_METHOD(Phar, offsetGet)
3543 {
3544 	char *fname, *error;
3545 	size_t fname_len;
3546 	zval zfname;
3547 	phar_entry_info *entry;
3548 	zend_string *sfname;
3549 
3550 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &fname, &fname_len) == FAILURE) {
3551 		RETURN_THROWS();
3552 	}
3553 
3554 	PHAR_ARCHIVE_OBJECT();
3555 
3556 	/* security is 0 here so that we can get a better error message than "entry doesn't exist" */
3557 	if (!(entry = phar_get_entry_info_dir(phar_obj->archive, fname, fname_len, 1, &error, 0))) {
3558 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s does not exist%s%s", fname, error?", ":"", error?error:"");
3559 	} else {
3560 		if (fname_len == sizeof(".phar/stub.php")-1 && !memcmp(fname, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
3561 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot get stub \".phar/stub.php\" directly in phar \"%s\", use getStub", phar_obj->archive->fname);
3562 			RETURN_THROWS();
3563 		}
3564 
3565 		if (fname_len == sizeof(".phar/alias.txt")-1 && !memcmp(fname, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
3566 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot get alias \".phar/alias.txt\" directly in phar \"%s\", use getAlias", phar_obj->archive->fname);
3567 			RETURN_THROWS();
3568 		}
3569 
3570 		if (fname_len >= sizeof(".phar")-1 && !memcmp(fname, ".phar", sizeof(".phar")-1)) {
3571 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot directly get any files or directories in magic \".phar\" directory");
3572 			RETURN_THROWS();
3573 		}
3574 
3575 		if (entry->is_temp_dir) {
3576 			efree(entry->filename);
3577 			efree(entry);
3578 		}
3579 
3580 		sfname = strpprintf(0, "phar://%s/%s", phar_obj->archive->fname, fname);
3581 		ZVAL_NEW_STR(&zfname, sfname);
3582 		spl_instantiate_arg_ex1(phar_obj->spl.info_class, return_value, &zfname);
3583 		zval_ptr_dtor(&zfname);
3584 	}
3585 }
3586 /* }}} */
3587 
3588 /* {{{ add a file within the phar archive from a string or resource */
phar_add_file(phar_archive_data ** pphar,char * filename,size_t filename_len,char * cont_str,size_t cont_len,zval * zresource)3589 static void phar_add_file(phar_archive_data **pphar, char *filename, size_t filename_len, char *cont_str, size_t cont_len, zval *zresource)
3590 {
3591 	size_t start_pos=0;
3592 	char *error;
3593 	size_t contents_len;
3594 	phar_entry_data *data;
3595 	php_stream *contents_file = NULL;
3596 	php_stream_statbuf ssb;
3597 #ifdef PHP_WIN32
3598 	char *save_filename;
3599 	ALLOCA_FLAG(filename_use_heap)
3600 #endif
3601 
3602 	if (filename_len >= sizeof(".phar")-1) {
3603 		start_pos = '/' == filename[0]; /* account for any leading slash: multiple-leads handled elsewhere */
3604 		if (!memcmp(&filename[start_pos], ".phar", sizeof(".phar")-1) && (filename[start_pos+5] == '/' || filename[start_pos+5] == '\\' || filename[start_pos+5] == '\0')) {
3605 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create any files in magic \".phar\" directory");
3606 			return;
3607 		}
3608 	}
3609 
3610 #ifdef PHP_WIN32
3611 	save_filename = filename;
3612 	if (memchr(filename, '\\', filename_len)) {
3613 		filename = do_alloca(filename_len + 1, filename_use_heap);
3614 		memcpy(filename, save_filename, filename_len);
3615 		filename[filename_len] = '\0';
3616 	}
3617 #endif
3618 
3619 	if (!(data = phar_get_or_create_entry_data((*pphar)->fname, (*pphar)->fname_len, filename, filename_len, "w+b", 0, &error, 1))) {
3620 		if (error) {
3621 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s does not exist and cannot be created: %s", filename, error);
3622 			efree(error);
3623 		} else {
3624 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s does not exist and cannot be created", filename);
3625 		}
3626 		goto finish;
3627 	} else {
3628 		if (error) {
3629 			efree(error);
3630 		}
3631 
3632 		if (!data->internal_file->is_dir) {
3633 			if (cont_str) {
3634 				contents_len = php_stream_write(data->fp, cont_str, cont_len);
3635 				if (contents_len != cont_len) {
3636 					zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s could not be written to", filename);
3637 					goto finish;
3638 				}
3639 			} else {
3640 				if (!(php_stream_from_zval_no_verify(contents_file, zresource))) {
3641 					zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s could not be written to", filename);
3642 					goto finish;
3643 				}
3644 				php_stream_copy_to_stream_ex(contents_file, data->fp, PHP_STREAM_COPY_ALL, &contents_len);
3645 			}
3646 			data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize = contents_len;
3647 		}
3648 
3649 		if (contents_file != NULL && php_stream_stat(contents_file, &ssb) != -1) {
3650 			data->internal_file->flags = ssb.sb.st_mode & PHAR_ENT_PERM_MASK ;
3651 		} else {
3652 #ifndef _WIN32
3653 			mode_t mask;
3654 			mask = umask(0);
3655 			umask(mask);
3656 			data->internal_file->flags &= ~mask;
3657 #endif
3658 		}
3659 
3660 		/* check for copy-on-write */
3661 		if (pphar[0] != data->phar) {
3662 			*pphar = data->phar;
3663 		}
3664 		phar_entry_delref(data);
3665 		phar_flush(*pphar, 0, 0, 0, &error);
3666 
3667 		if (error) {
3668 			zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
3669 			efree(error);
3670 		}
3671 	}
3672 
3673 finish: ;
3674 #ifdef PHP_WIN32
3675 	if (filename != save_filename) {
3676 		free_alloca(filename, filename_use_heap);
3677 		filename = save_filename;
3678 	}
3679 #endif
3680 }
3681 /* }}} */
3682 
3683 /* {{{ create a directory within the phar archive */
phar_mkdir(phar_archive_data ** pphar,char * dirname,size_t dirname_len)3684 static void phar_mkdir(phar_archive_data **pphar, char *dirname, size_t dirname_len)
3685 {
3686 	char *error;
3687 	phar_entry_data *data;
3688 
3689 	if (!(data = phar_get_or_create_entry_data((*pphar)->fname, (*pphar)->fname_len, dirname, dirname_len, "w+b", 2, &error, 1))) {
3690 		if (error) {
3691 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Directory %s does not exist and cannot be created: %s", dirname, error);
3692 			efree(error);
3693 		} else {
3694 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Directory %s does not exist and cannot be created", dirname);
3695 		}
3696 
3697 		return;
3698 	} else {
3699 		if (error) {
3700 			efree(error);
3701 		}
3702 
3703 		/* check for copy on write */
3704 		if (data->phar != *pphar) {
3705 			*pphar = data->phar;
3706 		}
3707 		phar_entry_delref(data);
3708 		phar_flush(*pphar, 0, 0, 0, &error);
3709 
3710 		if (error) {
3711 			zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
3712 			efree(error);
3713 		}
3714 	}
3715 }
3716 /* }}} */
3717 
3718 /* {{{ set the contents of an internal file to those of an external file */
PHP_METHOD(Phar,offsetSet)3719 PHP_METHOD(Phar, offsetSet)
3720 {
3721 	char *fname, *cont_str = NULL;
3722 	size_t fname_len, cont_len;
3723 	zval *zresource = NULL;
3724 
3725 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "pr", &fname, &fname_len, &zresource) == FAILURE
3726 	&& zend_parse_parameters(ZEND_NUM_ARGS(), "ps", &fname, &fname_len, &cont_str, &cont_len) == FAILURE) {
3727 		RETURN_THROWS();
3728 	}
3729 
3730 	PHAR_ARCHIVE_OBJECT();
3731 
3732 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
3733 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Write operations disabled by the php.ini setting phar.readonly");
3734 		RETURN_THROWS();
3735 	}
3736 
3737 	if (fname_len == sizeof(".phar/stub.php")-1 && !memcmp(fname, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
3738 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot set stub \".phar/stub.php\" directly in phar \"%s\", use setStub", phar_obj->archive->fname);
3739 		RETURN_THROWS();
3740 	}
3741 
3742 	if (fname_len == sizeof(".phar/alias.txt")-1 && !memcmp(fname, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
3743 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot set alias \".phar/alias.txt\" directly in phar \"%s\", use setAlias", phar_obj->archive->fname);
3744 		RETURN_THROWS();
3745 	}
3746 
3747 	if (fname_len >= sizeof(".phar")-1 && !memcmp(fname, ".phar", sizeof(".phar")-1)) {
3748 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot set any files or directories in magic \".phar\" directory");
3749 		RETURN_THROWS();
3750 	}
3751 
3752 	phar_add_file(&(phar_obj->archive), fname, fname_len, cont_str, cont_len, zresource);
3753 }
3754 /* }}} */
3755 
3756 /* {{{ remove a file from a phar */
PHP_METHOD(Phar,offsetUnset)3757 PHP_METHOD(Phar, offsetUnset)
3758 {
3759 	char *fname, *error;
3760 	size_t fname_len;
3761 	phar_entry_info *entry;
3762 
3763 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &fname, &fname_len) == FAILURE) {
3764 		RETURN_THROWS();
3765 	}
3766 
3767 	PHAR_ARCHIVE_OBJECT();
3768 
3769 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
3770 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Write operations disabled by the php.ini setting phar.readonly");
3771 		RETURN_THROWS();
3772 	}
3773 
3774 	if (zend_hash_str_exists(&phar_obj->archive->manifest, fname, (uint32_t) fname_len)) {
3775 		if (NULL != (entry = zend_hash_str_find_ptr(&phar_obj->archive->manifest, fname, (uint32_t) fname_len))) {
3776 			if (entry->is_deleted) {
3777 				/* entry is deleted, but has not been flushed to disk yet */
3778 				return;
3779 			}
3780 
3781 			if (phar_obj->archive->is_persistent) {
3782 				if (FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
3783 					zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
3784 					RETURN_THROWS();
3785 				}
3786 				/* re-populate entry after copy on write */
3787 				entry = zend_hash_str_find_ptr(&phar_obj->archive->manifest, fname, (uint32_t) fname_len);
3788 			}
3789 			entry->is_modified = 0;
3790 			entry->is_deleted = 1;
3791 			/* we need to "flush" the stream to save the newly deleted file on disk */
3792 			phar_flush(phar_obj->archive, 0, 0, 0, &error);
3793 
3794 			if (error) {
3795 				zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
3796 				efree(error);
3797 			}
3798 		}
3799 	}
3800 }
3801 /* }}} */
3802 
3803 /* {{{ Adds an empty directory to the phar archive */
PHP_METHOD(Phar,addEmptyDir)3804 PHP_METHOD(Phar, addEmptyDir)
3805 {
3806 	char *dirname;
3807 	size_t dirname_len;
3808 
3809 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &dirname, &dirname_len) == FAILURE) {
3810 		RETURN_THROWS();
3811 	}
3812 
3813 	PHAR_ARCHIVE_OBJECT();
3814 
3815 	if (dirname_len >= sizeof(".phar")-1 && !memcmp(dirname, ".phar", sizeof(".phar")-1)) {
3816 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create a directory in magic \".phar\" directory");
3817 		RETURN_THROWS();
3818 	}
3819 
3820 	phar_mkdir(&phar_obj->archive, dirname, dirname_len);
3821 }
3822 /* }}} */
3823 
3824 /* {{{ Adds a file to the archive using the filename, or the second parameter as the name within the archive */
PHP_METHOD(Phar,addFile)3825 PHP_METHOD(Phar, addFile)
3826 {
3827 	char *fname, *localname = NULL;
3828 	size_t fname_len, localname_len = 0;
3829 	php_stream *resource;
3830 	zval zresource;
3831 
3832 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|s!", &fname, &fname_len, &localname, &localname_len) == FAILURE) {
3833 		RETURN_THROWS();
3834 	}
3835 
3836 	PHAR_ARCHIVE_OBJECT();
3837 
3838 	if (!strstr(fname, "://") && php_check_open_basedir(fname)) {
3839 		zend_throw_exception_ex(spl_ce_RuntimeException, 0, "phar error: unable to open file \"%s\" to add to phar archive, open_basedir restrictions prevent this", fname);
3840 		RETURN_THROWS();
3841 	}
3842 
3843 	if (!(resource = php_stream_open_wrapper(fname, "rb", 0, NULL))) {
3844 		zend_throw_exception_ex(spl_ce_RuntimeException, 0, "phar error: unable to open file \"%s\" to add to phar archive", fname);
3845 		RETURN_THROWS();
3846 	}
3847 
3848 	if (localname) {
3849 		fname = localname;
3850 		fname_len = localname_len;
3851 	}
3852 
3853 	php_stream_to_zval(resource, &zresource);
3854 	phar_add_file(&(phar_obj->archive), fname, fname_len, NULL, 0, &zresource);
3855 	zval_ptr_dtor(&zresource);
3856 }
3857 /* }}} */
3858 
3859 /* {{{ Adds a file to the archive using its contents as a string */
PHP_METHOD(Phar,addFromString)3860 PHP_METHOD(Phar, addFromString)
3861 {
3862 	char *localname, *cont_str;
3863 	size_t localname_len, cont_len;
3864 
3865 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ps", &localname, &localname_len, &cont_str, &cont_len) == FAILURE) {
3866 		RETURN_THROWS();
3867 	}
3868 
3869 	PHAR_ARCHIVE_OBJECT();
3870 
3871 	phar_add_file(&(phar_obj->archive), localname, localname_len, cont_str, cont_len, NULL);
3872 }
3873 /* }}} */
3874 
3875 /* {{{ Returns the stub at the head of a phar archive as a string. */
PHP_METHOD(Phar,getStub)3876 PHP_METHOD(Phar, getStub)
3877 {
3878 	size_t len;
3879 	zend_string *buf;
3880 	php_stream *fp;
3881 	php_stream_filter *filter = NULL;
3882 	phar_entry_info *stub;
3883 
3884 	if (zend_parse_parameters_none() == FAILURE) {
3885 		RETURN_THROWS();
3886 	}
3887 
3888 	PHAR_ARCHIVE_OBJECT();
3889 
3890 	if (phar_obj->archive->is_tar || phar_obj->archive->is_zip) {
3891 
3892 		if (NULL != (stub = zend_hash_str_find_ptr(&(phar_obj->archive->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1))) {
3893 			if (phar_obj->archive->fp && !phar_obj->archive->is_brandnew && !(stub->flags & PHAR_ENT_COMPRESSION_MASK)) {
3894 				fp = phar_obj->archive->fp;
3895 			} else {
3896 				if (!(fp = php_stream_open_wrapper(phar_obj->archive->fname, "rb", 0, NULL))) {
3897 					zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "phar error: unable to open phar \"%s\"", phar_obj->archive->fname);
3898 					RETURN_THROWS();
3899 				}
3900 				if (stub->flags & PHAR_ENT_COMPRESSION_MASK) {
3901 					char *filter_name;
3902 
3903 					if ((filter_name = phar_decompress_filter(stub, 0)) != NULL) {
3904 						filter = php_stream_filter_create(filter_name, NULL, php_stream_is_persistent(fp));
3905 					} else {
3906 						filter = NULL;
3907 					}
3908 					if (!filter) {
3909 						zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "phar error: unable to read stub of phar \"%s\" (cannot create %s filter)", phar_obj->archive->fname, phar_decompress_filter(stub, 1));
3910 						RETURN_THROWS();
3911 					}
3912 					php_stream_filter_append(&fp->readfilters, filter);
3913 				}
3914 			}
3915 
3916 			if (!fp)  {
3917 				zend_throw_exception_ex(spl_ce_RuntimeException, 0,
3918 					"Unable to read stub");
3919 				RETURN_THROWS();
3920 			}
3921 
3922 			php_stream_seek(fp, stub->offset_abs, SEEK_SET);
3923 			len = stub->uncompressed_filesize;
3924 			goto carry_on;
3925 		} else {
3926 			RETURN_EMPTY_STRING();
3927 		}
3928 	}
3929 	len = phar_obj->archive->halt_offset;
3930 
3931 	if (phar_obj->archive->fp && !phar_obj->archive->is_brandnew) {
3932 		fp = phar_obj->archive->fp;
3933 	} else {
3934 		fp = php_stream_open_wrapper(phar_obj->archive->fname, "rb", 0, NULL);
3935 	}
3936 
3937 	if (!fp)  {
3938 		zend_throw_exception_ex(spl_ce_RuntimeException, 0,
3939 			"Unable to read stub");
3940 		RETURN_THROWS();
3941 	}
3942 
3943 	php_stream_rewind(fp);
3944 carry_on:
3945 	buf = zend_string_alloc(len, 0);
3946 
3947 	if (len != php_stream_read(fp, ZSTR_VAL(buf), len)) {
3948 		if (fp != phar_obj->archive->fp) {
3949 			php_stream_close(fp);
3950 		}
3951 		zend_throw_exception_ex(spl_ce_RuntimeException, 0,
3952 			"Unable to read stub");
3953 		zend_string_release_ex(buf, 0);
3954 		RETURN_THROWS();
3955 	}
3956 
3957 	if (filter) {
3958 		php_stream_filter_flush(filter, 1);
3959 		php_stream_filter_remove(filter, 1);
3960 	}
3961 
3962 	if (fp != phar_obj->archive->fp) {
3963 		php_stream_close(fp);
3964 	}
3965 
3966 	ZSTR_VAL(buf)[len] = '\0';
3967 	ZSTR_LEN(buf) = len;
3968 	RETVAL_STR(buf);
3969 }
3970 /* }}}*/
3971 
3972 /* {{{ Returns TRUE if the phar has global metadata, FALSE otherwise. */
PHP_METHOD(Phar,hasMetadata)3973 PHP_METHOD(Phar, hasMetadata)
3974 {
3975 	if (zend_parse_parameters_none() == FAILURE) {
3976 		RETURN_THROWS();
3977 	}
3978 
3979 	PHAR_ARCHIVE_OBJECT();
3980 
3981 	RETURN_BOOL(phar_metadata_tracker_has_data(&phar_obj->archive->metadata_tracker, phar_obj->archive->is_persistent));
3982 }
3983 /* }}} */
3984 
3985 /* {{{ Returns the global metadata of the phar */
PHP_METHOD(Phar,getMetadata)3986 PHP_METHOD(Phar, getMetadata)
3987 {
3988 	HashTable *unserialize_options = NULL;
3989 	phar_metadata_tracker *tracker;
3990 
3991 	ZEND_PARSE_PARAMETERS_START(0, 1)
3992 		Z_PARAM_OPTIONAL
3993 		Z_PARAM_ARRAY_HT(unserialize_options)
3994 	ZEND_PARSE_PARAMETERS_END();
3995 
3996 	PHAR_ARCHIVE_OBJECT();
3997 
3998 	tracker = &phar_obj->archive->metadata_tracker;
3999 	if (phar_metadata_tracker_has_data(tracker, phar_obj->archive->is_persistent)) {
4000 		phar_metadata_tracker_unserialize_or_copy(tracker, return_value, phar_obj->archive->is_persistent, unserialize_options, "Phar::getMetadata");
4001 	}
4002 }
4003 /* }}} */
4004 
4005 /* {{{ Modifies the phar metadata or throws */
serialize_metadata_or_throw(phar_metadata_tracker * tracker,int persistent,zval * metadata)4006 static int serialize_metadata_or_throw(phar_metadata_tracker *tracker, int persistent, zval *metadata)
4007 {
4008 	php_serialize_data_t metadata_hash;
4009 	smart_str main_metadata_str = {0};
4010 
4011 	PHP_VAR_SERIALIZE_INIT(metadata_hash);
4012 	php_var_serialize(&main_metadata_str, metadata, &metadata_hash);
4013 	PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
4014 	if (EG(exception)) {
4015 		/* Serialization can throw. Don't overwrite the original value or original string. */
4016 		return FAILURE;
4017 	}
4018 
4019 	phar_metadata_tracker_free(tracker, persistent);
4020 	if (EG(exception)) {
4021 		/* Destructor can throw. */
4022 		zend_string_release(main_metadata_str.s);
4023 		return FAILURE;
4024 	}
4025 
4026 	if (tracker->str) {
4027 		zend_throw_exception_ex(phar_ce_PharException, 0, "Metadata unexpectedly changed during setMetadata()");
4028 		zend_string_release(main_metadata_str.s);
4029 		return FAILURE;
4030 	}
4031 	ZVAL_COPY(&tracker->val, metadata);
4032 	tracker->str = main_metadata_str.s;
4033 	return SUCCESS;
4034 }
4035 
4036 /* {{{ Sets the global metadata of the phar */
PHP_METHOD(Phar,setMetadata)4037 PHP_METHOD(Phar, setMetadata)
4038 {
4039 	char *error;
4040 	zval *metadata;
4041 
4042 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &metadata) == FAILURE) {
4043 		RETURN_THROWS();
4044 	}
4045 
4046 	PHAR_ARCHIVE_OBJECT();
4047 
4048 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
4049 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Write operations disabled by the php.ini setting phar.readonly");
4050 		RETURN_THROWS();
4051 	}
4052 
4053 	if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
4054 		zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
4055 		RETURN_THROWS();
4056 	}
4057 
4058 	ZEND_ASSERT(!phar_obj->archive->is_persistent); /* Should no longer be persistent */
4059 	if (serialize_metadata_or_throw(&phar_obj->archive->metadata_tracker, phar_obj->archive->is_persistent, metadata) != SUCCESS) {
4060 		RETURN_THROWS();
4061 	}
4062 
4063 	phar_obj->archive->is_modified = 1;
4064 	phar_flush(phar_obj->archive, 0, 0, 0, &error);
4065 
4066 	if (error) {
4067 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
4068 		efree(error);
4069 	}
4070 }
4071 /* }}} */
4072 
4073 /* {{{ Deletes the global metadata of the phar */
PHP_METHOD(Phar,delMetadata)4074 PHP_METHOD(Phar, delMetadata)
4075 {
4076 	char *error;
4077 
4078 	if (zend_parse_parameters_none() == FAILURE) {
4079 		RETURN_THROWS();
4080 	}
4081 
4082 	PHAR_ARCHIVE_OBJECT();
4083 
4084 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
4085 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Write operations disabled by the php.ini setting phar.readonly");
4086 		RETURN_THROWS();
4087 	}
4088 
4089 	if (!phar_metadata_tracker_has_data(&phar_obj->archive->metadata_tracker, phar_obj->archive->is_persistent)) {
4090 		RETURN_TRUE;
4091 	}
4092 
4093 	phar_metadata_tracker_free(&phar_obj->archive->metadata_tracker, phar_obj->archive->is_persistent);
4094 	phar_obj->archive->is_modified = 1;
4095 	phar_flush(phar_obj->archive, 0, 0, 0, &error);
4096 
4097 	if (error) {
4098 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
4099 		efree(error);
4100 		RETURN_THROWS();
4101 	} else {
4102 		RETURN_TRUE;
4103 	}
4104 }
4105 /* }}} */
4106 
phar_extract_file(bool overwrite,phar_entry_info * entry,char * dest,size_t dest_len,char ** error)4107 static int phar_extract_file(bool overwrite, phar_entry_info *entry, char *dest, size_t dest_len, char **error) /* {{{ */
4108 {
4109 	php_stream_statbuf ssb;
4110 	size_t len;
4111 	php_stream *fp;
4112 	char *fullpath;
4113 	const char *slash;
4114 	mode_t mode;
4115 	cwd_state new_state;
4116 	char *filename;
4117 	size_t filename_len;
4118 
4119 	if (entry->is_mounted) {
4120 		/* silently ignore mounted entries */
4121 		return SUCCESS;
4122 	}
4123 
4124 	if (entry->filename_len >= sizeof(".phar")-1 && !memcmp(entry->filename, ".phar", sizeof(".phar")-1)) {
4125 		return SUCCESS;
4126 	}
4127 	/* strip .. from path and restrict it to be under dest directory */
4128 	new_state.cwd = (char*)emalloc(2);
4129 	new_state.cwd[0] = DEFAULT_SLASH;
4130 	new_state.cwd[1] = '\0';
4131 	new_state.cwd_length = 1;
4132 	if (virtual_file_ex(&new_state, entry->filename, NULL, CWD_EXPAND) != 0 ||
4133 			new_state.cwd_length <= 1) {
4134 		if (EINVAL == errno && entry->filename_len > 50) {
4135 			char *tmp = estrndup(entry->filename, 50);
4136 			spprintf(error, 4096, "Cannot extract \"%s...\" to \"%s...\", extracted filename is too long for filesystem", tmp, dest);
4137 			efree(tmp);
4138 		} else {
4139 			spprintf(error, 4096, "Cannot extract \"%s\", internal error", entry->filename);
4140 		}
4141 		efree(new_state.cwd);
4142 		return FAILURE;
4143 	}
4144 	filename = new_state.cwd + 1;
4145 	filename_len = new_state.cwd_length - 1;
4146 #ifdef PHP_WIN32
4147 	/* unixify the path back, otherwise non zip formats might be broken */
4148 	{
4149 		size_t cnt = 0;
4150 
4151 		do {
4152 			if ('\\' == filename[cnt]) {
4153 				filename[cnt] = '/';
4154 			}
4155 		} while (cnt++ < filename_len);
4156 	}
4157 #endif
4158 
4159 	len = spprintf(&fullpath, 0, "%s/%s", dest, filename);
4160 
4161 	if (len >= MAXPATHLEN) {
4162 		char *tmp;
4163 		/* truncate for error message */
4164 		fullpath[50] = '\0';
4165 		if (entry->filename_len > 50) {
4166 			tmp = estrndup(entry->filename, 50);
4167 			spprintf(error, 4096, "Cannot extract \"%s...\" to \"%s...\", extracted filename is too long for filesystem", tmp, fullpath);
4168 			efree(tmp);
4169 		} else {
4170 			spprintf(error, 4096, "Cannot extract \"%s\" to \"%s...\", extracted filename is too long for filesystem", entry->filename, fullpath);
4171 		}
4172 		efree(fullpath);
4173 		efree(new_state.cwd);
4174 		return FAILURE;
4175 	}
4176 
4177 	if (!len) {
4178 		spprintf(error, 4096, "Cannot extract \"%s\", internal error", entry->filename);
4179 		efree(fullpath);
4180 		efree(new_state.cwd);
4181 		return FAILURE;
4182 	}
4183 
4184 	if (php_check_open_basedir(fullpath)) {
4185 		spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", openbasedir/safe mode restrictions in effect", entry->filename, fullpath);
4186 		efree(fullpath);
4187 		efree(new_state.cwd);
4188 		return FAILURE;
4189 	}
4190 
4191 	/* let see if the path already exists */
4192 	if (!overwrite && SUCCESS == php_stream_stat_path(fullpath, &ssb)) {
4193 		spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", path already exists", entry->filename, fullpath);
4194 		efree(fullpath);
4195 		efree(new_state.cwd);
4196 		return FAILURE;
4197 	}
4198 
4199 	/* perform dirname */
4200 	slash = zend_memrchr(filename, '/', filename_len);
4201 
4202 	if (slash) {
4203 		fullpath[dest_len + (slash - filename) + 1] = '\0';
4204 	} else {
4205 		fullpath[dest_len] = '\0';
4206 	}
4207 
4208 	if (FAILURE == php_stream_stat_path(fullpath, &ssb)) {
4209 		if (entry->is_dir) {
4210 			if (!php_stream_mkdir(fullpath, entry->flags & PHAR_ENT_PERM_MASK,  PHP_STREAM_MKDIR_RECURSIVE, NULL)) {
4211 				spprintf(error, 4096, "Cannot extract \"%s\", could not create directory \"%s\"", entry->filename, fullpath);
4212 				efree(fullpath);
4213 				efree(new_state.cwd);
4214 				return FAILURE;
4215 			}
4216 		} else {
4217 			if (!php_stream_mkdir(fullpath, 0777,  PHP_STREAM_MKDIR_RECURSIVE, NULL)) {
4218 				spprintf(error, 4096, "Cannot extract \"%s\", could not create directory \"%s\"", entry->filename, fullpath);
4219 				efree(fullpath);
4220 				efree(new_state.cwd);
4221 				return FAILURE;
4222 			}
4223 		}
4224 	}
4225 
4226 	if (slash) {
4227 		fullpath[dest_len + (slash - filename) + 1] = '/';
4228 	} else {
4229 		fullpath[dest_len] = '/';
4230 	}
4231 
4232 	filename = NULL;
4233 	efree(new_state.cwd);
4234 	/* it is a standalone directory, job done */
4235 	if (entry->is_dir) {
4236 		efree(fullpath);
4237 		return SUCCESS;
4238 	}
4239 
4240 	fp = php_stream_open_wrapper(fullpath, "w+b", REPORT_ERRORS, NULL);
4241 
4242 	if (!fp) {
4243 		spprintf(error, 4096, "Cannot extract \"%s\", could not open for writing \"%s\"", entry->filename, fullpath);
4244 		efree(fullpath);
4245 		return FAILURE;
4246 	}
4247 
4248 	if ((phar_get_fp_type(entry) == PHAR_FP && (entry->flags & PHAR_ENT_COMPRESSION_MASK)) || !phar_get_efp(entry, 0)) {
4249 		if (FAILURE == phar_open_entry_fp(entry, error, 1)) {
4250 			if (error) {
4251 				spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", unable to open internal file pointer: %s", entry->filename, fullpath, *error);
4252 			} else {
4253 				spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", unable to open internal file pointer", entry->filename, fullpath);
4254 			}
4255 			efree(fullpath);
4256 			php_stream_close(fp);
4257 			return FAILURE;
4258 		}
4259 	}
4260 
4261 	if (FAILURE == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
4262 		spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", unable to seek internal file pointer", entry->filename, fullpath);
4263 		efree(fullpath);
4264 		php_stream_close(fp);
4265 		return FAILURE;
4266 	}
4267 
4268 	if (SUCCESS != php_stream_copy_to_stream_ex(phar_get_efp(entry, 0), fp, entry->uncompressed_filesize, NULL)) {
4269 		spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", copying contents failed", entry->filename, fullpath);
4270 		efree(fullpath);
4271 		php_stream_close(fp);
4272 		return FAILURE;
4273 	}
4274 
4275 	php_stream_close(fp);
4276 	mode = (mode_t) entry->flags & PHAR_ENT_PERM_MASK;
4277 
4278 	if (FAILURE == VCWD_CHMOD(fullpath, mode)) {
4279 		spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", setting file permissions failed", entry->filename, fullpath);
4280 		efree(fullpath);
4281 		return FAILURE;
4282 	}
4283 
4284 	efree(fullpath);
4285 	return SUCCESS;
4286 }
4287 /* }}} */
4288 
extract_helper(phar_archive_data * archive,zend_string * search,char * pathto,size_t pathto_len,bool overwrite,char ** error)4289 static int extract_helper(phar_archive_data *archive, zend_string *search, char *pathto, size_t pathto_len, bool overwrite, char **error) { /* {{{ */
4290 	int extracted = 0;
4291 	phar_entry_info *entry;
4292 
4293 	if (!search) {
4294 		/* nothing to match -- extract all files */
4295 		ZEND_HASH_MAP_FOREACH_PTR(&archive->manifest, entry) {
4296 			if (FAILURE == phar_extract_file(overwrite, entry, pathto, pathto_len, error)) return -1;
4297 			extracted++;
4298 		} ZEND_HASH_FOREACH_END();
4299 	} else if ('/' == ZSTR_VAL(search)[ZSTR_LEN(search) - 1]) {
4300 		/* ends in "/" -- extract all entries having that prefix */
4301 		ZEND_HASH_MAP_FOREACH_PTR(&archive->manifest, entry) {
4302 			if (0 != strncmp(ZSTR_VAL(search), entry->filename, ZSTR_LEN(search))) continue;
4303 			if (FAILURE == phar_extract_file(overwrite, entry, pathto, pathto_len, error)) return -1;
4304 			extracted++;
4305 		} ZEND_HASH_FOREACH_END();
4306 	} else {
4307 		/* otherwise, looking for an exact match */
4308 		entry = zend_hash_find_ptr(&archive->manifest, search);
4309 		if (NULL == entry) return 0;
4310 		if (FAILURE == phar_extract_file(overwrite, entry, pathto, pathto_len, error)) return -1;
4311 		return 1;
4312 	}
4313 
4314 	return extracted;
4315 }
4316 /* }}} */
4317 
4318 /* {{{ Extract one or more file from a phar archive, optionally overwriting existing files */
PHP_METHOD(Phar,extractTo)4319 PHP_METHOD(Phar, extractTo)
4320 {
4321 	php_stream *fp;
4322 	php_stream_statbuf ssb;
4323 	char *pathto;
4324 	zend_string *filename = NULL;
4325 	size_t pathto_len;
4326 	int ret;
4327 	zval *zval_file;
4328 	HashTable *files_ht = NULL;
4329 	bool overwrite = 0;
4330 	char *error = NULL;
4331 
4332 	ZEND_PARSE_PARAMETERS_START(1, 3)
4333 		Z_PARAM_PATH(pathto, pathto_len)
4334 		Z_PARAM_OPTIONAL
4335 		Z_PARAM_ARRAY_HT_OR_STR_OR_NULL(files_ht, filename)
4336 		Z_PARAM_BOOL(overwrite)
4337 	ZEND_PARSE_PARAMETERS_END();
4338 
4339 	PHAR_ARCHIVE_OBJECT();
4340 
4341 	fp = php_stream_open_wrapper(phar_obj->archive->fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, NULL);
4342 
4343 	if (!fp) {
4344 		zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0,
4345 			"Invalid argument, %s cannot be found", phar_obj->archive->fname);
4346 		RETURN_THROWS();
4347 	}
4348 
4349 	php_stream_close(fp);
4350 
4351 	if (pathto_len < 1) {
4352 		zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0,
4353 			"Invalid argument, extraction path must be non-zero length");
4354 		RETURN_THROWS();
4355 	}
4356 
4357 	if (pathto_len >= MAXPATHLEN) {
4358 		char *tmp = estrndup(pathto, 50);
4359 		/* truncate for error message */
4360 		zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "Cannot extract to \"%s...\", destination directory is too long for filesystem", tmp);
4361 		efree(tmp);
4362 		RETURN_THROWS();
4363 	}
4364 
4365 	if (php_stream_stat_path(pathto, &ssb) < 0) {
4366 		ret = php_stream_mkdir(pathto, 0777,  PHP_STREAM_MKDIR_RECURSIVE, NULL);
4367 		if (!ret) {
4368 			zend_throw_exception_ex(spl_ce_RuntimeException, 0,
4369 				"Unable to create path \"%s\" for extraction", pathto);
4370 			RETURN_THROWS();
4371 		}
4372 	} else if (!(ssb.sb.st_mode & S_IFDIR)) {
4373 		zend_throw_exception_ex(spl_ce_RuntimeException, 0,
4374 			"Unable to use path \"%s\" for extraction, it is a file, must be a directory", pathto);
4375 		RETURN_THROWS();
4376 	}
4377 
4378 	if (files_ht) {
4379 		if (zend_hash_num_elements(files_ht) == 0) {
4380 			RETURN_FALSE;
4381 		}
4382 
4383 		ZEND_HASH_FOREACH_VAL(files_ht, zval_file) {
4384 			ZVAL_DEREF(zval_file);
4385 			if (IS_STRING != Z_TYPE_P(zval_file)) {
4386 				zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0,
4387 					"Invalid argument, array of filenames to extract contains non-string value");
4388 				RETURN_THROWS();
4389 			}
4390 			switch (extract_helper(phar_obj->archive, Z_STR_P(zval_file), pathto, pathto_len, overwrite, &error)) {
4391 				case -1:
4392 					zend_throw_exception_ex(phar_ce_PharException, 0, "Extraction from phar \"%s\" failed: %s",
4393 						phar_obj->archive->fname, error);
4394 					efree(error);
4395 					RETURN_THROWS();
4396 				case 0:
4397 					zend_throw_exception_ex(phar_ce_PharException, 0,
4398 						"phar error: attempted to extract non-existent file or directory \"%s\" from phar \"%s\"",
4399 						ZSTR_VAL(Z_STR_P(zval_file)), phar_obj->archive->fname);
4400 					RETURN_THROWS();
4401 			}
4402 		} ZEND_HASH_FOREACH_END();
4403 		RETURN_TRUE;
4404 	}
4405 
4406 	ret = extract_helper(phar_obj->archive, filename, pathto, pathto_len, overwrite, &error);
4407 	if (-1 == ret) {
4408 		zend_throw_exception_ex(phar_ce_PharException, 0, "Extraction from phar \"%s\" failed: %s",
4409 			phar_obj->archive->fname, error);
4410 		efree(error);
4411 	} else if (0 == ret && NULL != filename) {
4412 		zend_throw_exception_ex(phar_ce_PharException, 0,
4413 			"phar error: attempted to extract non-existent file or directory \"%s\" from phar \"%s\"",
4414 			ZSTR_VAL(filename), phar_obj->archive->fname);
4415 	} else {
4416 		RETURN_TRUE;
4417 	}
4418 }
4419 /* }}} */
4420 
4421 
4422 /* {{{ Construct a Phar entry object */
PHP_METHOD(PharFileInfo,__construct)4423 PHP_METHOD(PharFileInfo, __construct)
4424 {
4425 	char *fname, *arch, *entry, *error;
4426 	size_t fname_len;
4427 	size_t arch_len, entry_len;
4428 	phar_entry_object *entry_obj;
4429 	phar_entry_info *entry_info;
4430 	phar_archive_data *phar_data;
4431 	zval *zobj = ZEND_THIS, arg1;
4432 
4433 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &fname, &fname_len) == FAILURE) {
4434 		RETURN_THROWS();
4435 	}
4436 
4437 	entry_obj = (phar_entry_object*)((char*)Z_OBJ_P(zobj) - Z_OBJ_P(zobj)->handlers->offset);
4438 
4439 	if (entry_obj->entry) {
4440 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot call constructor twice");
4441 		RETURN_THROWS();
4442 	}
4443 
4444 	if (fname_len < 7 || memcmp(fname, "phar://", 7) || phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0) == FAILURE) {
4445 		zend_throw_exception_ex(spl_ce_RuntimeException, 0,
4446 			"'%s' is not a valid phar archive URL (must have at least phar://filename.phar)", fname);
4447 		RETURN_THROWS();
4448 	}
4449 
4450 	if (phar_open_from_filename(arch, arch_len, NULL, 0, REPORT_ERRORS, &phar_data, &error) == FAILURE) {
4451 		efree(arch);
4452 		efree(entry);
4453 		if (error) {
4454 			zend_throw_exception_ex(spl_ce_RuntimeException, 0,
4455 				"Cannot open phar file '%s': %s", fname, error);
4456 			efree(error);
4457 		} else {
4458 			zend_throw_exception_ex(spl_ce_RuntimeException, 0,
4459 				"Cannot open phar file '%s'", fname);
4460 		}
4461 		RETURN_THROWS();
4462 	}
4463 
4464 	if ((entry_info = phar_get_entry_info_dir(phar_data, entry, entry_len, 1, &error, 1)) == NULL) {
4465 		zend_throw_exception_ex(spl_ce_RuntimeException, 0,
4466 			"Cannot access phar file entry '%s' in archive '%s'%s%s", entry, arch, error ? ", " : "", error ? error : "");
4467 		efree(arch);
4468 		efree(entry);
4469 		RETURN_THROWS();
4470 	}
4471 
4472 	efree(arch);
4473 	efree(entry);
4474 
4475 	entry_obj->entry = entry_info;
4476 
4477 	ZVAL_STRINGL(&arg1, fname, fname_len);
4478 
4479 	zend_call_known_instance_method_with_1_params(spl_ce_SplFileInfo->constructor,
4480 		Z_OBJ_P(zobj), NULL, &arg1);
4481 
4482 	zval_ptr_dtor(&arg1);
4483 }
4484 /* }}} */
4485 
4486 #define PHAR_ENTRY_OBJECT() \
4487 	zval *zobj = ZEND_THIS; \
4488 	phar_entry_object *entry_obj = (phar_entry_object*)((char*)Z_OBJ_P(zobj) - Z_OBJ_P(zobj)->handlers->offset); \
4489 	if (!entry_obj->entry) { \
4490 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, \
4491 			"Cannot call method on an uninitialized PharFileInfo object"); \
4492 		RETURN_THROWS(); \
4493 	}
4494 
4495 /* {{{ clean up directory-based entry objects */
PHP_METHOD(PharFileInfo,__destruct)4496 PHP_METHOD(PharFileInfo, __destruct)
4497 {
4498 	zval *zobj = ZEND_THIS;
4499 	phar_entry_object *entry_obj = (phar_entry_object*)((char*)Z_OBJ_P(zobj) - Z_OBJ_P(zobj)->handlers->offset);
4500 
4501 	if (zend_parse_parameters_none() == FAILURE) {
4502 		RETURN_THROWS();
4503 	}
4504 
4505 	if (entry_obj->entry && entry_obj->entry->is_temp_dir) {
4506 		if (entry_obj->entry->filename) {
4507 			efree(entry_obj->entry->filename);
4508 			entry_obj->entry->filename = NULL;
4509 		}
4510 
4511 		efree(entry_obj->entry);
4512 		entry_obj->entry = NULL;
4513 	}
4514 }
4515 /* }}} */
4516 
4517 /* {{{ Returns the compressed size */
PHP_METHOD(PharFileInfo,getCompressedSize)4518 PHP_METHOD(PharFileInfo, getCompressedSize)
4519 {
4520 	if (zend_parse_parameters_none() == FAILURE) {
4521 		RETURN_THROWS();
4522 	}
4523 
4524 	PHAR_ENTRY_OBJECT();
4525 
4526 	RETURN_LONG(entry_obj->entry->compressed_filesize);
4527 }
4528 /* }}} */
4529 
4530 /* {{{ Returns whether the entry is compressed, and whether it is compressed with Phar::GZ or Phar::BZ2 if specified */
PHP_METHOD(PharFileInfo,isCompressed)4531 PHP_METHOD(PharFileInfo, isCompressed)
4532 {
4533 	zend_long method;
4534 	bool method_is_null = 1;
4535 
4536 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l!", &method, &method_is_null) == FAILURE) {
4537 		RETURN_THROWS();
4538 	}
4539 
4540 	PHAR_ENTRY_OBJECT();
4541 
4542 	if (method_is_null) {
4543 		RETURN_BOOL(entry_obj->entry->flags & PHAR_ENT_COMPRESSION_MASK);
4544 	}
4545 
4546 	switch (method) {
4547 		case 9021976: /* Retained for BC */
4548 			RETURN_BOOL(entry_obj->entry->flags & PHAR_ENT_COMPRESSION_MASK);
4549 		case PHAR_ENT_COMPRESSED_GZ:
4550 			RETURN_BOOL(entry_obj->entry->flags & PHAR_ENT_COMPRESSED_GZ);
4551 		case PHAR_ENT_COMPRESSED_BZ2:
4552 			RETURN_BOOL(entry_obj->entry->flags & PHAR_ENT_COMPRESSED_BZ2);
4553 		default:
4554 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unknown compression type specified");
4555 			RETURN_THROWS();
4556 	}
4557 }
4558 /* }}} */
4559 
4560 /* {{{ Returns CRC32 code or throws an exception if not CRC checked */
PHP_METHOD(PharFileInfo,getCRC32)4561 PHP_METHOD(PharFileInfo, getCRC32)
4562 {
4563 	if (zend_parse_parameters_none() == FAILURE) {
4564 		RETURN_THROWS();
4565 	}
4566 
4567 	PHAR_ENTRY_OBJECT();
4568 
4569 	if (entry_obj->entry->is_dir) {
4570 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, \
4571 			"Phar entry is a directory, does not have a CRC"); \
4572 		RETURN_THROWS();
4573 	}
4574 
4575 	if (entry_obj->entry->is_crc_checked) {
4576 		RETURN_LONG(entry_obj->entry->crc32);
4577 	} else {
4578 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Phar entry was not CRC checked");
4579 		RETURN_THROWS();
4580 	}
4581 }
4582 /* }}} */
4583 
4584 /* {{{ Returns whether file entry is CRC checked */
PHP_METHOD(PharFileInfo,isCRCChecked)4585 PHP_METHOD(PharFileInfo, isCRCChecked)
4586 {
4587 	if (zend_parse_parameters_none() == FAILURE) {
4588 		RETURN_THROWS();
4589 	}
4590 
4591 	PHAR_ENTRY_OBJECT();
4592 
4593 	RETURN_BOOL(entry_obj->entry->is_crc_checked);
4594 }
4595 /* }}} */
4596 
4597 /* {{{ Returns the Phar file entry flags */
PHP_METHOD(PharFileInfo,getPharFlags)4598 PHP_METHOD(PharFileInfo, getPharFlags)
4599 {
4600 	if (zend_parse_parameters_none() == FAILURE) {
4601 		RETURN_THROWS();
4602 	}
4603 
4604 	PHAR_ENTRY_OBJECT();
4605 
4606 	RETURN_LONG(entry_obj->entry->flags & ~(PHAR_ENT_PERM_MASK|PHAR_ENT_COMPRESSION_MASK));
4607 }
4608 /* }}} */
4609 
4610 /* {{{ set the file permissions for the Phar.  This only allows setting execution bit, read/write */
PHP_METHOD(PharFileInfo,chmod)4611 PHP_METHOD(PharFileInfo, chmod)
4612 {
4613 	char *error;
4614 	zend_long perms;
4615 
4616 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &perms) == FAILURE) {
4617 		RETURN_THROWS();
4618 	}
4619 
4620 	PHAR_ENTRY_OBJECT();
4621 
4622 	if (entry_obj->entry->is_temp_dir) {
4623 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, \
4624 			"Phar entry \"%s\" is a temporary directory (not an actual entry in the archive), cannot chmod", entry_obj->entry->filename); \
4625 		RETURN_THROWS();
4626 	}
4627 
4628 	if (PHAR_G(readonly) && !entry_obj->entry->phar->is_data) {
4629 		zend_throw_exception_ex(phar_ce_PharException, 0, "Cannot modify permissions for file \"%s\" in phar \"%s\", write operations are prohibited", entry_obj->entry->filename, entry_obj->entry->phar->fname);
4630 		RETURN_THROWS();
4631 	}
4632 
4633 	if (entry_obj->entry->is_persistent) {
4634 		phar_archive_data *phar = entry_obj->entry->phar;
4635 
4636 		if (FAILURE == phar_copy_on_write(&phar)) {
4637 			zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar->fname);
4638 			RETURN_THROWS();
4639 		}
4640 		/* re-populate after copy-on-write */
4641 		entry_obj->entry = zend_hash_str_find_ptr(&phar->manifest, entry_obj->entry->filename, entry_obj->entry->filename_len);
4642 	}
4643 	/* clear permissions */
4644 	entry_obj->entry->flags &= ~PHAR_ENT_PERM_MASK;
4645 	perms &= 0777;
4646 	entry_obj->entry->flags |= perms;
4647 	entry_obj->entry->old_flags = entry_obj->entry->flags;
4648 	entry_obj->entry->phar->is_modified = 1;
4649 	entry_obj->entry->is_modified = 1;
4650 
4651 	/* hackish cache in php_stat needs to be cleared */
4652 	/* if this code fails to work, check main/streams/streams.c, _php_stream_stat_path */
4653 	if (BG(CurrentLStatFile)) {
4654 		zend_string_release(BG(CurrentLStatFile));
4655 	}
4656 
4657 	if (BG(CurrentStatFile)) {
4658 		zend_string_release(BG(CurrentStatFile));
4659 	}
4660 
4661 	BG(CurrentLStatFile) = NULL;
4662 	BG(CurrentStatFile) = NULL;
4663 	phar_flush(entry_obj->entry->phar, 0, 0, 0, &error);
4664 
4665 	if (error) {
4666 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
4667 		efree(error);
4668 	}
4669 }
4670 /* }}} */
4671 
4672 /* {{{ Returns the metadata of the entry */
PHP_METHOD(PharFileInfo,hasMetadata)4673 PHP_METHOD(PharFileInfo, hasMetadata)
4674 {
4675 	if (zend_parse_parameters_none() == FAILURE) {
4676 		RETURN_THROWS();
4677 	}
4678 
4679 	PHAR_ENTRY_OBJECT();
4680 
4681 	RETURN_BOOL(phar_metadata_tracker_has_data(&entry_obj->entry->metadata_tracker, entry_obj->entry->is_persistent));
4682 }
4683 /* }}} */
4684 
4685 /* {{{ Returns the metadata of the entry */
PHP_METHOD(PharFileInfo,getMetadata)4686 PHP_METHOD(PharFileInfo, getMetadata)
4687 {
4688 	HashTable *unserialize_options = NULL;
4689 	phar_metadata_tracker *tracker;
4690 
4691 	ZEND_PARSE_PARAMETERS_START(0, 1)
4692 		Z_PARAM_OPTIONAL
4693 		Z_PARAM_ARRAY_HT(unserialize_options)
4694 	ZEND_PARSE_PARAMETERS_END();
4695 
4696 	PHAR_ENTRY_OBJECT();
4697 
4698 	tracker = &entry_obj->entry->metadata_tracker;
4699 	if (phar_metadata_tracker_has_data(tracker, entry_obj->entry->is_persistent)) {
4700 		phar_metadata_tracker_unserialize_or_copy(tracker, return_value, entry_obj->entry->is_persistent, unserialize_options, "PharFileInfo::getMetadata");
4701 	}
4702 }
4703 /* }}} */
4704 
4705 /* {{{ Sets the metadata of the entry */
PHP_METHOD(PharFileInfo,setMetadata)4706 PHP_METHOD(PharFileInfo, setMetadata)
4707 {
4708 	char *error;
4709 	zval *metadata;
4710 
4711 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &metadata) == FAILURE) {
4712 		RETURN_THROWS();
4713 	}
4714 
4715 	PHAR_ENTRY_OBJECT();
4716 
4717 	if (PHAR_G(readonly) && !entry_obj->entry->phar->is_data) {
4718 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Write operations disabled by the php.ini setting phar.readonly");
4719 		RETURN_THROWS();
4720 	}
4721 
4722 	if (entry_obj->entry->is_temp_dir) {
4723 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, \
4724 			"Phar entry is a temporary directory (not an actual entry in the archive), cannot set metadata"); \
4725 		RETURN_THROWS();
4726 	}
4727 
4728 	if (entry_obj->entry->is_persistent) {
4729 		phar_archive_data *phar = entry_obj->entry->phar;
4730 
4731 		if (FAILURE == phar_copy_on_write(&phar)) {
4732 			zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar->fname);
4733 			RETURN_THROWS();
4734 		}
4735 		/* re-populate after copy-on-write */
4736 		entry_obj->entry = zend_hash_str_find_ptr(&phar->manifest, entry_obj->entry->filename, entry_obj->entry->filename_len);
4737 		ZEND_ASSERT(!entry_obj->entry->is_persistent); /* Should no longer be persistent */
4738 	}
4739 
4740 	if (serialize_metadata_or_throw(&entry_obj->entry->metadata_tracker, entry_obj->entry->is_persistent, metadata) != SUCCESS) {
4741 		RETURN_THROWS();
4742 	}
4743 
4744 	entry_obj->entry->is_modified = 1;
4745 	entry_obj->entry->phar->is_modified = 1;
4746 	phar_flush(entry_obj->entry->phar, 0, 0, 0, &error);
4747 
4748 	if (error) {
4749 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
4750 		efree(error);
4751 	}
4752 }
4753 /* }}} */
4754 
4755 /* {{{ Deletes the metadata of the entry */
PHP_METHOD(PharFileInfo,delMetadata)4756 PHP_METHOD(PharFileInfo, delMetadata)
4757 {
4758 	char *error;
4759 
4760 	if (zend_parse_parameters_none() == FAILURE) {
4761 		RETURN_THROWS();
4762 	}
4763 
4764 	PHAR_ENTRY_OBJECT();
4765 
4766 	if (PHAR_G(readonly) && !entry_obj->entry->phar->is_data) {
4767 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Write operations disabled by the php.ini setting phar.readonly");
4768 		RETURN_THROWS();
4769 	}
4770 
4771 	if (entry_obj->entry->is_temp_dir) {
4772 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, \
4773 			"Phar entry is a temporary directory (not an actual entry in the archive), cannot delete metadata"); \
4774 		RETURN_THROWS();
4775 	}
4776 
4777 	if (phar_metadata_tracker_has_data(&entry_obj->entry->metadata_tracker, entry_obj->entry->is_persistent)) {
4778 		if (entry_obj->entry->is_persistent) {
4779 			phar_archive_data *phar = entry_obj->entry->phar;
4780 
4781 			if (FAILURE == phar_copy_on_write(&phar)) {
4782 				zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar->fname);
4783 				RETURN_THROWS();
4784 			}
4785 			/* re-populate after copy-on-write */
4786 			entry_obj->entry = zend_hash_str_find_ptr(&phar->manifest, entry_obj->entry->filename, entry_obj->entry->filename_len);
4787 		}
4788 		/* multiple values may reference the metadata */
4789 		phar_metadata_tracker_free(&entry_obj->entry->metadata_tracker, entry_obj->entry->is_persistent);
4790 		entry_obj->entry->is_modified = 1;
4791 		entry_obj->entry->phar->is_modified = 1;
4792 
4793 		phar_flush(entry_obj->entry->phar, 0, 0, 0, &error);
4794 
4795 		if (error) {
4796 			zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
4797 			efree(error);
4798 			RETURN_THROWS();
4799 		} else {
4800 			RETURN_TRUE;
4801 		}
4802 
4803 	} else {
4804 		RETURN_TRUE;
4805 	}
4806 }
4807 /* }}} */
4808 
4809 /* {{{ return the complete file contents of the entry (like file_get_contents) */
PHP_METHOD(PharFileInfo,getContent)4810 PHP_METHOD(PharFileInfo, getContent)
4811 {
4812 	char *error;
4813 	php_stream *fp;
4814 	phar_entry_info *link;
4815 	zend_string *str;
4816 
4817 	if (zend_parse_parameters_none() == FAILURE) {
4818 		RETURN_THROWS();
4819 	}
4820 
4821 	PHAR_ENTRY_OBJECT();
4822 
4823 	if (entry_obj->entry->is_dir) {
4824 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4825 			"phar error: Cannot retrieve contents, \"%s\" in phar \"%s\" is a directory", entry_obj->entry->filename, entry_obj->entry->phar->fname);
4826 		RETURN_THROWS();
4827 	}
4828 
4829 	link = phar_get_link_source(entry_obj->entry);
4830 
4831 	if (!link) {
4832 		link = entry_obj->entry;
4833 	}
4834 
4835 	if (SUCCESS != phar_open_entry_fp(link, &error, 0)) {
4836 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4837 			"phar error: Cannot retrieve contents, \"%s\" in phar \"%s\": %s", entry_obj->entry->filename, entry_obj->entry->phar->fname, error);
4838 		efree(error);
4839 		RETURN_THROWS();
4840 	}
4841 
4842 	if (!(fp = phar_get_efp(link, 0))) {
4843 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4844 			"phar error: Cannot retrieve contents of \"%s\" in phar \"%s\"", entry_obj->entry->filename, entry_obj->entry->phar->fname);
4845 		RETURN_THROWS();
4846 	}
4847 
4848 	phar_seek_efp(link, 0, SEEK_SET, 0, 0);
4849 	str = php_stream_copy_to_mem(fp, link->uncompressed_filesize, 0);
4850 	if (str) {
4851 		RETURN_STR(str);
4852 	} else {
4853 		RETURN_EMPTY_STRING();
4854 	}
4855 }
4856 /* }}} */
4857 
4858 /* {{{ Instructs the Phar class to compress the current file using zlib or bzip2 compression */
PHP_METHOD(PharFileInfo,compress)4859 PHP_METHOD(PharFileInfo, compress)
4860 {
4861 	zend_long method;
4862 	char *error;
4863 
4864 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &method) == FAILURE) {
4865 		RETURN_THROWS();
4866 	}
4867 
4868 	PHAR_ENTRY_OBJECT();
4869 
4870 	if (entry_obj->entry->is_tar) {
4871 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4872 			"Cannot compress with Gzip compression, not possible with tar-based phar archives");
4873 		RETURN_THROWS();
4874 	}
4875 
4876 	if (entry_obj->entry->is_dir) {
4877 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, \
4878 			"Phar entry is a directory, cannot set compression"); \
4879 		RETURN_THROWS();
4880 	}
4881 
4882 	if (PHAR_G(readonly) && !entry_obj->entry->phar->is_data) {
4883 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4884 			"Phar is readonly, cannot change compression");
4885 		RETURN_THROWS();
4886 	}
4887 
4888 	if (entry_obj->entry->is_deleted) {
4889 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4890 			"Cannot compress deleted file");
4891 		RETURN_THROWS();
4892 	}
4893 
4894 	if (entry_obj->entry->is_persistent) {
4895 		phar_archive_data *phar = entry_obj->entry->phar;
4896 
4897 		if (FAILURE == phar_copy_on_write(&phar)) {
4898 			zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar->fname);
4899 			RETURN_THROWS();
4900 		}
4901 		/* re-populate after copy-on-write */
4902 		entry_obj->entry = zend_hash_str_find_ptr(&phar->manifest, entry_obj->entry->filename, entry_obj->entry->filename_len);
4903 	}
4904 	switch (method) {
4905 		case PHAR_ENT_COMPRESSED_GZ:
4906 			if (entry_obj->entry->flags & PHAR_ENT_COMPRESSED_GZ) {
4907 				RETURN_TRUE;
4908 			}
4909 
4910 			if ((entry_obj->entry->flags & PHAR_ENT_COMPRESSED_BZ2) != 0) {
4911 				if (!PHAR_G(has_bz2)) {
4912 					zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4913 						"Cannot compress with gzip compression, file is already compressed with bzip2 compression and bz2 extension is not enabled, cannot decompress");
4914 					RETURN_THROWS();
4915 				}
4916 
4917 				/* decompress this file indirectly */
4918 				if (SUCCESS != phar_open_entry_fp(entry_obj->entry, &error, 1)) {
4919 					zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4920 						"phar error: Cannot decompress bzip2-compressed file \"%s\" in phar \"%s\" in order to compress with gzip: %s", entry_obj->entry->filename, entry_obj->entry->phar->fname, error);
4921 					efree(error);
4922 					RETURN_THROWS();
4923 				}
4924 			}
4925 
4926 			if (!PHAR_G(has_zlib)) {
4927 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4928 					"Cannot compress with gzip compression, zlib extension is not enabled");
4929 				RETURN_THROWS();
4930 			}
4931 
4932 			entry_obj->entry->old_flags = entry_obj->entry->flags;
4933 			entry_obj->entry->flags &= ~PHAR_ENT_COMPRESSION_MASK;
4934 			entry_obj->entry->flags |= PHAR_ENT_COMPRESSED_GZ;
4935 			break;
4936 		case PHAR_ENT_COMPRESSED_BZ2:
4937 			if (entry_obj->entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
4938 				RETURN_TRUE;
4939 			}
4940 
4941 			if ((entry_obj->entry->flags & PHAR_ENT_COMPRESSED_GZ) != 0) {
4942 				if (!PHAR_G(has_zlib)) {
4943 					zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4944 						"Cannot compress with bzip2 compression, file is already compressed with gzip compression and zlib extension is not enabled, cannot decompress");
4945 					RETURN_THROWS();
4946 				}
4947 
4948 				/* decompress this file indirectly */
4949 				if (SUCCESS != phar_open_entry_fp(entry_obj->entry, &error, 1)) {
4950 					zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4951 						"phar error: Cannot decompress gzip-compressed file \"%s\" in phar \"%s\" in order to compress with bzip2: %s", entry_obj->entry->filename, entry_obj->entry->phar->fname, error);
4952 					efree(error);
4953 					RETURN_THROWS();
4954 				}
4955 			}
4956 
4957 			if (!PHAR_G(has_bz2)) {
4958 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4959 					"Cannot compress with bzip2 compression, bz2 extension is not enabled");
4960 				RETURN_THROWS();
4961 			}
4962 			entry_obj->entry->old_flags = entry_obj->entry->flags;
4963 			entry_obj->entry->flags &= ~PHAR_ENT_COMPRESSION_MASK;
4964 			entry_obj->entry->flags |= PHAR_ENT_COMPRESSED_BZ2;
4965 			break;
4966 		default:
4967 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unknown compression type specified");
4968 			RETURN_THROWS();
4969 	}
4970 
4971 	entry_obj->entry->phar->is_modified = 1;
4972 	entry_obj->entry->is_modified = 1;
4973 	phar_flush(entry_obj->entry->phar, 0, 0, 0, &error);
4974 
4975 	if (error) {
4976 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
4977 		efree(error);
4978 		RETURN_THROWS();
4979 	}
4980 
4981 	RETURN_TRUE;
4982 }
4983 /* }}} */
4984 
4985 /* {{{ Instructs the Phar class to decompress the current file */
PHP_METHOD(PharFileInfo,decompress)4986 PHP_METHOD(PharFileInfo, decompress)
4987 {
4988 	char *error;
4989 	char *compression_type;
4990 
4991 	if (zend_parse_parameters_none() == FAILURE) {
4992 		RETURN_THROWS();
4993 	}
4994 
4995 	PHAR_ENTRY_OBJECT();
4996 
4997 	if (entry_obj->entry->is_dir) {
4998 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, \
4999 			"Phar entry is a directory, cannot set compression"); \
5000 		RETURN_THROWS();
5001 	}
5002 
5003 	if ((entry_obj->entry->flags & PHAR_ENT_COMPRESSION_MASK) == 0) {
5004 		RETURN_TRUE;
5005 	}
5006 
5007 	if (PHAR_G(readonly) && !entry_obj->entry->phar->is_data) {
5008 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
5009 			"Phar is readonly, cannot decompress");
5010 		RETURN_THROWS();
5011 	}
5012 
5013 	if (entry_obj->entry->is_deleted) {
5014 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
5015 			"Cannot compress deleted file");
5016 		RETURN_THROWS();
5017 	}
5018 
5019 	if ((entry_obj->entry->flags & PHAR_ENT_COMPRESSED_GZ) != 0 && !PHAR_G(has_zlib)) {
5020 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
5021 			"Cannot decompress Gzip-compressed file, zlib extension is not enabled");
5022 		RETURN_THROWS();
5023 	}
5024 
5025 	if ((entry_obj->entry->flags & PHAR_ENT_COMPRESSED_BZ2) != 0 && !PHAR_G(has_bz2)) {
5026 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
5027 			"Cannot decompress Bzip2-compressed file, bz2 extension is not enabled");
5028 		RETURN_THROWS();
5029 	}
5030 
5031 	if (entry_obj->entry->is_persistent) {
5032 		phar_archive_data *phar = entry_obj->entry->phar;
5033 
5034 		if (FAILURE == phar_copy_on_write(&phar)) {
5035 			zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar->fname);
5036 			RETURN_THROWS();
5037 		}
5038 		/* re-populate after copy-on-write */
5039 		entry_obj->entry = zend_hash_str_find_ptr(&phar->manifest, entry_obj->entry->filename, entry_obj->entry->filename_len);
5040 	}
5041 	switch (entry_obj->entry->flags & PHAR_ENT_COMPRESSION_MASK) {
5042 		case PHAR_ENT_COMPRESSED_GZ:
5043 			compression_type = "gzip";
5044 			break;
5045 		case PHAR_ENT_COMPRESSED_BZ2:
5046 			compression_type = "bz2";
5047 			break;
5048 		default:
5049 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
5050 				"Cannot decompress file compressed with unknown compression type");
5051 			RETURN_THROWS();
5052 	}
5053 	/* decompress this file indirectly */
5054 	if (SUCCESS != phar_open_entry_fp(entry_obj->entry, &error, 1)) {
5055 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
5056 			"Phar error: Cannot decompress %s-compressed file \"%s\" in phar \"%s\": %s", compression_type, entry_obj->entry->filename, entry_obj->entry->phar->fname, error);
5057 		efree(error);
5058 		RETURN_THROWS();
5059 	}
5060 
5061 	entry_obj->entry->old_flags = entry_obj->entry->flags;
5062 	entry_obj->entry->flags &= ~PHAR_ENT_COMPRESSION_MASK;
5063 	entry_obj->entry->phar->is_modified = 1;
5064 	entry_obj->entry->is_modified = 1;
5065 	phar_flush(entry_obj->entry->phar, 0, 0, 0, &error);
5066 
5067 	if (error) {
5068 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
5069 		efree(error);
5070 		RETURN_THROWS();
5071 	}
5072 
5073 	RETURN_TRUE;
5074 }
5075 /* }}} */
5076 
5077 /* {{{ phar methods */
5078 
phar_object_init(void)5079 void phar_object_init(void) /* {{{ */
5080 {
5081 	phar_ce_PharException = register_class_PharException(zend_ce_exception);
5082 
5083 	phar_ce_archive = register_class_Phar(spl_ce_RecursiveDirectoryIterator, zend_ce_countable, zend_ce_arrayaccess);
5084 
5085 	phar_ce_data = register_class_PharData(spl_ce_RecursiveDirectoryIterator, zend_ce_countable, zend_ce_arrayaccess);
5086 
5087 	phar_ce_entry = register_class_PharFileInfo(spl_ce_SplFileInfo);
5088 }
5089 /* }}} */
5090