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