File indexing completed on 2025-02-16 13:00:35
0001 /* This file is part of the KDE libraries 0002 SPDX-FileCopyrightText: 2011 Mario Bensi <mbensi@ipsquad.net> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "k7zip.h" 0008 #include "karchive_p.h" 0009 #include "loggingcategory.h" 0010 0011 #include <QBuffer> 0012 #include <QDebug> 0013 #include <QDir> 0014 #include <QFile> 0015 #include <qplatformdefs.h> 0016 0017 #include "kcompressiondevice.h" 0018 #include "klimitediodevice_p.h" 0019 #include <kfilterbase.h> 0020 #include <kxzfilter.h> 0021 0022 #include "zlib.h" 0023 #include <memory> 0024 #include <time.h> // time() 0025 0026 #ifndef QT_STAT_LNK 0027 #define QT_STAT_LNK 0120000 0028 #endif // QT_STAT_LNK 0029 0030 //////////////////////////////////////////////////////////////////////// 0031 /////////////////////////// K7Zip ////////////////////////////////////// 0032 //////////////////////////////////////////////////////////////////////// 0033 0034 #define BUFFER_SIZE 8 * 1024 0035 0036 static const unsigned char k7zip_signature[6] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; 0037 // static const unsigned char XZ_HEADER_MAGIC[6] = { 0xFD, '7', 'z', 'X', 'Z', 0x00 }; 0038 0039 /* clang-format off */ 0040 static QChar GetUi16(const char *p, quint64 offset) 0041 { 0042 return QChar(static_cast<unsigned char>(p[offset + 0]) 0043 | (static_cast<unsigned char>(p[1]) << 8)); 0044 } 0045 0046 static quint32 GetUi32(const char *p, quint64 offset) 0047 { 0048 return (static_cast<unsigned char>(p[offset + 0]) 0049 | (static_cast<unsigned char>(p[offset + 1]) << 8) 0050 | (static_cast<unsigned char>(p[offset + 2]) << 16) 0051 | (static_cast<unsigned char>(p[offset + 3]) << 24)); 0052 } 0053 0054 static quint64 GetUi64(const char *p, quint64 offset) 0055 { 0056 return (GetUi32(p, offset) 0057 | (static_cast<quint64>(GetUi32(p, offset + 4)) << 32)); 0058 } 0059 0060 static quint32 lzma2_dic_size_from_prop(int p) 0061 { 0062 return ((static_cast<quint32>(2) | (p & 1)) << ((p / 2) + 11)); 0063 } 0064 0065 /* clang-format on*/ 0066 0067 #define FILE_ATTRIBUTE_READONLY 1 0068 #define FILE_ATTRIBUTE_HIDDEN 2 0069 #define FILE_ATTRIBUTE_SYSTEM 4 0070 #define FILE_ATTRIBUTE_DIRECTORY 16 0071 #define FILE_ATTRIBUTE_ARCHIVE 32 0072 #define FILE_ATTRIBUTE_DEVICE 64 0073 #define FILE_ATTRIBUTE_NORMAL 128 0074 #define FILE_ATTRIBUTE_TEMPORARY 256 0075 #define FILE_ATTRIBUTE_SPARSE_FILE 512 0076 #define FILE_ATTRIBUTE_REPARSE_POINT 1024 0077 #define FILE_ATTRIBUTE_COMPRESSED 2048 0078 #define FILE_ATTRIBUTE_OFFLINE 0x1000 0079 #define FILE_ATTRIBUTE_ENCRYPTED 0x4000 0080 #define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */ 0081 0082 enum HeaderType { 0083 kEnd, 0084 0085 kHeader, 0086 0087 kArchiveProperties, 0088 0089 kAdditionalStreamsInfo, 0090 kMainStreamsInfo, 0091 kFilesInfo, 0092 0093 kPackInfo, 0094 kUnpackInfo, 0095 kSubStreamsInfo, 0096 0097 kSize, 0098 kCRC, 0099 0100 kFolder, 0101 0102 kCodersUnpackSize, 0103 kNumUnpackStream, 0104 0105 kEmptyStream, 0106 kEmptyFile, 0107 kAnti, 0108 0109 kName, 0110 kCTime, 0111 kATime, 0112 kMTime, 0113 kAttributes, 0114 kComment, 0115 0116 kEncodedHeader, 0117 0118 kStartPos, 0119 kDummy, 0120 }; 0121 0122 // Method ID 0123 // static const quint64 k_Copy = 0x00; 0124 // static const quint64 k_Delta = 0x03; 0125 // static const quint64 k_x86 = 0x04; //BCJ 0126 // static const quint64 k_PPC = 0x05; // BIG Endian 0127 // static const quint64 k_IA64 = 0x06; 0128 // static const quint64 k_ARM = 0x07; // little Endian 0129 // static const quint64 k_ARM_Thumb = 0x08; // little Endian 0130 // static const quint64 k_SPARC = 0x09; 0131 static const quint64 k_LZMA2 = 0x21; 0132 // static const quint64 k_Swap2 = 0x020302; 0133 // static const quint64 k_Swap4 = 0x020304; 0134 static const quint64 k_LZMA = 0x030101; 0135 static const quint64 k_BCJ = 0x03030103; 0136 static const quint64 k_BCJ2 = 0x0303011B; 0137 // static const quint64 k_7zPPC = 0x03030205; 0138 // static const quint64 k_Alpha = 0x03030301; 0139 // static const quint64 k_7zIA64 = 0x03030401; 0140 // static const quint64 k_7zARM = 0x03030501; 0141 // static const quint64 k_M68 = 0x03030605; //Big Endian 0142 // static const quint64 k_ARMT = 0x03030701; 0143 // static const quint64 k_7zSPARC = 0x03030805; 0144 static const quint64 k_PPMD = 0x030401; 0145 // static const quint64 k_Experimental = 0x037F01; 0146 // static const quint64 k_Shrink = 0x040101; 0147 // static const quint64 k_Implode = 0x040106; 0148 // static const quint64 k_Deflate = 0x040108; 0149 // static const quint64 k_Deflate64 = 0x040109; 0150 // static const quint64 k_Imploding = 0x040110; 0151 // static const quint64 k_Jpeg = 0x040160; 0152 // static const quint64 k_WavPack = 0x040161; 0153 // static const quint64 k_PPMd = 0x040162; 0154 // static const quint64 k_wzAES = 0x040163; 0155 static const quint64 k_BZip2 = 0x040202; 0156 // static const quint64 k_Rar15 = 0x040301; 0157 // static const quint64 k_Rar20 = 0x040302; 0158 // static const quint64 k_Rar29 = 0x040303; 0159 // static const quint64 k_Arj = 0x040401; //1 2 3 0160 // static const quint64 k_Arj4 = 0x040402; 0161 // static const quint64 k_Z = 0x0405; 0162 // static const quint64 k_Lzh = 0x0406; 0163 // static const quint64 k_Cab = 0x0408; 0164 // static const quint64 k_DeflateNSIS = 0x040901; 0165 // static const quint64 k_Bzip2NSIS = 0x040902; 0166 static const quint64 k_AES = 0x06F10701; 0167 0168 /** 0169 * A K7ZipFileEntry represents a file in a 7zip archive. 0170 */ 0171 class K7ZipFileEntry : public KArchiveFile 0172 { 0173 public: 0174 K7ZipFileEntry(K7Zip *zip, 0175 const QString &name, 0176 int access, 0177 const QDateTime &date, 0178 const QString &user, 0179 const QString &group, 0180 const QString &symlink, 0181 qint64 pos, 0182 qint64 size, 0183 const QByteArray &data); 0184 0185 ~K7ZipFileEntry() override; 0186 0187 /** 0188 * @return the content of this file. 0189 * Call data() with care (only once per file), this data isn't cached. 0190 */ 0191 QByteArray data() const override; 0192 0193 /** 0194 * This method returns QIODevice (internal class: KLimitedIODevice) 0195 * on top of the underlying QIODevice. This is obviously for reading only. 0196 * 0197 * WARNING: Note that the ownership of the device is being transferred to the caller, 0198 * who will have to delete it. 0199 * 0200 * The returned device auto-opens (in readonly mode), no need to open it. 0201 * @return the QIODevice of the file 0202 */ 0203 QIODevice *createDevice() const override; 0204 0205 private: 0206 const QByteArray m_data; 0207 QBuffer *m_buffer; 0208 }; 0209 0210 K7ZipFileEntry::K7ZipFileEntry(K7Zip *zip, 0211 const QString &name, 0212 int access, 0213 const QDateTime &date, 0214 const QString &user, 0215 const QString &group, 0216 const QString &symlink, 0217 qint64 pos, 0218 qint64 size, 0219 const QByteArray &data) 0220 : KArchiveFile(zip, name, access, date, user, group, symlink, pos, size) 0221 , m_data(data) 0222 , m_buffer(new QBuffer) 0223 { 0224 m_buffer->setData(m_data); 0225 m_buffer->open(QIODevice::ReadOnly); 0226 } 0227 0228 K7ZipFileEntry::~K7ZipFileEntry() 0229 { 0230 delete m_buffer; 0231 } 0232 0233 QByteArray K7ZipFileEntry::data() const 0234 { 0235 return m_data.mid(position(), size()); 0236 } 0237 0238 QIODevice *K7ZipFileEntry::createDevice() const 0239 { 0240 return new KLimitedIODevice(m_buffer, position(), size()); 0241 } 0242 0243 class FileInfo 0244 { 0245 public: 0246 FileInfo() 0247 : size(0) 0248 , attributes(0) 0249 , crc(0) 0250 , attribDefined(false) 0251 , crcDefined(false) 0252 , hasStream(false) 0253 , isDir(false) 0254 { 0255 } 0256 0257 QString path; 0258 quint64 size; 0259 quint32 attributes; 0260 quint32 crc; 0261 bool attribDefined; 0262 bool crcDefined; 0263 bool hasStream; 0264 bool isDir; 0265 }; 0266 0267 class Folder 0268 { 0269 public: 0270 class FolderInfo 0271 { 0272 public: 0273 FolderInfo() 0274 : numInStreams(0) 0275 , numOutStreams(0) 0276 , methodID(0) 0277 { 0278 } 0279 0280 bool isSimpleCoder() const 0281 { 0282 return (numInStreams == 1) && (numOutStreams == 1); 0283 } 0284 0285 int numInStreams; 0286 int numOutStreams; 0287 QVector<unsigned char> properties; 0288 quint64 methodID; 0289 }; 0290 0291 Folder() 0292 : unpackCRCDefined(false) 0293 , unpackCRC(0) 0294 { 0295 } 0296 0297 ~Folder() 0298 { 0299 qDeleteAll(folderInfos); 0300 } 0301 0302 Q_DISABLE_COPY(Folder) 0303 0304 quint64 getUnpackSize() const 0305 { 0306 if (unpackSizes.isEmpty()) { 0307 return 0; 0308 } 0309 for (int i = unpackSizes.size() - 1; i >= 0; i--) { 0310 if (findBindPairForOutStream(i) < 0) { 0311 return unpackSizes.at(i); 0312 } 0313 } 0314 return 0; 0315 } 0316 0317 int getNumOutStreams() const 0318 { 0319 int result = 0; 0320 for (int i = 0; i < folderInfos.size(); i++) { 0321 result += folderInfos.at(i)->numOutStreams; 0322 } 0323 return result; 0324 } 0325 0326 quint32 getCoderInStreamIndex(quint32 coderIndex) const 0327 { 0328 quint32 streamIndex = 0; 0329 for (quint32 i = 0; i < coderIndex; i++) { 0330 streamIndex += folderInfos.at(i)->numInStreams; 0331 } 0332 return streamIndex; 0333 } 0334 0335 quint32 getCoderOutStreamIndex(quint32 coderIndex) const 0336 { 0337 quint32 streamIndex = 0; 0338 for (quint32 i = 0; i < coderIndex; i++) { 0339 streamIndex += folderInfos.at(i)->numOutStreams; 0340 } 0341 return streamIndex; 0342 } 0343 0344 int findBindPairForInStream(size_t inStreamIndex) const 0345 { 0346 for (int i = 0; i < inIndexes.size(); i++) { 0347 if (inIndexes[i] == inStreamIndex) { 0348 return i; 0349 } 0350 } 0351 return -1; 0352 } 0353 0354 int findBindPairForOutStream(size_t outStreamIndex) const 0355 { 0356 for (int i = 0; i < outIndexes.size(); i++) { 0357 if (outIndexes[i] == outStreamIndex) { 0358 return i; 0359 } 0360 } 0361 return -1; 0362 } 0363 0364 int findPackStreamArrayIndex(size_t inStreamIndex) const 0365 { 0366 for (int i = 0; i < packedStreams.size(); i++) { 0367 if (packedStreams[i] == inStreamIndex) { 0368 return i; 0369 } 0370 } 0371 return -1; 0372 } 0373 0374 void findInStream(quint32 streamIndex, quint32 &coderIndex, quint32 &coderStreamIndex) const 0375 { 0376 for (coderIndex = 0; coderIndex < (quint32)folderInfos.size(); coderIndex++) { 0377 quint32 curSize = folderInfos[coderIndex]->numInStreams; 0378 if (streamIndex < curSize) { 0379 coderStreamIndex = streamIndex; 0380 return; 0381 } 0382 streamIndex -= curSize; 0383 } 0384 } 0385 0386 void findOutStream(quint32 streamIndex, quint32 &coderIndex, quint32 &coderStreamIndex) const 0387 { 0388 for (coderIndex = 0; coderIndex < (quint32)folderInfos.size(); coderIndex++) { 0389 quint32 curSize = folderInfos[coderIndex]->numOutStreams; 0390 if (streamIndex < curSize) { 0391 coderStreamIndex = streamIndex; 0392 return; 0393 } 0394 streamIndex -= curSize; 0395 } 0396 } 0397 0398 bool isEncrypted() const 0399 { 0400 for (int i = folderInfos.size() - 1; i >= 0; i--) { 0401 if (folderInfos.at(i)->methodID == k_AES) { 0402 return true; 0403 } 0404 } 0405 return false; 0406 } 0407 0408 // bool CheckStructure() const; 0409 0410 bool unpackCRCDefined; 0411 quint32 unpackCRC; 0412 QVector<FolderInfo *> folderInfos; 0413 QVector<quint64> inIndexes; 0414 QVector<quint64> outIndexes; 0415 QVector<quint64> packedStreams; 0416 QVector<quint64> unpackSizes; 0417 }; 0418 0419 class Q_DECL_HIDDEN K7Zip::K7ZipPrivate 0420 { 0421 public: 0422 K7ZipPrivate(K7Zip *parent) 0423 : q(parent) 0424 , packPos(0) 0425 , numPackStreams(0) 0426 , buffer(nullptr) 0427 , pos(0) 0428 , end(0) 0429 , headerSize(0) 0430 , countSize(0) 0431 , m_currentFile(nullptr) 0432 { 0433 } 0434 0435 ~K7ZipPrivate() 0436 { 0437 qDeleteAll(folders); 0438 qDeleteAll(fileInfos); 0439 } 0440 0441 K7Zip *q; 0442 0443 QVector<bool> packCRCsDefined; 0444 QVector<quint32> packCRCs; 0445 QVector<quint64> numUnpackStreamsInFolders; 0446 0447 QVector<Folder *> folders; 0448 QVector<FileInfo *> fileInfos; 0449 // File information 0450 QVector<bool> cTimesDefined; 0451 QVector<quint64> cTimes; 0452 QVector<bool> aTimesDefined; 0453 QVector<quint64> aTimes; 0454 QVector<bool> mTimesDefined; 0455 QVector<quint64> mTimes; 0456 QVector<bool> startPositionsDefined; 0457 QVector<quint64> startPositions; 0458 QVector<int> fileInfoPopIDs; 0459 0460 quint64 packPos; 0461 quint64 numPackStreams; 0462 QVector<quint64> packSizes; 0463 QVector<quint64> unpackSizes; 0464 QVector<bool> digestsDefined; 0465 QVector<quint32> digests; 0466 0467 QVector<bool> isAnti; 0468 0469 const char *buffer; 0470 quint64 pos; 0471 quint64 end; 0472 quint64 headerSize; 0473 quint64 countSize; 0474 0475 // Write 0476 QByteArray header; 0477 QByteArray outData; // Store data in this buffer before compress and write in archive. 0478 K7ZipFileEntry *m_currentFile; 0479 QVector<KArchiveEntry *> m_entryList; 0480 0481 void clear() 0482 { 0483 packCRCsDefined.clear(); 0484 packCRCs.clear(); 0485 numUnpackStreamsInFolders.clear(); 0486 qDeleteAll(folders); 0487 folders.clear(); 0488 qDeleteAll(fileInfos); 0489 fileInfos.clear(); 0490 cTimesDefined.clear(); 0491 cTimes.clear(); 0492 aTimesDefined.clear(); 0493 aTimes.clear(); 0494 mTimesDefined.clear(); 0495 mTimes.clear(); 0496 startPositionsDefined.clear(); 0497 startPositions.clear(); 0498 fileInfoPopIDs.clear(); 0499 packSizes.clear(); 0500 unpackSizes.clear(); 0501 digestsDefined.clear(); 0502 digests.clear(); 0503 isAnti.clear(); 0504 0505 buffer = nullptr; 0506 pos = 0; 0507 end = 0; 0508 headerSize = 0; 0509 countSize = 0; 0510 } 0511 0512 // Read 0513 int readByte(); 0514 quint32 readUInt32(); 0515 quint64 readUInt64(); 0516 quint64 readNumber(); 0517 QString readString(); 0518 void readHashDigests(int numItems, QVector<bool> &digestsDefined, QVector<quint32> &digests); 0519 void readBoolVector(int numItems, QVector<bool> &v); 0520 void readBoolVector2(int numItems, QVector<bool> &v); 0521 void skipData(int size); 0522 bool findAttribute(int attribute); 0523 bool readUInt64DefVector(int numFiles, QVector<quint64> &values, QVector<bool> &defined); 0524 0525 Folder *folderItem(); 0526 bool readMainStreamsInfo(); 0527 bool readPackInfo(); 0528 bool readUnpackInfo(); 0529 bool readSubStreamsInfo(); 0530 QByteArray readAndDecodePackedStreams(bool readMainStreamInfo = true); 0531 0532 // Write 0533 void createItemsFromEntities(const KArchiveDirectory *, const QString &, QByteArray &); 0534 void writeByte(unsigned char b); 0535 void writeNumber(quint64 value); 0536 void writeBoolVector(const QVector<bool> &boolVector); 0537 void writeUInt32(quint32 value); 0538 void writeUInt64(quint64 value); 0539 void writeHashDigests(const QVector<bool> &digestsDefined, const QVector<quint32> &digests); 0540 void writeAlignedBoolHeader(const QVector<bool> &v, int numDefined, int type, unsigned itemSize); 0541 void writeUInt64DefVector(const QVector<quint64> &v, const QVector<bool> &defined, int type); 0542 void writeFolder(const Folder *folder); 0543 void writePackInfo(quint64 dataOffset, QVector<quint64> &packedSizes, QVector<bool> &packedCRCsDefined, QVector<quint32> &packedCRCs); 0544 void writeUnpackInfo(const QVector<Folder *> &folderItems); 0545 void writeSubStreamsInfo(const QVector<quint64> &unpackSizes, const QVector<bool> &digestsDefined, const QVector<quint32> &digests); 0546 void writeHeader(quint64 &headerOffset); 0547 void writeSignature(); 0548 void writeStartHeader(const quint64 nextHeaderSize, const quint32 nextHeaderCRC, const quint64 nextHeaderOffset); 0549 QByteArray encodeStream(QVector<quint64> &packSizes, QVector<Folder *> &folds); 0550 }; 0551 0552 K7Zip::K7Zip(const QString &fileName) 0553 : KArchive(fileName) 0554 , d(new K7ZipPrivate(this)) 0555 { 0556 } 0557 0558 K7Zip::K7Zip(QIODevice *dev) 0559 : KArchive(dev) 0560 , d(new K7ZipPrivate(this)) 0561 { 0562 Q_ASSERT(dev); 0563 } 0564 0565 K7Zip::~K7Zip() 0566 { 0567 if (isOpen()) { 0568 close(); 0569 } 0570 0571 delete d; 0572 } 0573 0574 int K7Zip::K7ZipPrivate::readByte() 0575 { 0576 if (!buffer || pos + 1 > end) { 0577 return -1; 0578 } 0579 return buffer[pos++]; 0580 } 0581 0582 quint32 K7Zip::K7ZipPrivate::readUInt32() 0583 { 0584 if (!buffer || (quint64)(pos + 4) > end) { 0585 qCDebug(KArchiveLog) << "error size"; 0586 return 0; 0587 } 0588 0589 quint32 res = GetUi32(buffer, pos); 0590 pos += 4; 0591 return res; 0592 } 0593 0594 quint64 K7Zip::K7ZipPrivate::readUInt64() 0595 { 0596 if (!buffer || (quint64)(pos + 8) > end) { 0597 qCDebug(KArchiveLog) << "error size"; 0598 return 0; 0599 } 0600 0601 quint64 res = GetUi64(buffer, pos); 0602 pos += 8; 0603 return res; 0604 } 0605 0606 quint64 K7Zip::K7ZipPrivate::readNumber() 0607 { 0608 if (!buffer || (quint64)(pos + 8) > end) { 0609 return 0; 0610 } 0611 0612 unsigned char firstByte = buffer[pos++]; 0613 unsigned char mask = 0x80; 0614 quint64 value = 0; 0615 for (int i = 0; i < 8; i++) { 0616 if ((firstByte & mask) == 0) { 0617 quint64 highPart = firstByte & (mask - 1); 0618 value += (highPart << (i * 8)); 0619 return value; 0620 } 0621 value |= ((unsigned char)buffer[pos++] << (8 * i)); 0622 mask >>= 1; 0623 } 0624 return value; 0625 } 0626 0627 QString K7Zip::K7ZipPrivate::readString() 0628 { 0629 if (!buffer) { 0630 return QString(); 0631 } 0632 0633 const char *buf = buffer + pos; 0634 size_t rem = (end - pos) / 2 * 2; 0635 { 0636 size_t i; 0637 for (i = 0; i < rem; i += 2) { 0638 if (buf[i] == 0 && buf[i + 1] == 0) { 0639 break; 0640 } 0641 } 0642 if (i == rem) { 0643 qCDebug(KArchiveLog) << "read string error"; 0644 return QString(); 0645 } 0646 rem = i; 0647 } 0648 0649 int len = (int)(rem / 2); 0650 if (len < 0 || (size_t)len * 2 != rem) { 0651 qCDebug(KArchiveLog) << "read string unsupported"; 0652 return QString(); 0653 } 0654 0655 QString p; 0656 for (int i = 0; i < len; i++, buf += 2) { 0657 p += GetUi16(buf, 0); 0658 } 0659 0660 pos += rem + 2; 0661 return p; 0662 } 0663 0664 void K7Zip::K7ZipPrivate::skipData(int size) 0665 { 0666 if (!buffer || pos + size > end) { 0667 return; 0668 } 0669 pos += size; 0670 } 0671 0672 bool K7Zip::K7ZipPrivate::findAttribute(int attribute) 0673 { 0674 if (!buffer) { 0675 return false; 0676 } 0677 0678 for (;;) { 0679 int type = readByte(); 0680 if (type == attribute) { 0681 return true; 0682 } 0683 if (type == kEnd) { 0684 return false; 0685 } 0686 skipData(readNumber()); 0687 } 0688 } 0689 0690 void K7Zip::K7ZipPrivate::readBoolVector(int numItems, QVector<bool> &v) 0691 { 0692 if (!buffer) { 0693 return; 0694 } 0695 0696 unsigned char b = 0; 0697 unsigned char mask = 0; 0698 for (int i = 0; i < numItems; i++) { 0699 if (mask == 0) { 0700 b = readByte(); 0701 mask = 0x80; 0702 } 0703 v.append((b & mask) != 0); 0704 mask >>= 1; 0705 } 0706 } 0707 0708 void K7Zip::K7ZipPrivate::readBoolVector2(int numItems, QVector<bool> &v) 0709 { 0710 if (!buffer) { 0711 return; 0712 } 0713 0714 int allAreDefined = readByte(); 0715 if (allAreDefined == 0) { 0716 readBoolVector(numItems, v); 0717 return; 0718 } 0719 0720 for (int i = 0; i < numItems; i++) { 0721 v.append(true); 0722 } 0723 } 0724 0725 void K7Zip::K7ZipPrivate::readHashDigests(int numItems, QVector<bool> &digestsDefined, QVector<quint32> &digests) 0726 { 0727 if (!buffer) { 0728 return; 0729 } 0730 0731 readBoolVector2(numItems, digestsDefined); 0732 for (int i = 0; i < numItems; i++) { 0733 quint32 crc = 0; 0734 if (digestsDefined[i]) { 0735 crc = GetUi32(buffer, pos); 0736 pos += 4; 0737 } 0738 digests.append(crc); 0739 } 0740 } 0741 0742 Folder *K7Zip::K7ZipPrivate::folderItem() 0743 { 0744 if (!buffer) { 0745 return nullptr; 0746 } 0747 0748 Folder *folder = new Folder; 0749 int numCoders = readNumber(); 0750 0751 quint64 numInStreamsTotal = 0; 0752 quint64 numOutStreamsTotal = 0; 0753 for (int i = 0; i < numCoders; ++i) { 0754 // BYTE 0755 // { 0756 // 0:3 CodecIdSize 0757 // 4: Is Complex Coder 0758 // 5: There Are Attributes 0759 // 6: Reserved 0760 // 7: There are more alternative methods. (Not used 0761 // anymore, must be 0). 0762 // } 0763 unsigned char coderInfo = readByte(); 0764 int codecIdSize = (coderInfo & 0xF); 0765 if (codecIdSize > 8) { 0766 qCDebug(KArchiveLog) << "unsupported codec id size"; 0767 delete folder; 0768 return nullptr; 0769 } 0770 Folder::FolderInfo *info = new Folder::FolderInfo(); 0771 std::unique_ptr<unsigned char[]> codecID(new unsigned char[codecIdSize]); 0772 for (int i = 0; i < codecIdSize; ++i) { 0773 codecID[i] = readByte(); 0774 } 0775 0776 int id = 0; 0777 for (int j = 0; j < codecIdSize; j++) { 0778 id |= codecID[codecIdSize - 1 - j] << (8 * j); 0779 } 0780 info->methodID = id; 0781 0782 // if (Is Complex Coder) 0783 if ((coderInfo & 0x10) != 0) { 0784 info->numInStreams = readNumber(); 0785 info->numOutStreams = readNumber(); 0786 } else { 0787 info->numInStreams = 1; 0788 info->numOutStreams = 1; 0789 } 0790 0791 // if (There Are Attributes) 0792 if ((coderInfo & 0x20) != 0) { 0793 int propertiesSize = readNumber(); 0794 for (int i = 0; i < propertiesSize; ++i) { 0795 info->properties.append(readByte()); 0796 } 0797 } 0798 0799 if ((coderInfo & 0x80) != 0) { 0800 qCDebug(KArchiveLog) << "unsupported"; 0801 delete info; 0802 delete folder; 0803 return nullptr; 0804 } 0805 0806 numInStreamsTotal += info->numInStreams; 0807 numOutStreamsTotal += info->numOutStreams; 0808 folder->folderInfos.append(info); 0809 } 0810 0811 int numBindPairs = numOutStreamsTotal - 1; 0812 for (int i = 0; i < numBindPairs; i++) { 0813 folder->inIndexes.append(readNumber()); 0814 folder->outIndexes.append(readNumber()); 0815 } 0816 0817 int numPackedStreams = numInStreamsTotal - numBindPairs; 0818 if (numPackedStreams > 1) { 0819 for (int i = 0; i < numPackedStreams; ++i) { 0820 folder->packedStreams.append(readNumber()); 0821 } 0822 } else { 0823 if (numPackedStreams == 1) { 0824 for (quint64 i = 0; i < numInStreamsTotal; i++) { 0825 if (folder->findBindPairForInStream(i) < 0) { 0826 folder->packedStreams.append(i); 0827 break; 0828 } 0829 } 0830 if (folder->packedStreams.size() != 1) { 0831 delete folder; 0832 return nullptr; 0833 } 0834 } 0835 } 0836 return folder; 0837 } 0838 0839 bool K7Zip::K7ZipPrivate::readUInt64DefVector(int numFiles, QVector<quint64> &values, QVector<bool> &defined) 0840 { 0841 if (!buffer) { 0842 return false; 0843 } 0844 0845 readBoolVector2(numFiles, defined); 0846 0847 int external = readByte(); 0848 if (external != 0) { 0849 int dataIndex = readNumber(); 0850 if (dataIndex < 0 /*|| dataIndex >= dataVector->Size()*/) { 0851 qCDebug(KArchiveLog) << "wrong data index"; 0852 return false; 0853 } 0854 0855 // TODO : go to the new index 0856 } 0857 0858 for (int i = 0; i < numFiles; i++) { 0859 quint64 t = 0; 0860 if (defined[i]) { 0861 t = readUInt64(); 0862 } 0863 values.append(t); 0864 } 0865 return true; 0866 } 0867 0868 bool K7Zip::K7ZipPrivate::readPackInfo() 0869 { 0870 if (!buffer) { 0871 return false; 0872 } 0873 0874 packPos = readNumber(); 0875 numPackStreams = readNumber(); 0876 packSizes.clear(); 0877 0878 packCRCsDefined.clear(); 0879 packCRCs.clear(); 0880 0881 if (!findAttribute(kSize)) { 0882 qCDebug(KArchiveLog) << "kSize not found"; 0883 return false; 0884 } 0885 0886 for (quint64 i = 0; i < numPackStreams; ++i) { 0887 packSizes.append(readNumber()); 0888 } 0889 0890 for (;;) { 0891 int type = readByte(); 0892 if (type == kEnd) { 0893 break; 0894 } 0895 if (type == kCRC) { 0896 readHashDigests(numPackStreams, packCRCsDefined, packCRCs); 0897 continue; 0898 } 0899 skipData(readNumber()); 0900 } 0901 0902 if (packCRCs.isEmpty()) { 0903 for (quint64 i = 0; i < numPackStreams; ++i) { 0904 packCRCsDefined.append(false); 0905 packCRCs.append(0); 0906 } 0907 } 0908 return true; 0909 } 0910 0911 bool K7Zip::K7ZipPrivate::readUnpackInfo() 0912 { 0913 if (!buffer) { 0914 return false; 0915 } 0916 0917 if (!findAttribute(kFolder)) { 0918 qCDebug(KArchiveLog) << "kFolder not found"; 0919 return false; 0920 } 0921 0922 int numFolders = readNumber(); 0923 qDeleteAll(folders); 0924 folders.clear(); 0925 int external = readByte(); 0926 switch (external) { 0927 case 0: { 0928 for (int i = 0; i < numFolders; ++i) { 0929 folders.append(folderItem()); 0930 } 0931 break; 0932 } 0933 case 1: { 0934 int dataIndex = readNumber(); 0935 if (dataIndex < 0 /*|| dataIndex >= dataVector->Size()*/) { 0936 qCDebug(KArchiveLog) << "wrong data index"; 0937 } 0938 // TODO : go to the new index 0939 break; 0940 } 0941 default: 0942 qCDebug(KArchiveLog) << "external error"; 0943 return false; 0944 } 0945 0946 if (!findAttribute(kCodersUnpackSize)) { 0947 qCDebug(KArchiveLog) << "kCodersUnpackSize not found"; 0948 return false; 0949 } 0950 0951 for (int i = 0; i < numFolders; ++i) { 0952 Folder *folder = folders.at(i); 0953 int numOutStreams = folder->getNumOutStreams(); 0954 for (int j = 0; j < numOutStreams; ++j) { 0955 folder->unpackSizes.append(readNumber()); 0956 } 0957 } 0958 0959 for (;;) { 0960 int type = readByte(); 0961 if (type == kEnd) { 0962 break; 0963 } 0964 if (type == kCRC) { 0965 QVector<bool> crcsDefined; 0966 QVector<quint32> crcs; 0967 readHashDigests(numFolders, crcsDefined, crcs); 0968 for (int i = 0; i < numFolders; i++) { 0969 Folder *folder = folders.at(i); 0970 folder->unpackCRCDefined = crcsDefined[i]; 0971 folder->unpackCRC = crcs[i]; 0972 } 0973 continue; 0974 } 0975 skipData(readNumber()); 0976 } 0977 return true; 0978 } 0979 0980 bool K7Zip::K7ZipPrivate::readSubStreamsInfo() 0981 { 0982 if (!buffer) { 0983 return false; 0984 } 0985 0986 numUnpackStreamsInFolders.clear(); 0987 0988 int type; 0989 for (;;) { 0990 type = readByte(); 0991 if (type == kNumUnpackStream) { 0992 for (int i = 0; i < folders.size(); i++) { 0993 numUnpackStreamsInFolders.append(readNumber()); 0994 } 0995 continue; 0996 } 0997 if (type == kCRC || type == kSize) { 0998 break; 0999 } 1000 if (type == kEnd) { 1001 break; 1002 } 1003 skipData(readNumber()); 1004 } 1005 1006 if (numUnpackStreamsInFolders.isEmpty()) { 1007 for (int i = 0; i < folders.size(); i++) { 1008 numUnpackStreamsInFolders.append(1); 1009 } 1010 } 1011 1012 for (int i = 0; i < numUnpackStreamsInFolders.size(); i++) { 1013 quint64 numSubstreams = numUnpackStreamsInFolders.at(i); 1014 if (numSubstreams == 0) { 1015 continue; 1016 } 1017 quint64 sum = 0; 1018 for (quint64 j = 1; j < numSubstreams; j++) { 1019 if (type == kSize) { 1020 int size = readNumber(); 1021 unpackSizes.append(size); 1022 sum += size; 1023 } 1024 } 1025 unpackSizes.append(folders.at(i)->getUnpackSize() - sum); 1026 } 1027 1028 if (type == kSize) { 1029 type = readByte(); 1030 } 1031 1032 int numDigests = 0; 1033 int numDigestsTotal = 0; 1034 for (int i = 0; i < folders.size(); i++) { 1035 quint64 numSubstreams = numUnpackStreamsInFolders.at(i); 1036 if (numSubstreams != 1 || !folders.at(i)->unpackCRCDefined) { 1037 numDigests += numSubstreams; 1038 } 1039 numDigestsTotal += numSubstreams; 1040 } 1041 1042 for (;;) { 1043 if (type == kCRC) { 1044 QVector<bool> digestsDefined2; 1045 QVector<quint32> digests2; 1046 readHashDigests(numDigests, digestsDefined2, digests2); 1047 int digestIndex = 0; 1048 for (int i = 0; i < folders.size(); i++) { 1049 quint64 numSubstreams = numUnpackStreamsInFolders.at(i); 1050 const Folder *folder = folders.at(i); 1051 if (numSubstreams == 1 && folder->unpackCRCDefined) { 1052 digestsDefined.append(true); 1053 digests.append(folder->unpackCRC); 1054 } else { 1055 for (quint64 j = 0; j < numSubstreams; j++, digestIndex++) { 1056 digestsDefined.append(digestsDefined2[digestIndex]); 1057 digests.append(digests2[digestIndex]); 1058 } 1059 } 1060 } 1061 } else if (type == kEnd) { 1062 if (digestsDefined.isEmpty()) { 1063 for (int i = 0; i < numDigestsTotal; i++) { 1064 digestsDefined.append(false); 1065 digests.append(0); 1066 } 1067 } 1068 1069 break; 1070 } else { 1071 skipData(readNumber()); 1072 } 1073 1074 type = readByte(); 1075 } 1076 return true; 1077 } 1078 1079 #define TICKSPERSEC 10000000 1080 #define TICKSPERMSEC 10000 1081 #define SECSPERDAY 86400 1082 #define SECSPERHOUR 3600 1083 #define SECSPERMIN 60 1084 #define EPOCHWEEKDAY 1 /* Jan 1, 1601 was Monday */ 1085 #define DAYSPERWEEK 7 1086 #define DAYSPERQUADRICENTENNIUM (365 * 400 + 97) 1087 #define DAYSPERNORMALQUADRENNIUM (365 * 4 + 1) 1088 #define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC) 1089 #define SECS_1601_TO_1970 ((369 * 365 + 89) * (unsigned long long)SECSPERDAY) 1090 1091 static uint toTimeT(const long long liTime) 1092 { 1093 long long time = liTime / TICKSPERSEC; 1094 1095 /* The native version of RtlTimeToTimeFields does not take leap seconds 1096 * into account */ 1097 1098 /* Split the time into days and seconds within the day */ 1099 long int days = time / SECSPERDAY; 1100 int secondsInDay = time % SECSPERDAY; 1101 1102 /* compute time of day */ 1103 short hour = (short)(secondsInDay / SECSPERHOUR); 1104 secondsInDay = secondsInDay % SECSPERHOUR; 1105 short minute = (short)(secondsInDay / SECSPERMIN); 1106 short second = (short)(secondsInDay % SECSPERMIN); 1107 1108 /* compute year, month and day of month. */ 1109 long int cleaps = (3 * ((4 * days + 1227) / DAYSPERQUADRICENTENNIUM) + 3) / 4; 1110 days += 28188 + cleaps; 1111 long int years = (20 * days - 2442) / (5 * DAYSPERNORMALQUADRENNIUM); 1112 long int yearday = days - (years * DAYSPERNORMALQUADRENNIUM) / 4; 1113 long int months = (64 * yearday) / 1959; 1114 /* the result is based on a year starting on March. 1115 * To convert take 12 from Januari and Februari and 1116 * increase the year by one. */ 1117 1118 short month; 1119 short year; 1120 if (months < 14) { 1121 month = (short)(months - 1); 1122 year = (short)(years + 1524); 1123 } else { 1124 month = (short)(months - 13); 1125 year = (short)(years + 1525); 1126 } 1127 /* calculation of day of month is based on the wonderful 1128 * sequence of INT( n * 30.6): it reproduces the· 1129 * 31-30-31-30-31-31 month lengths exactly for small n's */ 1130 short day = (short)(yearday - (1959 * months) / 64); 1131 1132 QDateTime t(QDate(year, month, day), QTime(hour, minute, second)); 1133 t.setTimeSpec(Qt::UTC); 1134 return t.toSecsSinceEpoch(); 1135 } 1136 1137 long long rtlSecondsSince1970ToSpecTime(quint32 seconds) 1138 { 1139 long long secs = seconds * (long long)TICKSPERSEC + TICKS_1601_TO_1970; 1140 return secs; 1141 } 1142 1143 bool K7Zip::K7ZipPrivate::readMainStreamsInfo() 1144 { 1145 if (!buffer) { 1146 return false; 1147 } 1148 1149 quint32 type; 1150 for (;;) { 1151 type = readByte(); 1152 if (type > ((quint32)1 << 30)) { 1153 qCDebug(KArchiveLog) << "type error"; 1154 return false; 1155 } 1156 switch (type) { 1157 case kEnd: 1158 return true; 1159 case kPackInfo: { 1160 if (!readPackInfo()) { 1161 qCDebug(KArchiveLog) << "error during read pack information"; 1162 return false; 1163 } 1164 break; 1165 } 1166 case kUnpackInfo: { 1167 if (!readUnpackInfo()) { 1168 qCDebug(KArchiveLog) << "error during read pack information"; 1169 return false; 1170 } 1171 break; 1172 } 1173 case kSubStreamsInfo: { 1174 if (!readSubStreamsInfo()) { 1175 qCDebug(KArchiveLog) << "error during read substreams information"; 1176 return false; 1177 } 1178 break; 1179 } 1180 default: 1181 qCDebug(KArchiveLog) << "Wrong type"; 1182 return false; 1183 } 1184 } 1185 1186 qCDebug(KArchiveLog) << "should not reach"; 1187 return false; 1188 } 1189 1190 static bool getInStream(const Folder *folder, quint32 streamIndex, int &seqInStream, quint32 &coderIndex) 1191 { 1192 for (int i = 0; i < folder->packedStreams.size(); i++) { 1193 if (folder->packedStreams[i] == streamIndex) { 1194 seqInStream = i; 1195 return true; 1196 } 1197 } 1198 1199 int binderIndex = folder->findBindPairForInStream(streamIndex); 1200 if (binderIndex < 0) { 1201 return false; 1202 } 1203 1204 quint32 coderStreamIndex; 1205 folder->findOutStream(folder->outIndexes[binderIndex], coderIndex, coderStreamIndex); 1206 1207 quint32 startIndex = folder->getCoderInStreamIndex(coderIndex); 1208 1209 if (folder->folderInfos[coderIndex]->numInStreams > 1) { 1210 return false; 1211 } 1212 1213 for (int i = 0; i < (int)folder->folderInfos[coderIndex]->numInStreams; i++) { 1214 getInStream(folder, startIndex + i, seqInStream, coderIndex); 1215 } 1216 1217 return true; 1218 } 1219 1220 static bool getOutStream(const Folder *folder, quint32 streamIndex, int &seqOutStream) 1221 { 1222 QVector<quint32> outStreams; 1223 quint32 outStreamIndex = 0; 1224 for (int i = 0; i < folder->folderInfos.size(); i++) { 1225 const Folder::FolderInfo *coderInfo = folder->folderInfos.at(i); 1226 1227 for (int j = 0; j < coderInfo->numOutStreams; j++, outStreamIndex++) { 1228 if (folder->findBindPairForOutStream(outStreamIndex) < 0) { 1229 outStreams.append(outStreamIndex); 1230 } 1231 } 1232 } 1233 1234 for (int i = 0; i < outStreams.size(); i++) { 1235 if (outStreams[i] == streamIndex) { 1236 seqOutStream = i; 1237 return true; 1238 } 1239 } 1240 1241 int binderIndex = folder->findBindPairForOutStream(streamIndex); 1242 if (binderIndex < 0) { 1243 return false; 1244 } 1245 1246 quint32 coderIndex; 1247 quint32 coderStreamIndex; 1248 folder->findInStream(folder->inIndexes[binderIndex], coderIndex, coderStreamIndex); 1249 1250 quint32 startIndex = folder->getCoderOutStreamIndex(coderIndex); 1251 1252 if (folder->folderInfos[coderIndex]->numOutStreams > 1) { 1253 return false; 1254 } 1255 1256 for (int i = 0; i < (int)folder->folderInfos[coderIndex]->numOutStreams; i++) { 1257 getOutStream(folder, startIndex + i, seqOutStream); 1258 } 1259 1260 return true; 1261 } 1262 1263 const int kNumTopBits = 24; 1264 const quint32 kTopValue = (1 << kNumTopBits); 1265 1266 class RangeDecoder 1267 { 1268 int pos; 1269 1270 public: 1271 QByteArray stream; 1272 quint32 range; 1273 quint32 code; 1274 1275 RangeDecoder(const QByteArray &s) 1276 : pos(0) 1277 , stream(s) 1278 , range(0xFFFFFFFF) 1279 , code(0) 1280 { 1281 for (int i = 0; i < 5; i++) { 1282 code = (code << 8) | readByte(); 1283 } 1284 } 1285 1286 unsigned char readByte() 1287 { 1288 return stream[pos++]; 1289 } 1290 1291 void normalize() 1292 { 1293 while (range < kTopValue) { 1294 code = (code << 8) | readByte(); 1295 range <<= 8; 1296 } 1297 } 1298 1299 quint32 getThreshold(quint32 total) 1300 { 1301 return (code) / (range /= total); 1302 } 1303 1304 void decode(quint32 start, quint32 size) 1305 { 1306 code -= start * range; 1307 range *= size; 1308 normalize(); 1309 } 1310 1311 quint32 decodeDirectBits(int numTotalBits) 1312 { 1313 quint32 r = range; 1314 quint32 c = code; 1315 quint32 result = 0; 1316 for (int i = numTotalBits; i != 0; i--) { 1317 r >>= 1; 1318 quint32 t = (c - r) >> 31; 1319 c -= r & (t - 1); 1320 result = (result << 1) | (1 - t); 1321 1322 if (r < kTopValue) { 1323 c = (c << 8) | readByte(); 1324 r <<= 8; 1325 } 1326 } 1327 range = r; 1328 code = c; 1329 return result; 1330 } 1331 1332 quint32 DecodeBit(quint32 size0, quint32 numTotalBits) 1333 { 1334 quint32 newBound = (range >> numTotalBits) * size0; 1335 quint32 symbol; 1336 if (code < newBound) { 1337 symbol = 0; 1338 range = newBound; 1339 } else { 1340 symbol = 1; 1341 code -= newBound; 1342 range -= newBound; 1343 } 1344 normalize(); 1345 return symbol; 1346 } 1347 }; 1348 1349 const int kNumBitModelTotalBits = 11; 1350 const quint32 kBitModelTotal = (1 << kNumBitModelTotalBits); 1351 1352 template<int numMoveBits> 1353 class CBitModel 1354 { 1355 public: 1356 quint32 prob; 1357 void updateModel(quint32 symbol) 1358 { 1359 if (symbol == 0) { 1360 prob += (kBitModelTotal - prob) >> numMoveBits; 1361 } else { 1362 prob -= (prob) >> numMoveBits; 1363 } 1364 } 1365 1366 void init() 1367 { 1368 prob = kBitModelTotal / 2; 1369 } 1370 }; 1371 1372 template<int numMoveBits> 1373 class CBitDecoder : public CBitModel<numMoveBits> 1374 { 1375 public: 1376 quint32 decode(RangeDecoder *decoder) 1377 { 1378 quint32 newBound = (decoder->range >> kNumBitModelTotalBits) * this->prob; 1379 if (decoder->code < newBound) { 1380 decoder->range = newBound; 1381 this->prob += (kBitModelTotal - this->prob) >> numMoveBits; 1382 if (decoder->range < kTopValue) { 1383 decoder->code = (decoder->code << 8) | decoder->readByte(); 1384 decoder->range <<= 8; 1385 } 1386 return 0; 1387 } else { 1388 decoder->range -= newBound; 1389 decoder->code -= newBound; 1390 this->prob -= (this->prob) >> numMoveBits; 1391 if (decoder->range < kTopValue) { 1392 decoder->code = (decoder->code << 8) | decoder->readByte(); 1393 decoder->range <<= 8; 1394 } 1395 return 1; 1396 } 1397 } 1398 }; 1399 1400 inline bool isJcc(unsigned char b0, unsigned char b1) 1401 { 1402 return (b0 == 0x0F && (b1 & 0xF0) == 0x80); 1403 } 1404 inline bool isJ(unsigned char b0, unsigned char b1) 1405 { 1406 return ((b1 & 0xFE) == 0xE8 || isJcc(b0, b1)); 1407 } 1408 inline unsigned getIndex(unsigned char b0, unsigned char b1) 1409 { 1410 return ((b1 == 0xE8) ? b0 : ((b1 == 0xE9) ? 256 : 257)); 1411 } 1412 1413 const int kNumMoveBits = 5; 1414 1415 static QByteArray decodeBCJ2(const QByteArray &mainStream, const QByteArray &callStream, const QByteArray &jumpStream, const QByteArray &rangeBuffer) 1416 { 1417 unsigned char prevByte = 0; 1418 QByteArray outStream; 1419 int mainStreamPos = 0; 1420 int callStreamPos = 0; 1421 int jumpStreamPos = 0; 1422 1423 RangeDecoder rangeDecoder(rangeBuffer); 1424 1425 QVector<CBitDecoder<kNumMoveBits>> statusDecoder(256 + 2); 1426 1427 for (int i = 0; i < 256 + 2; i++) { 1428 statusDecoder[i].init(); 1429 } 1430 1431 for (;;) { 1432 quint32 i; 1433 unsigned char b = 0; 1434 const quint32 kBurstSize = (1 << 18); 1435 for (i = 0; i < kBurstSize; i++) { 1436 if (mainStreamPos == mainStream.size()) { 1437 return outStream; 1438 } 1439 1440 b = mainStream[mainStreamPos++]; 1441 outStream.append(b); 1442 1443 if (isJ(prevByte, b)) { 1444 break; 1445 } 1446 prevByte = b; 1447 } 1448 1449 if (i == kBurstSize) { 1450 continue; 1451 } 1452 1453 unsigned index = getIndex(prevByte, b); 1454 if (statusDecoder[index].decode(&rangeDecoder) == 1) { 1455 if (b == 0xE8) { 1456 if (callStreamPos + 4 > callStream.size()) { 1457 return QByteArray(); 1458 } 1459 } else { 1460 if (jumpStreamPos + 4 > jumpStream.size()) { 1461 return QByteArray(); 1462 } 1463 } 1464 quint32 src = 0; 1465 for (int i = 0; i < 4; i++) { 1466 unsigned char b0; 1467 if (b == 0xE8) { 1468 b0 = callStream[callStreamPos++]; 1469 } else { 1470 b0 = jumpStream[jumpStreamPos++]; 1471 } 1472 src <<= 8; 1473 src |= ((quint32)b0); 1474 } 1475 1476 quint32 dest = src - (quint32(outStream.size()) + 4); 1477 outStream.append((unsigned char)(dest)); 1478 outStream.append((unsigned char)(dest >> 8)); 1479 outStream.append((unsigned char)(dest >> 16)); 1480 outStream.append((unsigned char)(dest >> 24)); 1481 prevByte = (unsigned char)(dest >> 24); 1482 } else { 1483 prevByte = b; 1484 } 1485 } 1486 } 1487 1488 QByteArray K7Zip::K7ZipPrivate::readAndDecodePackedStreams(bool readMainStreamInfo) 1489 { 1490 if (!buffer) { 1491 return QByteArray(); 1492 } 1493 1494 if (readMainStreamInfo) { 1495 readMainStreamsInfo(); 1496 } 1497 1498 QByteArray inflatedData; 1499 1500 quint64 startPos = 32 + packPos; 1501 for (int i = 0; i < folders.size(); i++) { 1502 const Folder *folder = folders.at(i); 1503 quint64 unpackSize64 = folder->getUnpackSize(); 1504 size_t unpackSize = (size_t)unpackSize64; 1505 if (unpackSize != unpackSize64) { 1506 qCDebug(KArchiveLog) << "unsupported"; 1507 return inflatedData; 1508 } 1509 1510 // Find main coder 1511 quint32 mainCoderIndex = 0; 1512 QVector<int> outStreamIndexed; 1513 int outStreamIndex = 0; 1514 for (int j = 0; j < folder->folderInfos.size(); j++) { 1515 const Folder::FolderInfo *info = folder->folderInfos[j]; 1516 for (int k = 0; k < info->numOutStreams; k++, outStreamIndex++) { 1517 if (folder->findBindPairForOutStream(outStreamIndex) < 0) { 1518 outStreamIndexed.append(outStreamIndex); 1519 break; 1520 } 1521 } 1522 } 1523 1524 quint32 temp = 0; 1525 if (!outStreamIndexed.isEmpty()) { 1526 folder->findOutStream(outStreamIndexed[0], mainCoderIndex, temp); 1527 } 1528 1529 quint32 startInIndex = folder->getCoderInStreamIndex(mainCoderIndex); 1530 quint32 startOutIndex = folder->getCoderOutStreamIndex(mainCoderIndex); 1531 1532 Folder::FolderInfo *mainCoder = folder->folderInfos[mainCoderIndex]; 1533 1534 QVector<int> seqInStreams; 1535 QVector<quint32> coderIndexes; 1536 seqInStreams.reserve(mainCoder->numInStreams); 1537 coderIndexes.reserve(mainCoder->numInStreams); 1538 for (int j = 0; j < (int)mainCoder->numInStreams; j++) { 1539 int seqInStream; 1540 quint32 coderIndex; 1541 getInStream(folder, startInIndex + j, seqInStream, coderIndex); 1542 seqInStreams.append(seqInStream); 1543 coderIndexes.append(coderIndex); 1544 } 1545 1546 QVector<int> seqOutStreams; 1547 seqOutStreams.reserve(mainCoder->numOutStreams); 1548 for (int j = 0; j < (int)mainCoder->numOutStreams; j++) { 1549 int seqOutStream; 1550 getOutStream(folder, startOutIndex + j, seqOutStream); 1551 seqOutStreams.append(seqOutStream); 1552 } 1553 1554 QVector<QByteArray> datas; 1555 for (int j = 0; j < (int)mainCoder->numInStreams; j++) { 1556 int size = packSizes[j + i]; 1557 std::unique_ptr<char[]> encodedBuffer(new char[size]); 1558 QIODevice *dev = q->device(); 1559 dev->seek(startPos); 1560 quint64 n = dev->read(encodedBuffer.get(), size); 1561 if (n != (quint64)size) { 1562 qCDebug(KArchiveLog) << "Failed read next size, should read " << size << ", read " << n; 1563 return inflatedData; 1564 } 1565 QByteArray deflatedData(encodedBuffer.get(), size); 1566 datas.append(deflatedData); 1567 startPos += size; 1568 pos += size; 1569 headerSize += size; 1570 } 1571 1572 QVector<QByteArray> inflatedDatas; 1573 QByteArray deflatedData; 1574 for (int j = 0; j < seqInStreams.size(); ++j) { 1575 Folder::FolderInfo *coder = nullptr; 1576 if ((quint32)j != mainCoderIndex) { 1577 coder = folder->folderInfos[coderIndexes[j]]; 1578 } else { 1579 coder = folder->folderInfos[mainCoderIndex]; 1580 } 1581 1582 deflatedData = datas[seqInStreams[j]]; 1583 1584 KFilterBase *filter = nullptr; 1585 1586 switch (coder->methodID) { 1587 case k_LZMA: 1588 filter = KCompressionDevice::filterForCompressionType(KCompressionDevice::Xz); 1589 if (!filter) { 1590 qCDebug(KArchiveLog) << "filter not found"; 1591 return QByteArray(); 1592 } 1593 static_cast<KXzFilter *>(filter)->init(QIODevice::ReadOnly, KXzFilter::LZMA, coder->properties); 1594 break; 1595 case k_LZMA2: 1596 filter = KCompressionDevice::filterForCompressionType(KCompressionDevice::Xz); 1597 if (!filter) { 1598 qCDebug(KArchiveLog) << "filter not found"; 1599 return QByteArray(); 1600 } 1601 static_cast<KXzFilter *>(filter)->init(QIODevice::ReadOnly, KXzFilter::LZMA2, coder->properties); 1602 break; 1603 case k_PPMD: { 1604 /*if (coder->properties.size() == 5) { 1605 //Byte order = *(const Byte *)coder.Props; 1606 qint32 dicSize = ((unsigned char)coder->properties[1] | 1607 (((unsigned char)coder->properties[2]) << 8) | 1608 (((unsigned char)coder->properties[3]) << 16) | 1609 (((unsigned char)coder->properties[4]) << 24)); 1610 }*/ 1611 break; 1612 } 1613 case k_AES: 1614 if (coder->properties.size() >= 1) { 1615 // const Byte *data = (const Byte *)coder.Props; 1616 // Byte firstByte = *data++; 1617 // UInt32 numCyclesPower = firstByte & 0x3F; 1618 } 1619 break; 1620 case k_BCJ: 1621 filter = KCompressionDevice::filterForCompressionType(KCompressionDevice::Xz); 1622 if (!filter) { 1623 qCDebug(KArchiveLog) << "filter not found"; 1624 return QByteArray(); 1625 } 1626 static_cast<KXzFilter *>(filter)->init(QIODevice::ReadOnly, KXzFilter::BCJ, coder->properties); 1627 break; 1628 case k_BCJ2: { 1629 QByteArray bcj2 = decodeBCJ2(inflatedDatas[0], inflatedDatas[1], inflatedDatas[2], deflatedData); 1630 inflatedDatas.clear(); 1631 inflatedDatas.append(bcj2); 1632 break; 1633 } 1634 case k_BZip2: 1635 filter = KCompressionDevice::filterForCompressionType(KCompressionDevice::BZip2); 1636 if (!filter) { 1637 qCDebug(KArchiveLog) << "filter not found"; 1638 return QByteArray(); 1639 } 1640 filter->init(QIODevice::ReadOnly); 1641 break; 1642 } 1643 1644 if (coder->methodID == k_BCJ2) { 1645 continue; 1646 } 1647 1648 if (!filter) { 1649 return QByteArray(); 1650 } 1651 1652 filter->setInBuffer(deflatedData.data(), deflatedData.size()); 1653 1654 QByteArray outBuffer; 1655 // reserve memory 1656 outBuffer.resize(unpackSize); 1657 1658 KFilterBase::Result result = KFilterBase::Ok; 1659 QByteArray inflatedDataTmp; 1660 while (result != KFilterBase::End && result != KFilterBase::Error && !filter->inBufferEmpty()) { 1661 filter->setOutBuffer(outBuffer.data(), outBuffer.size()); 1662 result = filter->uncompress(); 1663 if (result == KFilterBase::Error) { 1664 qCDebug(KArchiveLog) << " decode error"; 1665 filter->terminate(); 1666 delete filter; 1667 return QByteArray(); 1668 } 1669 int uncompressedBytes = outBuffer.size() - filter->outBufferAvailable(); 1670 1671 // append the uncompressed data to inflate buffer 1672 inflatedDataTmp.append(outBuffer.data(), uncompressedBytes); 1673 1674 if (result == KFilterBase::End) { 1675 // qCDebug(KArchiveLog) << "Finished unpacking"; 1676 break; // Finished. 1677 } 1678 } 1679 1680 if (result != KFilterBase::End && !filter->inBufferEmpty()) { 1681 qCDebug(KArchiveLog) << "decode failed result" << result; 1682 filter->terminate(); 1683 delete filter; 1684 return QByteArray(); 1685 } 1686 1687 filter->terminate(); 1688 delete filter; 1689 1690 inflatedDatas.append(inflatedDataTmp); 1691 } 1692 1693 QByteArray inflated; 1694 for (const QByteArray &data : std::as_const(inflatedDatas)) { 1695 inflated.append(data); 1696 } 1697 1698 inflatedDatas.clear(); 1699 1700 if (folder->unpackCRCDefined) { 1701 if ((size_t)inflated.size() < unpackSize) { 1702 qCDebug(KArchiveLog) << "wrong crc size data"; 1703 return QByteArray(); 1704 } 1705 quint32 crc = crc32(0, (Bytef *)(inflated.data()), unpackSize); 1706 if (crc != folder->unpackCRC) { 1707 qCDebug(KArchiveLog) << "wrong crc"; 1708 return QByteArray(); 1709 } 1710 } 1711 1712 inflatedData.append(inflated); 1713 } 1714 1715 return inflatedData; 1716 } 1717 1718 ///////////////// Write //////////////////// 1719 1720 void K7Zip::K7ZipPrivate::createItemsFromEntities(const KArchiveDirectory *dir, const QString &path, QByteArray &data) 1721 { 1722 const QStringList l = dir->entries(); 1723 QStringList::ConstIterator it = l.begin(); 1724 for (; it != l.end(); ++it) { 1725 const KArchiveEntry *entry = dir->entry((*it)); 1726 1727 FileInfo *fileInfo = new FileInfo; 1728 fileInfo->attribDefined = true; 1729 1730 fileInfo->path = path + entry->name(); 1731 mTimesDefined.append(true); 1732 mTimes.append(rtlSecondsSince1970ToSpecTime(entry->date().toSecsSinceEpoch())); 1733 1734 if (entry->isFile()) { 1735 const K7ZipFileEntry *fileEntry = static_cast<const K7ZipFileEntry *>(entry); 1736 1737 fileInfo->attributes = FILE_ATTRIBUTE_ARCHIVE; 1738 fileInfo->attributes |= FILE_ATTRIBUTE_UNIX_EXTENSION + ((entry->permissions() & 0xFFFF) << 16); 1739 fileInfo->size = fileEntry->size(); 1740 QString symLink = fileEntry->symLinkTarget(); 1741 if (fileInfo->size > 0) { 1742 fileInfo->hasStream = true; 1743 data.append(outData.mid(fileEntry->position(), fileEntry->size())); 1744 unpackSizes.append(fileInfo->size); 1745 } else if (!symLink.isEmpty()) { 1746 fileInfo->hasStream = true; 1747 data.append(symLink.toUtf8()); 1748 unpackSizes.append(symLink.size()); 1749 } 1750 fileInfos.append(fileInfo); 1751 } else if (entry->isDirectory()) { 1752 fileInfo->attributes = FILE_ATTRIBUTE_DIRECTORY; 1753 fileInfo->attributes |= FILE_ATTRIBUTE_UNIX_EXTENSION + ((entry->permissions() & 0xFFFF) << 16); 1754 fileInfo->isDir = true; 1755 fileInfos.append(fileInfo); 1756 createItemsFromEntities((KArchiveDirectory *)entry, path + (*it) + QLatin1Char('/'), data); 1757 } 1758 } 1759 } 1760 1761 void K7Zip::K7ZipPrivate::writeByte(unsigned char b) 1762 { 1763 header.append(b); 1764 countSize++; 1765 } 1766 1767 void K7Zip::K7ZipPrivate::writeNumber(quint64 value) 1768 { 1769 int firstByte = 0; 1770 short mask = 0x80; 1771 int i; 1772 for (i = 0; i < 8; i++) { 1773 if (value < ((quint64(1) << (7 * (i + 1))))) { 1774 firstByte |= (int)(value >> (8 * i)); 1775 break; 1776 } 1777 firstByte |= mask; 1778 mask >>= 1; 1779 } 1780 writeByte(firstByte); 1781 for (; i > 0; i--) { 1782 writeByte((int)value); 1783 value >>= 8; 1784 } 1785 } 1786 1787 void K7Zip::K7ZipPrivate::writeBoolVector(const QVector<bool> &boolVector) 1788 { 1789 int b = 0; 1790 short mask = 0x80; 1791 for (int i = 0; i < boolVector.size(); i++) { 1792 if (boolVector[i]) { 1793 b |= mask; 1794 } 1795 mask >>= 1; 1796 if (mask == 0) { 1797 writeByte(b); 1798 mask = 0x80; 1799 b = 0; 1800 } 1801 } 1802 if (mask != 0x80) { 1803 writeByte(b); 1804 } 1805 } 1806 1807 void K7Zip::K7ZipPrivate::writeUInt32(quint32 value) 1808 { 1809 for (int i = 0; i < 4; i++) { 1810 writeByte((unsigned char)value); 1811 value >>= 8; 1812 } 1813 } 1814 1815 void K7Zip::K7ZipPrivate::writeUInt64(quint64 value) 1816 { 1817 for (int i = 0; i < 8; i++) { 1818 writeByte((unsigned char)value); 1819 value >>= 8; 1820 } 1821 } 1822 1823 void K7Zip::K7ZipPrivate::writeAlignedBoolHeader(const QVector<bool> &v, int numDefined, int type, unsigned itemSize) 1824 { 1825 const unsigned bvSize = (numDefined == v.size()) ? 0 : ((unsigned)v.size() + 7) / 8; 1826 const quint64 dataSize = (quint64)numDefined * itemSize + bvSize + 2; 1827 // SkipAlign(3 + (unsigned)bvSize + (unsigned)GetBigNumberSize(dataSize), itemSize); 1828 1829 writeByte(type); 1830 writeNumber(dataSize); 1831 if (numDefined == v.size()) { 1832 writeByte(1); 1833 } else { 1834 writeByte(0); 1835 writeBoolVector(v); 1836 } 1837 writeByte(0); 1838 } 1839 1840 void K7Zip::K7ZipPrivate::writeUInt64DefVector(const QVector<quint64> &v, const QVector<bool> &defined, int type) 1841 { 1842 int numDefined = 0; 1843 1844 for (int i = 0; i < defined.size(); i++) { 1845 if (defined[i]) { 1846 numDefined++; 1847 } 1848 } 1849 1850 if (numDefined == 0) { 1851 return; 1852 } 1853 1854 writeAlignedBoolHeader(defined, numDefined, type, 8); 1855 1856 for (int i = 0; i < defined.size(); i++) { 1857 if (defined[i]) { 1858 writeUInt64(v[i]); 1859 } 1860 } 1861 } 1862 1863 void K7Zip::K7ZipPrivate::writeHashDigests(const QVector<bool> &digestsDefined, const QVector<quint32> &digests) 1864 { 1865 int numDefined = 0; 1866 int i; 1867 for (i = 0; i < digestsDefined.size(); i++) { 1868 if (digestsDefined[i]) { 1869 numDefined++; 1870 } 1871 } 1872 1873 if (numDefined == 0) { 1874 return; 1875 } 1876 1877 writeByte(kCRC); 1878 if (numDefined == digestsDefined.size()) { 1879 writeByte(1); 1880 } else { 1881 writeByte(0); 1882 writeBoolVector(digestsDefined); 1883 } 1884 1885 for (i = 0; i < digests.size(); i++) { 1886 if (digestsDefined[i]) { 1887 writeUInt32(digests[i]); 1888 } 1889 } 1890 } 1891 1892 void K7Zip::K7ZipPrivate::writePackInfo(quint64 dataOffset, QVector<quint64> &packedSizes, QVector<bool> &packedCRCsDefined, QVector<quint32> &packedCRCs) 1893 { 1894 if (packedSizes.isEmpty()) { 1895 return; 1896 } 1897 writeByte(kPackInfo); 1898 writeNumber(dataOffset); 1899 writeNumber(packedSizes.size()); 1900 writeByte(kSize); 1901 1902 for (int i = 0; i < packedSizes.size(); i++) { 1903 writeNumber(packedSizes[i]); 1904 } 1905 1906 writeHashDigests(packedCRCsDefined, packedCRCs); 1907 1908 writeByte(kEnd); 1909 } 1910 1911 void K7Zip::K7ZipPrivate::writeFolder(const Folder *folder) 1912 { 1913 writeNumber(folder->folderInfos.size()); 1914 for (int i = 0; i < folder->folderInfos.size(); i++) { 1915 const Folder::FolderInfo *info = folder->folderInfos.at(i); 1916 { 1917 size_t propsSize = info->properties.size(); 1918 1919 quint64 id = info->methodID; 1920 size_t idSize; 1921 for (idSize = 1; idSize < sizeof(id); idSize++) { 1922 if ((id >> (8 * idSize)) == 0) { 1923 break; 1924 } 1925 } 1926 1927 int longID[15]; 1928 for (int t = idSize - 1; t >= 0; t--, id >>= 8) { 1929 longID[t] = (int)(id & 0xFF); 1930 } 1931 1932 int b; 1933 b = (int)(idSize & 0xF); 1934 bool isComplex = !info->isSimpleCoder(); 1935 b |= (isComplex ? 0x10 : 0); 1936 b |= ((propsSize != 0) ? 0x20 : 0); 1937 1938 writeByte(b); 1939 for (size_t j = 0; j < idSize; ++j) { 1940 writeByte(longID[j]); 1941 } 1942 1943 if (isComplex) { 1944 writeNumber(info->numInStreams); 1945 writeNumber(info->numOutStreams); 1946 } 1947 1948 if (propsSize == 0) { 1949 continue; 1950 } 1951 1952 writeNumber(propsSize); 1953 for (size_t j = 0; j < propsSize; ++j) { 1954 writeByte(info->properties[j]); 1955 } 1956 } 1957 } 1958 1959 for (int i = 0; i < folder->inIndexes.size(); i++) { 1960 writeNumber(folder->inIndexes[i]); 1961 writeNumber(folder->outIndexes[i]); 1962 } 1963 1964 if (folder->packedStreams.size() > 1) { 1965 for (int i = 0; i < folder->packedStreams.size(); i++) { 1966 writeNumber(folder->packedStreams[i]); 1967 } 1968 } 1969 } 1970 1971 void K7Zip::K7ZipPrivate::writeUnpackInfo(const QVector<Folder *> &folderItems) 1972 { 1973 if (folderItems.isEmpty()) { 1974 return; 1975 } 1976 1977 writeByte(kUnpackInfo); 1978 1979 writeByte(kFolder); 1980 writeNumber(folderItems.size()); 1981 { 1982 writeByte(0); 1983 for (int i = 0; i < folderItems.size(); i++) { 1984 writeFolder(folderItems[i]); 1985 } 1986 } 1987 1988 writeByte(kCodersUnpackSize); 1989 int i; 1990 for (i = 0; i < folderItems.size(); i++) { 1991 const Folder *folder = folderItems[i]; 1992 for (int j = 0; j < folder->unpackSizes.size(); j++) { 1993 writeNumber(folder->unpackSizes.at(j)); 1994 } 1995 } 1996 1997 QVector<bool> unpackCRCsDefined; 1998 QVector<quint32> unpackCRCs; 1999 unpackCRCsDefined.reserve(folderItems.size()); 2000 unpackCRCs.reserve(folderItems.size()); 2001 for (i = 0; i < folderItems.size(); i++) { 2002 const Folder *folder = folderItems[i]; 2003 unpackCRCsDefined.append(folder->unpackCRCDefined); 2004 unpackCRCs.append(folder->unpackCRC); 2005 } 2006 writeHashDigests(unpackCRCsDefined, unpackCRCs); 2007 2008 writeByte(kEnd); 2009 } 2010 2011 void K7Zip::K7ZipPrivate::writeSubStreamsInfo(const QVector<quint64> &unpackSizes, const QVector<bool> &digestsDefined, const QVector<quint32> &digests) 2012 { 2013 writeByte(kSubStreamsInfo); 2014 2015 for (int i = 0; i < numUnpackStreamsInFolders.size(); i++) { 2016 if (numUnpackStreamsInFolders.at(i) != 1) { 2017 writeByte(kNumUnpackStream); 2018 for (int j = 0; j < numUnpackStreamsInFolders.size(); j++) { 2019 writeNumber(numUnpackStreamsInFolders.at(j)); 2020 } 2021 break; 2022 } 2023 } 2024 2025 bool needFlag = true; 2026 int index = 0; 2027 for (int i = 0; i < numUnpackStreamsInFolders.size(); i++) { 2028 for (quint32 j = 0; j < numUnpackStreamsInFolders.at(i); j++) { 2029 if (j + 1 != numUnpackStreamsInFolders.at(i)) { 2030 if (needFlag) { 2031 writeByte(kSize); 2032 } 2033 needFlag = false; 2034 writeNumber(unpackSizes[index]); 2035 } 2036 index++; 2037 } 2038 } 2039 2040 QVector<bool> digestsDefined2; 2041 QVector<quint32> digests2; 2042 2043 int digestIndex = 0; 2044 for (int i = 0; i < folders.size(); i++) { 2045 int numSubStreams = (int)numUnpackStreamsInFolders.at(i); 2046 if (numSubStreams == 1 && folders.at(i)->unpackCRCDefined) { 2047 digestIndex++; 2048 } else { 2049 for (int j = 0; j < numSubStreams; j++, digestIndex++) { 2050 digestsDefined2.append(digestsDefined[digestIndex]); 2051 digests2.append(digests[digestIndex]); 2052 } 2053 } 2054 } 2055 writeHashDigests(digestsDefined2, digests2); 2056 writeByte(kEnd); 2057 } 2058 2059 QByteArray K7Zip::K7ZipPrivate::encodeStream(QVector<quint64> &packSizes, QVector<Folder *> &folds) 2060 { 2061 Folder *folder = new Folder; 2062 folder->unpackCRCDefined = true; 2063 folder->unpackCRC = crc32(0, (Bytef *)(header.data()), header.size()); 2064 folder->unpackSizes.append(header.size()); 2065 2066 Folder::FolderInfo *info = new Folder::FolderInfo(); 2067 info->numInStreams = 1; 2068 info->numOutStreams = 1; 2069 info->methodID = k_LZMA2; 2070 2071 quint32 dictSize = header.size(); 2072 const quint32 kMinReduceSize = (1 << 16); 2073 if (dictSize < kMinReduceSize) { 2074 dictSize = kMinReduceSize; 2075 } 2076 2077 int dict; 2078 for (dict = 0; dict < 40; dict++) { 2079 if (dictSize <= lzma2_dic_size_from_prop(dict)) { 2080 break; 2081 } 2082 } 2083 2084 info->properties.append(dict); 2085 folder->folderInfos.append(info); 2086 2087 folds.append(folder); 2088 2089 // compress data 2090 QByteArray encodedData; 2091 if (!header.isEmpty()) { 2092 QByteArray enc; 2093 QBuffer inBuffer(&enc); 2094 2095 KCompressionDevice flt(&inBuffer, false, KCompressionDevice::Xz); 2096 flt.open(QIODevice::WriteOnly); 2097 2098 KFilterBase *filter = flt.filterBase(); 2099 2100 static_cast<KXzFilter *>(filter)->init(QIODevice::WriteOnly, KXzFilter::LZMA2, info->properties); 2101 2102 const int ret = flt.write(header); 2103 if (ret != header.size()) { 2104 qCDebug(KArchiveLog) << "write error write " << ret << "expected" << header.size(); 2105 return encodedData; 2106 } 2107 2108 flt.close(); 2109 encodedData = inBuffer.data(); 2110 } 2111 2112 packSizes.append(encodedData.size()); 2113 return encodedData; 2114 } 2115 2116 void K7Zip::K7ZipPrivate::writeHeader(quint64 &headerOffset) 2117 { 2118 quint64 packedSize = 0; 2119 for (int i = 0; i < packSizes.size(); ++i) { 2120 packedSize += packSizes[i]; 2121 } 2122 2123 headerOffset = packedSize; 2124 2125 writeByte(kHeader); 2126 2127 // Archive Properties 2128 2129 if (!folders.isEmpty()) { 2130 writeByte(kMainStreamsInfo); 2131 writePackInfo(0, packSizes, packCRCsDefined, packCRCs); 2132 2133 writeUnpackInfo(folders); 2134 2135 QVector<quint64> unpackFileSizes; 2136 QVector<bool> digestsDefined; 2137 QVector<quint32> digests; 2138 for (int i = 0; i < fileInfos.size(); i++) { 2139 const FileInfo *file = fileInfos.at(i); 2140 if (!file->hasStream) { 2141 continue; 2142 } 2143 unpackFileSizes.append(file->size); 2144 digestsDefined.append(file->crcDefined); 2145 digests.append(file->crc); 2146 } 2147 2148 writeSubStreamsInfo(unpackSizes, digestsDefined, digests); 2149 writeByte(kEnd); 2150 } 2151 2152 if (fileInfos.isEmpty()) { 2153 writeByte(kEnd); 2154 return; 2155 } 2156 2157 writeByte(kFilesInfo); 2158 writeNumber(fileInfos.size()); 2159 2160 { 2161 /* ---------- Empty Streams ---------- */ 2162 QVector<bool> emptyStreamVector; 2163 int numEmptyStreams = 0; 2164 for (int i = 0; i < fileInfos.size(); i++) { 2165 if (fileInfos.at(i)->hasStream) { 2166 emptyStreamVector.append(false); 2167 } else { 2168 emptyStreamVector.append(true); 2169 numEmptyStreams++; 2170 } 2171 } 2172 2173 if (numEmptyStreams > 0) { 2174 writeByte(kEmptyStream); 2175 writeNumber(((unsigned)emptyStreamVector.size() + 7) / 8); 2176 writeBoolVector(emptyStreamVector); 2177 2178 QVector<bool> emptyFileVector; 2179 QVector<bool> antiVector; 2180 int numEmptyFiles = 0; 2181 int numAntiItems = 0; 2182 for (int i = 0; i < fileInfos.size(); i++) { 2183 const FileInfo *file = fileInfos.at(i); 2184 if (!file->hasStream) { 2185 emptyFileVector.append(!file->isDir); 2186 if (!file->isDir) { 2187 numEmptyFiles++; 2188 bool isAnti = (i < this->isAnti.size() && this->isAnti[i]); 2189 antiVector.append(isAnti); 2190 if (isAnti) { 2191 numAntiItems++; 2192 } 2193 } 2194 } 2195 } 2196 2197 if (numEmptyFiles > 0) { 2198 writeByte(kEmptyFile); 2199 writeNumber(((unsigned)emptyFileVector.size() + 7) / 8); 2200 writeBoolVector(emptyFileVector); 2201 } 2202 2203 if (numAntiItems > 0) { 2204 writeByte(kAnti); 2205 writeNumber(((unsigned)antiVector.size() + 7) / 8); 2206 writeBoolVector(antiVector); 2207 } 2208 } 2209 } 2210 2211 { 2212 /* ---------- Names ---------- */ 2213 2214 int numDefined = 0; 2215 size_t namesDataSize = 0; 2216 for (int i = 0; i < fileInfos.size(); i++) { 2217 const QString &name = fileInfos.at(i)->path; 2218 if (!name.isEmpty()) { 2219 numDefined++; 2220 namesDataSize += (name.length() + 1) * 2; 2221 } 2222 } 2223 2224 if (numDefined > 0) { 2225 namesDataSize++; 2226 // SkipAlign(2 + GetBigNumberSize(namesDataSize), 2); 2227 2228 writeByte(kName); 2229 writeNumber(namesDataSize); 2230 writeByte(0); 2231 for (int i = 0; i < fileInfos.size(); i++) { 2232 const QString &name = fileInfos.at(i)->path; 2233 for (int t = 0; t < name.length(); t++) { 2234 wchar_t c = name[t].toLatin1(); 2235 writeByte((unsigned char)c); 2236 writeByte((unsigned char)(c >> 8)); 2237 } 2238 // End of string 2239 writeByte(0); 2240 writeByte(0); 2241 } 2242 } 2243 } 2244 2245 writeUInt64DefVector(mTimes, mTimesDefined, kMTime); 2246 2247 writeUInt64DefVector(startPositions, startPositionsDefined, kStartPos); 2248 2249 { 2250 /* ---------- Write Attrib ---------- */ 2251 QVector<bool> boolVector; 2252 int numDefined = 0; 2253 boolVector.reserve(fileInfos.size()); 2254 for (int i = 0; i < fileInfos.size(); i++) { 2255 bool defined = fileInfos.at(i)->attribDefined; 2256 boolVector.append(defined); 2257 if (defined) { 2258 numDefined++; 2259 } 2260 } 2261 2262 if (numDefined > 0) { 2263 writeAlignedBoolHeader(boolVector, numDefined, kAttributes, 4); 2264 for (int i = 0; i < fileInfos.size(); i++) { 2265 const FileInfo *file = fileInfos.at(i); 2266 if (file->attribDefined) { 2267 writeUInt32(file->attributes); 2268 } 2269 } 2270 } 2271 } 2272 2273 writeByte(kEnd); // for files 2274 writeByte(kEnd); // for headers*/ 2275 } 2276 2277 static void setUInt32(unsigned char *p, quint32 d) 2278 { 2279 for (int i = 0; i < 4; i++, d >>= 8) { 2280 p[i] = (unsigned)d; 2281 } 2282 } 2283 2284 static void setUInt64(unsigned char *p, quint64 d) 2285 { 2286 for (int i = 0; i < 8; i++, d >>= 8) { 2287 p[i] = (unsigned char)d; 2288 } 2289 } 2290 2291 void K7Zip::K7ZipPrivate::writeStartHeader(const quint64 nextHeaderSize, const quint32 nextHeaderCRC, const quint64 nextHeaderOffset) 2292 { 2293 unsigned char buf[24]; 2294 setUInt64(buf + 4, nextHeaderOffset); 2295 setUInt64(buf + 12, nextHeaderSize); 2296 setUInt32(buf + 20, nextHeaderCRC); 2297 setUInt32(buf, crc32(0, (Bytef *)(buf + 4), 20)); 2298 q->device()->write((char *)buf, 24); 2299 } 2300 2301 void K7Zip::K7ZipPrivate::writeSignature() 2302 { 2303 unsigned char buf[8]; 2304 memcpy(buf, k7zip_signature, 6); 2305 buf[6] = 0 /*kMajorVersion*/; 2306 buf[7] = 3; 2307 q->device()->write((char *)buf, 8); 2308 } 2309 2310 bool K7Zip::openArchive(QIODevice::OpenMode mode) 2311 { 2312 if (!(mode & QIODevice::ReadOnly)) { 2313 return true; 2314 } 2315 2316 QIODevice *dev = device(); 2317 2318 if (!dev) { 2319 setErrorString(tr("Could not get underlying device")); 2320 return false; 2321 } 2322 2323 char header[32]; 2324 // check signature 2325 qint64 n = dev->read(header, 32); 2326 if (n != 32) { 2327 setErrorString(tr("Read header failed")); 2328 return false; 2329 } 2330 2331 for (int i = 0; i < 6; ++i) { 2332 if ((unsigned char)header[i] != k7zip_signature[i]) { 2333 setErrorString(tr("Check signature failed")); 2334 return false; 2335 } 2336 } 2337 2338 // get Archive Version 2339 int major = header[6]; 2340 int minor = header[7]; 2341 2342 /*if (major > 0 || minor > 2) { 2343 qCDebug(KArchiveLog) << "wrong archive version"; 2344 return false; 2345 }*/ 2346 2347 // get Start Header CRC 2348 quint32 startHeaderCRC = GetUi32(header, 8); 2349 quint64 nextHeaderOffset = GetUi64(header, 12); 2350 quint64 nextHeaderSize = GetUi64(header, 20); 2351 quint32 nextHeaderCRC = GetUi32(header, 28); 2352 2353 quint32 crc = crc32(0, (Bytef *)(header + 0xC), 20); 2354 2355 if (crc != startHeaderCRC) { 2356 setErrorString(tr("Bad CRC")); 2357 return false; 2358 } 2359 2360 if (nextHeaderSize == 0) { 2361 return true; 2362 } 2363 2364 if (nextHeaderSize > (quint64)0xFFFFFFFF) { 2365 setErrorString(tr("Next header size is too big")); 2366 return false; 2367 } 2368 2369 if ((qint64)nextHeaderOffset < 0) { 2370 setErrorString(tr("Next header size is less than zero")); 2371 return false; 2372 } 2373 2374 dev->seek(nextHeaderOffset + 32); 2375 2376 QByteArray inBuffer; 2377 inBuffer.resize(nextHeaderSize); 2378 2379 n = dev->read(inBuffer.data(), inBuffer.size()); 2380 if (n != (qint64)nextHeaderSize) { 2381 setErrorString(tr("Failed read next header size; should read %1, read %2").arg(nextHeaderSize).arg(n)); 2382 return false; 2383 } 2384 d->buffer = inBuffer.data(); 2385 d->end = nextHeaderSize; 2386 2387 d->headerSize = 32 + nextHeaderSize; 2388 // int physSize = 32 + nextHeaderSize + nextHeaderOffset; 2389 2390 crc = crc32(0, (Bytef *)(d->buffer), (quint32)nextHeaderSize); 2391 2392 if (crc != nextHeaderCRC) { 2393 setErrorString(tr("Bad next header CRC")); 2394 return false; 2395 } 2396 2397 int type = d->readByte(); 2398 QByteArray decodedData; 2399 if (type != kHeader) { 2400 if (type != kEncodedHeader) { 2401 setErrorString(tr("Error in header")); 2402 return false; 2403 } 2404 2405 decodedData = d->readAndDecodePackedStreams(); 2406 2407 int external = d->readByte(); 2408 if (external != 0) { 2409 int dataIndex = (int)d->readNumber(); 2410 if (dataIndex < 0) { 2411 // qCDebug(KArchiveLog) << "dataIndex error"; 2412 } 2413 d->buffer = decodedData.constData(); 2414 d->pos = 0; 2415 d->end = decodedData.size(); 2416 } 2417 2418 type = d->readByte(); 2419 if (type != kHeader) { 2420 setErrorString(tr("Wrong header type")); 2421 return false; 2422 } 2423 } 2424 // read header 2425 2426 type = d->readByte(); 2427 2428 if (type == kArchiveProperties) { 2429 // TODO : implement this part 2430 setErrorString(tr("Not implemented")); 2431 return false; 2432 } 2433 2434 if (type == kAdditionalStreamsInfo) { 2435 // TODO : implement this part 2436 setErrorString(tr("Not implemented")); 2437 return false; 2438 } 2439 2440 if (type == kMainStreamsInfo) { 2441 if (!d->readMainStreamsInfo()) { 2442 setErrorString(tr("Error while reading main streams information")); 2443 return false; 2444 } 2445 type = d->readByte(); 2446 } else { 2447 for (int i = 0; i < d->folders.size(); ++i) { 2448 Folder *folder = d->folders.at(i); 2449 d->unpackSizes.append(folder->getUnpackSize()); 2450 d->digestsDefined.append(folder->unpackCRCDefined); 2451 d->digests.append(folder->unpackCRC); 2452 } 2453 } 2454 2455 if (type == kEnd) { 2456 return true; 2457 } 2458 2459 if (type != kFilesInfo) { 2460 setErrorString(tr("Error while reading header")); 2461 return false; 2462 } 2463 2464 // read files info 2465 int numFiles = d->readNumber(); 2466 for (int i = 0; i < numFiles; ++i) { 2467 d->fileInfos.append(new FileInfo); 2468 } 2469 2470 QVector<bool> emptyStreamVector; 2471 QVector<bool> emptyFileVector; 2472 QVector<bool> antiFileVector; 2473 int numEmptyStreams = 0; 2474 2475 for (;;) { 2476 quint64 type = d->readByte(); 2477 if (type == kEnd) { 2478 break; 2479 } 2480 2481 quint64 size = d->readNumber(); 2482 2483 size_t ppp = d->pos; 2484 2485 bool addPropIdToList = true; 2486 bool isKnownType = true; 2487 2488 if (type > ((quint32)1 << 30)) { 2489 isKnownType = false; 2490 } else { 2491 switch (type) { 2492 case kEmptyStream: { 2493 d->readBoolVector(numFiles, emptyStreamVector); 2494 for (int i = 0; i < emptyStreamVector.size(); ++i) { 2495 if (emptyStreamVector[i]) { 2496 numEmptyStreams++; 2497 } 2498 } 2499 2500 break; 2501 } 2502 case kEmptyFile: 2503 d->readBoolVector(numEmptyStreams, emptyFileVector); 2504 break; 2505 case kAnti: 2506 d->readBoolVector(numEmptyStreams, antiFileVector); 2507 break; 2508 case kCTime: 2509 if (!d->readUInt64DefVector(numFiles, d->cTimes, d->cTimesDefined)) { 2510 return false; 2511 } 2512 break; 2513 case kATime: 2514 if (!d->readUInt64DefVector(numFiles, d->aTimes, d->aTimesDefined)) { 2515 return false; 2516 } 2517 break; 2518 case kMTime: 2519 if (!d->readUInt64DefVector(numFiles, d->mTimes, d->mTimesDefined)) { 2520 setErrorString(tr("Error reading modification time")); 2521 return false; 2522 } 2523 break; 2524 case kName: { 2525 int external = d->readByte(); 2526 if (external != 0) { 2527 int dataIndex = d->readNumber(); 2528 if (dataIndex < 0 /*|| dataIndex >= dataVector->Size()*/) { 2529 qCDebug(KArchiveLog) << "wrong data index"; 2530 } 2531 2532 // TODO : go to the new index 2533 } 2534 2535 QString name; 2536 for (int i = 0; i < numFiles; i++) { 2537 name = d->readString(); 2538 d->fileInfos.at(i)->path = name; 2539 } 2540 break; 2541 } 2542 case kAttributes: { 2543 QVector<bool> attributesAreDefined; 2544 d->readBoolVector2(numFiles, attributesAreDefined); 2545 int external = d->readByte(); 2546 if (external != 0) { 2547 int dataIndex = d->readNumber(); 2548 if (dataIndex < 0) { 2549 qCDebug(KArchiveLog) << "wrong data index"; 2550 } 2551 2552 // TODO : go to the new index 2553 } 2554 2555 for (int i = 0; i < numFiles; i++) { 2556 FileInfo *fileInfo = d->fileInfos.at(i); 2557 fileInfo->attribDefined = attributesAreDefined[i]; 2558 if (fileInfo->attribDefined) { 2559 fileInfo->attributes = d->readUInt32(); 2560 } 2561 } 2562 break; 2563 } 2564 case kStartPos: 2565 if (!d->readUInt64DefVector(numFiles, d->startPositions, d->startPositionsDefined)) { 2566 setErrorString(tr("Error reading MTime")); 2567 return false; 2568 } 2569 break; 2570 case kDummy: { 2571 for (quint64 i = 0; i < size; i++) { 2572 if (d->readByte() != 0) { 2573 setErrorString(tr("Invalid")); 2574 return false; 2575 } 2576 } 2577 addPropIdToList = false; 2578 break; 2579 } 2580 default: 2581 addPropIdToList = isKnownType = false; 2582 } 2583 } 2584 2585 if (isKnownType) { 2586 if (addPropIdToList) { 2587 d->fileInfoPopIDs.append(type); 2588 } 2589 } else { 2590 d->skipData(d->readNumber()); 2591 } 2592 2593 bool checkRecordsSize = (major > 0 || minor > 2); 2594 if (checkRecordsSize && d->pos - ppp != size) { 2595 setErrorString(tr("Read size failed " 2596 "(checkRecordsSize: %1, d->pos - ppp: %2, size: %3)") 2597 .arg(checkRecordsSize) 2598 .arg(d->pos - ppp) 2599 .arg(size)); 2600 return false; 2601 } 2602 } 2603 2604 int emptyFileIndex = 0; 2605 int sizeIndex = 0; 2606 2607 int numAntiItems = 0; 2608 2609 if (emptyStreamVector.isEmpty()) { 2610 emptyStreamVector.fill(false, numFiles); 2611 } 2612 2613 if (antiFileVector.isEmpty()) { 2614 antiFileVector.fill(false, numEmptyStreams); 2615 } 2616 if (emptyFileVector.isEmpty()) { 2617 emptyFileVector.fill(false, numEmptyStreams); 2618 } 2619 2620 for (int i = 0; i < numEmptyStreams; i++) { 2621 if (antiFileVector[i]) { 2622 numAntiItems++; 2623 } 2624 } 2625 2626 d->outData = d->readAndDecodePackedStreams(false); 2627 2628 int oldPos = 0; 2629 for (int i = 0; i < numFiles; i++) { 2630 FileInfo *fileInfo = d->fileInfos.at(i); 2631 bool isAnti; 2632 fileInfo->hasStream = !emptyStreamVector[i]; 2633 if (fileInfo->hasStream) { 2634 fileInfo->isDir = false; 2635 isAnti = false; 2636 fileInfo->size = d->unpackSizes[sizeIndex]; 2637 fileInfo->crc = d->digests[sizeIndex]; 2638 fileInfo->crcDefined = d->digestsDefined[sizeIndex]; 2639 sizeIndex++; 2640 } else { 2641 fileInfo->isDir = !emptyFileVector[emptyFileIndex]; 2642 isAnti = antiFileVector[emptyFileIndex]; 2643 emptyFileIndex++; 2644 fileInfo->size = 0; 2645 fileInfo->crcDefined = false; 2646 } 2647 if (numAntiItems != 0) { 2648 d->isAnti.append(isAnti); 2649 } 2650 2651 int access; 2652 bool symlink = false; 2653 if (fileInfo->attributes & FILE_ATTRIBUTE_UNIX_EXTENSION) { 2654 access = fileInfo->attributes >> 16; 2655 if ((access & QT_STAT_MASK) == QT_STAT_LNK) { 2656 symlink = true; 2657 } 2658 } else { 2659 if (fileInfo->isDir) { 2660 access = S_IFDIR | 0755; 2661 } else { 2662 access = 0100644; 2663 } 2664 } 2665 2666 qint64 pos = 0; 2667 if (!fileInfo->isDir) { 2668 pos = oldPos; 2669 oldPos += fileInfo->size; 2670 } 2671 2672 KArchiveEntry *e; 2673 QString entryName; 2674 int index = fileInfo->path.lastIndexOf(QLatin1Char('/')); 2675 if (index == -1) { 2676 entryName = fileInfo->path; 2677 } else { 2678 entryName = fileInfo->path.mid(index + 1); 2679 } 2680 Q_ASSERT(!entryName.isEmpty()); 2681 2682 QDateTime mTime; 2683 if (d->mTimesDefined[i]) { 2684 mTime = KArchivePrivate::time_tToDateTime(toTimeT(d->mTimes[i])); 2685 } else { 2686 mTime = KArchivePrivate::time_tToDateTime(time(nullptr)); 2687 } 2688 2689 if (fileInfo->isDir) { 2690 QString path = QDir::cleanPath(fileInfo->path); 2691 const KArchiveEntry *ent = rootDir()->entry(path); 2692 if (ent && ent->isDirectory()) { 2693 e = nullptr; 2694 } else { 2695 e = new KArchiveDirectory(this, entryName, access, mTime, rootDir()->user(), rootDir()->group(), QString() /*symlink*/); 2696 } 2697 } else { 2698 if (!symlink) { 2699 e = new K7ZipFileEntry(this, 2700 entryName, 2701 access, 2702 mTime, 2703 rootDir()->user(), 2704 rootDir()->group(), 2705 QString() /*symlink*/, 2706 pos, 2707 fileInfo->size, 2708 d->outData); 2709 } else { 2710 QString target = QFile::decodeName(d->outData.mid(pos, fileInfo->size)); 2711 e = new K7ZipFileEntry(this, entryName, access, mTime, rootDir()->user(), rootDir()->group(), target, 0, 0, nullptr); 2712 } 2713 } 2714 2715 if (e) { 2716 if (index == -1) { 2717 rootDir()->addEntry(e); 2718 } else { 2719 QString path = QDir::cleanPath(fileInfo->path.left(index)); 2720 KArchiveDirectory *d = findOrCreate(path); 2721 d->addEntry(e); 2722 } 2723 } 2724 } 2725 2726 return true; 2727 } 2728 2729 bool K7Zip::closeArchive() 2730 { 2731 // Unnecessary check (already checked by KArchive::close()) 2732 if (!isOpen()) { 2733 // qCWarning(KArchiveLog) << "You must open the file before close it\n"; 2734 return false; 2735 } 2736 2737 if ((mode() == QIODevice::ReadOnly)) { 2738 return true; 2739 } 2740 2741 d->clear(); 2742 2743 Folder *folder = new Folder(); 2744 2745 folder->unpackSizes.clear(); 2746 folder->unpackSizes.append(d->outData.size()); 2747 2748 Folder::FolderInfo *info = new Folder::FolderInfo(); 2749 2750 info->numInStreams = 1; 2751 info->numOutStreams = 1; 2752 info->methodID = k_LZMA2; 2753 2754 quint32 dictSize = d->outData.size(); 2755 2756 const quint32 kMinReduceSize = (1 << 16); 2757 if (dictSize < kMinReduceSize) { 2758 dictSize = kMinReduceSize; 2759 } 2760 2761 // k_LZMA2 method 2762 int dict; 2763 for (dict = 0; dict < 40; dict++) { 2764 if (dictSize <= lzma2_dic_size_from_prop(dict)) { 2765 break; 2766 } 2767 } 2768 info->properties.append(dict); 2769 2770 folder->folderInfos.append(info); 2771 d->folders.append(folder); 2772 2773 const KArchiveDirectory *dir = directory(); 2774 QByteArray data; 2775 d->createItemsFromEntities(dir, QString(), data); 2776 d->outData = data; 2777 2778 folder->unpackCRCDefined = true; 2779 folder->unpackCRC = crc32(0, (Bytef *)(d->outData.data()), d->outData.size()); 2780 2781 // compress data 2782 QByteArray encodedData; 2783 if (!d->outData.isEmpty()) { 2784 QByteArray enc; 2785 QBuffer inBuffer(&enc); 2786 2787 KCompressionDevice flt(&inBuffer, false, KCompressionDevice::Xz); 2788 flt.open(QIODevice::WriteOnly); 2789 2790 KFilterBase *filter = flt.filterBase(); 2791 2792 static_cast<KXzFilter *>(filter)->init(QIODevice::WriteOnly, KXzFilter::LZMA2, info->properties); 2793 2794 const int ret = flt.write(d->outData); 2795 if (ret != d->outData.size()) { 2796 setErrorString(tr("Write error")); 2797 return false; 2798 } 2799 2800 flt.close(); 2801 encodedData = inBuffer.data(); 2802 } 2803 2804 d->packSizes.append(encodedData.size()); 2805 2806 int numUnpackStream = 0; 2807 for (int i = 0; i < d->fileInfos.size(); ++i) { 2808 if (d->fileInfos.at(i)->hasStream) { 2809 numUnpackStream++; 2810 } 2811 } 2812 d->numUnpackStreamsInFolders.append(numUnpackStream); 2813 2814 quint64 headerOffset; 2815 d->writeHeader(headerOffset); 2816 2817 // Encode Header 2818 QByteArray encodedStream; 2819 { 2820 QVector<quint64> packSizes; 2821 QVector<Folder *> folders; 2822 encodedStream = d->encodeStream(packSizes, folders); 2823 2824 if (folders.isEmpty()) { 2825 // FIXME Not sure why this is an error. Come up with a better message 2826 setErrorString(tr("Failed while encoding header")); 2827 return false; 2828 } 2829 2830 d->header.clear(); 2831 2832 d->writeByte(kEncodedHeader); 2833 QVector<bool> emptyDefined; 2834 QVector<quint32> emptyCrcs; 2835 d->writePackInfo(headerOffset, packSizes, emptyDefined, emptyCrcs); 2836 d->writeUnpackInfo(folders); 2837 d->writeByte(kEnd); 2838 for (int i = 0; i < packSizes.size(); i++) { 2839 headerOffset += packSizes.at(i); 2840 } 2841 qDeleteAll(folders); 2842 } 2843 // end encode header 2844 2845 quint64 nextHeaderSize = d->header.size(); 2846 quint32 nextHeaderCRC = crc32(0, (Bytef *)(d->header.data()), d->header.size()); 2847 quint64 nextHeaderOffset = headerOffset; 2848 2849 device()->seek(0); 2850 d->writeSignature(); 2851 d->writeStartHeader(nextHeaderSize, nextHeaderCRC, nextHeaderOffset); 2852 device()->write(encodedData.data(), encodedData.size()); 2853 device()->write(encodedStream.data(), encodedStream.size()); 2854 device()->write(d->header.data(), d->header.size()); 2855 2856 return true; 2857 } 2858 2859 bool K7Zip::doFinishWriting(qint64 size) 2860 { 2861 d->m_currentFile->setSize(size); 2862 d->m_currentFile = nullptr; 2863 2864 return true; 2865 } 2866 2867 bool K7Zip::writeData(const char *data, qint64 size) 2868 { 2869 if (!d->m_currentFile) { 2870 setErrorString(tr("No file currently selected")); 2871 return false; 2872 } 2873 2874 if (d->m_currentFile->position() == d->outData.size()) { 2875 d->outData.append(data, size); 2876 } else { 2877 d->outData.remove(d->m_currentFile->position(), d->m_currentFile->size()); 2878 d->outData.insert(d->m_currentFile->position(), data, size); 2879 } 2880 2881 return true; 2882 } 2883 2884 bool K7Zip::doPrepareWriting(const QString &name, 2885 const QString &user, 2886 const QString &group, 2887 qint64 /*size*/, 2888 mode_t perm, 2889 const QDateTime & /*atime*/, 2890 const QDateTime &mtime, 2891 const QDateTime & /*ctime*/) 2892 { 2893 if (!isOpen()) { 2894 setErrorString(tr("Application error: 7-Zip file must be open before being written into")); 2895 qCWarning(KArchiveLog) << "doPrepareWriting failed: !isOpen()"; 2896 return false; 2897 } 2898 2899 if (!(mode() & QIODevice::WriteOnly)) { 2900 setErrorString(tr("Application error: attempted to write into non-writable 7-Zip file")); 2901 qCWarning(KArchiveLog) << "doPrepareWriting failed: !(mode() & QIODevice::WriteOnly)"; 2902 return false; 2903 } 2904 2905 // Find or create parent dir 2906 KArchiveDirectory *parentDir = rootDir(); 2907 // QString fileName( name ); 2908 // In some files we can find dir/./file => call cleanPath 2909 QString fileName(QDir::cleanPath(name)); 2910 int i = name.lastIndexOf(QLatin1Char('/')); 2911 if (i != -1) { 2912 QString dir = name.left(i); 2913 fileName = name.mid(i + 1); 2914 parentDir = findOrCreate(dir); 2915 } 2916 2917 // test if the entry already exist 2918 const KArchiveEntry *entry = parentDir->entry(fileName); 2919 if (!entry) { 2920 K7ZipFileEntry *e = 2921 new K7ZipFileEntry(this, fileName, perm, mtime, user, group, QString() /*symlink*/, d->outData.size(), 0 /*unknown yet*/, d->outData); 2922 if (!parentDir->addEntryV2(e)) { 2923 return false; 2924 } 2925 d->m_entryList << e; 2926 d->m_currentFile = e; 2927 } else { 2928 // TODO : find and replace in m_entryList 2929 // d->m_currentFile = static_cast<K7ZipFileEntry*>(entry); 2930 } 2931 2932 return true; 2933 } 2934 2935 bool K7Zip::doWriteDir(const QString &name, 2936 const QString &user, 2937 const QString &group, 2938 mode_t perm, 2939 const QDateTime & /*atime*/, 2940 const QDateTime &mtime, 2941 const QDateTime & /*ctime*/) 2942 { 2943 if (!isOpen()) { 2944 setErrorString(tr("Application error: 7-Zip file must be open before being written into")); 2945 qCWarning(KArchiveLog) << "doWriteDir failed: !isOpen()"; 2946 return false; 2947 } 2948 2949 if (!(mode() & QIODevice::WriteOnly)) { 2950 // qCWarning(KArchiveLog) << "You must open the tar file for writing\n"; 2951 return false; 2952 } 2953 2954 // In some tar files we can find dir/./ => call cleanPath 2955 QString dirName(QDir::cleanPath(name)); 2956 2957 // Remove trailing '/' 2958 if (dirName.endsWith(QLatin1Char('/'))) { 2959 dirName.remove(dirName.size() - 1, 1); 2960 } 2961 2962 KArchiveDirectory *parentDir = rootDir(); 2963 int i = dirName.lastIndexOf(QLatin1Char('/')); 2964 if (i != -1) { 2965 QString dir = name.left(i); 2966 dirName = name.mid(i + 1); 2967 parentDir = findOrCreate(dir); 2968 } 2969 2970 KArchiveDirectory *e = new KArchiveDirectory(this, dirName, perm, mtime, user, group, QString() /*symlink*/); 2971 parentDir->addEntry(e); 2972 2973 return true; 2974 } 2975 2976 bool K7Zip::doWriteSymLink(const QString &name, 2977 const QString &target, 2978 const QString &user, 2979 const QString &group, 2980 mode_t perm, 2981 const QDateTime & /*atime*/, 2982 const QDateTime &mtime, 2983 const QDateTime & /*ctime*/) 2984 { 2985 if (!isOpen()) { 2986 setErrorString(tr("Application error: 7-Zip file must be open before being written into")); 2987 qCWarning(KArchiveLog) << "doWriteSymLink failed: !isOpen()"; 2988 return false; 2989 } 2990 2991 if (!(mode() & QIODevice::WriteOnly)) { 2992 setErrorString(tr("Application error: attempted to write into non-writable 7-Zip file")); 2993 qCWarning(KArchiveLog) << "doWriteSymLink failed: !(mode() & QIODevice::WriteOnly)"; 2994 return false; 2995 } 2996 2997 // Find or create parent dir 2998 KArchiveDirectory *parentDir = rootDir(); 2999 // In some files we can find dir/./file => call cleanPath 3000 QString fileName(QDir::cleanPath(name)); 3001 int i = name.lastIndexOf(QLatin1Char('/')); 3002 if (i != -1) { 3003 QString dir = name.left(i); 3004 fileName = name.mid(i + 1); 3005 parentDir = findOrCreate(dir); 3006 } 3007 QByteArray encodedTarget = QFile::encodeName(target); 3008 3009 K7ZipFileEntry *e = new K7ZipFileEntry(this, fileName, perm, mtime, user, group, target, 0, 0, nullptr); 3010 d->outData.append(encodedTarget); 3011 3012 if (!parentDir->addEntryV2(e)) { 3013 return false; 3014 } 3015 3016 d->m_entryList << e; 3017 3018 return true; 3019 } 3020 3021 void K7Zip::virtual_hook(int id, void *data) 3022 { 3023 KArchive::virtual_hook(id, data); 3024 }