Index: ext/zip/php_zip.c =================================================================== RCS file: /repository/php-src/ext/zip/php_zip.c,v retrieving revision 1.1.2.43 retrieving revision 1.1.2.44 diff -u -p -r1.1.2.43 -r1.1.2.44 --- ext/zip/php_zip.c 18 Jan 2008 00:51:38 -0000 1.1.2.43 +++ ext/zip/php_zip.c 23 Oct 2008 16:13:50 -0000 1.1.2.44 @@ -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; + + /* 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 (file_len >= MAXPATHLEN || zip_stat(za, file, 0, &sb) != 0) { + 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 z 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 z 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 z efree(fullpath); efree(file_basename); efree(file_dirname_fullpath); + free(new_state.cwd); if (n<0) { return 0;