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