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