summaryrefslogtreecommitdiffstats
path: root/chrome/common
diff options
context:
space:
mode:
authorerikkay@google.com <erikkay@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-12-31 17:02:20 +0000
committererikkay@google.com <erikkay@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-12-31 17:02:20 +0000
commit02a9d6406c7047e6a5b81ddf2e1b8473ba1415c1 (patch)
treedbe1b3c08d4131ec53e2eff7d060d271bb43601e /chrome/common
parent3ba7f3741449eca4e44b6424ff6671adb168fbf5 (diff)
downloadchromium_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.scons1
-rw-r--r--chrome/common/common.vcproj24
-rw-r--r--chrome/common/unzip.cc201
-rw-r--r--chrome/common/unzip.h20
-rw-r--r--chrome/common/unzip_unittest.cc94
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
+