1
0
Fork 0
mirror of https://github.com/HamletDuFromage/sigpatches-updater.git synced 2024-12-31 21:46:02 +00:00
sigpatches-updater/libs/minizip/source/unzipper.cpp
2020-09-12 18:12:07 +02:00

562 lines
14 KiB
C++

#include "unzipper.h"
#include "defs.h"
#include "tools.h"
#include <functional>
#include <exception>
#include <fstream>
#include <stdexcept>
namespace zipper {
struct Unzipper::Impl
{
Unzipper& m_outer;
zipFile m_zf;
ourmemory_t m_zipmem;
zlib_filefunc_def m_filefunc;
private:
bool initMemory(zlib_filefunc_def& filefunc)
{
m_zf = unzOpen2("__notused__", &filefunc);
return m_zf != NULL;
}
bool locateEntry(const std::string& name)
{
return UNZ_OK == unzLocateFile(m_zf, name.c_str(), NULL);
}
ZipEntry currentEntryInfo()
{
unz_file_info64 file_info = { 0 };
char filename_inzip[256] = { 0 };
int err = unzGetCurrentFileInfo64(m_zf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
if (UNZ_OK != err)
throw EXCEPTION_CLASS("Error, couln't get the current entry info");
return ZipEntry(std::string(filename_inzip), file_info.compressed_size, file_info.uncompressed_size,
file_info.tmu_date.tm_year, file_info.tmu_date.tm_mon, file_info.tmu_date.tm_mday,
file_info.tmu_date.tm_hour, file_info.tmu_date.tm_min, file_info.tmu_date.tm_sec, file_info.dosDate);
}
#if 0
// lambda as a parameter https://en.wikipedia.org/wiki/C%2B%2B11#Polymorphic_wrappers_for_function_objects
void iterEntries(std::function<void(ZipEntry&)> callback)
{
int err = unzGoToFirstFile(m_zf);
if (UNZ_OK == err)
{
do
{
ZipEntry entryinfo = currentEntryInfo();
if (entryinfo.valid())
{
callback(entryinfo);
err = unzGoToNextFile(m_zf);
}
else
err = UNZ_ERRNO;
} while (UNZ_OK == err);
if (UNZ_END_OF_LIST_OF_FILE != err && UNZ_OK != err)
return;
}
}
#endif
void getEntries(std::vector<ZipEntry>& entries)
{
int err = unzGoToFirstFile(m_zf);
if (UNZ_OK == err)
{
do
{
ZipEntry entryinfo = currentEntryInfo();
if (entryinfo.valid())
{
entries.push_back(entryinfo);
err = unzGoToNextFile(m_zf);
}
else
err = UNZ_ERRNO;
} while (UNZ_OK == err);
if (UNZ_END_OF_LIST_OF_FILE != err && UNZ_OK != err)
return;
}
}
public:
#if 0
bool extractCurrentEntry(ZipEntry& entryinfo, int (extractStrategy)(ZipEntry&) )
{
int err = UNZ_OK;
if (!entryinfo.valid())
return false;
err = extractStrategy(entryinfo);
if (UNZ_OK == err)
{
err = unzCloseCurrentFile(m_zf);
if (UNZ_OK != err)
throw EXCEPTION_CLASS(("Error " + std::to_string(err) + " closing internal file '" + entryinfo.name +
"' in zip").c_str());
}
return UNZ_OK == err;
}
#endif
bool extractCurrentEntryToFile(ZipEntry& entryinfo, const std::string& fileName)
{
int err = UNZ_OK;
if (!entryinfo.valid())
return false;
err = extractToFile(fileName, entryinfo);
if (UNZ_OK == err)
{
err = unzCloseCurrentFile(m_zf);
if (UNZ_OK != err)
{
std::stringstream str;
str << "Error " << err << " openinginternal file '"
<< entryinfo.name << "' in zip";
throw EXCEPTION_CLASS(str.str().c_str());
}
}
return UNZ_OK == err;
}
bool extractCurrentEntryToStream(ZipEntry& entryinfo, std::ostream& stream)
{
int err = UNZ_OK;
if (!entryinfo.valid())
return false;
err = extractToStream(stream, entryinfo);
if (UNZ_OK == err)
{
err = unzCloseCurrentFile(m_zf);
if (UNZ_OK != err)
{
std::stringstream str;
str << "Error " << err << " opening internal file '"
<< entryinfo.name << "' in zip";
throw EXCEPTION_CLASS(str.str().c_str());
}
}
return UNZ_OK == err;
}
bool extractCurrentEntryToMemory(ZipEntry& entryinfo, std::vector<unsigned char>& outvec)
{
int err = UNZ_OK;
if (!entryinfo.valid())
return false;
err = extractToMemory(outvec, entryinfo);
if (UNZ_OK == err)
{
err = unzCloseCurrentFile(m_zf);
if (UNZ_OK != err)
{
std::stringstream str;
str << "Error " << err << " opening internal file '"
<< entryinfo.name << "' in zip";
throw EXCEPTION_CLASS(str.str().c_str());
}
}
return UNZ_OK == err;
}
void changeFileDate(const std::string& filename, uLong dosdate, tm_unz tmu_date)
{
#ifdef _WIN32
HANDLE hFile;
FILETIME ftm, ftLocal, ftCreate, ftLastAcc, ftLastWrite;
hFile = CreateFileA(filename.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
GetFileTime(hFile, &ftCreate, &ftLastAcc, &ftLastWrite);
DosDateTimeToFileTime((WORD)(dosdate >> 16), (WORD)dosdate, &ftLocal);
LocalFileTimeToFileTime(&ftLocal, &ftm);
SetFileTime(hFile, &ftm, &ftLastAcc, &ftm);
CloseHandle(hFile);
}
#else
#if defined unix || defined __APPLE__
struct utimbuf ut;
struct tm newdate;
newdate.tm_sec = tmu_date.tm_sec;
newdate.tm_min = tmu_date.tm_min;
newdate.tm_hour = tmu_date.tm_hour;
newdate.tm_mday = tmu_date.tm_mday;
newdate.tm_mon = tmu_date.tm_mon;
if (tmu_date.tm_year > 1900)
newdate.tm_year = tmu_date.tm_year - 1900;
else
newdate.tm_year = tmu_date.tm_year;
newdate.tm_isdst = -1;
ut.actime = ut.modtime = mktime(&newdate);
utime(filename.c_str(), &ut);
#endif
#endif
}
int extractToFile(const std::string& filename, ZipEntry& info)
{
int err = UNZ_ERRNO;
/* If zip entry is a directory then create it on disk */
makedir(parentDirectory(filename));
/* Create the file on disk so we can unzip to it */
std::ofstream output_file(filename.c_str(), std::ofstream::binary);
if (output_file.good())
{
if (extractToStream(output_file, info))
err = UNZ_OK;
output_file.close();
/* Set the time of the file that has been unzipped */
tm_unz timeaux;
memcpy(&timeaux, &info.unixdate, sizeof(timeaux));
changeFileDate(filename, info.dosdate, timeaux);
}
else
output_file.close();
return err;
}
int extractToStream(std::ostream& stream, ZipEntry& info)
{
size_t err = UNZ_ERRNO;
err = unzOpenCurrentFilePassword(m_zf, m_outer.m_password.c_str());
if (UNZ_OK != err)
{
std::stringstream str;
str << "Error " << err << " opening internal file '"
<< info.name << "' in zip";
throw EXCEPTION_CLASS(str.str().c_str());
}
std::vector<char> buffer;
buffer.resize(WRITEBUFFERSIZE);
do
{
err = unzReadCurrentFile(m_zf, buffer.data(), (unsigned int)buffer.size());
if (err < 0 || err == 0)
break;
stream.write(buffer.data(), err);
if (!stream.good())
{
err = UNZ_ERRNO;
break;
}
} while (err > 0);
stream.flush();
return (int)err;
}
int extractToMemory(std::vector<unsigned char>& outvec, ZipEntry& info)
{
size_t err = UNZ_ERRNO;
err = unzOpenCurrentFilePassword(m_zf, m_outer.m_password.c_str());
if (UNZ_OK != err)
{
std::stringstream str;
str << "Error " << err << " opening internal file '"
<< info.name << "' in zip";
throw EXCEPTION_CLASS(str.str().c_str());
}
std::vector<unsigned char> buffer;
buffer.resize(WRITEBUFFERSIZE);
outvec.reserve((size_t)info.uncompressedSize);
do
{
err = unzReadCurrentFile(m_zf, buffer.data(), (unsigned int)buffer.size());
if (err < 0 || err == 0)
break;
outvec.insert(outvec.end(), buffer.data(), buffer.data() + err);
} while (err > 0);
return (int)err;
}
public:
Impl(Unzipper& outer) : m_outer(outer), m_zipmem(), m_filefunc()
{
m_zf = NULL;
}
~Impl()
{
}
void close()
{
if (m_zf)
unzClose(m_zf);
}
bool initFile(const std::string& filename)
{
#ifdef USEWIN32IOAPI
zlib_filefunc64_def ffunc;
fill_win32_filefunc64A(&ffunc);
m_zf = unzOpen2_64(filename.c_str(), &ffunc);
#else
m_zf = unzOpen64(filename.c_str());
#endif
return m_zf != NULL;
}
bool initWithStream(std::istream& stream)
{
stream.seekg(0, std::ios::end);
size_t size = (size_t)stream.tellg();
stream.seekg(0);
if (size > 0)
{
m_zipmem.base = new char[(size_t)size];
stream.read(m_zipmem.base, size);
}
fill_memory_filefunc(&m_filefunc, &m_zipmem);
return initMemory(m_filefunc);
}
bool initWithVector(std::vector<unsigned char>& buffer)
{
if (!buffer.empty())
{
m_zipmem.base = (char*)buffer.data();
m_zipmem.size = (uLong)buffer.size();
}
fill_memory_filefunc(&m_filefunc, &m_zipmem);
return initMemory(m_filefunc);
}
std::vector<ZipEntry> entries()
{
std::vector<ZipEntry> entrylist;
getEntries(entrylist);
return entrylist;
}
bool extractAll(const std::string& destination, const std::map<std::string, std::string>& alternativeNames)
{
std::vector<ZipEntry> entries;
getEntries(entries);
std::vector<ZipEntry>::iterator it = entries.begin();
for (; it != entries.end(); ++it)
{
if (!locateEntry(it->name))
continue;
std::string alternativeName = destination.empty() ? "" : destination + "/";
if (alternativeNames.find(it->name) != alternativeNames.end())
alternativeName += alternativeNames.at(it->name);
else
alternativeName += it->name;
this->extractCurrentEntryToFile(*it, alternativeName);
};
return true;
}
bool extractEntry(const std::string& name, const std::string& destination)
{
std::string outputFile = destination.empty() ? name : destination + "\\" + name;
if (locateEntry(name))
{
ZipEntry entry = currentEntryInfo();
return extractCurrentEntryToFile(entry, outputFile);
}
else
{
return false;
}
}
bool extractEntryToStream(const std::string& name, std::ostream& stream)
{
if (locateEntry(name))
{
ZipEntry entry = currentEntryInfo();
return extractCurrentEntryToStream(entry, stream);
}
else
{
return false;
}
}
bool extractEntryToMemory(const std::string& name, std::vector<unsigned char>& vec)
{
if (locateEntry(name))
{
ZipEntry entry = currentEntryInfo();
return extractCurrentEntryToMemory(entry, vec);
}
else
{
return false;
}
}
};
Unzipper::Unzipper(std::istream& zippedBuffer)
: m_ibuffer(zippedBuffer)
, m_vecbuffer(*(new std::vector<unsigned char>())) //not used but using local variable throws exception
, m_usingMemoryVector(false)
, m_usingStream(true)
, m_impl(new Impl(*this))
{
if (!m_impl->initWithStream(m_ibuffer))
throw EXCEPTION_CLASS("Error loading zip in memory!");
m_open = true;
}
Unzipper::Unzipper(std::vector<unsigned char>& zippedBuffer)
: m_ibuffer(*(new std::stringstream())) //not used but using local variable throws exception
, m_vecbuffer(zippedBuffer)
, m_usingMemoryVector(true)
, m_usingStream(false)
, m_impl(new Impl(*this))
{
if (!m_impl->initWithVector(m_vecbuffer))
throw EXCEPTION_CLASS("Error loading zip in memory!");
m_open = true;
}
Unzipper::Unzipper(const std::string& zipname)
: m_ibuffer(*(new std::stringstream())) //not used but using local variable throws exception
, m_vecbuffer(*(new std::vector<unsigned char>())) //not used but using local variable throws exception
, m_zipname(zipname)
, m_usingMemoryVector(false)
, m_usingStream(false)
, m_impl(new Impl(*this))
{
if (!m_impl->initFile(zipname))
throw EXCEPTION_CLASS("Error loading zip file!");
m_open = true;
}
Unzipper::Unzipper(const std::string& zipname, const std::string& password)
: m_ibuffer(*(new std::stringstream())) //not used but using local variable throws exception
, m_vecbuffer(*(new std::vector<unsigned char>())) //not used but using local variable throws exception
, m_zipname(zipname)
, m_password(password)
, m_usingMemoryVector(false)
, m_usingStream(false)
, m_impl(new Impl(*this))
{
if (!m_impl->initFile(zipname))
throw EXCEPTION_CLASS("Error loading zip file!");
m_open = true;
}
Unzipper::~Unzipper(void)
{
close();
}
std::vector<ZipEntry> Unzipper::entries()
{
return m_impl->entries();
}
bool Unzipper::extractEntry(const std::string& name, const std::string& destination)
{
return m_impl->extractEntry(name, destination);
}
bool Unzipper::extractEntryToStream(const std::string& name, std::ostream& stream)
{
return m_impl->extractEntryToStream(name, stream);
}
bool Unzipper::extractEntryToMemory(const std::string& name, std::vector<unsigned char>& vec)
{
return m_impl->extractEntryToMemory(name, vec);
}
bool Unzipper::extract(const std::string& destination, const std::map<std::string, std::string>& alternativeNames)
{
return m_impl->extractAll(destination, alternativeNames);
}
bool
Unzipper::extract(const std::string& destination)
{
return m_impl->extractAll(destination, std::map<std::string, std::string>());
}
void Unzipper::close()
{
if (m_open)
{
m_impl->close();
m_open = false;
}
}
}