| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/common/unzip.h" | |
| 6 | |
| 7 #include "base/file_util.h" | |
| 8 #include "base/string_util.h" | |
| 9 #include "net/base/file_stream.h" | |
| 10 #include "third_party/zlib/contrib/minizip/unzip.h" | |
| 11 #if defined(OS_WIN) | |
| 12 #include "third_party/zlib/contrib/minizip/iowin32.h" | |
| 13 #endif | |
| 14 | |
| 15 static const int kZipMaxPath = 256; | |
| 16 static const int kUnzipBufSize = 8192; | |
| 17 | |
| 18 // Extract the 'current' selected file from the zip into dest_dir. | |
| 19 // Output filename is stored in out_file. Returns true on success. | |
| 20 static bool ExtractCurrentFile(unzFile zip_file, | |
| 21 const FilePath& dest_dir, | |
| 22 FilePath* out_file) { | |
| 23 char filename_inzip[kZipMaxPath] = {0}; | |
| 24 unz_file_info file_info; | |
| 25 int err = unzGetCurrentFileInfo(zip_file, &file_info, filename_inzip, | |
| 26 sizeof(filename_inzip), NULL, 0, NULL, 0); | |
| 27 if (err != UNZ_OK) | |
| 28 return false; | |
| 29 if (filename_inzip[0] == '\0') | |
| 30 return false; | |
| 31 | |
| 32 err = unzOpenCurrentFile(zip_file); | |
| 33 if (err != UNZ_OK) | |
| 34 return false; | |
| 35 | |
| 36 FilePath::StringType filename; | |
| 37 std::vector<FilePath::StringType> filename_parts; | |
| 38 #if defined(OS_WIN) | |
| 39 filename = UTF8ToWide(filename_inzip); | |
| 40 #elif defined(OS_POSIX) | |
| 41 filename = filename_inzip; | |
| 42 #endif | |
| 43 SplitString(filename, '/', &filename_parts); | |
| 44 | |
| 45 FilePath dest_file(dest_dir); | |
| 46 std::vector<FilePath::StringType>::iterator iter; | |
| 47 for (iter = filename_parts.begin(); iter != filename_parts.end(); ++iter) | |
| 48 dest_file = dest_file.Append(*iter); | |
| 49 if (out_file) | |
| 50 *out_file = dest_file; | |
| 51 // If this is a directory, just create it and return. | |
| 52 if (filename_inzip[strlen(filename_inzip) - 1] == '/') { | |
| 53 if (!file_util::CreateDirectory(dest_file)) | |
| 54 return false; | |
| 55 return true; | |
| 56 } | |
| 57 // TODO(erikkay): Can we always count on the directory entry coming before a | |
| 58 // file in that directory? If so, then these three lines can be removed. | |
| 59 FilePath dir = dest_file.DirName(); | |
| 60 if (!file_util::CreateDirectory(dir)) | |
| 61 return false; | |
| 62 | |
| 63 net::FileStream stream; | |
| 64 int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE; | |
| 65 if (stream.Open(dest_file, flags) != 0) | |
| 66 return false; | |
| 67 | |
| 68 bool ret = true; | |
| 69 int num_bytes = 0; | |
| 70 char buf[kUnzipBufSize]; | |
| 71 do { | |
| 72 num_bytes = unzReadCurrentFile(zip_file, buf, kUnzipBufSize); | |
| 73 if (num_bytes < 0) { | |
| 74 // If num_bytes < 0, then it's a specific UNZ_* error code. | |
| 75 // While we're not currently handling these codes specifically, save | |
| 76 // it away in case we want to in the future. | |
| 77 err = num_bytes; | |
| 78 break; | |
| 79 } | |
| 80 if (num_bytes > 0) { | |
| 81 if (num_bytes != stream.Write(buf, num_bytes, NULL)) { | |
| 82 ret = false; | |
| 83 break; | |
| 84 } | |
| 85 } | |
| 86 } while (num_bytes > 0); | |
| 87 | |
| 88 stream.Close(); | |
| 89 if (err == UNZ_OK) | |
| 90 err = unzCloseCurrentFile(zip_file); | |
| 91 else | |
| 92 unzCloseCurrentFile(zip_file); // Don't lose the original error code. | |
| 93 if (err != UNZ_OK) | |
| 94 ret = false; | |
| 95 return ret; | |
| 96 } | |
| 97 | |
| 98 #if defined(OS_WIN) | |
| 99 typedef struct { | |
| 100 HANDLE hf; | |
| 101 int error; | |
| 102 } WIN32FILE_IOWIN; | |
| 103 | |
| 104 // This function is derived from third_party/minizip/iowin32.c. | |
| 105 // Its only difference is that it treats the char* as UTF8 and | |
| 106 // uses the Unicode version of CreateFile. | |
| 107 static void* UnzipOpenFunc(void *opaque, const char* filename, int mode) { | |
| 108 DWORD desired_access, creation_disposition; | |
| 109 DWORD share_mode, flags_and_attributes; | |
| 110 HANDLE file = 0; | |
| 111 void* ret = NULL; | |
| 112 | |
| 113 desired_access = share_mode = flags_and_attributes = 0; | |
| 114 | |
| 115 if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) { | |
| 116 desired_access = GENERIC_READ; | |
| 117 creation_disposition = OPEN_EXISTING; | |
| 118 share_mode = FILE_SHARE_READ; | |
| 119 } else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) { | |
| 120 desired_access = GENERIC_WRITE | GENERIC_READ; | |
| 121 creation_disposition = OPEN_EXISTING; | |
| 122 } else if (mode & ZLIB_FILEFUNC_MODE_CREATE) { | |
| 123 desired_access = GENERIC_WRITE | GENERIC_READ; | |
| 124 creation_disposition = CREATE_ALWAYS; | |
| 125 } | |
| 126 | |
| 127 std::wstring filename_wstr = UTF8ToWide(filename); | |
| 128 if ((filename != NULL) && (desired_access != 0)) { | |
| 129 file = CreateFile(filename_wstr.c_str(), desired_access, share_mode, | |
| 130 NULL, creation_disposition, flags_and_attributes, NULL); | |
| 131 } | |
| 132 | |
| 133 if (file == INVALID_HANDLE_VALUE) | |
| 134 file = NULL; | |
| 135 | |
| 136 if (file != NULL) { | |
| 137 WIN32FILE_IOWIN file_ret; | |
| 138 file_ret.hf = file; | |
| 139 file_ret.error = 0; | |
| 140 ret = malloc(sizeof(WIN32FILE_IOWIN)); | |
| 141 if (ret == NULL) | |
| 142 CloseHandle(file); | |
| 143 else | |
| 144 *(static_cast<WIN32FILE_IOWIN*>(ret)) = file_ret; | |
| 145 } | |
| 146 return ret; | |
| 147 } | |
| 148 #endif | |
| 149 | |
| 150 // TODO(erikkay): Make this function asynchronous so that a large zip file | |
| 151 // won't starve the thread it's running on. This won't be entirely possible | |
| 152 // since reads need to be synchronous, but we can at least make writes async. | |
| 153 bool Unzip(const FilePath& zip_path, const FilePath& dest_dir, | |
| 154 std::vector<FilePath>* files) { | |
| 155 #if defined(OS_WIN) | |
| 156 zlib_filefunc_def unzip_funcs; | |
| 157 fill_win32_filefunc(&unzip_funcs); | |
| 158 unzip_funcs.zopen_file = UnzipOpenFunc; | |
| 159 #endif | |
| 160 | |
| 161 #if defined(OS_POSIX) | |
| 162 std::string zip_file_str = zip_path.value(); | |
| 163 unzFile zip_file = unzOpen(zip_file_str.c_str()); | |
| 164 #elif defined(OS_WIN) | |
| 165 std::string zip_file_str = WideToUTF8(zip_path.value()); | |
| 166 unzFile zip_file = unzOpen2(zip_file_str.c_str(), &unzip_funcs); | |
| 167 #endif | |
| 168 if (!zip_file) { | |
| 169 LOG(WARNING) << "couldn't open extension file " << zip_file_str; | |
| 170 return false; | |
| 171 } | |
| 172 unz_global_info zip_info; | |
| 173 int err; | |
| 174 err = unzGetGlobalInfo(zip_file, &zip_info); | |
| 175 if (err != UNZ_OK) { | |
| 176 LOG(WARNING) << "couldn't open extension file " << zip_file_str; | |
| 177 return false; | |
| 178 } | |
| 179 bool ret = true; | |
| 180 for (unsigned int i = 0; i < zip_info.number_entry; ++i) { | |
| 181 FilePath dest_file; | |
| 182 if (!ExtractCurrentFile(zip_file, dest_dir, &dest_file)) { | |
| 183 ret = false; | |
| 184 break; | |
| 185 } | |
| 186 if (files) | |
| 187 files->push_back(dest_file); | |
| 188 | |
| 189 if (i + 1 < zip_info.number_entry) { | |
| 190 err = unzGoToNextFile(zip_file); | |
| 191 if (err != UNZ_OK) { | |
| 192 LOG(WARNING) << "error %d in unzGoToNextFile"; | |
| 193 ret = false; | |
| 194 break; | |
| 195 } | |
| 196 } | |
| 197 } | |
| 198 unzClose(zip_file); | |
| 199 return ret; | |
| 200 } | |
| OLD | NEW |