1 /* This file is part of the KDE libraries
2    SPDX-FileCopyrightText: 2011 Mario Bensi <mbensi@ipsquad.net>
3 
4    SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "k7zip.h"
8 #include "karchive_p.h"
9 #include "loggingcategory.h"
10 
11 #include <QBuffer>
12 #include <QDebug>
13 #include <QDir>
14 #include <QFile>
15 #include <qplatformdefs.h>
16 
17 #include "kcompressiondevice.h"
18 #include "klimitediodevice_p.h"
19 #include <kfilterbase.h>
20 #include <kxzfilter.h>
21 
22 #include "zlib.h"
23 #include <memory>
24 #include <time.h> // time()
25 
26 #ifndef QT_STAT_LNK
27 #define QT_STAT_LNK 0120000
28 #endif // QT_STAT_LNK
29 
30 ////////////////////////////////////////////////////////////////////////
31 /////////////////////////// K7Zip //////////////////////////////////////
32 ////////////////////////////////////////////////////////////////////////
33 
34 #define BUFFER_SIZE 8 * 1024
35 
36 static const unsigned char k7zip_signature[6] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C};
37 // static const unsigned char XZ_HEADER_MAGIC[6] = { 0xFD, '7', 'z', 'X', 'Z', 0x00 };
38 
39 /* clang-format off */
GetUi16(const char * p,quint64 offset)40 static QChar GetUi16(const char *p, quint64 offset)
41 {
42     return QChar(static_cast<unsigned char>(p[offset + 0])
43                  | (static_cast<unsigned char>(p[1]) << 8));
44 }
45 
GetUi32(const char * p,quint64 offset)46 static quint32 GetUi32(const char *p, quint64 offset)
47 {
48     return (static_cast<unsigned char>(p[offset + 0])
49             | (static_cast<unsigned char>(p[offset + 1]) << 8)
50             | (static_cast<unsigned char>(p[offset + 2]) << 16)
51             | (static_cast<unsigned char>(p[offset + 3]) << 24));
52 }
53 
GetUi64(const char * p,quint64 offset)54 static quint64 GetUi64(const char *p, quint64 offset)
55 {
56     return (GetUi32(p, offset)
57             | (static_cast<quint64>(GetUi32(p, offset + 4)) << 32));
58 }
59 
lzma2_dic_size_from_prop(int p)60 static quint32 lzma2_dic_size_from_prop(int p)
61 {
62     return ((static_cast<quint32>(2) | (p & 1)) << ((p / 2) + 11));
63 }
64 
65 /* clang-format on*/
66 
67 #define FILE_ATTRIBUTE_READONLY 1
68 #define FILE_ATTRIBUTE_HIDDEN 2
69 #define FILE_ATTRIBUTE_SYSTEM 4
70 #define FILE_ATTRIBUTE_DIRECTORY 16
71 #define FILE_ATTRIBUTE_ARCHIVE 32
72 #define FILE_ATTRIBUTE_DEVICE 64
73 #define FILE_ATTRIBUTE_NORMAL 128
74 #define FILE_ATTRIBUTE_TEMPORARY 256
75 #define FILE_ATTRIBUTE_SPARSE_FILE 512
76 #define FILE_ATTRIBUTE_REPARSE_POINT 1024
77 #define FILE_ATTRIBUTE_COMPRESSED 2048
78 #define FILE_ATTRIBUTE_OFFLINE 0x1000
79 #define FILE_ATTRIBUTE_ENCRYPTED 0x4000
80 #define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */
81 
82 enum HeaderType {
83     kEnd,
84 
85     kHeader,
86 
87     kArchiveProperties,
88 
89     kAdditionalStreamsInfo,
90     kMainStreamsInfo,
91     kFilesInfo,
92 
93     kPackInfo,
94     kUnpackInfo,
95     kSubStreamsInfo,
96 
97     kSize,
98     kCRC,
99 
100     kFolder,
101 
102     kCodersUnpackSize,
103     kNumUnpackStream,
104 
105     kEmptyStream,
106     kEmptyFile,
107     kAnti,
108 
109     kName,
110     kCTime,
111     kATime,
112     kMTime,
113     kAttributes,
114     kComment,
115 
116     kEncodedHeader,
117 
118     kStartPos,
119     kDummy,
120 };
121 
122 // Method ID
123 // static const quint64 k_Copy = 0x00;
124 // static const quint64 k_Delta = 0x03;
125 // static const quint64 k_x86 = 0x04; //BCJ
126 // static const quint64 k_PPC = 0x05; // BIG Endian
127 // static const quint64 k_IA64 = 0x06;
128 // static const quint64 k_ARM = 0x07; // little Endian
129 // static const quint64 k_ARM_Thumb = 0x08; // little Endian
130 // static const quint64 k_SPARC = 0x09;
131 static const quint64 k_LZMA2 = 0x21;
132 // static const quint64 k_Swap2 = 0x020302;
133 // static const quint64 k_Swap4 = 0x020304;
134 static const quint64 k_LZMA = 0x030101;
135 static const quint64 k_BCJ = 0x03030103;
136 static const quint64 k_BCJ2 = 0x0303011B;
137 // static const quint64 k_7zPPC = 0x03030205;
138 // static const quint64 k_Alpha = 0x03030301;
139 // static const quint64 k_7zIA64 = 0x03030401;
140 // static const quint64 k_7zARM = 0x03030501;
141 // static const quint64 k_M68 = 0x03030605; //Big Endian
142 // static const quint64 k_ARMT = 0x03030701;
143 // static const quint64 k_7zSPARC = 0x03030805;
144 static const quint64 k_PPMD = 0x030401;
145 // static const quint64 k_Experimental = 0x037F01;
146 // static const quint64 k_Shrink = 0x040101;
147 // static const quint64 k_Implode = 0x040106;
148 // static const quint64 k_Deflate = 0x040108;
149 // static const quint64 k_Deflate64 = 0x040109;
150 // static const quint64 k_Imploding = 0x040110;
151 // static const quint64 k_Jpeg = 0x040160;
152 // static const quint64 k_WavPack = 0x040161;
153 // static const quint64 k_PPMd = 0x040162;
154 // static const quint64 k_wzAES = 0x040163;
155 static const quint64 k_BZip2 = 0x040202;
156 // static const quint64 k_Rar15 = 0x040301;
157 // static const quint64 k_Rar20 = 0x040302;
158 // static const quint64 k_Rar29 = 0x040303;
159 // static const quint64 k_Arj = 0x040401; //1 2 3
160 // static const quint64 k_Arj4 = 0x040402;
161 // static const quint64 k_Z = 0x0405;
162 // static const quint64 k_Lzh = 0x0406;
163 // static const quint64 k_Cab = 0x0408;
164 // static const quint64 k_DeflateNSIS = 0x040901;
165 // static const quint64 k_Bzip2NSIS = 0x040902;
166 static const quint64 k_AES = 0x06F10701;
167 
168 /**
169  * A K7ZipFileEntry represents a file in a 7zip archive.
170  */
171 class K7ZipFileEntry : public KArchiveFile
172 {
173 public:
174     K7ZipFileEntry(K7Zip *zip,
175                    const QString &name,
176                    int access,
177                    const QDateTime &date,
178                    const QString &user,
179                    const QString &group,
180                    const QString &symlink,
181                    qint64 pos,
182                    qint64 size,
183                    const QByteArray &data);
184 
185     ~K7ZipFileEntry() override;
186 
187     /**
188      * @return the content of this file.
189      * Call data() with care (only once per file), this data isn't cached.
190      */
191     QByteArray data() const override;
192 
193     /**
194      * This method returns QIODevice (internal class: KLimitedIODevice)
195      * on top of the underlying QIODevice. This is obviously for reading only.
196      *
197      * WARNING: Note that the ownership of the device is being transferred to the caller,
198      * who will have to delete it.
199      *
200      * The returned device auto-opens (in readonly mode), no need to open it.
201      * @return the QIODevice of the file
202      */
203     QIODevice *createDevice() const override;
204 
205 private:
206     const QByteArray m_data;
207     QBuffer *m_buffer;
208 };
209 
K7ZipFileEntry(K7Zip * zip,const QString & name,int access,const QDateTime & date,const QString & user,const QString & group,const QString & symlink,qint64 pos,qint64 size,const QByteArray & data)210 K7ZipFileEntry::K7ZipFileEntry(K7Zip *zip,
211                                const QString &name,
212                                int access,
213                                const QDateTime &date,
214                                const QString &user,
215                                const QString &group,
216                                const QString &symlink,
217                                qint64 pos,
218                                qint64 size,
219                                const QByteArray &data)
220     : KArchiveFile(zip, name, access, date, user, group, symlink, pos, size)
221     , m_data(data)
222     , m_buffer(new QBuffer)
223 {
224     m_buffer->setData(m_data);
225     m_buffer->open(QIODevice::ReadOnly);
226 }
227 
~K7ZipFileEntry()228 K7ZipFileEntry::~K7ZipFileEntry()
229 {
230     delete m_buffer;
231 }
232 
data() const233 QByteArray K7ZipFileEntry::data() const
234 {
235     return m_data.mid(position(), size());
236 }
237 
createDevice() const238 QIODevice *K7ZipFileEntry::createDevice() const
239 {
240     return new KLimitedIODevice(m_buffer, position(), size());
241 }
242 
243 class FileInfo
244 {
245 public:
FileInfo()246     FileInfo()
247         : size(0)
248         , attributes(0)
249         , crc(0)
250         , attribDefined(false)
251         , crcDefined(false)
252         , hasStream(false)
253         , isDir(false)
254     {
255     }
256 
257     QString path;
258     quint64 size;
259     quint32 attributes;
260     quint32 crc;
261     bool attribDefined;
262     bool crcDefined;
263     bool hasStream;
264     bool isDir;
265 };
266 
267 class Folder
268 {
269 public:
270     class FolderInfo
271     {
272     public:
FolderInfo()273         FolderInfo()
274             : numInStreams(0)
275             , numOutStreams(0)
276             , methodID(0)
277         {
278         }
279 
isSimpleCoder() const280         bool isSimpleCoder() const
281         {
282             return (numInStreams == 1) && (numOutStreams == 1);
283         }
284 
285         int numInStreams;
286         int numOutStreams;
287         QVector<unsigned char> properties;
288         quint64 methodID;
289     };
290 
Folder()291     Folder()
292         : unpackCRCDefined(false)
293         , unpackCRC(0)
294     {
295     }
296 
~Folder()297     ~Folder()
298     {
299         qDeleteAll(folderInfos);
300     }
301 
Q_DISABLE_COPY(Folder)302     Q_DISABLE_COPY(Folder)
303 
304     quint64 getUnpackSize() const
305     {
306         if (unpackSizes.isEmpty()) {
307             return 0;
308         }
309         for (int i = unpackSizes.size() - 1; i >= 0; i--) {
310             if (findBindPairForOutStream(i) < 0) {
311                 return unpackSizes.at(i);
312             }
313         }
314         return 0;
315     }
316 
getNumOutStreams() const317     int getNumOutStreams() const
318     {
319         int result = 0;
320         for (int i = 0; i < folderInfos.size(); i++) {
321             result += folderInfos.at(i)->numOutStreams;
322         }
323         return result;
324     }
325 
getCoderInStreamIndex(quint32 coderIndex) const326     quint32 getCoderInStreamIndex(quint32 coderIndex) const
327     {
328         quint32 streamIndex = 0;
329         for (quint32 i = 0; i < coderIndex; i++) {
330             streamIndex += folderInfos.at(i)->numInStreams;
331         }
332         return streamIndex;
333     }
334 
getCoderOutStreamIndex(quint32 coderIndex) const335     quint32 getCoderOutStreamIndex(quint32 coderIndex) const
336     {
337         quint32 streamIndex = 0;
338         for (quint32 i = 0; i < coderIndex; i++) {
339             streamIndex += folderInfos.at(i)->numOutStreams;
340         }
341         return streamIndex;
342     }
343 
findBindPairForInStream(size_t inStreamIndex) const344     int findBindPairForInStream(size_t inStreamIndex) const
345     {
346         for (int i = 0; i < inIndexes.size(); i++) {
347             if (inIndexes[i] == inStreamIndex) {
348                 return i;
349             }
350         }
351         return -1;
352     }
353 
findBindPairForOutStream(size_t outStreamIndex) const354     int findBindPairForOutStream(size_t outStreamIndex) const
355     {
356         for (int i = 0; i < outIndexes.size(); i++) {
357             if (outIndexes[i] == outStreamIndex) {
358                 return i;
359             }
360         }
361         return -1;
362     }
363 
findPackStreamArrayIndex(size_t inStreamIndex) const364     int findPackStreamArrayIndex(size_t inStreamIndex) const
365     {
366         for (int i = 0; i < packedStreams.size(); i++) {
367             if (packedStreams[i] == inStreamIndex) {
368                 return i;
369             }
370         }
371         return -1;
372     }
373 
findInStream(quint32 streamIndex,quint32 & coderIndex,quint32 & coderStreamIndex) const374     void findInStream(quint32 streamIndex, quint32 &coderIndex, quint32 &coderStreamIndex) const
375     {
376         for (coderIndex = 0; coderIndex < (quint32)folderInfos.size(); coderIndex++) {
377             quint32 curSize = folderInfos[coderIndex]->numInStreams;
378             if (streamIndex < curSize) {
379                 coderStreamIndex = streamIndex;
380                 return;
381             }
382             streamIndex -= curSize;
383         }
384     }
385 
findOutStream(quint32 streamIndex,quint32 & coderIndex,quint32 & coderStreamIndex) const386     void findOutStream(quint32 streamIndex, quint32 &coderIndex, quint32 &coderStreamIndex) const
387     {
388         for (coderIndex = 0; coderIndex < (quint32)folderInfos.size(); coderIndex++) {
389             quint32 curSize = folderInfos[coderIndex]->numOutStreams;
390             if (streamIndex < curSize) {
391                 coderStreamIndex = streamIndex;
392                 return;
393             }
394             streamIndex -= curSize;
395         }
396     }
397 
isEncrypted() const398     bool isEncrypted() const
399     {
400         for (int i = folderInfos.size() - 1; i >= 0; i--) {
401             if (folderInfos.at(i)->methodID == k_AES) {
402                 return true;
403             }
404         }
405         return false;
406     }
407 
408     // bool CheckStructure() const;
409 
410     bool unpackCRCDefined;
411     quint32 unpackCRC;
412     QVector<FolderInfo *> folderInfos;
413     QVector<quint64> inIndexes;
414     QVector<quint64> outIndexes;
415     QVector<quint64> packedStreams;
416     QVector<quint64> unpackSizes;
417 };
418 
419 class Q_DECL_HIDDEN K7Zip::K7ZipPrivate
420 {
421 public:
K7ZipPrivate(K7Zip * parent)422     K7ZipPrivate(K7Zip *parent)
423         : q(parent)
424         , packPos(0)
425         , numPackStreams(0)
426         , buffer(nullptr)
427         , pos(0)
428         , end(0)
429         , headerSize(0)
430         , countSize(0)
431         , m_currentFile(nullptr)
432     {
433     }
434 
~K7ZipPrivate()435     ~K7ZipPrivate()
436     {
437         qDeleteAll(folders);
438         qDeleteAll(fileInfos);
439     }
440 
441     K7Zip *q;
442 
443     QVector<bool> packCRCsDefined;
444     QVector<quint32> packCRCs;
445     QVector<quint64> numUnpackStreamsInFolders;
446 
447     QVector<Folder *> folders;
448     QVector<FileInfo *> fileInfos;
449     // File information
450     QVector<bool> cTimesDefined;
451     QVector<quint64> cTimes;
452     QVector<bool> aTimesDefined;
453     QVector<quint64> aTimes;
454     QVector<bool> mTimesDefined;
455     QVector<quint64> mTimes;
456     QVector<bool> startPositionsDefined;
457     QVector<quint64> startPositions;
458     QVector<int> fileInfoPopIDs;
459 
460     quint64 packPos;
461     quint64 numPackStreams;
462     QVector<quint64> packSizes;
463     QVector<quint64> unpackSizes;
464     QVector<bool> digestsDefined;
465     QVector<quint32> digests;
466 
467     QVector<bool> isAnti;
468 
469     const char *buffer;
470     quint64 pos;
471     quint64 end;
472     quint64 headerSize;
473     quint64 countSize;
474 
475     // Write
476     QByteArray header;
477     QByteArray outData; // Store data in this buffer before compress and write in archive.
478     K7ZipFileEntry *m_currentFile;
479     QVector<KArchiveEntry *> m_entryList;
480 
clear()481     void clear()
482     {
483         packCRCsDefined.clear();
484         packCRCs.clear();
485         numUnpackStreamsInFolders.clear();
486         qDeleteAll(folders);
487         folders.clear();
488         qDeleteAll(fileInfos);
489         fileInfos.clear();
490         cTimesDefined.clear();
491         cTimes.clear();
492         aTimesDefined.clear();
493         aTimes.clear();
494         mTimesDefined.clear();
495         mTimes.clear();
496         startPositionsDefined.clear();
497         startPositions.clear();
498         fileInfoPopIDs.clear();
499         packSizes.clear();
500         unpackSizes.clear();
501         digestsDefined.clear();
502         digests.clear();
503         isAnti.clear();
504 
505         buffer = nullptr;
506         pos = 0;
507         end = 0;
508         headerSize = 0;
509         countSize = 0;
510     }
511 
512     // Read
513     int readByte();
514     quint32 readUInt32();
515     quint64 readUInt64();
516     quint64 readNumber();
517     QString readString();
518     void readHashDigests(int numItems, QVector<bool> &digestsDefined, QVector<quint32> &digests);
519     void readBoolVector(int numItems, QVector<bool> &v);
520     void readBoolVector2(int numItems, QVector<bool> &v);
521     void skipData(int size);
522     bool findAttribute(int attribute);
523     bool readUInt64DefVector(int numFiles, QVector<quint64> &values, QVector<bool> &defined);
524 
525     Folder *folderItem();
526     bool readMainStreamsInfo();
527     bool readPackInfo();
528     bool readUnpackInfo();
529     bool readSubStreamsInfo();
530     QByteArray readAndDecodePackedStreams(bool readMainStreamInfo = true);
531 
532     // Write
533     void createItemsFromEntities(const KArchiveDirectory *, const QString &, QByteArray &);
534     void writeByte(unsigned char b);
535     void writeNumber(quint64 value);
536     void writeBoolVector(const QVector<bool> &boolVector);
537     void writeUInt32(quint32 value);
538     void writeUInt64(quint64 value);
539     void writeHashDigests(const QVector<bool> &digestsDefined, const QVector<quint32> &digests);
540     void writeAlignedBoolHeader(const QVector<bool> &v, int numDefined, int type, unsigned itemSize);
541     void writeUInt64DefVector(const QVector<quint64> &v, const QVector<bool> &defined, int type);
542     void writeFolder(const Folder *folder);
543     void writePackInfo(quint64 dataOffset, QVector<quint64> &packedSizes, QVector<bool> &packedCRCsDefined, QVector<quint32> &packedCRCs);
544     void writeUnpackInfo(const QVector<Folder *> &folderItems);
545     void writeSubStreamsInfo(const QVector<quint64> &unpackSizes, const QVector<bool> &digestsDefined, const QVector<quint32> &digests);
546     void writeHeader(quint64 &headerOffset);
547     void writeSignature();
548     void writeStartHeader(const quint64 nextHeaderSize, const quint32 nextHeaderCRC, const quint64 nextHeaderOffset);
549     QByteArray encodeStream(QVector<quint64> &packSizes, QVector<Folder *> &folds);
550 };
551 
K7Zip(const QString & fileName)552 K7Zip::K7Zip(const QString &fileName)
553     : KArchive(fileName)
554     , d(new K7ZipPrivate(this))
555 {
556 }
557 
K7Zip(QIODevice * dev)558 K7Zip::K7Zip(QIODevice *dev)
559     : KArchive(dev)
560     , d(new K7ZipPrivate(this))
561 {
562     Q_ASSERT(dev);
563 }
564 
~K7Zip()565 K7Zip::~K7Zip()
566 {
567     if (isOpen()) {
568         close();
569     }
570 
571     delete d;
572 }
573 
readByte()574 int K7Zip::K7ZipPrivate::readByte()
575 {
576     if (!buffer || pos + 1 > end) {
577         return -1;
578     }
579     return buffer[pos++];
580 }
581 
readUInt32()582 quint32 K7Zip::K7ZipPrivate::readUInt32()
583 {
584     if (!buffer || (quint64)(pos + 4) > end) {
585         qCDebug(KArchiveLog) << "error size";
586         return 0;
587     }
588 
589     quint32 res = GetUi32(buffer, pos);
590     pos += 4;
591     return res;
592 }
593 
readUInt64()594 quint64 K7Zip::K7ZipPrivate::readUInt64()
595 {
596     if (!buffer || (quint64)(pos + 8) > end) {
597         qCDebug(KArchiveLog) << "error size";
598         return 0;
599     }
600 
601     quint64 res = GetUi64(buffer, pos);
602     pos += 8;
603     return res;
604 }
605 
readNumber()606 quint64 K7Zip::K7ZipPrivate::readNumber()
607 {
608     if (!buffer || (quint64)(pos + 8) > end) {
609         return 0;
610     }
611 
612     unsigned char firstByte = buffer[pos++];
613     unsigned char mask = 0x80;
614     quint64 value = 0;
615     for (int i = 0; i < 8; i++) {
616         if ((firstByte & mask) == 0) {
617             quint64 highPart = firstByte & (mask - 1);
618             value += (highPart << (i * 8));
619             return value;
620         }
621         value |= ((unsigned char)buffer[pos++] << (8 * i));
622         mask >>= 1;
623     }
624     return value;
625 }
626 
readString()627 QString K7Zip::K7ZipPrivate::readString()
628 {
629     if (!buffer) {
630         return QString();
631     }
632 
633     const char *buf = buffer + pos;
634     size_t rem = (end - pos) / 2 * 2;
635     {
636         size_t i;
637         for (i = 0; i < rem; i += 2) {
638             if (buf[i] == 0 && buf[i + 1] == 0) {
639                 break;
640             }
641         }
642         if (i == rem) {
643             qCDebug(KArchiveLog) << "read string error";
644             return QString();
645         }
646         rem = i;
647     }
648 
649     int len = (int)(rem / 2);
650     if (len < 0 || (size_t)len * 2 != rem) {
651         qCDebug(KArchiveLog) << "read string unsupported";
652         return QString();
653     }
654 
655     QString p;
656     for (int i = 0; i < len; i++, buf += 2) {
657         p += GetUi16(buf, 0);
658     }
659 
660     pos += rem + 2;
661     return p;
662 }
663 
skipData(int size)664 void K7Zip::K7ZipPrivate::skipData(int size)
665 {
666     if (!buffer || pos + size > end) {
667         return;
668     }
669     pos += size;
670 }
671 
findAttribute(int attribute)672 bool K7Zip::K7ZipPrivate::findAttribute(int attribute)
673 {
674     if (!buffer) {
675         return false;
676     }
677 
678     for (;;) {
679         int type = readByte();
680         if (type == attribute) {
681             return true;
682         }
683         if (type == kEnd) {
684             return false;
685         }
686         skipData(readNumber());
687     }
688 }
689 
readBoolVector(int numItems,QVector<bool> & v)690 void K7Zip::K7ZipPrivate::readBoolVector(int numItems, QVector<bool> &v)
691 {
692     if (!buffer) {
693         return;
694     }
695 
696     unsigned char b = 0;
697     unsigned char mask = 0;
698     for (int i = 0; i < numItems; i++) {
699         if (mask == 0) {
700             b = readByte();
701             mask = 0x80;
702         }
703         v.append((b & mask) != 0);
704         mask >>= 1;
705     }
706 }
707 
readBoolVector2(int numItems,QVector<bool> & v)708 void K7Zip::K7ZipPrivate::readBoolVector2(int numItems, QVector<bool> &v)
709 {
710     if (!buffer) {
711         return;
712     }
713 
714     int allAreDefined = readByte();
715     if (allAreDefined == 0) {
716         readBoolVector(numItems, v);
717         return;
718     }
719 
720     for (int i = 0; i < numItems; i++) {
721         v.append(true);
722     }
723 }
724 
readHashDigests(int numItems,QVector<bool> & digestsDefined,QVector<quint32> & digests)725 void K7Zip::K7ZipPrivate::readHashDigests(int numItems, QVector<bool> &digestsDefined, QVector<quint32> &digests)
726 {
727     if (!buffer) {
728         return;
729     }
730 
731     readBoolVector2(numItems, digestsDefined);
732     for (int i = 0; i < numItems; i++) {
733         quint32 crc = 0;
734         if (digestsDefined[i]) {
735             crc = GetUi32(buffer, pos);
736             pos += 4;
737         }
738         digests.append(crc);
739     }
740 }
741 
folderItem()742 Folder *K7Zip::K7ZipPrivate::folderItem()
743 {
744     if (!buffer) {
745         return nullptr;
746     }
747 
748     Folder *folder = new Folder;
749     int numCoders = readNumber();
750 
751     quint64 numInStreamsTotal = 0;
752     quint64 numOutStreamsTotal = 0;
753     for (int i = 0; i < numCoders; ++i) {
754         // BYTE
755         //    {
756         //      0:3 CodecIdSize
757         //      4:  Is Complex Coder
758         //      5:  There Are Attributes
759         //      6:  Reserved
760         //      7:  There are more alternative methods. (Not used
761         //      anymore, must be 0).
762         //    }
763         unsigned char coderInfo = readByte();
764         int codecIdSize = (coderInfo & 0xF);
765         if (codecIdSize > 8) {
766             qCDebug(KArchiveLog) << "unsupported codec id size";
767             delete folder;
768             return nullptr;
769         }
770         Folder::FolderInfo *info = new Folder::FolderInfo();
771         std::unique_ptr<unsigned char[]> codecID(new unsigned char[codecIdSize]);
772         for (int i = 0; i < codecIdSize; ++i) {
773             codecID[i] = readByte();
774         }
775 
776         int id = 0;
777         for (int j = 0; j < codecIdSize; j++) {
778             id |= codecID[codecIdSize - 1 - j] << (8 * j);
779         }
780         info->methodID = id;
781 
782         // if (Is Complex Coder)
783         if ((coderInfo & 0x10) != 0) {
784             info->numInStreams = readNumber();
785             info->numOutStreams = readNumber();
786         } else {
787             info->numInStreams = 1;
788             info->numOutStreams = 1;
789         }
790 
791         // if (There Are Attributes)
792         if ((coderInfo & 0x20) != 0) {
793             int propertiesSize = readNumber();
794             for (int i = 0; i < propertiesSize; ++i) {
795                 info->properties.append(readByte());
796             }
797         }
798 
799         if ((coderInfo & 0x80) != 0) {
800             qCDebug(KArchiveLog) << "unsupported";
801             delete info;
802             delete folder;
803             return nullptr;
804         }
805 
806         numInStreamsTotal += info->numInStreams;
807         numOutStreamsTotal += info->numOutStreams;
808         folder->folderInfos.append(info);
809     }
810 
811     int numBindPairs = numOutStreamsTotal - 1;
812     for (int i = 0; i < numBindPairs; i++) {
813         folder->inIndexes.append(readNumber());
814         folder->outIndexes.append(readNumber());
815     }
816 
817     int numPackedStreams = numInStreamsTotal - numBindPairs;
818     if (numPackedStreams > 1) {
819         for (int i = 0; i < numPackedStreams; ++i) {
820             folder->packedStreams.append(readNumber());
821         }
822     } else {
823         if (numPackedStreams == 1) {
824             for (quint64 i = 0; i < numInStreamsTotal; i++) {
825                 if (folder->findBindPairForInStream(i) < 0) {
826                     folder->packedStreams.append(i);
827                     break;
828                 }
829             }
830             if (folder->packedStreams.size() != 1) {
831                 delete folder;
832                 return nullptr;
833             }
834         }
835     }
836     return folder;
837 }
838 
readUInt64DefVector(int numFiles,QVector<quint64> & values,QVector<bool> & defined)839 bool K7Zip::K7ZipPrivate::readUInt64DefVector(int numFiles, QVector<quint64> &values, QVector<bool> &defined)
840 {
841     if (!buffer) {
842         return false;
843     }
844 
845     readBoolVector2(numFiles, defined);
846 
847     int external = readByte();
848     if (external != 0) {
849         int dataIndex = readNumber();
850         if (dataIndex < 0 /*|| dataIndex >= dataVector->Size()*/) {
851             qCDebug(KArchiveLog) << "wrong data index";
852             return false;
853         }
854 
855         // TODO : go to the new index
856     }
857 
858     for (int i = 0; i < numFiles; i++) {
859         quint64 t = 0;
860         if (defined[i]) {
861             t = readUInt64();
862         }
863         values.append(t);
864     }
865     return true;
866 }
867 
readPackInfo()868 bool K7Zip::K7ZipPrivate::readPackInfo()
869 {
870     if (!buffer) {
871         return false;
872     }
873 
874     packPos = readNumber();
875     numPackStreams = readNumber();
876     packSizes.clear();
877 
878     packCRCsDefined.clear();
879     packCRCs.clear();
880 
881     if (!findAttribute(kSize)) {
882         qCDebug(KArchiveLog) << "kSize not found";
883         return false;
884     }
885 
886     for (quint64 i = 0; i < numPackStreams; ++i) {
887         packSizes.append(readNumber());
888     }
889 
890     for (;;) {
891         int type = readByte();
892         if (type == kEnd) {
893             break;
894         }
895         if (type == kCRC) {
896             readHashDigests(numPackStreams, packCRCsDefined, packCRCs);
897             continue;
898         }
899         skipData(readNumber());
900     }
901 
902     if (packCRCs.isEmpty()) {
903         for (quint64 i = 0; i < numPackStreams; ++i) {
904             packCRCsDefined.append(false);
905             packCRCs.append(0);
906         }
907     }
908     return true;
909 }
910 
readUnpackInfo()911 bool K7Zip::K7ZipPrivate::readUnpackInfo()
912 {
913     if (!buffer) {
914         return false;
915     }
916 
917     if (!findAttribute(kFolder)) {
918         qCDebug(KArchiveLog) << "kFolder not found";
919         return false;
920     }
921 
922     int numFolders = readNumber();
923     qDeleteAll(folders);
924     folders.clear();
925     int external = readByte();
926     switch (external) {
927     case 0: {
928         for (int i = 0; i < numFolders; ++i) {
929             folders.append(folderItem());
930         }
931         break;
932     }
933     case 1: {
934         int dataIndex = readNumber();
935         if (dataIndex < 0 /*|| dataIndex >= dataVector->Size()*/) {
936             qCDebug(KArchiveLog) << "wrong data index";
937         }
938         // TODO : go to the new index
939         break;
940     }
941     default:
942         qCDebug(KArchiveLog) << "external error";
943         return false;
944     }
945 
946     if (!findAttribute(kCodersUnpackSize)) {
947         qCDebug(KArchiveLog) << "kCodersUnpackSize not found";
948         return false;
949     }
950 
951     for (int i = 0; i < numFolders; ++i) {
952         Folder *folder = folders.at(i);
953         int numOutStreams = folder->getNumOutStreams();
954         for (int j = 0; j < numOutStreams; ++j) {
955             folder->unpackSizes.append(readNumber());
956         }
957     }
958 
959     for (;;) {
960         int type = readByte();
961         if (type == kEnd) {
962             break;
963         }
964         if (type == kCRC) {
965             QVector<bool> crcsDefined;
966             QVector<quint32> crcs;
967             readHashDigests(numFolders, crcsDefined, crcs);
968             for (int i = 0; i < numFolders; i++) {
969                 Folder *folder = folders.at(i);
970                 folder->unpackCRCDefined = crcsDefined[i];
971                 folder->unpackCRC = crcs[i];
972             }
973             continue;
974         }
975         skipData(readNumber());
976     }
977     return true;
978 }
979 
readSubStreamsInfo()980 bool K7Zip::K7ZipPrivate::readSubStreamsInfo()
981 {
982     if (!buffer) {
983         return false;
984     }
985 
986     numUnpackStreamsInFolders.clear();
987 
988     int type;
989     for (;;) {
990         type = readByte();
991         if (type == kNumUnpackStream) {
992             for (int i = 0; i < folders.size(); i++) {
993                 numUnpackStreamsInFolders.append(readNumber());
994             }
995             continue;
996         }
997         if (type == kCRC || type == kSize) {
998             break;
999         }
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 
toTimeT(const long long liTime)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 
rtlSecondsSince1970ToSpecTime(quint32 seconds)1137 long long rtlSecondsSince1970ToSpecTime(quint32 seconds)
1138 {
1139     long long secs = seconds * (long long)TICKSPERSEC + TICKS_1601_TO_1970;
1140     return secs;
1141 }
1142 
readMainStreamsInfo()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 
getInStream(const Folder * folder,quint32 streamIndex,int & seqInStream,quint32 & coderIndex)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 
getOutStream(const Folder * folder,quint32 streamIndex,int & seqOutStream)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 
RangeDecoder()1275     RangeDecoder()
1276         : pos(0)
1277     {
1278     }
1279 
readByte()1280     unsigned char readByte()
1281     {
1282         return stream[pos++];
1283     }
1284 
normalize()1285     void normalize()
1286     {
1287         while (range < kTopValue) {
1288             code = (code << 8) | readByte();
1289             range <<= 8;
1290         }
1291     }
1292 
setStream(const QByteArray & s)1293     void setStream(const QByteArray &s)
1294     {
1295         stream = s;
1296     }
1297 
init()1298     void init()
1299     {
1300         code = 0;
1301         range = 0xFFFFFFFF;
1302         for (int i = 0; i < 5; i++) {
1303             code = (code << 8) | readByte();
1304         }
1305     }
1306 
getThreshold(quint32 total)1307     quint32 getThreshold(quint32 total)
1308     {
1309         return (code) / (range /= total);
1310     }
1311 
decode(quint32 start,quint32 size)1312     void decode(quint32 start, quint32 size)
1313     {
1314         code -= start * range;
1315         range *= size;
1316         normalize();
1317     }
1318 
decodeDirectBits(int numTotalBits)1319     quint32 decodeDirectBits(int numTotalBits)
1320     {
1321         quint32 r = range;
1322         quint32 c = code;
1323         quint32 result = 0;
1324         for (int i = numTotalBits; i != 0; i--) {
1325             r >>= 1;
1326             quint32 t = (c - r) >> 31;
1327             c -= r & (t - 1);
1328             result = (result << 1) | (1 - t);
1329 
1330             if (r < kTopValue) {
1331                 c = (c << 8) | readByte();
1332                 r <<= 8;
1333             }
1334         }
1335         range = r;
1336         code = c;
1337         return result;
1338     }
1339 
DecodeBit(quint32 size0,quint32 numTotalBits)1340     quint32 DecodeBit(quint32 size0, quint32 numTotalBits)
1341     {
1342         quint32 newBound = (range >> numTotalBits) * size0;
1343         quint32 symbol;
1344         if (code < newBound) {
1345             symbol = 0;
1346             range = newBound;
1347         } else {
1348             symbol = 1;
1349             code -= newBound;
1350             range -= newBound;
1351         }
1352         normalize();
1353         return symbol;
1354     }
1355 };
1356 
1357 const int kNumBitModelTotalBits = 11;
1358 const quint32 kBitModelTotal = (1 << kNumBitModelTotalBits);
1359 
1360 template<int numMoveBits>
1361 class CBitModel
1362 {
1363 public:
1364     quint32 prob;
updateModel(quint32 symbol)1365     void updateModel(quint32 symbol)
1366     {
1367         if (symbol == 0) {
1368             prob += (kBitModelTotal - prob) >> numMoveBits;
1369         } else {
1370             prob -= (prob) >> numMoveBits;
1371         }
1372     }
1373 
init()1374     void init()
1375     {
1376         prob = kBitModelTotal / 2;
1377     }
1378 };
1379 
1380 template<int numMoveBits>
1381 class CBitDecoder : public CBitModel<numMoveBits>
1382 {
1383 public:
decode(RangeDecoder * decoder)1384     quint32 decode(RangeDecoder *decoder)
1385     {
1386         quint32 newBound = (decoder->range >> kNumBitModelTotalBits) * this->prob;
1387         if (decoder->code < newBound) {
1388             decoder->range = newBound;
1389             this->prob += (kBitModelTotal - this->prob) >> numMoveBits;
1390             if (decoder->range < kTopValue) {
1391                 decoder->code = (decoder->code << 8) | decoder->readByte();
1392                 decoder->range <<= 8;
1393             }
1394             return 0;
1395         } else {
1396             decoder->range -= newBound;
1397             decoder->code -= newBound;
1398             this->prob -= (this->prob) >> numMoveBits;
1399             if (decoder->range < kTopValue) {
1400                 decoder->code = (decoder->code << 8) | decoder->readByte();
1401                 decoder->range <<= 8;
1402             }
1403             return 1;
1404         }
1405     }
1406 };
1407 
isJcc(unsigned char b0,unsigned char b1)1408 inline bool isJcc(unsigned char b0, unsigned char b1)
1409 {
1410     return (b0 == 0x0F && (b1 & 0xF0) == 0x80);
1411 }
isJ(unsigned char b0,unsigned char b1)1412 inline bool isJ(unsigned char b0, unsigned char b1)
1413 {
1414     return ((b1 & 0xFE) == 0xE8 || isJcc(b0, b1));
1415 }
getIndex(unsigned char b0,unsigned char b1)1416 inline unsigned getIndex(unsigned char b0, unsigned char b1)
1417 {
1418     return ((b1 == 0xE8) ? b0 : ((b1 == 0xE9) ? 256 : 257));
1419 }
1420 
1421 const int kNumMoveBits = 5;
1422 
decodeBCJ2(const QByteArray & mainStream,const QByteArray & callStream,const QByteArray & jumpStream,const QByteArray & rangeBuffer)1423 static QByteArray decodeBCJ2(const QByteArray &mainStream, const QByteArray &callStream, const QByteArray &jumpStream, const QByteArray &rangeBuffer)
1424 {
1425     unsigned char prevByte = 0;
1426     QByteArray outStream;
1427     int mainStreamPos = 0;
1428     int callStreamPos = 0;
1429     int jumpStreamPos = 0;
1430 
1431     RangeDecoder rangeDecoder;
1432     rangeDecoder.setStream(rangeBuffer);
1433     rangeDecoder.init();
1434 
1435     QVector<CBitDecoder<kNumMoveBits>> statusDecoder(256 + 2);
1436 
1437     for (int i = 0; i < 256 + 2; i++) {
1438         statusDecoder[i].init();
1439     }
1440 
1441     for (;;) {
1442         quint32 i;
1443         unsigned char b = 0;
1444         const quint32 kBurstSize = (1 << 18);
1445         for (i = 0; i < kBurstSize; i++) {
1446             if (mainStreamPos == mainStream.size()) {
1447                 return outStream;
1448             }
1449 
1450             b = mainStream[mainStreamPos++];
1451             outStream.append(b);
1452 
1453             if (isJ(prevByte, b)) {
1454                 break;
1455             }
1456             prevByte = b;
1457         }
1458 
1459         if (i == kBurstSize) {
1460             continue;
1461         }
1462 
1463         unsigned index = getIndex(prevByte, b);
1464         if (statusDecoder[index].decode(&rangeDecoder) == 1) {
1465             if (b == 0xE8) {
1466                 if (callStreamPos + 4 > callStream.size()) {
1467                     return QByteArray();
1468                 }
1469             } else {
1470                 if (jumpStreamPos + 4 > jumpStream.size()) {
1471                     return QByteArray();
1472                 }
1473             }
1474             quint32 src = 0;
1475             for (int i = 0; i < 4; i++) {
1476                 unsigned char b0;
1477                 if (b == 0xE8) {
1478                     b0 = callStream[callStreamPos++];
1479                 } else {
1480                     b0 = jumpStream[jumpStreamPos++];
1481                 }
1482                 src <<= 8;
1483                 src |= ((quint32)b0);
1484             }
1485 
1486             quint32 dest = src - (quint32(outStream.size()) + 4);
1487             outStream.append((unsigned char)(dest));
1488             outStream.append((unsigned char)(dest >> 8));
1489             outStream.append((unsigned char)(dest >> 16));
1490             outStream.append((unsigned char)(dest >> 24));
1491             prevByte = (unsigned char)(dest >> 24);
1492         } else {
1493             prevByte = b;
1494         }
1495     }
1496 }
1497 
readAndDecodePackedStreams(bool readMainStreamInfo)1498 QByteArray K7Zip::K7ZipPrivate::readAndDecodePackedStreams(bool readMainStreamInfo)
1499 {
1500     if (!buffer) {
1501         return QByteArray();
1502     }
1503 
1504     if (readMainStreamInfo) {
1505         readMainStreamsInfo();
1506     }
1507 
1508     QByteArray inflatedData;
1509 
1510     quint64 startPos = 32 + packPos;
1511     for (int i = 0; i < folders.size(); i++) {
1512         const Folder *folder = folders.at(i);
1513         quint64 unpackSize64 = folder->getUnpackSize();
1514         size_t unpackSize = (size_t)unpackSize64;
1515         if (unpackSize != unpackSize64) {
1516             qCDebug(KArchiveLog) << "unsupported";
1517             return inflatedData;
1518         }
1519 
1520         // Find main coder
1521         quint32 mainCoderIndex = 0;
1522         QVector<int> outStreamIndexed;
1523         int outStreamIndex = 0;
1524         for (int j = 0; j < folder->folderInfos.size(); j++) {
1525             const Folder::FolderInfo *info = folder->folderInfos[j];
1526             for (int k = 0; k < info->numOutStreams; k++, outStreamIndex++) {
1527                 if (folder->findBindPairForOutStream(outStreamIndex) < 0) {
1528                     outStreamIndexed.append(outStreamIndex);
1529                     break;
1530                 }
1531             }
1532         }
1533 
1534         quint32 temp = 0;
1535         if (!outStreamIndexed.isEmpty()) {
1536             folder->findOutStream(outStreamIndexed[0], mainCoderIndex, temp);
1537         }
1538 
1539         quint32 startInIndex = folder->getCoderInStreamIndex(mainCoderIndex);
1540         quint32 startOutIndex = folder->getCoderOutStreamIndex(mainCoderIndex);
1541 
1542         Folder::FolderInfo *mainCoder = folder->folderInfos[mainCoderIndex];
1543 
1544         QVector<int> seqInStreams;
1545         QVector<quint32> coderIndexes;
1546         seqInStreams.reserve(mainCoder->numInStreams);
1547         coderIndexes.reserve(mainCoder->numInStreams);
1548         for (int j = 0; j < (int)mainCoder->numInStreams; j++) {
1549             int seqInStream;
1550             quint32 coderIndex;
1551             getInStream(folder, startInIndex + j, seqInStream, coderIndex);
1552             seqInStreams.append(seqInStream);
1553             coderIndexes.append(coderIndex);
1554         }
1555 
1556         QVector<int> seqOutStreams;
1557         seqOutStreams.reserve(mainCoder->numOutStreams);
1558         for (int j = 0; j < (int)mainCoder->numOutStreams; j++) {
1559             int seqOutStream;
1560             getOutStream(folder, startOutIndex + j, seqOutStream);
1561             seqOutStreams.append(seqOutStream);
1562         }
1563 
1564         QVector<QByteArray> datas;
1565         for (int j = 0; j < (int)mainCoder->numInStreams; j++) {
1566             int size = packSizes[j + i];
1567             std::unique_ptr<char[]> encodedBuffer(new char[size]);
1568             QIODevice *dev = q->device();
1569             dev->seek(startPos);
1570             quint64 n = dev->read(encodedBuffer.get(), size);
1571             if (n != (quint64)size) {
1572                 qCDebug(KArchiveLog) << "Failed read next size, should read " << size << ", read " << n;
1573                 return inflatedData;
1574             }
1575             QByteArray deflatedData(encodedBuffer.get(), size);
1576             datas.append(deflatedData);
1577             startPos += size;
1578             pos += size;
1579             headerSize += size;
1580         }
1581 
1582         QVector<QByteArray> inflatedDatas;
1583         QByteArray deflatedData;
1584         for (int j = 0; j < seqInStreams.size(); ++j) {
1585             Folder::FolderInfo *coder = nullptr;
1586             if ((quint32)j != mainCoderIndex) {
1587                 coder = folder->folderInfos[coderIndexes[j]];
1588             } else {
1589                 coder = folder->folderInfos[mainCoderIndex];
1590             }
1591 
1592             deflatedData = datas[seqInStreams[j]];
1593 
1594             KFilterBase *filter = nullptr;
1595 
1596             switch (coder->methodID) {
1597             case k_LZMA:
1598                 filter = KCompressionDevice::filterForCompressionType(KCompressionDevice::Xz);
1599                 if (!filter) {
1600                     qCDebug(KArchiveLog) << "filter not found";
1601                     return QByteArray();
1602                 }
1603                 static_cast<KXzFilter *>(filter)->init(QIODevice::ReadOnly, KXzFilter::LZMA, coder->properties);
1604                 break;
1605             case k_LZMA2:
1606                 filter = KCompressionDevice::filterForCompressionType(KCompressionDevice::Xz);
1607                 if (!filter) {
1608                     qCDebug(KArchiveLog) << "filter not found";
1609                     return QByteArray();
1610                 }
1611                 static_cast<KXzFilter *>(filter)->init(QIODevice::ReadOnly, KXzFilter::LZMA2, coder->properties);
1612                 break;
1613             case k_PPMD: {
1614                 /*if (coder->properties.size() == 5) {
1615                     //Byte order = *(const Byte *)coder.Props;
1616                     qint32 dicSize = ((unsigned char)coder->properties[1]        |
1617                                      (((unsigned char)coder->properties[2]) <<  8) |
1618                                      (((unsigned char)coder->properties[3]) << 16) |
1619                                      (((unsigned char)coder->properties[4]) << 24));
1620                 }*/
1621                 break;
1622             }
1623             case k_AES:
1624                 if (coder->properties.size() >= 1) {
1625                     // const Byte *data = (const Byte *)coder.Props;
1626                     // Byte firstByte = *data++;
1627                     // UInt32 numCyclesPower = firstByte & 0x3F;
1628                 }
1629                 break;
1630             case k_BCJ:
1631                 filter = KCompressionDevice::filterForCompressionType(KCompressionDevice::Xz);
1632                 if (!filter) {
1633                     qCDebug(KArchiveLog) << "filter not found";
1634                     return QByteArray();
1635                 }
1636                 static_cast<KXzFilter *>(filter)->init(QIODevice::ReadOnly, KXzFilter::BCJ, coder->properties);
1637                 break;
1638             case k_BCJ2: {
1639                 QByteArray bcj2 = decodeBCJ2(inflatedDatas[0], inflatedDatas[1], inflatedDatas[2], deflatedData);
1640                 inflatedDatas.clear();
1641                 inflatedDatas.append(bcj2);
1642                 break;
1643             }
1644             case k_BZip2:
1645                 filter = KCompressionDevice::filterForCompressionType(KCompressionDevice::BZip2);
1646                 if (!filter) {
1647                     qCDebug(KArchiveLog) << "filter not found";
1648                     return QByteArray();
1649                 }
1650                 filter->init(QIODevice::ReadOnly);
1651                 break;
1652             }
1653 
1654             if (coder->methodID == k_BCJ2) {
1655                 continue;
1656             }
1657 
1658             if (!filter) {
1659                 return QByteArray();
1660             }
1661 
1662             filter->setInBuffer(deflatedData.data(), deflatedData.size());
1663 
1664             QByteArray outBuffer;
1665             // reserve memory
1666             outBuffer.resize(unpackSize);
1667 
1668             KFilterBase::Result result = KFilterBase::Ok;
1669             QByteArray inflatedDataTmp;
1670             while (result != KFilterBase::End && result != KFilterBase::Error && !filter->inBufferEmpty()) {
1671                 filter->setOutBuffer(outBuffer.data(), outBuffer.size());
1672                 result = filter->uncompress();
1673                 if (result == KFilterBase::Error) {
1674                     qCDebug(KArchiveLog) << " decode error";
1675                     filter->terminate();
1676                     delete filter;
1677                     return QByteArray();
1678                 }
1679                 int uncompressedBytes = outBuffer.size() - filter->outBufferAvailable();
1680 
1681                 // append the uncompressed data to inflate buffer
1682                 inflatedDataTmp.append(outBuffer.data(), uncompressedBytes);
1683 
1684                 if (result == KFilterBase::End) {
1685                     // qCDebug(KArchiveLog) << "Finished unpacking";
1686                     break; // Finished.
1687                 }
1688             }
1689 
1690             if (result != KFilterBase::End && !filter->inBufferEmpty()) {
1691                 qCDebug(KArchiveLog) << "decode failed result" << result;
1692                 filter->terminate();
1693                 delete filter;
1694                 return QByteArray();
1695             }
1696 
1697             filter->terminate();
1698             delete filter;
1699 
1700             inflatedDatas.append(inflatedDataTmp);
1701         }
1702 
1703         QByteArray inflated;
1704         for (const QByteArray &data : std::as_const(inflatedDatas)) {
1705             inflated.append(data);
1706         }
1707 
1708         inflatedDatas.clear();
1709 
1710         if (folder->unpackCRCDefined) {
1711             if ((size_t)inflated.size() < unpackSize) {
1712                 qCDebug(KArchiveLog) << "wrong crc size data";
1713                 return QByteArray();
1714             }
1715             quint32 crc = crc32(0, (Bytef *)(inflated.data()), unpackSize);
1716             if (crc != folder->unpackCRC) {
1717                 qCDebug(KArchiveLog) << "wrong crc";
1718                 return QByteArray();
1719             }
1720         }
1721 
1722         inflatedData.append(inflated);
1723     }
1724 
1725     return inflatedData;
1726 }
1727 
1728 ///////////////// Write ////////////////////
1729 
createItemsFromEntities(const KArchiveDirectory * dir,const QString & path,QByteArray & data)1730 void K7Zip::K7ZipPrivate::createItemsFromEntities(const KArchiveDirectory *dir, const QString &path, QByteArray &data)
1731 {
1732     const QStringList l = dir->entries();
1733     QStringList::ConstIterator it = l.begin();
1734     for (; it != l.end(); ++it) {
1735         const KArchiveEntry *entry = dir->entry((*it));
1736 
1737         FileInfo *fileInfo = new FileInfo;
1738         fileInfo->attribDefined = true;
1739 
1740         fileInfo->path = path + entry->name();
1741         mTimesDefined.append(true);
1742         mTimes.append(rtlSecondsSince1970ToSpecTime(entry->date().toSecsSinceEpoch()));
1743 
1744         if (entry->isFile()) {
1745             const K7ZipFileEntry *fileEntry = static_cast<const K7ZipFileEntry *>(entry);
1746 
1747             fileInfo->attributes = FILE_ATTRIBUTE_ARCHIVE;
1748             fileInfo->attributes |= FILE_ATTRIBUTE_UNIX_EXTENSION + ((entry->permissions() & 0xFFFF) << 16);
1749             fileInfo->size = fileEntry->size();
1750             QString symLink = fileEntry->symLinkTarget();
1751             if (fileInfo->size > 0) {
1752                 fileInfo->hasStream = true;
1753                 data.append(outData.mid(fileEntry->position(), fileEntry->size()));
1754                 unpackSizes.append(fileInfo->size);
1755             } else if (!symLink.isEmpty()) {
1756                 fileInfo->hasStream = true;
1757                 data.append(symLink.toUtf8());
1758                 unpackSizes.append(symLink.size());
1759             }
1760             fileInfos.append(fileInfo);
1761         } else if (entry->isDirectory()) {
1762             fileInfo->attributes = FILE_ATTRIBUTE_DIRECTORY;
1763             fileInfo->attributes |= FILE_ATTRIBUTE_UNIX_EXTENSION + ((entry->permissions() & 0xFFFF) << 16);
1764             fileInfo->isDir = true;
1765             fileInfos.append(fileInfo);
1766             createItemsFromEntities((KArchiveDirectory *)entry, path + (*it) + QLatin1Char('/'), data);
1767         }
1768     }
1769 }
1770 
writeByte(unsigned char b)1771 void K7Zip::K7ZipPrivate::writeByte(unsigned char b)
1772 {
1773     header.append(b);
1774     countSize++;
1775 }
1776 
writeNumber(quint64 value)1777 void K7Zip::K7ZipPrivate::writeNumber(quint64 value)
1778 {
1779     int firstByte = 0;
1780     short mask = 0x80;
1781     int i;
1782     for (i = 0; i < 8; i++) {
1783         if (value < ((quint64(1) << (7 * (i + 1))))) {
1784             firstByte |= (int)(value >> (8 * i));
1785             break;
1786         }
1787         firstByte |= mask;
1788         mask >>= 1;
1789     }
1790     writeByte(firstByte);
1791     for (; i > 0; i--) {
1792         writeByte((int)value);
1793         value >>= 8;
1794     }
1795 }
1796 
writeBoolVector(const QVector<bool> & boolVector)1797 void K7Zip::K7ZipPrivate::writeBoolVector(const QVector<bool> &boolVector)
1798 {
1799     int b = 0;
1800     short mask = 0x80;
1801     for (int i = 0; i < boolVector.size(); i++) {
1802         if (boolVector[i]) {
1803             b |= mask;
1804         }
1805         mask >>= 1;
1806         if (mask == 0) {
1807             writeByte(b);
1808             mask = 0x80;
1809             b = 0;
1810         }
1811     }
1812     if (mask != 0x80) {
1813         writeByte(b);
1814     }
1815 }
1816 
writeUInt32(quint32 value)1817 void K7Zip::K7ZipPrivate::writeUInt32(quint32 value)
1818 {
1819     for (int i = 0; i < 4; i++) {
1820         writeByte((unsigned char)value);
1821         value >>= 8;
1822     }
1823 }
1824 
writeUInt64(quint64 value)1825 void K7Zip::K7ZipPrivate::writeUInt64(quint64 value)
1826 {
1827     for (int i = 0; i < 8; i++) {
1828         writeByte((unsigned char)value);
1829         value >>= 8;
1830     }
1831 }
1832 
writeAlignedBoolHeader(const QVector<bool> & v,int numDefined,int type,unsigned itemSize)1833 void K7Zip::K7ZipPrivate::writeAlignedBoolHeader(const QVector<bool> &v, int numDefined, int type, unsigned itemSize)
1834 {
1835     const unsigned bvSize = (numDefined == v.size()) ? 0 : ((unsigned)v.size() + 7) / 8;
1836     const quint64 dataSize = (quint64)numDefined * itemSize + bvSize + 2;
1837     // SkipAlign(3 + (unsigned)bvSize + (unsigned)GetBigNumberSize(dataSize), itemSize);
1838 
1839     writeByte(type);
1840     writeNumber(dataSize);
1841     if (numDefined == v.size()) {
1842         writeByte(1);
1843     } else {
1844         writeByte(0);
1845         writeBoolVector(v);
1846     }
1847     writeByte(0);
1848 }
1849 
writeUInt64DefVector(const QVector<quint64> & v,const QVector<bool> & defined,int type)1850 void K7Zip::K7ZipPrivate::writeUInt64DefVector(const QVector<quint64> &v, const QVector<bool> &defined, int type)
1851 {
1852     int numDefined = 0;
1853 
1854     for (int i = 0; i < defined.size(); i++) {
1855         if (defined[i]) {
1856             numDefined++;
1857         }
1858     }
1859 
1860     if (numDefined == 0) {
1861         return;
1862     }
1863 
1864     writeAlignedBoolHeader(defined, numDefined, type, 8);
1865 
1866     for (int i = 0; i < defined.size(); i++) {
1867         if (defined[i]) {
1868             writeUInt64(v[i]);
1869         }
1870     }
1871 }
1872 
writeHashDigests(const QVector<bool> & digestsDefined,const QVector<quint32> & digests)1873 void K7Zip::K7ZipPrivate::writeHashDigests(const QVector<bool> &digestsDefined, const QVector<quint32> &digests)
1874 {
1875     int numDefined = 0;
1876     int i;
1877     for (i = 0; i < digestsDefined.size(); i++) {
1878         if (digestsDefined[i]) {
1879             numDefined++;
1880         }
1881     }
1882 
1883     if (numDefined == 0) {
1884         return;
1885     }
1886 
1887     writeByte(kCRC);
1888     if (numDefined == digestsDefined.size()) {
1889         writeByte(1);
1890     } else {
1891         writeByte(0);
1892         writeBoolVector(digestsDefined);
1893     }
1894 
1895     for (i = 0; i < digests.size(); i++) {
1896         if (digestsDefined[i]) {
1897             writeUInt32(digests[i]);
1898         }
1899     }
1900 }
1901 
writePackInfo(quint64 dataOffset,QVector<quint64> & packedSizes,QVector<bool> & packedCRCsDefined,QVector<quint32> & packedCRCs)1902 void K7Zip::K7ZipPrivate::writePackInfo(quint64 dataOffset, QVector<quint64> &packedSizes, QVector<bool> &packedCRCsDefined, QVector<quint32> &packedCRCs)
1903 {
1904     if (packedSizes.isEmpty()) {
1905         return;
1906     }
1907     writeByte(kPackInfo);
1908     writeNumber(dataOffset);
1909     writeNumber(packedSizes.size());
1910     writeByte(kSize);
1911 
1912     for (int i = 0; i < packedSizes.size(); i++) {
1913         writeNumber(packedSizes[i]);
1914     }
1915 
1916     writeHashDigests(packedCRCsDefined, packedCRCs);
1917 
1918     writeByte(kEnd);
1919 }
1920 
writeFolder(const Folder * folder)1921 void K7Zip::K7ZipPrivate::writeFolder(const Folder *folder)
1922 {
1923     writeNumber(folder->folderInfos.size());
1924     for (int i = 0; i < folder->folderInfos.size(); i++) {
1925         const Folder::FolderInfo *info = folder->folderInfos.at(i);
1926         {
1927             size_t propsSize = info->properties.size();
1928 
1929             quint64 id = info->methodID;
1930             size_t idSize;
1931             for (idSize = 1; idSize < sizeof(id); idSize++) {
1932                 if ((id >> (8 * idSize)) == 0) {
1933                     break;
1934                 }
1935             }
1936 
1937             int longID[15];
1938             for (int t = idSize - 1; t >= 0; t--, id >>= 8) {
1939                 longID[t] = (int)(id & 0xFF);
1940             }
1941 
1942             int b;
1943             b = (int)(idSize & 0xF);
1944             bool isComplex = !info->isSimpleCoder();
1945             b |= (isComplex ? 0x10 : 0);
1946             b |= ((propsSize != 0) ? 0x20 : 0);
1947 
1948             writeByte(b);
1949             for (size_t j = 0; j < idSize; ++j) {
1950                 writeByte(longID[j]);
1951             }
1952 
1953             if (isComplex) {
1954                 writeNumber(info->numInStreams);
1955                 writeNumber(info->numOutStreams);
1956             }
1957 
1958             if (propsSize == 0) {
1959                 continue;
1960             }
1961 
1962             writeNumber(propsSize);
1963             for (size_t j = 0; j < propsSize; ++j) {
1964                 writeByte(info->properties[j]);
1965             }
1966         }
1967     }
1968 
1969     for (int i = 0; i < folder->inIndexes.size(); i++) {
1970         writeNumber(folder->inIndexes[i]);
1971         writeNumber(folder->outIndexes[i]);
1972     }
1973 
1974     if (folder->packedStreams.size() > 1) {
1975         for (int i = 0; i < folder->packedStreams.size(); i++) {
1976             writeNumber(folder->packedStreams[i]);
1977         }
1978     }
1979 }
1980 
writeUnpackInfo(const QVector<Folder * > & folderItems)1981 void K7Zip::K7ZipPrivate::writeUnpackInfo(const QVector<Folder *> &folderItems)
1982 {
1983     if (folderItems.isEmpty()) {
1984         return;
1985     }
1986 
1987     writeByte(kUnpackInfo);
1988 
1989     writeByte(kFolder);
1990     writeNumber(folderItems.size());
1991     {
1992         writeByte(0);
1993         for (int i = 0; i < folderItems.size(); i++) {
1994             writeFolder(folderItems[i]);
1995         }
1996     }
1997 
1998     writeByte(kCodersUnpackSize);
1999     int i;
2000     for (i = 0; i < folderItems.size(); i++) {
2001         const Folder *folder = folderItems[i];
2002         for (int j = 0; j < folder->unpackSizes.size(); j++) {
2003             writeNumber(folder->unpackSizes.at(j));
2004         }
2005     }
2006 
2007     QVector<bool> unpackCRCsDefined;
2008     QVector<quint32> unpackCRCs;
2009     unpackCRCsDefined.reserve(folderItems.size());
2010     unpackCRCs.reserve(folderItems.size());
2011     for (i = 0; i < folderItems.size(); i++) {
2012         const Folder *folder = folderItems[i];
2013         unpackCRCsDefined.append(folder->unpackCRCDefined);
2014         unpackCRCs.append(folder->unpackCRC);
2015     }
2016     writeHashDigests(unpackCRCsDefined, unpackCRCs);
2017 
2018     writeByte(kEnd);
2019 }
2020 
writeSubStreamsInfo(const QVector<quint64> & unpackSizes,const QVector<bool> & digestsDefined,const QVector<quint32> & digests)2021 void K7Zip::K7ZipPrivate::writeSubStreamsInfo(const QVector<quint64> &unpackSizes, const QVector<bool> &digestsDefined, const QVector<quint32> &digests)
2022 {
2023     writeByte(kSubStreamsInfo);
2024 
2025     for (int i = 0; i < numUnpackStreamsInFolders.size(); i++) {
2026         if (numUnpackStreamsInFolders.at(i) != 1) {
2027             writeByte(kNumUnpackStream);
2028             for (int j = 0; j < numUnpackStreamsInFolders.size(); j++) {
2029                 writeNumber(numUnpackStreamsInFolders.at(j));
2030             }
2031             break;
2032         }
2033     }
2034 
2035     bool needFlag = true;
2036     int index = 0;
2037     for (int i = 0; i < numUnpackStreamsInFolders.size(); i++) {
2038         for (quint32 j = 0; j < numUnpackStreamsInFolders.at(i); j++) {
2039             if (j + 1 != numUnpackStreamsInFolders.at(i)) {
2040                 if (needFlag) {
2041                     writeByte(kSize);
2042                 }
2043                 needFlag = false;
2044                 writeNumber(unpackSizes[index]);
2045             }
2046             index++;
2047         }
2048     }
2049 
2050     QVector<bool> digestsDefined2;
2051     QVector<quint32> digests2;
2052 
2053     int digestIndex = 0;
2054     for (int i = 0; i < folders.size(); i++) {
2055         int numSubStreams = (int)numUnpackStreamsInFolders.at(i);
2056         if (numSubStreams == 1 && folders.at(i)->unpackCRCDefined) {
2057             digestIndex++;
2058         } else {
2059             for (int j = 0; j < numSubStreams; j++, digestIndex++) {
2060                 digestsDefined2.append(digestsDefined[digestIndex]);
2061                 digests2.append(digests[digestIndex]);
2062             }
2063         }
2064     }
2065     writeHashDigests(digestsDefined2, digests2);
2066     writeByte(kEnd);
2067 }
2068 
encodeStream(QVector<quint64> & packSizes,QVector<Folder * > & folds)2069 QByteArray K7Zip::K7ZipPrivate::encodeStream(QVector<quint64> &packSizes, QVector<Folder *> &folds)
2070 {
2071     Folder *folder = new Folder;
2072     folder->unpackCRCDefined = true;
2073     folder->unpackCRC = crc32(0, (Bytef *)(header.data()), header.size());
2074     folder->unpackSizes.append(header.size());
2075 
2076     Folder::FolderInfo *info = new Folder::FolderInfo();
2077     info->numInStreams = 1;
2078     info->numOutStreams = 1;
2079     info->methodID = k_LZMA2;
2080 
2081     quint32 dictSize = header.size();
2082     const quint32 kMinReduceSize = (1 << 16);
2083     if (dictSize < kMinReduceSize) {
2084         dictSize = kMinReduceSize;
2085     }
2086 
2087     int dict;
2088     for (dict = 0; dict < 40; dict++) {
2089         if (dictSize <= lzma2_dic_size_from_prop(dict)) {
2090             break;
2091         }
2092     }
2093 
2094     info->properties.append(dict);
2095     folder->folderInfos.append(info);
2096 
2097     folds.append(folder);
2098 
2099     // compress data
2100     QByteArray encodedData;
2101     if (!header.isEmpty()) {
2102         QByteArray enc;
2103         QBuffer inBuffer(&enc);
2104 
2105         KCompressionDevice flt(&inBuffer, false, KCompressionDevice::Xz);
2106         flt.open(QIODevice::WriteOnly);
2107 
2108         KFilterBase *filter = flt.filterBase();
2109 
2110         static_cast<KXzFilter *>(filter)->init(QIODevice::WriteOnly, KXzFilter::LZMA2, info->properties);
2111 
2112         const int ret = flt.write(header);
2113         if (ret != header.size()) {
2114             qCDebug(KArchiveLog) << "write error write " << ret << "expected" << header.size();
2115             return encodedData;
2116         }
2117 
2118         flt.close();
2119         encodedData = inBuffer.data();
2120     }
2121 
2122     packSizes.append(encodedData.size());
2123     return encodedData;
2124 }
2125 
writeHeader(quint64 & headerOffset)2126 void K7Zip::K7ZipPrivate::writeHeader(quint64 &headerOffset)
2127 {
2128     quint64 packedSize = 0;
2129     for (int i = 0; i < packSizes.size(); ++i) {
2130         packedSize += packSizes[i];
2131     }
2132 
2133     headerOffset = packedSize;
2134 
2135     writeByte(kHeader);
2136 
2137     // Archive Properties
2138 
2139     if (!folders.isEmpty()) {
2140         writeByte(kMainStreamsInfo);
2141         writePackInfo(0, packSizes, packCRCsDefined, packCRCs);
2142 
2143         writeUnpackInfo(folders);
2144 
2145         QVector<quint64> unpackFileSizes;
2146         QVector<bool> digestsDefined;
2147         QVector<quint32> digests;
2148         for (int i = 0; i < fileInfos.size(); i++) {
2149             const FileInfo *file = fileInfos.at(i);
2150             if (!file->hasStream) {
2151                 continue;
2152             }
2153             unpackFileSizes.append(file->size);
2154             digestsDefined.append(file->crcDefined);
2155             digests.append(file->crc);
2156         }
2157 
2158         writeSubStreamsInfo(unpackSizes, digestsDefined, digests);
2159         writeByte(kEnd);
2160     }
2161 
2162     if (fileInfos.isEmpty()) {
2163         writeByte(kEnd);
2164         return;
2165     }
2166 
2167     writeByte(kFilesInfo);
2168     writeNumber(fileInfos.size());
2169 
2170     {
2171         /* ---------- Empty Streams ---------- */
2172         QVector<bool> emptyStreamVector;
2173         int numEmptyStreams = 0;
2174         for (int i = 0; i < fileInfos.size(); i++) {
2175             if (fileInfos.at(i)->hasStream) {
2176                 emptyStreamVector.append(false);
2177             } else {
2178                 emptyStreamVector.append(true);
2179                 numEmptyStreams++;
2180             }
2181         }
2182 
2183         if (numEmptyStreams > 0) {
2184             writeByte(kEmptyStream);
2185             writeNumber(((unsigned)emptyStreamVector.size() + 7) / 8);
2186             writeBoolVector(emptyStreamVector);
2187 
2188             QVector<bool> emptyFileVector;
2189             QVector<bool> antiVector;
2190             int numEmptyFiles = 0;
2191             int numAntiItems = 0;
2192             for (int i = 0; i < fileInfos.size(); i++) {
2193                 const FileInfo *file = fileInfos.at(i);
2194                 if (!file->hasStream) {
2195                     emptyFileVector.append(!file->isDir);
2196                     if (!file->isDir) {
2197                         numEmptyFiles++;
2198                         bool isAnti = (i < this->isAnti.size() && this->isAnti[i]);
2199                         antiVector.append(isAnti);
2200                         if (isAnti) {
2201                             numAntiItems++;
2202                         }
2203                     }
2204                 }
2205             }
2206 
2207             if (numEmptyFiles > 0) {
2208                 writeByte(kEmptyFile);
2209                 writeNumber(((unsigned)emptyFileVector.size() + 7) / 8);
2210                 writeBoolVector(emptyFileVector);
2211             }
2212 
2213             if (numAntiItems > 0) {
2214                 writeByte(kAnti);
2215                 writeNumber(((unsigned)antiVector.size() + 7) / 8);
2216                 writeBoolVector(antiVector);
2217             }
2218         }
2219     }
2220 
2221     {
2222         /* ---------- Names ---------- */
2223 
2224         int numDefined = 0;
2225         size_t namesDataSize = 0;
2226         for (int i = 0; i < fileInfos.size(); i++) {
2227             const QString &name = fileInfos.at(i)->path;
2228             if (!name.isEmpty()) {
2229                 numDefined++;
2230                 namesDataSize += (name.length() + 1) * 2;
2231             }
2232         }
2233 
2234         if (numDefined > 0) {
2235             namesDataSize++;
2236             // SkipAlign(2 + GetBigNumberSize(namesDataSize), 2);
2237 
2238             writeByte(kName);
2239             writeNumber(namesDataSize);
2240             writeByte(0);
2241             for (int i = 0; i < fileInfos.size(); i++) {
2242                 const QString &name = fileInfos.at(i)->path;
2243                 for (int t = 0; t < name.length(); t++) {
2244                     wchar_t c = name[t].toLatin1();
2245                     writeByte((unsigned char)c);
2246                     writeByte((unsigned char)(c >> 8));
2247                 }
2248                 // End of string
2249                 writeByte(0);
2250                 writeByte(0);
2251             }
2252         }
2253     }
2254 
2255     writeUInt64DefVector(mTimes, mTimesDefined, kMTime);
2256 
2257     writeUInt64DefVector(startPositions, startPositionsDefined, kStartPos);
2258 
2259     {
2260         /* ---------- Write Attrib ---------- */
2261         QVector<bool> boolVector;
2262         int numDefined = 0;
2263         boolVector.reserve(fileInfos.size());
2264         for (int i = 0; i < fileInfos.size(); i++) {
2265             bool defined = fileInfos.at(i)->attribDefined;
2266             boolVector.append(defined);
2267             if (defined) {
2268                 numDefined++;
2269             }
2270         }
2271 
2272         if (numDefined > 0) {
2273             writeAlignedBoolHeader(boolVector, numDefined, kAttributes, 4);
2274             for (int i = 0; i < fileInfos.size(); i++) {
2275                 const FileInfo *file = fileInfos.at(i);
2276                 if (file->attribDefined) {
2277                     writeUInt32(file->attributes);
2278                 }
2279             }
2280         }
2281     }
2282 
2283     writeByte(kEnd); // for files
2284     writeByte(kEnd); // for headers*/
2285 }
2286 
setUInt32(unsigned char * p,quint32 d)2287 static void setUInt32(unsigned char *p, quint32 d)
2288 {
2289     for (int i = 0; i < 4; i++, d >>= 8) {
2290         p[i] = (unsigned)d;
2291     }
2292 }
2293 
setUInt64(unsigned char * p,quint64 d)2294 static void setUInt64(unsigned char *p, quint64 d)
2295 {
2296     for (int i = 0; i < 8; i++, d >>= 8) {
2297         p[i] = (unsigned char)d;
2298     }
2299 }
2300 
writeStartHeader(const quint64 nextHeaderSize,const quint32 nextHeaderCRC,const quint64 nextHeaderOffset)2301 void K7Zip::K7ZipPrivate::writeStartHeader(const quint64 nextHeaderSize, const quint32 nextHeaderCRC, const quint64 nextHeaderOffset)
2302 {
2303     unsigned char buf[24];
2304     setUInt64(buf + 4, nextHeaderOffset);
2305     setUInt64(buf + 12, nextHeaderSize);
2306     setUInt32(buf + 20, nextHeaderCRC);
2307     setUInt32(buf, crc32(0, (Bytef *)(buf + 4), 20));
2308     q->device()->write((char *)buf, 24);
2309 }
2310 
writeSignature()2311 void K7Zip::K7ZipPrivate::writeSignature()
2312 {
2313     unsigned char buf[8];
2314     memcpy(buf, k7zip_signature, 6);
2315     buf[6] = 0 /*kMajorVersion*/;
2316     buf[7] = 3;
2317     q->device()->write((char *)buf, 8);
2318 }
2319 
openArchive(QIODevice::OpenMode mode)2320 bool K7Zip::openArchive(QIODevice::OpenMode mode)
2321 {
2322     if (!(mode & QIODevice::ReadOnly)) {
2323         return true;
2324     }
2325 
2326     QIODevice *dev = device();
2327 
2328     if (!dev) {
2329         setErrorString(tr("Could not get underlying device"));
2330         return false;
2331     }
2332 
2333     char header[32];
2334     // check signature
2335     qint64 n = dev->read(header, 32);
2336     if (n != 32) {
2337         setErrorString(tr("Read header failed"));
2338         return false;
2339     }
2340 
2341     for (int i = 0; i < 6; ++i) {
2342         if ((unsigned char)header[i] != k7zip_signature[i]) {
2343             setErrorString(tr("Check signature failed"));
2344             return false;
2345         }
2346     }
2347 
2348     // get Archive Version
2349     int major = header[6];
2350     int minor = header[7];
2351 
2352     /*if (major > 0 || minor > 2) {
2353         qCDebug(KArchiveLog) << "wrong archive version";
2354         return false;
2355     }*/
2356 
2357     // get Start Header CRC
2358     quint32 startHeaderCRC = GetUi32(header, 8);
2359     quint64 nextHeaderOffset = GetUi64(header, 12);
2360     quint64 nextHeaderSize = GetUi64(header, 20);
2361     quint32 nextHeaderCRC = GetUi32(header, 28);
2362 
2363     quint32 crc = crc32(0, (Bytef *)(header + 0xC), 20);
2364 
2365     if (crc != startHeaderCRC) {
2366         setErrorString(tr("Bad CRC"));
2367         return false;
2368     }
2369 
2370     if (nextHeaderSize == 0) {
2371         return true;
2372     }
2373 
2374     if (nextHeaderSize > (quint64)0xFFFFFFFF) {
2375         setErrorString(tr("Next header size is too big"));
2376         return false;
2377     }
2378 
2379     if ((qint64)nextHeaderOffset < 0) {
2380         setErrorString(tr("Next header size is less than zero"));
2381         return false;
2382     }
2383 
2384     dev->seek(nextHeaderOffset + 32);
2385 
2386     QByteArray inBuffer;
2387     inBuffer.resize(nextHeaderSize);
2388 
2389     n = dev->read(inBuffer.data(), inBuffer.size());
2390     if (n != (qint64)nextHeaderSize) {
2391         setErrorString(tr("Failed read next header size; should read %1, read %2").arg(nextHeaderSize).arg(n));
2392         return false;
2393     }
2394     d->buffer = inBuffer.data();
2395     d->end = nextHeaderSize;
2396 
2397     d->headerSize = 32 + nextHeaderSize;
2398     // int physSize = 32 + nextHeaderSize + nextHeaderOffset;
2399 
2400     crc = crc32(0, (Bytef *)(d->buffer), (quint32)nextHeaderSize);
2401 
2402     if (crc != nextHeaderCRC) {
2403         setErrorString(tr("Bad next header CRC"));
2404         return false;
2405     }
2406 
2407     int type = d->readByte();
2408     QByteArray decodedData;
2409     if (type != kHeader) {
2410         if (type != kEncodedHeader) {
2411             setErrorString(tr("Error in header"));
2412             return false;
2413         }
2414 
2415         decodedData = d->readAndDecodePackedStreams();
2416 
2417         int external = d->readByte();
2418         if (external != 0) {
2419             int dataIndex = (int)d->readNumber();
2420             if (dataIndex < 0) {
2421                 // qCDebug(KArchiveLog) << "dataIndex error";
2422             }
2423             d->buffer = decodedData.constData();
2424             d->pos = 0;
2425             d->end = decodedData.size();
2426         }
2427 
2428         type = d->readByte();
2429         if (type != kHeader) {
2430             setErrorString(tr("Wrong header type"));
2431             return false;
2432         }
2433     }
2434     // read header
2435 
2436     type = d->readByte();
2437 
2438     if (type == kArchiveProperties) {
2439         // TODO : implement this part
2440         setErrorString(tr("Not implemented"));
2441         return false;
2442     }
2443 
2444     if (type == kAdditionalStreamsInfo) {
2445         // TODO : implement this part
2446         setErrorString(tr("Not implemented"));
2447         return false;
2448     }
2449 
2450     if (type == kMainStreamsInfo) {
2451         if (!d->readMainStreamsInfo()) {
2452             setErrorString(tr("Error while reading main streams information"));
2453             return false;
2454         }
2455         type = d->readByte();
2456     } else {
2457         for (int i = 0; i < d->folders.size(); ++i) {
2458             Folder *folder = d->folders.at(i);
2459             d->unpackSizes.append(folder->getUnpackSize());
2460             d->digestsDefined.append(folder->unpackCRCDefined);
2461             d->digests.append(folder->unpackCRC);
2462         }
2463     }
2464 
2465     if (type == kEnd) {
2466         return true;
2467     }
2468 
2469     if (type != kFilesInfo) {
2470         setErrorString(tr("Error while reading header"));
2471         return false;
2472     }
2473 
2474     // read files info
2475     int numFiles = d->readNumber();
2476     for (int i = 0; i < numFiles; ++i) {
2477         d->fileInfos.append(new FileInfo);
2478     }
2479 
2480     QVector<bool> emptyStreamVector;
2481     QVector<bool> emptyFileVector;
2482     QVector<bool> antiFileVector;
2483     int numEmptyStreams = 0;
2484 
2485     for (;;) {
2486         quint64 type = d->readByte();
2487         if (type == kEnd) {
2488             break;
2489         }
2490 
2491         quint64 size = d->readNumber();
2492 
2493         size_t ppp = d->pos;
2494 
2495         bool addPropIdToList = true;
2496         bool isKnownType = true;
2497 
2498         if (type > ((quint32)1 << 30)) {
2499             isKnownType = false;
2500         } else {
2501             switch (type) {
2502             case kEmptyStream: {
2503                 d->readBoolVector(numFiles, emptyStreamVector);
2504                 for (int i = 0; i < emptyStreamVector.size(); ++i) {
2505                     if (emptyStreamVector[i]) {
2506                         numEmptyStreams++;
2507                     }
2508                 }
2509 
2510                 break;
2511             }
2512             case kEmptyFile:
2513                 d->readBoolVector(numEmptyStreams, emptyFileVector);
2514                 break;
2515             case kAnti:
2516                 d->readBoolVector(numEmptyStreams, antiFileVector);
2517                 break;
2518             case kCTime:
2519                 if (!d->readUInt64DefVector(numFiles, d->cTimes, d->cTimesDefined)) {
2520                     return false;
2521                 }
2522                 break;
2523             case kATime:
2524                 if (!d->readUInt64DefVector(numFiles, d->aTimes, d->aTimesDefined)) {
2525                     return false;
2526                 }
2527                 break;
2528             case kMTime:
2529                 if (!d->readUInt64DefVector(numFiles, d->mTimes, d->mTimesDefined)) {
2530                     setErrorString(tr("Error reading modification time"));
2531                     return false;
2532                 }
2533                 break;
2534             case kName: {
2535                 int external = d->readByte();
2536                 if (external != 0) {
2537                     int dataIndex = d->readNumber();
2538                     if (dataIndex < 0 /*|| dataIndex >= dataVector->Size()*/) {
2539                         qCDebug(KArchiveLog) << "wrong data index";
2540                     }
2541 
2542                     // TODO : go to the new index
2543                 }
2544 
2545                 QString name;
2546                 for (int i = 0; i < numFiles; i++) {
2547                     name = d->readString();
2548                     d->fileInfos.at(i)->path = name;
2549                 }
2550                 break;
2551             }
2552             case kAttributes: {
2553                 QVector<bool> attributesAreDefined;
2554                 d->readBoolVector2(numFiles, attributesAreDefined);
2555                 int external = d->readByte();
2556                 if (external != 0) {
2557                     int dataIndex = d->readNumber();
2558                     if (dataIndex < 0) {
2559                         qCDebug(KArchiveLog) << "wrong data index";
2560                     }
2561 
2562                     // TODO : go to the new index
2563                 }
2564 
2565                 for (int i = 0; i < numFiles; i++) {
2566                     FileInfo *fileInfo = d->fileInfos.at(i);
2567                     fileInfo->attribDefined = attributesAreDefined[i];
2568                     if (fileInfo->attribDefined) {
2569                         fileInfo->attributes = d->readUInt32();
2570                     }
2571                 }
2572                 break;
2573             }
2574             case kStartPos:
2575                 if (!d->readUInt64DefVector(numFiles, d->startPositions, d->startPositionsDefined)) {
2576                     setErrorString(tr("Error reading MTime"));
2577                     return false;
2578                 }
2579                 break;
2580             case kDummy: {
2581                 for (quint64 i = 0; i < size; i++) {
2582                     if (d->readByte() != 0) {
2583                         setErrorString(tr("Invalid"));
2584                         return false;
2585                     }
2586                 }
2587                 addPropIdToList = false;
2588                 break;
2589             }
2590             default:
2591                 addPropIdToList = isKnownType = false;
2592             }
2593         }
2594 
2595         if (isKnownType) {
2596             if (addPropIdToList) {
2597                 d->fileInfoPopIDs.append(type);
2598             }
2599         } else {
2600             d->skipData(d->readNumber());
2601         }
2602 
2603         bool checkRecordsSize = (major > 0 || minor > 2);
2604         if (checkRecordsSize && d->pos - ppp != size) {
2605             setErrorString(tr("Read size failed "
2606                               "(checkRecordsSize: %1, d->pos - ppp: %2, size: %3)")
2607                                .arg(checkRecordsSize)
2608                                .arg(d->pos - ppp)
2609                                .arg(size));
2610             return false;
2611         }
2612     }
2613 
2614     int emptyFileIndex = 0;
2615     int sizeIndex = 0;
2616 
2617     int numAntiItems = 0;
2618 
2619     if (emptyStreamVector.isEmpty()) {
2620         emptyStreamVector.fill(false, numFiles);
2621     }
2622 
2623     if (antiFileVector.isEmpty()) {
2624         antiFileVector.fill(false, numEmptyStreams);
2625     }
2626     if (emptyFileVector.isEmpty()) {
2627         emptyFileVector.fill(false, numEmptyStreams);
2628     }
2629 
2630     for (int i = 0; i < numEmptyStreams; i++) {
2631         if (antiFileVector[i]) {
2632             numAntiItems++;
2633         }
2634     }
2635 
2636     d->outData = d->readAndDecodePackedStreams(false);
2637 
2638     int oldPos = 0;
2639     for (int i = 0; i < numFiles; i++) {
2640         FileInfo *fileInfo = d->fileInfos.at(i);
2641         bool isAnti;
2642         fileInfo->hasStream = !emptyStreamVector[i];
2643         if (fileInfo->hasStream) {
2644             fileInfo->isDir = false;
2645             isAnti = false;
2646             fileInfo->size = d->unpackSizes[sizeIndex];
2647             fileInfo->crc = d->digests[sizeIndex];
2648             fileInfo->crcDefined = d->digestsDefined[sizeIndex];
2649             sizeIndex++;
2650         } else {
2651             fileInfo->isDir = !emptyFileVector[emptyFileIndex];
2652             isAnti = antiFileVector[emptyFileIndex];
2653             emptyFileIndex++;
2654             fileInfo->size = 0;
2655             fileInfo->crcDefined = false;
2656         }
2657         if (numAntiItems != 0) {
2658             d->isAnti.append(isAnti);
2659         }
2660 
2661         int access;
2662         bool symlink = false;
2663         if (fileInfo->attributes & FILE_ATTRIBUTE_UNIX_EXTENSION) {
2664             access = fileInfo->attributes >> 16;
2665             if ((access & QT_STAT_MASK) == QT_STAT_LNK) {
2666                 symlink = true;
2667             }
2668         } else {
2669             if (fileInfo->isDir) {
2670                 access = S_IFDIR | 0755;
2671             } else {
2672                 access = 0100644;
2673             }
2674         }
2675 
2676         qint64 pos = 0;
2677         if (!fileInfo->isDir) {
2678             pos = oldPos;
2679             oldPos += fileInfo->size;
2680         }
2681 
2682         KArchiveEntry *e;
2683         QString entryName;
2684         int index = fileInfo->path.lastIndexOf(QLatin1Char('/'));
2685         if (index == -1) {
2686             entryName = fileInfo->path;
2687         } else {
2688             entryName = fileInfo->path.mid(index + 1);
2689         }
2690         Q_ASSERT(!entryName.isEmpty());
2691 
2692         QDateTime mTime;
2693         if (d->mTimesDefined[i]) {
2694             mTime = KArchivePrivate::time_tToDateTime(toTimeT(d->mTimes[i]));
2695         } else {
2696             mTime = KArchivePrivate::time_tToDateTime(time(nullptr));
2697         }
2698 
2699         if (fileInfo->isDir) {
2700             QString path = QDir::cleanPath(fileInfo->path);
2701             const KArchiveEntry *ent = rootDir()->entry(path);
2702             if (ent && ent->isDirectory()) {
2703                 e = nullptr;
2704             } else {
2705                 e = new KArchiveDirectory(this, entryName, access, mTime, rootDir()->user(), rootDir()->group(), QString() /*symlink*/);
2706             }
2707         } else {
2708             if (!symlink) {
2709                 e = new K7ZipFileEntry(this,
2710                                        entryName,
2711                                        access,
2712                                        mTime,
2713                                        rootDir()->user(),
2714                                        rootDir()->group(),
2715                                        QString() /*symlink*/,
2716                                        pos,
2717                                        fileInfo->size,
2718                                        d->outData);
2719             } else {
2720                 QString target = QFile::decodeName(d->outData.mid(pos, fileInfo->size));
2721                 e = new K7ZipFileEntry(this, entryName, access, mTime, rootDir()->user(), rootDir()->group(), target, 0, 0, nullptr);
2722             }
2723         }
2724 
2725         if (e) {
2726             if (index == -1) {
2727                 rootDir()->addEntry(e);
2728             } else {
2729                 QString path = QDir::cleanPath(fileInfo->path.left(index));
2730                 KArchiveDirectory *d = findOrCreate(path);
2731                 d->addEntry(e);
2732             }
2733         }
2734     }
2735 
2736     return true;
2737 }
2738 
closeArchive()2739 bool K7Zip::closeArchive()
2740 {
2741     // Unnecessary check (already checked by KArchive::close())
2742     if (!isOpen()) {
2743         // qCWarning(KArchiveLog) << "You must open the file before close it\n";
2744         return false;
2745     }
2746 
2747     if ((mode() == QIODevice::ReadOnly)) {
2748         return true;
2749     }
2750 
2751     d->clear();
2752 
2753     Folder *folder = new Folder();
2754 
2755     folder->unpackSizes.clear();
2756     folder->unpackSizes.append(d->outData.size());
2757 
2758     Folder::FolderInfo *info = new Folder::FolderInfo();
2759 
2760     info->numInStreams = 1;
2761     info->numOutStreams = 1;
2762     info->methodID = k_LZMA2;
2763 
2764     quint32 dictSize = d->outData.size();
2765 
2766     const quint32 kMinReduceSize = (1 << 16);
2767     if (dictSize < kMinReduceSize) {
2768         dictSize = kMinReduceSize;
2769     }
2770 
2771     // k_LZMA2 method
2772     int dict;
2773     for (dict = 0; dict < 40; dict++) {
2774         if (dictSize <= lzma2_dic_size_from_prop(dict)) {
2775             break;
2776         }
2777     }
2778     info->properties.append(dict);
2779 
2780     folder->folderInfos.append(info);
2781     d->folders.append(folder);
2782 
2783     const KArchiveDirectory *dir = directory();
2784     QByteArray data;
2785     d->createItemsFromEntities(dir, QString(), data);
2786     d->outData = data;
2787 
2788     folder->unpackCRCDefined = true;
2789     folder->unpackCRC = crc32(0, (Bytef *)(d->outData.data()), d->outData.size());
2790 
2791     // compress data
2792     QByteArray encodedData;
2793     if (!d->outData.isEmpty()) {
2794         QByteArray enc;
2795         QBuffer inBuffer(&enc);
2796 
2797         KCompressionDevice flt(&inBuffer, false, KCompressionDevice::Xz);
2798         flt.open(QIODevice::WriteOnly);
2799 
2800         KFilterBase *filter = flt.filterBase();
2801 
2802         static_cast<KXzFilter *>(filter)->init(QIODevice::WriteOnly, KXzFilter::LZMA2, info->properties);
2803 
2804         const int ret = flt.write(d->outData);
2805         if (ret != d->outData.size()) {
2806             setErrorString(tr("Write error"));
2807             return false;
2808         }
2809 
2810         flt.close();
2811         encodedData = inBuffer.data();
2812     }
2813 
2814     d->packSizes.append(encodedData.size());
2815 
2816     int numUnpackStream = 0;
2817     for (int i = 0; i < d->fileInfos.size(); ++i) {
2818         if (d->fileInfos.at(i)->hasStream) {
2819             numUnpackStream++;
2820         }
2821     }
2822     d->numUnpackStreamsInFolders.append(numUnpackStream);
2823 
2824     quint64 headerOffset;
2825     d->writeHeader(headerOffset);
2826 
2827     // Encode Header
2828     QByteArray encodedStream;
2829     {
2830         QVector<quint64> packSizes;
2831         QVector<Folder *> folders;
2832         encodedStream = d->encodeStream(packSizes, folders);
2833 
2834         if (folders.isEmpty()) {
2835             // FIXME Not sure why this is an error. Come up with a better message
2836             setErrorString(tr("Failed while encoding header"));
2837             return false;
2838         }
2839 
2840         d->header.clear();
2841 
2842         d->writeByte(kEncodedHeader);
2843         QVector<bool> emptyDefined;
2844         QVector<quint32> emptyCrcs;
2845         d->writePackInfo(headerOffset, packSizes, emptyDefined, emptyCrcs);
2846         d->writeUnpackInfo(folders);
2847         d->writeByte(kEnd);
2848         for (int i = 0; i < packSizes.size(); i++) {
2849             headerOffset += packSizes.at(i);
2850         }
2851         qDeleteAll(folders);
2852     }
2853     // end encode header
2854 
2855     quint64 nextHeaderSize = d->header.size();
2856     quint32 nextHeaderCRC = crc32(0, (Bytef *)(d->header.data()), d->header.size());
2857     quint64 nextHeaderOffset = headerOffset;
2858 
2859     device()->seek(0);
2860     d->writeSignature();
2861     d->writeStartHeader(nextHeaderSize, nextHeaderCRC, nextHeaderOffset);
2862     device()->write(encodedData.data(), encodedData.size());
2863     device()->write(encodedStream.data(), encodedStream.size());
2864     device()->write(d->header.data(), d->header.size());
2865 
2866     return true;
2867 }
2868 
doFinishWriting(qint64 size)2869 bool K7Zip::doFinishWriting(qint64 size)
2870 {
2871     d->m_currentFile->setSize(size);
2872     d->m_currentFile = nullptr;
2873 
2874     return true;
2875 }
2876 
writeData(const char * data,qint64 size)2877 bool K7Zip::writeData(const char *data, qint64 size)
2878 {
2879     if (!d->m_currentFile) {
2880         setErrorString(tr("No file currently selected"));
2881         return false;
2882     }
2883 
2884     if (d->m_currentFile->position() == d->outData.size()) {
2885         d->outData.append(data, size);
2886     } else {
2887         d->outData.remove(d->m_currentFile->position(), d->m_currentFile->size());
2888         d->outData.insert(d->m_currentFile->position(), data, size);
2889     }
2890 
2891     return true;
2892 }
2893 
doPrepareWriting(const QString & name,const QString & user,const QString & group,qint64,mode_t perm,const QDateTime &,const QDateTime & mtime,const QDateTime &)2894 bool K7Zip::doPrepareWriting(const QString &name,
2895                              const QString &user,
2896                              const QString &group,
2897                              qint64 /*size*/,
2898                              mode_t perm,
2899                              const QDateTime & /*atime*/,
2900                              const QDateTime &mtime,
2901                              const QDateTime & /*ctime*/)
2902 {
2903     if (!isOpen()) {
2904         setErrorString(tr("Application error: 7-Zip file must be open before being written into"));
2905         qCWarning(KArchiveLog) << "doPrepareWriting failed: !isOpen()";
2906         return false;
2907     }
2908 
2909     if (!(mode() & QIODevice::WriteOnly)) {
2910         setErrorString(tr("Application error: attempted to write into non-writable 7-Zip file"));
2911         qCWarning(KArchiveLog) << "doPrepareWriting failed: !(mode() & QIODevice::WriteOnly)";
2912         return false;
2913     }
2914 
2915     // Find or create parent dir
2916     KArchiveDirectory *parentDir = rootDir();
2917     // QString fileName( name );
2918     // In some files we can find dir/./file => call cleanPath
2919     QString fileName(QDir::cleanPath(name));
2920     int i = name.lastIndexOf(QLatin1Char('/'));
2921     if (i != -1) {
2922         QString dir = name.left(i);
2923         fileName = name.mid(i + 1);
2924         parentDir = findOrCreate(dir);
2925     }
2926 
2927     // test if the entry already exist
2928     const KArchiveEntry *entry = parentDir->entry(fileName);
2929     if (!entry) {
2930         K7ZipFileEntry *e =
2931             new K7ZipFileEntry(this, fileName, perm, mtime, user, group, QString() /*symlink*/, d->outData.size(), 0 /*unknown yet*/, d->outData);
2932         if (!parentDir->addEntryV2(e)) {
2933             return false;
2934         }
2935         d->m_entryList << e;
2936         d->m_currentFile = e;
2937     } else {
2938         // TODO : find and replace in m_entryList
2939         // d->m_currentFile = static_cast<K7ZipFileEntry*>(entry);
2940     }
2941 
2942     return true;
2943 }
2944 
doWriteDir(const QString & name,const QString & user,const QString & group,mode_t perm,const QDateTime &,const QDateTime & mtime,const QDateTime &)2945 bool K7Zip::doWriteDir(const QString &name,
2946                        const QString &user,
2947                        const QString &group,
2948                        mode_t perm,
2949                        const QDateTime & /*atime*/,
2950                        const QDateTime &mtime,
2951                        const QDateTime & /*ctime*/)
2952 {
2953     if (!isOpen()) {
2954         setErrorString(tr("Application error: 7-Zip file must be open before being written into"));
2955         qCWarning(KArchiveLog) << "doWriteDir failed: !isOpen()";
2956         return false;
2957     }
2958 
2959     if (!(mode() & QIODevice::WriteOnly)) {
2960         // qCWarning(KArchiveLog) << "You must open the tar file for writing\n";
2961         return false;
2962     }
2963 
2964     // In some tar files we can find dir/./ => call cleanPath
2965     QString dirName(QDir::cleanPath(name));
2966 
2967     // Remove trailing '/'
2968     if (dirName.endsWith(QLatin1Char('/'))) {
2969         dirName.remove(dirName.size() - 1, 1);
2970     }
2971 
2972     KArchiveDirectory *parentDir = rootDir();
2973     int i = dirName.lastIndexOf(QLatin1Char('/'));
2974     if (i != -1) {
2975         QString dir = name.left(i);
2976         dirName = name.mid(i + 1);
2977         parentDir = findOrCreate(dir);
2978     }
2979 
2980     KArchiveDirectory *e = new KArchiveDirectory(this, dirName, perm, mtime, user, group, QString() /*symlink*/);
2981     parentDir->addEntry(e);
2982 
2983     return true;
2984 }
2985 
doWriteSymLink(const QString & name,const QString & target,const QString & user,const QString & group,mode_t perm,const QDateTime &,const QDateTime & mtime,const QDateTime &)2986 bool K7Zip::doWriteSymLink(const QString &name,
2987                            const QString &target,
2988                            const QString &user,
2989                            const QString &group,
2990                            mode_t perm,
2991                            const QDateTime & /*atime*/,
2992                            const QDateTime &mtime,
2993                            const QDateTime & /*ctime*/)
2994 {
2995     if (!isOpen()) {
2996         setErrorString(tr("Application error: 7-Zip file must be open before being written into"));
2997         qCWarning(KArchiveLog) << "doWriteSymLink failed: !isOpen()";
2998         return false;
2999     }
3000 
3001     if (!(mode() & QIODevice::WriteOnly)) {
3002         setErrorString(tr("Application error: attempted to write into non-writable 7-Zip file"));
3003         qCWarning(KArchiveLog) << "doWriteSymLink failed: !(mode() & QIODevice::WriteOnly)";
3004         return false;
3005     }
3006 
3007     // Find or create parent dir
3008     KArchiveDirectory *parentDir = rootDir();
3009     // In some files we can find dir/./file => call cleanPath
3010     QString fileName(QDir::cleanPath(name));
3011     int i = name.lastIndexOf(QLatin1Char('/'));
3012     if (i != -1) {
3013         QString dir = name.left(i);
3014         fileName = name.mid(i + 1);
3015         parentDir = findOrCreate(dir);
3016     }
3017     QByteArray encodedTarget = QFile::encodeName(target);
3018 
3019     K7ZipFileEntry *e = new K7ZipFileEntry(this, fileName, perm, mtime, user, group, target, 0, 0, nullptr);
3020     d->outData.append(encodedTarget);
3021 
3022     if (!parentDir->addEntryV2(e)) {
3023         return false;
3024     }
3025 
3026     d->m_entryList << e;
3027 
3028     return true;
3029 }
3030 
virtual_hook(int id,void * data)3031 void K7Zip::virtual_hook(int id, void *data)
3032 {
3033     KArchive::virtual_hook(id, data);
3034 }
3035