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