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: ba734629367f9671b25202408d13914fa63d8396 $ */
21
22 #define PHAR_MAIN 1
23 #include "phar_internal.h"
24 #include "SAPI.h"
25 #include "func_interceptors.h"
26
27 static void destroy_phar_data(void *pDest);
28
29 ZEND_DECLARE_MODULE_GLOBALS(phar)
30 #if PHP_VERSION_ID >= 50300
31 char *(*phar_save_resolve_path)(const char *filename, int filename_len TSRMLS_DC);
32 #endif
33
34 /**
35 * set's phar->is_writeable based on the current INI value
36 */
phar_set_writeable_bit(void * pDest,void * argument TSRMLS_DC)37 static int phar_set_writeable_bit(void *pDest, void *argument TSRMLS_DC) /* {{{ */
38 {
39 zend_bool keep = *(zend_bool *)argument;
40 phar_archive_data *phar = *(phar_archive_data **)pDest;
41
42 if (!phar->is_data) {
43 phar->is_writeable = !keep;
44 }
45
46 return ZEND_HASH_APPLY_KEEP;
47 }
48 /* }}} */
49
50 /* if the original value is 0 (disabled), then allow setting/unsetting at will. Otherwise only allow 1 (enabled), and error on disabling */
ZEND_INI_MH(phar_ini_modify_handler)51 ZEND_INI_MH(phar_ini_modify_handler) /* {{{ */
52 {
53 zend_bool old, ini;
54
55 if (entry->name_length == 14) {
56 old = PHAR_G(readonly_orig);
57 } else {
58 old = PHAR_G(require_hash_orig);
59 }
60
61 if (new_value_length == 2 && !strcasecmp("on", new_value)) {
62 ini = (zend_bool) 1;
63 }
64 else if (new_value_length == 3 && !strcasecmp("yes", new_value)) {
65 ini = (zend_bool) 1;
66 }
67 else if (new_value_length == 4 && !strcasecmp("true", new_value)) {
68 ini = (zend_bool) 1;
69 }
70 else {
71 ini = (zend_bool) atoi(new_value);
72 }
73
74 /* do not allow unsetting in runtime */
75 if (stage == ZEND_INI_STAGE_STARTUP) {
76 if (entry->name_length == 14) {
77 PHAR_G(readonly_orig) = ini;
78 } else {
79 PHAR_G(require_hash_orig) = ini;
80 }
81 } else if (old && !ini) {
82 return FAILURE;
83 }
84
85 if (entry->name_length == 14) {
86 PHAR_G(readonly) = ini;
87 if (PHAR_GLOBALS->request_init && PHAR_GLOBALS->phar_fname_map.arBuckets) {
88 zend_hash_apply_with_argument(&(PHAR_GLOBALS->phar_fname_map), phar_set_writeable_bit, (void *)&ini TSRMLS_CC);
89 }
90 } else {
91 PHAR_G(require_hash) = ini;
92 }
93
94 return SUCCESS;
95 }
96 /* }}}*/
97
98 /* this global stores the global cached pre-parsed manifests */
99 HashTable cached_phars;
100 HashTable cached_alias;
101
phar_split_cache_list(TSRMLS_D)102 static void phar_split_cache_list(TSRMLS_D) /* {{{ */
103 {
104 char *tmp;
105 char *key, *lasts, *end;
106 char ds[2];
107 phar_archive_data *phar;
108 uint i = 0;
109
110 if (!PHAR_GLOBALS->cache_list || !(PHAR_GLOBALS->cache_list[0])) {
111 return;
112 }
113
114 ds[0] = DEFAULT_DIR_SEPARATOR;
115 ds[1] = '\0';
116 tmp = estrdup(PHAR_GLOBALS->cache_list);
117
118 /* fake request startup */
119 PHAR_GLOBALS->request_init = 1;
120 if (zend_hash_init(&EG(regular_list), 0, NULL, NULL, 0) == SUCCESS) {
121 EG(regular_list).nNextFreeElement=1; /* we don't want resource id 0 */
122 }
123
124 PHAR_G(has_bz2) = zend_hash_exists(&module_registry, "bz2", sizeof("bz2"));
125 PHAR_G(has_zlib) = zend_hash_exists(&module_registry, "zlib", sizeof("zlib"));
126 /* these two are dummies and will be destroyed later */
127 zend_hash_init(&cached_phars, sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data, 1);
128 zend_hash_init(&cached_alias, sizeof(phar_archive_data*), zend_get_hash_value, NULL, 1);
129 /* these two are real and will be copied over cached_phars/cached_alias later */
130 zend_hash_init(&(PHAR_GLOBALS->phar_fname_map), sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data, 1);
131 zend_hash_init(&(PHAR_GLOBALS->phar_alias_map), sizeof(phar_archive_data*), zend_get_hash_value, NULL, 1);
132 PHAR_GLOBALS->manifest_cached = 1;
133 PHAR_GLOBALS->persist = 1;
134
135 for (key = php_strtok_r(tmp, ds, &lasts);
136 key;
137 key = php_strtok_r(NULL, ds, &lasts)) {
138 end = strchr(key, DEFAULT_DIR_SEPARATOR);
139
140 if (end) {
141 if (SUCCESS == phar_open_from_filename(key, end - key, NULL, 0, 0, &phar, NULL TSRMLS_CC)) {
142 finish_up:
143 phar->phar_pos = i++;
144 php_stream_close(phar->fp);
145 phar->fp = NULL;
146 } else {
147 finish_error:
148 PHAR_GLOBALS->persist = 0;
149 PHAR_GLOBALS->manifest_cached = 0;
150 efree(tmp);
151 zend_hash_destroy(&(PHAR_G(phar_fname_map)));
152 PHAR_GLOBALS->phar_fname_map.arBuckets = 0;
153 zend_hash_destroy(&(PHAR_G(phar_alias_map)));
154 PHAR_GLOBALS->phar_alias_map.arBuckets = 0;
155 zend_hash_destroy(&cached_phars);
156 zend_hash_destroy(&cached_alias);
157 zend_hash_graceful_reverse_destroy(&EG(regular_list));
158 memset(&EG(regular_list), 0, sizeof(HashTable));
159 /* free cached manifests */
160 PHAR_GLOBALS->request_init = 0;
161 return;
162 }
163 } else {
164 if (SUCCESS == phar_open_from_filename(key, strlen(key), NULL, 0, 0, &phar, NULL TSRMLS_CC)) {
165 goto finish_up;
166 } else {
167 goto finish_error;
168 }
169 }
170 }
171
172 PHAR_GLOBALS->persist = 0;
173 PHAR_GLOBALS->request_init = 0;
174 /* destroy dummy values from before */
175 zend_hash_destroy(&cached_phars);
176 zend_hash_destroy(&cached_alias);
177 cached_phars = PHAR_GLOBALS->phar_fname_map;
178 cached_alias = PHAR_GLOBALS->phar_alias_map;
179 PHAR_GLOBALS->phar_fname_map.arBuckets = 0;
180 PHAR_GLOBALS->phar_alias_map.arBuckets = 0;
181 zend_hash_graceful_reverse_destroy(&EG(regular_list));
182 memset(&EG(regular_list), 0, sizeof(HashTable));
183 efree(tmp);
184 }
185 /* }}} */
186
ZEND_INI_MH(phar_ini_cache_list)187 ZEND_INI_MH(phar_ini_cache_list) /* {{{ */
188 {
189 PHAR_G(cache_list) = new_value;
190
191 if (stage == ZEND_INI_STAGE_STARTUP) {
192 phar_split_cache_list(TSRMLS_C);
193 }
194
195 return SUCCESS;
196 }
197 /* }}} */
198
199 PHP_INI_BEGIN()
200 STD_PHP_INI_BOOLEAN( "phar.readonly", "1", PHP_INI_ALL, phar_ini_modify_handler, readonly, zend_phar_globals, phar_globals)
201 STD_PHP_INI_BOOLEAN( "phar.require_hash", "1", PHP_INI_ALL, phar_ini_modify_handler, require_hash, zend_phar_globals, phar_globals)
202 STD_PHP_INI_ENTRY("phar.cache_list", "", PHP_INI_SYSTEM, phar_ini_cache_list, cache_list, zend_phar_globals, phar_globals)
PHP_INI_END()203 PHP_INI_END()
204
205 /**
206 * When all uses of a phar have been concluded, this frees the manifest
207 * and the phar slot
208 */
209 void phar_destroy_phar_data(phar_archive_data *phar TSRMLS_DC) /* {{{ */
210 {
211 if (phar->alias && phar->alias != phar->fname) {
212 pefree(phar->alias, phar->is_persistent);
213 phar->alias = NULL;
214 }
215
216 if (phar->fname) {
217 pefree(phar->fname, phar->is_persistent);
218 phar->fname = NULL;
219 }
220
221 if (phar->signature) {
222 pefree(phar->signature, phar->is_persistent);
223 phar->signature = NULL;
224 }
225
226 if (phar->manifest.arBuckets) {
227 zend_hash_destroy(&phar->manifest);
228 phar->manifest.arBuckets = NULL;
229 }
230
231 if (phar->mounted_dirs.arBuckets) {
232 zend_hash_destroy(&phar->mounted_dirs);
233 phar->mounted_dirs.arBuckets = NULL;
234 }
235
236 if (phar->virtual_dirs.arBuckets) {
237 zend_hash_destroy(&phar->virtual_dirs);
238 phar->virtual_dirs.arBuckets = NULL;
239 }
240
241 if (phar->metadata) {
242 if (phar->is_persistent) {
243 if (phar->metadata_len) {
244 /* for zip comments that are strings */
245 free(phar->metadata);
246 } else {
247 zval_internal_ptr_dtor(&phar->metadata);
248 }
249 } else {
250 zval_ptr_dtor(&phar->metadata);
251 }
252 phar->metadata_len = 0;
253 phar->metadata = 0;
254 }
255
256 if (phar->fp) {
257 php_stream_close(phar->fp);
258 phar->fp = 0;
259 }
260
261 if (phar->ufp) {
262 php_stream_close(phar->ufp);
263 phar->ufp = 0;
264 }
265
266 pefree(phar, phar->is_persistent);
267 }
268 /* }}}*/
269
270 /**
271 * Delete refcount and destruct if needed. On destruct return 1 else 0.
272 */
phar_archive_delref(phar_archive_data * phar TSRMLS_DC)273 int phar_archive_delref(phar_archive_data *phar TSRMLS_DC) /* {{{ */
274 {
275 if (phar->is_persistent) {
276 return 0;
277 }
278
279 if (--phar->refcount < 0) {
280 if (PHAR_GLOBALS->request_done
281 || zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), phar->fname, phar->fname_len) != SUCCESS) {
282 phar_destroy_phar_data(phar TSRMLS_CC);
283 }
284 return 1;
285 } else if (!phar->refcount) {
286 /* invalidate phar cache */
287 PHAR_G(last_phar) = NULL;
288 PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
289
290 if (phar->fp && !(phar->flags & PHAR_FILE_COMPRESSION_MASK)) {
291 /* close open file handle - allows removal or rename of
292 the file on windows, which has greedy locking
293 only close if the archive was not already compressed. If it
294 was compressed, then the fp does not refer to the original file */
295 php_stream_close(phar->fp);
296 phar->fp = NULL;
297 }
298
299 if (!zend_hash_num_elements(&phar->manifest)) {
300 /* this is a new phar that has perhaps had an alias/metadata set, but has never
301 been flushed */
302 if (zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), phar->fname, phar->fname_len) != SUCCESS) {
303 phar_destroy_phar_data(phar TSRMLS_CC);
304 }
305 return 1;
306 }
307 }
308 return 0;
309 }
310 /* }}}*/
311
312 /**
313 * Destroy phar's in shutdown, here we don't care about aliases
314 */
destroy_phar_data_only(void * pDest)315 static void destroy_phar_data_only(void *pDest) /* {{{ */
316 {
317 phar_archive_data *phar_data = *(phar_archive_data **) pDest;
318 TSRMLS_FETCH();
319
320 if (EG(exception) || --phar_data->refcount < 0) {
321 phar_destroy_phar_data(phar_data TSRMLS_CC);
322 }
323 }
324 /* }}}*/
325
326 /**
327 * Delete aliases to phar's that got kicked out of the global table
328 */
phar_unalias_apply(void * pDest,void * argument TSRMLS_DC)329 static int phar_unalias_apply(void *pDest, void *argument TSRMLS_DC) /* {{{ */
330 {
331 return *(void**)pDest == argument ? ZEND_HASH_APPLY_REMOVE : ZEND_HASH_APPLY_KEEP;
332 }
333 /* }}} */
334
335 /**
336 * Delete aliases to phar's that got kicked out of the global table
337 */
phar_tmpclose_apply(void * pDest TSRMLS_DC)338 static int phar_tmpclose_apply(void *pDest TSRMLS_DC) /* {{{ */
339 {
340 phar_entry_info *entry = (phar_entry_info *) pDest;
341
342 if (entry->fp_type != PHAR_TMP) {
343 return ZEND_HASH_APPLY_KEEP;
344 }
345
346 if (entry->fp && !entry->fp_refcount) {
347 php_stream_close(entry->fp);
348 entry->fp = NULL;
349 }
350
351 return ZEND_HASH_APPLY_KEEP;
352 }
353 /* }}} */
354
355 /**
356 * Filename map destructor
357 */
destroy_phar_data(void * pDest)358 static void destroy_phar_data(void *pDest) /* {{{ */
359 {
360 phar_archive_data *phar_data = *(phar_archive_data **) pDest;
361 TSRMLS_FETCH();
362
363 if (PHAR_GLOBALS->request_ends) {
364 /* first, iterate over the manifest and close all PHAR_TMP entry fp handles,
365 this prevents unnecessary unfreed stream resources */
366 zend_hash_apply(&(phar_data->manifest), phar_tmpclose_apply TSRMLS_CC);
367 destroy_phar_data_only(pDest);
368 return;
369 }
370
371 zend_hash_apply_with_argument(&(PHAR_GLOBALS->phar_alias_map), phar_unalias_apply, phar_data TSRMLS_CC);
372
373 if (--phar_data->refcount < 0) {
374 phar_destroy_phar_data(phar_data TSRMLS_CC);
375 }
376 }
377 /* }}}*/
378
379 /**
380 * destructor for the manifest hash, frees each file's entry
381 */
destroy_phar_manifest_entry(void * pDest)382 void destroy_phar_manifest_entry(void *pDest) /* {{{ */
383 {
384 phar_entry_info *entry = (phar_entry_info *)pDest;
385 TSRMLS_FETCH();
386
387 if (entry->cfp) {
388 php_stream_close(entry->cfp);
389 entry->cfp = 0;
390 }
391
392 if (entry->fp) {
393 php_stream_close(entry->fp);
394 entry->fp = 0;
395 }
396
397 if (entry->metadata) {
398 if (entry->is_persistent) {
399 if (entry->metadata_len) {
400 /* for zip comments that are strings */
401 free(entry->metadata);
402 } else {
403 zval_internal_ptr_dtor(&entry->metadata);
404 }
405 } else {
406 zval_ptr_dtor(&entry->metadata);
407 }
408 entry->metadata_len = 0;
409 entry->metadata = 0;
410 }
411
412 if (entry->metadata_str.c) {
413 smart_str_free(&entry->metadata_str);
414 entry->metadata_str.c = 0;
415 }
416
417 pefree(entry->filename, entry->is_persistent);
418
419 if (entry->link) {
420 pefree(entry->link, entry->is_persistent);
421 entry->link = 0;
422 }
423
424 if (entry->tmp) {
425 pefree(entry->tmp, entry->is_persistent);
426 entry->tmp = 0;
427 }
428 }
429 /* }}} */
430
phar_entry_delref(phar_entry_data * idata TSRMLS_DC)431 int phar_entry_delref(phar_entry_data *idata TSRMLS_DC) /* {{{ */
432 {
433 int ret = 0;
434
435 if (idata->internal_file && !idata->internal_file->is_persistent) {
436 if (--idata->internal_file->fp_refcount < 0) {
437 idata->internal_file->fp_refcount = 0;
438 }
439
440 if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) {
441 php_stream_close(idata->fp);
442 }
443 /* if phar_get_or_create_entry_data returns a sub-directory, we have to free it */
444 if (idata->internal_file->is_temp_dir) {
445 destroy_phar_manifest_entry((void *)idata->internal_file);
446 efree(idata->internal_file);
447 }
448 }
449
450 phar_archive_delref(idata->phar TSRMLS_CC);
451 efree(idata);
452 return ret;
453 }
454 /* }}} */
455
456 /**
457 * Removes an entry, either by actually removing it or by marking it.
458 */
phar_entry_remove(phar_entry_data * idata,char ** error TSRMLS_DC)459 void phar_entry_remove(phar_entry_data *idata, char **error TSRMLS_DC) /* {{{ */
460 {
461 phar_archive_data *phar;
462
463 phar = idata->phar;
464
465 if (idata->internal_file->fp_refcount < 2) {
466 if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) {
467 php_stream_close(idata->fp);
468 }
469 zend_hash_del(&idata->phar->manifest, idata->internal_file->filename, idata->internal_file->filename_len);
470 idata->phar->refcount--;
471 efree(idata);
472 } else {
473 idata->internal_file->is_deleted = 1;
474 phar_entry_delref(idata TSRMLS_CC);
475 }
476
477 if (!phar->donotflush) {
478 phar_flush(phar, 0, 0, 0, error TSRMLS_CC);
479 }
480 }
481 /* }}} */
482
483 #define MAPPHAR_ALLOC_FAIL(msg) \
484 if (fp) {\
485 php_stream_close(fp);\
486 }\
487 if (error) {\
488 spprintf(error, 0, msg, fname);\
489 }\
490 return FAILURE;
491
492 #define MAPPHAR_FAIL(msg) \
493 efree(savebuf);\
494 if (mydata) {\
495 phar_destroy_phar_data(mydata TSRMLS_CC);\
496 }\
497 if (signature) {\
498 pefree(signature, PHAR_G(persist));\
499 }\
500 MAPPHAR_ALLOC_FAIL(msg)
501
502 #ifdef WORDS_BIGENDIAN
503 # define PHAR_GET_32(buffer, var) \
504 var = ((((unsigned char*)(buffer))[3]) << 24) \
505 | ((((unsigned char*)(buffer))[2]) << 16) \
506 | ((((unsigned char*)(buffer))[1]) << 8) \
507 | (((unsigned char*)(buffer))[0]); \
508 (buffer) += 4
509 # define PHAR_GET_16(buffer, var) \
510 var = ((((unsigned char*)(buffer))[1]) << 8) \
511 | (((unsigned char*)(buffer))[0]); \
512 (buffer) += 2
513 #else
514 # define PHAR_GET_32(buffer, var) \
515 memcpy(&var, buffer, sizeof(var)); \
516 buffer += 4
517 # define PHAR_GET_16(buffer, var) \
518 var = *(php_uint16*)(buffer); \
519 buffer += 2
520 #endif
521 #define PHAR_ZIP_16(var) ((php_uint16)((((php_uint16)var[0]) & 0xff) | \
522 (((php_uint16)var[1]) & 0xff) << 8))
523 #define PHAR_ZIP_32(var) ((php_uint32)((((php_uint32)var[0]) & 0xff) | \
524 (((php_uint32)var[1]) & 0xff) << 8 | \
525 (((php_uint32)var[2]) & 0xff) << 16 | \
526 (((php_uint32)var[3]) & 0xff) << 24))
527
528 /**
529 * Open an already loaded phar
530 */
phar_open_parsed_phar(char * fname,int fname_len,char * alias,int alias_len,int is_data,int options,phar_archive_data ** pphar,char ** error TSRMLS_DC)531 int phar_open_parsed_phar(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
532 {
533 phar_archive_data *phar;
534 #ifdef PHP_WIN32
535 char *unixfname;
536 #endif
537
538 if (error) {
539 *error = NULL;
540 }
541 #ifdef PHP_WIN32
542 unixfname = estrndup(fname, fname_len);
543 phar_unixify_path_separators(unixfname, fname_len);
544
545 if (SUCCESS == phar_get_archive(&phar, unixfname, fname_len, alias, alias_len, error TSRMLS_CC)
546 && ((alias && fname_len == phar->fname_len
547 && !strncmp(unixfname, phar->fname, fname_len)) || !alias)
548 ) {
549 phar_entry_info *stub;
550 efree(unixfname);
551 #else
552 if (SUCCESS == phar_get_archive(&phar, fname, fname_len, alias, alias_len, error TSRMLS_CC)
553 && ((alias && fname_len == phar->fname_len
554 && !strncmp(fname, phar->fname, fname_len)) || !alias)
555 ) {
556 phar_entry_info *stub;
557 #endif
558 /* logic above is as follows:
559 If an explicit alias was requested, ensure the filename passed in
560 matches the phar's filename.
561 If no alias was passed in, then it can match either and be valid
562 */
563
564 if (!is_data) {
565 /* prevent any ".phar" without a stub getting through */
566 if (!phar->halt_offset && !phar->is_brandnew && (phar->is_tar || phar->is_zip)) {
567 if (PHAR_G(readonly) && FAILURE == zend_hash_find(&(phar->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&stub)) {
568 if (error) {
569 spprintf(error, 0, "'%s' is not a phar archive. Use PharData::__construct() for a standard zip or tar archive", fname);
570 }
571 return FAILURE;
572 }
573 }
574 }
575
576 if (pphar) {
577 *pphar = phar;
578 }
579
580 return SUCCESS;
581 } else {
582 #ifdef PHP_WIN32
583 efree(unixfname);
584 #endif
585 if (pphar) {
586 *pphar = NULL;
587 }
588
589 if (phar && error && !(options & REPORT_ERRORS)) {
590 efree(error);
591 }
592
593 return FAILURE;
594 }
595 }
596 /* }}}*/
597
598 /**
599 * Parse out metadata from the manifest for a single file
600 *
601 * Meta-data is in this format:
602 * [len32][data...]
603 *
604 * data is the serialized zval
605 */
606 int phar_parse_metadata(char **buffer, zval **metadata, php_uint32 zip_metadata_len TSRMLS_DC) /* {{{ */
607 {
608 php_unserialize_data_t var_hash;
609
610 if (zip_metadata_len) {
611 const unsigned char *p, *p_buff = estrndup(*buffer, zip_metadata_len);
612 p = p_buff;
613 ALLOC_ZVAL(*metadata);
614 INIT_ZVAL(**metadata);
615 PHP_VAR_UNSERIALIZE_INIT(var_hash);
616
617 if (!php_var_unserialize(metadata, &p, p + zip_metadata_len, &var_hash TSRMLS_CC)) {
618 efree(p_buff);
619 PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
620 zval_ptr_dtor(metadata);
621 *metadata = NULL;
622 return FAILURE;
623 }
624 efree(p_buff);
625 PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
626
627 if (PHAR_G(persist)) {
628 /* lazy init metadata */
629 zval_ptr_dtor(metadata);
630 *metadata = (zval *) pemalloc(zip_metadata_len, 1);
631 memcpy(*metadata, *buffer, zip_metadata_len);
632 return SUCCESS;
633 }
634 } else {
635 *metadata = NULL;
636 }
637
638 return SUCCESS;
639 }
640 /* }}}*/
641
642 /**
643 * Does not check for a previously opened phar in the cache.
644 *
645 * Parse a new one and add it to the cache, returning either SUCCESS or
646 * FAILURE, and setting pphar to the pointer to the manifest entry
647 *
648 * This is used by phar_open_from_filename to process the manifest, but can be called
649 * directly.
650 */
651 static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, long halt_offset, phar_archive_data** pphar, php_uint32 compression, char **error TSRMLS_DC) /* {{{ */
652 {
653 char b32[4], *buffer, *endbuffer, *savebuf;
654 phar_archive_data *mydata = NULL;
655 phar_entry_info entry;
656 php_uint32 manifest_len, manifest_count, manifest_flags, manifest_index, tmp_len, sig_flags;
657 php_uint16 manifest_ver;
658 php_uint32 len;
659 long offset;
660 int sig_len, register_alias = 0, temp_alias = 0;
661 char *signature = NULL;
662
663 if (pphar) {
664 *pphar = NULL;
665 }
666
667 if (error) {
668 *error = NULL;
669 }
670
671 /* check for ?>\n and increment accordingly */
672 if (-1 == php_stream_seek(fp, halt_offset, SEEK_SET)) {
673 MAPPHAR_ALLOC_FAIL("cannot seek to __HALT_COMPILER(); location in phar \"%s\"")
674 }
675
676 buffer = b32;
677
678 if (3 != php_stream_read(fp, buffer, 3)) {
679 MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
680 }
681
682 if ((*buffer == ' ' || *buffer == '\n') && *(buffer + 1) == '?' && *(buffer + 2) == '>') {
683 int nextchar;
684 halt_offset += 3;
685 if (EOF == (nextchar = php_stream_getc(fp))) {
686 MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
687 }
688
689 if ((char) nextchar == '\r') {
690 /* if we have an \r we require an \n as well */
691 if (EOF == (nextchar = php_stream_getc(fp)) || (char)nextchar != '\n') {
692 MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
693 }
694 ++halt_offset;
695 }
696
697 if ((char) nextchar == '\n') {
698 ++halt_offset;
699 }
700 }
701
702 /* make sure we are at the right location to read the manifest */
703 if (-1 == php_stream_seek(fp, halt_offset, SEEK_SET)) {
704 MAPPHAR_ALLOC_FAIL("cannot seek to __HALT_COMPILER(); location in phar \"%s\"")
705 }
706
707 /* read in manifest */
708 buffer = b32;
709
710 if (4 != php_stream_read(fp, buffer, 4)) {
711 MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at manifest length)")
712 }
713
714 PHAR_GET_32(buffer, manifest_len);
715
716 if (manifest_len > 1048576 * 100) {
717 /* prevent serious memory issues by limiting manifest to at most 100 MB in length */
718 MAPPHAR_ALLOC_FAIL("manifest cannot be larger than 100 MB in phar \"%s\"")
719 }
720
721 buffer = (char *)emalloc(manifest_len);
722 savebuf = buffer;
723 endbuffer = buffer + manifest_len;
724
725 if (manifest_len < 10 || manifest_len != php_stream_read(fp, buffer, manifest_len)) {
726 MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)")
727 }
728
729 /* extract the number of entries */
730 PHAR_GET_32(buffer, manifest_count);
731
732 if (manifest_count == 0) {
733 MAPPHAR_FAIL("in phar \"%s\", manifest claims to have zero entries. Phars must have at least 1 entry");
734 }
735
736 /* extract API version, lowest nibble currently unused */
737 manifest_ver = (((unsigned char)buffer[0]) << 8)
738 + ((unsigned char)buffer[1]);
739 buffer += 2;
740
741 if ((manifest_ver & PHAR_API_VER_MASK) < PHAR_API_MIN_READ) {
742 efree(savebuf);
743 php_stream_close(fp);
744 if (error) {
745 spprintf(error, 0, "phar \"%s\" is API version %1.u.%1.u.%1.u, and cannot be processed", fname, manifest_ver >> 12, (manifest_ver >> 8) & 0xF, (manifest_ver >> 4) & 0x0F);
746 }
747 return FAILURE;
748 }
749
750 PHAR_GET_32(buffer, manifest_flags);
751
752 manifest_flags &= ~PHAR_HDR_COMPRESSION_MASK;
753 manifest_flags &= ~PHAR_FILE_COMPRESSION_MASK;
754 /* remember whether this entire phar was compressed with gz/bzip2 */
755 manifest_flags |= compression;
756
757 /* The lowest nibble contains the phar wide flags. The compression flags can */
758 /* be ignored on reading because it is being generated anyways. */
759 if (manifest_flags & PHAR_HDR_SIGNATURE) {
760 char sig_buf[8], *sig_ptr = sig_buf;
761 off_t read_len;
762 size_t end_of_phar;
763
764 if (-1 == php_stream_seek(fp, -8, SEEK_END)
765 || (read_len = php_stream_tell(fp)) < 20
766 || 8 != php_stream_read(fp, sig_buf, 8)
767 || memcmp(sig_buf+4, "GBMB", 4)) {
768 efree(savebuf);
769 php_stream_close(fp);
770 if (error) {
771 spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
772 }
773 return FAILURE;
774 }
775
776 PHAR_GET_32(sig_ptr, sig_flags);
777
778 switch(sig_flags) {
779 case PHAR_SIG_OPENSSL: {
780 php_uint32 signature_len;
781 char *sig;
782 off_t whence;
783
784 /* we store the signature followed by the signature length */
785 if (-1 == php_stream_seek(fp, -12, SEEK_CUR)
786 || 4 != php_stream_read(fp, sig_buf, 4)) {
787 efree(savebuf);
788 php_stream_close(fp);
789 if (error) {
790 spprintf(error, 0, "phar \"%s\" openssl signature length could not be read", fname);
791 }
792 return FAILURE;
793 }
794
795 sig_ptr = sig_buf;
796 PHAR_GET_32(sig_ptr, signature_len);
797 sig = (char *) emalloc(signature_len);
798 whence = signature_len + 4;
799 whence = -whence;
800
801 if (-1 == php_stream_seek(fp, whence, SEEK_CUR)
802 || !(end_of_phar = php_stream_tell(fp))
803 || signature_len != php_stream_read(fp, sig, signature_len)) {
804 efree(savebuf);
805 efree(sig);
806 php_stream_close(fp);
807 if (error) {
808 spprintf(error, 0, "phar \"%s\" openssl signature could not be read", fname);
809 }
810 return FAILURE;
811 }
812
813 if (FAILURE == phar_verify_signature(fp, end_of_phar, PHAR_SIG_OPENSSL, sig, signature_len, fname, &signature, &sig_len, error TSRMLS_CC)) {
814 efree(savebuf);
815 efree(sig);
816 php_stream_close(fp);
817 if (error) {
818 char *save = *error;
819 spprintf(error, 0, "phar \"%s\" openssl signature could not be verified: %s", fname, *error);
820 efree(save);
821 }
822 return FAILURE;
823 }
824 efree(sig);
825 }
826 break;
827 #if PHAR_HASH_OK
828 case PHAR_SIG_SHA512: {
829 unsigned char digest[64];
830
831 php_stream_seek(fp, -(8 + 64), SEEK_END);
832 read_len = php_stream_tell(fp);
833
834 if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
835 efree(savebuf);
836 php_stream_close(fp);
837 if (error) {
838 spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
839 }
840 return FAILURE;
841 }
842
843 if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA512, (char *)digest, 64, fname, &signature, &sig_len, error TSRMLS_CC)) {
844 efree(savebuf);
845 php_stream_close(fp);
846 if (error) {
847 char *save = *error;
848 spprintf(error, 0, "phar \"%s\" SHA512 signature could not be verified: %s", fname, *error);
849 efree(save);
850 }
851 return FAILURE;
852 }
853 break;
854 }
855 case PHAR_SIG_SHA256: {
856 unsigned char digest[32];
857
858 php_stream_seek(fp, -(8 + 32), SEEK_END);
859 read_len = php_stream_tell(fp);
860
861 if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
862 efree(savebuf);
863 php_stream_close(fp);
864 if (error) {
865 spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
866 }
867 return FAILURE;
868 }
869
870 if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA256, (char *)digest, 32, fname, &signature, &sig_len, error TSRMLS_CC)) {
871 efree(savebuf);
872 php_stream_close(fp);
873 if (error) {
874 char *save = *error;
875 spprintf(error, 0, "phar \"%s\" SHA256 signature could not be verified: %s", fname, *error);
876 efree(save);
877 }
878 return FAILURE;
879 }
880 break;
881 }
882 #else
883 case PHAR_SIG_SHA512:
884 case PHAR_SIG_SHA256:
885 efree(savebuf);
886 php_stream_close(fp);
887
888 if (error) {
889 spprintf(error, 0, "phar \"%s\" has a unsupported signature", fname);
890 }
891 return FAILURE;
892 #endif
893 case PHAR_SIG_SHA1: {
894 unsigned char digest[20];
895
896 php_stream_seek(fp, -(8 + 20), SEEK_END);
897 read_len = php_stream_tell(fp);
898
899 if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
900 efree(savebuf);
901 php_stream_close(fp);
902 if (error) {
903 spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
904 }
905 return FAILURE;
906 }
907
908 if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA1, (char *)digest, 20, fname, &signature, &sig_len, error TSRMLS_CC)) {
909 efree(savebuf);
910 php_stream_close(fp);
911 if (error) {
912 char *save = *error;
913 spprintf(error, 0, "phar \"%s\" SHA1 signature could not be verified: %s", fname, *error);
914 efree(save);
915 }
916 return FAILURE;
917 }
918 break;
919 }
920 case PHAR_SIG_MD5: {
921 unsigned char digest[16];
922
923 php_stream_seek(fp, -(8 + 16), SEEK_END);
924 read_len = php_stream_tell(fp);
925
926 if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
927 efree(savebuf);
928 php_stream_close(fp);
929 if (error) {
930 spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
931 }
932 return FAILURE;
933 }
934
935 if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_MD5, (char *)digest, 16, fname, &signature, &sig_len, error TSRMLS_CC)) {
936 efree(savebuf);
937 php_stream_close(fp);
938 if (error) {
939 char *save = *error;
940 spprintf(error, 0, "phar \"%s\" MD5 signature could not be verified: %s", fname, *error);
941 efree(save);
942 }
943 return FAILURE;
944 }
945 break;
946 }
947 default:
948 efree(savebuf);
949 php_stream_close(fp);
950
951 if (error) {
952 spprintf(error, 0, "phar \"%s\" has a broken or unsupported signature", fname);
953 }
954 return FAILURE;
955 }
956 } else if (PHAR_G(require_hash)) {
957 efree(savebuf);
958 php_stream_close(fp);
959
960 if (error) {
961 spprintf(error, 0, "phar \"%s\" does not have a signature", fname);
962 }
963 return FAILURE;
964 } else {
965 sig_flags = 0;
966 sig_len = 0;
967 }
968
969 /* extract alias */
970 PHAR_GET_32(buffer, tmp_len);
971
972 if (buffer + tmp_len > endbuffer) {
973 MAPPHAR_FAIL("internal corruption of phar \"%s\" (buffer overrun)");
974 }
975
976 if (manifest_len < 10 + tmp_len) {
977 MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)")
978 }
979
980 /* tmp_len = 0 says alias length is 0, which means the alias is not stored in the phar */
981 if (tmp_len) {
982 /* if the alias is stored we enforce it (implicit overrides explicit) */
983 if (alias && alias_len && (alias_len != (int)tmp_len || strncmp(alias, buffer, tmp_len)))
984 {
985 buffer[tmp_len] = '\0';
986 php_stream_close(fp);
987
988 if (signature) {
989 efree(signature);
990 }
991
992 if (error) {
993 spprintf(error, 0, "cannot load phar \"%s\" with implicit alias \"%s\" under different alias \"%s\"", fname, buffer, alias);
994 }
995
996 efree(savebuf);
997 return FAILURE;
998 }
999
1000 alias_len = tmp_len;
1001 alias = buffer;
1002 buffer += tmp_len;
1003 register_alias = 1;
1004 } else if (!alias_len || !alias) {
1005 /* if we neither have an explicit nor an implicit alias, we use the filename */
1006 alias = NULL;
1007 alias_len = 0;
1008 register_alias = 0;
1009 } else if (alias_len) {
1010 register_alias = 1;
1011 temp_alias = 1;
1012 }
1013
1014 /* we have 5 32-bit items plus 1 byte at least */
1015 if (manifest_count > ((manifest_len - 10 - tmp_len) / (5 * 4 + 1))) {
1016 /* prevent serious memory issues */
1017 MAPPHAR_FAIL("internal corruption of phar \"%s\" (too many manifest entries for size of manifest)")
1018 }
1019
1020 mydata = pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist));
1021 mydata->is_persistent = PHAR_G(persist);
1022
1023 /* check whether we have meta data, zero check works regardless of byte order */
1024 PHAR_GET_32(buffer, len);
1025 if (mydata->is_persistent) {
1026 mydata->metadata_len = len;
1027 if(!len) {
1028 /* FIXME: not sure why this is needed but removing it breaks tests */
1029 PHAR_GET_32(buffer, len);
1030 }
1031 }
1032 if(len > endbuffer - buffer) {
1033 MAPPHAR_FAIL("internal corruption of phar \"%s\" (trying to read past buffer end)");
1034 }
1035 if (phar_parse_metadata(&buffer, &mydata->metadata, len TSRMLS_CC) == FAILURE) {
1036 MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\"");
1037 }
1038 buffer += len;
1039
1040 /* set up our manifest */
1041 zend_hash_init(&mydata->manifest, manifest_count,
1042 zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)mydata->is_persistent);
1043 zend_hash_init(&mydata->mounted_dirs, 5,
1044 zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
1045 zend_hash_init(&mydata->virtual_dirs, manifest_count * 2,
1046 zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
1047 mydata->fname = pestrndup(fname, fname_len, mydata->is_persistent);
1048 #ifdef PHP_WIN32
1049 phar_unixify_path_separators(mydata->fname, fname_len);
1050 #endif
1051 mydata->fname_len = fname_len;
1052 offset = halt_offset + manifest_len + 4;
1053 memset(&entry, 0, sizeof(phar_entry_info));
1054 entry.phar = mydata;
1055 entry.fp_type = PHAR_FP;
1056 entry.is_persistent = mydata->is_persistent;
1057
1058 for (manifest_index = 0; manifest_index < manifest_count; ++manifest_index) {
1059 if (buffer + 4 > endbuffer) {
1060 MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)")
1061 }
1062
1063 PHAR_GET_32(buffer, entry.filename_len);
1064
1065 if (entry.filename_len == 0) {
1066 MAPPHAR_FAIL("zero-length filename encountered in phar \"%s\"");
1067 }
1068
1069 if (entry.is_persistent) {
1070 entry.manifest_pos = manifest_index;
1071 }
1072
1073 if (entry.filename_len + 20 > endbuffer - buffer) {
1074 MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)");
1075 }
1076
1077 if ((manifest_ver & PHAR_API_VER_MASK) >= PHAR_API_MIN_DIR && buffer[entry.filename_len - 1] == '/') {
1078 entry.is_dir = 1;
1079 } else {
1080 entry.is_dir = 0;
1081 }
1082
1083 phar_add_virtual_dirs(mydata, buffer, entry.filename_len TSRMLS_CC);
1084 entry.filename = pestrndup(buffer, entry.filename_len, entry.is_persistent);
1085 buffer += entry.filename_len;
1086 PHAR_GET_32(buffer, entry.uncompressed_filesize);
1087 PHAR_GET_32(buffer, entry.timestamp);
1088
1089 if (offset == halt_offset + (int)manifest_len + 4) {
1090 mydata->min_timestamp = entry.timestamp;
1091 mydata->max_timestamp = entry.timestamp;
1092 } else {
1093 if (mydata->min_timestamp > entry.timestamp) {
1094 mydata->min_timestamp = entry.timestamp;
1095 } else if (mydata->max_timestamp < entry.timestamp) {
1096 mydata->max_timestamp = entry.timestamp;
1097 }
1098 }
1099
1100 PHAR_GET_32(buffer, entry.compressed_filesize);
1101 PHAR_GET_32(buffer, entry.crc32);
1102 PHAR_GET_32(buffer, entry.flags);
1103
1104 if (entry.is_dir) {
1105 entry.filename_len--;
1106 entry.flags |= PHAR_ENT_PERM_DEF_DIR;
1107 }
1108
1109 PHAR_GET_32(buffer, len);
1110 if (entry.is_persistent) {
1111 entry.metadata_len = len;
1112 } else {
1113 entry.metadata_len = 0;
1114 }
1115 if (len > endbuffer - buffer) {
1116 pefree(entry.filename, entry.is_persistent);
1117 MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)");
1118 }
1119 if (phar_parse_metadata(&buffer, &entry.metadata, len TSRMLS_CC) == FAILURE) {
1120 pefree(entry.filename, entry.is_persistent);
1121 MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\"");
1122 }
1123 buffer += len;
1124
1125 entry.offset = entry.offset_abs = offset;
1126 offset += entry.compressed_filesize;
1127
1128 switch (entry.flags & PHAR_ENT_COMPRESSION_MASK) {
1129 case PHAR_ENT_COMPRESSED_GZ:
1130 if (!PHAR_G(has_zlib)) {
1131 if (entry.metadata) {
1132 if (entry.is_persistent) {
1133 free(entry.metadata);
1134 } else {
1135 zval_ptr_dtor(&entry.metadata);
1136 }
1137 }
1138 pefree(entry.filename, entry.is_persistent);
1139 MAPPHAR_FAIL("zlib extension is required for gz compressed .phar file \"%s\"");
1140 }
1141 break;
1142 case PHAR_ENT_COMPRESSED_BZ2:
1143 if (!PHAR_G(has_bz2)) {
1144 if (entry.metadata) {
1145 if (entry.is_persistent) {
1146 free(entry.metadata);
1147 } else {
1148 zval_ptr_dtor(&entry.metadata);
1149 }
1150 }
1151 pefree(entry.filename, entry.is_persistent);
1152 MAPPHAR_FAIL("bz2 extension is required for bzip2 compressed .phar file \"%s\"");
1153 }
1154 break;
1155 default:
1156 if (entry.uncompressed_filesize != entry.compressed_filesize) {
1157 if (entry.metadata) {
1158 if (entry.is_persistent) {
1159 free(entry.metadata);
1160 } else {
1161 zval_ptr_dtor(&entry.metadata);
1162 }
1163 }
1164 pefree(entry.filename, entry.is_persistent);
1165 MAPPHAR_FAIL("internal corruption of phar \"%s\" (compressed and uncompressed size does not match for uncompressed entry)");
1166 }
1167 break;
1168 }
1169
1170 manifest_flags |= (entry.flags & PHAR_ENT_COMPRESSION_MASK);
1171 /* if signature matched, no need to check CRC32 for each file */
1172 entry.is_crc_checked = (manifest_flags & PHAR_HDR_SIGNATURE ? 1 : 0);
1173 phar_set_inode(&entry TSRMLS_CC);
1174 zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL);
1175 }
1176
1177 snprintf(mydata->version, sizeof(mydata->version), "%u.%u.%u", manifest_ver >> 12, (manifest_ver >> 8) & 0xF, (manifest_ver >> 4) & 0xF);
1178 mydata->internal_file_start = halt_offset + manifest_len + 4;
1179 mydata->halt_offset = halt_offset;
1180 mydata->flags = manifest_flags;
1181 endbuffer = strrchr(mydata->fname, '/');
1182
1183 if (endbuffer) {
1184 mydata->ext = memchr(endbuffer, '.', (mydata->fname + fname_len) - endbuffer);
1185 if (mydata->ext == endbuffer) {
1186 mydata->ext = memchr(endbuffer + 1, '.', (mydata->fname + fname_len) - endbuffer - 1);
1187 }
1188 if (mydata->ext) {
1189 mydata->ext_len = (mydata->fname + mydata->fname_len) - mydata->ext;
1190 }
1191 }
1192
1193 mydata->alias = alias ?
1194 pestrndup(alias, alias_len, mydata->is_persistent) :
1195 pestrndup(mydata->fname, fname_len, mydata->is_persistent);
1196 mydata->alias_len = alias ? alias_len : fname_len;
1197 mydata->sig_flags = sig_flags;
1198 mydata->fp = fp;
1199 mydata->sig_len = sig_len;
1200 mydata->signature = signature;
1201 phar_request_initialize(TSRMLS_C);
1202
1203 if (register_alias) {
1204 phar_archive_data **fd_ptr;
1205
1206 mydata->is_temporary_alias = temp_alias;
1207
1208 if (!phar_validate_alias(mydata->alias, mydata->alias_len)) {
1209 signature = NULL;
1210 fp = NULL;
1211 MAPPHAR_FAIL("Cannot open archive \"%s\", invalid alias");
1212 }
1213
1214 if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) {
1215 if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) {
1216 signature = NULL;
1217 fp = NULL;
1218 MAPPHAR_FAIL("Cannot open archive \"%s\", alias is already in use by existing archive");
1219 }
1220 }
1221
1222 zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
1223 } else {
1224 mydata->is_temporary_alias = 1;
1225 }
1226
1227 zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
1228 efree(savebuf);
1229
1230 if (pphar) {
1231 *pphar = mydata;
1232 }
1233
1234 return SUCCESS;
1235 }
1236 /* }}} */
1237
1238 /**
1239 * Create or open a phar for writing
1240 */
1241 int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
1242 {
1243 const char *ext_str, *z;
1244 char *my_error;
1245 int ext_len;
1246 phar_archive_data **test, *unused = NULL;
1247
1248 test = &unused;
1249
1250 if (error) {
1251 *error = NULL;
1252 }
1253
1254 /* first try to open an existing file */
1255 if (phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, !is_data, 0, 1 TSRMLS_CC) == SUCCESS) {
1256 goto check_file;
1257 }
1258
1259 /* next try to create a new file */
1260 if (FAILURE == phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, !is_data, 1, 1 TSRMLS_CC)) {
1261 if (error) {
1262 if (ext_len == -2) {
1263 spprintf(error, 0, "Cannot create a phar archive from a URL like \"%s\". Phar objects can only be created from local files", fname);
1264 } else {
1265 spprintf(error, 0, "Cannot create phar '%s', file extension (or combination) not recognised or the directory does not exist", fname);
1266 }
1267 }
1268 return FAILURE;
1269 }
1270 check_file:
1271 if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, test, &my_error TSRMLS_CC) == SUCCESS) {
1272 if (pphar) {
1273 *pphar = *test;
1274 }
1275
1276 if ((*test)->is_data && !(*test)->is_tar && !(*test)->is_zip) {
1277 if (error) {
1278 spprintf(error, 0, "Cannot open '%s' as a PharData object. Use Phar::__construct() for executable archives", fname);
1279 }
1280 return FAILURE;
1281 }
1282
1283 if (PHAR_G(readonly) && !(*test)->is_data && ((*test)->is_tar || (*test)->is_zip)) {
1284 phar_entry_info *stub;
1285 if (FAILURE == zend_hash_find(&((*test)->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&stub)) {
1286 spprintf(error, 0, "'%s' is not a phar archive. Use PharData::__construct() for a standard zip or tar archive", fname);
1287 return FAILURE;
1288 }
1289 }
1290
1291 if (!PHAR_G(readonly) || (*test)->is_data) {
1292 (*test)->is_writeable = 1;
1293 }
1294 return SUCCESS;
1295 } else if (my_error) {
1296 if (error) {
1297 *error = my_error;
1298 } else {
1299 efree(my_error);
1300 }
1301 return FAILURE;
1302 }
1303
1304 if (ext_len > 3 && (z = memchr(ext_str, 'z', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ip", 2)) {
1305 /* assume zip-based phar */
1306 return phar_open_or_create_zip(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC);
1307 }
1308
1309 if (ext_len > 3 && (z = memchr(ext_str, 't', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ar", 2)) {
1310 /* assume tar-based phar */
1311 return phar_open_or_create_tar(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC);
1312 }
1313
1314 return phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC);
1315 }
1316 /* }}} */
1317
1318 int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
1319 {
1320 phar_archive_data *mydata;
1321 php_stream *fp;
1322 char *actual = NULL, *p;
1323
1324 if (!pphar) {
1325 pphar = &mydata;
1326 }
1327 #if PHP_API_VERSION < 20100412
1328 if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
1329 return FAILURE;
1330 }
1331 #endif
1332 if (php_check_open_basedir(fname TSRMLS_CC)) {
1333 return FAILURE;
1334 }
1335
1336 /* first open readonly so it won't be created if not present */
1337 fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, &actual);
1338
1339 if (actual) {
1340 fname = actual;
1341 fname_len = strlen(actual);
1342 }
1343
1344 if (fp) {
1345 if (phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error TSRMLS_CC) == SUCCESS) {
1346 if ((*pphar)->is_data || !PHAR_G(readonly)) {
1347 (*pphar)->is_writeable = 1;
1348 }
1349 if (actual) {
1350 efree(actual);
1351 }
1352 return SUCCESS;
1353 } else {
1354 /* file exists, but is either corrupt or not a phar archive */
1355 if (actual) {
1356 efree(actual);
1357 }
1358 return FAILURE;
1359 }
1360 }
1361
1362 if (actual) {
1363 efree(actual);
1364 }
1365
1366 if (PHAR_G(readonly) && !is_data) {
1367 if (options & REPORT_ERRORS) {
1368 if (error) {
1369 spprintf(error, 0, "creating archive \"%s\" disabled by the php.ini setting phar.readonly", fname);
1370 }
1371 }
1372 return FAILURE;
1373 }
1374
1375 /* set up our manifest */
1376 mydata = ecalloc(1, sizeof(phar_archive_data));
1377 mydata->fname = expand_filepath(fname, NULL TSRMLS_CC);
1378 fname_len = strlen(mydata->fname);
1379 #ifdef PHP_WIN32
1380 phar_unixify_path_separators(mydata->fname, fname_len);
1381 #endif
1382 p = strrchr(mydata->fname, '/');
1383
1384 if (p) {
1385 mydata->ext = memchr(p, '.', (mydata->fname + fname_len) - p);
1386 if (mydata->ext == p) {
1387 mydata->ext = memchr(p + 1, '.', (mydata->fname + fname_len) - p - 1);
1388 }
1389 if (mydata->ext) {
1390 mydata->ext_len = (mydata->fname + fname_len) - mydata->ext;
1391 }
1392 }
1393
1394 if (pphar) {
1395 *pphar = mydata;
1396 }
1397
1398 zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
1399 zend_get_hash_value, destroy_phar_manifest_entry, 0);
1400 zend_hash_init(&mydata->mounted_dirs, sizeof(char *),
1401 zend_get_hash_value, NULL, 0);
1402 zend_hash_init(&mydata->virtual_dirs, sizeof(char *),
1403 zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
1404 mydata->fname_len = fname_len;
1405 snprintf(mydata->version, sizeof(mydata->version), "%s", PHP_PHAR_API_VERSION);
1406 mydata->is_temporary_alias = alias ? 0 : 1;
1407 mydata->internal_file_start = -1;
1408 mydata->fp = NULL;
1409 mydata->is_writeable = 1;
1410 mydata->is_brandnew = 1;
1411 phar_request_initialize(TSRMLS_C);
1412 zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
1413
1414 if (is_data) {
1415 alias = NULL;
1416 alias_len = 0;
1417 mydata->is_data = 1;
1418 /* assume tar format, PharData can specify other */
1419 mydata->is_tar = 1;
1420 } else {
1421 phar_archive_data **fd_ptr;
1422
1423 if (alias && SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) {
1424 if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) {
1425 if (error) {
1426 spprintf(error, 4096, "phar error: phar \"%s\" cannot set alias \"%s\", already in use by another phar archive", mydata->fname, alias);
1427 }
1428
1429 zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len);
1430
1431 if (pphar) {
1432 *pphar = NULL;
1433 }
1434
1435 return FAILURE;
1436 }
1437 }
1438
1439 mydata->alias = alias ? estrndup(alias, alias_len) : estrndup(mydata->fname, fname_len);
1440 mydata->alias_len = alias ? alias_len : fname_len;
1441 }
1442
1443 if (alias_len && alias) {
1444 if (FAILURE == zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL)) {
1445 if (options & REPORT_ERRORS) {
1446 if (error) {
1447 spprintf(error, 0, "archive \"%s\" cannot be associated with alias \"%s\", already in use", fname, alias);
1448 }
1449 }
1450
1451 zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len);
1452
1453 if (pphar) {
1454 *pphar = NULL;
1455 }
1456
1457 return FAILURE;
1458 }
1459 }
1460
1461 return SUCCESS;
1462 }
1463 /* }}}*/
1464
1465 /**
1466 * Return an already opened filename.
1467 *
1468 * Or scan a phar file for the required __HALT_COMPILER(); ?> token and verify
1469 * that the manifest is proper, then pass it to phar_parse_pharfile(). SUCCESS
1470 * or FAILURE is returned and pphar is set to a pointer to the phar's manifest
1471 */
1472 int phar_open_from_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
1473 {
1474 php_stream *fp;
1475 char *actual;
1476 int ret, is_data = 0;
1477
1478 if (error) {
1479 *error = NULL;
1480 }
1481
1482 if (!strstr(fname, ".phar")) {
1483 is_data = 1;
1484 }
1485
1486 if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC) == SUCCESS) {
1487 return SUCCESS;
1488 } else if (error && *error) {
1489 return FAILURE;
1490 }
1491 #if PHP_API_VERSION < 20100412
1492 if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
1493 return FAILURE;
1494 }
1495 #endif
1496 if (php_check_open_basedir(fname TSRMLS_CC)) {
1497 return FAILURE;
1498 }
1499
1500 fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, &actual);
1501
1502 if (!fp) {
1503 if (options & REPORT_ERRORS) {
1504 if (error) {
1505 spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
1506 }
1507 }
1508 if (actual) {
1509 efree(actual);
1510 }
1511 return FAILURE;
1512 }
1513
1514 if (actual) {
1515 fname = actual;
1516 fname_len = strlen(actual);
1517 }
1518
1519 ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error TSRMLS_CC);
1520
1521 if (actual) {
1522 efree(actual);
1523 }
1524
1525 return ret;
1526 }
1527 /* }}}*/
1528
1529 static inline char *phar_strnstr(const char *buf, int buf_len, const char *search, int search_len) /* {{{ */
1530 {
1531 const char *c;
1532 int so_far = 0;
1533
1534 if (buf_len < search_len) {
1535 return NULL;
1536 }
1537
1538 c = buf - 1;
1539
1540 do {
1541 if (!(c = memchr(c + 1, search[0], buf_len - search_len - so_far))) {
1542 return (char *) NULL;
1543 }
1544
1545 so_far = c - buf;
1546
1547 if (so_far >= (buf_len - search_len)) {
1548 return (char *) NULL;
1549 }
1550
1551 if (!memcmp(c, search, search_len)) {
1552 return (char *) c;
1553 }
1554 } while (1);
1555 }
1556 /* }}} */
1557
1558 /**
1559 * Scan an open fp for the required __HALT_COMPILER(); ?> token and verify
1560 * that the manifest is proper, then pass it to phar_parse_pharfile(). SUCCESS
1561 * or FAILURE is returned and pphar is set to a pointer to the phar's manifest
1562 */
1563 static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, int is_data, char **error TSRMLS_DC) /* {{{ */
1564 {
1565 const char token[] = "__HALT_COMPILER();";
1566 const char zip_magic[] = "PK\x03\x04";
1567 const char gz_magic[] = "\x1f\x8b\x08";
1568 const char bz_magic[] = "BZh";
1569 char *pos, test = '\0';
1570 const int window_size = 1024;
1571 char buffer[1024 + sizeof(token)]; /* a 1024 byte window + the size of the halt_compiler token (moving window) */
1572 const long readsize = sizeof(buffer) - sizeof(token);
1573 const long tokenlen = sizeof(token) - 1;
1574 long halt_offset;
1575 size_t got;
1576 php_uint32 compression = PHAR_FILE_COMPRESSED_NONE;
1577
1578 if (error) {
1579 *error = NULL;
1580 }
1581
1582 if (-1 == php_stream_rewind(fp)) {
1583 MAPPHAR_ALLOC_FAIL("cannot rewind phar \"%s\"")
1584 }
1585
1586 buffer[sizeof(buffer)-1] = '\0';
1587 memset(buffer, 32, sizeof(token));
1588 halt_offset = 0;
1589
1590 /* Maybe it's better to compile the file instead of just searching, */
1591 /* but we only want the offset. So we want a .re scanner to find it. */
1592 while(!php_stream_eof(fp)) {
1593 if ((got = php_stream_read(fp, buffer+tokenlen, readsize)) < (size_t) tokenlen) {
1594 MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)")
1595 }
1596
1597 if (!test) {
1598 test = '\1';
1599 pos = buffer+tokenlen;
1600 if (!memcmp(pos, gz_magic, 3)) {
1601 char err = 0;
1602 php_stream_filter *filter;
1603 php_stream *temp;
1604 /* to properly decompress, we have to tell zlib to look for a zlib or gzip header */
1605 zval filterparams;
1606
1607 if (!PHAR_G(has_zlib)) {
1608 MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file, enable zlib extension in php.ini")
1609 }
1610 array_init(&filterparams);
1611 /* this is defined in zlib's zconf.h */
1612 #ifndef MAX_WBITS
1613 #define MAX_WBITS 15
1614 #endif
1615 add_assoc_long(&filterparams, "window", MAX_WBITS + 32);
1616
1617 /* entire file is gzip-compressed, uncompress to temporary file */
1618 if (!(temp = php_stream_fopen_tmpfile())) {
1619 MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of gzipped phar archive \"%s\"")
1620 }
1621
1622 php_stream_rewind(fp);
1623 filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC);
1624
1625 if (!filter) {
1626 err = 1;
1627 add_assoc_long(&filterparams, "window", MAX_WBITS);
1628 filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC);
1629 zval_dtor(&filterparams);
1630
1631 if (!filter) {
1632 php_stream_close(temp);
1633 MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6")
1634 }
1635 } else {
1636 zval_dtor(&filterparams);
1637 }
1638
1639 php_stream_filter_append(&temp->writefilters, filter);
1640
1641 if (SUCCESS != phar_stream_copy_to_stream(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
1642 if (err) {
1643 php_stream_close(temp);
1644 MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6")
1645 }
1646 php_stream_close(temp);
1647 MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file")
1648 }
1649
1650 php_stream_filter_flush(filter, 1);
1651 php_stream_filter_remove(filter, 1 TSRMLS_CC);
1652 php_stream_close(fp);
1653 fp = temp;
1654 php_stream_rewind(fp);
1655 compression = PHAR_FILE_COMPRESSED_GZ;
1656
1657 /* now, start over */
1658 test = '\0';
1659 continue;
1660 } else if (!memcmp(pos, bz_magic, 3)) {
1661 php_stream_filter *filter;
1662 php_stream *temp;
1663
1664 if (!PHAR_G(has_bz2)) {
1665 MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file, enable bz2 extension in php.ini")
1666 }
1667
1668 /* entire file is bzip-compressed, uncompress to temporary file */
1669 if (!(temp = php_stream_fopen_tmpfile())) {
1670 MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of bzipped phar archive \"%s\"")
1671 }
1672
1673 php_stream_rewind(fp);
1674 filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp) TSRMLS_CC);
1675
1676 if (!filter) {
1677 php_stream_close(temp);
1678 MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\", filter creation failed")
1679 }
1680
1681 php_stream_filter_append(&temp->writefilters, filter);
1682
1683 if (SUCCESS != phar_stream_copy_to_stream(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
1684 php_stream_close(temp);
1685 MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file")
1686 }
1687
1688 php_stream_filter_flush(filter, 1);
1689 php_stream_filter_remove(filter, 1 TSRMLS_CC);
1690 php_stream_close(fp);
1691 fp = temp;
1692 php_stream_rewind(fp);
1693 compression = PHAR_FILE_COMPRESSED_BZ2;
1694
1695 /* now, start over */
1696 test = '\0';
1697 continue;
1698 }
1699
1700 if (!memcmp(pos, zip_magic, 4)) {
1701 php_stream_seek(fp, 0, SEEK_END);
1702 return phar_parse_zipfile(fp, fname, fname_len, alias, alias_len, pphar, error TSRMLS_CC);
1703 }
1704
1705 if (got > 512) {
1706 if (phar_is_tar(pos, fname)) {
1707 php_stream_rewind(fp);
1708 return phar_parse_tarfile(fp, fname, fname_len, alias, alias_len, pphar, is_data, compression, error TSRMLS_CC);
1709 }
1710 }
1711 }
1712
1713 if (got > 0 && (pos = phar_strnstr(buffer, got + sizeof(token), token, sizeof(token)-1)) != NULL) {
1714 halt_offset += (pos - buffer); /* no -tokenlen+tokenlen here */
1715 return phar_parse_pharfile(fp, fname, fname_len, alias, alias_len, halt_offset, pphar, compression, error TSRMLS_CC);
1716 }
1717
1718 halt_offset += got;
1719 memmove(buffer, buffer + window_size, tokenlen); /* move the memory buffer by the size of the window */
1720 }
1721
1722 MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (__HALT_COMPILER(); not found)")
1723 }
1724 /* }}} */
1725
1726 /*
1727 * given the location of the file extension and the start of the file path,
1728 * determine the end of the portion of the path (i.e. /path/to/file.ext/blah
1729 * grabs "/path/to/file.ext" as does the straight /path/to/file.ext),
1730 * stat it to determine if it exists.
1731 * if so, check to see if it is a directory and fail if so
1732 * if not, check to see if its dirname() exists (i.e. "/path/to") and is a directory
1733 * succeed if we are creating the file, otherwise fail.
1734 */
1735 static int phar_analyze_path(const char *fname, const char *ext, int ext_len, int for_create TSRMLS_DC) /* {{{ */
1736 {
1737 php_stream_statbuf ssb;
1738 char *realpath;
1739 char *filename = estrndup(fname, (ext - fname) + ext_len);
1740
1741 if ((realpath = expand_filepath(filename, NULL TSRMLS_CC))) {
1742 #ifdef PHP_WIN32
1743 phar_unixify_path_separators(realpath, strlen(realpath));
1744 #endif
1745 if (zend_hash_exists(&(PHAR_GLOBALS->phar_fname_map), realpath, strlen(realpath))) {
1746 efree(realpath);
1747 efree(filename);
1748 return SUCCESS;
1749 }
1750
1751 if (PHAR_G(manifest_cached) && zend_hash_exists(&cached_phars, realpath, strlen(realpath))) {
1752 efree(realpath);
1753 efree(filename);
1754 return SUCCESS;
1755 }
1756 efree(realpath);
1757 }
1758
1759 if (SUCCESS == php_stream_stat_path((char *) filename, &ssb)) {
1760
1761 efree(filename);
1762
1763 if (ssb.sb.st_mode & S_IFDIR) {
1764 return FAILURE;
1765 }
1766
1767 if (for_create == 1) {
1768 return FAILURE;
1769 }
1770
1771 return SUCCESS;
1772 } else {
1773 char *slash;
1774
1775 if (!for_create) {
1776 efree(filename);
1777 return FAILURE;
1778 }
1779
1780 slash = (char *) strrchr(filename, '/');
1781
1782 if (slash) {
1783 *slash = '\0';
1784 }
1785
1786 if (SUCCESS != php_stream_stat_path((char *) filename, &ssb)) {
1787 if (!slash) {
1788 if (!(realpath = expand_filepath(filename, NULL TSRMLS_CC))) {
1789 efree(filename);
1790 return FAILURE;
1791 }
1792 #ifdef PHP_WIN32
1793 phar_unixify_path_separators(realpath, strlen(realpath));
1794 #endif
1795 slash = strstr(realpath, filename) + ((ext - fname) + ext_len);
1796 *slash = '\0';
1797 slash = strrchr(realpath, '/');
1798
1799 if (slash) {
1800 *slash = '\0';
1801 } else {
1802 efree(realpath);
1803 efree(filename);
1804 return FAILURE;
1805 }
1806
1807 if (SUCCESS != php_stream_stat_path(realpath, &ssb)) {
1808 efree(realpath);
1809 efree(filename);
1810 return FAILURE;
1811 }
1812
1813 efree(realpath);
1814
1815 if (ssb.sb.st_mode & S_IFDIR) {
1816 efree(filename);
1817 return SUCCESS;
1818 }
1819 }
1820
1821 efree(filename);
1822 return FAILURE;
1823 }
1824
1825 efree(filename);
1826
1827 if (ssb.sb.st_mode & S_IFDIR) {
1828 return SUCCESS;
1829 }
1830
1831 return FAILURE;
1832 }
1833 }
1834 /* }}} */
1835
1836 /* check for ".phar" in extension */
1837 static int phar_check_str(const char *fname, const char *ext_str, int ext_len, int executable, int for_create TSRMLS_DC) /* {{{ */
1838 {
1839 char test[51];
1840 const char *pos;
1841
1842 if (ext_len >= 50) {
1843 return FAILURE;
1844 }
1845
1846 if (executable == 1) {
1847 /* copy "." as well */
1848 memcpy(test, ext_str - 1, ext_len + 1);
1849 test[ext_len + 1] = '\0';
1850 /* executable phars must contain ".phar" as a valid extension (phar://.pharmy/oops is invalid) */
1851 /* (phar://hi/there/.phar/oops is also invalid) */
1852 pos = strstr(test, ".phar");
1853
1854 if (pos && (*(pos - 1) != '/')
1855 && (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) {
1856 return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC);
1857 } else {
1858 return FAILURE;
1859 }
1860 }
1861
1862 /* data phars need only contain a single non-"." to be valid */
1863 if (!executable) {
1864 pos = strstr(ext_str, ".phar");
1865 if (!(pos && (*(pos - 1) != '/')
1866 && (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) && *(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
1867 return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC);
1868 }
1869 } else {
1870 if (*(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
1871 return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC);
1872 }
1873 }
1874
1875 return FAILURE;
1876 }
1877 /* }}} */
1878
1879 /*
1880 * if executable is 1, only returns SUCCESS if the extension is one of the tar/zip .phar extensions
1881 * if executable is 0, it returns SUCCESS only if the filename does *not* contain ".phar" anywhere, and treats
1882 * the first extension as the filename extension
1883 *
1884 * if an extension is found, it sets ext_str to the location of the file extension in filename,
1885 * and ext_len to the length of the extension.
1886 * for urls like "phar://alias/oops" it instead sets ext_len to -1 and returns FAILURE, which tells
1887 * the calling function to use "alias" as the phar alias
1888 *
1889 * the last parameter should be set to tell the thing to assume that filename is the full path, and only to check the
1890 * extension rules, not to iterate.
1891 */
1892 int phar_detect_phar_fname_ext(const char *filename, int filename_len, const char **ext_str, int *ext_len, int executable, int for_create, int is_complete TSRMLS_DC) /* {{{ */
1893 {
1894 const char *pos, *slash;
1895
1896 *ext_str = NULL;
1897 *ext_len = 0;
1898
1899 if (!filename_len || filename_len == 1) {
1900 return FAILURE;
1901 }
1902
1903 phar_request_initialize(TSRMLS_C);
1904 /* first check for alias in first segment */
1905 pos = memchr(filename, '/', filename_len);
1906
1907 if (pos && pos != filename) {
1908 /* check for url like http:// or phar:// */
1909 if (*(pos - 1) == ':' && (pos - filename) < filename_len - 1 && *(pos + 1) == '/') {
1910 *ext_len = -2;
1911 *ext_str = NULL;
1912 return FAILURE;
1913 }
1914 if (zend_hash_exists(&(PHAR_GLOBALS->phar_alias_map), (char *) filename, pos - filename)) {
1915 *ext_str = pos;
1916 *ext_len = -1;
1917 return FAILURE;
1918 }
1919
1920 if (PHAR_G(manifest_cached) && zend_hash_exists(&cached_alias, (char *) filename, pos - filename)) {
1921 *ext_str = pos;
1922 *ext_len = -1;
1923 return FAILURE;
1924 }
1925 }
1926
1927 if (zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map)) || PHAR_G(manifest_cached)) {
1928 phar_archive_data **pphar;
1929
1930 if (is_complete) {
1931 if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), (char *) filename, filename_len, (void **)&pphar)) {
1932 *ext_str = filename + (filename_len - (*pphar)->ext_len);
1933 woohoo:
1934 *ext_len = (*pphar)->ext_len;
1935
1936 if (executable == 2) {
1937 return SUCCESS;
1938 }
1939
1940 if (executable == 1 && !(*pphar)->is_data) {
1941 return SUCCESS;
1942 }
1943
1944 if (!executable && (*pphar)->is_data) {
1945 return SUCCESS;
1946 }
1947
1948 return FAILURE;
1949 }
1950
1951 if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_find(&cached_phars, (char *) filename, filename_len, (void **)&pphar)) {
1952 *ext_str = filename + (filename_len - (*pphar)->ext_len);
1953 goto woohoo;
1954 }
1955 } else {
1956 phar_zstr key;
1957 char *str_key;
1958 uint keylen;
1959 ulong unused;
1960
1961 zend_hash_internal_pointer_reset(&(PHAR_GLOBALS->phar_fname_map));
1962
1963 while (FAILURE != zend_hash_has_more_elements(&(PHAR_GLOBALS->phar_fname_map))) {
1964 if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(&(PHAR_GLOBALS->phar_fname_map), &key, &keylen, &unused, 0, NULL)) {
1965 break;
1966 }
1967
1968 PHAR_STR(key, str_key);
1969
1970 if (keylen > (uint) filename_len) {
1971 zend_hash_move_forward(&(PHAR_GLOBALS->phar_fname_map));
1972 PHAR_STR_FREE(str_key);
1973 continue;
1974 }
1975
1976 if (!memcmp(filename, str_key, keylen) && ((uint)filename_len == keylen
1977 || filename[keylen] == '/' || filename[keylen] == '\0')) {
1978 PHAR_STR_FREE(str_key);
1979 if (FAILURE == zend_hash_get_current_data(&(PHAR_GLOBALS->phar_fname_map), (void **) &pphar)) {
1980 break;
1981 }
1982 *ext_str = filename + (keylen - (*pphar)->ext_len);
1983 goto woohoo;
1984 }
1985
1986 PHAR_STR_FREE(str_key);
1987 zend_hash_move_forward(&(PHAR_GLOBALS->phar_fname_map));
1988 }
1989
1990 if (PHAR_G(manifest_cached)) {
1991 zend_hash_internal_pointer_reset(&cached_phars);
1992
1993 while (FAILURE != zend_hash_has_more_elements(&cached_phars)) {
1994 if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(&cached_phars, &key, &keylen, &unused, 0, NULL)) {
1995 break;
1996 }
1997
1998 PHAR_STR(key, str_key);
1999
2000 if (keylen > (uint) filename_len) {
2001 zend_hash_move_forward(&cached_phars);
2002 PHAR_STR_FREE(str_key);
2003 continue;
2004 }
2005
2006 if (!memcmp(filename, str_key, keylen) && ((uint)filename_len == keylen
2007 || filename[keylen] == '/' || filename[keylen] == '\0')) {
2008 PHAR_STR_FREE(str_key);
2009 if (FAILURE == zend_hash_get_current_data(&cached_phars, (void **) &pphar)) {
2010 break;
2011 }
2012 *ext_str = filename + (keylen - (*pphar)->ext_len);
2013 goto woohoo;
2014 }
2015 PHAR_STR_FREE(str_key);
2016 zend_hash_move_forward(&cached_phars);
2017 }
2018 }
2019 }
2020 }
2021
2022 pos = memchr(filename + 1, '.', filename_len);
2023 next_extension:
2024 if (!pos) {
2025 return FAILURE;
2026 }
2027
2028 while (pos != filename && (*(pos - 1) == '/' || *(pos - 1) == '\0')) {
2029 pos = memchr(pos + 1, '.', filename_len - (pos - filename) + 1);
2030 if (!pos) {
2031 return FAILURE;
2032 }
2033 }
2034
2035 slash = memchr(pos, '/', filename_len - (pos - filename));
2036
2037 if (!slash) {
2038 /* this is a url like "phar://blah.phar" with no directory */
2039 *ext_str = pos;
2040 *ext_len = strlen(pos);
2041
2042 /* file extension must contain "phar" */
2043 switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create TSRMLS_CC)) {
2044 case SUCCESS:
2045 return SUCCESS;
2046 case FAILURE:
2047 /* we are at the end of the string, so we fail */
2048 return FAILURE;
2049 }
2050 }
2051
2052 /* we've found an extension that ends at a directory separator */
2053 *ext_str = pos;
2054 *ext_len = slash - pos;
2055
2056 switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create TSRMLS_CC)) {
2057 case SUCCESS:
2058 return SUCCESS;
2059 case FAILURE:
2060 /* look for more extensions */
2061 pos = strchr(pos + 1, '.');
2062 if (pos) {
2063 *ext_str = NULL;
2064 *ext_len = 0;
2065 }
2066 goto next_extension;
2067 }
2068
2069 return FAILURE;
2070 }
2071 /* }}} */
2072
2073 static int php_check_dots(const char *element, int n) /* {{{ */
2074 {
2075 for(n--; n >= 0; --n) {
2076 if (element[n] != '.') {
2077 return 1;
2078 }
2079 }
2080 return 0;
2081 }
2082 /* }}} */
2083
2084 #define IS_DIRECTORY_UP(element, len) \
2085 (len >= 2 && !php_check_dots(element, len))
2086
2087 #define IS_DIRECTORY_CURRENT(element, len) \
2088 (len == 1 && element[0] == '.')
2089
2090 #define IS_BACKSLASH(c) ((c) == '/')
2091
2092 #ifdef COMPILE_DL_PHAR
2093 /* stupid-ass non-extern declaration in tsrm_strtok.h breaks dumbass MS compiler */
2094 static inline int in_character_class(char ch, const char *delim) /* {{{ */
2095 {
2096 while (*delim) {
2097 if (*delim == ch) {
2098 return 1;
2099 }
2100 ++delim;
2101 }
2102 return 0;
2103 }
2104 /* }}} */
2105
2106 char *tsrm_strtok_r(char *s, const char *delim, char **last) /* {{{ */
2107 {
2108 char *token;
2109
2110 if (s == NULL) {
2111 s = *last;
2112 }
2113
2114 while (*s && in_character_class(*s, delim)) {
2115 ++s;
2116 }
2117
2118 if (!*s) {
2119 return NULL;
2120 }
2121
2122 token = s;
2123
2124 while (*s && !in_character_class(*s, delim)) {
2125 ++s;
2126 }
2127
2128 if (!*s) {
2129 *last = s;
2130 } else {
2131 *s = '\0';
2132 *last = s + 1;
2133 }
2134
2135 return token;
2136 }
2137 /* }}} */
2138 #endif
2139
2140 /**
2141 * Remove .. and . references within a phar filename
2142 */
2143 char *phar_fix_filepath(char *path, int *new_len, int use_cwd TSRMLS_DC) /* {{{ */
2144 {
2145 char *newpath;
2146 int newpath_len;
2147 char *ptr;
2148 char *tok;
2149 int ptr_length, path_length = *new_len;
2150
2151 if (PHAR_G(cwd_len) && use_cwd && path_length > 2 && path[0] == '.' && path[1] == '/') {
2152 newpath_len = PHAR_G(cwd_len);
2153 newpath = emalloc(strlen(path) + newpath_len + 1);
2154 memcpy(newpath, PHAR_G(cwd), newpath_len);
2155 } else {
2156 newpath = emalloc(strlen(path) + 2);
2157 newpath[0] = '/';
2158 newpath_len = 1;
2159 }
2160
2161 ptr = path;
2162
2163 if (*ptr == '/') {
2164 ++ptr;
2165 }
2166
2167 tok = ptr;
2168
2169 do {
2170 ptr = memchr(ptr, '/', path_length - (ptr - path));
2171 } while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
2172
2173 if (!ptr && (path_length - (tok - path))) {
2174 switch (path_length - (tok - path)) {
2175 case 1:
2176 if (*tok == '.') {
2177 efree(path);
2178 *new_len = 1;
2179 efree(newpath);
2180 return estrndup("/", 1);
2181 }
2182 break;
2183 case 2:
2184 if (tok[0] == '.' && tok[1] == '.') {
2185 efree(path);
2186 *new_len = 1;
2187 efree(newpath);
2188 return estrndup("/", 1);
2189 }
2190 }
2191 efree(newpath);
2192 return path;
2193 }
2194
2195 while (ptr) {
2196 ptr_length = ptr - tok;
2197 last_time:
2198 if (IS_DIRECTORY_UP(tok, ptr_length)) {
2199 #define PREVIOUS newpath[newpath_len - 1]
2200
2201 while (newpath_len > 1 && !IS_BACKSLASH(PREVIOUS)) {
2202 newpath_len--;
2203 }
2204
2205 if (newpath[0] != '/') {
2206 newpath[newpath_len] = '\0';
2207 } else if (newpath_len > 1) {
2208 --newpath_len;
2209 }
2210 } else if (!IS_DIRECTORY_CURRENT(tok, ptr_length)) {
2211 if (newpath_len > 1) {
2212 newpath[newpath_len++] = '/';
2213 memcpy(newpath + newpath_len, tok, ptr_length+1);
2214 } else {
2215 memcpy(newpath + newpath_len, tok, ptr_length+1);
2216 }
2217
2218 newpath_len += ptr_length;
2219 }
2220
2221 if (ptr == path + path_length) {
2222 break;
2223 }
2224
2225 tok = ++ptr;
2226
2227 do {
2228 ptr = memchr(ptr, '/', path_length - (ptr - path));
2229 } while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
2230
2231 if (!ptr && (path_length - (tok - path))) {
2232 ptr_length = path_length - (tok - path);
2233 ptr = path + path_length;
2234 goto last_time;
2235 }
2236 }
2237
2238 efree(path);
2239 *new_len = newpath_len;
2240 newpath[newpath_len] = '\0';
2241 return erealloc(newpath, newpath_len + 1);
2242 }
2243 /* }}} */
2244
2245 /**
2246 * Process a phar stream name, ensuring we can handle any of:
2247 *
2248 * - whatever.phar
2249 * - whatever.phar.gz
2250 * - whatever.phar.bz2
2251 * - whatever.phar.php
2252 *
2253 * Optionally the name might start with 'phar://'
2254 *
2255 * This is used by phar_parse_url()
2256 */
2257 int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_len, char **entry, int *entry_len, int executable, int for_create TSRMLS_DC) /* {{{ */
2258 {
2259 const char *ext_str;
2260 #ifdef PHP_WIN32
2261 char *save;
2262 #endif
2263 int ext_len, free_filename = 0;
2264
2265 if (!strncasecmp(filename, "phar://", 7)) {
2266 filename += 7;
2267 filename_len -= 7;
2268 }
2269
2270 ext_len = 0;
2271 #ifdef PHP_WIN32
2272 free_filename = 1;
2273 save = filename;
2274 filename = estrndup(filename, filename_len);
2275 phar_unixify_path_separators(filename, filename_len);
2276 #endif
2277 if (phar_detect_phar_fname_ext(filename, filename_len, &ext_str, &ext_len, executable, for_create, 0 TSRMLS_CC) == FAILURE) {
2278 if (ext_len != -1) {
2279 if (!ext_str) {
2280 /* no / detected, restore arch for error message */
2281 #ifdef PHP_WIN32
2282 *arch = save;
2283 #else
2284 *arch = filename;
2285 #endif
2286 }
2287
2288 if (free_filename) {
2289 efree(filename);
2290 }
2291
2292 return FAILURE;
2293 }
2294
2295 ext_len = 0;
2296 /* no extension detected - instead we are dealing with an alias */
2297 }
2298
2299 *arch_len = ext_str - filename + ext_len;
2300 *arch = estrndup(filename, *arch_len);
2301
2302 if (ext_str[ext_len]) {
2303 *entry_len = filename_len - *arch_len;
2304 *entry = estrndup(ext_str+ext_len, *entry_len);
2305 #ifdef PHP_WIN32
2306 phar_unixify_path_separators(*entry, *entry_len);
2307 #endif
2308 *entry = phar_fix_filepath(*entry, entry_len, 0 TSRMLS_CC);
2309 } else {
2310 *entry_len = 1;
2311 *entry = estrndup("/", 1);
2312 }
2313
2314 if (free_filename) {
2315 efree(filename);
2316 }
2317
2318 return SUCCESS;
2319 }
2320 /* }}} */
2321
2322 /**
2323 * Invoked when a user calls Phar::mapPhar() from within an executing .phar
2324 * to set up its manifest directly
2325 */
2326 int phar_open_executed_filename(char *alias, int alias_len, char **error TSRMLS_DC) /* {{{ */
2327 {
2328 char *fname;
2329 zval *halt_constant;
2330 php_stream *fp;
2331 int fname_len;
2332 char *actual = NULL;
2333 int ret;
2334
2335 if (error) {
2336 *error = NULL;
2337 }
2338
2339 fname = (char*)zend_get_executed_filename(TSRMLS_C);
2340 fname_len = strlen(fname);
2341
2342 if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, 0, REPORT_ERRORS, NULL, 0 TSRMLS_CC) == SUCCESS) {
2343 return SUCCESS;
2344 }
2345
2346 if (!strcmp(fname, "[no active file]")) {
2347 if (error) {
2348 spprintf(error, 0, "cannot initialize a phar outside of PHP execution");
2349 }
2350 return FAILURE;
2351 }
2352
2353 MAKE_STD_ZVAL(halt_constant);
2354
2355 if (0 == zend_get_constant("__COMPILER_HALT_OFFSET__", 24, halt_constant TSRMLS_CC)) {
2356 FREE_ZVAL(halt_constant);
2357 if (error) {
2358 spprintf(error, 0, "__HALT_COMPILER(); must be declared in a phar");
2359 }
2360 return FAILURE;
2361 }
2362
2363 FREE_ZVAL(halt_constant);
2364
2365 #if PHP_API_VERSION < 20100412
2366 if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
2367 return FAILURE;
2368 }
2369 #endif
2370
2371 if (php_check_open_basedir(fname TSRMLS_CC)) {
2372 return FAILURE;
2373 }
2374
2375 fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, &actual);
2376
2377 if (!fp) {
2378 if (error) {
2379 spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
2380 }
2381 if (actual) {
2382 efree(actual);
2383 }
2384 return FAILURE;
2385 }
2386
2387 if (actual) {
2388 fname = actual;
2389 fname_len = strlen(actual);
2390 }
2391
2392 ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, REPORT_ERRORS, NULL, 0, error TSRMLS_CC);
2393
2394 if (actual) {
2395 efree(actual);
2396 }
2397
2398 return ret;
2399 }
2400 /* }}} */
2401
2402 /**
2403 * Validate the CRC32 of a file opened from within the phar
2404 */
2405 int phar_postprocess_file(phar_entry_data *idata, php_uint32 crc32, char **error, int process_zip TSRMLS_DC) /* {{{ */
2406 {
2407 php_uint32 crc = ~0;
2408 int len = idata->internal_file->uncompressed_filesize;
2409 php_stream *fp = idata->fp;
2410 phar_entry_info *entry = idata->internal_file;
2411
2412 if (error) {
2413 *error = NULL;
2414 }
2415
2416 if (entry->is_zip && process_zip > 0) {
2417 /* verify local file header */
2418 phar_zip_file_header local;
2419 phar_zip_data_desc desc;
2420
2421 if (SUCCESS != phar_open_archive_fp(idata->phar TSRMLS_CC)) {
2422 spprintf(error, 0, "phar error: unable to open zip-based phar archive \"%s\" to verify local file header for file \"%s\"", idata->phar->fname, entry->filename);
2423 return FAILURE;
2424 }
2425 php_stream_seek(phar_get_entrypfp(idata->internal_file TSRMLS_CC), entry->header_offset, SEEK_SET);
2426
2427 if (sizeof(local) != php_stream_read(phar_get_entrypfp(idata->internal_file TSRMLS_CC), (char *) &local, sizeof(local))) {
2428
2429 spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot read local file header for file \"%s\")", idata->phar->fname, entry->filename);
2430 return FAILURE;
2431 }
2432
2433 /* check for data descriptor */
2434 if (((PHAR_ZIP_16(local.flags)) & 0x8) == 0x8) {
2435 php_stream_seek(phar_get_entrypfp(idata->internal_file TSRMLS_CC),
2436 entry->header_offset + sizeof(local) +
2437 PHAR_ZIP_16(local.filename_len) +
2438 PHAR_ZIP_16(local.extra_len) +
2439 entry->compressed_filesize, SEEK_SET);
2440 if (sizeof(desc) != php_stream_read(phar_get_entrypfp(idata->internal_file TSRMLS_CC),
2441 (char *) &desc, sizeof(desc))) {
2442 spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot read local data descriptor for file \"%s\")", idata->phar->fname, entry->filename);
2443 return FAILURE;
2444 }
2445 if (desc.signature[0] == 'P' && desc.signature[1] == 'K') {
2446 memcpy(&(local.crc32), &(desc.crc32), 12);
2447 } else {
2448 /* old data descriptors have no signature */
2449 memcpy(&(local.crc32), &desc, 12);
2450 }
2451 }
2452 /* verify local header */
2453 if (entry->filename_len != PHAR_ZIP_16(local.filename_len) || entry->crc32 != PHAR_ZIP_32(local.crc32) || entry->uncompressed_filesize != PHAR_ZIP_32(local.uncompsize) || entry->compressed_filesize != PHAR_ZIP_32(local.compsize)) {
2454 spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (local header of file \"%s\" does not match central directory)", idata->phar->fname, entry->filename);
2455 return FAILURE;
2456 }
2457
2458 /* construct actual offset to file start - local extra_len can be different from central extra_len */
2459 entry->offset = entry->offset_abs =
2460 sizeof(local) + entry->header_offset + PHAR_ZIP_16(local.filename_len) + PHAR_ZIP_16(local.extra_len);
2461
2462 if (idata->zero && idata->zero != entry->offset_abs) {
2463 idata->zero = entry->offset_abs;
2464 }
2465 }
2466
2467 if (process_zip == 1) {
2468 return SUCCESS;
2469 }
2470
2471 php_stream_seek(fp, idata->zero, SEEK_SET);
2472
2473 while (len--) {
2474 CRC32(crc, php_stream_getc(fp));
2475 }
2476
2477 php_stream_seek(fp, idata->zero, SEEK_SET);
2478
2479 if (~crc == crc32) {
2480 entry->is_crc_checked = 1;
2481 return SUCCESS;
2482 } else {
2483 spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (crc32 mismatch on file \"%s\")", idata->phar->fname, entry->filename);
2484 return FAILURE;
2485 }
2486 }
2487 /* }}} */
2488
2489 static inline void phar_set_32(char *buffer, int var) /* {{{ */
2490 {
2491 #ifdef WORDS_BIGENDIAN
2492 *((buffer) + 3) = (unsigned char) (((var) >> 24) & 0xFF);
2493 *((buffer) + 2) = (unsigned char) (((var) >> 16) & 0xFF);
2494 *((buffer) + 1) = (unsigned char) (((var) >> 8) & 0xFF);
2495 *((buffer) + 0) = (unsigned char) ((var) & 0xFF);
2496 #else
2497 memcpy(buffer, &var, sizeof(var));
2498 #endif
2499 } /* }}} */
2500
2501 static int phar_flush_clean_deleted_apply(void *data TSRMLS_DC) /* {{{ */
2502 {
2503 phar_entry_info *entry = (phar_entry_info *)data;
2504
2505 if (entry->fp_refcount <= 0 && entry->is_deleted) {
2506 return ZEND_HASH_APPLY_REMOVE;
2507 } else {
2508 return ZEND_HASH_APPLY_KEEP;
2509 }
2510 }
2511 /* }}} */
2512
2513 #include "stub.h"
2514
2515 char *phar_create_default_stub(const char *index_php, const char *web_index, size_t *len, char **error TSRMLS_DC) /* {{{ */
2516 {
2517 char *stub = NULL;
2518 int index_len, web_len;
2519 size_t dummy;
2520
2521 if (!len) {
2522 len = &dummy;
2523 }
2524
2525 if (error) {
2526 *error = NULL;
2527 }
2528
2529 if (!index_php) {
2530 index_php = "index.php";
2531 }
2532
2533 if (!web_index) {
2534 web_index = "index.php";
2535 }
2536
2537 index_len = strlen(index_php);
2538 web_len = strlen(web_index);
2539
2540 if (index_len > 400) {
2541 /* ridiculous size not allowed for index.php startup filename */
2542 if (error) {
2543 spprintf(error, 0, "Illegal filename passed in for stub creation, was %d characters long, and only 400 or less is allowed", index_len);
2544 return NULL;
2545 }
2546 }
2547
2548 if (web_len > 400) {
2549 /* ridiculous size not allowed for index.php startup filename */
2550 if (error) {
2551 spprintf(error, 0, "Illegal web filename passed in for stub creation, was %d characters long, and only 400 or less is allowed", web_len);
2552 return NULL;
2553 }
2554 }
2555
2556 phar_get_stub(index_php, web_index, len, &stub, index_len+1, web_len+1 TSRMLS_CC);
2557 return stub;
2558 }
2559 /* }}} */
2560
2561 /**
2562 * Save phar contents to disk
2563 *
2564 * user_stub contains either a string, or a resource pointer, if len is a negative length.
2565 * user_stub and len should be both 0 if the default or existing stub should be used
2566 */
2567 int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, char **error TSRMLS_DC) /* {{{ */
2568 {
2569 char halt_stub[] = "__HALT_COMPILER();";
2570 char *newstub, *tmp;
2571 phar_entry_info *entry, *newentry;
2572 int halt_offset, restore_alias_len, global_flags = 0, closeoldfile;
2573 char *pos, has_dirs = 0;
2574 char manifest[18], entry_buffer[24];
2575 off_t manifest_ftell;
2576 long offset;
2577 size_t wrote;
2578 php_uint32 manifest_len, mytime, loc, new_manifest_count;
2579 php_uint32 newcrc32;
2580 php_stream *file, *oldfile, *newfile, *stubfile;
2581 php_stream_filter *filter;
2582 php_serialize_data_t metadata_hash;
2583 smart_str main_metadata_str = {0};
2584 int free_user_stub, free_fp = 1, free_ufp = 1;
2585 int manifest_hack = 0;
2586
2587 if (phar->is_persistent) {
2588 if (error) {
2589 spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
2590 }
2591 return EOF;
2592 }
2593
2594 if (error) {
2595 *error = NULL;
2596 }
2597
2598 if (!zend_hash_num_elements(&phar->manifest) && !user_stub) {
2599 return EOF;
2600 }
2601
2602 zend_hash_clean(&phar->virtual_dirs);
2603
2604 if (phar->is_zip) {
2605 return phar_zip_flush(phar, user_stub, len, convert, error TSRMLS_CC);
2606 }
2607
2608 if (phar->is_tar) {
2609 return phar_tar_flush(phar, user_stub, len, convert, error TSRMLS_CC);
2610 }
2611
2612 if (PHAR_G(readonly)) {
2613 return EOF;
2614 }
2615
2616 if (phar->fp && !phar->is_brandnew) {
2617 oldfile = phar->fp;
2618 closeoldfile = 0;
2619 php_stream_rewind(oldfile);
2620 } else {
2621 oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
2622 closeoldfile = oldfile != NULL;
2623 }
2624 newfile = php_stream_fopen_tmpfile();
2625 if (!newfile) {
2626 if (error) {
2627 spprintf(error, 0, "unable to create temporary file");
2628 }
2629 if (closeoldfile) {
2630 php_stream_close(oldfile);
2631 }
2632 return EOF;
2633 }
2634
2635 if (user_stub) {
2636 if (len < 0) {
2637 /* resource passed in */
2638 if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) {
2639 if (closeoldfile) {
2640 php_stream_close(oldfile);
2641 }
2642 php_stream_close(newfile);
2643 if (error) {
2644 spprintf(error, 0, "unable to access resource to copy stub to new phar \"%s\"", phar->fname);
2645 }
2646 return EOF;
2647 }
2648 if (len == -1) {
2649 len = PHP_STREAM_COPY_ALL;
2650 } else {
2651 len = -len;
2652 }
2653 user_stub = 0;
2654
2655 if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) {
2656 if (closeoldfile) {
2657 php_stream_close(oldfile);
2658 }
2659 php_stream_close(newfile);
2660 if (error) {
2661 spprintf(error, 0, "unable to read resource to copy stub to new phar \"%s\"", phar->fname);
2662 }
2663 return EOF;
2664 }
2665 free_user_stub = 1;
2666 } else {
2667 free_user_stub = 0;
2668 }
2669 tmp = estrndup(user_stub, len);
2670 if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
2671 efree(tmp);
2672 if (closeoldfile) {
2673 php_stream_close(oldfile);
2674 }
2675 php_stream_close(newfile);
2676 if (error) {
2677 spprintf(error, 0, "illegal stub for phar \"%s\"", phar->fname);
2678 }
2679 if (free_user_stub) {
2680 efree(user_stub);
2681 }
2682 return EOF;
2683 }
2684 pos = user_stub + (pos - tmp);
2685 efree(tmp);
2686 len = pos - user_stub + 18;
2687 if ((size_t)len != php_stream_write(newfile, user_stub, len)
2688 || 5 != php_stream_write(newfile, " ?>\r\n", 5)) {
2689 if (closeoldfile) {
2690 php_stream_close(oldfile);
2691 }
2692 php_stream_close(newfile);
2693 if (error) {
2694 spprintf(error, 0, "unable to create stub from string in new phar \"%s\"", phar->fname);
2695 }
2696 if (free_user_stub) {
2697 efree(user_stub);
2698 }
2699 return EOF;
2700 }
2701 phar->halt_offset = len + 5;
2702 if (free_user_stub) {
2703 efree(user_stub);
2704 }
2705 } else {
2706 size_t written;
2707
2708 if (!user_stub && phar->halt_offset && oldfile && !phar->is_brandnew) {
2709 phar_stream_copy_to_stream(oldfile, newfile, phar->halt_offset, &written);
2710 newstub = NULL;
2711 } else {
2712 /* this is either a brand new phar or a default stub overwrite */
2713 newstub = phar_create_default_stub(NULL, NULL, &(phar->halt_offset), NULL TSRMLS_CC);
2714 written = php_stream_write(newfile, newstub, phar->halt_offset);
2715 }
2716 if (phar->halt_offset != written) {
2717 if (closeoldfile) {
2718 php_stream_close(oldfile);
2719 }
2720 php_stream_close(newfile);
2721 if (error) {
2722 if (newstub) {
2723 spprintf(error, 0, "unable to create stub in new phar \"%s\"", phar->fname);
2724 } else {
2725 spprintf(error, 0, "unable to copy stub of old phar to new phar \"%s\"", phar->fname);
2726 }
2727 }
2728 if (newstub) {
2729 efree(newstub);
2730 }
2731 return EOF;
2732 }
2733 if (newstub) {
2734 efree(newstub);
2735 }
2736 }
2737 manifest_ftell = php_stream_tell(newfile);
2738 halt_offset = manifest_ftell;
2739
2740 /* Check whether we can get rid of some of the deleted entries which are
2741 * unused. However some might still be in use so even after this clean-up
2742 * we need to skip entries marked is_deleted. */
2743 zend_hash_apply(&phar->manifest, phar_flush_clean_deleted_apply TSRMLS_CC);
2744
2745 /* compress as necessary, calculate crcs, serialize meta-data, manifest size, and file sizes */
2746 main_metadata_str.c = 0;
2747 if (phar->metadata) {
2748 PHP_VAR_SERIALIZE_INIT(metadata_hash);
2749 php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash TSRMLS_CC);
2750 PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2751 } else {
2752 main_metadata_str.len = 0;
2753 }
2754 new_manifest_count = 0;
2755 offset = 0;
2756 for (zend_hash_internal_pointer_reset(&phar->manifest);
2757 zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
2758 zend_hash_move_forward(&phar->manifest)) {
2759 if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) {
2760 continue;
2761 }
2762 if (entry->cfp) {
2763 /* did we forget to get rid of cfp last time? */
2764 php_stream_close(entry->cfp);
2765 entry->cfp = 0;
2766 }
2767 if (entry->is_deleted || entry->is_mounted) {
2768 /* remove this from the new phar */
2769 continue;
2770 }
2771 if (!entry->is_modified && entry->fp_refcount) {
2772 /* open file pointers refer to this fp, do not free the stream */
2773 switch (entry->fp_type) {
2774 case PHAR_FP:
2775 free_fp = 0;
2776 break;
2777 case PHAR_UFP:
2778 free_ufp = 0;
2779 default:
2780 break;
2781 }
2782 }
2783 /* after excluding deleted files, calculate manifest size in bytes and number of entries */
2784 ++new_manifest_count;
2785 phar_add_virtual_dirs(phar, entry->filename, entry->filename_len TSRMLS_CC);
2786
2787 if (entry->is_dir) {
2788 /* we use this to calculate API version, 1.1.1 is used for phars with directories */
2789 has_dirs = 1;
2790 }
2791 if (entry->metadata) {
2792 if (entry->metadata_str.c) {
2793 smart_str_free(&entry->metadata_str);
2794 }
2795 entry->metadata_str.c = 0;
2796 entry->metadata_str.len = 0;
2797 PHP_VAR_SERIALIZE_INIT(metadata_hash);
2798 php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash TSRMLS_CC);
2799 PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2800 } else {
2801 if (entry->metadata_str.c) {
2802 smart_str_free(&entry->metadata_str);
2803 }
2804 entry->metadata_str.c = 0;
2805 entry->metadata_str.len = 0;
2806 }
2807
2808 /* 32 bits for filename length, length of filename, manifest + metadata, and add 1 for trailing / if a directory */
2809 offset += 4 + entry->filename_len + sizeof(entry_buffer) + entry->metadata_str.len + (entry->is_dir ? 1 : 0);
2810
2811 /* compress and rehash as necessary */
2812 if ((oldfile && !entry->is_modified) || entry->is_dir) {
2813 if (entry->fp_type == PHAR_UFP) {
2814 /* reset so we can copy the compressed data over */
2815 entry->fp_type = PHAR_FP;
2816 }
2817 continue;
2818 }
2819 if (!phar_get_efp(entry, 0 TSRMLS_CC)) {
2820 /* re-open internal file pointer just-in-time */
2821 newentry = phar_open_jit(phar, entry, error TSRMLS_CC);
2822 if (!newentry) {
2823 /* major problem re-opening, so we ignore this file and the error */
2824 efree(*error);
2825 *error = NULL;
2826 continue;
2827 }
2828 entry = newentry;
2829 }
2830 file = phar_get_efp(entry, 0 TSRMLS_CC);
2831 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 1 TSRMLS_CC)) {
2832 if (closeoldfile) {
2833 php_stream_close(oldfile);
2834 }
2835 php_stream_close(newfile);
2836 if (error) {
2837 spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2838 }
2839 return EOF;
2840 }
2841 newcrc32 = ~0;
2842 mytime = entry->uncompressed_filesize;
2843 for (loc = 0;loc < mytime; ++loc) {
2844 CRC32(newcrc32, php_stream_getc(file));
2845 }
2846 entry->crc32 = ~newcrc32;
2847 entry->is_crc_checked = 1;
2848 if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
2849 /* not compressed */
2850 entry->compressed_filesize = entry->uncompressed_filesize;
2851 continue;
2852 }
2853 filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0 TSRMLS_CC);
2854 if (!filter) {
2855 if (closeoldfile) {
2856 php_stream_close(oldfile);
2857 }
2858 php_stream_close(newfile);
2859 if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
2860 if (error) {
2861 spprintf(error, 0, "unable to gzip compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2862 }
2863 } else {
2864 if (error) {
2865 spprintf(error, 0, "unable to bzip2 compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2866 }
2867 }
2868 return EOF;
2869 }
2870
2871 /* create new file that holds the compressed version */
2872 /* work around inability to specify freedom in write and strictness
2873 in read count */
2874 entry->cfp = php_stream_fopen_tmpfile();
2875 if (!entry->cfp) {
2876 if (error) {
2877 spprintf(error, 0, "unable to create temporary file");
2878 }
2879 if (closeoldfile) {
2880 php_stream_close(oldfile);
2881 }
2882 php_stream_close(newfile);
2883 return EOF;
2884 }
2885 php_stream_flush(file);
2886 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
2887 if (closeoldfile) {
2888 php_stream_close(oldfile);
2889 }
2890 php_stream_close(newfile);
2891 if (error) {
2892 spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2893 }
2894 return EOF;
2895 }
2896 php_stream_filter_append((&entry->cfp->writefilters), filter);
2897 if (SUCCESS != phar_stream_copy_to_stream(file, entry->cfp, entry->uncompressed_filesize, NULL)) {
2898 if (closeoldfile) {
2899 php_stream_close(oldfile);
2900 }
2901 php_stream_close(newfile);
2902 if (error) {
2903 spprintf(error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2904 }
2905 return EOF;
2906 }
2907 php_stream_filter_flush(filter, 1);
2908 php_stream_flush(entry->cfp);
2909 php_stream_filter_remove(filter, 1 TSRMLS_CC);
2910 php_stream_seek(entry->cfp, 0, SEEK_END);
2911 entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
2912 /* generate crc on compressed file */
2913 php_stream_rewind(entry->cfp);
2914 entry->old_flags = entry->flags;
2915 entry->is_modified = 1;
2916 global_flags |= (entry->flags & PHAR_ENT_COMPRESSION_MASK);
2917 }
2918 global_flags |= PHAR_HDR_SIGNATURE;
2919
2920 /* write out manifest pre-header */
2921 /* 4: manifest length
2922 * 4: manifest entry count
2923 * 2: phar version
2924 * 4: phar global flags
2925 * 4: alias length
2926 * ?: the alias itself
2927 * 4: phar metadata length
2928 * ?: phar metadata
2929 */
2930 restore_alias_len = phar->alias_len;
2931 if (phar->is_temporary_alias) {
2932 phar->alias_len = 0;
2933 }
2934
2935 manifest_len = offset + phar->alias_len + sizeof(manifest) + main_metadata_str.len;
2936 phar_set_32(manifest, manifest_len);
2937 /* Hack - see bug #65028, add padding byte to the end of the manifest */
2938 if(manifest[0] == '\r' || manifest[0] == '\n') {
2939 manifest_len++;
2940 phar_set_32(manifest, manifest_len);
2941 manifest_hack = 1;
2942 }
2943 phar_set_32(manifest+4, new_manifest_count);
2944 if (has_dirs) {
2945 *(manifest + 8) = (unsigned char) (((PHAR_API_VERSION) >> 8) & 0xFF);
2946 *(manifest + 9) = (unsigned char) (((PHAR_API_VERSION) & 0xF0));
2947 } else {
2948 *(manifest + 8) = (unsigned char) (((PHAR_API_VERSION_NODIR) >> 8) & 0xFF);
2949 *(manifest + 9) = (unsigned char) (((PHAR_API_VERSION_NODIR) & 0xF0));
2950 }
2951 phar_set_32(manifest+10, global_flags);
2952 phar_set_32(manifest+14, phar->alias_len);
2953
2954 /* write the manifest header */
2955 if (sizeof(manifest) != php_stream_write(newfile, manifest, sizeof(manifest))
2956 || (size_t)phar->alias_len != php_stream_write(newfile, phar->alias, phar->alias_len)) {
2957
2958 if (closeoldfile) {
2959 php_stream_close(oldfile);
2960 }
2961
2962 php_stream_close(newfile);
2963 phar->alias_len = restore_alias_len;
2964
2965 if (error) {
2966 spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", phar->fname);
2967 }
2968
2969 return EOF;
2970 }
2971
2972 phar->alias_len = restore_alias_len;
2973
2974 phar_set_32(manifest, main_metadata_str.len);
2975 if (4 != php_stream_write(newfile, manifest, 4) || (main_metadata_str.len
2976 && main_metadata_str.len != php_stream_write(newfile, main_metadata_str.c, main_metadata_str.len))) {
2977 smart_str_free(&main_metadata_str);
2978
2979 if (closeoldfile) {
2980 php_stream_close(oldfile);
2981 }
2982
2983 php_stream_close(newfile);
2984 phar->alias_len = restore_alias_len;
2985
2986 if (error) {
2987 spprintf(error, 0, "unable to write manifest meta-data of new phar \"%s\"", phar->fname);
2988 }
2989
2990 return EOF;
2991 }
2992 smart_str_free(&main_metadata_str);
2993
2994 /* re-calculate the manifest location to simplify later code */
2995 manifest_ftell = php_stream_tell(newfile);
2996
2997 /* now write the manifest */
2998 for (zend_hash_internal_pointer_reset(&phar->manifest);
2999 zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
3000 zend_hash_move_forward(&phar->manifest)) {
3001
3002 if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) {
3003 continue;
3004 }
3005
3006 if (entry->is_deleted || entry->is_mounted) {
3007 /* remove this from the new phar if deleted, ignore if mounted */
3008 continue;
3009 }
3010
3011 if (entry->is_dir) {
3012 /* add 1 for trailing slash */
3013 phar_set_32(entry_buffer, entry->filename_len + 1);
3014 } else {
3015 phar_set_32(entry_buffer, entry->filename_len);
3016 }
3017
3018 if (4 != php_stream_write(newfile, entry_buffer, 4)
3019 || entry->filename_len != php_stream_write(newfile, entry->filename, entry->filename_len)
3020 || (entry->is_dir && 1 != php_stream_write(newfile, "/", 1))) {
3021 if (closeoldfile) {
3022 php_stream_close(oldfile);
3023 }
3024 php_stream_close(newfile);
3025 if (error) {
3026 if (entry->is_dir) {
3027 spprintf(error, 0, "unable to write filename of directory \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
3028 } else {
3029 spprintf(error, 0, "unable to write filename of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
3030 }
3031 }
3032 return EOF;
3033 }
3034
3035 /* set the manifest meta-data:
3036 4: uncompressed filesize
3037 4: creation timestamp
3038 4: compressed filesize
3039 4: crc32
3040 4: flags
3041 4: metadata-len
3042 +: metadata
3043 */
3044 mytime = time(NULL);
3045 phar_set_32(entry_buffer, entry->uncompressed_filesize);
3046 phar_set_32(entry_buffer+4, mytime);
3047 phar_set_32(entry_buffer+8, entry->compressed_filesize);
3048 phar_set_32(entry_buffer+12, entry->crc32);
3049 phar_set_32(entry_buffer+16, entry->flags);
3050 phar_set_32(entry_buffer+20, entry->metadata_str.len);
3051
3052 if (sizeof(entry_buffer) != php_stream_write(newfile, entry_buffer, sizeof(entry_buffer))
3053 || entry->metadata_str.len != php_stream_write(newfile, entry->metadata_str.c, entry->metadata_str.len)) {
3054 if (closeoldfile) {
3055 php_stream_close(oldfile);
3056 }
3057
3058 php_stream_close(newfile);
3059
3060 if (error) {
3061 spprintf(error, 0, "unable to write temporary manifest of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
3062 }
3063
3064 return EOF;
3065 }
3066 }
3067 /* Hack - see bug #65028, add padding byte to the end of the manifest */
3068 if(manifest_hack) {
3069 if(1 != php_stream_write(newfile, manifest, 1)) {
3070 if (closeoldfile) {
3071 php_stream_close(oldfile);
3072 }
3073
3074 php_stream_close(newfile);
3075
3076 if (error) {
3077 spprintf(error, 0, "unable to write manifest padding byte");
3078 }
3079
3080 return EOF;
3081 }
3082 }
3083
3084 /* now copy the actual file data to the new phar */
3085 offset = php_stream_tell(newfile);
3086 for (zend_hash_internal_pointer_reset(&phar->manifest);
3087 zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
3088 zend_hash_move_forward(&phar->manifest)) {
3089
3090 if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) {
3091 continue;
3092 }
3093
3094 if (entry->is_deleted || entry->is_dir || entry->is_mounted) {
3095 continue;
3096 }
3097
3098 if (entry->cfp) {
3099 file = entry->cfp;
3100 php_stream_rewind(file);
3101 } else {
3102 file = phar_get_efp(entry, 0 TSRMLS_CC);
3103 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
3104 if (closeoldfile) {
3105 php_stream_close(oldfile);
3106 }
3107 php_stream_close(newfile);
3108 if (error) {
3109 spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3110 }
3111 return EOF;
3112 }
3113 }
3114
3115 if (!file) {
3116 if (closeoldfile) {
3117 php_stream_close(oldfile);
3118 }
3119 php_stream_close(newfile);
3120 if (error) {
3121 spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3122 }
3123 return EOF;
3124 }
3125
3126 /* this will have changed for all files that have either changed compression or been modified */
3127 entry->offset = entry->offset_abs = offset;
3128 offset += entry->compressed_filesize;
3129 if (phar_stream_copy_to_stream(file, newfile, entry->compressed_filesize, &wrote) == FAILURE) {
3130 if (closeoldfile) {
3131 php_stream_close(oldfile);
3132 }
3133
3134 php_stream_close(newfile);
3135
3136 if (error) {
3137 spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
3138 }
3139
3140 return EOF;
3141 }
3142
3143 entry->is_modified = 0;
3144
3145 if (entry->cfp) {
3146 php_stream_close(entry->cfp);
3147 entry->cfp = NULL;
3148 }
3149
3150 if (entry->fp_type == PHAR_MOD) {
3151 /* this fp is in use by a phar_entry_data returned by phar_get_entry_data, it will be closed when the phar_entry_data is phar_entry_delref'ed */
3152 if (entry->fp_refcount == 0 && entry->fp != phar->fp && entry->fp != phar->ufp) {
3153 php_stream_close(entry->fp);
3154 }
3155
3156 entry->fp = NULL;
3157 entry->fp_type = PHAR_FP;
3158 } else if (entry->fp_type == PHAR_UFP) {
3159 entry->fp_type = PHAR_FP;
3160 }
3161 }
3162
3163 /* append signature */
3164 if (global_flags & PHAR_HDR_SIGNATURE) {
3165 char sig_buf[4];
3166
3167 php_stream_rewind(newfile);
3168
3169 if (phar->signature) {
3170 efree(phar->signature);
3171 phar->signature = NULL;
3172 }
3173
3174 switch(phar->sig_flags) {
3175 #ifndef PHAR_HASH_OK
3176 case PHAR_SIG_SHA512:
3177 case PHAR_SIG_SHA256:
3178 if (closeoldfile) {
3179 php_stream_close(oldfile);
3180 }
3181 php_stream_close(newfile);
3182 if (error) {
3183 spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\" with requested hash type", entry->filename, phar->fname);
3184 }
3185 return EOF;
3186 #endif
3187 default: {
3188 char *digest = NULL;
3189 int digest_len;
3190
3191 if (FAILURE == phar_create_signature(phar, newfile, &digest, &digest_len, error TSRMLS_CC)) {
3192 if (error) {
3193 char *save = *error;
3194 spprintf(error, 0, "phar error: unable to write signature: %s", save);
3195 efree(save);
3196 }
3197 if (digest) {
3198 efree(digest);
3199 }
3200 if (closeoldfile) {
3201 php_stream_close(oldfile);
3202 }
3203 php_stream_close(newfile);
3204 return EOF;
3205 }
3206
3207 php_stream_write(newfile, digest, digest_len);
3208 efree(digest);
3209 if (phar->sig_flags == PHAR_SIG_OPENSSL) {
3210 phar_set_32(sig_buf, digest_len);
3211 php_stream_write(newfile, sig_buf, 4);
3212 }
3213 break;
3214 }
3215 }
3216 phar_set_32(sig_buf, phar->sig_flags);
3217 php_stream_write(newfile, sig_buf, 4);
3218 php_stream_write(newfile, "GBMB", 4);
3219 }
3220
3221 /* finally, close the temp file, rename the original phar,
3222 move the temp to the old phar, unlink the old phar, and reload it into memory
3223 */
3224 if (phar->fp && free_fp) {
3225 php_stream_close(phar->fp);
3226 }
3227
3228 if (phar->ufp) {
3229 if (free_ufp) {
3230 php_stream_close(phar->ufp);
3231 }
3232 phar->ufp = NULL;
3233 }
3234
3235 if (closeoldfile) {
3236 php_stream_close(oldfile);
3237 }
3238
3239 phar->internal_file_start = halt_offset + manifest_len + 4;
3240 phar->halt_offset = halt_offset;
3241 phar->is_brandnew = 0;
3242
3243 php_stream_rewind(newfile);
3244
3245 if (phar->donotflush) {
3246 /* deferred flush */
3247 phar->fp = newfile;
3248 } else {
3249 phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
3250 if (!phar->fp) {
3251 phar->fp = newfile;
3252 if (error) {
3253 spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
3254 }
3255 return EOF;
3256 }
3257
3258 if (phar->flags & PHAR_FILE_COMPRESSED_GZ) {
3259 /* to properly compress, we have to tell zlib to add a zlib header */
3260 zval filterparams;
3261
3262 array_init(&filterparams);
3263 add_assoc_long(&filterparams, "window", MAX_WBITS+16);
3264 filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp) TSRMLS_CC);
3265 zval_dtor(&filterparams);
3266
3267 if (!filter) {
3268 if (error) {
3269 spprintf(error, 4096, "unable to compress all contents of phar \"%s\" using zlib, PHP versions older than 5.2.6 have a buggy zlib", phar->fname);
3270 }
3271 return EOF;
3272 }
3273
3274 php_stream_filter_append(&phar->fp->writefilters, filter);
3275 phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3276 php_stream_filter_flush(filter, 1);
3277 php_stream_filter_remove(filter, 1 TSRMLS_CC);
3278 php_stream_close(phar->fp);
3279 /* use the temp stream as our base */
3280 phar->fp = newfile;
3281 } else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) {
3282 filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp) TSRMLS_CC);
3283 php_stream_filter_append(&phar->fp->writefilters, filter);
3284 phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3285 php_stream_filter_flush(filter, 1);
3286 php_stream_filter_remove(filter, 1 TSRMLS_CC);
3287 php_stream_close(phar->fp);
3288 /* use the temp stream as our base */
3289 phar->fp = newfile;
3290 } else {
3291 phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3292 /* we could also reopen the file in "rb" mode but there is no need for that */
3293 php_stream_close(newfile);
3294 }
3295 }
3296
3297 if (-1 == php_stream_seek(phar->fp, phar->halt_offset, SEEK_SET)) {
3298 if (error) {
3299 spprintf(error, 0, "unable to seek to __HALT_COMPILER(); in new phar \"%s\"", phar->fname);
3300 }
3301 return EOF;
3302 }
3303
3304 return EOF;
3305 }
3306 /* }}} */
3307
3308 #ifdef COMPILE_DL_PHAR
3309 ZEND_GET_MODULE(phar)
3310 #endif
3311
3312 /* {{{ phar_functions[]
3313 *
3314 * Every user visible function must have an entry in phar_functions[].
3315 */
3316 zend_function_entry phar_functions[] = {
3317 PHP_FE_END
3318 };
3319 /* }}}*/
3320
3321 static size_t phar_zend_stream_reader(void *handle, char *buf, size_t len TSRMLS_DC) /* {{{ */
3322 {
3323 return php_stream_read(phar_get_pharfp((phar_archive_data*)handle TSRMLS_CC), buf, len);
3324 }
3325 /* }}} */
3326
3327 #if PHP_VERSION_ID >= 50300
3328 static size_t phar_zend_stream_fsizer(void *handle TSRMLS_DC) /* {{{ */
3329 {
3330 return ((phar_archive_data*)handle)->halt_offset + 32;
3331 } /* }}} */
3332
3333 #else /* PHP_VERSION_ID */
3334
3335 static long phar_stream_fteller_for_zend(void *handle TSRMLS_DC) /* {{{ */
3336 {
3337 return (long)php_stream_tell(phar_get_pharfp((phar_archive_data*)handle TSRMLS_CC));
3338 }
3339 /* }}} */
3340 #endif
3341
3342 zend_op_array *(*phar_orig_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);
3343 #if PHP_VERSION_ID >= 50300
3344 #define phar_orig_zend_open zend_stream_open_function
3345 static char *phar_resolve_path(const char *filename, int filename_len TSRMLS_DC)
3346 {
3347 return phar_find_in_include_path((char *) filename, filename_len, NULL TSRMLS_CC);
3348 }
3349 #else
3350 int (*phar_orig_zend_open)(const char *filename, zend_file_handle *handle TSRMLS_DC);
3351 #endif
3352
3353 static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type TSRMLS_DC) /* {{{ */
3354 {
3355 zend_op_array *res;
3356 char *name = NULL;
3357 int failed;
3358 phar_archive_data *phar;
3359
3360 if (!file_handle || !file_handle->filename) {
3361 return phar_orig_compile_file(file_handle, type TSRMLS_CC);
3362 }
3363 if (strstr(file_handle->filename, ".phar") && !strstr(file_handle->filename, "://")) {
3364 if (SUCCESS == phar_open_from_filename((char*)file_handle->filename, strlen(file_handle->filename), NULL, 0, 0, &phar, NULL TSRMLS_CC)) {
3365 if (phar->is_zip || phar->is_tar) {
3366 zend_file_handle f = *file_handle;
3367
3368 /* zip or tar-based phar */
3369 spprintf(&name, 4096, "phar://%s/%s", file_handle->filename, ".phar/stub.php");
3370 if (SUCCESS == phar_orig_zend_open((const char *)name, file_handle TSRMLS_CC)) {
3371 efree(name);
3372 name = NULL;
3373 file_handle->filename = f.filename;
3374 if (file_handle->opened_path) {
3375 efree(file_handle->opened_path);
3376 }
3377 file_handle->opened_path = f.opened_path;
3378 file_handle->free_filename = f.free_filename;
3379 } else {
3380 *file_handle = f;
3381 }
3382 } else if (phar->flags & PHAR_FILE_COMPRESSION_MASK) {
3383 /* compressed phar */
3384 #if PHP_VERSION_ID >= 50300
3385 file_handle->type = ZEND_HANDLE_STREAM;
3386 /* we do our own reading directly from the phar, don't change the next line */
3387 file_handle->handle.stream.handle = phar;
3388 file_handle->handle.stream.reader = phar_zend_stream_reader;
3389 file_handle->handle.stream.closer = NULL;
3390 file_handle->handle.stream.fsizer = phar_zend_stream_fsizer;
3391 file_handle->handle.stream.isatty = 0;
3392 phar->is_persistent ?
3393 php_stream_rewind(PHAR_GLOBALS->cached_fp[phar->phar_pos].fp) :
3394 php_stream_rewind(phar->fp);
3395 memset(&file_handle->handle.stream.mmap, 0, sizeof(file_handle->handle.stream.mmap));
3396 #else /* PHP_VERSION_ID */
3397 file_handle->type = ZEND_HANDLE_STREAM;
3398 /* we do our own reading directly from the phar, don't change the next line */
3399 file_handle->handle.stream.handle = phar;
3400 file_handle->handle.stream.reader = phar_zend_stream_reader;
3401 file_handle->handle.stream.closer = NULL; /* don't close - let phar handle this one */
3402 file_handle->handle.stream.fteller = phar_stream_fteller_for_zend;
3403 file_handle->handle.stream.interactive = 0;
3404 phar->is_persistent ?
3405 php_stream_rewind(PHAR_GLOBALS->cached_fp[phar->phar_pos].fp) :
3406 php_stream_rewind(phar->fp);
3407 #endif
3408 }
3409 }
3410 }
3411
3412 zend_try {
3413 failed = 0;
3414 res = phar_orig_compile_file(file_handle, type TSRMLS_CC);
3415 } zend_catch {
3416 failed = 1;
3417 res = NULL;
3418 } zend_end_try();
3419
3420 if (name) {
3421 efree(name);
3422 }
3423
3424 if (failed) {
3425 zend_bailout();
3426 }
3427
3428 return res;
3429 }
3430 /* }}} */
3431
3432 #if PHP_VERSION_ID < 50300
3433 int phar_zend_open(const char *filename, zend_file_handle *handle TSRMLS_DC) /* {{{ */
3434 {
3435 char *arch, *entry;
3436 int arch_len, entry_len;
3437
3438 /* this code is obsoleted in php 5.3 */
3439 entry = (char *) filename;
3440 if (!IS_ABSOLUTE_PATH(entry, strlen(entry)) && !strstr(entry, "://")) {
3441 phar_archive_data **pphar = NULL;
3442 char *fname;
3443 int fname_len;
3444
3445 fname = (char*)zend_get_executed_filename(TSRMLS_C);
3446 fname_len = strlen(fname);
3447
3448 if (fname_len > 7 && !strncasecmp(fname, "phar://", 7)) {
3449 if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 1, 0 TSRMLS_CC)) {
3450 zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar);
3451 if (!pphar && PHAR_G(manifest_cached)) {
3452 zend_hash_find(&cached_phars, arch, arch_len, (void **) &pphar);
3453 }
3454 efree(arch);
3455 efree(entry);
3456 }
3457 }
3458
3459 /* retrieving an include within the current directory, so use this if possible */
3460 if (!(entry = phar_find_in_include_path((char *) filename, strlen(filename), NULL TSRMLS_CC))) {
3461 /* this file is not in the phar, use the original path */
3462 goto skip_phar;
3463 }
3464
3465 if (SUCCESS == phar_orig_zend_open(entry, handle TSRMLS_CC)) {
3466 if (!handle->opened_path) {
3467 handle->opened_path = entry;
3468 }
3469 if (entry != filename) {
3470 handle->free_filename = 1;
3471 }
3472 return SUCCESS;
3473 }
3474
3475 if (entry != filename) {
3476 efree(entry);
3477 }
3478
3479 return FAILURE;
3480 }
3481 skip_phar:
3482 return phar_orig_zend_open(filename, handle TSRMLS_CC);
3483 }
3484 /* }}} */
3485 #endif
3486 typedef zend_op_array* (zend_compile_t)(zend_file_handle*, int TSRMLS_DC);
3487 typedef zend_compile_t* (compile_hook)(zend_compile_t *ptr);
3488
3489 PHP_GINIT_FUNCTION(phar) /* {{{ */
3490 {
3491 phar_mime_type mime;
3492
3493 memset(phar_globals, 0, sizeof(zend_phar_globals));
3494 phar_globals->readonly = 1;
3495
3496 zend_hash_init(&phar_globals->mime_types, 0, NULL, NULL, 1);
3497
3498 #define PHAR_SET_MIME(mimetype, ret, fileext) \
3499 mime.mime = mimetype; \
3500 mime.len = sizeof((mimetype))+1; \
3501 mime.type = ret; \
3502 zend_hash_add(&phar_globals->mime_types, fileext, sizeof(fileext)-1, (void *)&mime, sizeof(phar_mime_type), NULL); \
3503
3504 PHAR_SET_MIME("text/html", PHAR_MIME_PHPS, "phps")
3505 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c")
3506 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cc")
3507 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cpp")
3508 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c++")
3509 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "dtd")
3510 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "h")
3511 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "log")
3512 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "rng")
3513 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "txt")
3514 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "xsd")
3515 PHAR_SET_MIME("", PHAR_MIME_PHP, "php")
3516 PHAR_SET_MIME("", PHAR_MIME_PHP, "inc")
3517 PHAR_SET_MIME("video/avi", PHAR_MIME_OTHER, "avi")
3518 PHAR_SET_MIME("image/bmp", PHAR_MIME_OTHER, "bmp")
3519 PHAR_SET_MIME("text/css", PHAR_MIME_OTHER, "css")
3520 PHAR_SET_MIME("image/gif", PHAR_MIME_OTHER, "gif")
3521 PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htm")
3522 PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "html")
3523 PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htmls")
3524 PHAR_SET_MIME("image/x-ico", PHAR_MIME_OTHER, "ico")
3525 PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpe")
3526 PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpg")
3527 PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpeg")
3528 PHAR_SET_MIME("application/x-javascript", PHAR_MIME_OTHER, "js")
3529 PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "midi")
3530 PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "mid")
3531 PHAR_SET_MIME("audio/mod", PHAR_MIME_OTHER, "mod")
3532 PHAR_SET_MIME("movie/quicktime", PHAR_MIME_OTHER, "mov")
3533 PHAR_SET_MIME("audio/mp3", PHAR_MIME_OTHER, "mp3")
3534 PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpg")
3535 PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpeg")
3536 PHAR_SET_MIME("application/pdf", PHAR_MIME_OTHER, "pdf")
3537 PHAR_SET_MIME("image/png", PHAR_MIME_OTHER, "png")
3538 PHAR_SET_MIME("application/shockwave-flash", PHAR_MIME_OTHER, "swf")
3539 PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tif")
3540 PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tiff")
3541 PHAR_SET_MIME("audio/wav", PHAR_MIME_OTHER, "wav")
3542 PHAR_SET_MIME("image/xbm", PHAR_MIME_OTHER, "xbm")
3543 PHAR_SET_MIME("text/xml", PHAR_MIME_OTHER, "xml")
3544
3545 phar_restore_orig_functions(TSRMLS_C);
3546 }
3547 /* }}} */
3548
3549 PHP_GSHUTDOWN_FUNCTION(phar) /* {{{ */
3550 {
3551 zend_hash_destroy(&phar_globals->mime_types);
3552 }
3553 /* }}} */
3554
3555 PHP_MINIT_FUNCTION(phar) /* {{{ */
3556 {
3557 REGISTER_INI_ENTRIES();
3558
3559 phar_orig_compile_file = zend_compile_file;
3560 zend_compile_file = phar_compile_file;
3561
3562 #if PHP_VERSION_ID >= 50300
3563 phar_save_resolve_path = zend_resolve_path;
3564 zend_resolve_path = phar_resolve_path;
3565 #else
3566 phar_orig_zend_open = zend_stream_open_function;
3567 zend_stream_open_function = phar_zend_open;
3568 #endif
3569
3570 phar_object_init(TSRMLS_C);
3571
3572 phar_intercept_functions_init(TSRMLS_C);
3573 phar_save_orig_functions(TSRMLS_C);
3574
3575 return php_register_url_stream_wrapper("phar", &php_stream_phar_wrapper TSRMLS_CC);
3576 }
3577 /* }}} */
3578
3579 PHP_MSHUTDOWN_FUNCTION(phar) /* {{{ */
3580 {
3581 php_unregister_url_stream_wrapper("phar" TSRMLS_CC);
3582
3583 phar_intercept_functions_shutdown(TSRMLS_C);
3584
3585 if (zend_compile_file == phar_compile_file) {
3586 zend_compile_file = phar_orig_compile_file;
3587 }
3588
3589 #if PHP_VERSION_ID < 50300
3590 if (zend_stream_open_function == phar_zend_open) {
3591 zend_stream_open_function = phar_orig_zend_open;
3592 }
3593 #endif
3594 if (PHAR_G(manifest_cached)) {
3595 zend_hash_destroy(&(cached_phars));
3596 zend_hash_destroy(&(cached_alias));
3597 }
3598
3599 return SUCCESS;
3600 }
3601 /* }}} */
3602
3603 void phar_request_initialize(TSRMLS_D) /* {{{ */
3604 {
3605 if (!PHAR_GLOBALS->request_init)
3606 {
3607 PHAR_G(last_phar) = NULL;
3608 PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
3609 PHAR_G(has_bz2) = zend_hash_exists(&module_registry, "bz2", sizeof("bz2"));
3610 PHAR_G(has_zlib) = zend_hash_exists(&module_registry, "zlib", sizeof("zlib"));
3611 PHAR_GLOBALS->request_init = 1;
3612 PHAR_GLOBALS->request_ends = 0;
3613 PHAR_GLOBALS->request_done = 0;
3614 zend_hash_init(&(PHAR_GLOBALS->phar_fname_map), 5, zend_get_hash_value, destroy_phar_data, 0);
3615 zend_hash_init(&(PHAR_GLOBALS->phar_persist_map), 5, zend_get_hash_value, NULL, 0);
3616 zend_hash_init(&(PHAR_GLOBALS->phar_alias_map), 5, zend_get_hash_value, NULL, 0);
3617
3618 if (PHAR_G(manifest_cached)) {
3619 phar_archive_data **pphar;
3620 phar_entry_fp *stuff = (phar_entry_fp *) ecalloc(zend_hash_num_elements(&cached_phars), sizeof(phar_entry_fp));
3621
3622 for (zend_hash_internal_pointer_reset(&cached_phars);
3623 zend_hash_get_current_data(&cached_phars, (void **)&pphar) == SUCCESS;
3624 zend_hash_move_forward(&cached_phars)) {
3625 stuff[pphar[0]->phar_pos].manifest = (phar_entry_fp_info *) ecalloc( zend_hash_num_elements(&(pphar[0]->manifest)), sizeof(phar_entry_fp_info));
3626 }
3627
3628 PHAR_GLOBALS->cached_fp = stuff;
3629 }
3630
3631 PHAR_GLOBALS->phar_SERVER_mung_list = 0;
3632 PHAR_G(cwd) = NULL;
3633 PHAR_G(cwd_len) = 0;
3634 PHAR_G(cwd_init) = 0;
3635 }
3636 }
3637 /* }}} */
3638
3639 PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */
3640 {
3641 int i;
3642
3643 PHAR_GLOBALS->request_ends = 1;
3644
3645 if (PHAR_GLOBALS->request_init)
3646 {
3647 phar_release_functions(TSRMLS_C);
3648 zend_hash_destroy(&(PHAR_GLOBALS->phar_alias_map));
3649 PHAR_GLOBALS->phar_alias_map.arBuckets = NULL;
3650 zend_hash_destroy(&(PHAR_GLOBALS->phar_fname_map));
3651 PHAR_GLOBALS->phar_fname_map.arBuckets = NULL;
3652 zend_hash_destroy(&(PHAR_GLOBALS->phar_persist_map));
3653 PHAR_GLOBALS->phar_persist_map.arBuckets = NULL;
3654 PHAR_GLOBALS->phar_SERVER_mung_list = 0;
3655
3656 if (PHAR_GLOBALS->cached_fp) {
3657 for (i = 0; i < zend_hash_num_elements(&cached_phars); ++i) {
3658 if (PHAR_GLOBALS->cached_fp[i].fp) {
3659 php_stream_close(PHAR_GLOBALS->cached_fp[i].fp);
3660 }
3661 if (PHAR_GLOBALS->cached_fp[i].ufp) {
3662 php_stream_close(PHAR_GLOBALS->cached_fp[i].ufp);
3663 }
3664 efree(PHAR_GLOBALS->cached_fp[i].manifest);
3665 }
3666 efree(PHAR_GLOBALS->cached_fp);
3667 PHAR_GLOBALS->cached_fp = 0;
3668 }
3669
3670 PHAR_GLOBALS->request_init = 0;
3671
3672 if (PHAR_G(cwd)) {
3673 efree(PHAR_G(cwd));
3674 }
3675
3676 PHAR_G(cwd) = NULL;
3677 PHAR_G(cwd_len) = 0;
3678 PHAR_G(cwd_init) = 0;
3679 }
3680
3681 PHAR_GLOBALS->request_done = 1;
3682 return SUCCESS;
3683 }
3684 /* }}} */
3685
3686 PHP_MINFO_FUNCTION(phar) /* {{{ */
3687 {
3688 phar_request_initialize(TSRMLS_C);
3689 php_info_print_table_start();
3690 php_info_print_table_header(2, "Phar: PHP Archive support", "enabled");
3691 php_info_print_table_row(2, "Phar EXT version", PHP_PHAR_VERSION);
3692 php_info_print_table_row(2, "Phar API version", PHP_PHAR_API_VERSION);
3693 php_info_print_table_row(2, "SVN revision", "$Id: ba734629367f9671b25202408d13914fa63d8396 $");
3694 php_info_print_table_row(2, "Phar-based phar archives", "enabled");
3695 php_info_print_table_row(2, "Tar-based phar archives", "enabled");
3696 php_info_print_table_row(2, "ZIP-based phar archives", "enabled");
3697
3698 if (PHAR_G(has_zlib)) {
3699 php_info_print_table_row(2, "gzip compression", "enabled");
3700 } else {
3701 php_info_print_table_row(2, "gzip compression", "disabled (install ext/zlib)");
3702 }
3703
3704 if (PHAR_G(has_bz2)) {
3705 php_info_print_table_row(2, "bzip2 compression", "enabled");
3706 } else {
3707 php_info_print_table_row(2, "bzip2 compression", "disabled (install pecl/bz2)");
3708 }
3709 #ifdef PHAR_HAVE_OPENSSL
3710 php_info_print_table_row(2, "Native OpenSSL support", "enabled");
3711 #else
3712 if (zend_hash_exists(&module_registry, "openssl", sizeof("openssl"))) {
3713 php_info_print_table_row(2, "OpenSSL support", "enabled");
3714 } else {
3715 php_info_print_table_row(2, "OpenSSL support", "disabled (install ext/openssl)");
3716 }
3717 #endif
3718 php_info_print_table_end();
3719
3720 php_info_print_box_start(0);
3721 PUTS("Phar based on pear/PHP_Archive, original concept by Davey Shafik.");
3722 PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3723 PUTS("Phar fully realized by Gregory Beaver and Marcus Boerger.");
3724 PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3725 PUTS("Portions of tar implementation Copyright (c) 2003-2009 Tim Kientzle.");
3726 php_info_print_box_end();
3727
3728 DISPLAY_INI_ENTRIES();
3729 }
3730 /* }}} */
3731
3732 /* {{{ phar_module_entry
3733 */
3734 static const zend_module_dep phar_deps[] = {
3735 ZEND_MOD_OPTIONAL("apc")
3736 ZEND_MOD_OPTIONAL("bz2")
3737 ZEND_MOD_OPTIONAL("openssl")
3738 ZEND_MOD_OPTIONAL("zlib")
3739 ZEND_MOD_OPTIONAL("standard")
3740 #if defined(HAVE_HASH) && !defined(COMPILE_DL_HASH)
3741 ZEND_MOD_REQUIRED("hash")
3742 #endif
3743 #if HAVE_SPL
3744 ZEND_MOD_REQUIRED("spl")
3745 #endif
3746 ZEND_MOD_END
3747 };
3748
3749 zend_module_entry phar_module_entry = {
3750 STANDARD_MODULE_HEADER_EX, NULL,
3751 phar_deps,
3752 "Phar",
3753 phar_functions,
3754 PHP_MINIT(phar),
3755 PHP_MSHUTDOWN(phar),
3756 NULL,
3757 PHP_RSHUTDOWN(phar),
3758 PHP_MINFO(phar),
3759 PHP_PHAR_VERSION,
3760 PHP_MODULE_GLOBALS(phar), /* globals descriptor */
3761 PHP_GINIT(phar), /* globals ctor */
3762 PHP_GSHUTDOWN(phar), /* globals dtor */
3763 NULL, /* post deactivate */
3764 STANDARD_MODULE_PROPERTIES_EX
3765 };
3766 /* }}} */
3767
3768 /*
3769 * Local variables:
3770 * tab-width: 4
3771 * c-basic-offset: 4
3772 * End:
3773 * vim600: noet sw=4 ts=4 fdm=marker
3774 * vim<600: noet sw=4 ts=4
3775 */
3776