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