wireshark/ui/qt/utils/wireshark_zip_helper.cpp

284 lines
8.1 KiB
C++

/* wireshark_zip_helper.cpp
*
* Definitions for zip / unzip of files
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <ui/qt/utils/wireshark_zip_helper.h>
#ifdef HAVE_MINIZIP
#include "config.h"
#include "glib.h"
#include <iosfwd>
#include <iostream>
#include <zlib.h> // For Z_DEFLATED, etc.
#include <minizip/unzip.h>
#include <minizip/zip.h>
#include "epan/prefs.h"
#include "wsutil/file_util.h"
#include <QDataStream>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QDateTime>
#include <QMap>
/* Whether we are using minizip-ng and it uses an incompatible 'dos_date'
* struct member. */
#ifdef HAVE_MZCOMPAT_DOS_DATE
#define _MZDOSDATE dos_date
#else
#define _MZDOSDATE dosDate
#endif
bool WiresharkZipHelper::unzip(QString zipFile, QString directory, bool (*fileCheck)(QString, int), QString (*cleanName)(QString))
{
unzFile uf = Q_NULLPTR;
QFileInfo fi(zipFile);
QDir di(directory);
int files = 0;
if (! fi.exists() || ! di.exists())
return false;
if ((uf = unzOpen64(zipFile.toUtf8().constData())) == Q_NULLPTR)
return false;
unz_global_info64 gi;
int err;
unzGetGlobalInfo64(uf,&gi);
unsigned int nmbr = static_cast<unsigned int>(gi.number_entry);
if (nmbr <= 0)
return false;
QMap<QString, QString> cleanPaths;
for (unsigned int cnt = 0; cnt < nmbr; cnt++)
{
char filename_inzip[256];
unz_file_info64 file_info;
err = unzGetCurrentFileInfo64(uf, &file_info, filename_inzip, sizeof(filename_inzip),
Q_NULLPTR, 0, Q_NULLPTR, 0);
if (err == UNZ_OK)
{
QString fileInZip(filename_inzip);
int fileSize = static_cast<int>(file_info.uncompressed_size);
/* Sanity check for the file */
if (fileInZip.length() == 0 || (fileCheck && ! fileCheck(fileInZip, fileSize)) )
{
if ((cnt + 1) < nmbr)
{
err = unzGoToNextFile(uf);
if (err != UNZ_OK)
{
break;
}
}
continue;
}
if (di.exists())
{
#ifdef _WIN32
/* This is an additional fix for bug 16608, in which exports did contain the full path they
* where exported from, leading to imports not possible if the path does not exist on that
* machine */
if (fileInZip.contains(":/") || fileInZip.contains(":\\"))
{
QFileInfo fileName(fileInZip);
QFileInfo path(fileName.dir(), "");
QString newFile = path.baseName() + "/" + fileName.baseName();
fileInZip = newFile;
}
#endif
QString fullPath = di.path() + "/" + fileInZip;
QFileInfo fi(fullPath);
QString dirPath = fi.absolutePath();
/* clean up name from import. e.g. illegal characters in name */
if (cleanName)
{
if (! cleanPaths.keys().contains(dirPath))
{
QString tempPath = cleanName(dirPath);
int cnt = 1;
while (QFile::exists(tempPath))
{
tempPath = cleanName(dirPath) + QString::number(cnt);
cnt++;
}
cleanPaths.insert(dirPath, tempPath);
}
dirPath = cleanPaths[dirPath];
if (dirPath.length() == 0)
continue;
fi = QFileInfo(dirPath + "/" + fi.fileName());
fullPath = fi.absoluteFilePath();
}
if (fullPath.length() == 0)
continue;
QDir tP(fi.absolutePath());
if (! tP.exists())
di.mkpath(fi.absolutePath());
if (fileInZip.contains("/"))
{
QString filePath = fi.absoluteFilePath();
QFile file(filePath);
if (! file.exists())
{
err = unzOpenCurrentFile(uf);
if (err == UNZ_OK)
{
if (file.open(QIODevice::WriteOnly))
{
QByteArray buf;
buf.resize(IO_BUF_SIZE);
while ((err = unzReadCurrentFile(uf, buf.data(), buf.size())) != UNZ_EOF)
file.write(buf.constData(), err);
file.close();
}
unzCloseCurrentFile(uf);
files++;
}
}
}
}
}
if ((cnt+1) < nmbr)
{
err = unzGoToNextFile(uf);
if (err!=UNZ_OK)
{
break;
}
}
}
unzClose(uf);
return files > 0 ? true : false;
}
#ifndef UINT32_MAX
#define UINT32_MAX (0xffffffff)
#endif
static unsigned long qDateToDosDate(QDateTime time)
{
QDate ld = time.toLocalTime().date();
int year = ld.year() - 1900;
if (year >= 1980)
year -= 1980;
else if (year >= 80)
year -= 80;
else
year += 20;
int month = ld.month() - 1;
int day = ld.day();
if (year < 0 || year > 207 || month < 1 || month > 31)
return 0;
QTime lt = time.toLocalTime().time();
unsigned int dosDate = static_cast<unsigned int>((day + (32 * (month + 1)) + (512 * year)));
unsigned int dosTime = static_cast<unsigned int>((lt.second() / 2) + (32 * lt.minute()) + (2048 * lt.hour()));
return dosDate << 16 | dosTime;
}
void WiresharkZipHelper::addFileToZip(zipFile zf, QString filepath, QString fileInZip)
{
QFileInfo fi(filepath);
zip_fileinfo zi;
int err = ZIP_OK;
memset(&zi, 0, sizeof(zi));
QDateTime fTime = fi.lastModified();
zi._MZDOSDATE = qDateToDosDate(fTime);
QFile fh(filepath);
/* Checks if a large file block has to be written */
bool isLarge = (fh.size() > UINT32_MAX);
err = zipOpenNewFileInZip3_64(zf, fileInZip.toUtf8().constData(), &zi,
Q_NULLPTR, 0, Q_NULLPTR, 0, Q_NULLPTR, Z_DEFLATED, 9 , 0,
-MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
Q_NULLPTR, 0, static_cast<int>(isLarge));
if (err != ZIP_OK)
return;
if (fh.open(QIODevice::ReadOnly))
{
QByteArray buf;
buf.resize(IO_BUF_SIZE);
while (! fh.atEnd() && err == ZIP_OK)
{
qint64 bytesIn = fh.read(buf.data(), buf.size());
if (bytesIn > 0 && bytesIn <= buf.size())
{
err = zipWriteInFileInZip(zf, buf, (unsigned int) bytesIn);
}
}
fh.close();
}
zipCloseFileInZip(zf);
}
bool WiresharkZipHelper::zip(QString fileName, QStringList files, QString relativeTo)
{
QFileInfo fi(fileName);
if (fi.exists())
QFile::remove(fileName);
zipFile zf = zipOpen(fileName.toUtf8().constData(), APPEND_STATUS_CREATE);
if (zf == Q_NULLPTR)
return false;
for (int cnt = 0; cnt < files.count(); cnt++)
{
QFileInfo sf(files.at(cnt));
QString fileInZip = sf.absoluteFilePath();
QFileInfo relat(relativeTo);
fileInZip.replace(relat.absoluteFilePath(), "");
/* Windows cannot open zip files, if the filenames starts with a separator */
while (fileInZip.length() > 0 && fileInZip.startsWith("/"))
fileInZip = fileInZip.right(fileInZip.length() - 1);
WiresharkZipHelper::addFileToZip(zf, sf.absoluteFilePath(), fileInZip);
}
if (zipClose(zf, Q_NULLPTR))
return false;
return true;
}
#endif