OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 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/zip.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 #include "third_party/zlib/contrib/minizip/zip.h" |
| 12 #if defined(OS_WIN) |
| 13 #include "third_party/zlib/contrib/minizip/iowin32.h" |
| 14 #endif |
| 15 |
| 16 static const int kZipMaxPath = 256; |
| 17 static const int kZipBufSize = 8192; |
| 18 |
| 19 // Extract the 'current' selected file from the zip into dest_dir. |
| 20 // Output filename is stored in out_file. Returns true on success. |
| 21 static bool ExtractCurrentFile(unzFile zip_file, |
| 22 const FilePath& dest_dir, |
| 23 FilePath* out_file) { |
| 24 char filename_inzip[kZipMaxPath] = {0}; |
| 25 unz_file_info file_info; |
| 26 int err = unzGetCurrentFileInfo(zip_file, &file_info, filename_inzip, |
| 27 sizeof(filename_inzip), NULL, 0, NULL, 0); |
| 28 if (err != UNZ_OK) |
| 29 return false; |
| 30 if (filename_inzip[0] == '\0') |
| 31 return false; |
| 32 |
| 33 err = unzOpenCurrentFile(zip_file); |
| 34 if (err != UNZ_OK) |
| 35 return false; |
| 36 |
| 37 FilePath::StringType filename; |
| 38 std::vector<FilePath::StringType> filename_parts; |
| 39 #if defined(OS_WIN) |
| 40 filename = UTF8ToWide(filename_inzip); |
| 41 #elif defined(OS_POSIX) |
| 42 filename = filename_inzip; |
| 43 #endif |
| 44 SplitString(filename, '/', &filename_parts); |
| 45 |
| 46 FilePath dest_file(dest_dir); |
| 47 std::vector<FilePath::StringType>::iterator iter; |
| 48 for (iter = filename_parts.begin(); iter != filename_parts.end(); ++iter) |
| 49 dest_file = dest_file.Append(*iter); |
| 50 if (out_file) |
| 51 *out_file = dest_file; |
| 52 // If this is a directory, just create it and return. |
| 53 if (filename_inzip[strlen(filename_inzip) - 1] == '/') { |
| 54 if (!file_util::CreateDirectory(dest_file)) |
| 55 return false; |
| 56 return true; |
| 57 } |
| 58 // TODO(erikkay): Can we always count on the directory entry coming before a |
| 59 // file in that directory? If so, then these three lines can be removed. |
| 60 FilePath dir = dest_file.DirName(); |
| 61 if (!file_util::CreateDirectory(dir)) |
| 62 return false; |
| 63 |
| 64 net::FileStream stream; |
| 65 int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE; |
| 66 if (stream.Open(dest_file, flags) != 0) |
| 67 return false; |
| 68 |
| 69 bool ret = true; |
| 70 int num_bytes = 0; |
| 71 char buf[kZipBufSize]; |
| 72 do { |
| 73 num_bytes = unzReadCurrentFile(zip_file, buf, kZipBufSize); |
| 74 if (num_bytes < 0) { |
| 75 // If num_bytes < 0, then it's a specific UNZ_* error code. |
| 76 // While we're not currently handling these codes specifically, save |
| 77 // it away in case we want to in the future. |
| 78 err = num_bytes; |
| 79 break; |
| 80 } |
| 81 if (num_bytes > 0) { |
| 82 if (num_bytes != stream.Write(buf, num_bytes, NULL)) { |
| 83 ret = false; |
| 84 break; |
| 85 } |
| 86 } |
| 87 } while (num_bytes > 0); |
| 88 |
| 89 stream.Close(); |
| 90 if (err == UNZ_OK) |
| 91 err = unzCloseCurrentFile(zip_file); |
| 92 else |
| 93 unzCloseCurrentFile(zip_file); // Don't lose the original error code. |
| 94 if (err != UNZ_OK) |
| 95 ret = false; |
| 96 return ret; |
| 97 } |
| 98 |
| 99 #if defined(OS_WIN) |
| 100 typedef struct { |
| 101 HANDLE hf; |
| 102 int error; |
| 103 } WIN32FILE_IOWIN; |
| 104 |
| 105 // This function is derived from third_party/minizip/iowin32.c. |
| 106 // Its only difference is that it treats the char* as UTF8 and |
| 107 // uses the Unicode version of CreateFile. |
| 108 static void* ZipOpenFunc(void *opaque, const char* filename, int mode) { |
| 109 DWORD desired_access, creation_disposition; |
| 110 DWORD share_mode, flags_and_attributes; |
| 111 HANDLE file = 0; |
| 112 void* ret = NULL; |
| 113 |
| 114 desired_access = share_mode = flags_and_attributes = 0; |
| 115 |
| 116 if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) { |
| 117 desired_access = GENERIC_READ; |
| 118 creation_disposition = OPEN_EXISTING; |
| 119 share_mode = FILE_SHARE_READ; |
| 120 } else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) { |
| 121 desired_access = GENERIC_WRITE | GENERIC_READ; |
| 122 creation_disposition = OPEN_EXISTING; |
| 123 } else if (mode & ZLIB_FILEFUNC_MODE_CREATE) { |
| 124 desired_access = GENERIC_WRITE | GENERIC_READ; |
| 125 creation_disposition = CREATE_ALWAYS; |
| 126 } |
| 127 |
| 128 std::wstring filename_wstr = UTF8ToWide(filename); |
| 129 if ((filename != NULL) && (desired_access != 0)) { |
| 130 file = CreateFile(filename_wstr.c_str(), desired_access, share_mode, |
| 131 NULL, creation_disposition, flags_and_attributes, NULL); |
| 132 } |
| 133 |
| 134 if (file == INVALID_HANDLE_VALUE) |
| 135 file = NULL; |
| 136 |
| 137 if (file != NULL) { |
| 138 WIN32FILE_IOWIN file_ret; |
| 139 file_ret.hf = file; |
| 140 file_ret.error = 0; |
| 141 ret = malloc(sizeof(WIN32FILE_IOWIN)); |
| 142 if (ret == NULL) |
| 143 CloseHandle(file); |
| 144 else |
| 145 *(static_cast<WIN32FILE_IOWIN*>(ret)) = file_ret; |
| 146 } |
| 147 return ret; |
| 148 } |
| 149 #endif |
| 150 |
| 151 bool Unzip(const FilePath& src_file, const FilePath& dest_dir, |
| 152 std::vector<FilePath>* files) { |
| 153 #if defined(OS_WIN) |
| 154 zlib_filefunc_def zip_funcs; |
| 155 fill_win32_filefunc(&zip_funcs); |
| 156 zip_funcs.zopen_file = ZipOpenFunc; |
| 157 #endif |
| 158 |
| 159 #if defined(OS_POSIX) |
| 160 std::string src_file_str = src_file.value(); |
| 161 unzFile zip_file = unzOpen(src_file_str.c_str()); |
| 162 #elif defined(OS_WIN) |
| 163 std::string src_file_str = WideToUTF8(src_file.value()); |
| 164 unzFile zip_file = unzOpen2(src_file_str.c_str(), &zip_funcs); |
| 165 #endif |
| 166 if (!zip_file) { |
| 167 LOG(WARNING) << "couldn't open extension file " << src_file_str; |
| 168 return false; |
| 169 } |
| 170 unz_global_info zip_info; |
| 171 int err; |
| 172 err = unzGetGlobalInfo(zip_file, &zip_info); |
| 173 if (err != UNZ_OK) { |
| 174 LOG(WARNING) << "couldn't open extension file " << src_file_str; |
| 175 return false; |
| 176 } |
| 177 bool ret = true; |
| 178 for (unsigned int i = 0; i < zip_info.number_entry; ++i) { |
| 179 FilePath dest_file; |
| 180 if (!ExtractCurrentFile(zip_file, dest_dir, &dest_file)) { |
| 181 ret = false; |
| 182 break; |
| 183 } |
| 184 if (files) |
| 185 files->push_back(dest_file); |
| 186 |
| 187 if (i + 1 < zip_info.number_entry) { |
| 188 err = unzGoToNextFile(zip_file); |
| 189 if (err != UNZ_OK) { |
| 190 LOG(WARNING) << "error %d in unzGoToNextFile"; |
| 191 ret = false; |
| 192 break; |
| 193 } |
| 194 } |
| 195 } |
| 196 unzClose(zip_file); |
| 197 return ret; |
| 198 } |
| 199 |
| 200 static bool AddFileToZip(zipFile zip_file, const FilePath& src_path) { |
| 201 net::FileStream stream; |
| 202 int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ; |
| 203 if (stream.Open(src_path, flags) != 0) { |
| 204 LOG(ERROR) << "Could not open stream for path " |
| 205 << WideToASCII(src_path.ToWStringHack()); |
| 206 return false; |
| 207 } |
| 208 |
| 209 int num_bytes; |
| 210 char buf[kZipBufSize]; |
| 211 do { |
| 212 num_bytes = stream.Read(buf, kZipBufSize, NULL); |
| 213 if (num_bytes > 0) { |
| 214 if (ZIP_OK != zipWriteInFileInZip(zip_file, buf, num_bytes)) { |
| 215 LOG(ERROR) << "Could not write data to zip for path " |
| 216 << WideToASCII(src_path.ToWStringHack()); |
| 217 return false; |
| 218 } |
| 219 } |
| 220 } while (num_bytes > 0); |
| 221 |
| 222 return true; |
| 223 } |
| 224 |
| 225 bool Zip(const FilePath& src_path, const FilePath& dest_file) { |
| 226 #if defined(OS_WIN) |
| 227 zlib_filefunc_def zip_funcs; |
| 228 fill_win32_filefunc(&zip_funcs); |
| 229 zip_funcs.zopen_file = ZipOpenFunc; |
| 230 #endif |
| 231 |
| 232 #if defined(OS_POSIX) |
| 233 std::string src_path_str = src_path.value(); |
| 234 zipFile zip_file = zipOpen(src_path_str.c_str(), APPEND_STATUS_CREATE); |
| 235 #elif defined(OS_WIN) |
| 236 std::string dest_file_str = WideToUTF8(dest_file.value()); |
| 237 zipFile zip_file = zipOpen2(dest_file_str.c_str(), APPEND_STATUS_CREATE, |
| 238 NULL, // global comment |
| 239 &zip_funcs); |
| 240 #endif |
| 241 |
| 242 if (!zip_file) { |
| 243 LOG(WARNING) << "couldn't open extension file " << dest_file_str; |
| 244 return false; |
| 245 } |
| 246 |
| 247 file_util::FileEnumerator file_enumerator( |
| 248 src_path, true, // recursive |
| 249 file_util::FileEnumerator::FILES_AND_DIRECTORIES); |
| 250 for (FilePath path = file_enumerator.Next(); !path.value().empty(); |
| 251 path = file_enumerator.Next()) { |
| 252 #if defined(OS_WIN) |
| 253 std::string str_path = |
| 254 WideToUTF8(path.value().substr(src_path.value().length() + 1)); |
| 255 ReplaceSubstringsAfterOffset(&str_path, 0u, "\\", "/"); |
| 256 #else |
| 257 std::string str_path = path.value().substring(src_path.length() + 1); |
| 258 #endif |
| 259 |
| 260 bool is_directory = file_util::DirectoryExists(path); |
| 261 if (is_directory) |
| 262 str_path += "/"; |
| 263 |
| 264 if (ZIP_OK != zipOpenNewFileInZip( |
| 265 zip_file, str_path.c_str(), |
| 266 NULL, NULL, 0u, NULL, 0u, NULL, // file info, extrafield local, length, |
| 267 // extrafield global, length, comment |
| 268 Z_DEFLATED, Z_DEFAULT_COMPRESSION)) { |
| 269 LOG(ERROR) << "Could not open zip file entry " << str_path; |
| 270 return false; |
| 271 } |
| 272 |
| 273 bool success = true; |
| 274 if (!is_directory) { |
| 275 success = AddFileToZip(zip_file, path); |
| 276 } |
| 277 |
| 278 if (ZIP_OK != zipCloseFileInZip(zip_file)) { |
| 279 LOG(ERROR) << "Could not close zip file entry " << str_path; |
| 280 return false; |
| 281 } |
| 282 |
| 283 if (!success) |
| 284 return false; |
| 285 } |
| 286 |
| 287 if (ZIP_OK != zipClose(zip_file, NULL)) { // global comment |
| 288 LOG(ERROR) << "Error closing zip file " << dest_file_str; |
| 289 return false; |
| 290 } |
| 291 |
| 292 return true; |
| 293 } |
OLD | NEW |