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

source code of karchive/src/k7zip.cpp