xref: /PHP-8.2/ext/phar/phar_object.c (revision 85dbbe19)
1 /*
2   +----------------------------------------------------------------------+
3   | phar php single-file executable PHP extension                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) The PHP Group                                          |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | https://www.php.net/license/3_01.txt                                 |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Gregory Beaver <cellog@php.net>                             |
16   |          Marcus Boerger <helly@php.net>                              |
17   +----------------------------------------------------------------------+
18 */
19 
20 #include "phar_internal.h"
21 #include "func_interceptors.h"
22 #include "phar_object_arginfo.h"
23 
24 static zend_class_entry *phar_ce_archive;
25 static zend_class_entry *phar_ce_data;
26 static zend_class_entry *phar_ce_PharException;
27 static zend_class_entry *phar_ce_entry;
28 
phar_file_type(HashTable * mimes,char * file,char ** mime_type)29 static int phar_file_type(HashTable *mimes, char *file, char **mime_type) /* {{{ */
30 {
31 	char *ext;
32 	phar_mime_type *mime;
33 	ext = strrchr(file, '.');
34 	if (!ext) {
35 		*mime_type = "text/plain";
36 		/* no file extension = assume text/plain */
37 		return PHAR_MIME_OTHER;
38 	}
39 	++ext;
40 	if (NULL == (mime = zend_hash_str_find_ptr(mimes, ext, strlen(ext)))) {
41 		*mime_type = "application/octet-stream";
42 		return PHAR_MIME_OTHER;
43 	}
44 	*mime_type = mime->mime;
45 	return mime->type;
46 }
47 /* }}} */
48 
phar_mung_server_vars(char * fname,char * entry,size_t entry_len,char * basename,size_t request_uri_len)49 static void phar_mung_server_vars(char *fname, char *entry, size_t entry_len, char *basename, size_t request_uri_len) /* {{{ */
50 {
51 	HashTable *_SERVER;
52 	zval *stuff;
53 	char *path_info;
54 	size_t basename_len = strlen(basename);
55 	size_t code;
56 	zval temp;
57 
58 	/* "tweak" $_SERVER variables requested in earlier call to Phar::mungServer() */
59 	if (Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) == IS_UNDEF) {
60 		return;
61 	}
62 
63 	_SERVER = Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]);
64 
65 	/* PATH_INFO and PATH_TRANSLATED should always be munged */
66 	if (NULL != (stuff = zend_hash_str_find(_SERVER, "PATH_INFO", sizeof("PATH_INFO")-1))) {
67 		path_info = Z_STRVAL_P(stuff);
68 		code = Z_STRLEN_P(stuff);
69 		if (code > (size_t)entry_len && !memcmp(path_info, entry, entry_len)) {
70 			ZVAL_STR(&temp, Z_STR_P(stuff));
71 			ZVAL_STRINGL(stuff, path_info + entry_len, request_uri_len);
72 			zend_hash_str_update(_SERVER, "PHAR_PATH_INFO", sizeof("PHAR_PATH_INFO")-1, &temp);
73 		}
74 	}
75 
76 	if (NULL != (stuff = zend_hash_str_find(_SERVER, "PATH_TRANSLATED", sizeof("PATH_TRANSLATED")-1))) {
77 		zend_string *str = strpprintf(4096, "phar://%s%s", fname, entry);
78 
79 		ZVAL_STR(&temp, Z_STR_P(stuff));
80 		ZVAL_NEW_STR(stuff, str);
81 
82 		zend_hash_str_update(_SERVER, "PHAR_PATH_TRANSLATED", sizeof("PHAR_PATH_TRANSLATED")-1, &temp);
83 	}
84 
85 	if (!PHAR_G(phar_SERVER_mung_list)) {
86 		return;
87 	}
88 
89 	if (PHAR_G(phar_SERVER_mung_list) & PHAR_MUNG_REQUEST_URI) {
90 		if (NULL != (stuff = zend_hash_str_find(_SERVER, "REQUEST_URI", sizeof("REQUEST_URI")-1))) {
91 			path_info = Z_STRVAL_P(stuff);
92 			code = Z_STRLEN_P(stuff);
93 			if (code > basename_len && !memcmp(path_info, basename, basename_len)) {
94 				ZVAL_STR(&temp, Z_STR_P(stuff));
95 				ZVAL_STRINGL(stuff, path_info + basename_len, code - basename_len);
96 				zend_hash_str_update(_SERVER, "PHAR_REQUEST_URI", sizeof("PHAR_REQUEST_URI")-1, &temp);
97 			}
98 		}
99 	}
100 
101 	if (PHAR_G(phar_SERVER_mung_list) & PHAR_MUNG_PHP_SELF) {
102 		if (NULL != (stuff = zend_hash_str_find(_SERVER, "PHP_SELF", sizeof("PHP_SELF")-1))) {
103 			path_info = Z_STRVAL_P(stuff);
104 			code = Z_STRLEN_P(stuff);
105 
106 			if (code > basename_len && !memcmp(path_info, basename, basename_len)) {
107 				ZVAL_STR(&temp, Z_STR_P(stuff));
108 				ZVAL_STRINGL(stuff, path_info + basename_len, code - basename_len);
109 				zend_hash_str_update(_SERVER, "PHAR_PHP_SELF", sizeof("PHAR_PHP_SELF")-1, &temp);
110 			}
111 		}
112 	}
113 
114 	if (PHAR_G(phar_SERVER_mung_list) & PHAR_MUNG_SCRIPT_NAME) {
115 		if (NULL != (stuff = zend_hash_str_find(_SERVER, "SCRIPT_NAME", sizeof("SCRIPT_NAME")-1))) {
116 			ZVAL_STR(&temp, Z_STR_P(stuff));
117 			ZVAL_STRINGL(stuff, entry, entry_len);
118 			zend_hash_str_update(_SERVER, "PHAR_SCRIPT_NAME", sizeof("PHAR_SCRIPT_NAME")-1, &temp);
119 		}
120 	}
121 
122 	if (PHAR_G(phar_SERVER_mung_list) & PHAR_MUNG_SCRIPT_FILENAME) {
123 		if (NULL != (stuff = zend_hash_str_find(_SERVER, "SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME")-1))) {
124 			zend_string *str = strpprintf(4096, "phar://%s%s", fname, entry);
125 
126 			ZVAL_STR(&temp, Z_STR_P(stuff));
127 			ZVAL_NEW_STR(stuff, str);
128 
129 			zend_hash_str_update(_SERVER, "PHAR_SCRIPT_FILENAME", sizeof("PHAR_SCRIPT_FILENAME")-1, &temp);
130 		}
131 	}
132 }
133 /* }}} */
134 
phar_file_action(phar_archive_data * phar,phar_entry_info * info,char * mime_type,int code,char * entry,size_t entry_len,char * arch,char * basename,char * ru,size_t ru_len)135 static int phar_file_action(phar_archive_data *phar, phar_entry_info *info, char *mime_type, int code, char *entry, size_t entry_len, char *arch, char *basename, char *ru, size_t ru_len) /* {{{ */
136 {
137 	char *name = NULL, buf[8192];
138 	const char *cwd;
139 	zend_syntax_highlighter_ini syntax_highlighter_ini;
140 	sapi_header_line ctr = {0};
141 	size_t got;
142 	zval dummy;
143 	size_t name_len;
144 	zend_file_handle file_handle;
145 	zend_op_array *new_op_array;
146 	zval result;
147 	php_stream *fp;
148 	zend_off_t position;
149 
150 	switch (code) {
151 		case PHAR_MIME_PHPS:
152 			efree(basename);
153 			/* highlight source */
154 			if (entry[0] == '/') {
155 				spprintf(&name, 4096, "phar://%s%s", arch, entry);
156 			} else {
157 				spprintf(&name, 4096, "phar://%s/%s", arch, entry);
158 			}
159 			php_get_highlight_struct(&syntax_highlighter_ini);
160 
161 			highlight_file(name, &syntax_highlighter_ini);
162 
163 			efree(name);
164 #ifdef PHP_WIN32
165 			efree(arch);
166 #endif
167 			zend_bailout();
168 		case PHAR_MIME_OTHER:
169 			/* send headers, output file contents */
170 			efree(basename);
171 			ctr.line_len = spprintf((char **) &(ctr.line), 0, "Content-type: %s", mime_type);
172 			sapi_header_op(SAPI_HEADER_REPLACE, &ctr);
173 			efree((void *) ctr.line);
174 			ctr.line_len = spprintf((char **) &(ctr.line), 0, "Content-length: %u", info->uncompressed_filesize);
175 			sapi_header_op(SAPI_HEADER_REPLACE, &ctr);
176 			efree((void *) ctr.line);
177 
178 			if (FAILURE == sapi_send_headers()) {
179 				zend_bailout();
180 			}
181 
182 			/* prepare to output  */
183 			fp = phar_get_efp(info, 1);
184 
185 			if (!fp) {
186 				char *error;
187 				if (!phar_open_jit(phar, info, &error)) {
188 					if (error) {
189 						zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
190 						efree(error);
191 					}
192 					return -1;
193 				}
194 				fp = phar_get_efp(info, 1);
195 			}
196 			position = 0;
197 			phar_seek_efp(info, 0, SEEK_SET, 0, 1);
198 
199 			do {
200 				got = php_stream_read(fp, buf, MIN(8192, info->uncompressed_filesize - position));
201 				if (got > 0) {
202 					PHPWRITE(buf, got);
203 					position += got;
204 					if (position == (zend_off_t) info->uncompressed_filesize) {
205 						break;
206 					}
207 				}
208 			} while (1);
209 
210 			zend_bailout();
211 		case PHAR_MIME_PHP:
212 			if (basename) {
213 				phar_mung_server_vars(arch, entry, entry_len, basename, ru_len);
214 				efree(basename);
215 			}
216 
217 			if (entry[0] == '/') {
218 				name_len = spprintf(&name, 4096, "phar://%s%s", arch, entry);
219 			} else {
220 				name_len = spprintf(&name, 4096, "phar://%s/%s", arch, entry);
221 			}
222 
223 			zend_stream_init_filename(&file_handle, name);
224 
225 			PHAR_G(cwd) = NULL;
226 			PHAR_G(cwd_len) = 0;
227 
228 			ZVAL_NULL(&dummy);
229 			if (zend_hash_str_add(&EG(included_files), name, name_len, &dummy) != NULL) {
230 				if ((cwd = zend_memrchr(entry, '/', entry_len))) {
231 					PHAR_G(cwd_init) = 1;
232 					if (entry == cwd) {
233 						/* root directory */
234 						PHAR_G(cwd_len) = 0;
235 						PHAR_G(cwd) = NULL;
236 					} else if (entry[0] == '/') {
237 						PHAR_G(cwd_len) = (cwd - (entry + 1));
238 						PHAR_G(cwd) = estrndup(entry + 1, PHAR_G(cwd_len));
239 					} else {
240 						PHAR_G(cwd_len) = (cwd - entry);
241 						PHAR_G(cwd) = estrndup(entry, PHAR_G(cwd_len));
242 					}
243 				}
244 
245 				new_op_array = zend_compile_file(&file_handle, ZEND_REQUIRE);
246 
247 				if (!new_op_array) {
248 					zend_hash_str_del(&EG(included_files), name, name_len);
249 				}
250 			} else {
251 				efree(name);
252 				new_op_array = NULL;
253 			}
254 
255 			zend_destroy_file_handle(&file_handle);
256 #ifdef PHP_WIN32
257 			efree(arch);
258 #endif
259 			if (new_op_array) {
260 				ZVAL_UNDEF(&result);
261 
262 				zend_try {
263 					zend_execute(new_op_array, &result);
264 					if (PHAR_G(cwd)) {
265 						efree(PHAR_G(cwd));
266 						PHAR_G(cwd) = NULL;
267 						PHAR_G(cwd_len) = 0;
268 					}
269 
270 					PHAR_G(cwd_init) = 0;
271 					efree(name);
272 					destroy_op_array(new_op_array);
273 					efree(new_op_array);
274 					zval_ptr_dtor(&result);
275 				} zend_catch {
276 					if (PHAR_G(cwd)) {
277 						efree(PHAR_G(cwd));
278 						PHAR_G(cwd) = NULL;
279 						PHAR_G(cwd_len) = 0;
280 					}
281 
282 					PHAR_G(cwd_init) = 0;
283 					efree(name);
284 				} zend_end_try();
285 
286 				zend_bailout();
287 			}
288 
289 			return PHAR_MIME_PHP;
290 	}
291 	return -1;
292 }
293 /* }}} */
294 
phar_do_403(char * entry,size_t entry_len)295 static void phar_do_403(char *entry, size_t entry_len) /* {{{ */
296 {
297 	sapi_header_line ctr = {0};
298 
299 	ctr.response_code = 403;
300 	ctr.line_len = sizeof("HTTP/1.0 403 Access Denied")-1;
301 	ctr.line = "HTTP/1.0 403 Access Denied";
302 	sapi_header_op(SAPI_HEADER_REPLACE, &ctr);
303 	sapi_send_headers();
304 	PHPWRITE("<html>\n <head>\n  <title>Access Denied</title>\n </head>\n <body>\n  <h1>403 - File ", sizeof("<html>\n <head>\n  <title>Access Denied</title>\n </head>\n <body>\n  <h1>403 - File ") - 1);
305 	PHPWRITE("Access Denied</h1>\n </body>\n</html>", sizeof("Access Denied</h1>\n </body>\n</html>") - 1);
306 }
307 /* }}} */
308 
phar_do_404(phar_archive_data * phar,char * fname,size_t fname_len,char * f404,size_t f404_len,char * entry,size_t entry_len)309 static void phar_do_404(phar_archive_data *phar, char *fname, size_t fname_len, char *f404, size_t f404_len, char *entry, size_t entry_len) /* {{{ */
310 {
311 	sapi_header_line ctr = {0};
312 	phar_entry_info	*info;
313 
314 	if (phar && f404_len) {
315 		info = phar_get_entry_info(phar, f404, f404_len, NULL, 1);
316 
317 		if (info) {
318 			phar_file_action(phar, info, "text/html", PHAR_MIME_PHP, f404, f404_len, fname, NULL, NULL, 0);
319 			return;
320 		}
321 	}
322 
323 	ctr.response_code = 404;
324 	ctr.line_len = sizeof("HTTP/1.0 404 Not Found")-1;
325 	ctr.line = "HTTP/1.0 404 Not Found";
326 	sapi_header_op(SAPI_HEADER_REPLACE, &ctr);
327 	sapi_send_headers();
328 	PHPWRITE("<html>\n <head>\n  <title>File Not Found</title>\n </head>\n <body>\n  <h1>404 - File ", sizeof("<html>\n <head>\n  <title>File Not Found</title>\n </head>\n <body>\n  <h1>404 - File ") - 1);
329 	PHPWRITE("Not Found</h1>\n </body>\n</html>",  sizeof("Not Found</h1>\n </body>\n</html>") - 1);
330 }
331 /* }}} */
332 
333 /* post-process REQUEST_URI and retrieve the actual request URI.  This is for
334    cases like http://localhost/blah.phar/path/to/file.php/extra/stuff
335    which calls "blah.phar" file "path/to/file.php" with PATH_INFO "/extra/stuff" */
phar_postprocess_ru_web(char * fname,size_t fname_len,char ** entry,size_t * entry_len,char ** ru,size_t * ru_len)336 static void phar_postprocess_ru_web(char *fname, size_t fname_len, char **entry, size_t *entry_len, char **ru, size_t *ru_len) /* {{{ */
337 {
338 	char *e = *entry + 1, *u = NULL, *u1 = NULL, *saveu = NULL;
339 	size_t e_len = *entry_len - 1, u_len = 0;
340 	phar_archive_data *pphar;
341 
342 	/* we already know we can retrieve the phar if we reach here */
343 	pphar = zend_hash_str_find_ptr(&(PHAR_G(phar_fname_map)), fname, fname_len);
344 
345 	if (!pphar && PHAR_G(manifest_cached)) {
346 		pphar = zend_hash_str_find_ptr(&cached_phars, fname, fname_len);
347 	}
348 
349 	do {
350 		if (zend_hash_str_exists(&(pphar->manifest), e, e_len)) {
351 			if (u) {
352 				u[0] = '/';
353 				*ru = estrndup(u, u_len+1);
354 				++u_len;
355 				u[0] = '\0';
356 			} else {
357 				*ru = NULL;
358 			}
359 			*ru_len = u_len;
360 			*entry_len = e_len + 1;
361 			return;
362 		}
363 
364 		if (u) {
365 			u1 = strrchr(e, '/');
366 			u[0] = '/';
367 			saveu = u;
368 			e_len += u_len + 1;
369 			u = u1;
370 			if (!u) {
371 				return;
372 			}
373 		} else {
374 			u = strrchr(e, '/');
375 			if (!u) {
376 				if (saveu) {
377 					saveu[0] = '/';
378 				}
379 				return;
380 			}
381 		}
382 
383 		u[0] = '\0';
384 		u_len = strlen(u + 1);
385 		e_len -= u_len + 1;
386 	} while (1);
387 }
388 /* }}} */
389 
390 /* {{{ return the name of the currently running phar archive.  If the optional parameter
391  * is set to true, return the phar:// URL to the currently running phar
392  */
PHP_METHOD(Phar,running)393 PHP_METHOD(Phar, running)
394 {
395 	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 		newentry.is_modified = 1;
2290 		newentry.phar = phar;
2291 		newentry.old_flags = newentry.flags & ~PHAR_ENT_COMPRESSION_MASK; /* remove compression from old_flags */
2292 		phar_set_inode(&newentry);
2293 		zend_hash_str_add_mem(&(phar->manifest), newentry.filename, newentry.filename_len, (void*)&newentry, sizeof(phar_entry_info));
2294 		phar_add_virtual_dirs(phar, newentry.filename, newentry.filename_len);
2295 	} ZEND_HASH_FOREACH_END();
2296 
2297 	if ((ret = phar_rename_archive(&phar, ext))) {
2298 		return ret;
2299 	} else {
2300 		if(phar != NULL) {
2301 			zend_hash_destroy(&(phar->manifest));
2302 			zend_hash_destroy(&(phar->mounted_dirs));
2303 			zend_hash_destroy(&(phar->virtual_dirs));
2304 			if (phar->fp) {
2305 				php_stream_close(phar->fp);
2306 			}
2307 			efree(phar->fname);
2308 			efree(phar);
2309 		}
2310 		return NULL;
2311 	}
2312 }
2313 /* }}} */
2314 
2315 /* {{{ Convert a phar.tar or phar.zip archive to the phar file format. The
2316  * optional parameter allows the user to determine the new
2317  * filename extension (default is phar).
2318  */
PHP_METHOD(Phar,convertToExecutable)2319 PHP_METHOD(Phar, convertToExecutable)
2320 {
2321 	char *ext = NULL;
2322 	int is_data;
2323 	size_t ext_len = 0;
2324 	uint32_t flags;
2325 	zend_object *ret;
2326 	zend_long format, method;
2327 	bool format_is_null = 1, method_is_null = 1;
2328 
2329 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l!l!s!", &format, &format_is_null, &method, &method_is_null, &ext, &ext_len) == FAILURE) {
2330 		RETURN_THROWS();
2331 	}
2332 
2333 	PHAR_ARCHIVE_OBJECT();
2334 
2335 	if (PHAR_G(readonly)) {
2336 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2337 			"Cannot write out executable phar archive, phar is read-only");
2338 		RETURN_THROWS();
2339 	}
2340 
2341 	if (format_is_null) {
2342 		format = PHAR_FORMAT_SAME;
2343 	}
2344 	switch (format) {
2345 		case 9021976: /* Retained for BC */
2346 		case PHAR_FORMAT_SAME:
2347 			/* by default, use the existing format */
2348 			if (phar_obj->archive->is_tar) {
2349 				format = PHAR_FORMAT_TAR;
2350 			} else if (phar_obj->archive->is_zip) {
2351 				format = PHAR_FORMAT_ZIP;
2352 			} else {
2353 				format = PHAR_FORMAT_PHAR;
2354 			}
2355 			break;
2356 		case PHAR_FORMAT_PHAR:
2357 		case PHAR_FORMAT_TAR:
2358 		case PHAR_FORMAT_ZIP:
2359 			break;
2360 		default:
2361 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2362 				"Unknown file format specified, please pass one of Phar::PHAR, Phar::TAR or Phar::ZIP");
2363 			RETURN_THROWS();
2364 	}
2365 
2366 	if (method_is_null) {
2367 		flags = phar_obj->archive->flags & PHAR_FILE_COMPRESSION_MASK;
2368 	} else {
2369 		switch (method) {
2370 		case 9021976: /* Retained for BC */
2371 			flags = phar_obj->archive->flags & PHAR_FILE_COMPRESSION_MASK;
2372 			break;
2373 		case 0:
2374 			flags = PHAR_FILE_COMPRESSED_NONE;
2375 			break;
2376 		case PHAR_ENT_COMPRESSED_GZ:
2377 			if (format == PHAR_FORMAT_ZIP) {
2378 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2379 					"Cannot compress entire archive with gzip, zip archives do not support whole-archive compression");
2380 				RETURN_THROWS();
2381 			}
2382 
2383 			if (!PHAR_G(has_zlib)) {
2384 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2385 					"Cannot compress entire archive with gzip, enable ext/zlib in php.ini");
2386 				RETURN_THROWS();
2387 			}
2388 
2389 			flags = PHAR_FILE_COMPRESSED_GZ;
2390 			break;
2391 		case PHAR_ENT_COMPRESSED_BZ2:
2392 			if (format == PHAR_FORMAT_ZIP) {
2393 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2394 					"Cannot compress entire archive with bz2, zip archives do not support whole-archive compression");
2395 				RETURN_THROWS();
2396 			}
2397 
2398 			if (!PHAR_G(has_bz2)) {
2399 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2400 					"Cannot compress entire archive with bz2, enable ext/bz2 in php.ini");
2401 				RETURN_THROWS();
2402 			}
2403 
2404 			flags = PHAR_FILE_COMPRESSED_BZ2;
2405 			break;
2406 		default:
2407 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2408 				"Unknown compression specified, please pass one of Phar::GZ or Phar::BZ2");
2409 			RETURN_THROWS();
2410 		}
2411 	}
2412 
2413 	is_data = phar_obj->archive->is_data;
2414 	phar_obj->archive->is_data = 0;
2415 	ret = phar_convert_to_other(phar_obj->archive, format, ext, flags);
2416 	phar_obj->archive->is_data = is_data;
2417 
2418 	if (ret) {
2419 		RETURN_OBJ(ret);
2420 	} else {
2421 		RETURN_NULL();
2422 	}
2423 }
2424 /* }}} */
2425 
2426 /* {{{ Convert an archive to a non-executable .tar or .zip.
2427  * The optional parameter allows the user to determine the new
2428  * filename extension (default is .zip or .tar).
2429  */
PHP_METHOD(Phar,convertToData)2430 PHP_METHOD(Phar, convertToData)
2431 {
2432 	char *ext = NULL;
2433 	int is_data;
2434 	size_t ext_len = 0;
2435 	uint32_t flags;
2436 	zend_object *ret;
2437 	zend_long format, method;
2438 	bool format_is_null = 1, method_is_null = 1;
2439 
2440 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l!l!s!", &format, &format_is_null, &method, &method_is_null, &ext, &ext_len) == FAILURE) {
2441 		RETURN_THROWS();
2442 	}
2443 
2444 	PHAR_ARCHIVE_OBJECT();
2445 
2446 	if (format_is_null) {
2447 		format = PHAR_FORMAT_SAME;
2448 	}
2449 	switch (format) {
2450 		case 9021976: /* Retained for BC */
2451 		case PHAR_FORMAT_SAME:
2452 			/* by default, use the existing format */
2453 			if (phar_obj->archive->is_tar) {
2454 				format = PHAR_FORMAT_TAR;
2455 			} else if (phar_obj->archive->is_zip) {
2456 				format = PHAR_FORMAT_ZIP;
2457 			} else {
2458 				zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2459 					"Cannot write out data phar archive, use Phar::TAR or Phar::ZIP");
2460 				RETURN_THROWS();
2461 			}
2462 			break;
2463 		case PHAR_FORMAT_PHAR:
2464 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2465 				"Cannot write out data phar archive, use Phar::TAR or Phar::ZIP");
2466 			RETURN_THROWS();
2467 		case PHAR_FORMAT_TAR:
2468 		case PHAR_FORMAT_ZIP:
2469 			break;
2470 		default:
2471 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2472 				"Unknown file format specified, please pass one of Phar::TAR or Phar::ZIP");
2473 			RETURN_THROWS();
2474 	}
2475 
2476 	if (method_is_null) {
2477 		flags = phar_obj->archive->flags & PHAR_FILE_COMPRESSION_MASK;
2478 	} else  {
2479 		switch (method) {
2480 		case 9021976: /* Retained for BC */
2481 			flags = phar_obj->archive->flags & PHAR_FILE_COMPRESSION_MASK;
2482 			break;
2483 		case 0:
2484 			flags = PHAR_FILE_COMPRESSED_NONE;
2485 			break;
2486 		case PHAR_ENT_COMPRESSED_GZ:
2487 			if (format == PHAR_FORMAT_ZIP) {
2488 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2489 					"Cannot compress entire archive with gzip, zip archives do not support whole-archive compression");
2490 				RETURN_THROWS();
2491 			}
2492 
2493 			if (!PHAR_G(has_zlib)) {
2494 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2495 					"Cannot compress entire archive with gzip, enable ext/zlib in php.ini");
2496 				RETURN_THROWS();
2497 			}
2498 
2499 			flags = PHAR_FILE_COMPRESSED_GZ;
2500 			break;
2501 		case PHAR_ENT_COMPRESSED_BZ2:
2502 			if (format == PHAR_FORMAT_ZIP) {
2503 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2504 					"Cannot compress entire archive with bz2, zip archives do not support whole-archive compression");
2505 				RETURN_THROWS();
2506 			}
2507 
2508 			if (!PHAR_G(has_bz2)) {
2509 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2510 					"Cannot compress entire archive with bz2, enable ext/bz2 in php.ini");
2511 				RETURN_THROWS();
2512 			}
2513 
2514 			flags = PHAR_FILE_COMPRESSED_BZ2;
2515 			break;
2516 		default:
2517 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
2518 				"Unknown compression specified, please pass one of Phar::GZ or Phar::BZ2");
2519 			RETURN_THROWS();
2520 		}
2521 	}
2522 
2523 	is_data = phar_obj->archive->is_data;
2524 	phar_obj->archive->is_data = 1;
2525 	ret = phar_convert_to_other(phar_obj->archive, (int)format, ext, flags);
2526 	phar_obj->archive->is_data = is_data;
2527 
2528 	if (ret) {
2529 		RETURN_OBJ(ret);
2530 	} else {
2531 		RETURN_NULL();
2532 	}
2533 }
2534 /* }}} */
2535 
2536 /* {{{ Returns Phar::GZ or PHAR::BZ2 if the entire archive is compressed
2537  * (.tar.gz/tar.bz2 and so on), or FALSE otherwise.
2538  */
PHP_METHOD(Phar,isCompressed)2539 PHP_METHOD(Phar, isCompressed)
2540 {
2541 	if (zend_parse_parameters_none() == FAILURE) {
2542 		RETURN_THROWS();
2543 	}
2544 
2545 	PHAR_ARCHIVE_OBJECT();
2546 
2547 	if (phar_obj->archive->flags & PHAR_FILE_COMPRESSED_GZ) {
2548 		RETURN_LONG(PHAR_ENT_COMPRESSED_GZ);
2549 	}
2550 
2551 	if (phar_obj->archive->flags & PHAR_FILE_COMPRESSED_BZ2) {
2552 		RETURN_LONG(PHAR_ENT_COMPRESSED_BZ2);
2553 	}
2554 
2555 	RETURN_FALSE;
2556 }
2557 /* }}} */
2558 
2559 /* {{{ Returns true if phar.readonly=0 or phar is a PharData AND the actual file is writable. */
PHP_METHOD(Phar,isWritable)2560 PHP_METHOD(Phar, isWritable)
2561 {
2562 	php_stream_statbuf ssb;
2563 
2564 	if (zend_parse_parameters_none() == FAILURE) {
2565 		RETURN_THROWS();
2566 	}
2567 
2568 	PHAR_ARCHIVE_OBJECT();
2569 
2570 	if (!phar_obj->archive->is_writeable) {
2571 		RETURN_FALSE;
2572 	}
2573 
2574 	if (SUCCESS != php_stream_stat_path(phar_obj->archive->fname, &ssb)) {
2575 		if (phar_obj->archive->is_brandnew) {
2576 			/* assume it works if the file doesn't exist yet */
2577 			RETURN_TRUE;
2578 		}
2579 		RETURN_FALSE;
2580 	}
2581 
2582 	RETURN_BOOL((ssb.sb.st_mode & (S_IWOTH | S_IWGRP | S_IWUSR)) != 0);
2583 }
2584 /* }}} */
2585 
2586 /* {{{ Deletes a named file within the archive. */
PHP_METHOD(Phar,delete)2587 PHP_METHOD(Phar, delete)
2588 {
2589 	char *fname;
2590 	size_t fname_len;
2591 	char *error;
2592 	phar_entry_info *entry;
2593 
2594 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &fname, &fname_len) == FAILURE) {
2595 		RETURN_THROWS();
2596 	}
2597 
2598 	PHAR_ARCHIVE_OBJECT();
2599 
2600 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
2601 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2602 			"Cannot write out phar archive, phar is read-only");
2603 		RETURN_THROWS();
2604 	}
2605 
2606 	if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
2607 		zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
2608 		RETURN_THROWS();
2609 	}
2610 	if (zend_hash_str_exists(&phar_obj->archive->manifest, fname, (uint32_t) fname_len)) {
2611 		if (NULL != (entry = zend_hash_str_find_ptr(&phar_obj->archive->manifest, fname, (uint32_t) fname_len))) {
2612 			if (entry->is_deleted) {
2613 				/* entry is deleted, but has not been flushed to disk yet */
2614 				RETURN_TRUE;
2615 			} else {
2616 				entry->is_deleted = 1;
2617 				entry->is_modified = 1;
2618 				phar_obj->archive->is_modified = 1;
2619 			}
2620 		}
2621 	} else {
2622 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s does not exist and cannot be deleted", fname);
2623 		RETURN_THROWS();
2624 	}
2625 
2626 	phar_flush(phar_obj->archive, NULL, 0, 0, &error);
2627 	if (error) {
2628 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
2629 		efree(error);
2630 		RETURN_THROWS();
2631 	}
2632 
2633 	RETURN_TRUE;
2634 }
2635 /* }}} */
2636 
2637 /* {{{ Returns the alias for the Phar or NULL. */
PHP_METHOD(Phar,getAlias)2638 PHP_METHOD(Phar, getAlias)
2639 {
2640 	if (zend_parse_parameters_none() == FAILURE) {
2641 		RETURN_THROWS();
2642 	}
2643 
2644 	PHAR_ARCHIVE_OBJECT();
2645 
2646 	if (phar_obj->archive->alias && phar_obj->archive->alias != phar_obj->archive->fname) {
2647 		RETURN_STRINGL(phar_obj->archive->alias, phar_obj->archive->alias_len);
2648 	}
2649 }
2650 /* }}} */
2651 
2652 /* {{{ Returns the real path to the phar archive on disk */
PHP_METHOD(Phar,getPath)2653 PHP_METHOD(Phar, getPath)
2654 {
2655 	if (zend_parse_parameters_none() == FAILURE) {
2656 		RETURN_THROWS();
2657 	}
2658 
2659 	PHAR_ARCHIVE_OBJECT();
2660 
2661 	RETURN_STRINGL(phar_obj->archive->fname, phar_obj->archive->fname_len);
2662 }
2663 /* }}} */
2664 
2665 /* {{{ Sets the alias for a Phar archive. The default value is the full path
2666  * to the archive.
2667  */
PHP_METHOD(Phar,setAlias)2668 PHP_METHOD(Phar, setAlias)
2669 {
2670 	char *alias, *error, *oldalias;
2671 	phar_archive_data *fd_ptr;
2672 	size_t alias_len, oldalias_len;
2673 	int old_temp, readd = 0;
2674 
2675 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &alias, &alias_len) == FAILURE) {
2676 		RETURN_THROWS();
2677 	}
2678 
2679 	PHAR_ARCHIVE_OBJECT();
2680 
2681 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
2682 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2683 			"Cannot write out phar archive, phar is read-only");
2684 		RETURN_THROWS();
2685 	}
2686 
2687 	/* invalidate phar cache */
2688 	PHAR_G(last_phar) = NULL;
2689 	PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
2690 
2691 	if (phar_obj->archive->is_data) {
2692 		if (phar_obj->archive->is_tar) {
2693 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2694 				"A Phar alias cannot be set in a plain tar archive");
2695 		} else {
2696 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2697 				"A Phar alias cannot be set in a plain zip archive");
2698 		}
2699 		RETURN_THROWS();
2700 	}
2701 
2702 	if (alias_len == phar_obj->archive->alias_len && memcmp(phar_obj->archive->alias, alias, alias_len) == 0) {
2703 		RETURN_TRUE;
2704 	}
2705 	if (alias_len && NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len))) {
2706 		spprintf(&error, 0, "alias \"%s\" is already used for archive \"%s\" and cannot be used for other archives", alias, fd_ptr->fname);
2707 		if (SUCCESS == phar_free_alias(fd_ptr, alias, alias_len)) {
2708 			efree(error);
2709 			goto valid_alias;
2710 		}
2711 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
2712 		efree(error);
2713 		RETURN_THROWS();
2714 	}
2715 	if (!phar_validate_alias(alias, alias_len)) {
2716 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2717 			"Invalid alias \"%s\" specified for phar \"%s\"", alias, phar_obj->archive->fname);
2718 		RETURN_THROWS();
2719 	}
2720 valid_alias:
2721 	if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
2722 		zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
2723 		RETURN_THROWS();
2724 	}
2725 	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))) {
2726 		zend_hash_str_del(&(PHAR_G(phar_alias_map)), phar_obj->archive->alias, phar_obj->archive->alias_len);
2727 		readd = 1;
2728 	}
2729 
2730 	oldalias = phar_obj->archive->alias;
2731 	oldalias_len = phar_obj->archive->alias_len;
2732 	old_temp = phar_obj->archive->is_temporary_alias;
2733 
2734 	if (alias_len) {
2735 		phar_obj->archive->alias = estrndup(alias, alias_len);
2736 	} else {
2737 		phar_obj->archive->alias = NULL;
2738 	}
2739 
2740 	phar_obj->archive->alias_len = alias_len;
2741 	phar_obj->archive->is_temporary_alias = 0;
2742 	phar_flush(phar_obj->archive, NULL, 0, 0, &error);
2743 
2744 	if (error) {
2745 		phar_obj->archive->alias = oldalias;
2746 		phar_obj->archive->alias_len = oldalias_len;
2747 		phar_obj->archive->is_temporary_alias = old_temp;
2748 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
2749 		if (readd) {
2750 			zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), oldalias, oldalias_len, phar_obj->archive);
2751 		}
2752 		efree(error);
2753 		RETURN_THROWS();
2754 	}
2755 
2756 	zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len, phar_obj->archive);
2757 
2758 	if (oldalias) {
2759 		efree(oldalias);
2760 	}
2761 
2762 	RETURN_TRUE;
2763 }
2764 /* }}} */
2765 
2766 /* {{{ Return version info of Phar archive */
PHP_METHOD(Phar,getVersion)2767 PHP_METHOD(Phar, getVersion)
2768 {
2769 	if (zend_parse_parameters_none() == FAILURE) {
2770 		RETURN_THROWS();
2771 	}
2772 
2773 	PHAR_ARCHIVE_OBJECT();
2774 
2775 	RETURN_STRING(phar_obj->archive->version);
2776 }
2777 /* }}} */
2778 
2779 /* {{{ Do not flush a writeable phar (save its contents) until explicitly requested */
PHP_METHOD(Phar,startBuffering)2780 PHP_METHOD(Phar, startBuffering)
2781 {
2782 	if (zend_parse_parameters_none() == FAILURE) {
2783 		RETURN_THROWS();
2784 	}
2785 
2786 	PHAR_ARCHIVE_OBJECT();
2787 
2788 	phar_obj->archive->donotflush = 1;
2789 }
2790 /* }}} */
2791 
2792 /* {{{ Returns whether write operations are flushing to disk immediately. */
PHP_METHOD(Phar,isBuffering)2793 PHP_METHOD(Phar, isBuffering)
2794 {
2795 	if (zend_parse_parameters_none() == FAILURE) {
2796 		RETURN_THROWS();
2797 	}
2798 
2799 	PHAR_ARCHIVE_OBJECT();
2800 
2801 	RETURN_BOOL(phar_obj->archive->donotflush);
2802 }
2803 /* }}} */
2804 
2805 /* {{{ Saves the contents of a modified archive to disk. */
PHP_METHOD(Phar,stopBuffering)2806 PHP_METHOD(Phar, stopBuffering)
2807 {
2808 	char *error;
2809 
2810 	if (zend_parse_parameters_none() == FAILURE) {
2811 		RETURN_THROWS();
2812 	}
2813 
2814 	PHAR_ARCHIVE_OBJECT();
2815 
2816 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
2817 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2818 			"Cannot write out phar archive, phar is read-only");
2819 		RETURN_THROWS();
2820 	}
2821 
2822 	phar_obj->archive->donotflush = 0;
2823 	phar_flush(phar_obj->archive, 0, 0, 0, &error);
2824 
2825 	if (error) {
2826 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
2827 		efree(error);
2828 	}
2829 }
2830 /* }}} */
2831 
2832 /* {{{ Change the stub in a phar, phar.tar or phar.zip archive to something other
2833  * than the default. The stub *must* end with a call to __HALT_COMPILER().
2834  */
PHP_METHOD(Phar,setStub)2835 PHP_METHOD(Phar, setStub)
2836 {
2837 	zval *zstub;
2838 	char *stub, *error;
2839 	size_t stub_len;
2840 	zend_long len = -1;
2841 	php_stream *stream;
2842 
2843 	PHAR_ARCHIVE_OBJECT();
2844 
2845 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
2846 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2847 			"Cannot change stub, phar is read-only");
2848 		RETURN_THROWS();
2849 	}
2850 
2851 	if (phar_obj->archive->is_data) {
2852 		if (phar_obj->archive->is_tar) {
2853 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2854 				"A Phar stub cannot be set in a plain tar archive");
2855 		} else {
2856 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2857 				"A Phar stub cannot be set in a plain zip archive");
2858 		}
2859 		RETURN_THROWS();
2860 	}
2861 
2862 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r|l", &zstub, &len) == SUCCESS) {
2863 		if ((php_stream_from_zval_no_verify(stream, zstub)) != NULL) {
2864 			if (len > 0) {
2865 				len = -len;
2866 			} else {
2867 				len = -1;
2868 			}
2869 			if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
2870 				zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
2871 				RETURN_THROWS();
2872 			}
2873 			phar_flush(phar_obj->archive, (char *) zstub, len, 0, &error);
2874 			if (error) {
2875 				zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
2876 				efree(error);
2877 			}
2878 			RETURN_TRUE;
2879 		} else {
2880 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2881 				"Cannot change stub, unable to read from input stream");
2882 		}
2883 	} else if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &stub, &stub_len) == SUCCESS) {
2884 		if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
2885 			zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
2886 			RETURN_THROWS();
2887 		}
2888 		phar_flush(phar_obj->archive, stub, stub_len, 0, &error);
2889 
2890 		if (error) {
2891 			zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
2892 			efree(error);
2893 			RETURN_THROWS();
2894 		}
2895 
2896 		RETURN_TRUE;
2897 	}
2898 
2899 	RETURN_THROWS();
2900 }
2901 /* }}} */
2902 
2903 /* {{{ In a pure phar archive, sets a stub that can be used to run the archive
2904  * regardless of whether the phar extension is available. The first parameter
2905  * is the CLI startup filename, which defaults to "index.php". The second
2906  * parameter is the web startup filename and also defaults to "index.php"
2907  * (falling back to CLI behaviour).
2908  * Both parameters are optional.
2909  * In a phar.zip or phar.tar archive, the default stub is used only to
2910  * identify the archive to the extension as a Phar object. This allows the
2911  * extension to treat phar.zip and phar.tar types as honorary phars. Since
2912  * files cannot be loaded via this kind of stub, no parameters are accepted
2913  * when the Phar object is zip- or tar-based.
2914  */
PHP_METHOD(Phar,setDefaultStub)2915 PHP_METHOD(Phar, setDefaultStub)
2916 {
2917 	char *index = NULL, *webindex = NULL, *error = NULL;
2918 	zend_string *stub = NULL;
2919 	size_t index_len = 0, webindex_len = 0;
2920 	int created_stub = 0;
2921 
2922 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!s!", &index, &index_len, &webindex, &webindex_len) == FAILURE) {
2923 		RETURN_THROWS();
2924 	}
2925 
2926 	PHAR_ARCHIVE_OBJECT();
2927 
2928 	if (phar_obj->archive->is_data) {
2929 		if (phar_obj->archive->is_tar) {
2930 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2931 				"A Phar stub cannot be set in a plain tar archive");
2932 		} else {
2933 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2934 				"A Phar stub cannot be set in a plain zip archive");
2935 		}
2936 		RETURN_THROWS();
2937 	}
2938 
2939 	if ((index || webindex) && (phar_obj->archive->is_tar || phar_obj->archive->is_zip)) {
2940 		zend_argument_value_error(index ? 1 : 2, "must be null for a tar- or zip-based phar stub, string given");
2941 		RETURN_THROWS();
2942 	}
2943 
2944 	if (PHAR_G(readonly)) {
2945 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
2946 			"Cannot change stub: phar.readonly=1");
2947 		RETURN_THROWS();
2948 	}
2949 
2950 	if (!phar_obj->archive->is_tar && !phar_obj->archive->is_zip) {
2951 		stub = phar_create_default_stub(index, webindex, &error);
2952 
2953 		if (error) {
2954 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "%s", error);
2955 			efree(error);
2956 			if (stub) {
2957 				zend_string_free(stub);
2958 			}
2959 			RETURN_THROWS();
2960 		}
2961 
2962 		created_stub = 1;
2963 	}
2964 
2965 	if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
2966 		zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
2967 		RETURN_THROWS();
2968 	}
2969 	phar_flush(phar_obj->archive, stub ? ZSTR_VAL(stub) : 0, stub ? ZSTR_LEN(stub) : 0, 1, &error);
2970 
2971 	if (created_stub) {
2972 		zend_string_free(stub);
2973 	}
2974 
2975 	if (error) {
2976 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
2977 		efree(error);
2978 		RETURN_THROWS();
2979 	}
2980 
2981 	RETURN_TRUE;
2982 }
2983 /* }}} */
2984 
2985 /* {{{ Sets the signature algorithm for a phar and applies it. The signature
2986  * algorithm must be one of Phar::MD5, Phar::SHA1, Phar::SHA256,
2987  * Phar::SHA512, or Phar::OPENSSL. Note that zip- based phar archives
2988  * cannot support signatures.
2989  */
PHP_METHOD(Phar,setSignatureAlgorithm)2990 PHP_METHOD(Phar, setSignatureAlgorithm)
2991 {
2992 	zend_long algo;
2993 	char *error, *key = NULL;
2994 	size_t key_len = 0;
2995 
2996 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|s!", &algo, &key, &key_len) != SUCCESS) {
2997 		RETURN_THROWS();
2998 	}
2999 
3000 	PHAR_ARCHIVE_OBJECT();
3001 
3002 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
3003 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3004 			"Cannot set signature algorithm, phar is read-only");
3005 		RETURN_THROWS();
3006 	}
3007 
3008 	switch (algo) {
3009 		case PHAR_SIG_SHA256:
3010 		case PHAR_SIG_SHA512:
3011 		case PHAR_SIG_MD5:
3012 		case PHAR_SIG_SHA1:
3013 		case PHAR_SIG_OPENSSL:
3014 		case PHAR_SIG_OPENSSL_SHA256:
3015 		case PHAR_SIG_OPENSSL_SHA512:
3016 			if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
3017 				zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
3018 				RETURN_THROWS();
3019 			}
3020 			phar_obj->archive->sig_flags = (php_uint32)algo;
3021 			phar_obj->archive->is_modified = 1;
3022 			PHAR_G(openssl_privatekey) = key;
3023 			PHAR_G(openssl_privatekey_len) = key_len;
3024 
3025 			phar_flush(phar_obj->archive, 0, 0, 0, &error);
3026 			if (error) {
3027 				zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
3028 				efree(error);
3029 			}
3030 			break;
3031 		default:
3032 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3033 				"Unknown signature algorithm specified");
3034 	}
3035 }
3036 /* }}} */
3037 
3038 /* {{{ Returns a hash signature, or FALSE if the archive is unsigned. */
PHP_METHOD(Phar,getSignature)3039 PHP_METHOD(Phar, getSignature)
3040 {
3041 	if (zend_parse_parameters_none() == FAILURE) {
3042 		RETURN_THROWS();
3043 	}
3044 
3045 	PHAR_ARCHIVE_OBJECT();
3046 
3047 	if (phar_obj->archive->signature) {
3048 		zend_string *unknown;
3049 
3050 		array_init(return_value);
3051 		add_assoc_stringl(return_value, "hash", phar_obj->archive->signature, phar_obj->archive->sig_len);
3052 		switch(phar_obj->archive->sig_flags) {
3053 			case PHAR_SIG_MD5:
3054 				add_assoc_string(return_value, "hash_type", "MD5");
3055 				break;
3056 			case PHAR_SIG_SHA1:
3057 				add_assoc_string(return_value, "hash_type", "SHA-1");
3058 				break;
3059 			case PHAR_SIG_SHA256:
3060 				add_assoc_string(return_value, "hash_type", "SHA-256");
3061 				break;
3062 			case PHAR_SIG_SHA512:
3063 				add_assoc_string(return_value, "hash_type", "SHA-512");
3064 				break;
3065 			case PHAR_SIG_OPENSSL:
3066 				add_assoc_string(return_value, "hash_type", "OpenSSL");
3067 				break;
3068 			case PHAR_SIG_OPENSSL_SHA256:
3069 				add_assoc_string(return_value, "hash_type", "OpenSSL_SHA256");
3070 				break;
3071 			case PHAR_SIG_OPENSSL_SHA512:
3072 				add_assoc_string(return_value, "hash_type", "OpenSSL_SHA512");
3073 				break;
3074 			default:
3075 				unknown = strpprintf(0, "Unknown (%u)", phar_obj->archive->sig_flags);
3076 				add_assoc_str(return_value, "hash_type", unknown);
3077 				break;
3078 		}
3079 	} else {
3080 		RETURN_FALSE;
3081 	}
3082 }
3083 /* }}} */
3084 
3085 /* {{{ Return whether phar was modified */
PHP_METHOD(Phar,getModified)3086 PHP_METHOD(Phar, getModified)
3087 {
3088 	if (zend_parse_parameters_none() == FAILURE) {
3089 		RETURN_THROWS();
3090 	}
3091 
3092 	PHAR_ARCHIVE_OBJECT();
3093 
3094 	RETURN_BOOL(phar_obj->archive->is_modified);
3095 }
3096 /* }}} */
3097 
phar_set_compression(zval * zv,void * argument)3098 static int phar_set_compression(zval *zv, void *argument) /* {{{ */
3099 {
3100 	phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv);
3101 	uint32_t compress = *(uint32_t *)argument;
3102 
3103 	if (entry->is_deleted) {
3104 		return ZEND_HASH_APPLY_KEEP;
3105 	}
3106 
3107 	entry->old_flags = entry->flags;
3108 	entry->flags &= ~PHAR_ENT_COMPRESSION_MASK;
3109 	entry->flags |= compress;
3110 	entry->is_modified = 1;
3111 	return ZEND_HASH_APPLY_KEEP;
3112 }
3113 /* }}} */
3114 
phar_test_compression(zval * zv,void * argument)3115 static int phar_test_compression(zval *zv, void *argument) /* {{{ */
3116 {
3117 	phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv);
3118 
3119 	if (entry->is_deleted) {
3120 		return ZEND_HASH_APPLY_KEEP;
3121 	}
3122 
3123 	if (!PHAR_G(has_bz2)) {
3124 		if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
3125 			*(int *) argument = 0;
3126 		}
3127 	}
3128 
3129 	if (!PHAR_G(has_zlib)) {
3130 		if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
3131 			*(int *) argument = 0;
3132 		}
3133 	}
3134 
3135 	return ZEND_HASH_APPLY_KEEP;
3136 }
3137 /* }}} */
3138 
pharobj_set_compression(HashTable * manifest,uint32_t compress)3139 static void pharobj_set_compression(HashTable *manifest, uint32_t compress) /* {{{ */
3140 {
3141 	zend_hash_apply_with_argument(manifest, phar_set_compression, &compress);
3142 }
3143 /* }}} */
3144 
pharobj_cancompress(HashTable * manifest)3145 static int pharobj_cancompress(HashTable *manifest) /* {{{ */
3146 {
3147 	int test;
3148 
3149 	test = 1;
3150 	zend_hash_apply_with_argument(manifest, phar_test_compression, &test);
3151 	return test;
3152 }
3153 /* }}} */
3154 
3155 /* {{{ Compress a .tar, or .phar.tar with whole-file compression
3156  * The parameter can be one of Phar::GZ or Phar::BZ2 to specify
3157  * the kind of compression desired
3158  */
PHP_METHOD(Phar,compress)3159 PHP_METHOD(Phar, compress)
3160 {
3161 	zend_long method;
3162 	char *ext = NULL;
3163 	size_t ext_len = 0;
3164 	uint32_t flags;
3165 	zend_object *ret;
3166 
3167 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|s!", &method, &ext, &ext_len) == FAILURE) {
3168 		RETURN_THROWS();
3169 	}
3170 
3171 	PHAR_ARCHIVE_OBJECT();
3172 
3173 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
3174 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3175 			"Cannot compress phar archive, phar is read-only");
3176 		RETURN_THROWS();
3177 	}
3178 
3179 	if (phar_obj->archive->is_zip) {
3180 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3181 			"Cannot compress zip-based archives with whole-archive compression");
3182 		RETURN_THROWS();
3183 	}
3184 
3185 	switch (method) {
3186 		case 0:
3187 			flags = PHAR_FILE_COMPRESSED_NONE;
3188 			break;
3189 		case PHAR_ENT_COMPRESSED_GZ:
3190 			if (!PHAR_G(has_zlib)) {
3191 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3192 					"Cannot compress entire archive with gzip, enable ext/zlib in php.ini");
3193 				RETURN_THROWS();
3194 			}
3195 			flags = PHAR_FILE_COMPRESSED_GZ;
3196 			break;
3197 
3198 		case PHAR_ENT_COMPRESSED_BZ2:
3199 			if (!PHAR_G(has_bz2)) {
3200 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3201 					"Cannot compress entire archive with bz2, enable ext/bz2 in php.ini");
3202 				return;
3203 			}
3204 			flags = PHAR_FILE_COMPRESSED_BZ2;
3205 			break;
3206 		default:
3207 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3208 				"Unknown compression specified, please pass one of Phar::GZ or Phar::BZ2");
3209 			RETURN_THROWS();
3210 	}
3211 
3212 	if (phar_obj->archive->is_tar) {
3213 		ret = phar_convert_to_other(phar_obj->archive, PHAR_FORMAT_TAR, ext, flags);
3214 	} else {
3215 		ret = phar_convert_to_other(phar_obj->archive, PHAR_FORMAT_PHAR, ext, flags);
3216 	}
3217 
3218 	if (ret) {
3219 		RETURN_OBJ(ret);
3220 	} else {
3221 		RETURN_NULL();
3222 	}
3223 }
3224 /* }}} */
3225 
3226 /* {{{ Decompress a .tar, or .phar.tar with whole-file compression */
PHP_METHOD(Phar,decompress)3227 PHP_METHOD(Phar, decompress)
3228 {
3229 	char *ext = NULL;
3230 	size_t ext_len = 0;
3231 	zend_object *ret;
3232 
3233 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &ext, &ext_len) == FAILURE) {
3234 		RETURN_THROWS();
3235 	}
3236 
3237 	PHAR_ARCHIVE_OBJECT();
3238 
3239 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
3240 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3241 			"Cannot decompress phar archive, phar is read-only");
3242 		RETURN_THROWS();
3243 	}
3244 
3245 	if (phar_obj->archive->is_zip) {
3246 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3247 			"Cannot decompress zip-based archives with whole-archive compression");
3248 		RETURN_THROWS();
3249 	}
3250 
3251 	if (phar_obj->archive->is_tar) {
3252 		ret = phar_convert_to_other(phar_obj->archive, PHAR_FORMAT_TAR, ext, PHAR_FILE_COMPRESSED_NONE);
3253 	} else {
3254 		ret = phar_convert_to_other(phar_obj->archive, PHAR_FORMAT_PHAR, ext, PHAR_FILE_COMPRESSED_NONE);
3255 	}
3256 
3257 	if (ret) {
3258 		RETURN_OBJ(ret);
3259 	} else {
3260 		RETURN_NULL();
3261 	}
3262 }
3263 /* }}} */
3264 
3265 /* {{{ Compress all files within a phar or zip archive using the specified compression
3266  * The parameter can be one of Phar::GZ or Phar::BZ2 to specify
3267  * the kind of compression desired
3268  */
PHP_METHOD(Phar,compressFiles)3269 PHP_METHOD(Phar, compressFiles)
3270 {
3271 	char *error;
3272 	uint32_t flags;
3273 	zend_long method;
3274 
3275 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &method) == FAILURE) {
3276 		RETURN_THROWS();
3277 	}
3278 
3279 	PHAR_ARCHIVE_OBJECT();
3280 
3281 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
3282 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3283 			"Phar is readonly, cannot change compression");
3284 		RETURN_THROWS();
3285 	}
3286 
3287 	switch (method) {
3288 		case PHAR_ENT_COMPRESSED_GZ:
3289 			if (!PHAR_G(has_zlib)) {
3290 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3291 					"Cannot compress files within archive with gzip, enable ext/zlib in php.ini");
3292 				RETURN_THROWS();
3293 			}
3294 			flags = PHAR_ENT_COMPRESSED_GZ;
3295 			break;
3296 
3297 		case PHAR_ENT_COMPRESSED_BZ2:
3298 			if (!PHAR_G(has_bz2)) {
3299 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3300 					"Cannot compress files within archive with bz2, enable ext/bz2 in php.ini");
3301 				RETURN_THROWS();
3302 			}
3303 			flags = PHAR_ENT_COMPRESSED_BZ2;
3304 			break;
3305 		default:
3306 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3307 				"Unknown compression specified, please pass one of Phar::GZ or Phar::BZ2");
3308 			RETURN_THROWS();
3309 	}
3310 
3311 	if (phar_obj->archive->is_tar) {
3312 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3313 			"Cannot compress with Gzip compression, tar archives cannot compress individual files, use compress() to compress the whole archive");
3314 		RETURN_THROWS();
3315 	}
3316 
3317 	if (!pharobj_cancompress(&phar_obj->archive->manifest)) {
3318 		if (flags == PHAR_ENT_COMPRESSED_GZ) {
3319 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3320 				"Cannot compress all files as Gzip, some are compressed as bzip2 and cannot be decompressed");
3321 		} else {
3322 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3323 				"Cannot compress all files as Bzip2, some are compressed as gzip and cannot be decompressed");
3324 		}
3325 		RETURN_THROWS();
3326 	}
3327 
3328 	if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
3329 		zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
3330 		RETURN_THROWS();
3331 	}
3332 	pharobj_set_compression(&phar_obj->archive->manifest, flags);
3333 	phar_obj->archive->is_modified = 1;
3334 	phar_flush(phar_obj->archive, 0, 0, 0, &error);
3335 
3336 	if (error) {
3337 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s", error);
3338 		efree(error);
3339 	}
3340 }
3341 /* }}} */
3342 
3343 /* {{{ decompress every file */
PHP_METHOD(Phar,decompressFiles)3344 PHP_METHOD(Phar, decompressFiles)
3345 {
3346 	char *error;
3347 
3348 	if (zend_parse_parameters_none() == FAILURE) {
3349 		RETURN_THROWS();
3350 
3351 	}
3352 
3353 	PHAR_ARCHIVE_OBJECT();
3354 
3355 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
3356 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3357 			"Phar is readonly, cannot change compression");
3358 		RETURN_THROWS();
3359 	}
3360 
3361 	if (!pharobj_cancompress(&phar_obj->archive->manifest)) {
3362 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
3363 			"Cannot decompress all files, some are compressed as bzip2 or gzip and cannot be decompressed");
3364 		RETURN_THROWS();
3365 	}
3366 
3367 	if (phar_obj->archive->is_tar) {
3368 		RETURN_TRUE;
3369 	} else {
3370 		if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
3371 			zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
3372 			RETURN_THROWS();
3373 		}
3374 		pharobj_set_compression(&phar_obj->archive->manifest, PHAR_ENT_COMPRESSED_NONE);
3375 	}
3376 
3377 	phar_obj->archive->is_modified = 1;
3378 	phar_flush(phar_obj->archive, 0, 0, 0, &error);
3379 
3380 	if (error) {
3381 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s", error);
3382 		efree(error);
3383 	}
3384 
3385 	RETURN_TRUE;
3386 }
3387 /* }}} */
3388 
3389 /* {{{ copy a file internal to the phar archive to another new file within the phar */
PHP_METHOD(Phar,copy)3390 PHP_METHOD(Phar, copy)
3391 {
3392 	char *oldfile, *newfile, *error;
3393 	const char *pcr_error;
3394 	size_t oldfile_len, newfile_len;
3395 	phar_entry_info *oldentry, newentry = {0}, *temp;
3396 	size_t tmp_len = 0;
3397 
3398 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "pp", &oldfile, &oldfile_len, &newfile, &newfile_len) == FAILURE) {
3399 		RETURN_THROWS();
3400 	}
3401 
3402 	PHAR_ARCHIVE_OBJECT();
3403 
3404 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
3405 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3406 			"Cannot copy \"%s\" to \"%s\", phar is read-only", oldfile, newfile);
3407 		RETURN_THROWS();
3408 	}
3409 
3410 	if (oldfile_len >= sizeof(".phar")-1 && !memcmp(oldfile, ".phar", sizeof(".phar")-1)) {
3411 		/* can't copy a meta file */
3412 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3413 			"file \"%s\" cannot be copied to file \"%s\", cannot copy Phar meta-file in %s", oldfile, newfile, phar_obj->archive->fname);
3414 		RETURN_THROWS();
3415 	}
3416 
3417 	if (newfile_len >= sizeof(".phar")-1 && !memcmp(newfile, ".phar", sizeof(".phar")-1)) {
3418 		/* can't copy a meta file */
3419 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3420 			"file \"%s\" cannot be copied to file \"%s\", cannot copy to Phar meta-file in %s", oldfile, newfile, phar_obj->archive->fname);
3421 		RETURN_THROWS();
3422 	}
3423 
3424 	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) {
3425 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3426 			"file \"%s\" cannot be copied to file \"%s\", file does not exist in %s", oldfile, newfile, phar_obj->archive->fname);
3427 		RETURN_THROWS();
3428 	}
3429 
3430 	if (zend_hash_str_exists(&phar_obj->archive->manifest, newfile, (uint32_t) newfile_len)) {
3431 		if (NULL != (temp = zend_hash_str_find_ptr(&phar_obj->archive->manifest, newfile, (uint32_t) newfile_len)) || !temp->is_deleted) {
3432 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3433 				"file \"%s\" cannot be copied to file \"%s\", file must not already exist in phar %s", oldfile, newfile, phar_obj->archive->fname);
3434 			RETURN_THROWS();
3435 		}
3436 	}
3437 
3438 	tmp_len = newfile_len;
3439 	if (phar_path_check(&newfile, &tmp_len, &pcr_error) > pcr_is_ok) {
3440 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
3441 				"file \"%s\" contains invalid characters %s, cannot be copied from \"%s\" in phar %s", newfile, pcr_error, oldfile, phar_obj->archive->fname);
3442 		RETURN_THROWS();
3443 	}
3444 	newfile_len = tmp_len;
3445 
3446 	if (phar_obj->archive->is_persistent) {
3447 		if (FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
3448 			zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
3449 			RETURN_THROWS();
3450 		}
3451 		/* re-populate with copied-on-write entry */
3452 		oldentry = zend_hash_str_find_ptr(&phar_obj->archive->manifest, oldfile, (uint32_t) oldfile_len);
3453 	}
3454 
3455 	memcpy((void *) &newentry, oldentry, sizeof(phar_entry_info));
3456 
3457 	phar_metadata_tracker_clone(&newentry.metadata_tracker);
3458 
3459 	newentry.filename = estrndup(newfile, newfile_len);
3460 	newentry.filename_len = newfile_len;
3461 	newentry.fp_refcount = 0;
3462 
3463 	if (oldentry->fp_type != PHAR_FP) {
3464 		if (FAILURE == phar_copy_entry_fp(oldentry, &newentry, &error)) {
3465 			efree(newentry.filename);
3466 			php_stream_close(newentry.fp);
3467 			zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
3468 			efree(error);
3469 			RETURN_THROWS();
3470 		}
3471 	}
3472 
3473 	zend_hash_str_add_mem(&oldentry->phar->manifest, newfile, newfile_len, &newentry, sizeof(phar_entry_info));
3474 	phar_obj->archive->is_modified = 1;
3475 	phar_flush(phar_obj->archive, 0, 0, 0, &error);
3476 
3477 	if (error) {
3478 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
3479 		efree(error);
3480 	}
3481 
3482 	RETURN_TRUE;
3483 }
3484 /* }}} */
3485 
3486 /* {{{ determines whether a file exists in the phar */
PHP_METHOD(Phar,offsetExists)3487 PHP_METHOD(Phar, offsetExists)
3488 {
3489 	char *fname;
3490 	size_t fname_len;
3491 	phar_entry_info *entry;
3492 
3493 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &fname, &fname_len) == FAILURE) {
3494 		RETURN_THROWS();
3495 	}
3496 
3497 	PHAR_ARCHIVE_OBJECT();
3498 
3499 	if (zend_hash_str_exists(&phar_obj->archive->manifest, fname, (uint32_t) fname_len)) {
3500 		if (NULL != (entry = zend_hash_str_find_ptr(&phar_obj->archive->manifest, fname, (uint32_t) fname_len))) {
3501 			if (entry->is_deleted) {
3502 				/* entry is deleted, but has not been flushed to disk yet */
3503 				RETURN_FALSE;
3504 			}
3505 		}
3506 
3507 		if (fname_len >= sizeof(".phar")-1 && !memcmp(fname, ".phar", sizeof(".phar")-1)) {
3508 			/* none of these are real files, so they don't exist */
3509 			RETURN_FALSE;
3510 		}
3511 		RETURN_TRUE;
3512 	} else {
3513 		if (zend_hash_str_exists(&phar_obj->archive->virtual_dirs, fname, (uint32_t) fname_len)) {
3514 			RETURN_TRUE;
3515 		}
3516 		RETURN_FALSE;
3517 	}
3518 }
3519 /* }}} */
3520 
3521 /* {{{ get a PharFileInfo object for a specific file */
PHP_METHOD(Phar,offsetGet)3522 PHP_METHOD(Phar, offsetGet)
3523 {
3524 	char *fname, *error;
3525 	size_t fname_len;
3526 	zval zfname;
3527 	phar_entry_info *entry;
3528 	zend_string *sfname;
3529 
3530 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &fname, &fname_len) == FAILURE) {
3531 		RETURN_THROWS();
3532 	}
3533 
3534 	PHAR_ARCHIVE_OBJECT();
3535 
3536 	/* security is 0 here so that we can get a better error message than "entry doesn't exist" */
3537 	if (!(entry = phar_get_entry_info_dir(phar_obj->archive, fname, fname_len, 1, &error, 0))) {
3538 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s does not exist%s%s", fname, error?", ":"", error?error:"");
3539 	} else {
3540 		if (fname_len == sizeof(".phar/stub.php")-1 && !memcmp(fname, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
3541 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot get stub \".phar/stub.php\" directly in phar \"%s\", use getStub", phar_obj->archive->fname);
3542 			RETURN_THROWS();
3543 		}
3544 
3545 		if (fname_len == sizeof(".phar/alias.txt")-1 && !memcmp(fname, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
3546 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot get alias \".phar/alias.txt\" directly in phar \"%s\", use getAlias", phar_obj->archive->fname);
3547 			RETURN_THROWS();
3548 		}
3549 
3550 		if (fname_len >= sizeof(".phar")-1 && !memcmp(fname, ".phar", sizeof(".phar")-1)) {
3551 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot directly get any files or directories in magic \".phar\" directory");
3552 			RETURN_THROWS();
3553 		}
3554 
3555 		if (entry->is_temp_dir) {
3556 			efree(entry->filename);
3557 			efree(entry);
3558 		}
3559 
3560 		sfname = strpprintf(0, "phar://%s/%s", phar_obj->archive->fname, fname);
3561 		ZVAL_NEW_STR(&zfname, sfname);
3562 		spl_instantiate_arg_ex1(phar_obj->spl.info_class, return_value, &zfname);
3563 		zval_ptr_dtor(&zfname);
3564 	}
3565 }
3566 /* }}} */
3567 
3568 /* {{{ 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)3569 static void phar_add_file(phar_archive_data **pphar, char *filename, size_t filename_len, char *cont_str, size_t cont_len, zval *zresource)
3570 {
3571 	size_t start_pos=0;
3572 	char *error;
3573 	size_t contents_len;
3574 	phar_entry_data *data;
3575 	php_stream *contents_file = NULL;
3576 	php_stream_statbuf ssb;
3577 #ifdef PHP_WIN32
3578 	char *save_filename;
3579 	ALLOCA_FLAG(filename_use_heap)
3580 #endif
3581 
3582 	if (filename_len >= sizeof(".phar")-1) {
3583 		start_pos = '/' == filename[0]; /* account for any leading slash: multiple-leads handled elsewhere */
3584 		if (!memcmp(&filename[start_pos], ".phar", sizeof(".phar")-1) && (filename[start_pos+5] == '/' || filename[start_pos+5] == '\\' || filename[start_pos+5] == '\0')) {
3585 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create any files in magic \".phar\" directory");
3586 			return;
3587 		}
3588 	}
3589 
3590 #ifdef PHP_WIN32
3591 	save_filename = filename;
3592 	if (memchr(filename, '\\', filename_len)) {
3593 		filename = do_alloca(filename_len + 1, filename_use_heap);
3594 		memcpy(filename, save_filename, filename_len);
3595 		filename[filename_len] = '\0';
3596 	}
3597 #endif
3598 
3599 	if (!(data = phar_get_or_create_entry_data((*pphar)->fname, (*pphar)->fname_len, filename, filename_len, "w+b", 0, &error, 1))) {
3600 		if (error) {
3601 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s does not exist and cannot be created: %s", filename, error);
3602 			efree(error);
3603 		} else {
3604 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s does not exist and cannot be created", filename);
3605 		}
3606 		goto finish;
3607 	} else {
3608 		if (error) {
3609 			efree(error);
3610 		}
3611 
3612 		if (!data->internal_file->is_dir) {
3613 			if (cont_str) {
3614 				contents_len = php_stream_write(data->fp, cont_str, cont_len);
3615 				if (contents_len != cont_len) {
3616 					zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s could not be written to", filename);
3617 					goto finish;
3618 				}
3619 			} else {
3620 				if (!(php_stream_from_zval_no_verify(contents_file, zresource))) {
3621 					zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s could not be written to", filename);
3622 					goto finish;
3623 				}
3624 				php_stream_copy_to_stream_ex(contents_file, data->fp, PHP_STREAM_COPY_ALL, &contents_len);
3625 			}
3626 			data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize = contents_len;
3627 		}
3628 
3629 		if (contents_file != NULL && php_stream_stat(contents_file, &ssb) != -1) {
3630 			data->internal_file->flags = ssb.sb.st_mode & PHAR_ENT_PERM_MASK ;
3631 		} else {
3632 #ifndef _WIN32
3633 			mode_t mask;
3634 			mask = umask(0);
3635 			umask(mask);
3636 			data->internal_file->flags &= ~mask;
3637 #endif
3638 		}
3639 
3640 		/* check for copy-on-write */
3641 		if (pphar[0] != data->phar) {
3642 			*pphar = data->phar;
3643 		}
3644 		phar_entry_delref(data);
3645 		phar_flush(*pphar, 0, 0, 0, &error);
3646 
3647 		if (error) {
3648 			zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
3649 			efree(error);
3650 		}
3651 	}
3652 
3653 finish: ;
3654 #ifdef PHP_WIN32
3655 	if (filename != save_filename) {
3656 		free_alloca(filename, filename_use_heap);
3657 		filename = save_filename;
3658 	}
3659 #endif
3660 }
3661 /* }}} */
3662 
3663 /* {{{ create a directory within the phar archive */
phar_mkdir(phar_archive_data ** pphar,char * dirname,size_t dirname_len)3664 static void phar_mkdir(phar_archive_data **pphar, char *dirname, size_t dirname_len)
3665 {
3666 	char *error;
3667 	phar_entry_data *data;
3668 
3669 	if (!(data = phar_get_or_create_entry_data((*pphar)->fname, (*pphar)->fname_len, dirname, dirname_len, "w+b", 2, &error, 1))) {
3670 		if (error) {
3671 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Directory %s does not exist and cannot be created: %s", dirname, error);
3672 			efree(error);
3673 		} else {
3674 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Directory %s does not exist and cannot be created", dirname);
3675 		}
3676 
3677 		return;
3678 	} else {
3679 		if (error) {
3680 			efree(error);
3681 		}
3682 
3683 		/* check for copy on write */
3684 		if (data->phar != *pphar) {
3685 			*pphar = data->phar;
3686 		}
3687 		phar_entry_delref(data);
3688 		phar_flush(*pphar, 0, 0, 0, &error);
3689 
3690 		if (error) {
3691 			zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
3692 			efree(error);
3693 		}
3694 	}
3695 }
3696 /* }}} */
3697 
3698 /* {{{ set the contents of an internal file to those of an external file */
PHP_METHOD(Phar,offsetSet)3699 PHP_METHOD(Phar, offsetSet)
3700 {
3701 	char *fname, *cont_str = NULL;
3702 	size_t fname_len, cont_len;
3703 	zval *zresource = NULL;
3704 
3705 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "pr", &fname, &fname_len, &zresource) == FAILURE
3706 	&& zend_parse_parameters(ZEND_NUM_ARGS(), "ps", &fname, &fname_len, &cont_str, &cont_len) == FAILURE) {
3707 		RETURN_THROWS();
3708 	}
3709 
3710 	PHAR_ARCHIVE_OBJECT();
3711 
3712 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
3713 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Write operations disabled by the php.ini setting phar.readonly");
3714 		RETURN_THROWS();
3715 	}
3716 
3717 	if (fname_len == sizeof(".phar/stub.php")-1 && !memcmp(fname, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
3718 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot set stub \".phar/stub.php\" directly in phar \"%s\", use setStub", phar_obj->archive->fname);
3719 		RETURN_THROWS();
3720 	}
3721 
3722 	if (fname_len == sizeof(".phar/alias.txt")-1 && !memcmp(fname, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
3723 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot set alias \".phar/alias.txt\" directly in phar \"%s\", use setAlias", phar_obj->archive->fname);
3724 		RETURN_THROWS();
3725 	}
3726 
3727 	if (fname_len >= sizeof(".phar")-1 && !memcmp(fname, ".phar", sizeof(".phar")-1)) {
3728 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot set any files or directories in magic \".phar\" directory");
3729 		RETURN_THROWS();
3730 	}
3731 
3732 	phar_add_file(&(phar_obj->archive), fname, fname_len, cont_str, cont_len, zresource);
3733 }
3734 /* }}} */
3735 
3736 /* {{{ remove a file from a phar */
PHP_METHOD(Phar,offsetUnset)3737 PHP_METHOD(Phar, offsetUnset)
3738 {
3739 	char *fname, *error;
3740 	size_t fname_len;
3741 	phar_entry_info *entry;
3742 
3743 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &fname, &fname_len) == FAILURE) {
3744 		RETURN_THROWS();
3745 	}
3746 
3747 	PHAR_ARCHIVE_OBJECT();
3748 
3749 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
3750 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Write operations disabled by the php.ini setting phar.readonly");
3751 		RETURN_THROWS();
3752 	}
3753 
3754 	if (zend_hash_str_exists(&phar_obj->archive->manifest, fname, (uint32_t) fname_len)) {
3755 		if (NULL != (entry = zend_hash_str_find_ptr(&phar_obj->archive->manifest, fname, (uint32_t) fname_len))) {
3756 			if (entry->is_deleted) {
3757 				/* entry is deleted, but has not been flushed to disk yet */
3758 				return;
3759 			}
3760 
3761 			if (phar_obj->archive->is_persistent) {
3762 				if (FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
3763 					zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
3764 					RETURN_THROWS();
3765 				}
3766 				/* re-populate entry after copy on write */
3767 				entry = zend_hash_str_find_ptr(&phar_obj->archive->manifest, fname, (uint32_t) fname_len);
3768 			}
3769 			entry->is_modified = 0;
3770 			entry->is_deleted = 1;
3771 			/* we need to "flush" the stream to save the newly deleted file on disk */
3772 			phar_flush(phar_obj->archive, 0, 0, 0, &error);
3773 
3774 			if (error) {
3775 				zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
3776 				efree(error);
3777 			}
3778 		}
3779 	}
3780 }
3781 /* }}} */
3782 
3783 /* {{{ Adds an empty directory to the phar archive */
PHP_METHOD(Phar,addEmptyDir)3784 PHP_METHOD(Phar, addEmptyDir)
3785 {
3786 	char *dirname;
3787 	size_t dirname_len;
3788 
3789 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &dirname, &dirname_len) == FAILURE) {
3790 		RETURN_THROWS();
3791 	}
3792 
3793 	PHAR_ARCHIVE_OBJECT();
3794 
3795 	if (dirname_len >= sizeof(".phar")-1 && !memcmp(dirname, ".phar", sizeof(".phar")-1)) {
3796 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create a directory in magic \".phar\" directory");
3797 		RETURN_THROWS();
3798 	}
3799 
3800 	phar_mkdir(&phar_obj->archive, dirname, dirname_len);
3801 }
3802 /* }}} */
3803 
3804 /* {{{ Adds a file to the archive using the filename, or the second parameter as the name within the archive */
PHP_METHOD(Phar,addFile)3805 PHP_METHOD(Phar, addFile)
3806 {
3807 	char *fname, *localname = NULL;
3808 	size_t fname_len, localname_len = 0;
3809 	php_stream *resource;
3810 	zval zresource;
3811 
3812 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|s!", &fname, &fname_len, &localname, &localname_len) == FAILURE) {
3813 		RETURN_THROWS();
3814 	}
3815 
3816 	PHAR_ARCHIVE_OBJECT();
3817 
3818 	if (!strstr(fname, "://") && php_check_open_basedir(fname)) {
3819 		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);
3820 		RETURN_THROWS();
3821 	}
3822 
3823 	if (!(resource = php_stream_open_wrapper(fname, "rb", 0, NULL))) {
3824 		zend_throw_exception_ex(spl_ce_RuntimeException, 0, "phar error: unable to open file \"%s\" to add to phar archive", fname);
3825 		RETURN_THROWS();
3826 	}
3827 
3828 	if (localname) {
3829 		fname = localname;
3830 		fname_len = localname_len;
3831 	}
3832 
3833 	php_stream_to_zval(resource, &zresource);
3834 	phar_add_file(&(phar_obj->archive), fname, fname_len, NULL, 0, &zresource);
3835 	zval_ptr_dtor(&zresource);
3836 }
3837 /* }}} */
3838 
3839 /* {{{ Adds a file to the archive using its contents as a string */
PHP_METHOD(Phar,addFromString)3840 PHP_METHOD(Phar, addFromString)
3841 {
3842 	char *localname, *cont_str;
3843 	size_t localname_len, cont_len;
3844 
3845 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ps", &localname, &localname_len, &cont_str, &cont_len) == FAILURE) {
3846 		RETURN_THROWS();
3847 	}
3848 
3849 	PHAR_ARCHIVE_OBJECT();
3850 
3851 	phar_add_file(&(phar_obj->archive), localname, localname_len, cont_str, cont_len, NULL);
3852 }
3853 /* }}} */
3854 
3855 /* {{{ Returns the stub at the head of a phar archive as a string. */
PHP_METHOD(Phar,getStub)3856 PHP_METHOD(Phar, getStub)
3857 {
3858 	size_t len;
3859 	zend_string *buf;
3860 	php_stream *fp;
3861 	php_stream_filter *filter = NULL;
3862 	phar_entry_info *stub;
3863 
3864 	if (zend_parse_parameters_none() == FAILURE) {
3865 		RETURN_THROWS();
3866 	}
3867 
3868 	PHAR_ARCHIVE_OBJECT();
3869 
3870 	if (phar_obj->archive->is_tar || phar_obj->archive->is_zip) {
3871 
3872 		if (NULL != (stub = zend_hash_str_find_ptr(&(phar_obj->archive->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1))) {
3873 			if (phar_obj->archive->fp && !phar_obj->archive->is_brandnew && !(stub->flags & PHAR_ENT_COMPRESSION_MASK)) {
3874 				fp = phar_obj->archive->fp;
3875 			} else {
3876 				if (!(fp = php_stream_open_wrapper(phar_obj->archive->fname, "rb", 0, NULL))) {
3877 					zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "phar error: unable to open phar \"%s\"", phar_obj->archive->fname);
3878 					RETURN_THROWS();
3879 				}
3880 				if (stub->flags & PHAR_ENT_COMPRESSION_MASK) {
3881 					char *filter_name;
3882 
3883 					if ((filter_name = phar_decompress_filter(stub, 0)) != NULL) {
3884 						filter = php_stream_filter_create(filter_name, NULL, php_stream_is_persistent(fp));
3885 					} else {
3886 						filter = NULL;
3887 					}
3888 					if (!filter) {
3889 						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));
3890 						RETURN_THROWS();
3891 					}
3892 					php_stream_filter_append(&fp->readfilters, filter);
3893 				}
3894 			}
3895 
3896 			if (!fp)  {
3897 				zend_throw_exception_ex(spl_ce_RuntimeException, 0,
3898 					"Unable to read stub");
3899 				RETURN_THROWS();
3900 			}
3901 
3902 			php_stream_seek(fp, stub->offset_abs, SEEK_SET);
3903 			len = stub->uncompressed_filesize;
3904 			goto carry_on;
3905 		} else {
3906 			RETURN_EMPTY_STRING();
3907 		}
3908 	}
3909 	len = phar_obj->archive->halt_offset;
3910 
3911 	if (phar_obj->archive->fp && !phar_obj->archive->is_brandnew) {
3912 		fp = phar_obj->archive->fp;
3913 	} else {
3914 		fp = php_stream_open_wrapper(phar_obj->archive->fname, "rb", 0, NULL);
3915 	}
3916 
3917 	if (!fp)  {
3918 		zend_throw_exception_ex(spl_ce_RuntimeException, 0,
3919 			"Unable to read stub");
3920 		RETURN_THROWS();
3921 	}
3922 
3923 	php_stream_rewind(fp);
3924 carry_on:
3925 	buf = zend_string_alloc(len, 0);
3926 
3927 	if (len != php_stream_read(fp, ZSTR_VAL(buf), len)) {
3928 		if (fp != phar_obj->archive->fp) {
3929 			php_stream_close(fp);
3930 		}
3931 		zend_throw_exception_ex(spl_ce_RuntimeException, 0,
3932 			"Unable to read stub");
3933 		zend_string_release_ex(buf, 0);
3934 		RETURN_THROWS();
3935 	}
3936 
3937 	if (filter) {
3938 		php_stream_filter_flush(filter, 1);
3939 		php_stream_filter_remove(filter, 1);
3940 	}
3941 
3942 	if (fp != phar_obj->archive->fp) {
3943 		php_stream_close(fp);
3944 	}
3945 
3946 	ZSTR_VAL(buf)[len] = '\0';
3947 	ZSTR_LEN(buf) = len;
3948 	RETVAL_STR(buf);
3949 }
3950 /* }}}*/
3951 
3952 /* {{{ Returns TRUE if the phar has global metadata, FALSE otherwise. */
PHP_METHOD(Phar,hasMetadata)3953 PHP_METHOD(Phar, hasMetadata)
3954 {
3955 	if (zend_parse_parameters_none() == FAILURE) {
3956 		RETURN_THROWS();
3957 	}
3958 
3959 	PHAR_ARCHIVE_OBJECT();
3960 
3961 	RETURN_BOOL(phar_metadata_tracker_has_data(&phar_obj->archive->metadata_tracker, phar_obj->archive->is_persistent));
3962 }
3963 /* }}} */
3964 
3965 /* {{{ Returns the global metadata of the phar */
PHP_METHOD(Phar,getMetadata)3966 PHP_METHOD(Phar, getMetadata)
3967 {
3968 	HashTable *unserialize_options = NULL;
3969 	phar_metadata_tracker *tracker;
3970 
3971 	ZEND_PARSE_PARAMETERS_START(0, 1)
3972 		Z_PARAM_OPTIONAL
3973 		Z_PARAM_ARRAY_HT(unserialize_options)
3974 	ZEND_PARSE_PARAMETERS_END();
3975 
3976 	PHAR_ARCHIVE_OBJECT();
3977 
3978 	tracker = &phar_obj->archive->metadata_tracker;
3979 	if (phar_metadata_tracker_has_data(tracker, phar_obj->archive->is_persistent)) {
3980 		phar_metadata_tracker_unserialize_or_copy(tracker, return_value, phar_obj->archive->is_persistent, unserialize_options, "Phar::getMetadata");
3981 	}
3982 }
3983 /* }}} */
3984 
3985 /* {{{ Modifies the phar metadata or throws */
serialize_metadata_or_throw(phar_metadata_tracker * tracker,int persistent,zval * metadata)3986 static int serialize_metadata_or_throw(phar_metadata_tracker *tracker, int persistent, zval *metadata)
3987 {
3988 	php_serialize_data_t metadata_hash;
3989 	smart_str main_metadata_str = {0};
3990 
3991 	PHP_VAR_SERIALIZE_INIT(metadata_hash);
3992 	php_var_serialize(&main_metadata_str, metadata, &metadata_hash);
3993 	PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
3994 	if (EG(exception)) {
3995 		/* Serialization can throw. Don't overwrite the original value or original string. */
3996 		return FAILURE;
3997 	}
3998 
3999 	phar_metadata_tracker_free(tracker, persistent);
4000 	if (EG(exception)) {
4001 		/* Destructor can throw. */
4002 		zend_string_release(main_metadata_str.s);
4003 		return FAILURE;
4004 	}
4005 
4006 	if (tracker->str) {
4007 		zend_throw_exception_ex(phar_ce_PharException, 0, "Metadata unexpectedly changed during setMetadata()");
4008 		zend_string_release(main_metadata_str.s);
4009 		return FAILURE;
4010 	}
4011 	ZVAL_COPY(&tracker->val, metadata);
4012 	tracker->str = main_metadata_str.s;
4013 	return SUCCESS;
4014 }
4015 
4016 /* {{{ Sets the global metadata of the phar */
PHP_METHOD(Phar,setMetadata)4017 PHP_METHOD(Phar, setMetadata)
4018 {
4019 	char *error;
4020 	zval *metadata;
4021 
4022 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &metadata) == FAILURE) {
4023 		RETURN_THROWS();
4024 	}
4025 
4026 	PHAR_ARCHIVE_OBJECT();
4027 
4028 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
4029 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Write operations disabled by the php.ini setting phar.readonly");
4030 		RETURN_THROWS();
4031 	}
4032 
4033 	if (phar_obj->archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->archive))) {
4034 		zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
4035 		RETURN_THROWS();
4036 	}
4037 
4038 	ZEND_ASSERT(!phar_obj->archive->is_persistent); /* Should no longer be persistent */
4039 	if (serialize_metadata_or_throw(&phar_obj->archive->metadata_tracker, phar_obj->archive->is_persistent, metadata) != SUCCESS) {
4040 		RETURN_THROWS();
4041 	}
4042 
4043 	phar_obj->archive->is_modified = 1;
4044 	phar_flush(phar_obj->archive, 0, 0, 0, &error);
4045 
4046 	if (error) {
4047 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
4048 		efree(error);
4049 	}
4050 }
4051 /* }}} */
4052 
4053 /* {{{ Deletes the global metadata of the phar */
PHP_METHOD(Phar,delMetadata)4054 PHP_METHOD(Phar, delMetadata)
4055 {
4056 	char *error;
4057 
4058 	if (zend_parse_parameters_none() == FAILURE) {
4059 		RETURN_THROWS();
4060 	}
4061 
4062 	PHAR_ARCHIVE_OBJECT();
4063 
4064 	if (PHAR_G(readonly) && !phar_obj->archive->is_data) {
4065 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Write operations disabled by the php.ini setting phar.readonly");
4066 		RETURN_THROWS();
4067 	}
4068 
4069 	if (!phar_metadata_tracker_has_data(&phar_obj->archive->metadata_tracker, phar_obj->archive->is_persistent)) {
4070 		RETURN_TRUE;
4071 	}
4072 
4073 	phar_metadata_tracker_free(&phar_obj->archive->metadata_tracker, phar_obj->archive->is_persistent);
4074 	phar_obj->archive->is_modified = 1;
4075 	phar_flush(phar_obj->archive, 0, 0, 0, &error);
4076 
4077 	if (error) {
4078 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
4079 		efree(error);
4080 		RETURN_THROWS();
4081 	} else {
4082 		RETURN_TRUE;
4083 	}
4084 }
4085 /* }}} */
4086 
phar_extract_file(bool overwrite,phar_entry_info * entry,char * dest,size_t dest_len,char ** error)4087 static int phar_extract_file(bool overwrite, phar_entry_info *entry, char *dest, size_t dest_len, char **error) /* {{{ */
4088 {
4089 	php_stream_statbuf ssb;
4090 	size_t len;
4091 	php_stream *fp;
4092 	char *fullpath;
4093 	const char *slash;
4094 	mode_t mode;
4095 	cwd_state new_state;
4096 	char *filename;
4097 	size_t filename_len;
4098 
4099 	if (entry->is_mounted) {
4100 		/* silently ignore mounted entries */
4101 		return SUCCESS;
4102 	}
4103 
4104 	if (entry->filename_len >= sizeof(".phar")-1 && !memcmp(entry->filename, ".phar", sizeof(".phar")-1)) {
4105 		return SUCCESS;
4106 	}
4107 	/* strip .. from path and restrict it to be under dest directory */
4108 	new_state.cwd = (char*)emalloc(2);
4109 	new_state.cwd[0] = DEFAULT_SLASH;
4110 	new_state.cwd[1] = '\0';
4111 	new_state.cwd_length = 1;
4112 	if (virtual_file_ex(&new_state, entry->filename, NULL, CWD_EXPAND) != 0 ||
4113 			new_state.cwd_length <= 1) {
4114 		if (EINVAL == errno && entry->filename_len > 50) {
4115 			char *tmp = estrndup(entry->filename, 50);
4116 			spprintf(error, 4096, "Cannot extract \"%s...\" to \"%s...\", extracted filename is too long for filesystem", tmp, dest);
4117 			efree(tmp);
4118 		} else {
4119 			spprintf(error, 4096, "Cannot extract \"%s\", internal error", entry->filename);
4120 		}
4121 		efree(new_state.cwd);
4122 		return FAILURE;
4123 	}
4124 	filename = new_state.cwd + 1;
4125 	filename_len = new_state.cwd_length - 1;
4126 #ifdef PHP_WIN32
4127 	/* unixify the path back, otherwise non zip formats might be broken */
4128 	{
4129 		size_t cnt = 0;
4130 
4131 		do {
4132 			if ('\\' == filename[cnt]) {
4133 				filename[cnt] = '/';
4134 			}
4135 		} while (cnt++ < filename_len);
4136 	}
4137 #endif
4138 
4139 	len = spprintf(&fullpath, 0, "%s/%s", dest, filename);
4140 
4141 	if (len >= MAXPATHLEN) {
4142 		char *tmp;
4143 		/* truncate for error message */
4144 		fullpath[50] = '\0';
4145 		if (entry->filename_len > 50) {
4146 			tmp = estrndup(entry->filename, 50);
4147 			spprintf(error, 4096, "Cannot extract \"%s...\" to \"%s...\", extracted filename is too long for filesystem", tmp, fullpath);
4148 			efree(tmp);
4149 		} else {
4150 			spprintf(error, 4096, "Cannot extract \"%s\" to \"%s...\", extracted filename is too long for filesystem", entry->filename, fullpath);
4151 		}
4152 		efree(fullpath);
4153 		efree(new_state.cwd);
4154 		return FAILURE;
4155 	}
4156 
4157 	if (!len) {
4158 		spprintf(error, 4096, "Cannot extract \"%s\", internal error", entry->filename);
4159 		efree(fullpath);
4160 		efree(new_state.cwd);
4161 		return FAILURE;
4162 	}
4163 
4164 	if (php_check_open_basedir(fullpath)) {
4165 		spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", openbasedir/safe mode restrictions in effect", entry->filename, fullpath);
4166 		efree(fullpath);
4167 		efree(new_state.cwd);
4168 		return FAILURE;
4169 	}
4170 
4171 	/* let see if the path already exists */
4172 	if (!overwrite && SUCCESS == php_stream_stat_path(fullpath, &ssb)) {
4173 		spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", path already exists", entry->filename, fullpath);
4174 		efree(fullpath);
4175 		efree(new_state.cwd);
4176 		return FAILURE;
4177 	}
4178 
4179 	/* perform dirname */
4180 	slash = zend_memrchr(filename, '/', filename_len);
4181 
4182 	if (slash) {
4183 		fullpath[dest_len + (slash - filename) + 1] = '\0';
4184 	} else {
4185 		fullpath[dest_len] = '\0';
4186 	}
4187 
4188 	if (FAILURE == php_stream_stat_path(fullpath, &ssb)) {
4189 		if (entry->is_dir) {
4190 			if (!php_stream_mkdir(fullpath, entry->flags & PHAR_ENT_PERM_MASK,  PHP_STREAM_MKDIR_RECURSIVE, NULL)) {
4191 				spprintf(error, 4096, "Cannot extract \"%s\", could not create directory \"%s\"", entry->filename, fullpath);
4192 				efree(fullpath);
4193 				efree(new_state.cwd);
4194 				return FAILURE;
4195 			}
4196 		} else {
4197 			if (!php_stream_mkdir(fullpath, 0777,  PHP_STREAM_MKDIR_RECURSIVE, NULL)) {
4198 				spprintf(error, 4096, "Cannot extract \"%s\", could not create directory \"%s\"", entry->filename, fullpath);
4199 				efree(fullpath);
4200 				efree(new_state.cwd);
4201 				return FAILURE;
4202 			}
4203 		}
4204 	}
4205 
4206 	if (slash) {
4207 		fullpath[dest_len + (slash - filename) + 1] = '/';
4208 	} else {
4209 		fullpath[dest_len] = '/';
4210 	}
4211 
4212 	filename = NULL;
4213 	efree(new_state.cwd);
4214 	/* it is a standalone directory, job done */
4215 	if (entry->is_dir) {
4216 		efree(fullpath);
4217 		return SUCCESS;
4218 	}
4219 
4220 	fp = php_stream_open_wrapper(fullpath, "w+b", REPORT_ERRORS, NULL);
4221 
4222 	if (!fp) {
4223 		spprintf(error, 4096, "Cannot extract \"%s\", could not open for writing \"%s\"", entry->filename, fullpath);
4224 		efree(fullpath);
4225 		return FAILURE;
4226 	}
4227 
4228 	if ((phar_get_fp_type(entry) == PHAR_FP && (entry->flags & PHAR_ENT_COMPRESSION_MASK)) || !phar_get_efp(entry, 0)) {
4229 		if (FAILURE == phar_open_entry_fp(entry, error, 1)) {
4230 			if (error) {
4231 				spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", unable to open internal file pointer: %s", entry->filename, fullpath, *error);
4232 			} else {
4233 				spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", unable to open internal file pointer", entry->filename, fullpath);
4234 			}
4235 			efree(fullpath);
4236 			php_stream_close(fp);
4237 			return FAILURE;
4238 		}
4239 	}
4240 
4241 	if (FAILURE == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
4242 		spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", unable to seek internal file pointer", entry->filename, fullpath);
4243 		efree(fullpath);
4244 		php_stream_close(fp);
4245 		return FAILURE;
4246 	}
4247 
4248 	if (SUCCESS != php_stream_copy_to_stream_ex(phar_get_efp(entry, 0), fp, entry->uncompressed_filesize, NULL)) {
4249 		spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", copying contents failed", entry->filename, fullpath);
4250 		efree(fullpath);
4251 		php_stream_close(fp);
4252 		return FAILURE;
4253 	}
4254 
4255 	php_stream_close(fp);
4256 	mode = (mode_t) entry->flags & PHAR_ENT_PERM_MASK;
4257 
4258 	if (FAILURE == VCWD_CHMOD(fullpath, mode)) {
4259 		spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", setting file permissions failed", entry->filename, fullpath);
4260 		efree(fullpath);
4261 		return FAILURE;
4262 	}
4263 
4264 	efree(fullpath);
4265 	return SUCCESS;
4266 }
4267 /* }}} */
4268 
extract_helper(phar_archive_data * archive,zend_string * search,char * pathto,size_t pathto_len,bool overwrite,char ** error)4269 static int extract_helper(phar_archive_data *archive, zend_string *search, char *pathto, size_t pathto_len, bool overwrite, char **error) { /* {{{ */
4270 	int extracted = 0;
4271 	phar_entry_info *entry;
4272 
4273 	if (!search) {
4274 		/* nothing to match -- extract all files */
4275 		ZEND_HASH_MAP_FOREACH_PTR(&archive->manifest, entry) {
4276 			if (FAILURE == phar_extract_file(overwrite, entry, pathto, pathto_len, error)) return -1;
4277 			extracted++;
4278 		} ZEND_HASH_FOREACH_END();
4279 	} else if ('/' == ZSTR_VAL(search)[ZSTR_LEN(search) - 1]) {
4280 		/* ends in "/" -- extract all entries having that prefix */
4281 		ZEND_HASH_MAP_FOREACH_PTR(&archive->manifest, entry) {
4282 			if (0 != strncmp(ZSTR_VAL(search), entry->filename, ZSTR_LEN(search))) continue;
4283 			if (FAILURE == phar_extract_file(overwrite, entry, pathto, pathto_len, error)) return -1;
4284 			extracted++;
4285 		} ZEND_HASH_FOREACH_END();
4286 	} else {
4287 		/* otherwise, looking for an exact match */
4288 		entry = zend_hash_find_ptr(&archive->manifest, search);
4289 		if (NULL == entry) return 0;
4290 		if (FAILURE == phar_extract_file(overwrite, entry, pathto, pathto_len, error)) return -1;
4291 		return 1;
4292 	}
4293 
4294 	return extracted;
4295 }
4296 /* }}} */
4297 
4298 /* {{{ Extract one or more file from a phar archive, optionally overwriting existing files */
PHP_METHOD(Phar,extractTo)4299 PHP_METHOD(Phar, extractTo)
4300 {
4301 	php_stream *fp;
4302 	php_stream_statbuf ssb;
4303 	char *pathto;
4304 	zend_string *filename = NULL;
4305 	size_t pathto_len;
4306 	int ret;
4307 	zval *zval_file;
4308 	HashTable *files_ht = NULL;
4309 	bool overwrite = 0;
4310 	char *error = NULL;
4311 
4312 	ZEND_PARSE_PARAMETERS_START(1, 3)
4313 		Z_PARAM_PATH(pathto, pathto_len)
4314 		Z_PARAM_OPTIONAL
4315 		Z_PARAM_ARRAY_HT_OR_STR_OR_NULL(files_ht, filename)
4316 		Z_PARAM_BOOL(overwrite)
4317 	ZEND_PARSE_PARAMETERS_END();
4318 
4319 	PHAR_ARCHIVE_OBJECT();
4320 
4321 	fp = php_stream_open_wrapper(phar_obj->archive->fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, NULL);
4322 
4323 	if (!fp) {
4324 		zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0,
4325 			"Invalid argument, %s cannot be found", phar_obj->archive->fname);
4326 		RETURN_THROWS();
4327 	}
4328 
4329 	php_stream_close(fp);
4330 
4331 	if (pathto_len < 1) {
4332 		zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0,
4333 			"Invalid argument, extraction path must be non-zero length");
4334 		RETURN_THROWS();
4335 	}
4336 
4337 	if (pathto_len >= MAXPATHLEN) {
4338 		char *tmp = estrndup(pathto, 50);
4339 		/* truncate for error message */
4340 		zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "Cannot extract to \"%s...\", destination directory is too long for filesystem", tmp);
4341 		efree(tmp);
4342 		RETURN_THROWS();
4343 	}
4344 
4345 	if (php_stream_stat_path(pathto, &ssb) < 0) {
4346 		ret = php_stream_mkdir(pathto, 0777,  PHP_STREAM_MKDIR_RECURSIVE, NULL);
4347 		if (!ret) {
4348 			zend_throw_exception_ex(spl_ce_RuntimeException, 0,
4349 				"Unable to create path \"%s\" for extraction", pathto);
4350 			RETURN_THROWS();
4351 		}
4352 	} else if (!(ssb.sb.st_mode & S_IFDIR)) {
4353 		zend_throw_exception_ex(spl_ce_RuntimeException, 0,
4354 			"Unable to use path \"%s\" for extraction, it is a file, must be a directory", pathto);
4355 		RETURN_THROWS();
4356 	}
4357 
4358 	if (files_ht) {
4359 		if (zend_hash_num_elements(files_ht) == 0) {
4360 			RETURN_FALSE;
4361 		}
4362 
4363 		ZEND_HASH_FOREACH_VAL(files_ht, zval_file) {
4364 			ZVAL_DEREF(zval_file);
4365 			if (IS_STRING != Z_TYPE_P(zval_file)) {
4366 				zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0,
4367 					"Invalid argument, array of filenames to extract contains non-string value");
4368 				RETURN_THROWS();
4369 			}
4370 			switch (extract_helper(phar_obj->archive, Z_STR_P(zval_file), pathto, pathto_len, overwrite, &error)) {
4371 				case -1:
4372 					zend_throw_exception_ex(phar_ce_PharException, 0, "Extraction from phar \"%s\" failed: %s",
4373 						phar_obj->archive->fname, error);
4374 					efree(error);
4375 					RETURN_THROWS();
4376 				case 0:
4377 					zend_throw_exception_ex(phar_ce_PharException, 0,
4378 						"phar error: attempted to extract non-existent file or directory \"%s\" from phar \"%s\"",
4379 						ZSTR_VAL(Z_STR_P(zval_file)), phar_obj->archive->fname);
4380 					RETURN_THROWS();
4381 			}
4382 		} ZEND_HASH_FOREACH_END();
4383 		RETURN_TRUE;
4384 	}
4385 
4386 	ret = extract_helper(phar_obj->archive, filename, pathto, pathto_len, overwrite, &error);
4387 	if (-1 == ret) {
4388 		zend_throw_exception_ex(phar_ce_PharException, 0, "Extraction from phar \"%s\" failed: %s",
4389 			phar_obj->archive->fname, error);
4390 		efree(error);
4391 	} else if (0 == ret && NULL != filename) {
4392 		zend_throw_exception_ex(phar_ce_PharException, 0,
4393 			"phar error: attempted to extract non-existent file or directory \"%s\" from phar \"%s\"",
4394 			ZSTR_VAL(filename), phar_obj->archive->fname);
4395 	} else {
4396 		RETURN_TRUE;
4397 	}
4398 }
4399 /* }}} */
4400 
4401 
4402 /* {{{ Construct a Phar entry object */
PHP_METHOD(PharFileInfo,__construct)4403 PHP_METHOD(PharFileInfo, __construct)
4404 {
4405 	char *fname, *arch, *entry, *error;
4406 	size_t fname_len;
4407 	size_t arch_len, entry_len;
4408 	phar_entry_object *entry_obj;
4409 	phar_entry_info *entry_info;
4410 	phar_archive_data *phar_data;
4411 	zval *zobj = ZEND_THIS, arg1;
4412 
4413 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &fname, &fname_len) == FAILURE) {
4414 		RETURN_THROWS();
4415 	}
4416 
4417 	entry_obj = (phar_entry_object*)((char*)Z_OBJ_P(zobj) - Z_OBJ_P(zobj)->handlers->offset);
4418 
4419 	if (entry_obj->entry) {
4420 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot call constructor twice");
4421 		RETURN_THROWS();
4422 	}
4423 
4424 	if (fname_len < 7 || memcmp(fname, "phar://", 7) || phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0) == FAILURE) {
4425 		zend_throw_exception_ex(spl_ce_RuntimeException, 0,
4426 			"'%s' is not a valid phar archive URL (must have at least phar://filename.phar)", fname);
4427 		RETURN_THROWS();
4428 	}
4429 
4430 	if (phar_open_from_filename(arch, arch_len, NULL, 0, REPORT_ERRORS, &phar_data, &error) == FAILURE) {
4431 		efree(arch);
4432 		efree(entry);
4433 		if (error) {
4434 			zend_throw_exception_ex(spl_ce_RuntimeException, 0,
4435 				"Cannot open phar file '%s': %s", fname, error);
4436 			efree(error);
4437 		} else {
4438 			zend_throw_exception_ex(spl_ce_RuntimeException, 0,
4439 				"Cannot open phar file '%s'", fname);
4440 		}
4441 		RETURN_THROWS();
4442 	}
4443 
4444 	if ((entry_info = phar_get_entry_info_dir(phar_data, entry, entry_len, 1, &error, 1)) == NULL) {
4445 		zend_throw_exception_ex(spl_ce_RuntimeException, 0,
4446 			"Cannot access phar file entry '%s' in archive '%s'%s%s", entry, arch, error ? ", " : "", error ? error : "");
4447 		efree(arch);
4448 		efree(entry);
4449 		RETURN_THROWS();
4450 	}
4451 
4452 	efree(arch);
4453 	efree(entry);
4454 
4455 	entry_obj->entry = entry_info;
4456 
4457 	ZVAL_STRINGL(&arg1, fname, fname_len);
4458 
4459 	zend_call_known_instance_method_with_1_params(spl_ce_SplFileInfo->constructor,
4460 		Z_OBJ_P(zobj), NULL, &arg1);
4461 
4462 	zval_ptr_dtor(&arg1);
4463 }
4464 /* }}} */
4465 
4466 #define PHAR_ENTRY_OBJECT() \
4467 	zval *zobj = ZEND_THIS; \
4468 	phar_entry_object *entry_obj = (phar_entry_object*)((char*)Z_OBJ_P(zobj) - Z_OBJ_P(zobj)->handlers->offset); \
4469 	if (!entry_obj->entry) { \
4470 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, \
4471 			"Cannot call method on an uninitialized PharFileInfo object"); \
4472 		RETURN_THROWS(); \
4473 	}
4474 
4475 /* {{{ clean up directory-based entry objects */
PHP_METHOD(PharFileInfo,__destruct)4476 PHP_METHOD(PharFileInfo, __destruct)
4477 {
4478 	zval *zobj = ZEND_THIS;
4479 	phar_entry_object *entry_obj = (phar_entry_object*)((char*)Z_OBJ_P(zobj) - Z_OBJ_P(zobj)->handlers->offset);
4480 
4481 	if (zend_parse_parameters_none() == FAILURE) {
4482 		RETURN_THROWS();
4483 	}
4484 
4485 	if (entry_obj->entry && entry_obj->entry->is_temp_dir) {
4486 		if (entry_obj->entry->filename) {
4487 			efree(entry_obj->entry->filename);
4488 			entry_obj->entry->filename = NULL;
4489 		}
4490 
4491 		efree(entry_obj->entry);
4492 		entry_obj->entry = NULL;
4493 	}
4494 }
4495 /* }}} */
4496 
4497 /* {{{ Returns the compressed size */
PHP_METHOD(PharFileInfo,getCompressedSize)4498 PHP_METHOD(PharFileInfo, getCompressedSize)
4499 {
4500 	if (zend_parse_parameters_none() == FAILURE) {
4501 		RETURN_THROWS();
4502 	}
4503 
4504 	PHAR_ENTRY_OBJECT();
4505 
4506 	RETURN_LONG(entry_obj->entry->compressed_filesize);
4507 }
4508 /* }}} */
4509 
4510 /* {{{ Returns whether the entry is compressed, and whether it is compressed with Phar::GZ or Phar::BZ2 if specified */
PHP_METHOD(PharFileInfo,isCompressed)4511 PHP_METHOD(PharFileInfo, isCompressed)
4512 {
4513 	zend_long method;
4514 	bool method_is_null = 1;
4515 
4516 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l!", &method, &method_is_null) == FAILURE) {
4517 		RETURN_THROWS();
4518 	}
4519 
4520 	PHAR_ENTRY_OBJECT();
4521 
4522 	if (method_is_null) {
4523 		RETURN_BOOL(entry_obj->entry->flags & PHAR_ENT_COMPRESSION_MASK);
4524 	}
4525 
4526 	switch (method) {
4527 		case 9021976: /* Retained for BC */
4528 			RETURN_BOOL(entry_obj->entry->flags & PHAR_ENT_COMPRESSION_MASK);
4529 		case PHAR_ENT_COMPRESSED_GZ:
4530 			RETURN_BOOL(entry_obj->entry->flags & PHAR_ENT_COMPRESSED_GZ);
4531 		case PHAR_ENT_COMPRESSED_BZ2:
4532 			RETURN_BOOL(entry_obj->entry->flags & PHAR_ENT_COMPRESSED_BZ2);
4533 		default:
4534 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unknown compression type specified");
4535 			RETURN_THROWS();
4536 	}
4537 }
4538 /* }}} */
4539 
4540 /* {{{ Returns CRC32 code or throws an exception if not CRC checked */
PHP_METHOD(PharFileInfo,getCRC32)4541 PHP_METHOD(PharFileInfo, getCRC32)
4542 {
4543 	if (zend_parse_parameters_none() == FAILURE) {
4544 		RETURN_THROWS();
4545 	}
4546 
4547 	PHAR_ENTRY_OBJECT();
4548 
4549 	if (entry_obj->entry->is_dir) {
4550 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, \
4551 			"Phar entry is a directory, does not have a CRC"); \
4552 		RETURN_THROWS();
4553 	}
4554 
4555 	if (entry_obj->entry->is_crc_checked) {
4556 		RETURN_LONG(entry_obj->entry->crc32);
4557 	} else {
4558 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Phar entry was not CRC checked");
4559 		RETURN_THROWS();
4560 	}
4561 }
4562 /* }}} */
4563 
4564 /* {{{ Returns whether file entry is CRC checked */
PHP_METHOD(PharFileInfo,isCRCChecked)4565 PHP_METHOD(PharFileInfo, isCRCChecked)
4566 {
4567 	if (zend_parse_parameters_none() == FAILURE) {
4568 		RETURN_THROWS();
4569 	}
4570 
4571 	PHAR_ENTRY_OBJECT();
4572 
4573 	RETURN_BOOL(entry_obj->entry->is_crc_checked);
4574 }
4575 /* }}} */
4576 
4577 /* {{{ Returns the Phar file entry flags */
PHP_METHOD(PharFileInfo,getPharFlags)4578 PHP_METHOD(PharFileInfo, getPharFlags)
4579 {
4580 	if (zend_parse_parameters_none() == FAILURE) {
4581 		RETURN_THROWS();
4582 	}
4583 
4584 	PHAR_ENTRY_OBJECT();
4585 
4586 	RETURN_LONG(entry_obj->entry->flags & ~(PHAR_ENT_PERM_MASK|PHAR_ENT_COMPRESSION_MASK));
4587 }
4588 /* }}} */
4589 
4590 /* {{{ set the file permissions for the Phar.  This only allows setting execution bit, read/write */
PHP_METHOD(PharFileInfo,chmod)4591 PHP_METHOD(PharFileInfo, chmod)
4592 {
4593 	char *error;
4594 	zend_long perms;
4595 
4596 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &perms) == FAILURE) {
4597 		RETURN_THROWS();
4598 	}
4599 
4600 	PHAR_ENTRY_OBJECT();
4601 
4602 	if (entry_obj->entry->is_temp_dir) {
4603 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, \
4604 			"Phar entry \"%s\" is a temporary directory (not an actual entry in the archive), cannot chmod", entry_obj->entry->filename); \
4605 		RETURN_THROWS();
4606 	}
4607 
4608 	if (PHAR_G(readonly) && !entry_obj->entry->phar->is_data) {
4609 		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);
4610 		RETURN_THROWS();
4611 	}
4612 
4613 	if (entry_obj->entry->is_persistent) {
4614 		phar_archive_data *phar = entry_obj->entry->phar;
4615 
4616 		if (FAILURE == phar_copy_on_write(&phar)) {
4617 			zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar->fname);
4618 			RETURN_THROWS();
4619 		}
4620 		/* re-populate after copy-on-write */
4621 		entry_obj->entry = zend_hash_str_find_ptr(&phar->manifest, entry_obj->entry->filename, entry_obj->entry->filename_len);
4622 	}
4623 	/* clear permissions */
4624 	entry_obj->entry->flags &= ~PHAR_ENT_PERM_MASK;
4625 	perms &= 0777;
4626 	entry_obj->entry->flags |= perms;
4627 	entry_obj->entry->old_flags = entry_obj->entry->flags;
4628 	entry_obj->entry->phar->is_modified = 1;
4629 	entry_obj->entry->is_modified = 1;
4630 
4631 	/* hackish cache in php_stat needs to be cleared */
4632 	/* if this code fails to work, check main/streams/streams.c, _php_stream_stat_path */
4633 	if (BG(CurrentLStatFile)) {
4634 		zend_string_release(BG(CurrentLStatFile));
4635 	}
4636 
4637 	if (BG(CurrentStatFile)) {
4638 		zend_string_release(BG(CurrentStatFile));
4639 	}
4640 
4641 	BG(CurrentLStatFile) = NULL;
4642 	BG(CurrentStatFile) = NULL;
4643 	phar_flush(entry_obj->entry->phar, 0, 0, 0, &error);
4644 
4645 	if (error) {
4646 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
4647 		efree(error);
4648 	}
4649 }
4650 /* }}} */
4651 
4652 /* {{{ Returns the metadata of the entry */
PHP_METHOD(PharFileInfo,hasMetadata)4653 PHP_METHOD(PharFileInfo, hasMetadata)
4654 {
4655 	if (zend_parse_parameters_none() == FAILURE) {
4656 		RETURN_THROWS();
4657 	}
4658 
4659 	PHAR_ENTRY_OBJECT();
4660 
4661 	RETURN_BOOL(phar_metadata_tracker_has_data(&entry_obj->entry->metadata_tracker, entry_obj->entry->is_persistent));
4662 }
4663 /* }}} */
4664 
4665 /* {{{ Returns the metadata of the entry */
PHP_METHOD(PharFileInfo,getMetadata)4666 PHP_METHOD(PharFileInfo, getMetadata)
4667 {
4668 	HashTable *unserialize_options = NULL;
4669 	phar_metadata_tracker *tracker;
4670 
4671 	ZEND_PARSE_PARAMETERS_START(0, 1)
4672 		Z_PARAM_OPTIONAL
4673 		Z_PARAM_ARRAY_HT(unserialize_options)
4674 	ZEND_PARSE_PARAMETERS_END();
4675 
4676 	PHAR_ENTRY_OBJECT();
4677 
4678 	tracker = &entry_obj->entry->metadata_tracker;
4679 	if (phar_metadata_tracker_has_data(tracker, entry_obj->entry->is_persistent)) {
4680 		phar_metadata_tracker_unserialize_or_copy(tracker, return_value, entry_obj->entry->is_persistent, unserialize_options, "PharFileInfo::getMetadata");
4681 	}
4682 }
4683 /* }}} */
4684 
4685 /* {{{ Sets the metadata of the entry */
PHP_METHOD(PharFileInfo,setMetadata)4686 PHP_METHOD(PharFileInfo, setMetadata)
4687 {
4688 	char *error;
4689 	zval *metadata;
4690 
4691 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &metadata) == FAILURE) {
4692 		RETURN_THROWS();
4693 	}
4694 
4695 	PHAR_ENTRY_OBJECT();
4696 
4697 	if (PHAR_G(readonly) && !entry_obj->entry->phar->is_data) {
4698 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Write operations disabled by the php.ini setting phar.readonly");
4699 		RETURN_THROWS();
4700 	}
4701 
4702 	if (entry_obj->entry->is_temp_dir) {
4703 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, \
4704 			"Phar entry is a temporary directory (not an actual entry in the archive), cannot set metadata"); \
4705 		RETURN_THROWS();
4706 	}
4707 
4708 	if (entry_obj->entry->is_persistent) {
4709 		phar_archive_data *phar = entry_obj->entry->phar;
4710 
4711 		if (FAILURE == phar_copy_on_write(&phar)) {
4712 			zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar->fname);
4713 			RETURN_THROWS();
4714 		}
4715 		/* re-populate after copy-on-write */
4716 		entry_obj->entry = zend_hash_str_find_ptr(&phar->manifest, entry_obj->entry->filename, entry_obj->entry->filename_len);
4717 		ZEND_ASSERT(!entry_obj->entry->is_persistent); /* Should no longer be persistent */
4718 	}
4719 
4720 	if (serialize_metadata_or_throw(&entry_obj->entry->metadata_tracker, entry_obj->entry->is_persistent, metadata) != SUCCESS) {
4721 		RETURN_THROWS();
4722 	}
4723 
4724 	entry_obj->entry->is_modified = 1;
4725 	entry_obj->entry->phar->is_modified = 1;
4726 	phar_flush(entry_obj->entry->phar, 0, 0, 0, &error);
4727 
4728 	if (error) {
4729 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
4730 		efree(error);
4731 	}
4732 }
4733 /* }}} */
4734 
4735 /* {{{ Deletes the metadata of the entry */
PHP_METHOD(PharFileInfo,delMetadata)4736 PHP_METHOD(PharFileInfo, delMetadata)
4737 {
4738 	char *error;
4739 
4740 	if (zend_parse_parameters_none() == FAILURE) {
4741 		RETURN_THROWS();
4742 	}
4743 
4744 	PHAR_ENTRY_OBJECT();
4745 
4746 	if (PHAR_G(readonly) && !entry_obj->entry->phar->is_data) {
4747 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Write operations disabled by the php.ini setting phar.readonly");
4748 		RETURN_THROWS();
4749 	}
4750 
4751 	if (entry_obj->entry->is_temp_dir) {
4752 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, \
4753 			"Phar entry is a temporary directory (not an actual entry in the archive), cannot delete metadata"); \
4754 		RETURN_THROWS();
4755 	}
4756 
4757 	if (phar_metadata_tracker_has_data(&entry_obj->entry->metadata_tracker, entry_obj->entry->is_persistent)) {
4758 		if (entry_obj->entry->is_persistent) {
4759 			phar_archive_data *phar = entry_obj->entry->phar;
4760 
4761 			if (FAILURE == phar_copy_on_write(&phar)) {
4762 				zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar->fname);
4763 				RETURN_THROWS();
4764 			}
4765 			/* re-populate after copy-on-write */
4766 			entry_obj->entry = zend_hash_str_find_ptr(&phar->manifest, entry_obj->entry->filename, entry_obj->entry->filename_len);
4767 		}
4768 		/* multiple values may reference the metadata */
4769 		phar_metadata_tracker_free(&entry_obj->entry->metadata_tracker, entry_obj->entry->is_persistent);
4770 		entry_obj->entry->is_modified = 1;
4771 		entry_obj->entry->phar->is_modified = 1;
4772 
4773 		phar_flush(entry_obj->entry->phar, 0, 0, 0, &error);
4774 
4775 		if (error) {
4776 			zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
4777 			efree(error);
4778 			RETURN_THROWS();
4779 		} else {
4780 			RETURN_TRUE;
4781 		}
4782 
4783 	} else {
4784 		RETURN_TRUE;
4785 	}
4786 }
4787 /* }}} */
4788 
4789 /* {{{ return the complete file contents of the entry (like file_get_contents) */
PHP_METHOD(PharFileInfo,getContent)4790 PHP_METHOD(PharFileInfo, getContent)
4791 {
4792 	char *error;
4793 	php_stream *fp;
4794 	phar_entry_info *link;
4795 	zend_string *str;
4796 
4797 	if (zend_parse_parameters_none() == FAILURE) {
4798 		RETURN_THROWS();
4799 	}
4800 
4801 	PHAR_ENTRY_OBJECT();
4802 
4803 	if (entry_obj->entry->is_dir) {
4804 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4805 			"phar error: Cannot retrieve contents, \"%s\" in phar \"%s\" is a directory", entry_obj->entry->filename, entry_obj->entry->phar->fname);
4806 		RETURN_THROWS();
4807 	}
4808 
4809 	link = phar_get_link_source(entry_obj->entry);
4810 
4811 	if (!link) {
4812 		link = entry_obj->entry;
4813 	}
4814 
4815 	if (SUCCESS != phar_open_entry_fp(link, &error, 0)) {
4816 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4817 			"phar error: Cannot retrieve contents, \"%s\" in phar \"%s\": %s", entry_obj->entry->filename, entry_obj->entry->phar->fname, error);
4818 		efree(error);
4819 		RETURN_THROWS();
4820 	}
4821 
4822 	if (!(fp = phar_get_efp(link, 0))) {
4823 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4824 			"phar error: Cannot retrieve contents of \"%s\" in phar \"%s\"", entry_obj->entry->filename, entry_obj->entry->phar->fname);
4825 		RETURN_THROWS();
4826 	}
4827 
4828 	phar_seek_efp(link, 0, SEEK_SET, 0, 0);
4829 	str = php_stream_copy_to_mem(fp, link->uncompressed_filesize, 0);
4830 	if (str) {
4831 		RETURN_STR(str);
4832 	} else {
4833 		RETURN_EMPTY_STRING();
4834 	}
4835 }
4836 /* }}} */
4837 
4838 /* {{{ Instructs the Phar class to compress the current file using zlib or bzip2 compression */
PHP_METHOD(PharFileInfo,compress)4839 PHP_METHOD(PharFileInfo, compress)
4840 {
4841 	zend_long method;
4842 	char *error;
4843 
4844 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &method) == FAILURE) {
4845 		RETURN_THROWS();
4846 	}
4847 
4848 	PHAR_ENTRY_OBJECT();
4849 
4850 	if (entry_obj->entry->is_tar) {
4851 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4852 			"Cannot compress with Gzip compression, not possible with tar-based phar archives");
4853 		RETURN_THROWS();
4854 	}
4855 
4856 	if (entry_obj->entry->is_dir) {
4857 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, \
4858 			"Phar entry is a directory, cannot set compression"); \
4859 		RETURN_THROWS();
4860 	}
4861 
4862 	if (PHAR_G(readonly) && !entry_obj->entry->phar->is_data) {
4863 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4864 			"Phar is readonly, cannot change compression");
4865 		RETURN_THROWS();
4866 	}
4867 
4868 	if (entry_obj->entry->is_deleted) {
4869 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4870 			"Cannot compress deleted file");
4871 		RETURN_THROWS();
4872 	}
4873 
4874 	if (entry_obj->entry->is_persistent) {
4875 		phar_archive_data *phar = entry_obj->entry->phar;
4876 
4877 		if (FAILURE == phar_copy_on_write(&phar)) {
4878 			zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar->fname);
4879 			RETURN_THROWS();
4880 		}
4881 		/* re-populate after copy-on-write */
4882 		entry_obj->entry = zend_hash_str_find_ptr(&phar->manifest, entry_obj->entry->filename, entry_obj->entry->filename_len);
4883 	}
4884 	switch (method) {
4885 		case PHAR_ENT_COMPRESSED_GZ:
4886 			if (entry_obj->entry->flags & PHAR_ENT_COMPRESSED_GZ) {
4887 				RETURN_TRUE;
4888 			}
4889 
4890 			if ((entry_obj->entry->flags & PHAR_ENT_COMPRESSED_BZ2) != 0) {
4891 				if (!PHAR_G(has_bz2)) {
4892 					zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4893 						"Cannot compress with gzip compression, file is already compressed with bzip2 compression and bz2 extension is not enabled, cannot decompress");
4894 					RETURN_THROWS();
4895 				}
4896 
4897 				/* decompress this file indirectly */
4898 				if (SUCCESS != phar_open_entry_fp(entry_obj->entry, &error, 1)) {
4899 					zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4900 						"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);
4901 					efree(error);
4902 					RETURN_THROWS();
4903 				}
4904 			}
4905 
4906 			if (!PHAR_G(has_zlib)) {
4907 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4908 					"Cannot compress with gzip compression, zlib extension is not enabled");
4909 				RETURN_THROWS();
4910 			}
4911 
4912 			entry_obj->entry->old_flags = entry_obj->entry->flags;
4913 			entry_obj->entry->flags &= ~PHAR_ENT_COMPRESSION_MASK;
4914 			entry_obj->entry->flags |= PHAR_ENT_COMPRESSED_GZ;
4915 			break;
4916 		case PHAR_ENT_COMPRESSED_BZ2:
4917 			if (entry_obj->entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
4918 				RETURN_TRUE;
4919 			}
4920 
4921 			if ((entry_obj->entry->flags & PHAR_ENT_COMPRESSED_GZ) != 0) {
4922 				if (!PHAR_G(has_zlib)) {
4923 					zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4924 						"Cannot compress with bzip2 compression, file is already compressed with gzip compression and zlib extension is not enabled, cannot decompress");
4925 					RETURN_THROWS();
4926 				}
4927 
4928 				/* decompress this file indirectly */
4929 				if (SUCCESS != phar_open_entry_fp(entry_obj->entry, &error, 1)) {
4930 					zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4931 						"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);
4932 					efree(error);
4933 					RETURN_THROWS();
4934 				}
4935 			}
4936 
4937 			if (!PHAR_G(has_bz2)) {
4938 				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4939 					"Cannot compress with bzip2 compression, bz2 extension is not enabled");
4940 				RETURN_THROWS();
4941 			}
4942 			entry_obj->entry->old_flags = entry_obj->entry->flags;
4943 			entry_obj->entry->flags &= ~PHAR_ENT_COMPRESSION_MASK;
4944 			entry_obj->entry->flags |= PHAR_ENT_COMPRESSED_BZ2;
4945 			break;
4946 		default:
4947 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unknown compression type specified");
4948 			RETURN_THROWS();
4949 	}
4950 
4951 	entry_obj->entry->phar->is_modified = 1;
4952 	entry_obj->entry->is_modified = 1;
4953 	phar_flush(entry_obj->entry->phar, 0, 0, 0, &error);
4954 
4955 	if (error) {
4956 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
4957 		efree(error);
4958 		RETURN_THROWS();
4959 	}
4960 
4961 	RETURN_TRUE;
4962 }
4963 /* }}} */
4964 
4965 /* {{{ Instructs the Phar class to decompress the current file */
PHP_METHOD(PharFileInfo,decompress)4966 PHP_METHOD(PharFileInfo, decompress)
4967 {
4968 	char *error;
4969 	char *compression_type;
4970 
4971 	if (zend_parse_parameters_none() == FAILURE) {
4972 		RETURN_THROWS();
4973 	}
4974 
4975 	PHAR_ENTRY_OBJECT();
4976 
4977 	if (entry_obj->entry->is_dir) {
4978 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, \
4979 			"Phar entry is a directory, cannot set compression"); \
4980 		RETURN_THROWS();
4981 	}
4982 
4983 	if ((entry_obj->entry->flags & PHAR_ENT_COMPRESSION_MASK) == 0) {
4984 		RETURN_TRUE;
4985 	}
4986 
4987 	if (PHAR_G(readonly) && !entry_obj->entry->phar->is_data) {
4988 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4989 			"Phar is readonly, cannot decompress");
4990 		RETURN_THROWS();
4991 	}
4992 
4993 	if (entry_obj->entry->is_deleted) {
4994 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
4995 			"Cannot compress deleted file");
4996 		RETURN_THROWS();
4997 	}
4998 
4999 	if ((entry_obj->entry->flags & PHAR_ENT_COMPRESSED_GZ) != 0 && !PHAR_G(has_zlib)) {
5000 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
5001 			"Cannot decompress Gzip-compressed file, zlib extension is not enabled");
5002 		RETURN_THROWS();
5003 	}
5004 
5005 	if ((entry_obj->entry->flags & PHAR_ENT_COMPRESSED_BZ2) != 0 && !PHAR_G(has_bz2)) {
5006 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
5007 			"Cannot decompress Bzip2-compressed file, bz2 extension is not enabled");
5008 		RETURN_THROWS();
5009 	}
5010 
5011 	if (entry_obj->entry->is_persistent) {
5012 		phar_archive_data *phar = entry_obj->entry->phar;
5013 
5014 		if (FAILURE == phar_copy_on_write(&phar)) {
5015 			zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar->fname);
5016 			RETURN_THROWS();
5017 		}
5018 		/* re-populate after copy-on-write */
5019 		entry_obj->entry = zend_hash_str_find_ptr(&phar->manifest, entry_obj->entry->filename, entry_obj->entry->filename_len);
5020 	}
5021 	switch (entry_obj->entry->flags & PHAR_ENT_COMPRESSION_MASK) {
5022 		case PHAR_ENT_COMPRESSED_GZ:
5023 			compression_type = "gzip";
5024 			break;
5025 		case PHAR_ENT_COMPRESSED_BZ2:
5026 			compression_type = "bz2";
5027 			break;
5028 		default:
5029 			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
5030 				"Cannot decompress file compressed with unknown compression type");
5031 			RETURN_THROWS();
5032 	}
5033 	/* decompress this file indirectly */
5034 	if (SUCCESS != phar_open_entry_fp(entry_obj->entry, &error, 1)) {
5035 		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
5036 			"Phar error: Cannot decompress %s-compressed file \"%s\" in phar \"%s\": %s", compression_type, entry_obj->entry->filename, entry_obj->entry->phar->fname, error);
5037 		efree(error);
5038 		RETURN_THROWS();
5039 	}
5040 
5041 	entry_obj->entry->old_flags = entry_obj->entry->flags;
5042 	entry_obj->entry->flags &= ~PHAR_ENT_COMPRESSION_MASK;
5043 	entry_obj->entry->phar->is_modified = 1;
5044 	entry_obj->entry->is_modified = 1;
5045 	phar_flush(entry_obj->entry->phar, 0, 0, 0, &error);
5046 
5047 	if (error) {
5048 		zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
5049 		efree(error);
5050 		RETURN_THROWS();
5051 	}
5052 
5053 	RETURN_TRUE;
5054 }
5055 /* }}} */
5056 
5057 /* {{{ phar methods */
5058 
phar_object_init(void)5059 void phar_object_init(void) /* {{{ */
5060 {
5061 	phar_ce_PharException = register_class_PharException(zend_ce_exception);
5062 
5063 	phar_ce_archive = register_class_Phar(spl_ce_RecursiveDirectoryIterator, zend_ce_countable, zend_ce_arrayaccess);
5064 
5065 	phar_ce_data = register_class_PharData(spl_ce_RecursiveDirectoryIterator, zend_ce_countable, zend_ce_arrayaccess);
5066 
5067 	phar_ce_entry = register_class_PharFileInfo(spl_ce_SplFileInfo);
5068 }
5069 /* }}} */
5070