1 /* wireshark_zip_helper.cpp
2 *
3 * Definitions for zip / unzip of files
4 *
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12 #include <ui/qt/utils/wireshark_zip_helper.h>
13
14 #ifdef HAVE_MINIZIP
15 #include "config.h"
16
17 #include "glib.h"
18
19 #include <iosfwd>
20 #include <iostream>
21 #include <zlib.h> // For Z_DEFLATED, etc.
22 #include <minizip/unzip.h>
23 #include <minizip/zip.h>
24
25 #include "epan/prefs.h"
26 #include "wsutil/file_util.h"
27
28 #include <QDataStream>
29 #include <QDir>
30 #include <QFile>
31 #include <QFileInfo>
32 #include <QDateTime>
33 #include <QMap>
34
35 /* Whether we are using minizip-ng and it uses an incompatible 'dos_date'
36 * struct member. */
37 #ifdef HAVE_MZCOMPAT_DOS_DATE
38 #define _MZDOSDATE dos_date
39 #else
40 #define _MZDOSDATE dosDate
41 #endif
42
unzip(QString zipFile,QString directory,bool (* fileCheck)(QString,int),QString (* cleanName)(QString))43 bool WiresharkZipHelper::unzip(QString zipFile, QString directory, bool (*fileCheck)(QString, int), QString (*cleanName)(QString))
44 {
45 unzFile uf = Q_NULLPTR;
46 QFileInfo fi(zipFile);
47 QDir di(directory);
48 int files = 0;
49
50 if (! fi.exists() || ! di.exists())
51 return false;
52
53 if ((uf = unzOpen64(zipFile.toUtf8().constData())) == Q_NULLPTR)
54 return false;
55
56 unz_global_info64 gi;
57 int err;
58 unzGetGlobalInfo64(uf,&gi);
59 unsigned int nmbr = static_cast<unsigned int>(gi.number_entry);
60 if (nmbr <= 0)
61 return false;
62
63 QMap<QString, QString> cleanPaths;
64
65 for (unsigned int cnt = 0; cnt < nmbr; cnt++)
66 {
67 char filename_inzip[256];
68 unz_file_info64 file_info;
69 err = unzGetCurrentFileInfo64(uf, &file_info, filename_inzip, sizeof(filename_inzip),
70 Q_NULLPTR, 0, Q_NULLPTR, 0);
71 if (err == UNZ_OK)
72 {
73 QString fileInZip(filename_inzip);
74 int fileSize = static_cast<int>(file_info.uncompressed_size);
75
76 /* Sanity check for the file */
77 if (fileInZip.length() == 0 || (fileCheck && ! fileCheck(fileInZip, fileSize)) )
78 {
79 if ((cnt + 1) < nmbr)
80 {
81 err = unzGoToNextFile(uf);
82 if (err != UNZ_OK)
83 {
84 break;
85 }
86 }
87 continue;
88 }
89
90 if (di.exists())
91 {
92 #ifdef _WIN32
93 /* This is an additional fix for bug 16608, in which exports did contain the full path they
94 * where exported from, leading to imports not possible if the path does not exist on that
95 * machine */
96
97 if (fileInZip.contains(":/") || fileInZip.contains(":\\"))
98 {
99 QFileInfo fileName(fileInZip);
100 QFileInfo path(fileName.dir(), "");
101 QString newFile = path.baseName() + "/" + fileName.baseName();
102 fileInZip = newFile;
103 }
104 #endif
105
106 QString fullPath = di.path() + "/" + fileInZip;
107 QFileInfo fi(fullPath);
108 QString dirPath = fi.absolutePath();
109
110 /* clean up name from import. e.g. illegal characters in name */
111 if (cleanName)
112 {
113 if (! cleanPaths.keys().contains(dirPath))
114 {
115 QString tempPath = cleanName(dirPath);
116 int cnt = 1;
117 while (QFile::exists(tempPath))
118 {
119 tempPath = cleanName(dirPath) + QString::number(cnt);
120 cnt++;
121 }
122 cleanPaths.insert(dirPath, tempPath);
123 }
124
125 dirPath = cleanPaths[dirPath];
126 if (dirPath.length() == 0)
127 continue;
128
129 fi = QFileInfo(dirPath + "/" + fi.fileName());
130 fullPath = fi.absoluteFilePath();
131 }
132 if (fullPath.length() == 0)
133 continue;
134
135 QDir tP(fi.absolutePath());
136 if (! tP.exists())
137 di.mkpath(fi.absolutePath());
138
139 if (fileInZip.contains("/"))
140 {
141 QString filePath = fi.absoluteFilePath();
142 QFile file(filePath);
143 if (! file.exists())
144 {
145 err = unzOpenCurrentFile(uf);
146 if (err == UNZ_OK)
147 {
148 if (file.open(QIODevice::WriteOnly))
149 {
150 QByteArray buf;
151 buf.resize(IO_BUF_SIZE);
152 while ((err = unzReadCurrentFile(uf, buf.data(), buf.size())) != UNZ_EOF)
153 file.write(buf.constData(), err);
154
155 file.close();
156 }
157 unzCloseCurrentFile(uf);
158
159 files++;
160 }
161 }
162 }
163 }
164 }
165
166 if ((cnt+1) < nmbr)
167 {
168 err = unzGoToNextFile(uf);
169 if (err!=UNZ_OK)
170 {
171 break;
172 }
173 }
174 }
175
176 unzClose(uf);
177
178 return files > 0 ? true : false;
179 }
180
181 #ifndef UINT32_MAX
182 #define UINT32_MAX (0xffffffff)
183 #endif
184
qDateToDosDate(QDateTime time)185 static unsigned long qDateToDosDate(QDateTime time)
186 {
187 QDate ld = time.toLocalTime().date();
188
189 int year = ld.year() - 1900;
190 if (year >= 1980)
191 year -= 1980;
192 else if (year >= 80)
193 year -= 80;
194 else
195 year += 20;
196
197 int month = ld.month() - 1;
198 int day = ld.day();
199
200 if (year < 0 || year > 207 || month < 1 || month > 31)
201 return 0;
202
203 QTime lt = time.toLocalTime().time();
204
205 unsigned int dosDate = static_cast<unsigned int>((day + (32 * (month + 1)) + (512 * year)));
206 unsigned int dosTime = static_cast<unsigned int>((lt.second() / 2) + (32 * lt.minute()) + (2048 * lt.hour()));
207
208 return dosDate << 16 | dosTime;
209 }
210
addFileToZip(zipFile zf,QString filepath,QString fileInZip)211 void WiresharkZipHelper::addFileToZip(zipFile zf, QString filepath, QString fileInZip)
212 {
213 QFileInfo fi(filepath);
214 zip_fileinfo zi;
215 int err = ZIP_OK;
216
217 memset(&zi, 0, sizeof(zi));
218
219 QDateTime fTime = fi.lastModified();
220 zi._MZDOSDATE = qDateToDosDate(fTime);
221
222 QFile fh(filepath);
223 /* Checks if a large file block has to be written */
224 bool isLarge = (fh.size() > UINT32_MAX);
225
226 err = zipOpenNewFileInZip3_64(zf, fileInZip.toUtf8().constData(), &zi,
227 Q_NULLPTR, 0, Q_NULLPTR, 0, Q_NULLPTR, Z_DEFLATED, 9 , 0,
228 -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
229 Q_NULLPTR, 0, static_cast<int>(isLarge));
230
231 if (err != ZIP_OK)
232 return;
233
234 if (fh.open(QIODevice::ReadOnly))
235 {
236 QByteArray buf;
237 buf.resize(IO_BUF_SIZE);
238 while (! fh.atEnd() && err == ZIP_OK)
239 {
240 qint64 bytesIn = fh.read(buf.data(), buf.size());
241 if (bytesIn > 0 && bytesIn <= buf.size())
242 {
243 err = zipWriteInFileInZip(zf, buf, (unsigned int) bytesIn);
244 }
245 }
246 fh.close();
247 }
248
249 zipCloseFileInZip(zf);
250 }
251
zip(QString fileName,QStringList files,QString relativeTo)252 bool WiresharkZipHelper::zip(QString fileName, QStringList files, QString relativeTo)
253 {
254
255 QFileInfo fi(fileName);
256 if (fi.exists())
257 QFile::remove(fileName);
258
259 zipFile zf = zipOpen(fileName.toUtf8().constData(), APPEND_STATUS_CREATE);
260 if (zf == Q_NULLPTR)
261 return false;
262
263 for (int cnt = 0; cnt < files.count(); cnt++)
264 {
265 QFileInfo sf(files.at(cnt));
266 QString fileInZip = sf.absoluteFilePath();
267 QFileInfo relat(relativeTo);
268 fileInZip.replace(relat.absoluteFilePath(), "");
269 /* Windows cannot open zip files, if the filenames starts with a separator */
270 while (fileInZip.length() > 0 && fileInZip.startsWith("/"))
271 fileInZip = fileInZip.right(fileInZip.length() - 1);
272
273 WiresharkZipHelper::addFileToZip(zf, sf.absoluteFilePath(), fileInZip);
274
275 }
276
277 if (zipClose(zf, Q_NULLPTR))
278 return false;
279
280 return true;
281 }
282
283 #endif
284