b230cf3aa2
Update to latest quazip release. Note that quazip is now LGPL and not GPL / LGPL dual licensed anymore. Change-Id: Ie1e975b5b546dd31218eef9df472527493fe81e0
502 lines
14 KiB
C++
502 lines
14 KiB
C++
/*
|
|
Copyright (C) 2005-2014 Sergey A. Tachenov
|
|
|
|
This file is part of QuaZIP.
|
|
|
|
QuaZIP is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation, either version 2.1 of the License, or
|
|
(at your option) any later version.
|
|
|
|
QuaZIP is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with QuaZIP. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
See COPYING file for the full LGPL text.
|
|
|
|
Original ZIP package is copyrighted by Gilles Vollant, see
|
|
quazip/(un)zip.h files for details, basically it's zlib license.
|
|
**/
|
|
|
|
#include "quazipfile.h"
|
|
|
|
using namespace std;
|
|
|
|
/// The implementation class for QuaZip.
|
|
/**
|
|
\internal
|
|
|
|
This class contains all the private stuff for the QuaZipFile class, thus
|
|
allowing to preserve binary compatibility between releases, the
|
|
technique known as the Pimpl (private implementation) idiom.
|
|
*/
|
|
class QuaZipFilePrivate {
|
|
friend class QuaZipFile;
|
|
private:
|
|
/// The pointer to the associated QuaZipFile instance.
|
|
QuaZipFile *q;
|
|
/// The QuaZip object to work with.
|
|
QuaZip *zip;
|
|
/// The file name.
|
|
QString fileName;
|
|
/// Case sensitivity mode.
|
|
QuaZip::CaseSensitivity caseSensitivity;
|
|
/// Whether this file is opened in the raw mode.
|
|
bool raw;
|
|
/// Write position to keep track of.
|
|
/**
|
|
QIODevice::pos() is broken for non-seekable devices, so we need
|
|
our own position.
|
|
*/
|
|
qint64 writePos;
|
|
/// Uncompressed size to write along with a raw file.
|
|
quint64 uncompressedSize;
|
|
/// CRC to write along with a raw file.
|
|
quint32 crc;
|
|
/// Whether \ref zip points to an internal QuaZip instance.
|
|
/**
|
|
This is true if the archive was opened by name, rather than by
|
|
supplying an existing QuaZip instance.
|
|
*/
|
|
bool internal;
|
|
/// The last error.
|
|
int zipError;
|
|
/// Resets \ref zipError.
|
|
inline void resetZipError() const {setZipError(UNZ_OK);}
|
|
/// Sets the zip error.
|
|
/**
|
|
This function is marked as const although it changes one field.
|
|
This allows to call it from const functions that don't change
|
|
anything by themselves.
|
|
*/
|
|
void setZipError(int zipError) const;
|
|
/// The constructor for the corresponding QuaZipFile constructor.
|
|
inline QuaZipFilePrivate(QuaZipFile *q):
|
|
q(q), zip(NULL), internal(true), zipError(UNZ_OK) {}
|
|
/// The constructor for the corresponding QuaZipFile constructor.
|
|
inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName):
|
|
q(q), internal(true), zipError(UNZ_OK)
|
|
{
|
|
zip=new QuaZip(zipName);
|
|
}
|
|
/// The constructor for the corresponding QuaZipFile constructor.
|
|
inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName, const QString &fileName,
|
|
QuaZip::CaseSensitivity cs):
|
|
q(q), internal(true), zipError(UNZ_OK)
|
|
{
|
|
zip=new QuaZip(zipName);
|
|
this->fileName=fileName;
|
|
if (this->fileName.startsWith('/'))
|
|
this->fileName = this->fileName.mid(1);
|
|
this->caseSensitivity=cs;
|
|
}
|
|
/// The constructor for the QuaZipFile constructor accepting a file name.
|
|
inline QuaZipFilePrivate(QuaZipFile *q, QuaZip *zip):
|
|
q(q), zip(zip), internal(false), zipError(UNZ_OK) {}
|
|
/// The destructor.
|
|
inline ~QuaZipFilePrivate()
|
|
{
|
|
if (internal)
|
|
delete zip;
|
|
}
|
|
};
|
|
|
|
QuaZipFile::QuaZipFile():
|
|
p(new QuaZipFilePrivate(this))
|
|
{
|
|
}
|
|
|
|
QuaZipFile::QuaZipFile(QObject *parent):
|
|
QIODevice(parent),
|
|
p(new QuaZipFilePrivate(this))
|
|
{
|
|
}
|
|
|
|
QuaZipFile::QuaZipFile(const QString& zipName, QObject *parent):
|
|
QIODevice(parent),
|
|
p(new QuaZipFilePrivate(this, zipName))
|
|
{
|
|
}
|
|
|
|
QuaZipFile::QuaZipFile(const QString& zipName, const QString& fileName,
|
|
QuaZip::CaseSensitivity cs, QObject *parent):
|
|
QIODevice(parent),
|
|
p(new QuaZipFilePrivate(this, zipName, fileName, cs))
|
|
{
|
|
}
|
|
|
|
QuaZipFile::QuaZipFile(QuaZip *zip, QObject *parent):
|
|
QIODevice(parent),
|
|
p(new QuaZipFilePrivate(this, zip))
|
|
{
|
|
}
|
|
|
|
QuaZipFile::~QuaZipFile()
|
|
{
|
|
if (isOpen())
|
|
close();
|
|
delete p;
|
|
}
|
|
|
|
QString QuaZipFile::getZipName() const
|
|
{
|
|
return p->zip==NULL ? QString() : p->zip->getZipName();
|
|
}
|
|
|
|
QuaZip *QuaZipFile::getZip() const
|
|
{
|
|
return p->internal ? NULL : p->zip;
|
|
}
|
|
|
|
QString QuaZipFile::getActualFileName()const
|
|
{
|
|
p->setZipError(UNZ_OK);
|
|
if (p->zip == NULL || (openMode() & WriteOnly))
|
|
return QString();
|
|
QString name=p->zip->getCurrentFileName();
|
|
if(name.isNull())
|
|
p->setZipError(p->zip->getZipError());
|
|
return name;
|
|
}
|
|
|
|
void QuaZipFile::setZipName(const QString& zipName)
|
|
{
|
|
if(isOpen()) {
|
|
qWarning("QuaZipFile::setZipName(): file is already open - can not set ZIP name");
|
|
return;
|
|
}
|
|
if(p->zip!=NULL && p->internal)
|
|
delete p->zip;
|
|
p->zip=new QuaZip(zipName);
|
|
p->internal=true;
|
|
}
|
|
|
|
void QuaZipFile::setZip(QuaZip *zip)
|
|
{
|
|
if(isOpen()) {
|
|
qWarning("QuaZipFile::setZip(): file is already open - can not set ZIP");
|
|
return;
|
|
}
|
|
if(p->zip!=NULL && p->internal)
|
|
delete p->zip;
|
|
p->zip=zip;
|
|
p->fileName=QString();
|
|
p->internal=false;
|
|
}
|
|
|
|
void QuaZipFile::setFileName(const QString& fileName, QuaZip::CaseSensitivity cs)
|
|
{
|
|
if(p->zip==NULL) {
|
|
qWarning("QuaZipFile::setFileName(): call setZipName() first");
|
|
return;
|
|
}
|
|
if(!p->internal) {
|
|
qWarning("QuaZipFile::setFileName(): should not be used when not using internal QuaZip");
|
|
return;
|
|
}
|
|
if(isOpen()) {
|
|
qWarning("QuaZipFile::setFileName(): can not set file name for already opened file");
|
|
return;
|
|
}
|
|
p->fileName=fileName;
|
|
if (p->fileName.startsWith('/'))
|
|
p->fileName = p->fileName.mid(1);
|
|
p->caseSensitivity=cs;
|
|
}
|
|
|
|
void QuaZipFilePrivate::setZipError(int zipError) const
|
|
{
|
|
QuaZipFilePrivate *fakeThis = const_cast<QuaZipFilePrivate*>(this); // non-const
|
|
fakeThis->zipError=zipError;
|
|
if(zipError==UNZ_OK)
|
|
q->setErrorString(QString());
|
|
else
|
|
q->setErrorString(QuaZipFile::tr("ZIP/UNZIP API error %1").arg(zipError));
|
|
}
|
|
|
|
bool QuaZipFile::open(OpenMode mode)
|
|
{
|
|
return open(mode, NULL);
|
|
}
|
|
|
|
bool QuaZipFile::open(OpenMode mode, int *method, int *level, bool raw, const char *password)
|
|
{
|
|
p->resetZipError();
|
|
if(isOpen()) {
|
|
qWarning("QuaZipFile::open(): already opened");
|
|
return false;
|
|
}
|
|
if(mode&Unbuffered) {
|
|
qWarning("QuaZipFile::open(): Unbuffered mode is not supported");
|
|
return false;
|
|
}
|
|
if((mode&ReadOnly)&&!(mode&WriteOnly)) {
|
|
if(p->internal) {
|
|
if(!p->zip->open(QuaZip::mdUnzip)) {
|
|
p->setZipError(p->zip->getZipError());
|
|
return false;
|
|
}
|
|
if(!p->zip->setCurrentFile(p->fileName, p->caseSensitivity)) {
|
|
p->setZipError(p->zip->getZipError());
|
|
p->zip->close();
|
|
return false;
|
|
}
|
|
} else {
|
|
if(p->zip==NULL) {
|
|
qWarning("QuaZipFile::open(): zip is NULL");
|
|
return false;
|
|
}
|
|
if(p->zip->getMode()!=QuaZip::mdUnzip) {
|
|
qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d",
|
|
(int)mode, (int)p->zip->getMode());
|
|
return false;
|
|
}
|
|
if(!p->zip->hasCurrentFile()) {
|
|
qWarning("QuaZipFile::open(): zip does not have current file");
|
|
return false;
|
|
}
|
|
}
|
|
p->setZipError(unzOpenCurrentFile3(p->zip->getUnzFile(), method, level, (int)raw, password));
|
|
if(p->zipError==UNZ_OK) {
|
|
setOpenMode(mode);
|
|
p->raw=raw;
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode);
|
|
return false;
|
|
}
|
|
|
|
bool QuaZipFile::open(OpenMode mode, const QuaZipNewInfo& info,
|
|
const char *password, quint32 crc,
|
|
int method, int level, bool raw,
|
|
int windowBits, int memLevel, int strategy)
|
|
{
|
|
zip_fileinfo info_z;
|
|
p->resetZipError();
|
|
if(isOpen()) {
|
|
qWarning("QuaZipFile::open(): already opened");
|
|
return false;
|
|
}
|
|
if((mode&WriteOnly)&&!(mode&ReadOnly)) {
|
|
if(p->internal) {
|
|
qWarning("QuaZipFile::open(): write mode is incompatible with internal QuaZip approach");
|
|
return false;
|
|
}
|
|
if(p->zip==NULL) {
|
|
qWarning("QuaZipFile::open(): zip is NULL");
|
|
return false;
|
|
}
|
|
if(p->zip->getMode()!=QuaZip::mdCreate&&p->zip->getMode()!=QuaZip::mdAppend&&p->zip->getMode()!=QuaZip::mdAdd) {
|
|
qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d",
|
|
(int)mode, (int)p->zip->getMode());
|
|
return false;
|
|
}
|
|
info_z.tmz_date.tm_year=info.dateTime.date().year();
|
|
info_z.tmz_date.tm_mon=info.dateTime.date().month() - 1;
|
|
info_z.tmz_date.tm_mday=info.dateTime.date().day();
|
|
info_z.tmz_date.tm_hour=info.dateTime.time().hour();
|
|
info_z.tmz_date.tm_min=info.dateTime.time().minute();
|
|
info_z.tmz_date.tm_sec=info.dateTime.time().second();
|
|
info_z.dosDate = 0;
|
|
info_z.internal_fa=(uLong)info.internalAttr;
|
|
info_z.external_fa=(uLong)info.externalAttr;
|
|
if (p->zip->isDataDescriptorWritingEnabled())
|
|
zipSetFlags(p->zip->getZipFile(), ZIP_WRITE_DATA_DESCRIPTOR);
|
|
else
|
|
zipClearFlags(p->zip->getZipFile(), ZIP_WRITE_DATA_DESCRIPTOR);
|
|
p->setZipError(zipOpenNewFileInZip3_64(p->zip->getZipFile(),
|
|
p->zip->getFileNameCodec()->fromUnicode(info.name).constData(), &info_z,
|
|
info.extraLocal.constData(), info.extraLocal.length(),
|
|
info.extraGlobal.constData(), info.extraGlobal.length(),
|
|
p->zip->getCommentCodec()->fromUnicode(info.comment).constData(),
|
|
method, level, (int)raw,
|
|
windowBits, memLevel, strategy,
|
|
password, (uLong)crc, p->zip->isZip64Enabled()));
|
|
if(p->zipError==UNZ_OK) {
|
|
p->writePos=0;
|
|
setOpenMode(mode);
|
|
p->raw=raw;
|
|
if(raw) {
|
|
p->crc=crc;
|
|
p->uncompressedSize=info.uncompressedSize;
|
|
}
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode);
|
|
return false;
|
|
}
|
|
|
|
bool QuaZipFile::isSequential()const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
qint64 QuaZipFile::pos()const
|
|
{
|
|
if(p->zip==NULL) {
|
|
qWarning("QuaZipFile::pos(): call setZipName() or setZip() first");
|
|
return -1;
|
|
}
|
|
if(!isOpen()) {
|
|
qWarning("QuaZipFile::pos(): file is not open");
|
|
return -1;
|
|
}
|
|
if(openMode()&ReadOnly)
|
|
// QIODevice::pos() is broken for sequential devices,
|
|
// but thankfully bytesAvailable() returns the number of
|
|
// bytes buffered, so we know how far ahead we are.
|
|
return unztell(p->zip->getUnzFile()) - QIODevice::bytesAvailable();
|
|
else
|
|
return p->writePos;
|
|
}
|
|
|
|
bool QuaZipFile::atEnd()const
|
|
{
|
|
if(p->zip==NULL) {
|
|
qWarning("QuaZipFile::atEnd(): call setZipName() or setZip() first");
|
|
return false;
|
|
}
|
|
if(!isOpen()) {
|
|
qWarning("QuaZipFile::atEnd(): file is not open");
|
|
return false;
|
|
}
|
|
if(openMode()&ReadOnly)
|
|
// the same problem as with pos()
|
|
return QIODevice::bytesAvailable() == 0
|
|
&& unzeof(p->zip->getUnzFile())==1;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
qint64 QuaZipFile::size()const
|
|
{
|
|
if(!isOpen()) {
|
|
qWarning("QuaZipFile::atEnd(): file is not open");
|
|
return -1;
|
|
}
|
|
if(openMode()&ReadOnly)
|
|
return p->raw?csize():usize();
|
|
else
|
|
return p->writePos;
|
|
}
|
|
|
|
qint64 QuaZipFile::csize()const
|
|
{
|
|
unz_file_info64 info_z;
|
|
p->setZipError(UNZ_OK);
|
|
if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1;
|
|
p->setZipError(unzGetCurrentFileInfo64(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0));
|
|
if(p->zipError!=UNZ_OK)
|
|
return -1;
|
|
return info_z.compressed_size;
|
|
}
|
|
|
|
qint64 QuaZipFile::usize()const
|
|
{
|
|
unz_file_info64 info_z;
|
|
p->setZipError(UNZ_OK);
|
|
if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1;
|
|
p->setZipError(unzGetCurrentFileInfo64(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0));
|
|
if(p->zipError!=UNZ_OK)
|
|
return -1;
|
|
return info_z.uncompressed_size;
|
|
}
|
|
|
|
bool QuaZipFile::getFileInfo(QuaZipFileInfo *info)
|
|
{
|
|
QuaZipFileInfo64 info64;
|
|
if (getFileInfo(&info64)) {
|
|
info64.toQuaZipFileInfo(*info);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool QuaZipFile::getFileInfo(QuaZipFileInfo64 *info)
|
|
{
|
|
if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return false;
|
|
p->zip->getCurrentFileInfo(info);
|
|
p->setZipError(p->zip->getZipError());
|
|
return p->zipError==UNZ_OK;
|
|
}
|
|
|
|
void QuaZipFile::close()
|
|
{
|
|
p->resetZipError();
|
|
if(p->zip==NULL||!p->zip->isOpen()) return;
|
|
if(!isOpen()) {
|
|
qWarning("QuaZipFile::close(): file isn't open");
|
|
return;
|
|
}
|
|
if(openMode()&ReadOnly)
|
|
p->setZipError(unzCloseCurrentFile(p->zip->getUnzFile()));
|
|
else if(openMode()&WriteOnly)
|
|
if(isRaw()) p->setZipError(zipCloseFileInZipRaw64(p->zip->getZipFile(), p->uncompressedSize, p->crc));
|
|
else p->setZipError(zipCloseFileInZip(p->zip->getZipFile()));
|
|
else {
|
|
qWarning("Wrong open mode: %d", (int)openMode());
|
|
return;
|
|
}
|
|
if(p->zipError==UNZ_OK) setOpenMode(QIODevice::NotOpen);
|
|
else return;
|
|
if(p->internal) {
|
|
p->zip->close();
|
|
p->setZipError(p->zip->getZipError());
|
|
}
|
|
}
|
|
|
|
qint64 QuaZipFile::readData(char *data, qint64 maxSize)
|
|
{
|
|
p->setZipError(UNZ_OK);
|
|
qint64 bytesRead=unzReadCurrentFile(p->zip->getUnzFile(), data, (unsigned)maxSize);
|
|
if (bytesRead < 0) {
|
|
p->setZipError((int) bytesRead);
|
|
return -1;
|
|
}
|
|
return bytesRead;
|
|
}
|
|
|
|
qint64 QuaZipFile::writeData(const char* data, qint64 maxSize)
|
|
{
|
|
p->setZipError(ZIP_OK);
|
|
p->setZipError(zipWriteInFileInZip(p->zip->getZipFile(), data, (uint)maxSize));
|
|
if(p->zipError!=ZIP_OK) return -1;
|
|
else {
|
|
p->writePos+=maxSize;
|
|
return maxSize;
|
|
}
|
|
}
|
|
|
|
QString QuaZipFile::getFileName() const
|
|
{
|
|
return p->fileName;
|
|
}
|
|
|
|
QuaZip::CaseSensitivity QuaZipFile::getCaseSensitivity() const
|
|
{
|
|
return p->caseSensitivity;
|
|
}
|
|
|
|
bool QuaZipFile::isRaw() const
|
|
{
|
|
return p->raw;
|
|
}
|
|
|
|
int QuaZipFile::getZipError() const
|
|
{
|
|
return p->zipError;
|
|
}
|
|
|
|
qint64 QuaZipFile::bytesAvailable() const
|
|
{
|
|
return size() - pos();
|
|
}
|