KArchive

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

KDE's Doxygen guidelines are available online.