diff options
author | Pierre Joye <pajoye@php.net> | 2008-10-23 16:13:51 +0000 |
---|---|---|
committer | Pierre Joye <pajoye@php.net> | 2008-10-23 16:13:51 +0000 |
commit | 047bda3cb82c5b398a610e28433487bd87609383 (patch) | |
tree | 1c719997f4a570e2ba10a958d72b7e22468caa51 /ext/zip/php_zip.c | |
parent | 8d916b2ec6ec5be8fe68a1461af87d14826a425a (diff) | |
download | php-git-047bda3cb82c5b398a610e28433487bd87609383.tar.gz |
- MFH
- Update ziplib
- flatten path and make them relative before extraction
- remove unnecessary export
Diffstat (limited to 'ext/zip/php_zip.c')
-rw-r--r-- | ext/zip/php_zip.c | 357 |
1 files changed, 261 insertions, 96 deletions
diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 0dcbf2a8a7..e6fb77511e 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -79,81 +79,152 @@ static int le_zip_entry; RETURN_FALSE; \ } \ RETURN_TRUE; +/* }}} */ + +#if (PHP_MAJOR_VERSION < 6) +# define add_ascii_assoc_string add_assoc_string +# define add_ascii_assoc_long add_assoc_long +#endif + +/* Flatten a path by creating a relative path (to .) */ +static char * php_zip_make_relative_path(char *path, int path_len) /* {{{ */ +{ + char *path_begin = path; + int prev_is_slash = 0; + char *e = path + path_len - 1; + size_t pos = path_len - 1; + size_t i; + + if (IS_SLASH(path[0])) { + return path + 1; + } + + if (path_len < 1 || path == NULL) { + return NULL; + } + i = path_len; + + while (1) { + while (i > 0 && !IS_SLASH(path[i])) { + i--; + } + + if (!i) { + return path; + } + + if (i >= 2 && (path[i -1] == '.' || path[i -1] == ':')) { + /* i is the position of . or :, add 1 for / */ + path_begin = path + i + 1; + break; + } + i--; + } + + return path_begin; +} /* }}} */ /* {{{ php_zip_extract_file */ -/* TODO: Simplify it */ static int php_zip_extract_file(struct zip * za, char *dest, char *file, int file_len TSRMLS_DC) { php_stream_statbuf ssb; struct zip_file *zf; struct zip_stat sb; char b[8192]; - int n, len, ret; - php_stream *stream; - char *fullpath; char *file_dirname_fullpath; char file_dirname[MAXPATHLEN]; size_t dir_len; - char *file_basename; size_t file_basename_len; int is_dir_only = 0; + char *path_cleaned; + size_t path_cleaned_len; + cwd_state new_state; + + new_state.cwd = (char*)malloc(1); + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; - if (file_len >= MAXPATHLEN || zip_stat(za, file, 0, &sb) != 0) { + /* Clean/normlize the path and then transform any path (absolute or relative) + to a path relative to cwd (../../mydir/foo.txt > mydir/foo.txt) + */ + virtual_file_ex(&new_state, file, NULL, CWD_EXPAND); + path_cleaned = php_zip_make_relative_path(new_state.cwd, new_state.cwd_length); + path_cleaned_len = strlen(path_cleaned); + + if (path_cleaned_len >= MAXPATHLEN || zip_stat(za, file, 0, &sb) != 0) { return 0; } - if (file_len > 1 && file[file_len - 1] == '/') { + /* it is a directory only, see #40228 */ + if (path_cleaned_len > 1 && IS_SLASH(path_cleaned[path_cleaned_len - 1])) { len = spprintf(&file_dirname_fullpath, 0, "%s/%s", dest, file); is_dir_only = 1; } else { - memcpy(file_dirname, file, file_len); - dir_len = php_dirname(file_dirname, file_len); + memcpy(file_dirname, path_cleaned, path_cleaned_len); + dir_len = php_dirname(file_dirname, path_cleaned_len); - if (dir_len > 0) { - len = spprintf(&file_dirname_fullpath, 0, "%s/%s", dest, file_dirname); - } else { + if (dir_len <= 0 || (dir_len == 1 && file_dirname[0] == '.')) { len = spprintf(&file_dirname_fullpath, 0, "%s", dest); + } else { + len = spprintf(&file_dirname_fullpath, 0, "%s/%s", dest, file_dirname); } - php_basename(file, file_len, NULL, 0, &file_basename, (size_t *)&file_basename_len TSRMLS_CC); + php_basename(path_cleaned, path_cleaned_len, NULL, 0, &file_basename, (unsigned int *)&file_basename_len TSRMLS_CC); if (OPENBASEDIR_CHECKPATH(file_dirname_fullpath)) { efree(file_dirname_fullpath); efree(file_basename); + free(new_state.cwd); return 0; } } /* let see if the path already exists */ if (php_stream_stat_path(file_dirname_fullpath, &ssb) < 0) { - ret = php_stream_mkdir(file_dirname_fullpath, 0777, PHP_STREAM_MKDIR_RECURSIVE, NULL); + +#if defined(PHP_WIN32) && (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 1) + char *e; + e = file_dirname_fullpath; + while (*e) { + if (*e == '/') { + *e = DEFAULT_SLASH; + } + e++; + } +#endif + + ret = php_stream_mkdir(file_dirname_fullpath, 0777, PHP_STREAM_MKDIR_RECURSIVE|REPORT_ERRORS, NULL); if (!ret) { efree(file_dirname_fullpath); + if (!is_dir_only) { efree(file_basename); + free(new_state.cwd); + } return 0; } } /* it is a standalone directory, job done */ - if (file[file_len - 1] == '/') { + if (is_dir_only) { efree(file_dirname_fullpath); - if (!is_dir_only) { - efree(file_basename); - } + free(new_state.cwd); return 1; } - len = spprintf(&fullpath, 0, "%s/%s/%s", dest, file_dirname, file_basename); + len = spprintf(&fullpath, 0, "%s/%s", file_dirname_fullpath, file_basename); if (!len) { efree(file_dirname_fullpath); efree(file_basename); + free(new_state.cwd); return 0; + } else if (len > MAXPATHLEN) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Full extraction path exceed MAXPATHLEN (%i)", MAXPATHLEN); } /* check again the full path, not sure if it @@ -164,6 +235,7 @@ static int php_zip_extract_file(struct zip * za, char *dest, char *file, int fil efree(fullpath); efree(file_dirname_fullpath); efree(file_basename); + free(new_state.cwd); return 0; } @@ -172,10 +244,15 @@ static int php_zip_extract_file(struct zip * za, char *dest, char *file, int fil efree(fullpath); efree(file_dirname_fullpath); efree(file_basename); + free(new_state.cwd); return 0; } +#if (PHP_MAJOR_VERSION < 6) stream = php_stream_open_wrapper(fullpath, "w+b", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL); +#else + stream = php_stream_open_wrapper(fullpath, "w+b", REPORT_ERRORS, NULL); +#endif n = 0; if (stream) { while ((n=zip_fread(zf, b, sizeof(b))) > 0) php_stream_write(stream, b, n); @@ -186,6 +263,7 @@ static int php_zip_extract_file(struct zip * za, char *dest, char *file, int fil efree(fullpath); efree(file_basename); efree(file_dirname_fullpath); + free(new_state.cwd); if (n<0) { return 0; @@ -195,6 +273,111 @@ static int php_zip_extract_file(struct zip * za, char *dest, char *file, int fil } /* }}} */ +static int php_zip_add_file(struct zip *za, const char *filename, int filename_len, + char *entry_name, int entry_name_len, long offset_start, long offset_len TSRMLS_DC) /* {{{ */ +{ + struct zip_source *zs; + int cur_idx; + char resolved_path[MAXPATHLEN]; + + + if (OPENBASEDIR_CHECKPATH(filename)) { + return -1; + } + + if (!expand_filepath(filename, resolved_path TSRMLS_CC)) { + return -1; + } + + zs = zip_source_file(za, resolved_path, offset_start, offset_len); + if (!zs) { + return -1; + } + + cur_idx = zip_name_locate(za, (const char *)entry_name, 0); + /* TODO: fix _zip_replace */ + if (cur_idx<0) { + /* reset the error */ + if (za->error.str) { + _zip_error_fini(&za->error); + } + _zip_error_init(&za->error); + } else { + if (zip_delete(za, cur_idx) == -1) { + zip_source_free(zs); + return -1; + } + } + + if (zip_add(za, entry_name, zs) == -1) { + return -1; + } else { + return 1; + } +} +/* }}} */ + +static int php_zip_parse_options(zval *options, long *remove_all_path, + char **remove_path, int *remove_path_len, char **add_path, int *add_path_len TSRMLS_DC) /* {{{ */ +{ + zval **option; + if (zend_hash_find(HASH_OF(options), "remove_all_path", sizeof("remove_all_path"), (void **)&option) == SUCCESS) { + long opt; + if (Z_TYPE_PP(option) != IS_LONG) { + zval tmp = **option; + zval_copy_ctor(&tmp); + convert_to_long(&tmp); + opt = Z_LVAL(tmp); + } else { + opt = Z_LVAL_PP(option); + } + *remove_all_path = opt; + } + + /* If I add more options, it would make sense to create a nice static struct and loop over it. */ + if (zend_hash_find(HASH_OF(options), "remove_path", sizeof("remove_path"), (void **)&option) == SUCCESS) { + if (Z_TYPE_PP(option) != IS_STRING) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "remove_path option expected to be a string"); + return -1; + } + + if (Z_STRLEN_PP(option) < 1) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Empty string given as remove_path option"); + return -1; + } + + if (Z_STRLEN_PP(option) >= MAXPATHLEN) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "remove_path string is too long (max: %i, %i given)", + MAXPATHLEN - 1, Z_STRLEN_PP(option)); + return -1; + } + *remove_path_len = Z_STRLEN_PP(option); + *remove_path = Z_STRVAL_PP(option); + } + + if (zend_hash_find(HASH_OF(options), "add_path", sizeof("add_path"), (void **)&option) == SUCCESS) { + if (Z_TYPE_PP(option) != IS_STRING) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "add_path option expected to be a string"); + return -1; + } + + if (Z_STRLEN_PP(option) < 1) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Empty string given as the add_path option"); + return -1; + } + + if (Z_STRLEN_PP(option) >= MAXPATHLEN) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "add_path string too long (max: %i, %i given)", + MAXPATHLEN - 1, Z_STRLEN_PP(option)); + return -1; + } + *add_path_len = Z_STRLEN_PP(option); + *add_path = Z_STRVAL_PP(option); + } + return 1; +} +/* }}} */ + /* {{{ REGISTER_ZIP_CLASS_CONST_LONG */ #define REGISTER_ZIP_CLASS_CONST_LONG(const_name, value) \ zend_declare_class_constant_long(zip_class_entry, const_name, sizeof(const_name)-1, (long)value TSRMLS_CC); @@ -216,13 +399,13 @@ static int php_zip_extract_file(struct zip * za, char *dest, char *file, int fil #define RETURN_SB(sb) \ { \ array_init(return_value); \ - add_assoc_string(return_value, "name", (char *)(sb)->name, 1); \ - add_assoc_long(return_value, "index", (long) (sb)->index); \ - add_assoc_long(return_value, "crc", (long) (sb)->crc); \ - add_assoc_long(return_value, "size", (long) (sb)->size); \ - add_assoc_long(return_value, "mtime", (long) (sb)->mtime); \ - add_assoc_long(return_value, "comp_size", (long) (sb)->comp_size); \ - add_assoc_long(return_value, "comp_method", (long) (sb)->comp_method); \ + add_ascii_assoc_string(return_value, "name", (char *)(sb)->name, 1); \ + add_ascii_assoc_long(return_value, "index", (long) (sb)->index); \ + add_ascii_assoc_long(return_value, "crc", (long) (sb)->crc); \ + add_ascii_assoc_long(return_value, "size", (long) (sb)->size); \ + add_ascii_assoc_long(return_value, "mtime", (long) (sb)->mtime); \ + add_ascii_assoc_long(return_value, "comp_size", (long) (sb)->comp_size); \ + add_ascii_assoc_long(return_value, "comp_method", (long) (sb)->comp_method); \ } /* }}} */ @@ -290,6 +473,7 @@ static zend_function_entry zip_functions[] = { /* }}} */ /* {{{ ZE2 OO definitions */ +#ifdef ZEND_ENGINE_2_1 static zend_class_entry *zip_class_entry; static zend_object_handlers zip_object_handlers; @@ -306,8 +490,10 @@ typedef struct _zip_prop_handler { int type; } zip_prop_handler; +#endif /* }}} */ +#ifdef ZEND_ENGINE_2_1 static void php_zip_register_prop_handler(HashTable *prop_handler, char *name, zip_read_int_t read_int_func, zip_read_const_char_t read_char_func, zip_read_const_char_from_ze_t read_char_from_obj_func, int rettype TSRMLS_DC) /* {{{ */ { zip_prop_handler hnd; @@ -434,7 +620,7 @@ static zval* php_zip_read_property(zval *object, zval *member, int type TSRMLS_D ret = php_zip_property_reader(obj, hnd, &retval, 1 TSRMLS_CC); if (ret == SUCCESS) { /* ensure we're creating a temporary variable */ - retval->refcount = 0; + Z_SET_REFCOUNT_P(retval, 0); } else { retval = EG(uninitialized_zval_ptr); } @@ -478,8 +664,8 @@ static int php_zip_has_property(zval *object, zval *member, int type TSRMLS_DC) if (type == 2) { retval = 1; } else if (php_zip_property_reader(obj, hnd, &tmp, 1 TSRMLS_CC) == SUCCESS) { - tmp->refcount = 1; - tmp->is_ref = 0; + Z_SET_REFCOUNT_P(tmp, 1); + Z_UNSET_ISREF_P(tmp); if (type == 1) { retval = zend_is_true(tmp); } else if (type == 0) { @@ -580,7 +766,14 @@ static zend_object_value php_zip_object_new(zend_class_entry *class_type TSRMLS_ intern->buffers_cnt = 0; intern->prop_handler = &zip_prop_handlers; +#if ((PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 1) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 1 && PHP_RELEASE_VERSION > 2)) zend_object_std_init(&intern->zo, class_type TSRMLS_CC); +#else + ALLOC_HASHTABLE(intern->zo.properties); + zend_hash_init(intern->zo.properties, 0, NULL, ZVAL_PTR_DTOR, 0); + intern->zo.ce = class_type; +#endif + zend_hash_copy(intern->zo.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); @@ -594,6 +787,7 @@ static zend_object_value php_zip_object_new(zend_class_entry *class_type TSRMLS_ return retval; } /* }}} */ +#endif /* {{{ Resource dtors */ @@ -636,7 +830,7 @@ static void php_zip_free_entry(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* }}}*/ /* reset macro */ -#undef zip + /* {{{ function prototypes */ static PHP_MINIT_FUNCTION(zip); static PHP_MSHUTDOWN_FUNCTION(zip); @@ -654,7 +848,7 @@ zend_module_entry zip_module_entry = { NULL, NULL, PHP_MINFO(zip), - "2.0.0", + PHP_ZIP_VERSION_STRING, STANDARD_MODULE_PROPERTIES }; /* }}} */ @@ -663,7 +857,6 @@ zend_module_entry zip_module_entry = { ZEND_GET_MODULE(zip) #endif /* set macro */ -#define zip php_ziplib__zip /* {{{ proto resource zip_open(string filename) Create new zip using source uri for output */ @@ -694,7 +887,7 @@ static PHP_NAMED_FUNCTION(zif_zip_open) rsrc_int = (zip_rsrc *)emalloc(sizeof(zip_rsrc)); - rsrc_int->za = zip_open(filename, 0, &err); + rsrc_int->za = zip_open(resolved_path, 0, &err); if (rsrc_int->za == NULL) { efree(rsrc_int); RETURN_LONG((long)err); @@ -749,7 +942,7 @@ static PHP_NAMED_FUNCTION(zif_zip_read) if (ret != 0) { efree(zr_rsrc); - RETURN_LONG((long)ret); + RETURN_FALSE; } zr_rsrc->zf = zip_fopen_index(rsrc_int->za, rsrc_int->index_current, 0); @@ -944,6 +1137,7 @@ static PHP_NAMED_FUNCTION(zif_zip_entry_compressionmethod) } /* }}} */ +#ifdef ZEND_ENGINE_2_1 /* {{{ proto mixed ZipArchive::open(string source [, int flags]) Create new zip using source uri for output, return TRUE on success or the error code */ static ZIPARCHIVE_METHOD(open) @@ -991,6 +1185,7 @@ static ZIPARCHIVE_METHOD(open) efree(ze_obj->filename); ze_obj->filename = NULL; } + intern = zip_open(resolved_path, flags, &err); if (!intern || err) { RETURN_LONG((long)err); @@ -1031,6 +1226,28 @@ static ZIPARCHIVE_METHOD(close) } /* }}} */ +/* {{{ proto string ZipArchive::getStatusString() + * Returns the status error message, system and/or zip messages */ +static ZIPARCHIVE_METHOD(getStatusString) +{ + struct zip *intern; + zval *this = getThis(); + int zep, syp, len; + char error_string[128]; + + if (!this) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, this); + + zip_error_get(intern, &zep, &syp); + + len = zip_error_to_str(error_string, 128, zep, syp); + RETVAL_STRINGL(error_string, len, 1); +} +/* }}} */ + /* {{{ proto bool ZipArchive::createEmptyDir(string dirname) Returns the index of the entry named filename in the archive */ static ZIPARCHIVE_METHOD(addEmptyDir) @@ -1071,17 +1288,10 @@ static ZIPARCHIVE_METHOD(addEmptyDir) if (idx >= 0) { RETVAL_FALSE; } else { - /* reset the error */ - if (intern->error.str) { - _zip_error_fini(&intern->error); - } - _zip_error_init(&intern->error); - if (zip_add_dir(intern, (const char *)s) == -1) { RETVAL_FALSE; - } else { - RETVAL_TRUE; } + RETVAL_TRUE; } if (s != dirname) { @@ -1100,10 +1310,7 @@ static ZIPARCHIVE_METHOD(addFile) int filename_len; char *entry_name = NULL; int entry_name_len = 0; - struct zip_source *zs; long offset_start = 0, offset_len = 0; - int cur_idx; - char resolved_path[MAXPATHLEN]; if (!this) { RETURN_FALSE; @@ -1126,35 +1333,8 @@ static ZIPARCHIVE_METHOD(addFile) entry_name_len = filename_len; } - if (OPENBASEDIR_CHECKPATH(filename)) { - RETURN_FALSE; - } - - if (!expand_filepath(filename, resolved_path TSRMLS_CC)) { - RETURN_FALSE; - } - - zs = zip_source_file(intern, resolved_path, 0, 0); - if (!zs) { - RETURN_FALSE; - } - - cur_idx = zip_name_locate(intern, (const char *)entry_name, 0); - /* TODO: fix _zip_replace */ - if (cur_idx<0) { - /* reset the error */ - if (intern->error.str) { - _zip_error_fini(&intern->error); - } - _zip_error_init(&intern->error); - - } else { - if (zip_delete(intern, cur_idx) == -1) { - RETURN_FALSE; - } - } - - if (zip_add(intern, entry_name, zs) == -1) { + if (php_zip_add_file(intern, filename, filename_len, + entry_name, entry_name_len, 0, 0 TSRMLS_CC) < 0) { RETURN_FALSE; } else { RETURN_TRUE; @@ -1206,14 +1386,7 @@ static ZIPARCHIVE_METHOD(addFromString) cur_idx = zip_name_locate(intern, (const char *)name, 0); /* TODO: fix _zip_replace */ - if (cur_idx<0) { - /* reset the error */ - if (intern->error.str) { - _zip_error_fini(&intern->error); - } - _zip_error_init(&intern->error); - - } else { + if (cur_idx >= 0) { if (zip_delete(intern, cur_idx) == -1) { RETURN_FALSE; } @@ -1310,15 +1483,10 @@ static ZIPARCHIVE_METHOD(locateName) idx = (long)zip_name_locate(intern, (const char *)name, flags); - if (idx<0) { - /* reset the error */ - if (intern->error.str) { - _zip_error_fini(&intern->error); - } - _zip_error_init(&intern->error); - RETURN_FALSE; - } else { + if (idx >= 0) { RETURN_LONG(idx); + } else { + RETURN_FALSE; } } /* }}} */ @@ -1979,6 +2147,7 @@ static ZIPARCHIVE_METHOD(getStream) static zend_function_entry zip_class_functions[] = { ZIPARCHIVE_ME(open, NULL, ZEND_ACC_PUBLIC) ZIPARCHIVE_ME(close, NULL, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getStatusString, NULL, ZEND_ACC_PUBLIC) ZIPARCHIVE_ME(addEmptyDir, NULL, ZEND_ACC_PUBLIC) ZIPARCHIVE_ME(addFromString, NULL, ZEND_ACC_PUBLIC) ZIPARCHIVE_ME(addFile, NULL, ZEND_ACC_PUBLIC) @@ -2007,11 +2176,10 @@ static zend_function_entry zip_class_functions[] = { {NULL, NULL, NULL} }; /* }}} */ +#endif /* {{{ PHP_MINIT_FUNCTION */ -#undef zip static PHP_MINIT_FUNCTION(zip) -#define zip php_ziplib__zip { zend_class_entry ce; @@ -2092,9 +2260,7 @@ static PHP_MINIT_FUNCTION(zip) /* {{{ PHP_MSHUTDOWN_FUNCTION */ -#undef zip static PHP_MSHUTDOWN_FUNCTION(zip) -#define zip php_ziplib__zip { zend_hash_destroy(&zip_prop_handlers); php_unregister_url_stream_wrapper("zip" TSRMLS_CC); @@ -2105,7 +2271,6 @@ static PHP_MSHUTDOWN_FUNCTION(zip) /* {{{ PHP_MINFO_FUNCTION */ -#undef zip static PHP_MINFO_FUNCTION(zip) { php_info_print_table_start(); @@ -2113,7 +2278,7 @@ static PHP_MINFO_FUNCTION(zip) php_info_print_table_row(2, "Zip", "enabled"); php_info_print_table_row(2, "Extension Version","$Id$"); php_info_print_table_row(2, "Zip version", PHP_ZIP_VERSION_STRING); - php_info_print_table_row(2, "Libzip version", "0.8.0-compatible"); + php_info_print_table_row(2, "Libzip version", "0.9.0"); php_info_print_table_end(); } |