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

source code of karchive/src/k7zip.cpp