Coverage Report

Created: 2026-05-31 06:50

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