summaryrefslogtreecommitdiffstats
path: root/chrome/common/zip.cc
diff options
context:
space:
mode:
authoraa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-01 02:53:00 +0000
committeraa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-01 02:53:00 +0000
commit4777bc566e7931c891c4fa16261f04ed940ec9d5 (patch)
tree400f33237743c863f4c95883b014ea4890141df2 /chrome/common/zip.cc
parent08ff58c867a17b9fe7dd6988d1eac83cb3b06df0 (diff)
downloadchromium_src-4777bc566e7931c891c4fa16261f04ed940ec9d5.zip
chromium_src-4777bc566e7931c891c4fa16261f04ed940ec9d5.tar.gz
chromium_src-4777bc566e7931c891c4fa16261f04ed940ec9d5.tar.bz2
Implements a Zip() utility function. Refactor existing
Unzip-relatedness into shared locations. Review URL: http://codereview.chromium.org/118028 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17305 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common/zip.cc')
-rw-r--r--chrome/common/zip.cc299
1 files changed, 299 insertions, 0 deletions
diff --git a/chrome/common/zip.cc b/chrome/common/zip.cc
new file mode 100644
index 0000000..8ffd12c
--- /dev/null
+++ b/chrome/common/zip.cc
@@ -0,0 +1,299 @@
+// Copyright (c) 2009 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/zip.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"
+#include "third_party/zlib/contrib/minizip/zip.h"
+#if defined(OS_WIN)
+#include "third_party/zlib/contrib/minizip/iowin32.h"
+#endif
+
+static const int kZipMaxPath = 256;
+static const int kZipBufSize = 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) {
+ 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 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;
+ }
+
+ // We can't rely on parent directory entries being specified in the zip, so we
+ // make sure they are created.
+ 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, flags) != 0)
+ return false;
+
+ bool ret = true;
+ int num_bytes = 0;
+ char buf[kZipBufSize];
+ do {
+ num_bytes = unzReadCurrentFile(zip_file, buf, kZipBufSize);
+ 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* ZipOpenFunc(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
+
+bool Unzip(const FilePath& src_file, const FilePath& dest_dir) {
+#if defined(OS_WIN)
+ zlib_filefunc_def zip_funcs;
+ fill_win32_filefunc(&zip_funcs);
+ zip_funcs.zopen_file = ZipOpenFunc;
+#endif
+
+#if defined(OS_POSIX)
+ std::string src_file_str = src_file.value();
+ unzFile zip_file = unzOpen(src_file_str.c_str());
+#elif defined(OS_WIN)
+ std::string src_file_str = WideToUTF8(src_file.value());
+ unzFile zip_file = unzOpen2(src_file_str.c_str(), &zip_funcs);
+#endif
+ if (!zip_file) {
+ LOG(WARNING) << "couldn't create file " << src_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 zip " << src_file_str;
+ return false;
+ }
+ bool ret = true;
+ for (unsigned int i = 0; i < zip_info.number_entry; ++i) {
+ if (!ExtractCurrentFile(zip_file, dest_dir)) {
+ ret = false;
+ break;
+ }
+
+ if (i + 1 < zip_info.number_entry) {
+ err = unzGoToNextFile(zip_file);
+ if (err != UNZ_OK) {
+ LOG(WARNING) << "error %d in unzGoToNextFile";
+ ret = false;
+ break;
+ }
+ }
+ }
+ unzClose(zip_file);
+ return ret;
+}
+
+static bool AddFileToZip(zipFile zip_file, const FilePath& src_dir) {
+ net::FileStream stream;
+ int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ;
+ if (stream.Open(src_dir, flags) != 0) {
+ LOG(ERROR) << "Could not open stream for path "
+ << WideToASCII(src_dir.ToWStringHack());
+ return false;
+ }
+
+ int num_bytes;
+ char buf[kZipBufSize];
+ do {
+ num_bytes = stream.Read(buf, kZipBufSize, NULL);
+ if (num_bytes > 0) {
+ if (ZIP_OK != zipWriteInFileInZip(zip_file, buf, num_bytes)) {
+ LOG(ERROR) << "Could not write data to zip for path "
+ << WideToASCII(src_dir.ToWStringHack());
+ return false;
+ }
+ }
+ } while (num_bytes > 0);
+
+ return true;
+}
+
+static bool AddEntryToZip(zipFile zip_file, const FilePath& path,
+ const FilePath& root_path) {
+#if defined(OS_WIN)
+ std::string str_path =
+ WideToUTF8(path.value().substr(root_path.value().length() + 1));
+ ReplaceSubstringsAfterOffset(&str_path, 0u, "\\", "/");
+#else
+ std::string str_path = path.value().substr(root_path.value().length() + 1);
+#endif
+
+ bool is_directory = file_util::DirectoryExists(path);
+ if (is_directory)
+ str_path += "/";
+
+ if (ZIP_OK != zipOpenNewFileInZip(
+ zip_file, str_path.c_str(),
+ NULL, NULL, 0u, NULL, 0u, NULL, // file info, extrafield local, length,
+ // extrafield global, length, comment
+ Z_DEFLATED, Z_DEFAULT_COMPRESSION)) {
+ LOG(ERROR) << "Could not open zip file entry " << str_path;
+ return false;
+ }
+
+ bool success = true;
+ if (!is_directory) {
+ success = AddFileToZip(zip_file, path);
+ }
+
+ if (ZIP_OK != zipCloseFileInZip(zip_file)) {
+ LOG(ERROR) << "Could not close zip file entry " << str_path;
+ return false;
+ }
+
+ return success;
+}
+
+bool Zip(const FilePath& src_dir, const FilePath& dest_file) {
+ DCHECK(file_util::DirectoryExists(src_dir));
+
+#if defined(OS_WIN)
+ zlib_filefunc_def zip_funcs;
+ fill_win32_filefunc(&zip_funcs);
+ zip_funcs.zopen_file = ZipOpenFunc;
+#endif
+
+#if defined(OS_POSIX)
+ std::string dest_file_str = dest_file.value();
+ std::string src_dir_str = src_dir.value();
+ zipFile zip_file = zipOpen(src_dir_str.c_str(), APPEND_STATUS_CREATE);
+#elif defined(OS_WIN)
+ std::string dest_file_str = WideToUTF8(dest_file.value());
+ zipFile zip_file = zipOpen2(dest_file_str.c_str(), APPEND_STATUS_CREATE,
+ NULL, // global comment
+ &zip_funcs);
+#endif
+
+ if (!zip_file) {
+ LOG(WARNING) << "couldn't create file " << dest_file_str;
+ return false;
+ }
+
+ bool success = true;
+ file_util::FileEnumerator file_enumerator(
+ src_dir, true, // recursive
+ file_util::FileEnumerator::FILES_AND_DIRECTORIES);
+ for (FilePath path = file_enumerator.Next(); !path.value().empty();
+ path = file_enumerator.Next()) {
+ if (!AddEntryToZip(zip_file, path, src_dir)) {
+ success = false;
+ return false;
+ }
+ }
+
+ if (ZIP_OK != zipClose(zip_file, NULL)) { // global comment
+ LOG(ERROR) << "Error closing zip file " << dest_file_str;
+ return false;
+ }
+
+ return success;
+}