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