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