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