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