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