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

source code of karchive/src/k7zip.cpp