Coverage Report

Created: 2026-10-28 07:09

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