1/*
2  +----------------------------------------------------------------------+
3  | phar php single-file executable PHP extension                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 2005-2014 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_uchar key_type;
1438    zend_bool close_fp = 1;
1439    ulong int_key;
1440    struct _phar_t *p_obj = (struct _phar_t*) puser;
1441    uint str_key_len, base_len = p_obj->l, fname_len;
1442    phar_entry_data *data;
1443    php_stream *fp;
1444    size_t contents_len;
1445    char *fname, *error = NULL, *base = p_obj->b, *opened, *save = NULL, *temp = NULL;
1446    phar_zstr key;
1447    char *str_key;
1448    zend_class_entry *ce = p_obj->c;
1449    phar_archive_object *phar_obj = p_obj->p;
1450    char *str = "[stream]";
1451
1452    iter->funcs->get_current_data(iter, &value TSRMLS_CC);
1453
1454    if (EG(exception)) {
1455        return ZEND_HASH_APPLY_STOP;
1456    }
1457
1458    if (!value) {
1459        /* failure in get_current_data */
1460        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned no value", ce->name);
1461        return ZEND_HASH_APPLY_STOP;
1462    }
1463
1464    switch (Z_TYPE_PP(value)) {
1465#if PHP_VERSION_ID >= 60000
1466        case IS_UNICODE:
1467            zval_unicode_to_string(*(value) TSRMLS_CC);
1468            /* break intentionally omitted */
1469#endif
1470        case IS_STRING:
1471            break;
1472        case IS_RESOURCE:
1473            php_stream_from_zval_no_verify(fp, value);
1474
1475            if (!fp) {
1476                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Iterator %v returned an invalid stream handle", ce->name);
1477                return ZEND_HASH_APPLY_STOP;
1478            }
1479
1480            if (iter->funcs->get_current_key) {
1481                key_type = iter->funcs->get_current_key(iter, &key, &str_key_len, &int_key TSRMLS_CC);
1482
1483                if (EG(exception)) {
1484                    return ZEND_HASH_APPLY_STOP;
1485                }
1486
1487                if (key_type == HASH_KEY_IS_LONG) {
1488                    zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name);
1489                    return ZEND_HASH_APPLY_STOP;
1490                }
1491
1492                if (key_type > 9) { /* IS_UNICODE == 10 */
1493#if PHP_VERSION_ID < 60000
1494/* this can never happen, but fixes a compile warning */
1495                    spprintf(&str_key, 0, "%s", key);
1496#else
1497                    spprintf(&str_key, 0, "%v", key);
1498                    ezfree(key);
1499#endif
1500                } else {
1501                    PHAR_STR(key, str_key);
1502                }
1503
1504                save = str_key;
1505
1506                if (str_key[str_key_len - 1] == '\0') {
1507                    str_key_len--;
1508                }
1509
1510            } else {
1511                zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name);
1512                return ZEND_HASH_APPLY_STOP;
1513            }
1514
1515            close_fp = 0;
1516            opened = (char *) estrndup(str, sizeof("[stream]") + 1);
1517            goto after_open_fp;
1518        case IS_OBJECT:
1519            if (instanceof_function(Z_OBJCE_PP(value), spl_ce_SplFileInfo TSRMLS_CC)) {
1520                char *test = NULL;
1521                zval dummy;
1522                spl_filesystem_object *intern = (spl_filesystem_object*)zend_object_store_get_object(*value TSRMLS_CC);
1523
1524                if (!base_len) {
1525                    zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Iterator %v returns an SplFileInfo object, so base directory must be specified", ce->name);
1526                    return ZEND_HASH_APPLY_STOP;
1527                }
1528
1529                switch (intern->type) {
1530                    case SPL_FS_DIR:
1531#if PHP_VERSION_ID >= 60000
1532                        test = spl_filesystem_object_get_path(intern, NULL, NULL TSRMLS_CC).s;
1533#elif PHP_VERSION_ID >= 50300
1534                        test = spl_filesystem_object_get_path(intern, NULL TSRMLS_CC);
1535#else
1536                        test = intern->path;
1537#endif
1538                        fname_len = spprintf(&fname, 0, "%s%c%s", test, DEFAULT_SLASH, intern->u.dir.entry.d_name);
1539                        php_stat(fname, fname_len, FS_IS_DIR, &dummy TSRMLS_CC);
1540
1541                        if (Z_BVAL(dummy)) {
1542                            /* ignore directories */
1543                            efree(fname);
1544                            return ZEND_HASH_APPLY_KEEP;
1545                        }
1546
1547                        test = expand_filepath(fname, NULL TSRMLS_CC);
1548                        efree(fname);
1549
1550                        if (test) {
1551                            fname = test;
1552                            fname_len = strlen(fname);
1553                        } else {
1554                            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Could not resolve file path");
1555                            return ZEND_HASH_APPLY_STOP;
1556                        }
1557
1558                        save = fname;
1559                        goto phar_spl_fileinfo;
1560                    case SPL_FS_INFO:
1561                    case SPL_FS_FILE:
1562#if PHP_VERSION_ID >= 60000
1563                        if (intern->file_name_type == IS_UNICODE) {
1564                            zval zv;
1565
1566                            INIT_ZVAL(zv);
1567                            Z_UNIVAL(zv) = intern->file_name;
1568                            Z_UNILEN(zv) = intern->file_name_len;
1569                            Z_TYPE(zv) = IS_UNICODE;
1570
1571                            zval_copy_ctor(&zv);
1572                            zval_unicode_to_string(&zv TSRMLS_CC);
1573                            fname = expand_filepath(Z_STRVAL(zv), NULL TSRMLS_CC);
1574                            ezfree(Z_UNIVAL(zv));
1575                        } else {
1576                            fname = expand_filepath(intern->file_name.s, NULL TSRMLS_CC);
1577                        }
1578#else
1579                        fname = expand_filepath(intern->file_name, NULL TSRMLS_CC);
1580#endif
1581                        if (!fname) {
1582                            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Could not resolve file path");
1583                            return ZEND_HASH_APPLY_STOP;
1584                        }
1585
1586                        fname_len = strlen(fname);
1587                        save = fname;
1588                        goto phar_spl_fileinfo;
1589                }
1590            }
1591            /* fall-through */
1592        default:
1593            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid value (must return a string)", ce->name);
1594            return ZEND_HASH_APPLY_STOP;
1595    }
1596
1597    fname = Z_STRVAL_PP(value);
1598    fname_len = Z_STRLEN_PP(value);
1599
1600phar_spl_fileinfo:
1601    if (base_len) {
1602        temp = expand_filepath(base, NULL TSRMLS_CC);
1603        if (!temp) {
1604            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Could not resolve file path");
1605            if (save) {
1606                efree(save);
1607            }
1608            return ZEND_HASH_APPLY_STOP;
1609        }
1610
1611        base = temp;
1612        base_len = strlen(base);
1613
1614        if (strstr(fname, base)) {
1615            str_key_len = fname_len - base_len;
1616
1617            if (str_key_len <= 0) {
1618                if (save) {
1619                    efree(save);
1620                    efree(temp);
1621                }
1622                return ZEND_HASH_APPLY_KEEP;
1623            }
1624
1625            str_key = fname + base_len;
1626
1627            if (*str_key == '/' || *str_key == '\\') {
1628                str_key++;
1629                str_key_len--;
1630            }
1631
1632        } else {
1633            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);
1634
1635            if (save) {
1636                efree(save);
1637                efree(temp);
1638            }
1639
1640            return ZEND_HASH_APPLY_STOP;
1641        }
1642    } else {
1643        if (iter->funcs->get_current_key) {
1644            key_type = iter->funcs->get_current_key(iter, &key, &str_key_len, &int_key TSRMLS_CC);
1645
1646            if (EG(exception)) {
1647                return ZEND_HASH_APPLY_STOP;
1648            }
1649
1650            if (key_type == HASH_KEY_IS_LONG) {
1651                zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name);
1652                return ZEND_HASH_APPLY_STOP;
1653            }
1654
1655            if (key_type > 9) { /* IS_UNICODE == 10 */
1656#if PHP_VERSION_ID < 60000
1657/* this can never happen, but fixes a compile warning */
1658                spprintf(&str_key, 0, "%s", key);
1659#else
1660                spprintf(&str_key, 0, "%v", key);
1661                ezfree(key);
1662#endif
1663            } else {
1664                PHAR_STR(key, str_key);
1665            }
1666
1667            save = str_key;
1668
1669            if (str_key[str_key_len - 1] == '\0') str_key_len--;
1670        } else {
1671            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name);
1672            return ZEND_HASH_APPLY_STOP;
1673        }
1674    }
1675#if PHP_API_VERSION < 20100412
1676    if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
1677        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned a path \"%s\" that safe mode prevents opening", ce->name, fname);
1678
1679        if (save) {
1680            efree(save);
1681        }
1682
1683        if (temp) {
1684            efree(temp);
1685        }
1686
1687        return ZEND_HASH_APPLY_STOP;
1688    }
1689#endif
1690
1691    if (php_check_open_basedir(fname TSRMLS_CC)) {
1692        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned a path \"%s\" that open_basedir prevents opening", ce->name, fname);
1693
1694        if (save) {
1695            efree(save);
1696        }
1697
1698        if (temp) {
1699            efree(temp);
1700        }
1701
1702        return ZEND_HASH_APPLY_STOP;
1703    }
1704
1705    /* try to open source file, then create internal phar file and copy contents */
1706    fp = php_stream_open_wrapper(fname, "rb", STREAM_MUST_SEEK|0, &opened);
1707
1708    if (!fp) {
1709        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned a file that could not be opened \"%s\"", ce->name, fname);
1710
1711        if (save) {
1712            efree(save);
1713        }
1714
1715        if (temp) {
1716            efree(temp);
1717        }
1718
1719        return ZEND_HASH_APPLY_STOP;
1720    }
1721after_open_fp:
1722    if (str_key_len >= sizeof(".phar")-1 && !memcmp(str_key, ".phar", sizeof(".phar")-1)) {
1723        /* silently skip any files that would be added to the magic .phar directory */
1724        if (save) {
1725            efree(save);
1726        }
1727
1728        if (temp) {
1729            efree(temp);
1730        }
1731
1732        if (opened) {
1733            efree(opened);
1734        }
1735
1736        if (close_fp) {
1737            php_stream_close(fp);
1738        }
1739
1740        return ZEND_HASH_APPLY_KEEP;
1741    }
1742
1743    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))) {
1744        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s cannot be created: %s", str_key, error);
1745        efree(error);
1746
1747        if (save) {
1748            efree(save);
1749        }
1750
1751        if (opened) {
1752            efree(opened);
1753        }
1754
1755        if (temp) {
1756            efree(temp);
1757        }
1758
1759        if (close_fp) {
1760            php_stream_close(fp);
1761        }
1762
1763        return ZEND_HASH_APPLY_STOP;
1764
1765    } else {
1766        if (error) {
1767            efree(error);
1768        }
1769        /* convert to PHAR_UFP */
1770        if (data->internal_file->fp_type == PHAR_MOD) {
1771            php_stream_close(data->internal_file->fp);
1772        }
1773
1774        data->internal_file->fp = NULL;
1775        data->internal_file->fp_type = PHAR_UFP;
1776        data->internal_file->offset_abs = data->internal_file->offset = php_stream_tell(p_obj->fp);
1777        data->fp = NULL;
1778        phar_stream_copy_to_stream(fp, p_obj->fp, PHP_STREAM_COPY_ALL, &contents_len);
1779        data->internal_file->uncompressed_filesize = data->internal_file->compressed_filesize =
1780            php_stream_tell(p_obj->fp) - data->internal_file->offset;
1781    }
1782
1783    if (close_fp) {
1784        php_stream_close(fp);
1785    }
1786
1787    add_assoc_string(p_obj->ret, str_key, opened, 0);
1788
1789    if (save) {
1790        efree(save);
1791    }
1792
1793    if (temp) {
1794        efree(temp);
1795    }
1796
1797    data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize = contents_len;
1798    phar_entry_delref(data TSRMLS_CC);
1799
1800    return ZEND_HASH_APPLY_KEEP;
1801}
1802/* }}} */
1803
1804/* {{{ proto array Phar::buildFromDirectory(string base_dir[, string regex])
1805 * Construct a phar archive from an existing directory, recursively.
1806 * Optional second parameter is a regular expression for filtering directory contents.
1807 *
1808 * Return value is an array mapping phar index to actual files added.
1809 */
1810PHP_METHOD(Phar, buildFromDirectory)
1811{
1812    char *dir, *error, *regex = NULL;
1813    int dir_len, regex_len = 0;
1814    zend_bool apply_reg = 0;
1815    zval arg, arg2, *iter, *iteriter, *regexiter = NULL;
1816    struct _phar_t pass;
1817
1818    PHAR_ARCHIVE_OBJECT();
1819
1820    if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) {
1821        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
1822            "Cannot write to archive - write operations restricted by INI setting");
1823        return;
1824    }
1825
1826    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &dir, &dir_len, &regex, &regex_len) == FAILURE) {
1827        RETURN_FALSE;
1828    }
1829
1830    MAKE_STD_ZVAL(iter);
1831
1832    if (SUCCESS != object_init_ex(iter, spl_ce_RecursiveDirectoryIterator)) {
1833        zval_ptr_dtor(&iter);
1834        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to instantiate directory iterator for %s", phar_obj->arc.archive->fname);
1835        RETURN_FALSE;
1836    }
1837
1838    INIT_PZVAL(&arg);
1839    ZVAL_STRINGL(&arg, dir, dir_len, 0);
1840    INIT_PZVAL(&arg2);
1841#if PHP_VERSION_ID < 50300
1842    ZVAL_LONG(&arg2, 0);
1843#else
1844    ZVAL_LONG(&arg2, SPL_FILE_DIR_SKIPDOTS|SPL_FILE_DIR_UNIXPATHS);
1845#endif
1846
1847    zend_call_method_with_2_params(&iter, spl_ce_RecursiveDirectoryIterator,
1848            &spl_ce_RecursiveDirectoryIterator->constructor, "__construct", NULL, &arg, &arg2);
1849
1850    if (EG(exception)) {
1851        zval_ptr_dtor(&iter);
1852        RETURN_FALSE;
1853    }
1854
1855    MAKE_STD_ZVAL(iteriter);
1856
1857    if (SUCCESS != object_init_ex(iteriter, spl_ce_RecursiveIteratorIterator)) {
1858        zval_ptr_dtor(&iter);
1859        zval_ptr_dtor(&iteriter);
1860        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to instantiate directory iterator for %s", phar_obj->arc.archive->fname);
1861        RETURN_FALSE;
1862    }
1863
1864    zend_call_method_with_1_params(&iteriter, spl_ce_RecursiveIteratorIterator,
1865            &spl_ce_RecursiveIteratorIterator->constructor, "__construct", NULL, iter);
1866
1867    if (EG(exception)) {
1868        zval_ptr_dtor(&iter);
1869        zval_ptr_dtor(&iteriter);
1870        RETURN_FALSE;
1871    }
1872
1873    zval_ptr_dtor(&iter);
1874
1875    if (regex_len > 0) {
1876        apply_reg = 1;
1877        MAKE_STD_ZVAL(regexiter);
1878
1879        if (SUCCESS != object_init_ex(regexiter, spl_ce_RegexIterator)) {
1880            zval_ptr_dtor(&iteriter);
1881            zval_dtor(regexiter);
1882            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to instantiate regex iterator for %s", phar_obj->arc.archive->fname);
1883            RETURN_FALSE;
1884        }
1885
1886        INIT_PZVAL(&arg2);
1887        ZVAL_STRINGL(&arg2, regex, regex_len, 0);
1888
1889        zend_call_method_with_2_params(&regexiter, spl_ce_RegexIterator,
1890            &spl_ce_RegexIterator->constructor, "__construct", NULL, iteriter, &arg2);
1891    }
1892
1893    array_init(return_value);
1894
1895    pass.c = apply_reg ? Z_OBJCE_P(regexiter) : Z_OBJCE_P(iteriter);
1896    pass.p = phar_obj;
1897    pass.b = dir;
1898    pass.l = dir_len;
1899    pass.count = 0;
1900    pass.ret = return_value;
1901    pass.fp = php_stream_fopen_tmpfile();
1902    if (pass.fp == NULL) {
1903        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" unable to create temporary file", phar_obj->arc.archive->fname);
1904        return;
1905    }
1906
1907    if (phar_obj->arc.archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->arc.archive) TSRMLS_CC)) {
1908        zval_ptr_dtor(&iteriter);
1909        if (apply_reg) {
1910            zval_ptr_dtor(&regexiter);
1911        }
1912        php_stream_close(pass.fp);
1913        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar_obj->arc.archive->fname);
1914        return;
1915    }
1916
1917    if (SUCCESS == spl_iterator_apply((apply_reg ? regexiter : iteriter), (spl_iterator_apply_func_t) phar_build, (void *) &pass TSRMLS_CC)) {
1918        zval_ptr_dtor(&iteriter);
1919
1920        if (apply_reg) {
1921            zval_ptr_dtor(&regexiter);
1922        }
1923
1924        phar_obj->arc.archive->ufp = pass.fp;
1925        phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC);
1926
1927        if (error) {
1928            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
1929            efree(error);
1930        }
1931
1932    } else {
1933        zval_ptr_dtor(&iteriter);
1934        if (apply_reg) {
1935            zval_ptr_dtor(&regexiter);
1936        }
1937        php_stream_close(pass.fp);
1938    }
1939}
1940/* }}} */
1941
1942/* {{{ proto array Phar::buildFromIterator(Iterator iter[, string base_directory])
1943 * Construct a phar archive from an iterator.  The iterator must return a series of strings
1944 * that are full paths to files that should be added to the phar.  The iterator key should
1945 * be the path that the file will have within the phar archive.
1946 *
1947 * If base directory is specified, then the key will be ignored, and instead the portion of
1948 * the current value minus the base directory will be used
1949 *
1950 * Returned is an array mapping phar index to actual file added
1951 */
1952PHP_METHOD(Phar, buildFromIterator)
1953{
1954    zval *obj;
1955    char *error;
1956    uint base_len = 0;
1957    char *base = NULL;
1958    struct _phar_t pass;
1959
1960    PHAR_ARCHIVE_OBJECT();
1961
1962    if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) {
1963        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
1964            "Cannot write out phar archive, phar is read-only");
1965        return;
1966    }
1967
1968    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|s", &obj, zend_ce_traversable, &base, &base_len) == FAILURE) {
1969        RETURN_FALSE;
1970    }
1971
1972    if (phar_obj->arc.archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->arc.archive) TSRMLS_CC)) {
1973        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar_obj->arc.archive->fname);
1974        return;
1975    }
1976
1977    array_init(return_value);
1978
1979    pass.c = Z_OBJCE_P(obj);
1980    pass.p = phar_obj;
1981    pass.b = base;
1982    pass.l = base_len;
1983    pass.ret = return_value;
1984    pass.count = 0;
1985    pass.fp = php_stream_fopen_tmpfile();
1986    if (pass.fp == NULL) {
1987        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\": unable to create temporary file", phar_obj->arc.archive->fname);
1988        return;
1989    }
1990
1991    if (SUCCESS == spl_iterator_apply(obj, (spl_iterator_apply_func_t) phar_build, (void *) &pass TSRMLS_CC)) {
1992        phar_obj->arc.archive->ufp = pass.fp;
1993        phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC);
1994        if (error) {
1995            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
1996            efree(error);
1997        }
1998    } else {
1999        php_stream_close(pass.fp);
2000    }
2001}
2002/* }}} */
2003
2004/* {{{ proto int Phar::count()
2005 * Returns the number of entries in the Phar archive
2006 */
2007PHP_METHOD(Phar, count)
2008{
2009    PHAR_ARCHIVE_OBJECT();
2010
2011    if (zend_parse_parameters_none() == FAILURE) {
2012        return;
2013    }
2014
2015    RETURN_LONG(zend_hash_num_elements(&phar_obj->arc.archive->manifest));
2016}
2017/* }}} */
2018
2019/* {{{ proto bool Phar::isFileFormat(int format)
2020 * Returns true if the phar archive is based on the tar/zip/phar file format depending
2021 * on whether Phar::TAR, Phar::ZIP or Phar::PHAR was passed in
2022 */
2023PHP_METHOD(Phar, isFileFormat)
2024{
2025    long type;
2026    PHAR_ARCHIVE_OBJECT();
2027
2028    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type) == FAILURE) {
2029        RETURN_FALSE;
2030    }
2031
2032    switch (type) {
2033        case PHAR_FORMAT_TAR:
2034            RETURN_BOOL(phar_obj->arc.archive->is_tar);
2035        case PHAR_FORMAT_ZIP:
2036            RETURN_BOOL(phar_obj->arc.archive->is_zip);
2037        case PHAR_FORMAT_PHAR:
2038            RETURN_BOOL(!phar_obj->arc.archive->is_tar && !phar_obj->arc.archive->is_zip);
2039        default:
2040            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Unknown file format specified");
2041    }
2042}
2043/* }}} */
2044
2045static int phar_copy_file_contents(phar_entry_info *entry, php_stream *fp TSRMLS_DC) /* {{{ */
2046{
2047    char *error;
2048    off_t offset;
2049    phar_entry_info *link;
2050
2051    if (FAILURE == phar_open_entry_fp(entry, &error, 1 TSRMLS_CC)) {
2052        if (error) {
2053            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
2054                "Cannot convert phar archive \"%s\", unable to open entry \"%s\" contents: %s", entry->phar->fname, entry->filename, error);
2055            efree(error);
2056        } else {
2057            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
2058                "Cannot convert phar archive \"%s\", unable to open entry \"%s\" contents", entry->phar->fname, entry->filename);
2059        }
2060        return FAILURE;
2061    }
2062
2063    /* copy old contents in entirety */
2064    phar_seek_efp(entry, 0, SEEK_SET, 0, 1 TSRMLS_CC);
2065    offset = php_stream_tell(fp);
2066    link = phar_get_link_source(entry TSRMLS_CC);
2067
2068    if (!link) {
2069        link = entry;
2070    }
2071
2072    if (SUCCESS != phar_stream_copy_to_stream(phar_get_efp(link, 0 TSRMLS_CC), fp, link->uncompressed_filesize, NULL)) {
2073        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
2074            "Cannot convert phar archive \"%s\", unable to copy entry \"%s\" contents", entry->phar->fname, entry->filename);
2075        return FAILURE;
2076    }
2077
2078    if (entry->fp_type == PHAR_MOD) {
2079        /* save for potential restore on error */
2080        entry->cfp = entry->fp;
2081        entry->fp = NULL;
2082    }
2083
2084    /* set new location of file contents */
2085    entry->fp_type = PHAR_FP;
2086    entry->offset = offset;
2087    return SUCCESS;
2088}
2089/* }}} */
2090
2091static zval *phar_rename_archive(phar_archive_data *phar, char *ext, zend_bool compress TSRMLS_DC) /* {{{ */
2092{
2093    const char *oldname = NULL;
2094    char *oldpath = NULL;
2095    char *basename = NULL, *basepath = NULL;
2096    char *newname = NULL, *newpath = NULL;
2097    zval *ret, arg1;
2098    zend_class_entry *ce;
2099    char *error;
2100    const char *pcr_error;
2101    int ext_len = ext ? strlen(ext) : 0;
2102    int oldname_len;
2103    phar_archive_data **pphar = NULL;
2104    php_stream_statbuf ssb;
2105
2106    if (!ext) {
2107        if (phar->is_zip) {
2108
2109            if (phar->is_data) {
2110                ext = "zip";
2111            } else {
2112                ext = "phar.zip";
2113            }
2114
2115        } else if (phar->is_tar) {
2116
2117            switch (phar->flags) {
2118                case PHAR_FILE_COMPRESSED_GZ:
2119                    if (phar->is_data) {
2120                        ext = "tar.gz";
2121                    } else {
2122                        ext = "phar.tar.gz";
2123                    }
2124                    break;
2125                case PHAR_FILE_COMPRESSED_BZ2:
2126                    if (phar->is_data) {
2127                        ext = "tar.bz2";
2128                    } else {
2129                        ext = "phar.tar.bz2";
2130                    }
2131                    break;
2132                default:
2133                    if (phar->is_data) {
2134                        ext = "tar";
2135                    } else {
2136                        ext = "phar.tar";
2137                    }
2138            }
2139        } else {
2140
2141            switch (phar->flags) {
2142                case PHAR_FILE_COMPRESSED_GZ:
2143                    ext = "phar.gz";
2144                    break;
2145                case PHAR_FILE_COMPRESSED_BZ2:
2146                    ext = "phar.bz2";
2147                    break;
2148                default:
2149                    ext = "phar";
2150            }
2151        }
2152    } else if (phar_path_check(&ext, &ext_len, &pcr_error) > pcr_is_ok) {
2153
2154        if (phar->is_data) {
2155            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "data phar converted from \"%s\" has invalid extension %s", phar->fname, ext);
2156        } else {
2157            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "phar converted from \"%s\" has invalid extension %s", phar->fname, ext);
2158        }
2159        return NULL;
2160    }
2161
2162    if (ext[0] == '.') {
2163        ++ext;
2164    }
2165
2166    oldpath = estrndup(phar->fname, phar->fname_len);
2167    oldname = zend_memrchr(phar->fname, '/', phar->fname_len);
2168    ++oldname;
2169    oldname_len = strlen(oldname);
2170
2171    basename = estrndup(oldname, oldname_len);
2172    spprintf(&newname, 0, "%s.%s", strtok(basename, "."), ext);
2173    efree(basename);
2174
2175
2176
2177    basepath = estrndup(oldpath, (strlen(oldpath) - oldname_len));
2178    phar->fname_len = spprintf(&newpath, 0, "%s%s", basepath, newname);
2179    phar->fname = newpath;
2180    phar->ext = newpath + phar->fname_len - strlen(ext) - 1;
2181    efree(basepath);
2182    efree(newname);
2183
2184    if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_find(&cached_phars, newpath, phar->fname_len, (void **) &pphar)) {
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, new phar name is in phar.cache_list", phar->fname);
2187        return NULL;
2188    }
2189
2190    if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), newpath, phar->fname_len, (void **) &pphar)) {
2191        if ((*pphar)->fname_len == phar->fname_len && !memcmp((*pphar)->fname, phar->fname, phar->fname_len)) {
2192            if (!zend_hash_num_elements(&phar->manifest)) {
2193                (*pphar)->is_tar = phar->is_tar;
2194                (*pphar)->is_zip = phar->is_zip;
2195                (*pphar)->is_data = phar->is_data;
2196                (*pphar)->flags = phar->flags;
2197                (*pphar)->fp = phar->fp;
2198                phar->fp = NULL;
2199                phar_destroy_phar_data(phar TSRMLS_CC);
2200                phar = *pphar;
2201                phar->refcount++;
2202                newpath = oldpath;
2203                goto its_ok;
2204            }
2205        }
2206
2207        efree(oldpath);
2208        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);
2209        return NULL;
2210    }
2211its_ok:
2212    if (SUCCESS == php_stream_stat_path(newpath, &ssb)) {
2213        efree(oldpath);
2214        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "phar \"%s\" exists and must be unlinked prior to conversion", newpath);
2215        return NULL;
2216    }
2217    if (!phar->is_data) {
2218        if (SUCCESS != phar_detect_phar_fname_ext(newpath, phar->fname_len, (const char **) &(phar->ext), &(phar->ext_len), 1, 1, 1 TSRMLS_CC)) {
2219            efree(oldpath);
2220            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "phar \"%s\" has invalid extension %s", phar->fname, ext);
2221            return NULL;
2222        }
2223
2224        if (phar->alias) {
2225            if (phar->is_temporary_alias) {
2226                phar->alias = NULL;
2227                phar->alias_len = 0;
2228            } else {
2229                phar->alias = estrndup(newpath, strlen(newpath));
2230                phar->alias_len = strlen(newpath);
2231                phar->is_temporary_alias = 1;
2232                zend_hash_update(&(PHAR_GLOBALS->phar_alias_map), newpath, phar->fname_len, (void*)&phar, sizeof(phar_archive_data*), NULL);
2233            }
2234        }
2235
2236    } else {
2237
2238        if (SUCCESS != phar_detect_phar_fname_ext(newpath, phar->fname_len, (const char **) &(phar->ext), &(phar->ext_len), 0, 1, 1 TSRMLS_CC)) {
2239            efree(oldpath);
2240            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "data phar \"%s\" has invalid extension %s", phar->fname, ext);
2241            return NULL;
2242        }
2243
2244        phar->alias = NULL;
2245        phar->alias_len = 0;
2246    }
2247
2248    if ((!pphar || phar == *pphar) && SUCCESS != zend_hash_update(&(PHAR_GLOBALS->phar_fname_map), newpath, phar->fname_len, (void*)&phar, sizeof(phar_archive_data*), NULL)) {
2249        efree(oldpath);
2250        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to add newly converted phar \"%s\" to the list of phars", phar->fname);
2251        return NULL;
2252    }
2253
2254    phar_flush(phar, 0, 0, 1, &error TSRMLS_CC);
2255
2256    if (error) {
2257        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "%s", error);
2258        efree(error);
2259        efree(oldpath);
2260        return NULL;
2261    }
2262
2263    efree(oldpath);
2264
2265    if (phar->is_data) {
2266        ce = phar_ce_data;
2267    } else {
2268        ce = phar_ce_archive;
2269    }
2270
2271    MAKE_STD_ZVAL(ret);
2272
2273    if (SUCCESS != object_init_ex(ret, ce)) {
2274        zval_dtor(ret);
2275        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to instantiate phar object when converting archive \"%s\"", phar->fname);
2276        return NULL;
2277    }
2278
2279    INIT_PZVAL(&arg1);
2280    ZVAL_STRINGL(&arg1, phar->fname, phar->fname_len, 0);
2281
2282    zend_call_method_with_1_params(&ret, ce, &ce->constructor, "__construct", NULL, &arg1);
2283    return ret;
2284}
2285/* }}} */
2286
2287static zval *phar_convert_to_other(phar_archive_data *source, int convert, char *ext, php_uint32 flags TSRMLS_DC) /* {{{ */
2288{
2289    phar_archive_data *phar;
2290    phar_entry_info *entry, newentry;
2291    zval *ret;
2292
2293    /* invalidate phar cache */
2294    PHAR_G(last_phar) = NULL;
2295    PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
2296
2297    phar = (phar_archive_data *) ecalloc(1, sizeof(phar_archive_data));
2298    /* set whole-archive compression and type from parameter */
2299    phar->flags = flags;
2300    phar->is_data = source->is_data;
2301
2302    switch (convert) {
2303        case PHAR_FORMAT_TAR:
2304            phar->is_tar = 1;
2305            break;
2306        case PHAR_FORMAT_ZIP:
2307            phar->is_zip = 1;
2308            break;
2309        default:
2310            phar->is_data = 0;
2311            break;
2312    }
2313
2314    zend_hash_init(&(phar->manifest), sizeof(phar_entry_info),
2315        zend_get_hash_value, destroy_phar_manifest_entry, 0);
2316    zend_hash_init(&phar->mounted_dirs, sizeof(char *),
2317        zend_get_hash_value, NULL, 0);
2318    zend_hash_init(&phar->virtual_dirs, sizeof(char *),
2319        zend_get_hash_value, NULL, 0);
2320
2321    phar->fp = php_stream_fopen_tmpfile();
2322    if (phar->fp == NULL) {
2323        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "unable to create temporary file");
2324        return NULL;
2325    }
2326    phar->fname = source->fname;
2327    phar->fname_len = source->fname_len;
2328    phar->is_temporary_alias = source->is_temporary_alias;
2329    phar->alias = source->alias;
2330
2331    if (source->metadata) {
2332        zval *t;
2333
2334        t = source->metadata;
2335        ALLOC_ZVAL(phar->metadata);
2336        *phar->metadata = *t;
2337        zval_copy_ctor(phar->metadata);
2338#if PHP_VERSION_ID < 50300
2339        phar->metadata->refcount = 1;
2340#else
2341        Z_SET_REFCOUNT_P(phar->metadata, 1);
2342#endif
2343
2344        phar->metadata_len = 0;
2345    }
2346
2347    /* first copy each file's uncompressed contents to a temporary file and set per-file flags */
2348    for (zend_hash_internal_pointer_reset(&source->manifest); SUCCESS == zend_hash_has_more_elements(&source->manifest); zend_hash_move_forward(&source->manifest)) {
2349
2350        if (FAILURE == zend_hash_get_current_data(&source->manifest, (void **) &entry)) {
2351            zend_hash_destroy(&(phar->manifest));
2352            php_stream_close(phar->fp);
2353            efree(phar);
2354            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
2355                "Cannot convert phar archive \"%s\"", source->fname);
2356            return NULL;
2357        }
2358
2359        newentry = *entry;
2360
2361        if (newentry.link) {
2362            newentry.link = estrdup(newentry.link);
2363            goto no_copy;
2364        }
2365
2366        if (newentry.tmp) {
2367            newentry.tmp = estrdup(newentry.tmp);
2368            goto no_copy;
2369        }
2370
2371        newentry.metadata_str.c = 0;
2372
2373        if (FAILURE == phar_copy_file_contents(&newentry, phar->fp TSRMLS_CC)) {
2374            zend_hash_destroy(&(phar->manifest));
2375            php_stream_close(phar->fp);
2376            efree(phar);
2377            /* exception already thrown */
2378            return NULL;
2379        }
2380no_copy:
2381        newentry.filename = estrndup(newentry.filename, newentry.filename_len);
2382
2383        if (newentry.metadata) {
2384            zval *t;
2385
2386            t = newentry.metadata;
2387            ALLOC_ZVAL(newentry.metadata);
2388            *newentry.metadata = *t;
2389            zval_copy_ctor(newentry.metadata);
2390#if PHP_VERSION_ID < 50300
2391            newentry.metadata->refcount = 1;
2392#else
2393            Z_SET_REFCOUNT_P(newentry.metadata, 1);
2394#endif
2395
2396            newentry.metadata_str.c = NULL;
2397            newentry.metadata_str.len = 0;
2398        }
2399
2400        newentry.is_zip = phar->is_zip;
2401        newentry.is_tar = phar->is_tar;
2402
2403        if (newentry.is_tar) {
2404            newentry.tar_type = (entry->is_dir ? TAR_DIR : TAR_FILE);
2405        }
2406
2407        newentry.is_modified = 1;
2408        newentry.phar = phar;
2409        newentry.old_flags = newentry.flags & ~PHAR_ENT_COMPRESSION_MASK; /* remove compression from old_flags */
2410        phar_set_inode(&newentry TSRMLS_CC);
2411        zend_hash_add(&(phar->manifest), newentry.filename, newentry.filename_len, (void*)&newentry, sizeof(phar_entry_info), NULL);
2412        phar_add_virtual_dirs(phar, newentry.filename, newentry.filename_len TSRMLS_CC);
2413    }
2414
2415    if ((ret = phar_rename_archive(phar, ext, 0 TSRMLS_CC))) {
2416        return ret;
2417    } else {
2418        zend_hash_destroy(&(phar->manifest));
2419        zend_hash_destroy(&(phar->mounted_dirs));
2420        zend_hash_destroy(&(phar->virtual_dirs));
2421        php_stream_close(phar->fp);
2422        efree(phar->fname);
2423        efree(phar);
2424        return NULL;
2425    }
2426}
2427/* }}} */
2428
2429/* {{{ proto object Phar::convertToExecutable([int format[, int compression [, string file_ext]]])
2430 * Convert a phar.tar or phar.zip archive to the phar file format. The
2431 * optional parameter allows the user to determine the new
2432 * filename extension (default is phar).
2433 */
2434PHP_METHOD(Phar, convertToExecutable)
2435{
2436    char *ext = NULL;
2437    int is_data, ext_len = 0;
2438    php_uint32 flags;
2439    zval *ret;
2440    /* a number that is not 0, 1 or 2 (Which is also Greg's birthday, so there) */
2441    long format = 9021976, method = 9021976;
2442    PHAR_ARCHIVE_OBJECT();
2443
2444    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lls", &format, &method, &ext, &ext_len) == FAILURE) {
2445        return;
2446    }
2447
2448    if (PHAR_G(readonly)) {
2449        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
2450            "Cannot write out executable phar archive, phar is read-only");
2451        return;
2452    }
2453
2454    switch (format) {
2455        case 9021976:
2456        case PHAR_FORMAT_SAME: /* null is converted to 0 */
2457            /* by default, use the existing format */
2458            if (phar_obj->arc.archive->is_tar) {
2459                format = PHAR_FORMAT_TAR;
2460            } else if (phar_obj->arc.archive->is_zip) {
2461                format = PHAR_FORMAT_ZIP;
2462            } else {
2463                format = PHAR_FORMAT_PHAR;
2464            }
2465            break;
2466        case PHAR_FORMAT_PHAR:
2467        case PHAR_FORMAT_TAR:
2468        case PHAR_FORMAT_ZIP:
2469            break;
2470        default:
2471            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
2472                "Unknown file format specified, please pass one of Phar::PHAR, Phar::TAR or Phar::ZIP");
2473            return;
2474    }
2475
2476    switch (method) {
2477        case 9021976:
2478            flags = phar_obj->arc.archive->flags & PHAR_FILE_COMPRESSION_MASK;
2479            break;
2480        case 0:
2481            flags = PHAR_FILE_COMPRESSED_NONE;
2482            break;
2483        case PHAR_ENT_COMPRESSED_GZ:
2484            if (format == PHAR_FORMAT_ZIP) {
2485                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
2486                    "Cannot compress entire archive with gzip, zip archives do not support whole-archive compression");
2487                return;
2488            }
2489
2490            if (!PHAR_G(has_zlib)) {
2491                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
2492                    "Cannot compress entire archive with gzip, enable ext/zlib in php.ini");
2493                return;
2494            }
2495
2496            flags = PHAR_FILE_COMPRESSED_GZ;
2497            break;
2498        case PHAR_ENT_COMPRESSED_BZ2:
2499            if (format == PHAR_FORMAT_ZIP) {
2500                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
2501                    "Cannot compress entire archive with bz2, zip archives do not support whole-archive compression");
2502                return;
2503            }
2504
2505            if (!PHAR_G(has_bz2)) {
2506                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
2507                    "Cannot compress entire archive with bz2, enable ext/bz2 in php.ini");
2508                return;
2509            }
2510
2511            flags = PHAR_FILE_COMPRESSED_BZ2;
2512            break;
2513        default:
2514            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
2515                "Unknown compression specified, please pass one of Phar::GZ or Phar::BZ2");
2516            return;
2517    }
2518
2519    is_data = phar_obj->arc.archive->is_data;
2520    phar_obj->arc.archive->is_data = 0;
2521    ret = phar_convert_to_other(phar_obj->arc.archive, format, ext, flags TSRMLS_CC);
2522    phar_obj->arc.archive->is_data = is_data;
2523
2524    if (ret) {
2525        RETURN_ZVAL(ret, 1, 1);
2526    } else {
2527        RETURN_NULL();
2528    }
2529}
2530/* }}} */
2531
2532/* {{{ proto object Phar::convertToData([int format[, int compression [, string file_ext]]])
2533 * Convert an archive to a non-executable .tar or .zip.
2534 * The optional parameter allows the user to determine the new
2535 * filename extension (default is .zip or .tar).
2536 */
2537PHP_METHOD(Phar, convertToData)
2538{
2539    char *ext = NULL;
2540    int is_data, ext_len = 0;
2541    php_uint32 flags;
2542    zval *ret;
2543    /* a number that is not 0, 1 or 2 (Which is also Greg's birthday so there) */
2544    long format = 9021976, method = 9021976;
2545    PHAR_ARCHIVE_OBJECT();
2546
2547    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lls", &format, &method, &ext, &ext_len) == FAILURE) {
2548        return;
2549    }
2550
2551    switch (format) {
2552        case 9021976:
2553        case PHAR_FORMAT_SAME: /* null is converted to 0 */
2554            /* by default, use the existing format */
2555            if (phar_obj->arc.archive->is_tar) {
2556                format = PHAR_FORMAT_TAR;
2557            } else if (phar_obj->arc.archive->is_zip) {
2558                format = PHAR_FORMAT_ZIP;
2559            } else {
2560                zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
2561                    "Cannot write out data phar archive, use Phar::TAR or Phar::ZIP");
2562                return;
2563            }
2564            break;
2565        case PHAR_FORMAT_PHAR:
2566            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
2567                "Cannot write out data phar archive, use Phar::TAR or Phar::ZIP");
2568            return;
2569        case PHAR_FORMAT_TAR:
2570        case PHAR_FORMAT_ZIP:
2571            break;
2572        default:
2573            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
2574                "Unknown file format specified, please pass one of Phar::TAR or Phar::ZIP");
2575            return;
2576    }
2577
2578    switch (method) {
2579        case 9021976:
2580            flags = phar_obj->arc.archive->flags & PHAR_FILE_COMPRESSION_MASK;
2581            break;
2582        case 0:
2583            flags = PHAR_FILE_COMPRESSED_NONE;
2584            break;
2585        case PHAR_ENT_COMPRESSED_GZ:
2586            if (format == PHAR_FORMAT_ZIP) {
2587                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
2588                    "Cannot compress entire archive with gzip, zip archives do not support whole-archive compression");
2589                return;
2590            }
2591
2592            if (!PHAR_G(has_zlib)) {
2593                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
2594                    "Cannot compress entire archive with gzip, enable ext/zlib in php.ini");
2595                return;
2596            }
2597
2598            flags = PHAR_FILE_COMPRESSED_GZ;
2599            break;
2600        case PHAR_ENT_COMPRESSED_BZ2:
2601            if (format == PHAR_FORMAT_ZIP) {
2602                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
2603                    "Cannot compress entire archive with bz2, zip archives do not support whole-archive compression");
2604                return;
2605            }
2606
2607            if (!PHAR_G(has_bz2)) {
2608                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
2609                    "Cannot compress entire archive with bz2, enable ext/bz2 in php.ini");
2610                return;
2611            }
2612
2613            flags = PHAR_FILE_COMPRESSED_BZ2;
2614            break;
2615        default:
2616            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
2617                "Unknown compression specified, please pass one of Phar::GZ or Phar::BZ2");
2618            return;
2619    }
2620
2621    is_data = phar_obj->arc.archive->is_data;
2622    phar_obj->arc.archive->is_data = 1;
2623    ret = phar_convert_to_other(phar_obj->arc.archive, format, ext, flags TSRMLS_CC);
2624    phar_obj->arc.archive->is_data = is_data;
2625
2626    if (ret) {
2627        RETURN_ZVAL(ret, 1, 1);
2628    } else {
2629        RETURN_NULL();
2630    }
2631}
2632/* }}} */
2633
2634/* {{{ proto int|false Phar::isCompressed()
2635 * Returns Phar::GZ or PHAR::BZ2 if the entire archive is compressed
2636 * (.tar.gz/tar.bz2 and so on), or FALSE otherwise.
2637 */
2638PHP_METHOD(Phar, isCompressed)
2639{
2640    PHAR_ARCHIVE_OBJECT();
2641
2642    if (zend_parse_parameters_none() == FAILURE) {
2643        return;
2644    }
2645
2646    if (phar_obj->arc.archive->flags & PHAR_FILE_COMPRESSED_GZ) {
2647        RETURN_LONG(PHAR_ENT_COMPRESSED_GZ);
2648    }
2649
2650    if (phar_obj->arc.archive->flags & PHAR_FILE_COMPRESSED_BZ2) {
2651        RETURN_LONG(PHAR_ENT_COMPRESSED_BZ2);
2652    }
2653
2654    RETURN_FALSE;
2655}
2656/* }}} */
2657
2658/* {{{ proto bool Phar::isWritable()
2659 * Returns true if phar.readonly=0 or phar is a PharData AND the actual file is writable.
2660 */
2661PHP_METHOD(Phar, isWritable)
2662{
2663    php_stream_statbuf ssb;
2664    PHAR_ARCHIVE_OBJECT();
2665
2666    if (zend_parse_parameters_none() == FAILURE) {
2667        return;
2668    }
2669
2670    if (!phar_obj->arc.archive->is_writeable) {
2671        RETURN_FALSE;
2672    }
2673
2674    if (SUCCESS != php_stream_stat_path(phar_obj->arc.archive->fname, &ssb)) {
2675        if (phar_obj->arc.archive->is_brandnew) {
2676            /* assume it works if the file doesn't exist yet */
2677            RETURN_TRUE;
2678        }
2679        RETURN_FALSE;
2680    }
2681
2682    RETURN_BOOL((ssb.sb.st_mode & (S_IWOTH | S_IWGRP | S_IWUSR)) != 0);
2683}
2684/* }}} */
2685
2686/* {{{ proto bool Phar::delete(string entry)
2687 * Deletes a named file within the archive.
2688 */
2689PHP_METHOD(Phar, delete)
2690{
2691    char *fname;
2692    int fname_len;
2693    char *error;
2694    phar_entry_info *entry;
2695    PHAR_ARCHIVE_OBJECT();
2696
2697    if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) {
2698        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
2699            "Cannot write out phar archive, phar is read-only");
2700        return;
2701    }
2702
2703    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &fname, &fname_len) == FAILURE) {
2704        RETURN_FALSE;
2705    }
2706
2707    if (phar_obj->arc.archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->arc.archive) TSRMLS_CC)) {
2708        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar_obj->arc.archive->fname);
2709        return;
2710    }
2711    if (zend_hash_exists(&phar_obj->arc.archive->manifest, fname, (uint) fname_len)) {
2712        if (SUCCESS == zend_hash_find(&phar_obj->arc.archive->manifest, fname, (uint) fname_len, (void**)&entry)) {
2713            if (entry->is_deleted) {
2714                /* entry is deleted, but has not been flushed to disk yet */
2715                RETURN_TRUE;
2716            } else {
2717                entry->is_deleted = 1;
2718                entry->is_modified = 1;
2719                phar_obj->arc.archive->is_modified = 1;
2720            }
2721        }
2722    } else {
2723        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s does not exist and cannot be deleted", fname);
2724        RETURN_FALSE;
2725    }
2726
2727    phar_flush(phar_obj->arc.archive, NULL, 0, 0, &error TSRMLS_CC);
2728    if (error) {
2729        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
2730        efree(error);
2731    }
2732
2733    RETURN_TRUE;
2734}
2735/* }}} */
2736
2737/* {{{ proto int Phar::getAlias()
2738 * Returns the alias for the Phar or NULL.
2739 */
2740PHP_METHOD(Phar, getAlias)
2741{
2742    PHAR_ARCHIVE_OBJECT();
2743
2744    if (zend_parse_parameters_none() == FAILURE) {
2745        return;
2746    }
2747
2748    if (phar_obj->arc.archive->alias && phar_obj->arc.archive->alias != phar_obj->arc.archive->fname) {
2749        RETURN_STRINGL(phar_obj->arc.archive->alias, phar_obj->arc.archive->alias_len, 1);
2750    }
2751}
2752/* }}} */
2753
2754/* {{{ proto int Phar::getPath()
2755 * Returns the real path to the phar archive on disk
2756 */
2757PHP_METHOD(Phar, getPath)
2758{
2759    PHAR_ARCHIVE_OBJECT();
2760
2761    if (zend_parse_parameters_none() == FAILURE) {
2762        return;
2763    }
2764
2765    RETURN_STRINGL(phar_obj->arc.archive->fname, phar_obj->arc.archive->fname_len, 1);
2766}
2767/* }}} */
2768
2769/* {{{ proto bool Phar::setAlias(string alias)
2770 * Sets the alias for a Phar archive. The default value is the full path
2771 * to the archive.
2772 */
2773PHP_METHOD(Phar, setAlias)
2774{
2775    char *alias, *error, *oldalias;
2776    phar_archive_data **fd_ptr;
2777    int alias_len, oldalias_len, old_temp, readd = 0;
2778
2779    PHAR_ARCHIVE_OBJECT();
2780
2781    if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) {
2782        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
2783            "Cannot write out phar archive, phar is read-only");
2784        RETURN_FALSE;
2785    }
2786
2787    /* invalidate phar cache */
2788    PHAR_G(last_phar) = NULL;
2789    PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
2790
2791    if (phar_obj->arc.archive->is_data) {
2792        if (phar_obj->arc.archive->is_tar) {
2793            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
2794                "A Phar alias cannot be set in a plain tar archive");
2795        } else {
2796            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
2797                "A Phar alias cannot be set in a plain zip archive");
2798        }
2799        RETURN_FALSE;
2800    }
2801
2802    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &alias, &alias_len) == SUCCESS) {
2803        if (alias_len == phar_obj->arc.archive->alias_len && memcmp(phar_obj->arc.archive->alias, alias, alias_len) == 0) {
2804            RETURN_TRUE;
2805        }
2806        if (alias_len && SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void**)&fd_ptr)) {
2807            spprintf(&error, 0, "alias \"%s\" is already used for archive \"%s\" and cannot be used for other archives", alias, (*fd_ptr)->fname);
2808            if (SUCCESS == phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) {
2809                efree(error);
2810                goto valid_alias;
2811            }
2812            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
2813            efree(error);
2814            RETURN_FALSE;
2815        }
2816        if (!phar_validate_alias(alias, alias_len)) {
2817            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
2818                "Invalid alias \"%s\" specified for phar \"%s\"", alias, phar_obj->arc.archive->fname);
2819            RETURN_FALSE;
2820        }
2821valid_alias:
2822        if (phar_obj->arc.archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->arc.archive) TSRMLS_CC)) {
2823            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar_obj->arc.archive->fname);
2824            return;
2825        }
2826        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)) {
2827            zend_hash_del(&(PHAR_GLOBALS->phar_alias_map), phar_obj->arc.archive->alias, phar_obj->arc.archive->alias_len);
2828            readd = 1;
2829        }
2830
2831        oldalias = phar_obj->arc.archive->alias;
2832        oldalias_len = phar_obj->arc.archive->alias_len;
2833        old_temp = phar_obj->arc.archive->is_temporary_alias;
2834
2835        if (alias_len) {
2836            phar_obj->arc.archive->alias = estrndup(alias, alias_len);
2837        } else {
2838            phar_obj->arc.archive->alias = NULL;
2839        }
2840
2841        phar_obj->arc.archive->alias_len = alias_len;
2842        phar_obj->arc.archive->is_temporary_alias = 0;
2843        phar_flush(phar_obj->arc.archive, NULL, 0, 0, &error TSRMLS_CC);
2844
2845        if (error) {
2846            phar_obj->arc.archive->alias = oldalias;
2847            phar_obj->arc.archive->alias_len = oldalias_len;
2848            phar_obj->arc.archive->is_temporary_alias = old_temp;
2849            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
2850            if (readd) {
2851                zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), oldalias, oldalias_len, (void*)&(phar_obj->arc.archive), sizeof(phar_archive_data*), NULL);
2852            }
2853            efree(error);
2854            RETURN_FALSE;
2855        }
2856
2857        zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&(phar_obj->arc.archive), sizeof(phar_archive_data*), NULL);
2858
2859        if (oldalias) {
2860            efree(oldalias);
2861        }
2862
2863        RETURN_TRUE;
2864    }
2865
2866    RETURN_FALSE;
2867}
2868/* }}} */
2869
2870/* {{{ proto string Phar::getVersion()
2871 * Return version info of Phar archive
2872 */
2873PHP_METHOD(Phar, getVersion)
2874{
2875    PHAR_ARCHIVE_OBJECT();
2876
2877    if (zend_parse_parameters_none() == FAILURE) {
2878        return;
2879    }
2880
2881    RETURN_STRING(phar_obj->arc.archive->version, 1);
2882}
2883/* }}} */
2884
2885/* {{{ proto void Phar::startBuffering()
2886 * Do not flush a writeable phar (save its contents) until explicitly requested
2887 */
2888PHP_METHOD(Phar, startBuffering)
2889{
2890    PHAR_ARCHIVE_OBJECT();
2891
2892    if (zend_parse_parameters_none() == FAILURE) {
2893        return;
2894    }
2895
2896    phar_obj->arc.archive->donotflush = 1;
2897}
2898/* }}} */
2899
2900/* {{{ proto bool Phar::isBuffering()
2901 * Returns whether write operations are flushing to disk immediately.
2902 */
2903PHP_METHOD(Phar, isBuffering)
2904{
2905    PHAR_ARCHIVE_OBJECT();
2906
2907    if (zend_parse_parameters_none() == FAILURE) {
2908        return;
2909    }
2910
2911    RETURN_BOOL(phar_obj->arc.archive->donotflush);
2912}
2913/* }}} */
2914
2915/* {{{ proto bool Phar::stopBuffering()
2916 * Saves the contents of a modified archive to disk.
2917 */
2918PHP_METHOD(Phar, stopBuffering)
2919{
2920    char *error;
2921
2922    PHAR_ARCHIVE_OBJECT();
2923
2924    if (zend_parse_parameters_none() == FAILURE) {
2925        return;
2926    }
2927
2928    if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) {
2929        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
2930            "Cannot write out phar archive, phar is read-only");
2931        return;
2932    }
2933
2934    phar_obj->arc.archive->donotflush = 0;
2935    phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC);
2936
2937    if (error) {
2938        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
2939        efree(error);
2940    }
2941}
2942/* }}} */
2943
2944/* {{{ proto bool Phar::setStub(string|stream stub [, int len])
2945 * Change the stub in a phar, phar.tar or phar.zip archive to something other
2946 * than the default. The stub *must* end with a call to __HALT_COMPILER().
2947 */
2948PHP_METHOD(Phar, setStub)
2949{
2950    zval *zstub;
2951    char *stub, *error;
2952    int stub_len;
2953    long len = -1;
2954    php_stream *stream;
2955    PHAR_ARCHIVE_OBJECT();
2956
2957    if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) {
2958        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
2959            "Cannot change stub, phar is read-only");
2960        return;
2961    }
2962
2963    if (phar_obj->arc.archive->is_data) {
2964        if (phar_obj->arc.archive->is_tar) {
2965            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
2966                "A Phar stub cannot be set in a plain tar archive");
2967        } else {
2968            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
2969                "A Phar stub cannot be set in a plain zip archive");
2970        }
2971        return;
2972    }
2973
2974    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &zstub, &len) == SUCCESS) {
2975        if ((php_stream_from_zval_no_verify(stream, &zstub)) != NULL) {
2976            if (len > 0) {
2977                len = -len;
2978            } else {
2979                len = -1;
2980            }
2981            if (phar_obj->arc.archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->arc.archive) TSRMLS_CC)) {
2982                zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar_obj->arc.archive->fname);
2983                return;
2984            }
2985            phar_flush(phar_obj->arc.archive, (char *) &zstub, len, 0, &error TSRMLS_CC);
2986            if (error) {
2987                zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
2988                efree(error);
2989            }
2990            RETURN_TRUE;
2991        } else {
2992            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
2993                "Cannot change stub, unable to read from input stream");
2994        }
2995    } else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &stub, &stub_len) == SUCCESS) {
2996        if (phar_obj->arc.archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->arc.archive) TSRMLS_CC)) {
2997            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar_obj->arc.archive->fname);
2998            return;
2999        }
3000        phar_flush(phar_obj->arc.archive, stub, stub_len, 0, &error TSRMLS_CC);
3001
3002        if (error) {
3003            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
3004            efree(error);
3005        }
3006
3007        RETURN_TRUE;
3008    }
3009
3010    RETURN_FALSE;
3011}
3012/* }}} */
3013
3014/* {{{ proto bool Phar::setDefaultStub([string index[, string webindex]])
3015 * In a pure phar archive, sets a stub that can be used to run the archive
3016 * regardless of whether the phar extension is available. The first parameter
3017 * is the CLI startup filename, which defaults to "index.php". The second
3018 * parameter is the web startup filename and also defaults to "index.php"
3019 * (falling back to CLI behaviour).
3020 * Both parameters are optional.
3021 * In a phar.zip or phar.tar archive, the default stub is used only to
3022 * identify the archive to the extension as a Phar object. This allows the
3023 * extension to treat phar.zip and phar.tar types as honorary phars. Since
3024 * files cannot be loaded via this kind of stub, no parameters are accepted
3025 * when the Phar object is zip- or tar-based.
3026 */
3027PHP_METHOD(Phar, setDefaultStub)
3028{
3029    char *index = NULL, *webindex = NULL, *error = NULL, *stub = NULL;
3030    int index_len = 0, webindex_len = 0, created_stub = 0;
3031    size_t stub_len = 0;
3032    PHAR_ARCHIVE_OBJECT();
3033
3034    if (phar_obj->arc.archive->is_data) {
3035        if (phar_obj->arc.archive->is_tar) {
3036            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
3037                "A Phar stub cannot be set in a plain tar archive");
3038        } else {
3039            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
3040                "A Phar stub cannot be set in a plain zip archive");
3041        }
3042        return;
3043    }
3044
3045    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s", &index, &index_len, &webindex, &webindex_len) == FAILURE) {
3046        RETURN_FALSE;
3047    }
3048
3049    if (ZEND_NUM_ARGS() > 0 && (phar_obj->arc.archive->is_tar || phar_obj->arc.archive->is_zip)) {
3050        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());
3051        RETURN_FALSE;
3052    }
3053
3054    if (PHAR_G(readonly)) {
3055        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
3056            "Cannot change stub: phar.readonly=1");
3057        RETURN_FALSE;
3058    }
3059
3060    if (!phar_obj->arc.archive->is_tar && !phar_obj->arc.archive->is_zip) {
3061        stub = phar_create_default_stub(index, webindex, &stub_len, &error TSRMLS_CC);
3062
3063        if (error) {
3064            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "%s", error);
3065            efree(error);
3066            if (stub) {
3067                efree(stub);
3068            }
3069            RETURN_FALSE;
3070        }
3071
3072        created_stub = 1;
3073    }
3074
3075    if (phar_obj->arc.archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->arc.archive) TSRMLS_CC)) {
3076        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar_obj->arc.archive->fname);
3077        return;
3078    }
3079    phar_flush(phar_obj->arc.archive, stub, stub_len, 1, &error TSRMLS_CC);
3080
3081    if (created_stub) {
3082        efree(stub);
3083    }
3084
3085    if (error) {
3086        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
3087        efree(error);
3088        RETURN_FALSE;
3089    }
3090
3091    RETURN_TRUE;
3092}
3093/* }}} */
3094
3095/* {{{ proto array Phar::setSignatureAlgorithm(int sigtype[, string privatekey])
3096 * Sets the signature algorithm for a phar and applies it. The signature
3097 * algorithm must be one of Phar::MD5, Phar::SHA1, Phar::SHA256,
3098 * Phar::SHA512, or Phar::OPENSSL. Note that zip- based phar archives
3099 * cannot support signatures.
3100 */
3101PHP_METHOD(Phar, setSignatureAlgorithm)
3102{
3103    long algo;
3104    char *error, *key = NULL;
3105    int key_len = 0;
3106
3107    PHAR_ARCHIVE_OBJECT();
3108
3109    if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) {
3110        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
3111            "Cannot set signature algorithm, phar is read-only");
3112        return;
3113    }
3114
3115    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "l|s", &algo, &key, &key_len) != SUCCESS) {
3116        return;
3117    }
3118
3119    switch (algo) {
3120        case PHAR_SIG_SHA256:
3121        case PHAR_SIG_SHA512:
3122#ifndef PHAR_HASH_OK
3123            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
3124                "SHA-256 and SHA-512 signatures are only supported if the hash extension is enabled and built non-shared");
3125            return;
3126#endif
3127        case PHAR_SIG_MD5:
3128        case PHAR_SIG_SHA1:
3129        case PHAR_SIG_OPENSSL:
3130            if (phar_obj->arc.archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->arc.archive) TSRMLS_CC)) {
3131                zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar_obj->arc.archive->fname);
3132                return;
3133            }
3134            phar_obj->arc.archive->sig_flags = algo;
3135            phar_obj->arc.archive->is_modified = 1;
3136            PHAR_G(openssl_privatekey) = key;
3137            PHAR_G(openssl_privatekey_len) = key_len;
3138
3139            phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC);
3140            if (error) {
3141                zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
3142                efree(error);
3143            }
3144            break;
3145        default:
3146            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
3147                "Unknown signature algorithm specified");
3148    }
3149}
3150/* }}} */
3151
3152/* {{{ proto array|false Phar::getSignature()
3153 * Returns a hash signature, or FALSE if the archive is unsigned.
3154 */
3155PHP_METHOD(Phar, getSignature)
3156{
3157    PHAR_ARCHIVE_OBJECT();
3158
3159    if (zend_parse_parameters_none() == FAILURE) {
3160        return;
3161    }
3162
3163    if (phar_obj->arc.archive->signature) {
3164        char *unknown;
3165        int unknown_len;
3166
3167        array_init(return_value);
3168        add_assoc_stringl(return_value, "hash", phar_obj->arc.archive->signature, phar_obj->arc.archive->sig_len, 1);
3169        switch(phar_obj->arc.archive->sig_flags) {
3170            case PHAR_SIG_MD5:
3171                add_assoc_stringl(return_value, "hash_type", "MD5", 3, 1);
3172                break;
3173            case PHAR_SIG_SHA1:
3174                add_assoc_stringl(return_value, "hash_type", "SHA-1", 5, 1);
3175                break;
3176            case PHAR_SIG_SHA256:
3177                add_assoc_stringl(return_value, "hash_type", "SHA-256", 7, 1);
3178                break;
3179            case PHAR_SIG_SHA512:
3180                add_assoc_stringl(return_value, "hash_type", "SHA-512", 7, 1);
3181                break;
3182            case PHAR_SIG_OPENSSL:
3183                add_assoc_stringl(return_value, "hash_type", "OpenSSL", 7, 1);
3184                break;
3185            default:
3186                unknown_len = spprintf(&unknown, 0, "Unknown (%u)", phar_obj->arc.archive->sig_flags);
3187                add_assoc_stringl(return_value, "hash_type", unknown, unknown_len, 0);
3188                break;
3189        }
3190    } else {
3191        RETURN_FALSE;
3192    }
3193}
3194/* }}} */
3195
3196/* {{{ proto bool Phar::getModified()
3197 * Return whether phar was modified
3198 */
3199PHP_METHOD(Phar, getModified)
3200{
3201    PHAR_ARCHIVE_OBJECT();
3202
3203    if (zend_parse_parameters_none() == FAILURE) {
3204        return;
3205    }
3206
3207    RETURN_BOOL(phar_obj->arc.archive->is_modified);
3208}
3209/* }}} */
3210
3211static int phar_set_compression(void *pDest, void *argument TSRMLS_DC) /* {{{ */
3212{
3213    phar_entry_info *entry = (phar_entry_info *)pDest;
3214    php_uint32 compress = *(php_uint32 *)argument;
3215
3216    if (entry->is_deleted) {
3217        return ZEND_HASH_APPLY_KEEP;
3218    }
3219
3220    entry->old_flags = entry->flags;
3221    entry->flags &= ~PHAR_ENT_COMPRESSION_MASK;
3222    entry->flags |= compress;
3223    entry->is_modified = 1;
3224    return ZEND_HASH_APPLY_KEEP;
3225}
3226/* }}} */
3227
3228static int phar_test_compression(void *pDest, void *argument TSRMLS_DC) /* {{{ */
3229{
3230    phar_entry_info *entry = (phar_entry_info *)pDest;
3231
3232    if (entry->is_deleted) {
3233        return ZEND_HASH_APPLY_KEEP;
3234    }
3235
3236    if (!PHAR_G(has_bz2)) {
3237        if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
3238            *(int *) argument = 0;
3239        }
3240    }
3241
3242    if (!PHAR_G(has_zlib)) {
3243        if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
3244            *(int *) argument = 0;
3245        }
3246    }
3247
3248    return ZEND_HASH_APPLY_KEEP;
3249}
3250/* }}} */
3251
3252static void pharobj_set_compression(HashTable *manifest, php_uint32 compress TSRMLS_DC) /* {{{ */
3253{
3254    zend_hash_apply_with_argument(manifest, phar_set_compression, &compress TSRMLS_CC);
3255}
3256/* }}} */
3257
3258static int pharobj_cancompress(HashTable *manifest TSRMLS_DC) /* {{{ */
3259{
3260    int test;
3261
3262    test = 1;
3263    zend_hash_apply_with_argument(manifest, phar_test_compression, &test TSRMLS_CC);
3264    return test;
3265}
3266/* }}} */
3267
3268/* {{{ proto object Phar::compress(int method[, string extension])
3269 * Compress a .tar, or .phar.tar with whole-file compression
3270 * The parameter can be one of Phar::GZ or Phar::BZ2 to specify
3271 * the kind of compression desired
3272 */
3273PHP_METHOD(Phar, compress)
3274{
3275    long method;
3276    char *ext = NULL;
3277    int ext_len = 0;
3278    php_uint32 flags;
3279    zval *ret;
3280    PHAR_ARCHIVE_OBJECT();
3281
3282    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|s", &method, &ext, &ext_len) == FAILURE) {
3283        return;
3284    }
3285
3286    if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) {
3287        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
3288            "Cannot compress phar archive, phar is read-only");
3289        return;
3290    }
3291
3292    if (phar_obj->arc.archive->is_zip) {
3293        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
3294            "Cannot compress zip-based archives with whole-archive compression");
3295        return;
3296    }
3297
3298    switch (method) {
3299        case 0:
3300            flags = PHAR_FILE_COMPRESSED_NONE;
3301            break;
3302        case PHAR_ENT_COMPRESSED_GZ:
3303            if (!PHAR_G(has_zlib)) {
3304                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
3305                    "Cannot compress entire archive with gzip, enable ext/zlib in php.ini");
3306                return;
3307            }
3308            flags = PHAR_FILE_COMPRESSED_GZ;
3309            break;
3310
3311        case PHAR_ENT_COMPRESSED_BZ2:
3312            if (!PHAR_G(has_bz2)) {
3313                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
3314                    "Cannot compress entire archive with bz2, enable ext/bz2 in php.ini");
3315                return;
3316            }
3317            flags = PHAR_FILE_COMPRESSED_BZ2;
3318            break;
3319        default:
3320            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
3321                "Unknown compression specified, please pass one of Phar::GZ or Phar::BZ2");
3322            return;
3323    }
3324
3325    if (phar_obj->arc.archive->is_tar) {
3326        ret = phar_convert_to_other(phar_obj->arc.archive, PHAR_FORMAT_TAR, ext, flags TSRMLS_CC);
3327    } else {
3328        ret = phar_convert_to_other(phar_obj->arc.archive, PHAR_FORMAT_PHAR, ext, flags TSRMLS_CC);
3329    }
3330
3331    if (ret) {
3332        RETURN_ZVAL(ret, 1, 1);
3333    } else {
3334        RETURN_NULL();
3335    }
3336}
3337/* }}} */
3338
3339/* {{{ proto object Phar::decompress([string extension])
3340 * Decompress a .tar, or .phar.tar with whole-file compression
3341 */
3342PHP_METHOD(Phar, decompress)
3343{
3344    char *ext = NULL;
3345    int ext_len = 0;
3346    zval *ret;
3347    PHAR_ARCHIVE_OBJECT();
3348
3349    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &ext, &ext_len) == FAILURE) {
3350        return;
3351    }
3352
3353    if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) {
3354        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
3355            "Cannot decompress phar archive, phar is read-only");
3356        return;
3357    }
3358
3359    if (phar_obj->arc.archive->is_zip) {
3360        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
3361            "Cannot decompress zip-based archives with whole-archive compression");
3362        return;
3363    }
3364
3365    if (phar_obj->arc.archive->is_tar) {
3366        ret = phar_convert_to_other(phar_obj->arc.archive, PHAR_FORMAT_TAR, ext, PHAR_FILE_COMPRESSED_NONE TSRMLS_CC);
3367    } else {
3368        ret = phar_convert_to_other(phar_obj->arc.archive, PHAR_FORMAT_PHAR, ext, PHAR_FILE_COMPRESSED_NONE TSRMLS_CC);
3369    }
3370
3371    if (ret) {
3372        RETURN_ZVAL(ret, 1, 1);
3373    } else {
3374        RETURN_NULL();
3375    }
3376}
3377/* }}} */
3378
3379/* {{{ proto object Phar::compressFiles(int method)
3380 * Compress all files within a phar or zip archive using the specified compression
3381 * The parameter can be one of Phar::GZ or Phar::BZ2 to specify
3382 * the kind of compression desired
3383 */
3384PHP_METHOD(Phar, compressFiles)
3385{
3386    char *error;
3387    php_uint32 flags;
3388    long method;
3389    PHAR_ARCHIVE_OBJECT();
3390
3391    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &method) == FAILURE) {
3392        return;
3393    }
3394
3395    if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) {
3396        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
3397            "Phar is readonly, cannot change compression");
3398        return;
3399    }
3400
3401    switch (method) {
3402        case PHAR_ENT_COMPRESSED_GZ:
3403            if (!PHAR_G(has_zlib)) {
3404                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
3405                    "Cannot compress files within archive with gzip, enable ext/zlib in php.ini");
3406                return;
3407            }
3408            flags = PHAR_ENT_COMPRESSED_GZ;
3409            break;
3410
3411        case PHAR_ENT_COMPRESSED_BZ2:
3412            if (!PHAR_G(has_bz2)) {
3413                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
3414                    "Cannot compress files within archive with bz2, enable ext/bz2 in php.ini");
3415                return;
3416            }
3417            flags = PHAR_ENT_COMPRESSED_BZ2;
3418            break;
3419        default:
3420            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
3421                "Unknown compression specified, please pass one of Phar::GZ or Phar::BZ2");
3422            return;
3423    }
3424
3425    if (phar_obj->arc.archive->is_tar) {
3426        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
3427            "Cannot compress with Gzip compression, tar archives cannot compress individual files, use compress() to compress the whole archive");
3428        return;
3429    }
3430
3431    if (!pharobj_cancompress(&phar_obj->arc.archive->manifest TSRMLS_CC)) {
3432        if (flags == PHAR_FILE_COMPRESSED_GZ) {
3433            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
3434                "Cannot compress all files as Gzip, some are compressed as bzip2 and cannot be decompressed");
3435        } else {
3436            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
3437                "Cannot compress all files as Bzip2, some are compressed as gzip and cannot be decompressed");
3438        }
3439        return;
3440    }
3441
3442    if (phar_obj->arc.archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->arc.archive) TSRMLS_CC)) {
3443        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar_obj->arc.archive->fname);
3444        return;
3445    }
3446    pharobj_set_compression(&phar_obj->arc.archive->manifest, flags TSRMLS_CC);
3447    phar_obj->arc.archive->is_modified = 1;
3448    phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC);
3449
3450    if (error) {
3451        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "%s", error);
3452        efree(error);
3453    }
3454}
3455/* }}} */
3456
3457/* {{{ proto bool Phar::decompressFiles()
3458 * decompress every file
3459 */
3460PHP_METHOD(Phar, decompressFiles)
3461{
3462    char *error;
3463    PHAR_ARCHIVE_OBJECT();
3464
3465    if (zend_parse_parameters_none() == FAILURE) {
3466        return;
3467    }
3468
3469    if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) {
3470        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
3471            "Phar is readonly, cannot change compression");
3472        return;
3473    }
3474
3475    if (!pharobj_cancompress(&phar_obj->arc.archive->manifest TSRMLS_CC)) {
3476        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
3477            "Cannot decompress all files, some are compressed as bzip2 or gzip and cannot be decompressed");
3478        return;
3479    }
3480
3481    if (phar_obj->arc.archive->is_tar) {
3482        RETURN_TRUE;
3483    } else {
3484        if (phar_obj->arc.archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->arc.archive) TSRMLS_CC)) {
3485            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar_obj->arc.archive->fname);
3486            return;
3487        }
3488        pharobj_set_compression(&phar_obj->arc.archive->manifest, PHAR_ENT_COMPRESSED_NONE TSRMLS_CC);
3489    }
3490
3491    phar_obj->arc.archive->is_modified = 1;
3492    phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC);
3493
3494    if (error) {
3495        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "%s", error);
3496        efree(error);
3497    }
3498
3499    RETURN_TRUE;
3500}
3501/* }}} */
3502
3503/* {{{ proto bool Phar::copy(string oldfile, string newfile)
3504 * copy a file internal to the phar archive to another new file within the phar
3505 */
3506PHP_METHOD(Phar, copy)
3507{
3508    char *oldfile, *newfile, *error;
3509    const char *pcr_error;
3510    int oldfile_len, newfile_len;
3511    phar_entry_info *oldentry, newentry = {0}, *temp;
3512
3513    PHAR_ARCHIVE_OBJECT();
3514
3515    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &oldfile, &oldfile_len, &newfile, &newfile_len) == FAILURE) {
3516        return;
3517    }
3518
3519    if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) {
3520        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
3521            "Cannot copy \"%s\" to \"%s\", phar is read-only", oldfile, newfile);
3522        RETURN_FALSE;
3523    }
3524
3525    if (oldfile_len >= sizeof(".phar")-1 && !memcmp(oldfile, ".phar", sizeof(".phar")-1)) {
3526        /* can't copy a meta file */
3527        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
3528            "file \"%s\" cannot be copied to file \"%s\", cannot copy Phar meta-file in %s", oldfile, newfile, phar_obj->arc.archive->fname);
3529        RETURN_FALSE;
3530    }
3531
3532    if (newfile_len >= sizeof(".phar")-1 && !memcmp(newfile, ".phar", sizeof(".phar")-1)) {
3533        /* can't copy a meta file */
3534        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
3535            "file \"%s\" cannot be copied to file \"%s\", cannot copy to Phar meta-file in %s", oldfile, newfile, phar_obj->arc.archive->fname);
3536        RETURN_FALSE;
3537    }
3538
3539    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) {
3540        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
3541            "file \"%s\" cannot be copied to file \"%s\", file does not exist in %s", oldfile, newfile, phar_obj->arc.archive->fname);
3542        RETURN_FALSE;
3543    }
3544
3545    if (zend_hash_exists(&phar_obj->arc.archive->manifest, newfile, (uint) newfile_len)) {
3546        if (SUCCESS == zend_hash_find(&phar_obj->arc.archive->manifest, newfile, (uint) newfile_len, (void**)&temp) || !temp->is_deleted) {
3547            zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
3548                "file \"%s\" cannot be copied to file \"%s\", file must not already exist in phar %s", oldfile, newfile, phar_obj->arc.archive->fname);
3549            RETURN_FALSE;
3550        }
3551    }
3552
3553    if (phar_path_check(&newfile, &newfile_len, &pcr_error) > pcr_is_ok) {
3554        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
3555                "file \"%s\" contains invalid characters %s, cannot be copied from \"%s\" in phar %s", newfile, pcr_error, oldfile, phar_obj->arc.archive->fname);
3556        RETURN_FALSE;
3557    }
3558
3559    if (phar_obj->arc.archive->is_persistent) {
3560        if (FAILURE == phar_copy_on_write(&(phar_obj->arc.archive) TSRMLS_CC)) {
3561            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar_obj->arc.archive->fname);
3562            return;
3563        }
3564        /* re-populate with copied-on-write entry */
3565        zend_hash_find(&phar_obj->arc.archive->manifest, oldfile, (uint) oldfile_len, (void**)&oldentry);
3566    }
3567
3568    memcpy((void *) &newentry, oldentry, sizeof(phar_entry_info));
3569
3570    if (newentry.metadata) {
3571        zval *t;
3572
3573        t = newentry.metadata;
3574        ALLOC_ZVAL(newentry.metadata);
3575        *newentry.metadata = *t;
3576        zval_copy_ctor(newentry.metadata);
3577#if PHP_VERSION_ID < 50300
3578        newentry.metadata->refcount = 1;
3579#else
3580        Z_SET_REFCOUNT_P(newentry.metadata, 1);
3581#endif
3582
3583        newentry.metadata_str.c = NULL;
3584        newentry.metadata_str.len = 0;
3585    }
3586
3587    newentry.filename = estrndup(newfile, newfile_len);
3588    newentry.filename_len = newfile_len;
3589    newentry.fp_refcount = 0;
3590
3591    if (oldentry->fp_type != PHAR_FP) {
3592        if (FAILURE == phar_copy_entry_fp(oldentry, &newentry, &error TSRMLS_CC)) {
3593            efree(newentry.filename);
3594            php_stream_close(newentry.fp);
3595            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
3596            efree(error);
3597            return;
3598        }
3599    }
3600
3601    zend_hash_add(&oldentry->phar->manifest, newfile, newfile_len, (void*)&newentry, sizeof(phar_entry_info), NULL);
3602    phar_obj->arc.archive->is_modified = 1;
3603    phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC);
3604
3605    if (error) {
3606        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
3607        efree(error);
3608    }
3609
3610    RETURN_TRUE;
3611}
3612/* }}} */
3613
3614/* {{{ proto int Phar::offsetExists(string entry)
3615 * determines whether a file exists in the phar
3616 */
3617PHP_METHOD(Phar, offsetExists)
3618{
3619    char *fname;
3620    int fname_len;
3621    phar_entry_info *entry;
3622
3623    PHAR_ARCHIVE_OBJECT();
3624
3625    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &fname, &fname_len) == FAILURE) {
3626        return;
3627    }
3628
3629    if (zend_hash_exists(&phar_obj->arc.archive->manifest, fname, (uint) fname_len)) {
3630        if (SUCCESS == zend_hash_find(&phar_obj->arc.archive->manifest, fname, (uint) fname_len, (void**)&entry)) {
3631            if (entry->is_deleted) {
3632                /* entry is deleted, but has not been flushed to disk yet */
3633                RETURN_FALSE;
3634            }
3635        }
3636
3637        if (fname_len >= sizeof(".phar")-1 && !memcmp(fname, ".phar", sizeof(".phar")-1)) {
3638            /* none of these are real files, so they don't exist */
3639            RETURN_FALSE;
3640        }
3641        RETURN_TRUE;
3642    } else {
3643        if (zend_hash_exists(&phar_obj->arc.archive->virtual_dirs, fname, (uint) fname_len)) {
3644            RETURN_TRUE;
3645        }
3646        RETURN_FALSE;
3647    }
3648}
3649/* }}} */
3650
3651/* {{{ proto int Phar::offsetGet(string entry)
3652 * get a PharFileInfo object for a specific file
3653 */
3654PHP_METHOD(Phar, offsetGet)
3655{
3656    char *fname, *error;
3657    int fname_len;
3658    zval *zfname;
3659    phar_entry_info *entry;
3660    PHAR_ARCHIVE_OBJECT();
3661
3662    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &fname, &fname_len) == FAILURE) {
3663        return;
3664    }
3665
3666    /* security is 0 here so that we can get a better error message than "entry doesn't exist" */
3667    if (!(entry = phar_get_entry_info_dir(phar_obj->arc.archive, fname, fname_len, 1, &error, 0 TSRMLS_CC))) {
3668        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s does not exist%s%s", fname, error?", ":"", error?error:"");
3669    } else {
3670        if (fname_len == sizeof(".phar/stub.php")-1 && !memcmp(fname, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
3671            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);
3672            return;
3673        }
3674
3675        if (fname_len == sizeof(".phar/alias.txt")-1 && !memcmp(fname, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
3676            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);
3677            return;
3678        }
3679
3680        if (fname_len >= sizeof(".phar")-1 && !memcmp(fname, ".phar", sizeof(".phar")-1)) {
3681            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);
3682            return;
3683        }
3684
3685        if (entry->is_temp_dir) {
3686            efree(entry->filename);
3687            efree(entry);
3688        }
3689
3690        fname_len = spprintf(&fname, 0, "phar://%s/%s", phar_obj->arc.archive->fname, fname);
3691        MAKE_STD_ZVAL(zfname);
3692        ZVAL_STRINGL(zfname, fname, fname_len, 0);
3693        spl_instantiate_arg_ex1(phar_obj->spl.info_class, &return_value, 0, zfname TSRMLS_CC);
3694        zval_ptr_dtor(&zfname);
3695    }
3696}
3697/* }}} */
3698
3699/* {{{ add a file within the phar archive from a string or resource
3700 */
3701static void phar_add_file(phar_archive_data **pphar, char *filename, int filename_len, char *cont_str, int cont_len, zval *zresource TSRMLS_DC)
3702{
3703    char *error;
3704    size_t contents_len;
3705    phar_entry_data *data;
3706    php_stream *contents_file;
3707
3708    if (filename_len >= sizeof(".phar")-1 && !memcmp(filename, ".phar", sizeof(".phar")-1)) {
3709        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot create any files in magic \".phar\" directory", (*pphar)->fname);
3710        return;
3711    }
3712
3713    if (!(data = phar_get_or_create_entry_data((*pphar)->fname, (*pphar)->fname_len, filename, filename_len, "w+b", 0, &error, 1 TSRMLS_CC))) {
3714        if (error) {
3715            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s does not exist and cannot be created: %s", filename, error);
3716            efree(error);
3717        } else {
3718            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s does not exist and cannot be created", filename);
3719        }
3720        return;
3721    } else {
3722        if (error) {
3723            efree(error);
3724        }
3725
3726        if (!data->internal_file->is_dir) {
3727            if (cont_str) {
3728                contents_len = php_stream_write(data->fp, cont_str, cont_len);
3729                if (contents_len != cont_len) {
3730                    zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s could not be written to", filename);
3731                    return;
3732                }
3733            } else {
3734                if (!(php_stream_from_zval_no_verify(contents_file, &zresource))) {
3735                    zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s could not be written to", filename);
3736                    return;
3737                }
3738                phar_stream_copy_to_stream(contents_file, data->fp, PHP_STREAM_COPY_ALL, &contents_len);
3739            }
3740
3741            data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize = contents_len;
3742        }
3743
3744        /* check for copy-on-write */
3745        if (pphar[0] != data->phar) {
3746            *pphar = data->phar;
3747        }
3748        phar_entry_delref(data TSRMLS_CC);
3749        phar_flush(*pphar, 0, 0, 0, &error TSRMLS_CC);
3750
3751        if (error) {
3752            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
3753            efree(error);
3754        }
3755    }
3756}
3757/* }}} */
3758
3759/* {{{ create a directory within the phar archive
3760 */
3761static void phar_mkdir(phar_archive_data **pphar, char *dirname, int dirname_len TSRMLS_DC)
3762{
3763    char *error;
3764    phar_entry_data *data;
3765
3766    if (!(data = phar_get_or_create_entry_data((*pphar)->fname, (*pphar)->fname_len, dirname, dirname_len, "w+b", 2, &error, 1 TSRMLS_CC))) {
3767        if (error) {
3768            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Directory %s does not exist and cannot be created: %s", dirname, error);
3769            efree(error);
3770        } else {
3771            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Directory %s does not exist and cannot be created", dirname);
3772        }
3773
3774        return;
3775    } else {
3776        if (error) {
3777            efree(error);
3778        }
3779
3780        /* check for copy on write */
3781        if (data->phar != *pphar) {
3782            *pphar = data->phar;
3783        }
3784        phar_entry_delref(data TSRMLS_CC);
3785        phar_flush(*pphar, 0, 0, 0, &error TSRMLS_CC);
3786
3787        if (error) {
3788            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
3789            efree(error);
3790        }
3791    }
3792}
3793/* }}} */
3794
3795/* {{{ proto int Phar::offsetSet(string entry, string value)
3796 * set the contents of an internal file to those of an external file
3797 */
3798PHP_METHOD(Phar, offsetSet)
3799{
3800    char *fname, *cont_str = NULL;
3801    int fname_len, cont_len;
3802    zval *zresource;
3803    PHAR_ARCHIVE_OBJECT();
3804
3805    if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) {
3806        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Write operations disabled by the php.ini setting phar.readonly");
3807        return;
3808    }
3809
3810    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "sr", &fname, &fname_len, &zresource) == FAILURE
3811    && zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &fname, &fname_len, &cont_str, &cont_len) == FAILURE) {
3812        return;
3813    }
3814
3815    if (fname_len == sizeof(".phar/stub.php")-1 && !memcmp(fname, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
3816        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);
3817        return;
3818    }
3819
3820    if (fname_len == sizeof(".phar/alias.txt")-1 && !memcmp(fname, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
3821        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);
3822        return;
3823    }
3824
3825    if (fname_len >= sizeof(".phar")-1 && !memcmp(fname, ".phar", sizeof(".phar")-1)) {
3826        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);
3827        return;
3828    }
3829
3830    phar_add_file(&(phar_obj->arc.archive), fname, fname_len, cont_str, cont_len, zresource TSRMLS_CC);
3831}
3832/* }}} */
3833
3834/* {{{ proto int Phar::offsetUnset(string entry)
3835 * remove a file from a phar
3836 */
3837PHP_METHOD(Phar, offsetUnset)
3838{
3839    char *fname, *error;
3840    int fname_len;
3841    phar_entry_info *entry;
3842    PHAR_ARCHIVE_OBJECT();
3843
3844    if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) {
3845        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Write operations disabled by the php.ini setting phar.readonly");
3846        return;
3847    }
3848
3849    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &fname, &fname_len) == FAILURE) {
3850        return;
3851    }
3852
3853    if (zend_hash_exists(&phar_obj->arc.archive->manifest, fname, (uint) fname_len)) {
3854        if (SUCCESS == zend_hash_find(&phar_obj->arc.archive->manifest, fname, (uint) fname_len, (void**)&entry)) {
3855            if (entry->is_deleted) {
3856                /* entry is deleted, but has not been flushed to disk yet */
3857                return;
3858            }
3859
3860            if (phar_obj->arc.archive->is_persistent) {
3861                if (FAILURE == phar_copy_on_write(&(phar_obj->arc.archive) TSRMLS_CC)) {
3862                    zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar_obj->arc.archive->fname);
3863                    return;
3864                }
3865                /* re-populate entry after copy on write */
3866                zend_hash_find(&phar_obj->arc.archive->manifest, fname, (uint) fname_len, (void **)&entry);
3867            }
3868            entry->is_modified = 0;
3869            entry->is_deleted = 1;
3870            /* we need to "flush" the stream to save the newly deleted file on disk */
3871            phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC);
3872
3873            if (error) {
3874                zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
3875                efree(error);
3876            }
3877
3878            RETURN_TRUE;
3879        }
3880    } else {
3881        RETURN_FALSE;
3882    }
3883}
3884/* }}} */
3885
3886/* {{{ proto string Phar::addEmptyDir(string dirname)
3887 * Adds an empty directory to the phar archive
3888 */
3889PHP_METHOD(Phar, addEmptyDir)
3890{
3891    char *dirname;
3892    int dirname_len;
3893
3894    PHAR_ARCHIVE_OBJECT();
3895
3896    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &dirname, &dirname_len) == FAILURE) {
3897        return;
3898    }
3899
3900    if (dirname_len >= sizeof(".phar")-1 && !memcmp(dirname, ".phar", sizeof(".phar")-1)) {
3901        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot create a directory in magic \".phar\" directory");
3902        return;
3903    }
3904
3905    phar_mkdir(&phar_obj->arc.archive, dirname, dirname_len TSRMLS_CC);
3906}
3907/* }}} */
3908
3909/* {{{ proto string Phar::addFile(string filename[, string localname])
3910 * Adds a file to the archive using the filename, or the second parameter as the name within the archive
3911 */
3912PHP_METHOD(Phar, addFile)
3913{
3914    char *fname, *localname = NULL;
3915    int fname_len, localname_len = 0;
3916    php_stream *resource;
3917    zval *zresource;
3918
3919    PHAR_ARCHIVE_OBJECT();
3920
3921    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &fname, &fname_len, &localname, &localname_len) == FAILURE) {
3922        return;
3923    }
3924
3925#if PHP_API_VERSION < 20100412
3926    if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
3927        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);
3928        return;
3929    }
3930#endif
3931
3932    if (!strstr(fname, "://") && php_check_open_basedir(fname TSRMLS_CC)) {
3933        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);
3934        return;
3935    }
3936
3937    if (!(resource = php_stream_open_wrapper(fname, "rb", 0, NULL))) {
3938        zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "phar error: unable to open file \"%s\" to add to phar archive", fname);
3939        return;
3940    }
3941
3942    if (localname) {
3943        fname = localname;
3944        fname_len = localname_len;
3945    }
3946
3947    MAKE_STD_ZVAL(zresource);
3948    php_stream_to_zval(resource, zresource);
3949    phar_add_file(&(phar_obj->arc.archive), fname, fname_len, NULL, 0, zresource TSRMLS_CC);
3950    efree(zresource);
3951    php_stream_close(resource);
3952}
3953/* }}} */
3954
3955/* {{{ proto string Phar::addFromString(string localname, string contents)
3956 * Adds a file to the archive using its contents as a string
3957 */
3958PHP_METHOD(Phar, addFromString)
3959{
3960    char *localname, *cont_str;
3961    int localname_len, cont_len;
3962
3963    PHAR_ARCHIVE_OBJECT();
3964
3965    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &localname, &localname_len, &cont_str, &cont_len) == FAILURE) {
3966        return;
3967    }
3968
3969    phar_add_file(&(phar_obj->arc.archive), localname, localname_len, cont_str, cont_len, NULL TSRMLS_CC);
3970}
3971/* }}} */
3972
3973/* {{{ proto string Phar::getStub()
3974 * Returns the stub at the head of a phar archive as a string.
3975 */
3976PHP_METHOD(Phar, getStub)
3977{
3978    size_t len;
3979    char *buf;
3980    php_stream *fp;
3981    php_stream_filter *filter = NULL;
3982    phar_entry_info *stub;
3983
3984    PHAR_ARCHIVE_OBJECT();
3985
3986    if (zend_parse_parameters_none() == FAILURE) {
3987        return;
3988    }
3989
3990    if (phar_obj->arc.archive->is_tar || phar_obj->arc.archive->is_zip) {
3991
3992        if (SUCCESS == zend_hash_find(&(phar_obj->arc.archive->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&stub)) {
3993            if (phar_obj->arc.archive->fp && !phar_obj->arc.archive->is_brandnew && !(stub->flags & PHAR_ENT_COMPRESSION_MASK)) {
3994                fp = phar_obj->arc.archive->fp;
3995            } else {
3996                if (!(fp = php_stream_open_wrapper(phar_obj->arc.archive->fname, "rb", 0, NULL))) {
3997                    zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "phar error: unable to open phar \"%s\"", phar_obj->arc.archive->fname);
3998                    return;
3999                }
4000                if (stub->flags & PHAR_ENT_COMPRESSION_MASK) {
4001                    char *filter_name;
4002
4003                    if ((filter_name = phar_decompress_filter(stub, 0)) != NULL) {
4004                        filter = php_stream_filter_create(filter_name, NULL, php_stream_is_persistent(fp) TSRMLS_CC);
4005                    } else {
4006                        filter = NULL;
4007                    }
4008                    if (!filter) {
4009                        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));
4010                        return;
4011                    }
4012                    php_stream_filter_append(&fp->readfilters, filter);
4013                }
4014            }
4015
4016            if (!fp)  {
4017                zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC,
4018                    "Unable to read stub");
4019                return;
4020            }
4021
4022            php_stream_seek(fp, stub->offset_abs, SEEK_SET);
4023            len = stub->uncompressed_filesize;
4024            goto carry_on;
4025        } else {
4026            RETURN_STRINGL("", 0, 1);
4027        }
4028    }
4029    len = phar_obj->arc.archive->halt_offset;
4030
4031    if (phar_obj->arc.archive->fp && !phar_obj->arc.archive->is_brandnew) {
4032        fp = phar_obj->arc.archive->fp;
4033    } else {
4034        fp = php_stream_open_wrapper(phar_obj->arc.archive->fname, "rb", 0, NULL);
4035    }
4036
4037    if (!fp)  {
4038        zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC,
4039            "Unable to read stub");
4040        return;
4041    }
4042
4043    php_stream_rewind(fp);
4044carry_on:
4045    buf = safe_emalloc(len, 1, 1);
4046
4047    if (len != php_stream_read(fp, buf, len)) {
4048        if (fp != phar_obj->arc.archive->fp) {
4049            php_stream_close(fp);
4050        }
4051        zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC,
4052            "Unable to read stub");
4053        efree(buf);
4054        return;
4055    }
4056
4057    if (filter) {
4058        php_stream_filter_flush(filter, 1);
4059        php_stream_filter_remove(filter, 1 TSRMLS_CC);
4060    }
4061
4062    if (fp != phar_obj->arc.archive->fp) {
4063        php_stream_close(fp);
4064    }
4065
4066    buf[len] = '\0';
4067    RETURN_STRINGL(buf, len, 0);
4068}
4069/* }}}*/
4070
4071/* {{{ proto int Phar::hasMetaData()
4072 * Returns TRUE if the phar has global metadata, FALSE otherwise.
4073 */
4074PHP_METHOD(Phar, hasMetadata)
4075{
4076    PHAR_ARCHIVE_OBJECT();
4077
4078    RETURN_BOOL(phar_obj->arc.archive->metadata != NULL);
4079}
4080/* }}} */
4081
4082/* {{{ proto int Phar::getMetaData()
4083 * Returns the global metadata of the phar
4084 */
4085PHP_METHOD(Phar, getMetadata)
4086{
4087    PHAR_ARCHIVE_OBJECT();
4088
4089    if (zend_parse_parameters_none() == FAILURE) {
4090        return;
4091    }
4092
4093    if (phar_obj->arc.archive->metadata) {
4094        if (phar_obj->arc.archive->is_persistent) {
4095            zval *ret;
4096            char *buf = estrndup((char *) phar_obj->arc.archive->metadata, phar_obj->arc.archive->metadata_len);
4097            /* assume success, we would have failed before */
4098            phar_parse_metadata(&buf, &ret, phar_obj->arc.archive->metadata_len TSRMLS_CC);
4099            efree(buf);
4100            RETURN_ZVAL(ret, 0, 1);
4101        }
4102        RETURN_ZVAL(phar_obj->arc.archive->metadata, 1, 0);
4103    }
4104}
4105/* }}} */
4106
4107/* {{{ proto int Phar::setMetaData(mixed $metadata)
4108 * Sets the global metadata of the phar
4109 */
4110PHP_METHOD(Phar, setMetadata)
4111{
4112    char *error;
4113    zval *metadata;
4114
4115    PHAR_ARCHIVE_OBJECT();
4116
4117    if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) {
4118        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Write operations disabled by the php.ini setting phar.readonly");
4119        return;
4120    }
4121
4122    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &metadata) == FAILURE) {
4123        return;
4124    }
4125
4126    if (phar_obj->arc.archive->is_persistent && FAILURE == phar_copy_on_write(&(phar_obj->arc.archive) TSRMLS_CC)) {
4127        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar_obj->arc.archive->fname);
4128        return;
4129    }
4130    if (phar_obj->arc.archive->metadata) {
4131        zval_ptr_dtor(&phar_obj->arc.archive->metadata);
4132        phar_obj->arc.archive->metadata = NULL;
4133    }
4134
4135    MAKE_STD_ZVAL(phar_obj->arc.archive->metadata);
4136    ZVAL_ZVAL(phar_obj->arc.archive->metadata, metadata, 1, 0);
4137    phar_obj->arc.archive->is_modified = 1;
4138    phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC);
4139
4140    if (error) {
4141        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
4142        efree(error);
4143    }
4144}
4145/* }}} */
4146
4147/* {{{ proto int Phar::delMetadata()
4148 * Deletes the global metadata of the phar
4149 */
4150PHP_METHOD(Phar, delMetadata)
4151{
4152    char *error;
4153
4154    PHAR_ARCHIVE_OBJECT();
4155
4156    if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) {
4157        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Write operations disabled by the php.ini setting phar.readonly");
4158        return;
4159    }
4160
4161    if (phar_obj->arc.archive->metadata) {
4162        zval_ptr_dtor(&phar_obj->arc.archive->metadata);
4163        phar_obj->arc.archive->metadata = NULL;
4164        phar_obj->arc.archive->is_modified = 1;
4165        phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC);
4166
4167        if (error) {
4168            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
4169            efree(error);
4170            RETURN_FALSE;
4171        } else {
4172            RETURN_TRUE;
4173        }
4174
4175    } else {
4176        RETURN_TRUE;
4177    }
4178}
4179/* }}} */
4180#if PHP_API_VERSION < 20100412
4181#define PHAR_OPENBASEDIR_CHECKPATH(filename) \
4182    (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(filename TSRMLS_CC)
4183#else
4184#define PHAR_OPENBASEDIR_CHECKPATH(filename) \
4185    php_check_open_basedir(filename TSRMLS_CC)
4186#endif
4187
4188static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char *dest, int dest_len, char **error TSRMLS_DC) /* {{{ */
4189{
4190    php_stream_statbuf ssb;
4191    int len;
4192    php_stream *fp;
4193    char *fullpath;
4194    const char *slash;
4195    mode_t mode;
4196
4197    if (entry->is_mounted) {
4198        /* silently ignore mounted entries */
4199        return SUCCESS;
4200    }
4201
4202    if (entry->filename_len >= sizeof(".phar")-1 && !memcmp(entry->filename, ".phar", sizeof(".phar")-1)) {
4203        return SUCCESS;
4204    }
4205
4206    len = spprintf(&fullpath, 0, "%s/%s", dest, entry->filename);
4207
4208    if (len >= MAXPATHLEN) {
4209        char *tmp;
4210        /* truncate for error message */
4211        fullpath[50] = '\0';
4212        if (entry->filename_len > 50) {
4213            tmp = estrndup(entry->filename, 50);
4214            spprintf(error, 4096, "Cannot extract \"%s...\" to \"%s...\", extracted filename is too long for filesystem", tmp, fullpath);
4215            efree(tmp);
4216        } else {
4217            spprintf(error, 4096, "Cannot extract \"%s\" to \"%s...\", extracted filename is too long for filesystem", entry->filename, fullpath);
4218        }
4219        efree(fullpath);
4220        return FAILURE;
4221    }
4222
4223    if (!len) {
4224        spprintf(error, 4096, "Cannot extract \"%s\", internal error", entry->filename);
4225        efree(fullpath);
4226        return FAILURE;
4227    }
4228
4229    if (PHAR_OPENBASEDIR_CHECKPATH(fullpath)) {
4230        spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", openbasedir/safe mode restrictions in effect", entry->filename, fullpath);
4231        efree(fullpath);
4232        return FAILURE;
4233    }
4234
4235    /* let see if the path already exists */
4236    if (!overwrite && SUCCESS == php_stream_stat_path(fullpath, &ssb)) {
4237        spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", path already exists", entry->filename, fullpath);
4238        efree(fullpath);
4239        return FAILURE;
4240    }
4241
4242    /* perform dirname */
4243    slash = zend_memrchr(entry->filename, '/', entry->filename_len);
4244
4245    if (slash) {
4246        fullpath[dest_len + (slash - entry->filename) + 1] = '\0';
4247    } else {
4248        fullpath[dest_len] = '\0';
4249    }
4250
4251    if (FAILURE == php_stream_stat_path(fullpath, &ssb)) {
4252        if (entry->is_dir) {
4253            if (!php_stream_mkdir(fullpath, entry->flags & PHAR_ENT_PERM_MASK,  PHP_STREAM_MKDIR_RECURSIVE, NULL)) {
4254                spprintf(error, 4096, "Cannot extract \"%s\", could not create directory \"%s\"", entry->filename, fullpath);
4255                efree(fullpath);
4256                return FAILURE;
4257            }
4258        } else {
4259            if (!php_stream_mkdir(fullpath, 0777,  PHP_STREAM_MKDIR_RECURSIVE, NULL)) {
4260                spprintf(error, 4096, "Cannot extract \"%s\", could not create directory \"%s\"", entry->filename, fullpath);
4261                efree(fullpath);
4262                return FAILURE;
4263            }
4264        }
4265    }
4266
4267    if (slash) {
4268        fullpath[dest_len + (slash - entry->filename) + 1] = '/';
4269    } else {
4270        fullpath[dest_len] = '/';
4271    }
4272
4273    /* it is a standalone directory, job done */
4274    if (entry->is_dir) {
4275        efree(fullpath);
4276        return SUCCESS;
4277    }
4278
4279#if PHP_API_VERSION < 20100412
4280    fp = php_stream_open_wrapper(fullpath, "w+b", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL);
4281#else
4282    fp = php_stream_open_wrapper(fullpath, "w+b", REPORT_ERRORS, NULL);
4283#endif
4284
4285    if (!fp) {
4286        spprintf(error, 4096, "Cannot extract \"%s\", could not open for writing \"%s\"", entry->filename, fullpath);
4287        efree(fullpath);
4288        return FAILURE;
4289    }
4290
4291    if (!phar_get_efp(entry, 0 TSRMLS_CC)) {
4292        if (FAILURE == phar_open_entry_fp(entry, error, 1 TSRMLS_CC)) {
4293            if (error) {
4294                spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", unable to open internal file pointer: %s", entry->filename, fullpath, *error);
4295            } else {
4296                spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", unable to open internal file pointer", entry->filename, fullpath);
4297            }
4298            efree(fullpath);
4299            php_stream_close(fp);
4300            return FAILURE;
4301        }
4302    }
4303
4304    if (FAILURE == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
4305        spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", unable to seek internal file pointer", entry->filename, fullpath);
4306        efree(fullpath);
4307        php_stream_close(fp);
4308        return FAILURE;
4309    }
4310
4311    if (SUCCESS != phar_stream_copy_to_stream(phar_get_efp(entry, 0 TSRMLS_CC), fp, entry->uncompressed_filesize, NULL)) {
4312        spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", copying contents failed", entry->filename, fullpath);
4313        efree(fullpath);
4314        php_stream_close(fp);
4315        return FAILURE;
4316    }
4317
4318    php_stream_close(fp);
4319    mode = (mode_t) entry->flags & PHAR_ENT_PERM_MASK;
4320
4321    if (FAILURE == VCWD_CHMOD(fullpath, mode)) {
4322        spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", setting file permissions failed", entry->filename, fullpath);
4323        efree(fullpath);
4324        return FAILURE;
4325    }
4326
4327    efree(fullpath);
4328    return SUCCESS;
4329}
4330/* }}} */
4331
4332/* {{{ proto bool Phar::extractTo(string pathto[[, mixed files], bool overwrite])
4333 * Extract one or more file from a phar archive, optionally overwriting existing files
4334 */
4335PHP_METHOD(Phar, extractTo)
4336{
4337    char *error = NULL;
4338    php_stream *fp;
4339    php_stream_statbuf ssb;
4340    phar_entry_info *entry;
4341    char *pathto, *filename, *actual;
4342    int pathto_len, filename_len;
4343    int ret, i;
4344    int nelems;
4345    zval *zval_files = NULL;
4346    zend_bool overwrite = 0;
4347
4348    PHAR_ARCHIVE_OBJECT();
4349
4350    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z!b", &pathto, &pathto_len, &zval_files, &overwrite) == FAILURE) {
4351        return;
4352    }
4353
4354    fp = php_stream_open_wrapper(phar_obj->arc.archive->fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, &actual);
4355
4356    if (!fp) {
4357        zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC,
4358            "Invalid argument, %s cannot be found", phar_obj->arc.archive->fname);
4359        return;
4360    }
4361
4362    efree(actual);
4363    php_stream_close(fp);
4364
4365    if (pathto_len < 1) {
4366        zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC,
4367            "Invalid argument, extraction path must be non-zero length");
4368        return;
4369    }
4370
4371    if (pathto_len >= MAXPATHLEN) {
4372        char *tmp = estrndup(pathto, 50);
4373        /* truncate for error message */
4374        zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Cannot extract to \"%s...\", destination directory is too long for filesystem", tmp);
4375        efree(tmp);
4376        return;
4377    }
4378
4379    if (php_stream_stat_path(pathto, &ssb) < 0) {
4380        ret = php_stream_mkdir(pathto, 0777,  PHP_STREAM_MKDIR_RECURSIVE, NULL);
4381        if (!ret) {
4382            zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC,
4383                "Unable to create path \"%s\" for extraction", pathto);
4384            return;
4385        }
4386    } else if (!(ssb.sb.st_mode & S_IFDIR)) {
4387        zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC,
4388            "Unable to use path \"%s\" for extraction, it is a file, must be a directory", pathto);
4389        return;
4390    }
4391
4392    if (zval_files) {
4393        switch (Z_TYPE_P(zval_files)) {
4394            case IS_NULL:
4395                goto all_files;
4396#if PHP_VERSION_ID >= 60000
4397            case IS_UNICODE:
4398                zval_unicode_to_string(zval_files TSRMLS_CC);
4399                /* break intentionally omitted */
4400#endif
4401            case IS_STRING:
4402                filename = Z_STRVAL_P(zval_files);
4403                filename_len = Z_STRLEN_P(zval_files);
4404                break;
4405            case IS_ARRAY:
4406                nelems = zend_hash_num_elements(Z_ARRVAL_P(zval_files));
4407                if (nelems == 0 ) {
4408                    RETURN_FALSE;
4409                }
4410                for (i = 0; i < nelems; i++) {
4411                    zval **zval_file;
4412                    if (zend_hash_index_find(Z_ARRVAL_P(zval_files), i, (void **) &zval_file) == SUCCESS) {
4413                        switch (Z_TYPE_PP(zval_file)) {
4414#if PHP_VERSION_ID >= 60000
4415                            case IS_UNICODE:
4416                                zval_unicode_to_string(*(zval_file) TSRMLS_CC);
4417                                /* break intentionally omitted */
4418#endif
4419                            case IS_STRING:
4420                                break;
4421                            default:
4422                                zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC,
4423                                    "Invalid argument, array of filenames to extract contains non-string value");
4424                                return;
4425                        }
4426                        if (FAILURE == zend_hash_find(&phar_obj->arc.archive->manifest, Z_STRVAL_PP(zval_file), Z_STRLEN_PP(zval_file), (void **)&entry)) {
4427                            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC,
4428                                "Phar Error: attempted to extract non-existent file \"%s\" from phar \"%s\"", Z_STRVAL_PP(zval_file), phar_obj->arc.archive->fname);
4429                        }
4430                        if (FAILURE == phar_extract_file(overwrite, entry, pathto, pathto_len, &error TSRMLS_CC)) {
4431                            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC,
4432                                "Extraction from phar \"%s\" failed: %s", phar_obj->arc.archive->fname, error);
4433                            efree(error);
4434                            return;
4435                        }
4436                    }
4437                }
4438                RETURN_TRUE;
4439            default:
4440                zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC,
4441                    "Invalid argument, expected a filename (string) or array of filenames");
4442                return;
4443        }
4444
4445        if (FAILURE == zend_hash_find(&phar_obj->arc.archive->manifest, filename, filename_len, (void **)&entry)) {
4446            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC,
4447                "Phar Error: attempted to extract non-existent file \"%s\" from phar \"%s\"", filename, phar_obj->arc.archive->fname);
4448            return;
4449        }
4450
4451        if (FAILURE == phar_extract_file(overwrite, entry, pathto, pathto_len, &error TSRMLS_CC)) {
4452            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC,
4453                "Extraction from phar \"%s\" failed: %s", phar_obj->arc.archive->fname, error);
4454            efree(error);
4455            return;
4456        }
4457    } else {
4458        phar_archive_data *phar;
4459all_files:
4460        phar = phar_obj->arc.archive;
4461        /* Extract all files */
4462        if (!zend_hash_num_elements(&(phar->manifest))) {
4463            RETURN_TRUE;
4464        }
4465
4466        for (zend_hash_internal_pointer_reset(&phar->manifest);
4467        zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
4468        zend_hash_move_forward(&phar->manifest)) {
4469
4470            if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) {
4471                continue;
4472            }
4473
4474            if (FAILURE == phar_extract_file(overwrite, entry, pathto, pathto_len, &error TSRMLS_CC)) {
4475                zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC,
4476                    "Extraction from phar \"%s\" failed: %s", phar->fname, error);
4477                efree(error);
4478                return;
4479            }
4480        }
4481    }
4482    RETURN_TRUE;
4483}
4484/* }}} */
4485
4486
4487/* {{{ proto void PharFileInfo::__construct(string entry)
4488 * Construct a Phar entry object
4489 */
4490PHP_METHOD(PharFileInfo, __construct)
4491{
4492    char *fname, *arch, *entry, *error;
4493    int fname_len, arch_len, entry_len;
4494    phar_entry_object *entry_obj;
4495    phar_entry_info *entry_info;
4496    phar_archive_data *phar_data;
4497    zval *zobj = getThis(), arg1;
4498
4499    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &fname, &fname_len) == FAILURE) {
4500        return;
4501    }
4502
4503    entry_obj = (phar_entry_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
4504
4505    if (entry_obj->ent.entry) {
4506        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot call constructor twice");
4507        return;
4508    }
4509
4510    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) {
4511        zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC,
4512            "'%s' is not a valid phar archive URL (must have at least phar://filename.phar)", fname);
4513        return;
4514    }
4515
4516    if (phar_open_from_filename(arch, arch_len, NULL, 0, REPORT_ERRORS, &phar_data, &error TSRMLS_CC) == FAILURE) {
4517        efree(arch);
4518        efree(entry);
4519        if (error) {
4520            zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC,
4521                "Cannot open phar file '%s': %s", fname, error);
4522            efree(error);
4523        } else {
4524            zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC,
4525                "Cannot open phar file '%s'", fname);
4526        }
4527        return;
4528    }
4529
4530    if ((entry_info = phar_get_entry_info_dir(phar_data, entry, entry_len, 1, &error, 1 TSRMLS_CC)) == NULL) {
4531        zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC,
4532            "Cannot access phar file entry '%s' in archive '%s'%s%s", entry, arch, error ? ", " : "", error ? error : "");
4533        efree(arch);
4534        efree(entry);
4535        return;
4536    }
4537
4538    efree(arch);
4539    efree(entry);
4540
4541    entry_obj->ent.entry = entry_info;
4542
4543    INIT_PZVAL(&arg1);
4544    ZVAL_STRINGL(&arg1, fname, fname_len, 0);
4545
4546    zend_call_method_with_1_params(&zobj, Z_OBJCE_P(zobj),
4547        &spl_ce_SplFileInfo->constructor, "__construct", NULL, &arg1);
4548}
4549/* }}} */
4550
4551#define PHAR_ENTRY_OBJECT() \
4552    phar_entry_object *entry_obj = (phar_entry_object*)zend_object_store_get_object(getThis() TSRMLS_CC); \
4553    if (!entry_obj->ent.entry) { \
4554        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \
4555            "Cannot call method on an uninitialized PharFileInfo object"); \
4556        return; \
4557    }
4558
4559/* {{{ proto void PharFileInfo::__destruct()
4560 * clean up directory-based entry objects
4561 */
4562PHP_METHOD(PharFileInfo, __destruct)
4563{
4564    phar_entry_object *entry_obj = (phar_entry_object*)zend_object_store_get_object(getThis() TSRMLS_CC); \
4565
4566    if (entry_obj->ent.entry && entry_obj->ent.entry->is_temp_dir) {
4567        if (entry_obj->ent.entry->filename) {
4568            efree(entry_obj->ent.entry->filename);
4569            entry_obj->ent.entry->filename = NULL;
4570        }
4571
4572        efree(entry_obj->ent.entry);
4573        entry_obj->ent.entry = NULL;
4574    }
4575}
4576/* }}} */
4577
4578/* {{{ proto int PharFileInfo::getCompressedSize()
4579 * Returns the compressed size
4580 */
4581PHP_METHOD(PharFileInfo, getCompressedSize)
4582{
4583    PHAR_ENTRY_OBJECT();
4584
4585    if (zend_parse_parameters_none() == FAILURE) {
4586        return;
4587    }
4588
4589    RETURN_LONG(entry_obj->ent.entry->compressed_filesize);
4590}
4591/* }}} */
4592
4593/* {{{ proto bool PharFileInfo::isCompressed([int compression_type])
4594 * Returns whether the entry is compressed, and whether it is compressed with Phar::GZ or Phar::BZ2 if specified
4595 */
4596PHP_METHOD(PharFileInfo, isCompressed)
4597{
4598    /* a number that is not Phar::GZ or Phar::BZ2 */
4599    long method = 9021976;
4600    PHAR_ENTRY_OBJECT();
4601
4602    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &method) == FAILURE) {
4603        return;
4604    }
4605
4606    switch (method) {
4607        case 9021976:
4608            RETURN_BOOL(entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSION_MASK);
4609        case PHAR_ENT_COMPRESSED_GZ:
4610            RETURN_BOOL(entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_GZ);
4611        case PHAR_ENT_COMPRESSED_BZ2:
4612            RETURN_BOOL(entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_BZ2);
4613        default:
4614            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \
4615                "Unknown compression type specified"); \
4616    }
4617}
4618/* }}} */
4619
4620/* {{{ proto int PharFileInfo::getCRC32()
4621 * Returns CRC32 code or throws an exception if not CRC checked
4622 */
4623PHP_METHOD(PharFileInfo, getCRC32)
4624{
4625    PHAR_ENTRY_OBJECT();
4626
4627    if (zend_parse_parameters_none() == FAILURE) {
4628        return;
4629    }
4630
4631    if (entry_obj->ent.entry->is_dir) {
4632        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \
4633            "Phar entry is a directory, does not have a CRC"); \
4634        return;
4635    }
4636
4637    if (entry_obj->ent.entry->is_crc_checked) {
4638        RETURN_LONG(entry_obj->ent.entry->crc32);
4639    } else {
4640        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \
4641            "Phar entry was not CRC checked"); \
4642    }
4643}
4644/* }}} */
4645
4646/* {{{ proto int PharFileInfo::isCRCChecked()
4647 * Returns whether file entry is CRC checked
4648 */
4649PHP_METHOD(PharFileInfo, isCRCChecked)
4650{
4651    PHAR_ENTRY_OBJECT();
4652
4653    if (zend_parse_parameters_none() == FAILURE) {
4654        return;
4655    }
4656
4657    RETURN_BOOL(entry_obj->ent.entry->is_crc_checked);
4658}
4659/* }}} */
4660
4661/* {{{ proto int PharFileInfo::getPharFlags()
4662 * Returns the Phar file entry flags
4663 */
4664PHP_METHOD(PharFileInfo, getPharFlags)
4665{
4666    PHAR_ENTRY_OBJECT();
4667
4668    if (zend_parse_parameters_none() == FAILURE) {
4669        return;
4670    }
4671
4672    RETURN_LONG(entry_obj->ent.entry->flags & ~(PHAR_ENT_PERM_MASK|PHAR_ENT_COMPRESSION_MASK));
4673}
4674/* }}} */
4675
4676/* {{{ proto int PharFileInfo::chmod()
4677 * set the file permissions for the Phar.  This only allows setting execution bit, read/write
4678 */
4679PHP_METHOD(PharFileInfo, chmod)
4680{
4681    char *error;
4682    long perms;
4683    PHAR_ENTRY_OBJECT();
4684
4685    if (entry_obj->ent.entry->is_temp_dir) {
4686        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \
4687            "Phar entry \"%s\" is a temporary directory (not an actual entry in the archive), cannot chmod", entry_obj->ent.entry->filename); \
4688        return;
4689    }
4690
4691    if (PHAR_G(readonly) && !entry_obj->ent.entry->phar->is_data) {
4692        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);
4693        return;
4694    }
4695
4696    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &perms) == FAILURE) {
4697        return;
4698    }
4699
4700    if (entry_obj->ent.entry->is_persistent) {
4701        phar_archive_data *phar = entry_obj->ent.entry->phar;
4702
4703        if (FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) {
4704            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar->fname);
4705            return;
4706        }
4707        /* re-populate after copy-on-write */
4708        zend_hash_find(&phar->manifest, entry_obj->ent.entry->filename, entry_obj->ent.entry->filename_len, (void **)&entry_obj->ent.entry);
4709    }
4710    /* clear permissions */
4711    entry_obj->ent.entry->flags &= ~PHAR_ENT_PERM_MASK;
4712    perms &= 0777;
4713    entry_obj->ent.entry->flags |= perms;
4714    entry_obj->ent.entry->old_flags = entry_obj->ent.entry->flags;
4715    entry_obj->ent.entry->phar->is_modified = 1;
4716    entry_obj->ent.entry->is_modified = 1;
4717
4718    /* hackish cache in php_stat needs to be cleared */
4719    /* if this code fails to work, check main/streams/streams.c, _php_stream_stat_path */
4720    if (BG(CurrentLStatFile)) {
4721        efree(BG(CurrentLStatFile));
4722    }
4723
4724    if (BG(CurrentStatFile)) {
4725        efree(BG(CurrentStatFile));
4726    }
4727
4728    BG(CurrentLStatFile) = NULL;
4729    BG(CurrentStatFile) = NULL;
4730    phar_flush(entry_obj->ent.entry->phar, 0, 0, 0, &error TSRMLS_CC);
4731
4732    if (error) {
4733        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
4734        efree(error);
4735    }
4736}
4737/* }}} */
4738
4739/* {{{ proto int PharFileInfo::hasMetaData()
4740 * Returns the metadata of the entry
4741 */
4742PHP_METHOD(PharFileInfo, hasMetadata)
4743{
4744    PHAR_ENTRY_OBJECT();
4745
4746    if (zend_parse_parameters_none() == FAILURE) {
4747        return;
4748    }
4749
4750    RETURN_BOOL(entry_obj->ent.entry->metadata != NULL);
4751}
4752/* }}} */
4753
4754/* {{{ proto int PharFileInfo::getMetaData()
4755 * Returns the metadata of the entry
4756 */
4757PHP_METHOD(PharFileInfo, getMetadata)
4758{
4759    PHAR_ENTRY_OBJECT();
4760
4761    if (zend_parse_parameters_none() == FAILURE) {
4762        return;
4763    }
4764
4765    if (entry_obj->ent.entry->metadata) {
4766        if (entry_obj->ent.entry->is_persistent) {
4767            zval *ret;
4768            char *buf = estrndup((char *) entry_obj->ent.entry->metadata, entry_obj->ent.entry->metadata_len);
4769            /* assume success, we would have failed before */
4770            phar_parse_metadata(&buf, &ret, entry_obj->ent.entry->metadata_len TSRMLS_CC);
4771            efree(buf);
4772            RETURN_ZVAL(ret, 0, 1);
4773        }
4774        RETURN_ZVAL(entry_obj->ent.entry->metadata, 1, 0);
4775    }
4776}
4777/* }}} */
4778
4779/* {{{ proto int PharFileInfo::setMetaData(mixed $metadata)
4780 * Sets the metadata of the entry
4781 */
4782PHP_METHOD(PharFileInfo, setMetadata)
4783{
4784    char *error;
4785    zval *metadata;
4786
4787    PHAR_ENTRY_OBJECT();
4788
4789    if (PHAR_G(readonly) && !entry_obj->ent.entry->phar->is_data) {
4790        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Write operations disabled by the php.ini setting phar.readonly");
4791        return;
4792    }
4793
4794    if (entry_obj->ent.entry->is_temp_dir) {
4795        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \
4796            "Phar entry is a temporary directory (not an actual entry in the archive), cannot set metadata"); \
4797        return;
4798    }
4799
4800    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &metadata) == FAILURE) {
4801        return;
4802    }
4803
4804    if (entry_obj->ent.entry->is_persistent) {
4805        phar_archive_data *phar = entry_obj->ent.entry->phar;
4806
4807        if (FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) {
4808            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar->fname);
4809            return;
4810        }
4811        /* re-populate after copy-on-write */
4812        zend_hash_find(&phar->manifest, entry_obj->ent.entry->filename, entry_obj->ent.entry->filename_len, (void **)&entry_obj->ent.entry);
4813    }
4814    if (entry_obj->ent.entry->metadata) {
4815        zval_ptr_dtor(&entry_obj->ent.entry->metadata);
4816        entry_obj->ent.entry->metadata = NULL;
4817    }
4818
4819    MAKE_STD_ZVAL(entry_obj->ent.entry->metadata);
4820    ZVAL_ZVAL(entry_obj->ent.entry->metadata, metadata, 1, 0);
4821
4822    entry_obj->ent.entry->is_modified = 1;
4823    entry_obj->ent.entry->phar->is_modified = 1;
4824    phar_flush(entry_obj->ent.entry->phar, 0, 0, 0, &error TSRMLS_CC);
4825
4826    if (error) {
4827        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
4828        efree(error);
4829    }
4830}
4831/* }}} */
4832
4833/* {{{ proto bool PharFileInfo::delMetaData()
4834 * Deletes the metadata of the entry
4835 */
4836PHP_METHOD(PharFileInfo, delMetadata)
4837{
4838    char *error;
4839
4840    PHAR_ENTRY_OBJECT();
4841
4842    if (zend_parse_parameters_none() == FAILURE) {
4843        return;
4844    }
4845
4846    if (PHAR_G(readonly) && !entry_obj->ent.entry->phar->is_data) {
4847        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Write operations disabled by the php.ini setting phar.readonly");
4848        return;
4849    }
4850
4851    if (entry_obj->ent.entry->is_temp_dir) {
4852        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \
4853            "Phar entry is a temporary directory (not an actual entry in the archive), cannot delete metadata"); \
4854        return;
4855    }
4856
4857    if (entry_obj->ent.entry->metadata) {
4858        if (entry_obj->ent.entry->is_persistent) {
4859            phar_archive_data *phar = entry_obj->ent.entry->phar;
4860
4861            if (FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) {
4862                zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar->fname);
4863                return;
4864            }
4865            /* re-populate after copy-on-write */
4866            zend_hash_find(&phar->manifest, entry_obj->ent.entry->filename, entry_obj->ent.entry->filename_len, (void **)&entry_obj->ent.entry);
4867        }
4868        zval_ptr_dtor(&entry_obj->ent.entry->metadata);
4869        entry_obj->ent.entry->metadata = NULL;
4870        entry_obj->ent.entry->is_modified = 1;
4871        entry_obj->ent.entry->phar->is_modified = 1;
4872
4873        phar_flush(entry_obj->ent.entry->phar, 0, 0, 0, &error TSRMLS_CC);
4874
4875        if (error) {
4876            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
4877            efree(error);
4878            RETURN_FALSE;
4879        } else {
4880            RETURN_TRUE;
4881        }
4882
4883    } else {
4884        RETURN_TRUE;
4885    }
4886}
4887/* }}} */
4888
4889/* {{{ proto string PharFileInfo::getContent()
4890 * return the complete file contents of the entry (like file_get_contents)
4891 */
4892PHP_METHOD(PharFileInfo, getContent)
4893{
4894    char *error;
4895    php_stream *fp;
4896    phar_entry_info *link;
4897
4898    PHAR_ENTRY_OBJECT();
4899
4900    if (zend_parse_parameters_none() == FAILURE) {
4901        return;
4902    }
4903
4904    if (entry_obj->ent.entry->is_dir) {
4905        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
4906            "Phar error: Cannot retrieve contents, \"%s\" in phar \"%s\" is a directory", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname);
4907        return;
4908    }
4909
4910    link = phar_get_link_source(entry_obj->ent.entry TSRMLS_CC);
4911
4912    if (!link) {
4913        link = entry_obj->ent.entry;
4914    }
4915
4916    if (SUCCESS != phar_open_entry_fp(link, &error, 0 TSRMLS_CC)) {
4917        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
4918            "Phar error: Cannot retrieve contents, \"%s\" in phar \"%s\": %s", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname, error);
4919        efree(error);
4920        return;
4921    }
4922
4923    if (!(fp = phar_get_efp(link, 0 TSRMLS_CC))) {
4924        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
4925            "Phar error: Cannot retrieve contents of \"%s\" in phar \"%s\"", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname);
4926        return;
4927    }
4928
4929    phar_seek_efp(link, 0, SEEK_SET, 0, 0 TSRMLS_CC);
4930    Z_TYPE_P(return_value) = IS_STRING;
4931    Z_STRLEN_P(return_value) = php_stream_copy_to_mem(fp, &(Z_STRVAL_P(return_value)), link->uncompressed_filesize, 0);
4932
4933    if (!Z_STRVAL_P(return_value)) {
4934        Z_STRVAL_P(return_value) = estrndup("", 0);
4935    }
4936}
4937/* }}} */
4938
4939/* {{{ proto int PharFileInfo::compress(int compression_type)
4940 * Instructs the Phar class to compress the current file using zlib or bzip2 compression
4941 */
4942PHP_METHOD(PharFileInfo, compress)
4943{
4944    long method;
4945    char *error;
4946    PHAR_ENTRY_OBJECT();
4947
4948    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &method) == FAILURE) {
4949        return;
4950    }
4951
4952    if (entry_obj->ent.entry->is_tar) {
4953        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
4954            "Cannot compress with Gzip compression, not possible with tar-based phar archives");
4955        return;
4956    }
4957
4958    if (entry_obj->ent.entry->is_dir) {
4959        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \
4960            "Phar entry is a directory, cannot set compression"); \
4961        return;
4962    }
4963
4964    if (PHAR_G(readonly) && !entry_obj->ent.entry->phar->is_data) {
4965        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
4966            "Phar is readonly, cannot change compression");
4967        return;
4968    }
4969
4970    if (entry_obj->ent.entry->is_deleted) {
4971        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
4972            "Cannot compress deleted file");
4973        return;
4974    }
4975
4976    if (entry_obj->ent.entry->is_persistent) {
4977        phar_archive_data *phar = entry_obj->ent.entry->phar;
4978
4979        if (FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) {
4980            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar->fname);
4981            return;
4982        }
4983        /* re-populate after copy-on-write */
4984        zend_hash_find(&phar->manifest, entry_obj->ent.entry->filename, entry_obj->ent.entry->filename_len, (void **)&entry_obj->ent.entry);
4985    }
4986    switch (method) {
4987        case PHAR_ENT_COMPRESSED_GZ:
4988            if (entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_GZ) {
4989                RETURN_TRUE;
4990            }
4991
4992            if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_BZ2) != 0) {
4993                if (!PHAR_G(has_bz2)) {
4994                    zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
4995                        "Cannot compress with gzip compression, file is already compressed with bzip2 compression and bz2 extension is not enabled, cannot decompress");
4996                    return;
4997                }
4998
4999                /* decompress this file indirectly */
5000                if (SUCCESS != phar_open_entry_fp(entry_obj->ent.entry, &error, 1 TSRMLS_CC)) {
5001                    zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
5002                        "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);
5003                    efree(error);
5004                    return;
5005                }
5006            }
5007
5008            if (!PHAR_G(has_zlib)) {
5009                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
5010                    "Cannot compress with gzip compression, zlib extension is not enabled");
5011                return;
5012            }
5013
5014            entry_obj->ent.entry->old_flags = entry_obj->ent.entry->flags;
5015            entry_obj->ent.entry->flags &= ~PHAR_ENT_COMPRESSION_MASK;
5016            entry_obj->ent.entry->flags |= PHAR_ENT_COMPRESSED_GZ;
5017            break;
5018        case PHAR_ENT_COMPRESSED_BZ2:
5019            if (entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
5020                RETURN_TRUE;
5021            }
5022
5023            if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_GZ) != 0) {
5024                if (!PHAR_G(has_zlib)) {
5025                    zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
5026                        "Cannot compress with bzip2 compression, file is already compressed with gzip compression and zlib extension is not enabled, cannot decompress");
5027                    return;
5028                }
5029
5030                /* decompress this file indirectly */
5031                if (SUCCESS != phar_open_entry_fp(entry_obj->ent.entry, &error, 1 TSRMLS_CC)) {
5032                    zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
5033                        "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);
5034                    efree(error);
5035                    return;
5036                }
5037            }
5038
5039            if (!PHAR_G(has_bz2)) {
5040                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
5041                    "Cannot compress with bzip2 compression, bz2 extension is not enabled");
5042                return;
5043            }
5044            entry_obj->ent.entry->old_flags = entry_obj->ent.entry->flags;
5045            entry_obj->ent.entry->flags &= ~PHAR_ENT_COMPRESSION_MASK;
5046            entry_obj->ent.entry->flags |= PHAR_ENT_COMPRESSED_BZ2;
5047            break;
5048        default:
5049            zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \
5050                "Unknown compression type specified"); \
5051    }
5052
5053    entry_obj->ent.entry->phar->is_modified = 1;
5054    entry_obj->ent.entry->is_modified = 1;
5055    phar_flush(entry_obj->ent.entry->phar, 0, 0, 0, &error TSRMLS_CC);
5056
5057    if (error) {
5058        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
5059        efree(error);
5060    }
5061
5062    RETURN_TRUE;
5063}
5064/* }}} */
5065
5066/* {{{ proto int PharFileInfo::decompress()
5067 * Instructs the Phar class to decompress the current file
5068 */
5069PHP_METHOD(PharFileInfo, decompress)
5070{
5071    char *error;
5072    PHAR_ENTRY_OBJECT();
5073
5074    if (zend_parse_parameters_none() == FAILURE) {
5075        return;
5076    }
5077
5078    if (entry_obj->ent.entry->is_dir) {
5079        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \
5080            "Phar entry is a directory, cannot set compression"); \
5081        return;
5082    }
5083
5084    if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSION_MASK) == 0) {
5085        RETURN_TRUE;
5086    }
5087
5088    if (PHAR_G(readonly) && !entry_obj->ent.entry->phar->is_data) {
5089        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
5090            "Phar is readonly, cannot decompress");
5091        return;
5092    }
5093
5094    if (entry_obj->ent.entry->is_deleted) {
5095        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
5096            "Cannot compress deleted file");
5097        return;
5098    }
5099
5100    if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_GZ) != 0 && !PHAR_G(has_zlib)) {
5101        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
5102            "Cannot decompress Gzip-compressed file, zlib extension is not enabled");
5103        return;
5104    }
5105
5106    if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_BZ2) != 0 && !PHAR_G(has_bz2)) {
5107        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
5108            "Cannot decompress Bzip2-compressed file, bz2 extension is not enabled");
5109        return;
5110    }
5111
5112    if (entry_obj->ent.entry->is_persistent) {
5113        phar_archive_data *phar = entry_obj->ent.entry->phar;
5114
5115        if (FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) {
5116            zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar->fname);
5117            return;
5118        }
5119        /* re-populate after copy-on-write */
5120        zend_hash_find(&phar->manifest, entry_obj->ent.entry->filename, entry_obj->ent.entry->filename_len, (void **)&entry_obj->ent.entry);
5121    }
5122    if (!entry_obj->ent.entry->fp) {
5123        if (FAILURE == phar_open_archive_fp(entry_obj->ent.entry->phar TSRMLS_CC)) {
5124            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);
5125            return;
5126        }
5127        entry_obj->ent.entry->fp_type = PHAR_FP;
5128    }
5129
5130    entry_obj->ent.entry->old_flags = entry_obj->ent.entry->flags;
5131    entry_obj->ent.entry->flags &= ~PHAR_ENT_COMPRESSION_MASK;
5132    entry_obj->ent.entry->phar->is_modified = 1;
5133    entry_obj->ent.entry->is_modified = 1;
5134    phar_flush(entry_obj->ent.entry->phar, 0, 0, 0, &error TSRMLS_CC);
5135
5136    if (error) {
5137        zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
5138        efree(error);
5139    }
5140    RETURN_TRUE;
5141}
5142/* }}} */
5143
5144#endif /* HAVE_SPL */
5145
5146/* {{{ phar methods */
5147PHAR_ARG_INFO
5148ZEND_BEGIN_ARG_INFO_EX(arginfo_phar___construct, 0, 0, 1)
5149    ZEND_ARG_INFO(0, filename)
5150    ZEND_ARG_INFO(0, flags)
5151    ZEND_ARG_INFO(0, alias)
5152    ZEND_ARG_INFO(0, fileformat)
5153ZEND_END_ARG_INFO()
5154
5155PHAR_ARG_INFO
5156ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_createDS, 0, 0, 0)
5157    ZEND_ARG_INFO(0, index)
5158    ZEND_ARG_INFO(0, webindex)
5159ZEND_END_ARG_INFO()
5160
5161PHAR_ARG_INFO
5162ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_cancompress, 0, 0, 0)
5163    ZEND_ARG_INFO(0, method)
5164ZEND_END_ARG_INFO()
5165
5166PHAR_ARG_INFO
5167ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_isvalidpharfilename, 0, 0, 1)
5168    ZEND_ARG_INFO(0, filename)
5169    ZEND_ARG_INFO(0, executable)
5170ZEND_END_ARG_INFO()
5171
5172PHAR_ARG_INFO
5173ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_loadPhar, 0, 0, 1)
5174    ZEND_ARG_INFO(0, filename)
5175    ZEND_ARG_INFO(0, alias)
5176ZEND_END_ARG_INFO()
5177
5178PHAR_ARG_INFO
5179ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_mapPhar, 0, 0, 0)
5180    ZEND_ARG_INFO(0, alias)
5181    ZEND_ARG_INFO(0, offset)
5182ZEND_END_ARG_INFO()
5183
5184PHAR_ARG_INFO
5185ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_mount, 0, 0, 2)
5186    ZEND_ARG_INFO(0, inphar)
5187    ZEND_ARG_INFO(0, externalfile)
5188ZEND_END_ARG_INFO()
5189
5190PHAR_ARG_INFO
5191ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_mungServer, 0, 0, 1)
5192    ZEND_ARG_INFO(0, munglist)
5193ZEND_END_ARG_INFO()
5194
5195PHAR_ARG_INFO
5196ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_webPhar, 0, 0, 0)
5197    ZEND_ARG_INFO(0, alias)
5198    ZEND_ARG_INFO(0, index)
5199    ZEND_ARG_INFO(0, f404)
5200    ZEND_ARG_INFO(0, mimetypes)
5201    ZEND_ARG_INFO(0, rewrites)
5202ZEND_END_ARG_INFO()
5203
5204PHAR_ARG_INFO
5205ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_running, 0, 0, 1)
5206    ZEND_ARG_INFO(0, retphar)
5207ZEND_END_ARG_INFO()
5208
5209PHAR_ARG_INFO
5210ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_ua, 0, 0, 1)
5211    ZEND_ARG_INFO(0, archive)
5212ZEND_END_ARG_INFO()
5213
5214#if HAVE_SPL
5215PHAR_ARG_INFO
5216ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_build, 0, 0, 1)
5217    ZEND_ARG_INFO(0, iterator)
5218    ZEND_ARG_INFO(0, base_directory)
5219ZEND_END_ARG_INFO()
5220
5221PHAR_ARG_INFO
5222ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_conv, 0, 0, 0)
5223    ZEND_ARG_INFO(0, format)
5224    ZEND_ARG_INFO(0, compression_type)
5225    ZEND_ARG_INFO(0, file_ext)
5226ZEND_END_ARG_INFO()
5227
5228PHAR_ARG_INFO
5229ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_comps, 0, 0, 1)
5230    ZEND_ARG_INFO(0, compression_type)
5231    ZEND_ARG_INFO(0, file_ext)
5232ZEND_END_ARG_INFO()
5233
5234PHAR_ARG_INFO
5235ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_decomp, 0, 0, 0)
5236    ZEND_ARG_INFO(0, file_ext)
5237ZEND_END_ARG_INFO()
5238
5239PHAR_ARG_INFO
5240ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_comp, 0, 0, 1)
5241    ZEND_ARG_INFO(0, compression_type)
5242ZEND_END_ARG_INFO()
5243
5244PHAR_ARG_INFO
5245ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_compo, 0, 0, 0)
5246    ZEND_ARG_INFO(0, compression_type)
5247ZEND_END_ARG_INFO()
5248
5249PHAR_ARG_INFO
5250ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_copy, 0, 0, 2)
5251    ZEND_ARG_INFO(0, newfile)
5252    ZEND_ARG_INFO(0, oldfile)
5253ZEND_END_ARG_INFO()
5254
5255PHAR_ARG_INFO
5256ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_delete, 0, 0, 1)
5257    ZEND_ARG_INFO(0, entry)
5258ZEND_END_ARG_INFO()
5259
5260PHAR_ARG_INFO
5261ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_fromdir, 0, 0, 1)
5262    ZEND_ARG_INFO(0, base_dir)
5263    ZEND_ARG_INFO(0, regex)
5264ZEND_END_ARG_INFO()
5265
5266PHAR_ARG_INFO
5267ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_offsetExists, 0, 0, 1)
5268    ZEND_ARG_INFO(0, entry)
5269ZEND_END_ARG_INFO()
5270
5271PHAR_ARG_INFO
5272ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_offsetSet, 0, 0, 2)
5273    ZEND_ARG_INFO(0, entry)
5274    ZEND_ARG_INFO(0, value)
5275ZEND_END_ARG_INFO()
5276
5277PHAR_ARG_INFO
5278ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_setAlias, 0, 0, 1)
5279    ZEND_ARG_INFO(0, alias)
5280ZEND_END_ARG_INFO()
5281
5282PHAR_ARG_INFO
5283ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_setMetadata, 0, 0, 1)
5284    ZEND_ARG_INFO(0, metadata)
5285ZEND_END_ARG_INFO()
5286
5287PHAR_ARG_INFO
5288ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_setSigAlgo, 0, 0, 1)
5289    ZEND_ARG_INFO(0, algorithm)
5290    ZEND_ARG_INFO(0, privatekey)
5291ZEND_END_ARG_INFO()
5292
5293PHAR_ARG_INFO
5294ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_setStub, 0, 0, 1)
5295    ZEND_ARG_INFO(0, newstub)
5296    ZEND_ARG_INFO(0, maxlen)
5297ZEND_END_ARG_INFO()
5298
5299PHAR_ARG_INFO
5300ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_emptydir, 0, 0, 0)
5301    ZEND_ARG_INFO(0, dirname)
5302ZEND_END_ARG_INFO()
5303
5304PHAR_ARG_INFO
5305ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_extract, 0, 0, 1)
5306    ZEND_ARG_INFO(0, pathto)
5307    ZEND_ARG_INFO(0, files)
5308    ZEND_ARG_INFO(0, overwrite)
5309ZEND_END_ARG_INFO()
5310
5311PHAR_ARG_INFO
5312ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_addfile, 0, 0, 1)
5313    ZEND_ARG_INFO(0, filename)
5314    ZEND_ARG_INFO(0, localname)
5315ZEND_END_ARG_INFO()
5316
5317PHAR_ARG_INFO
5318ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_fromstring, 0, 0, 1)
5319    ZEND_ARG_INFO(0, localname)
5320    ZEND_ARG_INFO(0, contents)
5321ZEND_END_ARG_INFO()
5322
5323PHAR_ARG_INFO
5324ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_isff, 0, 0, 1)
5325    ZEND_ARG_INFO(0, fileformat)
5326ZEND_END_ARG_INFO()
5327
5328PHAR_ARG_INFO
5329ZEND_BEGIN_ARG_INFO(arginfo_phar__void, 0)
5330ZEND_END_ARG_INFO()
5331
5332
5333#endif /* HAVE_SPL */
5334
5335zend_function_entry php_archive_methods[] = {
5336#if !HAVE_SPL
5337    PHP_ME(Phar, __construct,           arginfo_phar___construct,  ZEND_ACC_PRIVATE)
5338#else
5339    PHP_ME(Phar, __construct,           arginfo_phar___construct,  ZEND_ACC_PUBLIC)
5340    PHP_ME(Phar, __destruct,            arginfo_phar__void,        ZEND_ACC_PUBLIC)
5341    PHP_ME(Phar, addEmptyDir,           arginfo_phar_emptydir,     ZEND_ACC_PUBLIC)
5342    PHP_ME(Phar, addFile,               arginfo_phar_addfile,      ZEND_ACC_PUBLIC)
5343    PHP_ME(Phar, addFromString,         arginfo_phar_fromstring,   ZEND_ACC_PUBLIC)
5344    PHP_ME(Phar, buildFromDirectory,    arginfo_phar_fromdir,      ZEND_ACC_PUBLIC)
5345    PHP_ME(Phar, buildFromIterator,     arginfo_phar_build,        ZEND_ACC_PUBLIC)
5346    PHP_ME(Phar, compressFiles,         arginfo_phar_comp,         ZEND_ACC_PUBLIC)
5347    PHP_ME(Phar, decompressFiles,       arginfo_phar__void,        ZEND_ACC_PUBLIC)
5348    PHP_ME(Phar, compress,              arginfo_phar_comps,        ZEND_ACC_PUBLIC)
5349    PHP_ME(Phar, decompress,            arginfo_phar_decomp,       ZEND_ACC_PUBLIC)
5350    PHP_ME(Phar, convertToExecutable,   arginfo_phar_conv,         ZEND_ACC_PUBLIC)
5351    PHP_ME(Phar, convertToData,         arginfo_phar_conv,         ZEND_ACC_PUBLIC)
5352    PHP_ME(Phar, copy,                  arginfo_phar_copy,         ZEND_ACC_PUBLIC)
5353    PHP_ME(Phar, count,                 arginfo_phar__void,        ZEND_ACC_PUBLIC)
5354    PHP_ME(Phar, delete,                arginfo_phar_delete,       ZEND_ACC_PUBLIC)
5355    PHP_ME(Phar, delMetadata,           arginfo_phar__void,        ZEND_ACC_PUBLIC)
5356    PHP_ME(Phar, extractTo,             arginfo_phar_extract,      ZEND_ACC_PUBLIC)
5357    PHP_ME(Phar, getAlias,              arginfo_phar__void,        ZEND_ACC_PUBLIC)
5358    PHP_ME(Phar, getPath,               arginfo_phar__void,        ZEND_ACC_PUBLIC)
5359    PHP_ME(Phar, getMetadata,           arginfo_phar__void,        ZEND_ACC_PUBLIC)
5360    PHP_ME(Phar, getModified,           arginfo_phar__void,        ZEND_ACC_PUBLIC)
5361    PHP_ME(Phar, getSignature,          arginfo_phar__void,        ZEND_ACC_PUBLIC)
5362    PHP_ME(Phar, getStub,               arginfo_phar__void,        ZEND_ACC_PUBLIC)
5363    PHP_ME(Phar, getVersion,            arginfo_phar__void,        ZEND_ACC_PUBLIC)
5364    PHP_ME(Phar, hasMetadata,           arginfo_phar__void,        ZEND_ACC_PUBLIC)
5365    PHP_ME(Phar, isBuffering,           arginfo_phar__void,        ZEND_ACC_PUBLIC)
5366    PHP_ME(Phar, isCompressed,          arginfo_phar__void,        ZEND_ACC_PUBLIC)
5367    PHP_ME(Phar, isFileFormat,          arginfo_phar_isff,         ZEND_ACC_PUBLIC)
5368    PHP_ME(Phar, isWritable,            arginfo_phar__void,        ZEND_ACC_PUBLIC)
5369    PHP_ME(Phar, offsetExists,          arginfo_phar_offsetExists, ZEND_ACC_PUBLIC)
5370    PHP_ME(Phar, offsetGet,             arginfo_phar_offsetExists, ZEND_ACC_PUBLIC)
5371    PHP_ME(Phar, offsetSet,             arginfo_phar_offsetSet,    ZEND_ACC_PUBLIC)
5372    PHP_ME(Phar, offsetUnset,           arginfo_phar_offsetExists, ZEND_ACC_PUBLIC)
5373    PHP_ME(Phar, setAlias,              arginfo_phar_setAlias,     ZEND_ACC_PUBLIC)
5374    PHP_ME(Phar, setDefaultStub,        arginfo_phar_createDS,     ZEND_ACC_PUBLIC)
5375    PHP_ME(Phar, setMetadata,           arginfo_phar_setMetadata,  ZEND_ACC_PUBLIC)
5376    PHP_ME(Phar, setSignatureAlgorithm, arginfo_phar_setSigAlgo,   ZEND_ACC_PUBLIC)
5377    PHP_ME(Phar, setStub,               arginfo_phar_setStub,      ZEND_ACC_PUBLIC)
5378    PHP_ME(Phar, startBuffering,        arginfo_phar__void,        ZEND_ACC_PUBLIC)
5379    PHP_ME(Phar, stopBuffering,         arginfo_phar__void,        ZEND_ACC_PUBLIC)
5380#endif
5381    /* static member functions */
5382    PHP_ME(Phar, apiVersion,            arginfo_phar__void,        ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
5383    PHP_ME(Phar, canCompress,           arginfo_phar_cancompress,  ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
5384    PHP_ME(Phar, canWrite,              arginfo_phar__void,        ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
5385    PHP_ME(Phar, createDefaultStub,     arginfo_phar_createDS,     ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
5386    PHP_ME(Phar, getSupportedCompression,arginfo_phar__void,       ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
5387    PHP_ME(Phar, getSupportedSignatures,arginfo_phar__void,        ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
5388    PHP_ME(Phar, interceptFileFuncs,    arginfo_phar__void,        ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
5389    PHP_ME(Phar, isValidPharFilename,   arginfo_phar_isvalidpharfilename, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
5390    PHP_ME(Phar, loadPhar,              arginfo_phar_loadPhar,     ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
5391    PHP_ME(Phar, mapPhar,               arginfo_phar_mapPhar,      ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
5392    PHP_ME(Phar, running,               arginfo_phar_running,      ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
5393    PHP_ME(Phar, mount,                 arginfo_phar_mount,        ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
5394    PHP_ME(Phar, mungServer,            arginfo_phar_mungServer,   ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
5395    PHP_ME(Phar, unlinkArchive,         arginfo_phar_ua,           ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
5396    PHP_ME(Phar, webPhar,               arginfo_phar_webPhar,      ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
5397    PHP_FE_END
5398};
5399
5400#if HAVE_SPL
5401PHAR_ARG_INFO
5402ZEND_BEGIN_ARG_INFO_EX(arginfo_entry___construct, 0, 0, 1)
5403    ZEND_ARG_INFO(0, filename)
5404ZEND_END_ARG_INFO()
5405
5406PHAR_ARG_INFO
5407ZEND_BEGIN_ARG_INFO_EX(arginfo_entry_chmod, 0, 0, 1)
5408    ZEND_ARG_INFO(0, perms)
5409ZEND_END_ARG_INFO()
5410
5411zend_function_entry php_entry_methods[] = {
5412    PHP_ME(PharFileInfo, __construct,        arginfo_entry___construct,  ZEND_ACC_PUBLIC)
5413    PHP_ME(PharFileInfo, __destruct,         arginfo_phar__void,         ZEND_ACC_PUBLIC)
5414    PHP_ME(PharFileInfo, chmod,              arginfo_entry_chmod,        ZEND_ACC_PUBLIC)
5415    PHP_ME(PharFileInfo, compress,           arginfo_phar_comp,          ZEND_ACC_PUBLIC)
5416    PHP_ME(PharFileInfo, decompress,         arginfo_phar__void,         ZEND_ACC_PUBLIC)
5417    PHP_ME(PharFileInfo, delMetadata,        arginfo_phar__void,         ZEND_ACC_PUBLIC)
5418    PHP_ME(PharFileInfo, getCompressedSize,  arginfo_phar__void,         ZEND_ACC_PUBLIC)
5419    PHP_ME(PharFileInfo, getCRC32,           arginfo_phar__void,         ZEND_ACC_PUBLIC)
5420    PHP_ME(PharFileInfo, getContent,         arginfo_phar__void,         ZEND_ACC_PUBLIC)
5421    PHP_ME(PharFileInfo, getMetadata,        arginfo_phar__void,         ZEND_ACC_PUBLIC)
5422    PHP_ME(PharFileInfo, getPharFlags,       arginfo_phar__void,         ZEND_ACC_PUBLIC)
5423    PHP_ME(PharFileInfo, hasMetadata,        arginfo_phar__void,         ZEND_ACC_PUBLIC)
5424    PHP_ME(PharFileInfo, isCompressed,       arginfo_phar_compo,         ZEND_ACC_PUBLIC)
5425    PHP_ME(PharFileInfo, isCRCChecked,       arginfo_phar__void,         ZEND_ACC_PUBLIC)
5426    PHP_ME(PharFileInfo, setMetadata,        arginfo_phar_setMetadata,   ZEND_ACC_PUBLIC)
5427    PHP_FE_END
5428};
5429#endif /* HAVE_SPL */
5430
5431zend_function_entry phar_exception_methods[] = {
5432    PHP_FE_END
5433};
5434/* }}} */
5435
5436#define REGISTER_PHAR_CLASS_CONST_LONG(class_name, const_name, value) \
5437    zend_declare_class_constant_long(class_name, const_name, sizeof(const_name)-1, (long)value TSRMLS_CC);
5438
5439#if PHP_VERSION_ID < 50200
5440# define phar_exception_get_default() zend_exception_get_default()
5441#else
5442# define phar_exception_get_default() zend_exception_get_default(TSRMLS_C)
5443#endif
5444
5445void phar_object_init(TSRMLS_D) /* {{{ */
5446{
5447    zend_class_entry ce;
5448
5449    INIT_CLASS_ENTRY(ce, "PharException", phar_exception_methods);
5450    phar_ce_PharException = zend_register_internal_class_ex(&ce, phar_exception_get_default(), NULL  TSRMLS_CC);
5451
5452#if HAVE_SPL
5453    INIT_CLASS_ENTRY(ce, "Phar", php_archive_methods);
5454    phar_ce_archive = zend_register_internal_class_ex(&ce, spl_ce_RecursiveDirectoryIterator, NULL  TSRMLS_CC);
5455
5456    zend_class_implements(phar_ce_archive TSRMLS_CC, 2, spl_ce_Countable, zend_ce_arrayaccess);
5457
5458    INIT_CLASS_ENTRY(ce, "PharData", php_archive_methods);
5459    phar_ce_data = zend_register_internal_class_ex(&ce, spl_ce_RecursiveDirectoryIterator, NULL  TSRMLS_CC);
5460
5461    zend_class_implements(phar_ce_data TSRMLS_CC, 2, spl_ce_Countable, zend_ce_arrayaccess);
5462
5463    INIT_CLASS_ENTRY(ce, "PharFileInfo", php_entry_methods);
5464    phar_ce_entry = zend_register_internal_class_ex(&ce, spl_ce_SplFileInfo, NULL  TSRMLS_CC);
5465#else
5466    INIT_CLASS_ENTRY(ce, "Phar", php_archive_methods);
5467    phar_ce_archive = zend_register_internal_class(&ce TSRMLS_CC);
5468    phar_ce_archive->ce_flags |= ZEND_ACC_FINAL_CLASS;
5469
5470    INIT_CLASS_ENTRY(ce, "PharData", php_archive_methods);
5471    phar_ce_data = zend_register_internal_class(&ce TSRMLS_CC);
5472    phar_ce_data->ce_flags |= ZEND_ACC_FINAL_CLASS;
5473#endif
5474
5475    REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "BZ2", PHAR_ENT_COMPRESSED_BZ2)
5476    REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "GZ", PHAR_ENT_COMPRESSED_GZ)
5477    REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "NONE", PHAR_ENT_COMPRESSED_NONE)
5478    REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "PHAR", PHAR_FORMAT_PHAR)
5479    REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "TAR", PHAR_FORMAT_TAR)
5480    REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "ZIP", PHAR_FORMAT_ZIP)
5481    REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "COMPRESSED", PHAR_ENT_COMPRESSION_MASK)
5482    REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "PHP", PHAR_MIME_PHP)
5483    REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "PHPS", PHAR_MIME_PHPS)
5484    REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "MD5", PHAR_SIG_MD5)
5485    REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "OPENSSL", PHAR_SIG_OPENSSL)
5486    REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "SHA1", PHAR_SIG_SHA1)
5487    REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "SHA256", PHAR_SIG_SHA256)
5488    REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "SHA512", PHAR_SIG_SHA512)
5489}
5490/* }}} */
5491
5492/*
5493 * Local variables:
5494 * tab-width: 4
5495 * c-basic-offset: 4
5496 * End:
5497 * vim600: noet sw=4 ts=4 fdm=marker
5498 * vim<600: noet sw=4 ts=4
5499 */
5500