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