diff options
author | erikkay@google.com <erikkay@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-12-31 17:02:20 +0000 |
---|---|---|
committer | erikkay@google.com <erikkay@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-12-31 17:02:20 +0000 |
commit | 02a9d6406c7047e6a5b81ddf2e1b8473ba1415c1 (patch) | |
tree | dbe1b3c08d4131ec53e2eff7d060d271bb43601e /chrome/common | |
parent | 3ba7f3741449eca4e44b6424ff6671adb168fbf5 (diff) | |
download | chromium_src-02a9d6406c7047e6a5b81ddf2e1b8473ba1415c1.zip chromium_src-02a9d6406c7047e6a5b81ddf2e1b8473ba1415c1.tar.gz chromium_src-02a9d6406c7047e6a5b81ddf2e1b8473ba1415c1.tar.bz2 |
Add generic unzip support. This is to be used by extensions.
depends on http://codereview.chromium.org/16605
Review URL: http://codereview.chromium.org/16487
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@7530 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common')
-rw-r--r-- | chrome/common/common.scons | 1 | ||||
-rw-r--r-- | chrome/common/common.vcproj | 24 | ||||
-rw-r--r-- | chrome/common/unzip.cc | 201 | ||||
-rw-r--r-- | chrome/common/unzip.h | 20 | ||||
-rw-r--r-- | chrome/common/unzip_unittest.cc | 94 |
5 files changed, 332 insertions, 8 deletions
diff --git a/chrome/common/common.scons b/chrome/common/common.scons index d16afc5..cfdc390 100644 --- a/chrome/common/common.scons +++ b/chrome/common/common.scons @@ -74,6 +74,7 @@ input_files.extend([ 'task_queue.cc', 'throb_animation.cc', 'thumbnail_score.cc', + 'unzip.cc', 'visitedlink_common.cc', ]) diff --git a/chrome/common/common.vcproj b/chrome/common/common.vcproj index 074d1cd..159348f 100644 --- a/chrome/common/common.vcproj +++ b/chrome/common/common.vcproj @@ -153,19 +153,19 @@ > </File> <File - RelativePath=".\gfx\chrome_canvas_win.cc" + RelativePath=".\gfx\chrome_canvas.h" > </File> <File - RelativePath=".\gfx\chrome_canvas.h" + RelativePath=".\gfx\chrome_canvas_win.cc" > </File> <File - RelativePath=".\gfx\chrome_font_win.cc" + RelativePath=".\gfx\chrome_font.h" > </File> <File - RelativePath=".\gfx\chrome_font.h" + RelativePath=".\gfx\chrome_font_win.cc" > </File> <File @@ -225,10 +225,6 @@ Name="ipc" > <File - RelativePath=".\ipc_channel_win.cc" - > - </File> - <File RelativePath=".\ipc_channel.h" > </File> @@ -241,6 +237,10 @@ > </File> <File + RelativePath=".\ipc_channel_win.cc" + > + </File> + <File RelativePath=".\ipc_logging.cc" > </File> @@ -694,6 +694,14 @@ > </File> <File + RelativePath=".\unzip.cc" + > + </File> + <File + RelativePath=".\unzip.h" + > + </File> + <File RelativePath=".\visitedlink_common.cc" > </File> diff --git a/chrome/common/unzip.cc b/chrome/common/unzip.cc new file mode 100644 index 0000000..35f262c --- /dev/null +++ b/chrome/common/unzip.cc @@ -0,0 +1,201 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/unzip.h" + +#include "base/file_util.h" +#include "base/string_util.h" +#include "net/base/file_stream.h" +#include "third_party/zlib/contrib/minizip/unzip.h" +#if defined(OS_WIN) +#include "third_party/zlib/contrib/minizip/iowin32.h" +#endif + +static const int kZipMaxPath = 256; +static const int kUnzipBufSize = 8192; + +// Extract the 'current' selected file from the zip into dest_dir. +// Output filename is stored in out_file. Returns true on success. +static bool ExtractCurrentFile(unzFile zip_file, + const FilePath& dest_dir, + FilePath* out_file) { + char filename_inzip[kZipMaxPath] = {0}; + unz_file_info file_info; + int err = unzGetCurrentFileInfo(zip_file, &file_info, filename_inzip, + sizeof(filename_inzip), NULL, 0, NULL, 0); + if (err != UNZ_OK) + return false; + if (filename_inzip[0] == '\0') + return false; + + err = unzOpenCurrentFile(zip_file); + if (err != UNZ_OK) + return false; + + FilePath::StringType filename; + std::vector<FilePath::StringType> filename_parts; +#if defined(OS_WIN) + filename = UTF8ToWide(filename_inzip); +#elif defined(OS_POSIX) + filename = filename_inzip; +#endif + SplitString(filename, '/', &filename_parts); + + FilePath dest_file(dest_dir); + std::vector<FilePath::StringType>::iterator iter; + for (iter = filename_parts.begin(); iter != filename_parts.end(); ++iter) + dest_file = dest_file.Append(*iter); + if (out_file) + *out_file = dest_file; + // If this is a directory, just create it and return. + if (filename_inzip[strlen(filename_inzip) - 1] == '/') { + if (!file_util::CreateDirectory(dest_file)) + return false; + return true; + } + // TODO(erikkay): Can we always count on the directory entry coming before a + // file in that directory? If so, then these three lines can be removed. + FilePath dir = dest_file.DirName(); + if (!file_util::CreateDirectory(dir)) + return false; + + net::FileStream stream; + int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE; + if (stream.Open(dest_file.ToWStringHack(), flags) != 0) + return false; + + bool ret = true; + int num_bytes = 0; + char buf[kUnzipBufSize]; + do { + num_bytes = unzReadCurrentFile(zip_file, buf, kUnzipBufSize); + if (num_bytes < 0) { + // If num_bytes < 0, then it's a specific UNZ_* error code. + // While we're not currently handling these codes specifically, save + // it away in case we want to in the future. + err = num_bytes; + break; + } + if (num_bytes > 0) { + if (num_bytes != stream.Write(buf, num_bytes, NULL)) { + ret = false; + break; + } + } + } while (num_bytes > 0); + + stream.Close(); + if (err == UNZ_OK) + err = unzCloseCurrentFile(zip_file); + else + unzCloseCurrentFile(zip_file); // Don't lose the original error code. + if (err != UNZ_OK) + ret = false; + return ret; +} + +#if defined(OS_WIN) +typedef struct { + HANDLE hf; + int error; +} WIN32FILE_IOWIN; + +// This function is derived from third_party/minizip/iowin32.c. +// Its only difference is that it treats the char* as UTF8 and +// uses the Unicode version of CreateFile. +static void* UnzipOpenFunc(void *opaque, const char* filename, int mode) { + DWORD desired_access, creation_disposition; + DWORD share_mode, flags_and_attributes; + HANDLE file = 0; + void* ret = NULL; + + desired_access = share_mode = flags_and_attributes = 0; + + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) { + desired_access = GENERIC_READ; + creation_disposition = OPEN_EXISTING; + share_mode = FILE_SHARE_READ; + } else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) { + desired_access = GENERIC_WRITE | GENERIC_READ; + creation_disposition = OPEN_EXISTING; + } else if (mode & ZLIB_FILEFUNC_MODE_CREATE) { + desired_access = GENERIC_WRITE | GENERIC_READ; + creation_disposition = CREATE_ALWAYS; + } + + std::wstring filename_wstr = UTF8ToWide(filename); + if ((filename != NULL) && (desired_access != 0)) { + file = CreateFile(filename_wstr.c_str(), desired_access, share_mode, + NULL, creation_disposition, flags_and_attributes, NULL); + } + + if (file == INVALID_HANDLE_VALUE) + file = NULL; + + if (file != NULL) { + WIN32FILE_IOWIN file_ret; + file_ret.hf = file; + file_ret.error = 0; + ret = malloc(sizeof(WIN32FILE_IOWIN)); + if (ret == NULL) + CloseHandle(file); + else + *(static_cast<WIN32FILE_IOWIN*>(ret)) = file_ret; + } + return ret; +} +#endif + +// TODO(erikkay): Make this function asynchronous so that a large zip file +// won't starve the thread it's running on. This won't be entirely possible +// since reads need to be synchronous, but we can at least make writes async. +bool Unzip(const FilePath& zip_path, const FilePath& dest_dir, + std::vector<FilePath>* files) { +#if defined(OS_WIN) + zlib_filefunc_def unzip_funcs; + fill_win32_filefunc(&unzip_funcs); + unzip_funcs.zopen_file = UnzipOpenFunc; +#endif + +#if defined(OS_POSIX) + std::string zip_file_str = zip_path.value(); + unzFile zip_file = unzOpen(zip_file_str.c_str()); +#elif defined(OS_WIN) + std::string zip_file_str = WideToUTF8(zip_path.value()); + unzFile zip_file = unzOpen2(zip_file_str.c_str(), &unzip_funcs); +#endif + if (!zip_file) { + LOG(WARNING) << "couldn't open extension file " << zip_file_str; + return false; + } + unz_global_info zip_info; + int err; + err = unzGetGlobalInfo(zip_file, &zip_info); + if (err != UNZ_OK) { + LOG(WARNING) << "couldn't open extension file " << zip_file_str; + return false; + } + bool ret = true; + for (unsigned int i = 0; i < zip_info.number_entry; ++i) { + FilePath dest_file; + if (!ExtractCurrentFile(zip_file, dest_dir, &dest_file)) { + ret = false; + break; + } + if (files) + files->push_back(dest_file); + + if (i + 1 < zip_info.number_entry) { + err = unzGoToNextFile(zip_file); + if (err != UNZ_OK) { + LOG(WARNING) << "error %d in unzGoToNextFile"; + ret = false; + break; + } + } + } + unzCloseCurrentFile(zip_file); + return ret; +} + diff --git a/chrome/common/unzip.h b/chrome/common/unzip.h new file mode 100644 index 0000000..fc9aa64 --- /dev/null +++ b/chrome/common/unzip.h @@ -0,0 +1,20 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_UNZIP_H_ +#define CHROME_COMMON_UNZIP_H_ + +#include <vector> + +#include "base/file_path.h" + +// Unzip the contents of zip_file into dest_dir. The complete paths of all +// created files and directories are added to files if it is non-NULL. +// Returns true on success. Does not clean up dest_dir on failure. +// Does not support encrypted or password protected zip files. +bool Unzip(const FilePath& zip_file, const FilePath& dest_dir, + std::vector<FilePath>* files); + +#endif // CHROME_COMMON_UNZIP_H_ + diff --git a/chrome/common/unzip_unittest.cc b/chrome/common/unzip_unittest.cc new file mode 100644 index 0000000..f572811 --- /dev/null +++ b/chrome/common/unzip_unittest.cc @@ -0,0 +1,94 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <set> + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/unzip.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace { + +// Make the test a PlatformTest to setup autorelease pools properly on Mac. +class UnzipTest : public PlatformTest { + protected: + virtual void SetUp() { + PlatformTest::SetUp(); + + ASSERT_TRUE(file_util::CreateNewTempDirectory( + FILE_PATH_LITERAL("unzip_unittest_"), &test_dir_)); + + FilePath zip_path(test_dir_.Append(FILE_PATH_LITERAL("test"))); + zip_contents_.insert(zip_path); + zip_contents_.insert(zip_path.Append(FILE_PATH_LITERAL("foo.txt"))); + zip_path = zip_path.Append(FILE_PATH_LITERAL("foo")); + zip_contents_.insert(zip_path); + zip_contents_.insert(zip_path.Append(FILE_PATH_LITERAL("bar.txt"))); + zip_path = zip_path.Append(FILE_PATH_LITERAL("bar")); + zip_contents_.insert(zip_path); + zip_contents_.insert(zip_path.Append(FILE_PATH_LITERAL("baz.txt"))); + zip_contents_.insert(zip_path.Append(FILE_PATH_LITERAL("quux.txt"))); + zip_path = zip_path.Append(FILE_PATH_LITERAL("baz")); + zip_contents_.insert(zip_path); + } + + virtual void TearDown() { + PlatformTest::TearDown(); + // Clean up test directory + ASSERT_TRUE(file_util::Delete(test_dir_, true)); + ASSERT_FALSE(file_util::PathExists(test_dir_)); + } + + void TestZipFile(const FilePath::StringType& filename) { + FilePath test_dir; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir)); + test_dir = test_dir.Append(FILE_PATH_LITERAL("unzip")); + FilePath path = test_dir.Append(filename); + + ASSERT_TRUE(file_util::PathExists(path)) << "no file " << path.value(); + std::vector<FilePath> out_files; + ASSERT_TRUE(Unzip(path, test_dir_, &out_files)); + + file_util::FileEnumerator files(test_dir_, true, + file_util::FileEnumerator::FILES_AND_DIRECTORIES); + FilePath next_path = files.Next(); + size_t count = 0; + while (!next_path.value().empty()) { + EXPECT_EQ(zip_contents_.count(next_path), 1U) << + "Couldn't find " << next_path.value(); + count++; + next_path = files.Next(); + } + EXPECT_EQ(count, zip_contents_.size()); + EXPECT_EQ(count, out_files.size()); + std::vector<FilePath>::iterator iter; + for (iter = out_files.begin(); iter != out_files.end(); ++iter) { + EXPECT_EQ(zip_contents_.count(*iter), 1U) << + "Couldn't find " << (*iter).value(); + } + } + + // the path to temporary directory used to contain the test operations + FilePath test_dir_; + + // hard-coded contents of a known zip file + std::set<FilePath> zip_contents_; +}; + + +TEST_F(UnzipTest, Unzip) { + TestZipFile(FILE_PATH_LITERAL("test.zip")); +} + +TEST_F(UnzipTest, UnzipUncompressed) { + TestZipFile(FILE_PATH_LITERAL("test_nocompress.zip")); +} + +} // namespace + |