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