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