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