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