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