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