1// Copyright (c) 2011 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 "third_party/zlib/google/zip_internal.h"
6
7#include <algorithm>
8
9#include "base/files/file_util.h"
10#include "base/logging.h"
11#include "base/strings/utf_string_conversions.h"
12#include "base/time/time.h"
13
14#if defined(USE_SYSTEM_MINIZIP)
15#include <minizip/ioapi.h>
16#include <minizip/unzip.h>
17#include <minizip/zip.h>
18#else
19#include "third_party/zlib/contrib/minizip/unzip.h"
20#include "third_party/zlib/contrib/minizip/zip.h"
21#if defined(OS_WIN)
22#include "third_party/zlib/contrib/minizip/iowin32.h"
23#elif defined(OS_POSIX)
24#include "third_party/zlib/contrib/minizip/ioapi.h"
25#endif  // defined(OS_POSIX)
26#endif  // defined(USE_SYSTEM_MINIZIP)
27
28namespace {
29
30#if defined(OS_WIN)
31typedef struct {
32  HANDLE hf;
33  int error;
34} WIN32FILE_IOWIN;
35
36// This function is derived from third_party/minizip/iowin32.c.
37// Its only difference is that it treats the char* as UTF8 and
38// uses the Unicode version of CreateFile.
39void* ZipOpenFunc(void *opaque, const char* filename, int mode) {
40  DWORD desired_access = 0, creation_disposition = 0;
41  DWORD share_mode = 0, flags_and_attributes = 0;
42  HANDLE file = 0;
43  void* ret = NULL;
44
45  if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) {
46    desired_access = GENERIC_READ;
47    creation_disposition = OPEN_EXISTING;
48    share_mode = FILE_SHARE_READ;
49  } else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) {
50    desired_access = GENERIC_WRITE | GENERIC_READ;
51    creation_disposition = OPEN_EXISTING;
52  } else if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
53    desired_access = GENERIC_WRITE | GENERIC_READ;
54    creation_disposition = CREATE_ALWAYS;
55  }
56
57  base::string16 filename16 = base::UTF8ToUTF16(filename);
58  if ((filename != NULL) && (desired_access != 0)) {
59    file = CreateFile(filename16.c_str(), desired_access, share_mode,
60        NULL, creation_disposition, flags_and_attributes, NULL);
61  }
62
63  if (file == INVALID_HANDLE_VALUE)
64    file = NULL;
65
66  if (file != NULL) {
67    WIN32FILE_IOWIN file_ret;
68    file_ret.hf = file;
69    file_ret.error = 0;
70    ret = malloc(sizeof(WIN32FILE_IOWIN));
71    if (ret == NULL)
72      CloseHandle(file);
73    else
74      *(static_cast<WIN32FILE_IOWIN*>(ret)) = file_ret;
75  }
76  return ret;
77}
78#endif
79
80#if defined(OS_POSIX)
81// Callback function for zlib that opens a file stream from a file descriptor.
82void* FdOpenFileFunc(void* opaque, const char* filename, int mode) {
83  FILE* file = NULL;
84  const char* mode_fopen = NULL;
85
86  if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ)
87    mode_fopen = "rb";
88  else if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
89    mode_fopen = "r+b";
90  else if (mode & ZLIB_FILEFUNC_MODE_CREATE)
91    mode_fopen = "wb";
92
93  if ((filename != NULL) && (mode_fopen != NULL))
94    file = fdopen(*static_cast<int*>(opaque), mode_fopen);
95
96  return file;
97}
98
99// We don't actually close the file stream since that would close
100// the underlying file descriptor, and we don't own it. However we do need to
101// flush buffers and free |opaque| since we malloc'ed it in FillFdOpenFileFunc.
102int CloseFileFunc(void* opaque, void* stream) {
103  fflush(static_cast<FILE*>(stream));
104  free(opaque);
105  return 0;
106}
107
108// Fills |pzlib_filecunc_def| appropriately to handle the zip file
109// referred to by |fd|.
110void FillFdOpenFileFunc(zlib_filefunc_def* pzlib_filefunc_def, int fd) {
111  fill_fopen_filefunc(pzlib_filefunc_def);
112  pzlib_filefunc_def->zopen_file = FdOpenFileFunc;
113  pzlib_filefunc_def->zclose_file = CloseFileFunc;
114  int* ptr_fd = static_cast<int*>(malloc(sizeof(fd)));
115  *ptr_fd = fd;
116  pzlib_filefunc_def->opaque = ptr_fd;
117}
118#endif  // defined(OS_POSIX)
119
120#if defined(OS_WIN)
121// Callback function for zlib that opens a file stream from a Windows handle.
122void* HandleOpenFileFunc(void* opaque, const char* filename, int mode) {
123  WIN32FILE_IOWIN file_ret;
124  file_ret.hf = static_cast<HANDLE>(opaque);
125  file_ret.error = 0;
126  if (file_ret.hf == INVALID_HANDLE_VALUE)
127    return NULL;
128
129  void* ret = malloc(sizeof(WIN32FILE_IOWIN));
130  if (ret != NULL)
131    *(static_cast<WIN32FILE_IOWIN*>(ret)) = file_ret;
132  return ret;
133}
134#endif
135
136// A struct that contains data required for zlib functions to extract files from
137// a zip archive stored in memory directly. The following I/O API functions
138// expect their opaque parameters refer to this struct.
139struct ZipBuffer {
140  const char* data;  // weak
141  size_t length;
142  size_t offset;
143};
144
145// Opens the specified file. When this function returns a non-NULL pointer, zlib
146// uses this pointer as a stream parameter while compressing or uncompressing
147// data. (Returning NULL represents an error.) This function initializes the
148// given opaque parameter and returns it because this parameter stores all
149// information needed for uncompressing data. (This function does not support
150// writing compressed data and it returns NULL for this case.)
151void* OpenZipBuffer(void* opaque, const char* /*filename*/, int mode) {
152  if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) != ZLIB_FILEFUNC_MODE_READ) {
153    NOTREACHED();
154    return NULL;
155  }
156  ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
157  if (!buffer || !buffer->data || !buffer->length)
158    return NULL;
159  buffer->offset = 0;
160  return opaque;
161}
162
163// Reads compressed data from the specified stream. This function copies data
164// refered by the opaque parameter and returns the size actually copied.
165uLong ReadZipBuffer(void* opaque, void* /*stream*/, void* buf, uLong size) {
166  ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
167  DCHECK_LE(buffer->offset, buffer->length);
168  size_t remaining_bytes = buffer->length - buffer->offset;
169  if (!buffer || !buffer->data || !remaining_bytes)
170    return 0;
171  size = std::min(size, static_cast<uLong>(remaining_bytes));
172  memcpy(buf, &buffer->data[buffer->offset], size);
173  buffer->offset += size;
174  return size;
175}
176
177// Writes compressed data to the stream. This function always returns zero
178// because this implementation is only for reading compressed data.
179uLong WriteZipBuffer(void* /*opaque*/,
180                     void* /*stream*/,
181                     const void* /*buf*/,
182                     uLong /*size*/) {
183  NOTREACHED();
184  return 0;
185}
186
187// Returns the offset from the beginning of the data.
188long GetOffsetOfZipBuffer(void* opaque, void* /*stream*/) {
189  ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
190  if (!buffer)
191    return -1;
192  return static_cast<long>(buffer->offset);
193}
194
195// Moves the current offset to the specified position.
196long SeekZipBuffer(void* opaque, void* /*stream*/, uLong offset, int origin) {
197  ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
198  if (!buffer)
199    return -1;
200  if (origin == ZLIB_FILEFUNC_SEEK_CUR) {
201    buffer->offset = std::min(buffer->offset + static_cast<size_t>(offset),
202                              buffer->length);
203    return 0;
204  }
205  if (origin == ZLIB_FILEFUNC_SEEK_END) {
206    buffer->offset = (buffer->length > offset) ? buffer->length - offset : 0;
207    return 0;
208  }
209  if (origin == ZLIB_FILEFUNC_SEEK_SET) {
210    buffer->offset = std::min(buffer->length, static_cast<size_t>(offset));
211    return 0;
212  }
213  NOTREACHED();
214  return -1;
215}
216
217// Closes the input offset and deletes all resources used for compressing or
218// uncompressing data. This function deletes the ZipBuffer object referred by
219// the opaque parameter since zlib deletes the unzFile object and it does not
220// use this object any longer.
221int CloseZipBuffer(void* opaque, void* /*stream*/) {
222  if (opaque)
223    free(opaque);
224  return 0;
225}
226
227// Returns the last error happened when reading or writing data. This function
228// always returns zero, which means there are not any errors.
229int GetErrorOfZipBuffer(void* /*opaque*/, void* /*stream*/) {
230  return 0;
231}
232
233// Returns a zip_fileinfo struct with the time represented by |file_time|.
234zip_fileinfo TimeToZipFileInfo(const base::Time& file_time) {
235  base::Time::Exploded file_time_parts;
236  file_time.LocalExplode(&file_time_parts);
237
238  zip_fileinfo zip_info = {};
239  if (file_time_parts.year >= 1980) {
240    // This if check works around the handling of the year value in
241    // contrib/minizip/zip.c in function zip64local_TmzDateToDosDate
242    // It assumes that dates below 1980 are in the double digit format.
243    // Hence the fail safe option is to leave the date unset. Some programs
244    // might show the unset date as 1980-0-0 which is invalid.
245    zip_info.tmz_date.tm_year = file_time_parts.year;
246    zip_info.tmz_date.tm_mon = file_time_parts.month - 1;
247    zip_info.tmz_date.tm_mday = file_time_parts.day_of_month;
248    zip_info.tmz_date.tm_hour = file_time_parts.hour;
249    zip_info.tmz_date.tm_min = file_time_parts.minute;
250    zip_info.tmz_date.tm_sec = file_time_parts.second;
251  }
252
253  return zip_info;
254}
255}  // namespace
256
257namespace zip {
258namespace internal {
259
260unzFile OpenForUnzipping(const std::string& file_name_utf8) {
261  zlib_filefunc_def* zip_func_ptrs = NULL;
262#if defined(OS_WIN)
263  zlib_filefunc_def zip_funcs;
264  fill_win32_filefunc(&zip_funcs);
265  zip_funcs.zopen_file = ZipOpenFunc;
266  zip_func_ptrs = &zip_funcs;
267#endif
268  return unzOpen2(file_name_utf8.c_str(), zip_func_ptrs);
269}
270
271#if defined(OS_POSIX)
272unzFile OpenFdForUnzipping(int zip_fd) {
273  zlib_filefunc_def zip_funcs;
274  FillFdOpenFileFunc(&zip_funcs, zip_fd);
275  // Passing dummy "fd" filename to zlib.
276  return unzOpen2("fd", &zip_funcs);
277}
278#endif
279
280#if defined(OS_WIN)
281unzFile OpenHandleForUnzipping(HANDLE zip_handle) {
282  zlib_filefunc_def zip_funcs;
283  fill_win32_filefunc(&zip_funcs);
284  zip_funcs.zopen_file = HandleOpenFileFunc;
285  zip_funcs.opaque = zip_handle;
286  return unzOpen2("fd", &zip_funcs);
287}
288#endif
289
290// static
291unzFile PrepareMemoryForUnzipping(const std::string& data) {
292  if (data.empty())
293    return NULL;
294
295  ZipBuffer* buffer = static_cast<ZipBuffer*>(malloc(sizeof(ZipBuffer)));
296  if (!buffer)
297    return NULL;
298  buffer->data = data.data();
299  buffer->length = data.length();
300  buffer->offset = 0;
301
302  zlib_filefunc_def zip_functions;
303  zip_functions.zopen_file = OpenZipBuffer;
304  zip_functions.zread_file = ReadZipBuffer;
305  zip_functions.zwrite_file = WriteZipBuffer;
306  zip_functions.ztell_file = GetOffsetOfZipBuffer;
307  zip_functions.zseek_file = SeekZipBuffer;
308  zip_functions.zclose_file = CloseZipBuffer;
309  zip_functions.zerror_file = GetErrorOfZipBuffer;
310  zip_functions.opaque = static_cast<void*>(buffer);
311  return unzOpen2(NULL, &zip_functions);
312}
313
314zipFile OpenForZipping(const std::string& file_name_utf8, int append_flag) {
315  zlib_filefunc_def* zip_func_ptrs = NULL;
316#if defined(OS_WIN)
317  zlib_filefunc_def zip_funcs;
318  fill_win32_filefunc(&zip_funcs);
319  zip_funcs.zopen_file = ZipOpenFunc;
320  zip_func_ptrs = &zip_funcs;
321#endif
322  return zipOpen2(file_name_utf8.c_str(),
323                  append_flag,
324                  NULL,  // global comment
325                  zip_func_ptrs);
326}
327
328#if defined(OS_POSIX)
329zipFile OpenFdForZipping(int zip_fd, int append_flag) {
330  zlib_filefunc_def zip_funcs;
331  FillFdOpenFileFunc(&zip_funcs, zip_fd);
332  // Passing dummy "fd" filename to zlib.
333  return zipOpen2("fd", append_flag, NULL, &zip_funcs);
334}
335#endif
336
337zip_fileinfo GetFileInfoForZipping(const base::FilePath& path) {
338  base::Time file_time;
339  base::File::Info file_info;
340  if (base::GetFileInfo(path, &file_info))
341    file_time = file_info.last_modified;
342  return TimeToZipFileInfo(file_time);
343}
344
345bool ZipOpenNewFileInZip(zipFile zip_file,
346                         const std::string& str_path,
347                         const zip_fileinfo* file_info) {
348  // Section 4.4.4 http://www.pkware.com/documents/casestudies/APPNOTE.TXT
349  // Setting the Language encoding flag so the file is told to be in utf-8.
350  const uLong LANGUAGE_ENCODING_FLAG = 0x1 << 11;
351
352  if (ZIP_OK != zipOpenNewFileInZip4(
353                    zip_file,  // file
354                    str_path.c_str(),  // filename
355                    file_info,  // zipfi
356                    NULL,  // extrafield_local,
357                    0u,  // size_extrafield_local
358                    NULL,  // extrafield_global
359                    0u,  // size_extrafield_global
360                    NULL,  // comment
361                    Z_DEFLATED,  // method
362                    Z_DEFAULT_COMPRESSION,  // level
363                    0,  // raw
364                    -MAX_WBITS,  // windowBits
365                    DEF_MEM_LEVEL,  // memLevel
366                    Z_DEFAULT_STRATEGY,  // strategy
367                    NULL,  // password
368                    0,  // crcForCrypting
369                    0,  // versionMadeBy
370                    LANGUAGE_ENCODING_FLAG)) {  // flagBase
371    DLOG(ERROR) << "Could not open zip file entry " << str_path;
372    return false;
373  }
374  return true;
375}
376
377}  // namespace internal
378}  // namespace zip
379