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