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 }