File indexing completed on 2025-01-26 04:24:54

0001 /*
0002 Copyright (C) 2005-2014 Sergey A. Tachenov
0003 
0004 This file is part of QuaZIP.
0005 
0006 QuaZIP is free software: you can redistribute it and/or modify
0007 it under the terms of the GNU Lesser General Public License as published by
0008 the Free Software Foundation, either version 2.1 of the License, or
0009 (at your option) any later version.
0010 
0011 QuaZIP is distributed in the hope that it will be useful,
0012 but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 GNU Lesser General Public License for more details.
0015 
0016 You should have received a copy of the GNU Lesser General Public License
0017 along with QuaZIP.  If not, see <http://www.gnu.org/licenses/>.
0018 
0019 See COPYING file for the full LGPL text.
0020 
0021 Original ZIP package is copyrighted by Gilles Vollant, see
0022 quazip/(un)zip.h files for details, basically it's zlib license.
0023  **/
0024 
0025 #include "quazipfile.h"
0026 
0027 using namespace std;
0028 
0029 /// The implementation class for QuaZip.
0030 /**
0031 \internal
0032 
0033 This class contains all the private stuff for the QuaZipFile class, thus
0034 allowing to preserve binary compatibility between releases, the
0035 technique known as the Pimpl (private implementation) idiom.
0036 */
0037 class QuaZipFilePrivate {
0038   friend class QuaZipFile;
0039   private:
0040     Q_DISABLE_COPY(QuaZipFilePrivate)
0041     /// The pointer to the associated QuaZipFile instance.
0042     QuaZipFile *q;
0043     /// The QuaZip object to work with.
0044     QuaZip *zip;
0045     /// The file name.
0046     QString fileName;
0047     /// Case sensitivity mode.
0048     QuaZip::CaseSensitivity caseSensitivity;
0049     /// Whether this file is opened in the raw mode.
0050     bool raw;
0051     /// Write position to keep track of.
0052     /**
0053       QIODevice::pos() is broken for non-seekable devices, so we need
0054       our own position.
0055       */
0056     qint64 writePos;
0057     /// Uncompressed size to write along with a raw file.
0058     quint64 uncompressedSize;
0059     /// CRC to write along with a raw file.
0060     quint32 crc;
0061     /// Whether \ref zip points to an internal QuaZip instance.
0062     /**
0063       This is true if the archive was opened by name, rather than by
0064       supplying an existing QuaZip instance.
0065       */
0066     bool internal;
0067     /// The last error.
0068     int zipError;
0069     /// Resets \ref zipError.
0070     inline void resetZipError() const {setZipError(UNZ_OK);}
0071     /// Sets the zip error.
0072     /**
0073       This function is marked as const although it changes one field.
0074       This allows to call it from const functions that don't change
0075       anything by themselves.
0076       */
0077     void setZipError(int zipError) const;
0078     /// The constructor for the corresponding QuaZipFile constructor.
0079     inline QuaZipFilePrivate(QuaZipFile *q):
0080       q(q),
0081       zip(NULL),
0082       caseSensitivity(QuaZip::csDefault),
0083       raw(false),
0084       writePos(0),
0085       uncompressedSize(0),
0086       crc(0),
0087       internal(true),
0088       zipError(UNZ_OK) {}
0089     /// The constructor for the corresponding QuaZipFile constructor.
0090     inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName):
0091       q(q),
0092       caseSensitivity(QuaZip::csDefault),
0093       raw(false),
0094       writePos(0),
0095       uncompressedSize(0),
0096       crc(0),
0097       internal(true),
0098       zipError(UNZ_OK)
0099       {
0100         zip=new QuaZip(zipName);
0101       }
0102     /// The constructor for the corresponding QuaZipFile constructor.
0103     inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName, const QString &fileName,
0104         QuaZip::CaseSensitivity cs):
0105       q(q),
0106       raw(false),
0107       writePos(0),
0108       uncompressedSize(0),
0109       crc(0),
0110       internal(true),
0111       zipError(UNZ_OK)
0112       {
0113         zip=new QuaZip(zipName);
0114         this->fileName=fileName;
0115         if (this->fileName.startsWith('/'))
0116             this->fileName = this->fileName.mid(1);
0117         this->caseSensitivity=cs;
0118       }
0119     /// The constructor for the QuaZipFile constructor accepting a file name.
0120     inline QuaZipFilePrivate(QuaZipFile *q, QuaZip *zip):
0121       q(q),
0122       zip(zip),
0123       raw(false),
0124       writePos(0),
0125       uncompressedSize(0),
0126       crc(0),
0127       internal(false),
0128       zipError(UNZ_OK) {}
0129     /// The destructor.
0130     inline ~QuaZipFilePrivate()
0131     {
0132       if (internal)
0133         delete zip;
0134     }
0135 };
0136 
0137 QuaZipFile::QuaZipFile():
0138   p(new QuaZipFilePrivate(this))
0139 {
0140 }
0141 
0142 QuaZipFile::QuaZipFile(QObject *parent):
0143   QIODevice(parent),
0144   p(new QuaZipFilePrivate(this))
0145 {
0146 }
0147 
0148 QuaZipFile::QuaZipFile(const QString& zipName, QObject *parent):
0149   QIODevice(parent),
0150   p(new QuaZipFilePrivate(this, zipName))
0151 {
0152 }
0153 
0154 QuaZipFile::QuaZipFile(const QString& zipName, const QString& fileName,
0155     QuaZip::CaseSensitivity cs, QObject *parent):
0156   QIODevice(parent),
0157   p(new QuaZipFilePrivate(this, zipName, fileName, cs))
0158 {
0159 }
0160 
0161 QuaZipFile::QuaZipFile(QuaZip *zip, QObject *parent):
0162   QIODevice(parent),
0163   p(new QuaZipFilePrivate(this, zip))
0164 {
0165 }
0166 
0167 QuaZipFile::~QuaZipFile()
0168 {
0169   if (isOpen())
0170     close();
0171   delete p;
0172 }
0173 
0174 QString QuaZipFile::getZipName() const
0175 {
0176   return p->zip==NULL ? QString() : p->zip->getZipName();
0177 }
0178 
0179 QuaZip *QuaZipFile::getZip() const
0180 {
0181     return p->internal ? NULL : p->zip;
0182 }
0183 
0184 QString QuaZipFile::getActualFileName()const
0185 {
0186   p->setZipError(UNZ_OK);
0187   if (p->zip == NULL || (openMode() & WriteOnly))
0188     return QString();
0189   QString name=p->zip->getCurrentFileName();
0190   if(name.isNull())
0191     p->setZipError(p->zip->getZipError());
0192   return name;
0193 }
0194 
0195 void QuaZipFile::setZipName(const QString& zipName)
0196 {
0197   if(isOpen()) {
0198     qWarning("QuaZipFile::setZipName(): file is already open - can not set ZIP name");
0199     return;
0200   }
0201   if(p->zip!=NULL && p->internal)
0202     delete p->zip;
0203   p->zip=new QuaZip(zipName);
0204   p->internal=true;
0205 }
0206 
0207 void QuaZipFile::setZip(QuaZip *zip)
0208 {
0209   if(isOpen()) {
0210     qWarning("QuaZipFile::setZip(): file is already open - can not set ZIP");
0211     return;
0212   }
0213   if(p->zip!=NULL && p->internal)
0214     delete p->zip;
0215   p->zip=zip;
0216   p->fileName=QString();
0217   p->internal=false;
0218 }
0219 
0220 void QuaZipFile::setFileName(const QString& fileName, QuaZip::CaseSensitivity cs)
0221 {
0222   if(p->zip==NULL) {
0223     qWarning("QuaZipFile::setFileName(): call setZipName() first");
0224     return;
0225   }
0226   if(!p->internal) {
0227     qWarning("QuaZipFile::setFileName(): should not be used when not using internal QuaZip");
0228     return;
0229   }
0230   if(isOpen()) {
0231     qWarning("QuaZipFile::setFileName(): can not set file name for already opened file");
0232     return;
0233   }
0234   p->fileName=fileName;
0235   if (p->fileName.startsWith('/'))
0236       p->fileName = p->fileName.mid(1);
0237   p->caseSensitivity=cs;
0238 }
0239 
0240 void QuaZipFilePrivate::setZipError(int zipError) const
0241 {
0242   QuaZipFilePrivate *fakeThis = const_cast<QuaZipFilePrivate*>(this); // non-const
0243   fakeThis->zipError=zipError;
0244   if(zipError==UNZ_OK)
0245     q->setErrorString(QString());
0246   else
0247     q->setErrorString(QuaZipFile::tr("ZIP/UNZIP API error %1").arg(zipError));
0248 }
0249 
0250 bool QuaZipFile::open(OpenMode mode)
0251 {
0252   return open(mode, NULL);
0253 }
0254 
0255 bool QuaZipFile::open(OpenMode mode, int *method, int *level, bool raw, const char *password)
0256 {
0257   p->resetZipError();
0258   if(isOpen()) {
0259     qWarning("QuaZipFile::open(): already opened");
0260     return false;
0261   }
0262   if(mode&Unbuffered) {
0263     qWarning("QuaZipFile::open(): Unbuffered mode is not supported");
0264     return false;
0265   }
0266   if((mode&ReadOnly)&&!(mode&WriteOnly)) {
0267     if(p->internal) {
0268       if(!p->zip->open(QuaZip::mdUnzip)) {
0269         p->setZipError(p->zip->getZipError());
0270         return false;
0271       }
0272       if(!p->zip->setCurrentFile(p->fileName, p->caseSensitivity)) {
0273         p->setZipError(p->zip->getZipError());
0274         p->zip->close();
0275         return false;
0276       }
0277     } else {
0278       if(p->zip==NULL) {
0279         qWarning("QuaZipFile::open(): zip is NULL");
0280         return false;
0281       }
0282       if(p->zip->getMode()!=QuaZip::mdUnzip) {
0283         qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d",
0284             (int)mode, (int)p->zip->getMode());
0285         return false;
0286       }
0287       if(!p->zip->hasCurrentFile()) {
0288         qWarning("QuaZipFile::open(): zip does not have current file");
0289         return false;
0290       }
0291     }
0292     p->setZipError(unzOpenCurrentFile3(p->zip->getUnzFile(), method, level, (int)raw, password));
0293     if(p->zipError==UNZ_OK) {
0294       setOpenMode(mode);
0295       p->raw=raw;
0296       return true;
0297     } else
0298       return false;
0299   }
0300   qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode);
0301   return false;
0302 }
0303 
0304 bool QuaZipFile::open(OpenMode mode, const QuaZipNewInfo& info,
0305     const char *password, quint32 crc,
0306     int method, int level, bool raw,
0307     int windowBits, int memLevel, int strategy)
0308 {
0309   zip_fileinfo info_z;
0310   p->resetZipError();
0311   if(isOpen()) {
0312     qWarning("QuaZipFile::open(): already opened");
0313     return false;
0314   }
0315   if((mode&WriteOnly)&&!(mode&ReadOnly)) {
0316     if(p->internal) {
0317       qWarning("QuaZipFile::open(): write mode is incompatible with internal QuaZip approach");
0318       return false;
0319     }
0320     if(p->zip==NULL) {
0321       qWarning("QuaZipFile::open(): zip is NULL");
0322       return false;
0323     }
0324     if(p->zip->getMode()!=QuaZip::mdCreate&&p->zip->getMode()!=QuaZip::mdAppend&&p->zip->getMode()!=QuaZip::mdAdd) {
0325       qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d",
0326           (int)mode, (int)p->zip->getMode());
0327       return false;
0328     }
0329     info_z.tmz_date.tm_year=info.dateTime.date().year();
0330     info_z.tmz_date.tm_mon=info.dateTime.date().month() - 1;
0331     info_z.tmz_date.tm_mday=info.dateTime.date().day();
0332     info_z.tmz_date.tm_hour=info.dateTime.time().hour();
0333     info_z.tmz_date.tm_min=info.dateTime.time().minute();
0334     info_z.tmz_date.tm_sec=info.dateTime.time().second();
0335     info_z.dosDate = 0;
0336     info_z.internal_fa=(uLong)info.internalAttr;
0337     info_z.external_fa=(uLong)info.externalAttr;
0338     if (p->zip->isDataDescriptorWritingEnabled())
0339         zipSetFlags(p->zip->getZipFile(), ZIP_WRITE_DATA_DESCRIPTOR);
0340     else
0341         zipClearFlags(p->zip->getZipFile(), ZIP_WRITE_DATA_DESCRIPTOR);
0342     p->setZipError(zipOpenNewFileInZip3_64(p->zip->getZipFile(),
0343           p->zip->getFileNameCodec()->fromUnicode(info.name).constData(), &info_z,
0344           info.extraLocal.constData(), info.extraLocal.length(),
0345           info.extraGlobal.constData(), info.extraGlobal.length(),
0346           p->zip->getCommentCodec()->fromUnicode(info.comment).constData(),
0347           method, level, (int)raw,
0348           windowBits, memLevel, strategy,
0349           password, (uLong)crc, p->zip->isZip64Enabled()));
0350     if(p->zipError==UNZ_OK) {
0351       p->writePos=0;
0352       setOpenMode(mode);
0353       p->raw=raw;
0354       if(raw) {
0355         p->crc=crc;
0356         p->uncompressedSize=info.uncompressedSize;
0357       }
0358       return true;
0359     } else
0360       return false;
0361   }
0362   qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode);
0363   return false;
0364 }
0365 
0366 bool QuaZipFile::isSequential()const
0367 {
0368   return true;
0369 }
0370 
0371 qint64 QuaZipFile::pos()const
0372 {
0373   if(p->zip==NULL) {
0374     qWarning("QuaZipFile::pos(): call setZipName() or setZip() first");
0375     return -1;
0376   }
0377   if(!isOpen()) {
0378     qWarning("QuaZipFile::pos(): file is not open");
0379     return -1;
0380   }
0381   if(openMode()&ReadOnly)
0382       // QIODevice::pos() is broken for sequential devices,
0383       // but thankfully bytesAvailable() returns the number of
0384       // bytes buffered, so we know how far ahead we are.
0385     return unztell64(p->zip->getUnzFile()) - QIODevice::bytesAvailable();
0386   else
0387     return p->writePos;
0388 }
0389 
0390 bool QuaZipFile::atEnd()const
0391 {
0392   if(p->zip==NULL) {
0393     qWarning("QuaZipFile::atEnd(): call setZipName() or setZip() first");
0394     return false;
0395   }
0396   if(!isOpen()) {
0397     qWarning("QuaZipFile::atEnd(): file is not open");
0398     return false;
0399   }
0400   if(openMode()&ReadOnly)
0401       // the same problem as with pos()
0402     return QIODevice::bytesAvailable() == 0
0403         && unzeof(p->zip->getUnzFile())==1;
0404   else
0405     return true;
0406 }
0407 
0408 qint64 QuaZipFile::size()const
0409 {
0410   if(!isOpen()) {
0411     qWarning("QuaZipFile::atEnd(): file is not open");
0412     return -1;
0413   }
0414   if(openMode()&ReadOnly)
0415     return p->raw?csize():usize();
0416   else
0417     return p->writePos;
0418 }
0419 
0420 qint64 QuaZipFile::csize()const
0421 {
0422   unz_file_info64 info_z;
0423   p->setZipError(UNZ_OK);
0424   if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1;
0425   p->setZipError(unzGetCurrentFileInfo64(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0));
0426   if(p->zipError!=UNZ_OK)
0427     return -1;
0428   return info_z.compressed_size;
0429 }
0430 
0431 qint64 QuaZipFile::usize()const
0432 {
0433   unz_file_info64 info_z;
0434   p->setZipError(UNZ_OK);
0435   if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1;
0436   p->setZipError(unzGetCurrentFileInfo64(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0));
0437   if(p->zipError!=UNZ_OK)
0438     return -1;
0439   return info_z.uncompressed_size;
0440 }
0441 
0442 bool QuaZipFile::getFileInfo(QuaZipFileInfo *info)
0443 {
0444     QuaZipFileInfo64 info64;
0445     if (getFileInfo(&info64)) {
0446         info64.toQuaZipFileInfo(*info);
0447         return true;
0448     } else {
0449         return false;
0450     }
0451 }
0452 
0453 bool QuaZipFile::getFileInfo(QuaZipFileInfo64 *info)
0454 {
0455     if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return false;
0456     p->zip->getCurrentFileInfo(info);
0457     p->setZipError(p->zip->getZipError());
0458     return p->zipError==UNZ_OK;
0459 }
0460 
0461 void QuaZipFile::close()
0462 {
0463   p->resetZipError();
0464   if(p->zip==NULL||!p->zip->isOpen()) return;
0465   if(!isOpen()) {
0466     qWarning("QuaZipFile::close(): file isn't open");
0467     return;
0468   }
0469   if(openMode()&ReadOnly)
0470     p->setZipError(unzCloseCurrentFile(p->zip->getUnzFile()));
0471   else if(openMode()&WriteOnly)
0472     if(isRaw()) p->setZipError(zipCloseFileInZipRaw64(p->zip->getZipFile(), p->uncompressedSize, p->crc));
0473     else p->setZipError(zipCloseFileInZip(p->zip->getZipFile()));
0474   else {
0475     qWarning("Wrong open mode: %d", (int)openMode());
0476     return;
0477   }
0478   if(p->zipError==UNZ_OK) setOpenMode(QIODevice::NotOpen);
0479   else return;
0480   if(p->internal) {
0481     p->zip->close();
0482     p->setZipError(p->zip->getZipError());
0483   }
0484 }
0485 
0486 qint64 QuaZipFile::readData(char *data, qint64 maxSize)
0487 {
0488   p->setZipError(UNZ_OK);
0489   qint64 bytesRead=unzReadCurrentFile(p->zip->getUnzFile(), data, (unsigned)maxSize);
0490   if (bytesRead < 0) {
0491     p->setZipError((int) bytesRead);
0492     return -1;
0493   }
0494   return bytesRead;
0495 }
0496 
0497 qint64 QuaZipFile::writeData(const char* data, qint64 maxSize)
0498 {
0499   p->setZipError(ZIP_OK);
0500   p->setZipError(zipWriteInFileInZip(p->zip->getZipFile(), data, (uint)maxSize));
0501   if(p->zipError!=ZIP_OK) return -1;
0502   else {
0503     p->writePos+=maxSize;
0504     return maxSize;
0505   }
0506 }
0507 
0508 QString QuaZipFile::getFileName() const
0509 {
0510   return p->fileName;
0511 }
0512 
0513 QuaZip::CaseSensitivity QuaZipFile::getCaseSensitivity() const
0514 {
0515   return p->caseSensitivity;
0516 }
0517 
0518 bool QuaZipFile::isRaw() const
0519 {
0520   return p->raw;
0521 }
0522 
0523 int QuaZipFile::getZipError() const
0524 {
0525   return p->zipError;
0526 }
0527 
0528 qint64 QuaZipFile::bytesAvailable() const
0529 {
0530     return size() - pos();
0531 }