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