}
/* }}} */
-/* {{{ proto mixed Phar::mapPhar(string alias, mixed compressed, [mixed unused [, mixed unused]])
- * Maps the curretnly executed file (a phar) with the given alias */
-PHP_METHOD(Phar, mapPhar)
+#define MAPPHAR_ALLOC_FAIL(msg) \
+ php_stream_close(fp);\
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, msg, fname);\
+ return FAILURE;
+
+#define MAPPHAR_FAIL(msg) \
+ efree(savebuf);\
+ MAPPHAR_ALLOC_FAIL(msg)
+
+static int phar_open_file(php_stream *fp, char *fname, char *alias, int alias_len, zend_bool compressed, long halt_offset) /* {{{ */
{
- char *fname, *alias, *buffer, *endbuffer, *unpack_var, *savebuf;
+ char *buffer, *endbuffer, *unpack_var, *savebuf;
phar_file_data mydata;
- zend_bool compressed;
phar_manifest_entry entry;
HashTable *manifest;
- int alias_len, i;
- long halt_offset;
+ int i;
php_uint32 manifest_len, manifest_count, manifest_index;
- zval *halt_constant, **unused1, **unused2;
- php_stream *fp;
-
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sb|zz", &alias, &alias_len, &compressed, &unused1, &unused2) == FAILURE) {
- return;
- }
-#ifndef HAVE_PHAR_ZLIB
- if (compressed) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "zlib extension is required for compressed .phar files");
- return;
- }
-#endif
- fname = zend_get_executed_filename(TSRMLS_C);
- if (!strcmp(fname, "[no active file]")) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "cannot initialize a phar outside of PHP execution");
- return;
- }
-
- MAKE_STD_ZVAL(halt_constant);
- if (0 == zend_get_constant("__COMPILER_HALT_OFFSET__", 24, halt_constant TSRMLS_CC)) {
- zval_dtor(halt_constant);
- FREE_ZVAL(halt_constant);
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "__HALT_COMPILER(); must be declared in a phar");
- return;
- }
- halt_offset = Z_LVAL(*halt_constant);
- zval_dtor(halt_constant);
- FREE_ZVAL(halt_constant);
if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
- return;
+ return FAILURE;
}
if (php_check_open_basedir(fname TSRMLS_CC)) {
- return;
+ return FAILURE;
}
fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
if (!fp) {
php_error_docref(NULL TSRMLS_CC, E_ERROR, "unable to open phar for reading \"%s\"", fname);
- return;
+ return FAILURE;
}
-#define MAPPHAR_ALLOC_FAIL(msg) php_stream_close(fp);\
- php_stream_close(fp);\
- php_error_docref(NULL TSRMLS_CC, E_ERROR, msg, fname);\
- return;
-#define MAPPHAR_FAIL(msg) efree(savebuf);\
- MAPPHAR_ALLOC_FAIL(msg)
/* check for ?>\n and increment accordingly */
if (-1 == php_stream_seek(fp, halt_offset, SEEK_SET)) {
zend_hash_add(&(PHAR_GLOBALS->phar_data), alias, alias_len, &mydata,
sizeof(phar_file_data), NULL);
efree(savebuf);
+
+ return SUCCESS;
+}
+/* }}} */
+
+static int phar_open_filename(char *fname, char *alias, int alias_len, zend_bool compressed) /* {{{ */
+{
+ const char token[] = "__HALT_COMPILER();";
+ char *pos, buffer[1024 + sizeof(token)];
+ const long readsize = sizeof(buffer) - sizeof(token);
+ const long tokenlen = sizeof(token) - 1;
+ int result;
+ long halt_offset;
+ php_stream *fp;
+
+ fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
+
+ if (!fp) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "unable to open phar for reading \"%s\"", fname);
+ return FAILURE;
+ }
+
+ /* Maybe it's better to compile the file instead of just searching, */
+ /* but we only want the offset. So we want a .re scanner to find it. */
+
+ if (-1 == php_stream_seek(fp, 0, SEEK_SET)) {
+ MAPPHAR_ALLOC_FAIL("cannot seek to __HALT_COMPILER(); location in phar \"%s\"")
+ }
+
+ buffer[sizeof(buffer)-1] = '\0';
+ memset(buffer, 32, sizeof(token));
+ halt_offset = 0;
+ while(!php_stream_eof(fp)) {
+ if (php_stream_read(fp, buffer+tokenlen, readsize) < 0) {
+ MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest)")
+ }
+
+ if ((pos = strstr(buffer, token)) != NULL) {
+ halt_offset = (pos - buffer); /* no -tokenlen+tokenlen here */
+ result = phar_open_file(fp, fname, alias, alias_len, compressed, halt_offset);
+ php_stream_close(fp);
+ return result;
+ }
+
+ halt_offset += readsize;
+ memmove(buffer, buffer + tokenlen, readsize + 1);
+ }
+
+ MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (__HALT_COMPILER(); not found)")
php_stream_close(fp);
+ return FAILURE;
+}
+/* }}} */
+
+static php_url* phar_open_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options) /* {{{ */
+{
+ php_url *resource;
+ char *pos_p, *pos_z, *ext_str, *alias;
+ int len, ext_len;
+
+ if (!strncasecmp(filename, "phar://", 7)) {
+ filename += 7;
+ pos_p = strstr(filename, ".phar.php");
+ pos_z = strstr(filename, ".phar.gz");
+ if (pos_p) {
+ if (pos_z) {
+ php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\" (cannot contain .phar.php and .phar.gz)", filename);
+ return NULL;
+ }
+ ext_str = pos_p;
+ ext_len = 9;
+ } else if (pos_z) {
+ ext_str = pos_z;
+ ext_len = 8;
+ } else {
+ php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\" (filename extension must be either .phar.php or .phar.gz)", filename);
+ return NULL;
+ }
+ resource = emalloc(sizeof(php_url));
+ resource->scheme = estrndup("phar", 4);
+ len = ext_str - filename + ext_len;
+ alias = resource->host = estrndup(filename, len);
+ if (ext_str[ext_len]) {
+ resource->path = estrdup(ext_str+ext_len);
+ } else {
+ resource->path = estrndup("/", 1);
+ }
+#if MBO_0
+ if (resource) {
+ fprintf(stderr, "Alias: %s\n", alias);
+ fprintf(stderr, "Compressed:%s\n", pos_z ? "yes" : "no");
+ fprintf(stderr, "Scheme: %s\n", resource->scheme);
+/* fprintf(stderr, "User: %s\n", resource->user);*/
+/* fprintf(stderr, "Pass: %s\n", resource->pass ? "***" : NULL);*/
+ fprintf(stderr, "Host: %s\n", resource->host);
+/* fprintf(stderr, "Port: %d\n", resource->port);*/
+ fprintf(stderr, "Path: %s\n", resource->path);
+/* fprintf(stderr, "Query: %s\n", resource->query);*/
+/* fprintf(stderr, "Fragment: %s\n", resource->fragment);*/
+ }
+#endif
+ if (phar_open_filename(resource->host, alias, len, pos_z ? 1 : 0) == FAILURE)
+ {
+ php_url_free(resource);
+ return NULL;
+ }
+
+ return resource;
+ }
+
+ return NULL;
}
/* }}} */
+static int phar_open_compiled_file(char *alias, int alias_len, zend_bool compressed) /* {{{ */
+{
+ char *fname;
+ int result;
+ long halt_offset;
+ zval *halt_constant;
+ php_stream *fp;
+
+ fname = zend_get_executed_filename(TSRMLS_C);
+
+ if (!strcmp(fname, "[no active file]")) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "cannot initialize a phar outside of PHP execution");
+ return FAILURE;
+ }
+
+ MAKE_STD_ZVAL(halt_constant);
+ if (0 == zend_get_constant("__COMPILER_HALT_OFFSET__", 24, halt_constant TSRMLS_CC)) {
+ zval_dtor(halt_constant);
+ FREE_ZVAL(halt_constant);
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "__HALT_COMPILER(); must be declared in a phar");
+ return FAILURE;
+ }
+ halt_offset = Z_LVAL(*halt_constant);
+ zval_dtor(halt_constant);
+ FREE_ZVAL(halt_constant);
+
+ fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
+
+ if (!fp) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "unable to open phar for reading \"%s\"", fname);
+ return FAILURE;
+ }
+
+ result = phar_open_file(fp, fname, alias, alias_len, compressed, halt_offset);
+ php_stream_close(fp);
+ return result;
+}
+/* }}} */
+
+/* {{{ proto mixed Phar::mapPhar(string alias, mixed compressed, [mixed unused [, mixed unused]])
+ * Maps the curretnly executed file (a phar) with the given alias */
+PHP_METHOD(Phar, mapPhar)
+{
+ char *alias;
+ zend_bool compressed;
+ int alias_len;
+ zval **unused1, **unused2;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sb|zz", &alias, &alias_len, &compressed, &unused1, &unused2) == FAILURE) {
+ return;
+ }
+#ifndef HAVE_PHAR_ZLIB
+ if (compressed) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "zlib extension is required for compressed .phar files");
+ return;
+ }
+#endif
+
+ RETURN_BOOL(phar_open_compiled_file(alias, alias_len, compressed) == SUCCESS);
+} /* }}} */
+
PHP_PHAR_API php_stream *php_stream_phar_url_wrapper(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
PHP_PHAR_API int phar_close(php_stream *stream, int close_handle TSRMLS_DC);
PHP_PHAR_API int phar_closedir(php_stream *stream, int close_handle TSRMLS_DC);
+PHP_PHAR_API int phar_seekdir(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC);
PHP_PHAR_API size_t phar_read(php_stream *stream, char *buf, size_t count TSRMLS_DC);
PHP_PHAR_API size_t phar_readdir(php_stream *stream, char *buf, size_t count TSRMLS_DC);
PHP_PHAR_API int phar_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC);
phar_closedir, /* close */
phar_flush, /* flush (does nothing) */
"phar stream",
- NULL, /* seek */
+ phar_seekdir, /* seek */
NULL, /* cast */
phar_stat, /* stat */
NULL, /* set option */
#endif
resource = php_url_parse(path);
+
+ if (!resource && (resource = phar_open_url(wrapper, path, mode, options)) == NULL) {
+ return NULL;
+ }
+
/* we must have at the very least phar://alias.phar/internalfile.php */
if (!resource || !resource->scheme || !resource->host || !resource->path) {
if (resource) {
}
/* }}} */
+PHP_PHAR_API int phar_seekdir(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) /* {{{ */
+{
+ HashTable *data = (HashTable *)stream->abstract;
+
+ if (data)
+ {
+ if (whence == SEEK_END) {
+ whence = SEEK_SET;
+ offset = zend_hash_num_elements(data) + offset;
+ }
+ if (whence == SEEK_SET) {
+ zend_hash_internal_pointer_reset(data);
+ }
+
+ if (offset < 0) {
+ php_stream_wrapper_log_error(stream->wrapper, stream->flags TSRMLS_CC, "phar error: cannot seek because the resulting seek is negative");
+ return -1;
+ } else {
+ *newoffset = 0;
+ while (*newoffset < offset && zend_hash_move_forward(data) == SUCCESS) {
+ (*newoffset)++;
+ }
+ return 0;
+ }
+ }
+ return -1;
+}
+/* }}} */
+
PHP_PHAR_API size_t phar_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */
{
size_t to_read;
phar_manifest_entry *file_data;
resource = php_url_parse(url);
+
+ if (!resource && (resource = phar_open_url(wrapper, url, "r", 0)) == NULL) {
+ return -1;
+ }
+
/* we must have at the very least phar://alias.phar/internalfile.php */
if (!resource || !resource->scheme || !resource->host || !resource->path) {
- php_url_free(resource);
+ if (resource) {
+ php_url_free(resource);
+ }
php_stream_wrapper_log_error(wrapper, flags TSRMLS_CC, "phar error: invalid url \"%s\"", url);
- php_url_free(resource);
return -1;
}
phar_manifest_entry *file_data;
resource = php_url_parse(filename);
+
+ if (!resource && (resource = phar_open_url(wrapper, filename, mode, options)) == NULL) {
+ return NULL;
+ }
+
/* we must have at the very least phar://alias.phar/ */
if (!resource || !resource->scheme || !resource->host || !resource->path) {
if (resource && resource->host && !resource->path) {
little_endian_long_map[2] = size - 3;
little_endian_long_map[3] = size - 4;
}
+
INIT_CLASS_ENTRY(php_archive_entry, "Phar", php_archive_methods);
php_archive_entry_ptr = zend_register_internal_class(&php_archive_entry TSRMLS_CC);
+
return php_register_url_stream_wrapper("phar", &php_stream_phar_wrapper TSRMLS_CC);
}
/* }}} */
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
- "phar",
+ "Phar",
phar_functions,
PHP_MINIT(phar),
PHP_MSHUTDOWN(phar),