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

KDE's Doxygen guidelines are available online.