9#include "loggingcategory.h"
12#include <QCryptographicHash>
17#include <qplatformdefs.h>
19#include "kcompressiondevice.h"
20#include "klimitediodevice_p.h"
21#include <kfilterbase.h>
28#if HAVE_OPENSSL_SUPPORT
29#include <openssl/evp.h>
33#define QT_STAT_LNK 0120000
40#define BUFFER_SIZE 8 * 1024
42static const unsigned char k7zip_signature[6] = {
'7',
'z', 0xBC, 0xAF, 0x27, 0x1C};
46static QChar GetUi16(
const char *p)
48 return QChar(
static_cast<unsigned char>(p[0])
49 | (
static_cast<unsigned char>(p[1]) << 8));
52static quint32 GetUi32(
const char *p)
54 return (
static_cast<unsigned char>(p[0])
55 | (
static_cast<unsigned char>(p[1]) << 8)
56 | (
static_cast<unsigned char>(p[2]) << 16)
57 | (
static_cast<unsigned char>(p[3]) << 24));
60static quint64 GetUi64(
const char *p)
63 | (
static_cast<quint64
>(GetUi32(p + 4)) << 32));
66static quint32 lzma2_dic_size_from_prop(
int p)
68 return ((
static_cast<quint32
>(2) | (p & 1)) << ((p / 2) + 11));
73#define FILE_ATTRIBUTE_READONLY 1
74#define FILE_ATTRIBUTE_HIDDEN 2
75#define FILE_ATTRIBUTE_SYSTEM 4
76#define FILE_ATTRIBUTE_DIRECTORY 16
77#define FILE_ATTRIBUTE_ARCHIVE 32
78#define FILE_ATTRIBUTE_DEVICE 64
79#define FILE_ATTRIBUTE_NORMAL 128
80#define FILE_ATTRIBUTE_TEMPORARY 256
81#define FILE_ATTRIBUTE_SPARSE_FILE 512
82#define FILE_ATTRIBUTE_REPARSE_POINT 1024
83#define FILE_ATTRIBUTE_COMPRESSED 2048
84#define FILE_ATTRIBUTE_OFFLINE 0x1000
85#define FILE_ATTRIBUTE_ENCRYPTED 0x4000
86#define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000
95 kAdditionalStreamsInfo,
137static const quint64 k_LZMA2 = 0x21;
140static const quint64 k_LZMA = 0x030101;
141static const quint64 k_BCJ = 0x03030103;
142static const quint64 k_BCJ2 = 0x0303011B;
150static const quint64 k_PPMD = 0x030401;
161static const quint64 k_BZip2 = 0x040202;
172static const quint64 k_AES = 0x06F10701;
180 K7ZipFileEntry(K7Zip *zip,
183 const QDateTime &
date,
185 const QString &
group,
186 const QString &symlink,
189 const QByteArray &data);
191 ~K7ZipFileEntry()
override;
197 QByteArray data()
const override;
209 QIODevice *createDevice()
const override;
212 const QByteArray m_data;
216K7ZipFileEntry::K7ZipFileEntry(
K7Zip *zip,
230 m_buffer->setData(m_data);
234K7ZipFileEntry::~K7ZipFileEntry()
244QIODevice *K7ZipFileEntry::createDevice()
const
246 return new KLimitedIODevice(m_buffer,
position(),
size());
256 , attribDefined(false)
286 bool isSimpleCoder()
const
288 return (numInStreams == 1) && (numOutStreams == 1);
293 QList<unsigned char> properties;
298 : unpackCRCDefined(false)
305 qDeleteAll(folderInfos);
308 Q_DISABLE_COPY(Folder)
310 quint64 getUnpackSize()
const
312 if (unpackSizes.isEmpty()) {
315 for (
int i = unpackSizes.size() - 1; i >= 0; i--) {
316 if (findBindPairForOutStream(i) < 0) {
317 return unpackSizes.at(i);
323 int getNumOutStreams()
const
326 for (
int i = 0; i < folderInfos.size(); i++) {
327 result += folderInfos.at(i)->numOutStreams;
332 quint32 getCoderInStreamIndex(quint32 coderIndex)
const
334 quint32 streamIndex = 0;
335 for (quint32 i = 0; i < coderIndex; i++) {
336 streamIndex += folderInfos.at(i)->numInStreams;
341 quint32 getCoderOutStreamIndex(quint32 coderIndex)
const
343 quint32 streamIndex = 0;
344 for (quint32 i = 0; i < coderIndex; i++) {
345 streamIndex += folderInfos.at(i)->numOutStreams;
350 int findBindPairForInStream(
size_t inStreamIndex)
const
352 for (
int i = 0; i < inIndexes.size(); i++) {
353 if (inIndexes[i] == inStreamIndex) {
360 int findBindPairForOutStream(
size_t outStreamIndex)
const
362 for (
int i = 0; i < outIndexes.size(); i++) {
363 if (outIndexes[i] == outStreamIndex) {
370 int findPackStreamArrayIndex(
size_t inStreamIndex)
const
372 for (
int i = 0; i < packedStreams.size(); i++) {
373 if (packedStreams[i] == inStreamIndex) {
380 void findInStream(quint32 streamIndex, quint32 &coderIndex, quint32 &coderStreamIndex)
const
382 for (coderIndex = 0; coderIndex < (quint32)folderInfos.size(); coderIndex++) {
383 quint32 curSize = folderInfos[coderIndex]->numInStreams;
384 if (streamIndex < curSize) {
385 coderStreamIndex = streamIndex;
388 streamIndex -= curSize;
392 void findOutStream(quint32 streamIndex, quint32 &coderIndex, quint32 &coderStreamIndex)
const
394 for (coderIndex = 0; coderIndex < (quint32)folderInfos.size(); coderIndex++) {
395 quint32 curSize = folderInfos[coderIndex]->numOutStreams;
396 if (streamIndex < curSize) {
397 coderStreamIndex = streamIndex;
400 streamIndex -= curSize;
404 bool isEncrypted()
const
406 for (
int i = folderInfos.size() - 1; i >= 0; i--) {
407 if (folderInfos.at(i)->methodID == k_AES) {
416 bool unpackCRCDefined;
418 QList<FolderInfo *> folderInfos;
419 QList<quint64> inIndexes;
420 QList<quint64> outIndexes;
421 QList<quint64> packedStreams;
422 QList<quint64> unpackSizes;
425class Q_DECL_HIDDEN
K7Zip::K7ZipPrivate
428 K7ZipPrivate(K7Zip *parent)
437 , m_currentFile(nullptr)
444 qDeleteAll(fileInfos);
449 QList<bool> packCRCsDefined;
450 QList<quint32> packCRCs;
451 QList<quint64> numUnpackStreamsInFolders;
453 QList<Folder *> folders;
454 QList<FileInfo *> fileInfos;
456 QList<bool> cTimesDefined;
457 QList<quint64> cTimes;
458 QList<bool> aTimesDefined;
459 QList<quint64> aTimes;
460 QList<bool> mTimesDefined;
461 QList<quint64> mTimes;
462 QList<bool> startPositionsDefined;
463 QList<quint64> startPositions;
464 QList<int> fileInfoPopIDs;
467 quint64 numPackStreams;
468 QList<quint64> packSizes;
469 QList<quint64> unpackSizes;
470 QList<bool> digestsDefined;
471 QList<quint32> digests;
486 K7ZipFileEntry *m_currentFile;
487 QList<KArchiveEntry *> m_entryList;
491 packCRCsDefined.
clear();
493 numUnpackStreamsInFolders.
clear();
496 qDeleteAll(fileInfos);
498 cTimesDefined.
clear();
500 aTimesDefined.
clear();
502 mTimesDefined.
clear();
504 startPositionsDefined.
clear();
505 startPositions.
clear();
506 fileInfoPopIDs.
clear();
509 digestsDefined.
clear();
522 quint32 readUInt32();
523 quint64 readUInt64();
524 quint64 readNumber();
525 QString readString();
526 void readHashDigests(
int numItems, QList<bool> &digestsDefined, QList<quint32> &digests);
527 void readBoolVector(
int numItems, QList<bool> &v);
528 void readBoolVector2(
int numItems, QList<bool> &v);
529 void skipData(
int size);
530 bool findAttribute(
int attribute);
531 bool readUInt64DefVector(
int numFiles, QList<quint64> &values, QList<bool> &defined);
534 bool readMainStreamsInfo();
536 bool readUnpackInfo();
537 bool readSubStreamsInfo();
538 KFilterBase *getFilter(
const Folder *folder,
const Folder::FolderInfo *coder,
const int currentCoderIndex, QByteArray &deflatedData, QList<QByteArray> &inflatedDatas);
539 QByteArray readAndDecodePackedStreams(
bool readMainStreamInfo =
true);
542 void createItemsFromEntities(
const KArchiveDirectory *,
const QString &, QByteArray &);
543 void writeByte(
unsigned char b);
544 void writeNumber(quint64 value);
545 void writeBoolVector(
const QList<bool> &boolVector);
546 void writeUInt32(quint32 value);
547 void writeUInt64(quint64 value);
548 void writeHashDigests(
const QList<bool> &digestsDefined,
const QList<quint32> &digests);
549 void writeAlignedBoolHeader(
const QList<bool> &v,
int numDefined,
int type,
unsigned itemSize);
550 void writeUInt64DefVector(
const QList<quint64> &v,
const QList<bool> &defined,
int type);
551 void writeFolder(
const Folder *folder);
552 void writePackInfo(quint64 dataOffset, QList<quint64> &packedSizes, QList<bool> &packedCRCsDefined, QList<quint32> &packedCRCs);
553 void writeUnpackInfo(
const QList<Folder *> &folderItems);
554 void writeSubStreamsInfo(
const QList<quint64> &unpackSizes,
const QList<bool> &digestsDefined,
const QList<quint32> &digests);
555 void writeHeader(quint64 &headerOffset);
556 void writeSignature();
557 void writeStartHeader(
const quint64 nextHeaderSize,
const quint32 nextHeaderCRC,
const quint64 nextHeaderOffset);
558 QByteArray encodeStream(QList<quint64> &packSizes, QList<Folder *> &folds);
563 , d(new K7ZipPrivate(this))
569 , d(new K7ZipPrivate(this))
584 d->password = password;
589 for (
int i = 0; i < d->folders.size(); i++) {
590 if (d->folders.at(i)->isEncrypted()) {
598int K7Zip::K7ZipPrivate::readByte()
600 if (!buffer || pos + 1 > end) {
603 return buffer[pos++];
606quint32 K7Zip::K7ZipPrivate::readUInt32()
608 if (!buffer || (quint64)(pos + 4) > end) {
609 qCDebug(KArchiveLog) <<
"error size";
613 quint32 res = GetUi32(buffer + pos);
618quint64 K7Zip::K7ZipPrivate::readUInt64()
620 if (!buffer || (quint64)(pos + 8) > end) {
621 qCDebug(KArchiveLog) <<
"error size";
625 quint64 res = GetUi64(buffer + pos);
630quint64 K7Zip::K7ZipPrivate::readNumber()
632 if (!buffer || pos >= end) {
636 unsigned char firstByte = buffer[pos++];
637 unsigned char mask = 0x80;
639 for (
int i = 0; i < 8; i++) {
640 if ((firstByte & mask) == 0) {
641 quint64 highPart = firstByte & (mask - 1);
642 value += (highPart << (i * 8));
650 value |= ((
unsigned char)buffer[pos++] << (8 * i));
656QString K7Zip::K7ZipPrivate::readString()
662 const char *buf = buffer + pos;
663 size_t rem = (
end - pos) / 2 * 2;
666 for (i = 0; i < rem; i += 2) {
667 if (buf[i] == 0 && buf[i + 1] == 0) {
672 qCDebug(KArchiveLog) <<
"read string error";
678 int len = (int)(rem / 2);
679 if (len < 0 || (
size_t)len * 2 != rem) {
680 qCDebug(KArchiveLog) <<
"read string unsupported";
685 for (
int i = 0; i < len; i++, buf += 2) {
693void K7Zip::K7ZipPrivate::skipData(
int size)
695 if (!buffer || pos + size > end) {
701bool K7Zip::K7ZipPrivate::findAttribute(
int attribute)
708 int type = readByte();
709 if (type == attribute) {
715 skipData(readNumber());
719void K7Zip::K7ZipPrivate::readBoolVector(
int numItems, QList<bool> &v)
726 unsigned char mask = 0;
727 for (
int i = 0; i < numItems; i++) {
732 v.
append((b & mask) != 0);
737void K7Zip::K7ZipPrivate::readBoolVector2(
int numItems, QList<bool> &v)
743 int allAreDefined = readByte();
744 if (allAreDefined == 0) {
745 readBoolVector(numItems, v);
749 for (
int i = 0; i < numItems; i++) {
754void K7Zip::K7ZipPrivate::readHashDigests(
int numItems, QList<bool> &digestsDefined, QList<quint32> &digests)
760 readBoolVector2(numItems, digestsDefined);
761 for (
int i = 0; i < numItems; i++) {
763 if (digestsDefined[i]) {
770Folder *K7Zip::K7ZipPrivate::folderItem()
777 int numCoders = readNumber();
779 quint64 numInStreamsTotal = 0;
780 quint64 numOutStreamsTotal = 0;
781 for (
int i = 0; i < numCoders; ++i) {
791 unsigned char coderInfo = readByte();
792 int codecIdSize = (coderInfo & 0xF);
793 if (codecIdSize > 8) {
794 qCDebug(KArchiveLog) <<
"unsupported codec id size";
798 Folder::FolderInfo *info =
new Folder::FolderInfo();
799 std::unique_ptr<unsigned char[]> codecID(
new unsigned char[codecIdSize]);
800 for (
int i = 0; i < codecIdSize; ++i) {
801 codecID[i] = readByte();
805 for (
int j = 0; j < codecIdSize; j++) {
806 id |= codecID[codecIdSize - 1 - j] << (8 * j);
811 if ((coderInfo & 0x10) != 0) {
812 info->numInStreams = readNumber();
813 info->numOutStreams = readNumber();
815 info->numInStreams = 1;
816 info->numOutStreams = 1;
820 if ((coderInfo & 0x20) != 0) {
821 int propertiesSize = readNumber();
822 for (
int i = 0; i < propertiesSize; ++i) {
823 info->properties.
append(readByte());
827 if ((coderInfo & 0x80) != 0) {
828 qCDebug(KArchiveLog) <<
"unsupported";
834 numInStreamsTotal += info->numInStreams;
835 numOutStreamsTotal += info->numOutStreams;
836 folder->folderInfos.
append(info);
839 int numBindPairs = numOutStreamsTotal - 1;
840 for (
int i = 0; i < numBindPairs; i++) {
841 folder->inIndexes.
append(readNumber());
842 folder->outIndexes.
append(readNumber());
845 int numPackedStreams = numInStreamsTotal - numBindPairs;
846 if (numPackedStreams > 1) {
847 for (
int i = 0; i < numPackedStreams; ++i) {
848 folder->packedStreams.
append(readNumber());
851 if (numPackedStreams == 1) {
852 for (quint64 i = 0; i < numInStreamsTotal; i++) {
853 if (folder->findBindPairForInStream(i) < 0) {
854 folder->packedStreams.
append(i);
858 if (folder->packedStreams.
size() != 1) {
867bool K7Zip::K7ZipPrivate::readUInt64DefVector(
int numFiles, QList<quint64> &values, QList<bool> &defined)
873 readBoolVector2(numFiles, defined);
875 int external = readByte();
877 int dataIndex = readNumber();
878 if (dataIndex < 0 /*|| dataIndex >= dataVector->Size()*/) {
879 qCDebug(KArchiveLog) <<
"wrong data index";
886 for (
int i = 0; i < numFiles; i++) {
896bool K7Zip::K7ZipPrivate::readPackInfo()
902 packPos = readNumber();
903 numPackStreams = readNumber();
906 packCRCsDefined.
clear();
909 if (!findAttribute(kSize)) {
910 qCDebug(KArchiveLog) <<
"kSize not found";
914 for (quint64 i = 0; i < numPackStreams; ++i) {
915 packSizes.
append(readNumber());
919 int type = readByte();
924 readHashDigests(numPackStreams, packCRCsDefined, packCRCs);
927 skipData(readNumber());
931 for (quint64 i = 0; i < numPackStreams; ++i) {
932 packCRCsDefined.
append(
false);
939bool K7Zip::K7ZipPrivate::readUnpackInfo()
945 if (!findAttribute(kFolder)) {
946 qCDebug(KArchiveLog) <<
"kFolder not found";
950 int numFolders = readNumber();
953 int external = readByte();
956 for (
int i = 0; i < numFolders; ++i) {
957 folders.
append(folderItem());
962 int dataIndex = readNumber();
963 if (dataIndex < 0 /*|| dataIndex >= dataVector->Size()*/) {
964 qCDebug(KArchiveLog) <<
"wrong data index";
970 qCDebug(KArchiveLog) <<
"external error";
974 if (!findAttribute(kCodersUnpackSize)) {
975 qCDebug(KArchiveLog) <<
"kCodersUnpackSize not found";
979 for (
int i = 0; i < numFolders; ++i) {
981 int numOutStreams = folder->getNumOutStreams();
982 for (
int j = 0; j < numOutStreams; ++j) {
983 folder->unpackSizes.
append(readNumber());
988 int type = readByte();
993 QList<bool> crcsDefined;
995 readHashDigests(numFolders, crcsDefined, crcs);
996 for (
int i = 0; i < numFolders; i++) {
998 folder->unpackCRCDefined = crcsDefined[i];
999 folder->unpackCRC = crcs[i];
1003 skipData(readNumber());
1008bool K7Zip::K7ZipPrivate::readSubStreamsInfo()
1014 numUnpackStreamsInFolders.
clear();
1019 if (type == kNumUnpackStream) {
1020 for (
int i = 0; i < folders.
size(); i++) {
1021 numUnpackStreamsInFolders.
append(readNumber());
1025 if (type == kCRC || type == kSize) {
1031 skipData(readNumber());
1034 if (numUnpackStreamsInFolders.
isEmpty()) {
1035 for (
int i = 0; i < folders.
size(); i++) {
1036 numUnpackStreamsInFolders.
append(1);
1040 for (
int i = 0; i < numUnpackStreamsInFolders.
size(); i++) {
1041 quint64 numSubstreams = numUnpackStreamsInFolders.
at(i);
1042 if (numSubstreams == 0) {
1046 for (quint64 j = 1; j < numSubstreams; j++) {
1047 if (type == kSize) {
1048 int size = readNumber();
1049 unpackSizes.
append(size);
1053 unpackSizes.
append(folders.
at(i)->getUnpackSize() - sum);
1056 if (type == kSize) {
1061 int numDigestsTotal = 0;
1062 for (
int i = 0; i < folders.
size(); i++) {
1063 quint64 numSubstreams = numUnpackStreamsInFolders.
at(i);
1064 if (numSubstreams != 1 || !folders.
at(i)->unpackCRCDefined) {
1065 numDigests += numSubstreams;
1067 numDigestsTotal += numSubstreams;
1072 QList<bool> digestsDefined2;
1073 QList<quint32> digests2;
1074 readHashDigests(numDigests, digestsDefined2, digests2);
1075 int digestIndex = 0;
1076 for (
int i = 0; i < folders.
size(); i++) {
1077 quint64 numSubstreams = numUnpackStreamsInFolders.
at(i);
1078 const Folder *folder = folders.
at(i);
1079 if (numSubstreams == 1 && folder->unpackCRCDefined) {
1080 digestsDefined.
append(
true);
1081 digests.
append(folder->unpackCRC);
1083 for (quint64 j = 0; j < numSubstreams; j++, digestIndex++) {
1084 digestsDefined.
append(digestsDefined2[digestIndex]);
1085 digests.
append(digests2[digestIndex]);
1089 }
else if (type == kEnd) {
1090 if (digestsDefined.
isEmpty()) {
1091 for (
int i = 0; i < numDigestsTotal; i++) {
1092 digestsDefined.
append(
false);
1099 skipData(readNumber());
1107#define TICKSPERSEC 10000000
1108#define TICKSPERMSEC 10000
1109#define SECSPERDAY 86400
1110#define SECSPERHOUR 3600
1111#define SECSPERMIN 60
1112#define EPOCHWEEKDAY 1
1113#define DAYSPERWEEK 7
1114#define DAYSPERQUADRICENTENNIUM (365 * 400 + 97)
1115#define DAYSPERNORMALQUADRENNIUM (365 * 4 + 1)
1116#define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC)
1117#define SECS_1601_TO_1970 ((369 * 365 + 89) * (unsigned long long)SECSPERDAY)
1119static uint toTimeT(
const long long liTime)
1121 long long time = liTime / TICKSPERSEC;
1127 long int days = time / SECSPERDAY;
1128 int secondsInDay = time % SECSPERDAY;
1131 short hour = (short)(secondsInDay / SECSPERHOUR);
1132 secondsInDay = secondsInDay % SECSPERHOUR;
1133 short minute = (short)(secondsInDay / SECSPERMIN);
1134 short second = (short)(secondsInDay % SECSPERMIN);
1137 long int cleaps = (3 * ((4 * days + 1227) / DAYSPERQUADRICENTENNIUM) + 3) / 4;
1138 days += 28188 + cleaps;
1139 long int years = (20 * days - 2442) / (5 * DAYSPERNORMALQUADRENNIUM);
1140 long int yearday = days - (years * DAYSPERNORMALQUADRENNIUM) / 4;
1141 long int months = (64 * yearday) / 1959;
1149 month = (short)(months - 1);
1150 year = (short)(years + 1524);
1152 month = (short)(months - 13);
1153 year = (short)(years + 1525);
1158 short day = (short)(yearday - (1959 * months) / 64);
1160 QDateTime t(QDate(year, month, day), QTime(hour, minute, second));
1162 return t.toSecsSinceEpoch();
1165long long rtlSecondsSince1970ToSpecTime(quint32 seconds)
1167 long long secs = seconds * (
long long)TICKSPERSEC + TICKS_1601_TO_1970;
1171bool K7Zip::K7ZipPrivate::readMainStreamsInfo()
1180 if (type > ((quint32)1 << 30)) {
1181 qCDebug(KArchiveLog) <<
"type error";
1188 if (!readPackInfo()) {
1189 qCDebug(KArchiveLog) <<
"error during read pack information";
1195 if (!readUnpackInfo()) {
1196 qCDebug(KArchiveLog) <<
"error during read pack information";
1201 case kSubStreamsInfo: {
1202 if (!readSubStreamsInfo()) {
1203 qCDebug(KArchiveLog) <<
"error during read substreams information";
1209 qCDebug(KArchiveLog) <<
"Wrong type";
1214 qCDebug(KArchiveLog) <<
"should not reach";
1218static bool getInStream(
const Folder *folder, quint32 streamIndex,
int &seqInStream, quint32 &coderIndex)
1220 for (
int i = 0; i < folder->packedStreams.
size(); i++) {
1221 if (folder->packedStreams[i] == streamIndex) {
1227 int binderIndex = folder->findBindPairForInStream(streamIndex);
1228 if (binderIndex < 0) {
1232 quint32 coderStreamIndex;
1233 folder->findOutStream(folder->outIndexes[binderIndex], coderIndex, coderStreamIndex);
1235 quint32 startIndex = folder->getCoderInStreamIndex(coderIndex);
1237 if (folder->folderInfos[coderIndex]->numInStreams > 1) {
1241 for (
int i = 0; i < (int)folder->folderInfos[coderIndex]->numInStreams; i++) {
1242 getInStream(folder, startIndex + i, seqInStream, coderIndex);
1248static bool getOutStream(
const Folder *folder, quint32 streamIndex,
int &seqOutStream)
1250 QList<quint32> outStreams;
1251 quint32 outStreamIndex = 0;
1252 for (
int i = 0; i < folder->folderInfos.
size(); i++) {
1253 const Folder::FolderInfo *coderInfo = folder->folderInfos.
at(i);
1255 for (
int j = 0; j < coderInfo->numOutStreams; j++, outStreamIndex++) {
1256 if (folder->findBindPairForOutStream(outStreamIndex) < 0) {
1257 outStreams.
append(outStreamIndex);
1262 for (
int i = 0; i < outStreams.
size(); i++) {
1263 if (outStreams[i] == streamIndex) {
1269 int binderIndex = folder->findBindPairForOutStream(streamIndex);
1270 if (binderIndex < 0) {
1275 quint32 coderStreamIndex;
1276 folder->findInStream(folder->inIndexes[binderIndex], coderIndex, coderStreamIndex);
1278 quint32 startIndex = folder->getCoderOutStreamIndex(coderIndex);
1280 if (folder->folderInfos[coderIndex]->numOutStreams > 1) {
1284 for (
int i = 0; i < (int)folder->folderInfos[coderIndex]->numOutStreams; i++) {
1285 getOutStream(folder, startIndex + i, seqOutStream);
1291static const int catCycle = 6;
1293static QByteArray calculateKey(
const QByteArray &password, quint32 numCyclesPower,
const QByteArray &salt)
1295 quint32 rounds, stages;
1297 if (numCyclesPower > catCycle) {
1298 rounds = 1 << catCycle;
1299 stages = 1 << (numCyclesPower - catCycle);
1301 rounds = 1 << numCyclesPower;
1305 QByteArray saltPassword = salt + password;
1310 for (quint32 i = 0; i < stages; i++) {
1314 for (quint32 j = 0; j < rounds; j++) {
1315 result += saltPassword;
1317 quint64 value = s + j;
1318 for (
int k = 0; k < 8; k++) {
1319 result.
append(value >> (k * 8));
1323 hash.addData(result);
1327 return hash.result();
1330#if HAVE_OPENSSL_SUPPORT
1332static QByteArray decryptAES(
const QList<quint8> &coderProperties,
const QString &password, QByteArray &encryptedData)
1335 const QByteArray passwordBytes = toUtf16LE(password);
1337 quint8 firstByte = coderProperties[0];
1338 quint32 numCyclesPower = firstByte & 0x3F;
1340 if ((firstByte & 0xC0) == 0) {
1341 qCDebug(KArchiveLog) <<
"Unsupported AES properties";
1342 return QByteArray();
1345 int saltSize = ((firstByte >> 7) & 1) + (coderProperties[1] >> 4);
1346 int ivSize = ((firstByte >> 6) & 1) + (coderProperties[1] & 0x0F);
1348 QByteArray salt((
const char *)coderProperties.
data() + 2, saltSize);
1349 QByteArray iv((
const char *)coderProperties.
data() + 2 + saltSize, ivSize);
1352 iv.append(16 - ivSize,
'\x00');
1355 const QByteArray key = calculateKey(passwordBytes, numCyclesPower, salt);
1356 if (key.
size() != 32) {
1357 qCDebug(KArchiveLog) <<
"Failed to calculate key";
1358 return QByteArray();
1361 EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
1363 qCDebug(KArchiveLog) <<
"Failed to create OpenSSL cipher context";
1364 return QByteArray();
1367 const auto ctxCleanupGuard = qScopeGuard([&ctx] {
1368 EVP_CIPHER_CTX_free(ctx);
1372 int padLen = encryptedData.
size() % 16;
1374 encryptedData.
append(16 - padLen,
'\x00');
1377 QByteArray decryptedData;
1378 int len, plainTextLen = 0;
1381 if (EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(),
nullptr, (
const unsigned char *)key.
constData(), (
const unsigned char *)iv.constData()) != 1) {
1382 qCDebug(KArchiveLog) <<
"EVP_DecryptInit_ex failed";
1383 return QByteArray();
1387 EVP_CIPHER_CTX_set_padding(ctx, 0);
1389 if (EVP_DecryptUpdate(ctx, (
unsigned char *)decryptedData.
data(), &len, (
const unsigned char *)encryptedData.
constData(), encryptedData.
size()) != 1) {
1390 qCDebug(KArchiveLog) <<
"EVP_DecryptUpdate failed";
1391 return QByteArray();
1393 plainTextLen += len;
1395 if (EVP_DecryptFinal_ex(ctx, (
unsigned char *)decryptedData.
data() + len, &len) != 1) {
1396 qCDebug(KArchiveLog) <<
"EVP_DecryptFinal_ex failed";
1397 return QByteArray();
1399 plainTextLen += len;
1401 decryptedData.
resize(plainTextLen);
1402 return decryptedData;
1407const int kNumTopBits = 24;
1408const quint32 kTopValue = (1 << kNumTopBits);
1419 RangeDecoder(
const QByteArray &s)
1425 for (
int i = 0; i < 5; i++) {
1426 code = (code << 8) | readByte();
1430 unsigned char readByte()
1432 if (pos >= stream.
size()) {
1435 return stream[pos++];
1440 while (range < kTopValue) {
1441 code = (code << 8) | readByte();
1446 quint32 getThreshold(quint32 total)
1448 return (code) / (range /= total);
1451 void decode(quint32
start, quint32 size)
1453 code -=
start * range;
1458 quint32 decodeDirectBits(
int numTotalBits)
1463 for (
int i = numTotalBits; i != 0; i--) {
1465 quint32 t = (c - r) >> 31;
1467 result = (result << 1) | (1 - t);
1469 if (r < kTopValue) {
1470 c = (c << 8) | readByte();
1479 quint32 DecodeBit(quint32 size0, quint32 numTotalBits)
1481 quint32 newBound = (range >> numTotalBits) * size0;
1483 if (code < newBound) {
1496const int kNumBitModelTotalBits = 11;
1497const quint32 kBitModelTotal = (1 << kNumBitModelTotalBits);
1499template<
int numMoveBits>
1504 void updateModel(quint32 symbol)
1507 prob += (kBitModelTotal - prob) >> numMoveBits;
1509 prob -= (prob) >> numMoveBits;
1515 prob = kBitModelTotal / 2;
1519template<
int numMoveBits>
1520class CBitDecoder :
public CBitModel<numMoveBits>
1523 quint32 decode(RangeDecoder *decoder)
1525 quint32 newBound = (decoder->range >> kNumBitModelTotalBits) * this->prob;
1526 if (decoder->code < newBound) {
1527 decoder->range = newBound;
1528 this->prob += (kBitModelTotal - this->prob) >> numMoveBits;
1529 if (decoder->range < kTopValue) {
1530 decoder->code = (decoder->code << 8) | decoder->readByte();
1531 decoder->range <<= 8;
1535 decoder->range -= newBound;
1536 decoder->code -= newBound;
1537 this->prob -= (this->prob) >> numMoveBits;
1538 if (decoder->range < kTopValue) {
1539 decoder->code = (decoder->code << 8) | decoder->readByte();
1540 decoder->range <<= 8;
1547inline bool isJcc(
unsigned char b0,
unsigned char b1)
1549 return (b0 == 0x0F && (b1 & 0xF0) == 0x80);
1551inline bool isJ(
unsigned char b0,
unsigned char b1)
1553 return ((b1 & 0xFE) == 0xE8 || isJcc(b0, b1));
1555inline unsigned getIndex(
unsigned char b0,
unsigned char b1)
1557 return ((b1 == 0xE8) ? b0 : ((b1 == 0xE9) ? 256 : 257));
1560const int kNumMoveBits = 5;
1562static QByteArray decodeBCJ2(
const QByteArray &mainStream,
const QByteArray &callStream,
const QByteArray &jumpStream,
const QByteArray &rangeBuffer)
1564 unsigned char prevByte = 0;
1565 QByteArray outStream;
1566 int mainStreamPos = 0;
1567 int callStreamPos = 0;
1568 int jumpStreamPos = 0;
1570 RangeDecoder rangeDecoder(rangeBuffer);
1572 QList<CBitDecoder<kNumMoveBits>> statusDecoder(256 + 2);
1574 for (
int i = 0; i < 256 + 2; i++) {
1575 statusDecoder[i].init();
1580 unsigned char b = 0;
1581 const quint32 kBurstSize = (1 << 18);
1582 for (i = 0; i < kBurstSize; i++) {
1583 if (mainStreamPos == mainStream.
size()) {
1587 b = mainStream[mainStreamPos++];
1590 if (isJ(prevByte, b)) {
1596 if (i == kBurstSize) {
1600 unsigned index = getIndex(prevByte, b);
1601 if (statusDecoder[index].decode(&rangeDecoder) == 1) {
1603 if (callStreamPos + 4 > callStream.
size()) {
1604 return QByteArray();
1607 if (jumpStreamPos + 4 > jumpStream.
size()) {
1608 return QByteArray();
1612 for (
int i = 0; i < 4; i++) {
1615 b0 = callStream[callStreamPos++];
1617 b0 = jumpStream[jumpStreamPos++];
1620 src |= ((quint32)b0);
1623 quint32 dest = src - (quint32(outStream.
size()) + 4);
1624 outStream.
append((
unsigned char)(dest));
1625 outStream.
append((
unsigned char)(dest >> 8));
1626 outStream.
append((
unsigned char)(dest >> 16));
1627 outStream.
append((
unsigned char)(dest >> 24));
1628 prevByte = (
unsigned char)(dest >> 24);
1635KFilterBase *K7Zip::K7ZipPrivate::getFilter(
const Folder *folder,
1636 const Folder::FolderInfo *coder,
1637 const int currentCoderIndex,
1638 QByteArray &deflatedData,
1639 QList<QByteArray> &inflatedDatas)
1641 KFilterBase *
filter =
nullptr;
1643 switch (coder->methodID) {
1647 qCDebug(KArchiveLog) <<
"filter not found";
1655 qCDebug(KArchiveLog) <<
"filter not found";
1671 if (coder->properties.
size() >= 2) {
1673 qCDebug(KArchiveLog) <<
"Password is required for AES decryption";
1677#if HAVE_OPENSSL_SUPPORT
1678 QByteArray decryptedData = decryptAES(coder->properties, password, deflatedData);
1679 if (decryptedData.
isEmpty()) {
1680 qCDebug(KArchiveLog) <<
"AES decryption failed";
1684 if (folder->folderInfos.
size() > 1 && currentCoderIndex < folder->folderInfos.size()) {
1685 deflatedData = decryptedData;
1686 int nextCoderIndex = currentCoderIndex + 1;
1687 filter = getFilter(folder, folder->folderInfos[nextCoderIndex], nextCoderIndex, decryptedData, inflatedDatas);
1689 inflatedDatas.
append(decryptedData);
1698 qCDebug(KArchiveLog) <<
"filter not found";
1704 QByteArray bcj2 = decodeBCJ2(inflatedDatas[0], inflatedDatas[1], inflatedDatas[2], deflatedData);
1705 inflatedDatas.
clear();
1706 inflatedDatas.
append(bcj2);
1712 qCDebug(KArchiveLog) <<
"filter not found";
1722QByteArray K7Zip::K7ZipPrivate::readAndDecodePackedStreams(
bool readMainStreamInfo)
1725 return QByteArray();
1728 if (readMainStreamInfo) {
1729 readMainStreamsInfo();
1732 QByteArray inflatedData;
1734 quint64 startPos = 32 + packPos;
1735 for (
int i = 0; i < folders.
size(); i++) {
1736 const Folder *folder = folders.
at(i);
1737 quint64 unpackSize64 = folder->getUnpackSize();
1738 size_t unpackSize = (size_t)unpackSize64;
1739 if (unpackSize != unpackSize64) {
1740 qCDebug(KArchiveLog) <<
"unsupported";
1741 return inflatedData;
1745 quint32 mainCoderIndex = 0;
1746 QList<int> outStreamIndexed;
1747 int outStreamIndex = 0;
1748 for (
int j = 0; j < folder->folderInfos.
size(); j++) {
1749 const Folder::FolderInfo *info = folder->folderInfos[j];
1750 for (
int k = 0; k < info->numOutStreams; k++, outStreamIndex++) {
1751 if (folder->findBindPairForOutStream(outStreamIndex) < 0) {
1752 outStreamIndexed.
append(outStreamIndex);
1759 if (!outStreamIndexed.
isEmpty()) {
1760 folder->findOutStream(outStreamIndexed[0], mainCoderIndex, temp);
1763 quint32 startInIndex = folder->getCoderInStreamIndex(mainCoderIndex);
1764 quint32 startOutIndex = folder->getCoderOutStreamIndex(mainCoderIndex);
1766 Folder::FolderInfo *mainCoder = folder->folderInfos[mainCoderIndex];
1768 QList<int> seqInStreams;
1769 QList<quint32> coderIndexes;
1770 seqInStreams.
reserve(mainCoder->numInStreams);
1771 coderIndexes.
reserve(mainCoder->numInStreams);
1772 for (
int j = 0; j < (int)mainCoder->numInStreams; j++) {
1775 getInStream(folder, startInIndex + j, seqInStream, coderIndex);
1776 seqInStreams.
append(seqInStream);
1777 coderIndexes.
append(coderIndex);
1780 QList<int> seqOutStreams;
1781 seqOutStreams.
reserve(mainCoder->numOutStreams);
1782 for (
int j = 0; j < (int)mainCoder->numOutStreams; j++) {
1784 getOutStream(folder, startOutIndex + j, seqOutStream);
1785 seqOutStreams.
append(seqOutStream);
1788 QList<QByteArray> datas;
1789 for (
int j = 0; j < (int)mainCoder->numInStreams; j++) {
1790 quint64 size = packSizes[j + i];
1791 std::unique_ptr<char[]> encodedBuffer(
new char[size]);
1792 QIODevice *dev = q->
device();
1793 dev->
seek(startPos);
1794 quint64 n = dev->
read(encodedBuffer.get(), size);
1796 qCDebug(KArchiveLog) <<
"Failed read next size, should read " << size <<
", read " << n;
1797 return inflatedData;
1799 QByteArray deflatedData(encodedBuffer.get(), size);
1800 datas.
append(deflatedData);
1806 QList<QByteArray> inflatedDatas;
1807 QByteArray deflatedData;
1808 for (
int j = 0; j < seqInStreams.
size(); ++j) {
1811 if ((quint32)j != mainCoderIndex) {
1812 coderIndex = coderIndexes[j];
1814 coderIndex = mainCoderIndex;
1817 Folder::FolderInfo *coder = folder->folderInfos[coderIndex];
1818 deflatedData = datas[seqInStreams[j]];
1820 KFilterBase *
filter = getFilter(folder, coder, coderIndex, deflatedData, inflatedDatas);
1821 if (coder->methodID == k_BCJ2) {
1826 if (coder->methodID == k_AES) {
1829 return QByteArray();
1834 QByteArray outBuffer;
1836 outBuffer.
resize(unpackSize);
1839 QByteArray inflatedDataTmp;
1840 while (result != KFilterBase::End && result != KFilterBase::Error && !
filter->inBufferEmpty()) {
1842 result =
filter->uncompress();
1843 if (result == KFilterBase::Error) {
1844 qCDebug(KArchiveLog) <<
" decode error";
1847 return QByteArray();
1849 int uncompressedBytes = outBuffer.
size() -
filter->outBufferAvailable();
1852 inflatedDataTmp.
append(outBuffer.
data(), uncompressedBytes);
1854 if (result == KFilterBase::End) {
1860 if (result != KFilterBase::End && !
filter->inBufferEmpty()) {
1861 qCDebug(KArchiveLog) <<
"decode failed result" << result;
1864 return QByteArray();
1870 inflatedDatas.
append(inflatedDataTmp);
1873 QByteArray inflated;
1874 for (
const QByteArray &data : std::as_const(inflatedDatas)) {
1878 inflatedDatas.
clear();
1880 if (folder->unpackCRCDefined) {
1881 if ((
size_t)inflated.
size() < unpackSize) {
1882 qCDebug(KArchiveLog) <<
"wrong crc size data";
1883 return QByteArray();
1885 quint32 crc = crc32(0, (Bytef *)(inflated.
data()), unpackSize);
1886 if (crc != folder->unpackCRC) {
1887 qCDebug(KArchiveLog) <<
"wrong crc";
1888 return QByteArray();
1892 inflatedData.
append(inflated);
1895 return inflatedData;
1900void K7Zip::K7ZipPrivate::createItemsFromEntities(
const KArchiveDirectory *dir,
const QString &path, QByteArray &data)
1902 const QStringList l =
dir->entries();
1904 for (; it != l.
end(); ++it) {
1905 const KArchiveEntry *entry =
dir->entry((*it));
1907 FileInfo *fileInfo =
new FileInfo;
1908 fileInfo->attribDefined =
true;
1910 fileInfo->path =
path + entry->
name();
1911 mTimesDefined.
append(
true);
1915 const K7ZipFileEntry *fileEntry =
static_cast<const K7ZipFileEntry *
>(entry);
1917 fileInfo->attributes = FILE_ATTRIBUTE_ARCHIVE;
1918 fileInfo->attributes |= FILE_ATTRIBUTE_UNIX_EXTENSION + ((entry->
permissions() & 0xFFFF) << 16);
1919 fileInfo->size = fileEntry->
size();
1921 if (fileInfo->size > 0) {
1922 fileInfo->hasStream =
true;
1924 unpackSizes.
append(fileInfo->size);
1925 }
else if (!symLink.
isEmpty()) {
1926 fileInfo->hasStream =
true;
1930 fileInfos.
append(fileInfo);
1932 fileInfo->attributes = FILE_ATTRIBUTE_DIRECTORY;
1933 fileInfo->attributes |= FILE_ATTRIBUTE_UNIX_EXTENSION + ((entry->
permissions() & 0xFFFF) << 16);
1934 fileInfo->isDir =
true;
1935 fileInfos.
append(fileInfo);
1936 createItemsFromEntities((KArchiveDirectory *)entry, path + (*it) + QLatin1Char(
'/'), data);
1941void K7Zip::K7ZipPrivate::writeByte(
unsigned char b)
1947void K7Zip::K7ZipPrivate::writeNumber(quint64 value)
1952 for (i = 0; i < 8; i++) {
1953 if (value < ((quint64(1) << (7 * (i + 1))))) {
1954 firstByte |= (int)(value >> (8 * i));
1960 writeByte(firstByte);
1961 for (; i > 0; i--) {
1962 writeByte((
int)value);
1967void K7Zip::K7ZipPrivate::writeBoolVector(
const QList<bool> &boolVector)
1971 for (
int i = 0; i < boolVector.
size(); i++) {
1972 if (boolVector[i]) {
1987void K7Zip::K7ZipPrivate::writeUInt32(quint32 value)
1989 for (
int i = 0; i < 4; i++) {
1990 writeByte((
unsigned char)value);
1995void K7Zip::K7ZipPrivate::writeUInt64(quint64 value)
1997 for (
int i = 0; i < 8; i++) {
1998 writeByte((
unsigned char)value);
2003void K7Zip::K7ZipPrivate::writeAlignedBoolHeader(
const QList<bool> &v,
int numDefined,
int type,
unsigned itemSize)
2005 const unsigned bvSize = (numDefined == v.
size()) ? 0 : ((unsigned)v.
size() + 7) / 8;
2006 const quint64 dataSize = (quint64)numDefined * itemSize + bvSize + 2;
2010 writeNumber(dataSize);
2011 if (numDefined == v.
size()) {
2020void K7Zip::K7ZipPrivate::writeUInt64DefVector(
const QList<quint64> &v,
const QList<bool> &defined,
int type)
2024 for (
int i = 0; i < defined.
size(); i++) {
2030 if (numDefined == 0) {
2034 writeAlignedBoolHeader(defined, numDefined, type, 8);
2036 for (
int i = 0; i < defined.
size(); i++) {
2043void K7Zip::K7ZipPrivate::writeHashDigests(
const QList<bool> &digestsDefined,
const QList<quint32> &digests)
2047 for (i = 0; i < digestsDefined.
size(); i++) {
2048 if (digestsDefined[i]) {
2053 if (numDefined == 0) {
2058 if (numDefined == digestsDefined.
size()) {
2062 writeBoolVector(digestsDefined);
2065 for (i = 0; i < digests.
size(); i++) {
2066 if (digestsDefined[i]) {
2067 writeUInt32(digests[i]);
2072void K7Zip::K7ZipPrivate::writePackInfo(quint64 dataOffset, QList<quint64> &packedSizes, QList<bool> &packedCRCsDefined, QList<quint32> &packedCRCs)
2077 writeByte(kPackInfo);
2078 writeNumber(dataOffset);
2079 writeNumber(packedSizes.
size());
2082 for (
int i = 0; i < packedSizes.
size(); i++) {
2083 writeNumber(packedSizes[i]);
2086 writeHashDigests(packedCRCsDefined, packedCRCs);
2091void K7Zip::K7ZipPrivate::writeFolder(
const Folder *folder)
2093 writeNumber(folder->folderInfos.
size());
2094 for (
int i = 0; i < folder->folderInfos.
size(); i++) {
2095 const Folder::FolderInfo *info = folder->folderInfos.
at(i);
2097 size_t propsSize = info->properties.
size();
2099 quint64
id = info->methodID;
2101 for (idSize = 1; idSize <
sizeof(id); idSize++) {
2102 if ((
id >> (8 * idSize)) == 0) {
2108 for (
int t = idSize - 1; t >= 0; t--,
id >>= 8) {
2109 longID[t] = (int)(
id & 0xFF);
2113 b = (int)(idSize & 0xF);
2114 bool isComplex = !info->isSimpleCoder();
2115 b |= (isComplex ? 0x10 : 0);
2116 b |= ((propsSize != 0) ? 0x20 : 0);
2119 for (
size_t j = 0; j < idSize; ++j) {
2120 writeByte(longID[j]);
2124 writeNumber(info->numInStreams);
2125 writeNumber(info->numOutStreams);
2128 if (propsSize == 0) {
2132 writeNumber(propsSize);
2133 for (
size_t j = 0; j < propsSize; ++j) {
2134 writeByte(info->properties[j]);
2139 for (
int i = 0; i < folder->inIndexes.
size(); i++) {
2140 writeNumber(folder->inIndexes[i]);
2141 writeNumber(folder->outIndexes[i]);
2144 if (folder->packedStreams.
size() > 1) {
2145 for (
int i = 0; i < folder->packedStreams.
size(); i++) {
2146 writeNumber(folder->packedStreams[i]);
2151void K7Zip::K7ZipPrivate::writeUnpackInfo(
const QList<Folder *> &folderItems)
2157 writeByte(kUnpackInfo);
2160 writeNumber(folderItems.
size());
2163 for (
int i = 0; i < folderItems.
size(); i++) {
2164 writeFolder(folderItems[i]);
2168 writeByte(kCodersUnpackSize);
2170 for (i = 0; i < folderItems.
size(); i++) {
2171 const Folder *folder = folderItems[i];
2172 for (
int j = 0; j < folder->unpackSizes.
size(); j++) {
2173 writeNumber(folder->unpackSizes.
at(j));
2177 QList<bool> unpackCRCsDefined;
2178 QList<quint32> unpackCRCs;
2181 for (i = 0; i < folderItems.
size(); i++) {
2182 const Folder *folder = folderItems[i];
2183 unpackCRCsDefined.
append(folder->unpackCRCDefined);
2184 unpackCRCs.
append(folder->unpackCRC);
2186 writeHashDigests(unpackCRCsDefined, unpackCRCs);
2191void K7Zip::K7ZipPrivate::writeSubStreamsInfo(
const QList<quint64> &unpackSizes,
const QList<bool> &digestsDefined,
const QList<quint32> &digests)
2193 writeByte(kSubStreamsInfo);
2195 for (
int i = 0; i < numUnpackStreamsInFolders.
size(); i++) {
2196 if (numUnpackStreamsInFolders.
at(i) != 1) {
2197 writeByte(kNumUnpackStream);
2198 for (
int j = 0; j < numUnpackStreamsInFolders.
size(); j++) {
2199 writeNumber(numUnpackStreamsInFolders.
at(j));
2205 bool needFlag =
true;
2207 for (
int i = 0; i < numUnpackStreamsInFolders.
size(); i++) {
2208 for (quint32 j = 0; j < numUnpackStreamsInFolders.
at(i); j++) {
2209 if (j + 1 != numUnpackStreamsInFolders.
at(i)) {
2214 writeNumber(unpackSizes[index]);
2220 QList<bool> digestsDefined2;
2221 QList<quint32> digests2;
2223 int digestIndex = 0;
2224 for (
int i = 0; i < folders.
size(); i++) {
2225 int numSubStreams = (int)numUnpackStreamsInFolders.
at(i);
2226 if (numSubStreams == 1 && folders.
at(i)->unpackCRCDefined) {
2229 for (
int j = 0; j < numSubStreams; j++, digestIndex++) {
2230 digestsDefined2.
append(digestsDefined[digestIndex]);
2231 digests2.
append(digests[digestIndex]);
2235 writeHashDigests(digestsDefined2, digests2);
2239QByteArray K7Zip::K7ZipPrivate::encodeStream(QList<quint64> &packSizes, QList<Folder *> &folds)
2242 folder->unpackCRCDefined =
true;
2243 folder->unpackCRC = crc32(0, (Bytef *)(header.
data()), header.
size());
2246 Folder::FolderInfo *info =
new Folder::FolderInfo();
2247 info->numInStreams = 1;
2248 info->numOutStreams = 1;
2249 info->methodID = k_LZMA2;
2251 quint32 dictSize = header.
size();
2252 const quint32 kMinReduceSize = (1 << 16);
2253 if (dictSize < kMinReduceSize) {
2254 dictSize = kMinReduceSize;
2258 for (dict = 0; dict < 40; dict++) {
2259 if (dictSize <= lzma2_dic_size_from_prop(dict)) {
2264 info->properties.
append(dict);
2265 folder->folderInfos.
append(info);
2270 QByteArray encodedData;
2273 QBuffer inBuffer(&enc);
2275 KCompressionDevice flt(&inBuffer,
false, KCompressionDevice::Xz);
2278 KFilterBase *
filter = flt.filterBase();
2282 const int ret = flt.write(header);
2283 if (ret != header.
size()) {
2284 qCDebug(KArchiveLog) <<
"write error write " << ret <<
"expected" << header.
size();
2289 encodedData = inBuffer.
data();
2296void K7Zip::K7ZipPrivate::writeHeader(quint64 &headerOffset)
2298 quint64 packedSize = 0;
2299 for (
int i = 0; i < packSizes.
size(); ++i) {
2300 packedSize += packSizes[i];
2303 headerOffset = packedSize;
2310 writeByte(kMainStreamsInfo);
2311 writePackInfo(0, packSizes, packCRCsDefined, packCRCs);
2313 writeUnpackInfo(folders);
2315 QList<quint64> unpackFileSizes;
2316 QList<bool> digestsDefined;
2317 QList<quint32> digests;
2318 for (
int i = 0; i < fileInfos.
size(); i++) {
2319 const FileInfo *file = fileInfos.
at(i);
2320 if (!file->hasStream) {
2323 unpackFileSizes.
append(file->size);
2324 digestsDefined.
append(file->crcDefined);
2325 digests.
append(file->crc);
2328 writeSubStreamsInfo(unpackSizes, digestsDefined, digests);
2337 writeByte(kFilesInfo);
2338 writeNumber(fileInfos.
size());
2342 QList<bool> emptyStreamVector;
2343 int numEmptyStreams = 0;
2344 for (
int i = 0; i < fileInfos.
size(); i++) {
2345 if (fileInfos.
at(i)->hasStream) {
2346 emptyStreamVector.
append(
false);
2348 emptyStreamVector.
append(
true);
2353 if (numEmptyStreams > 0) {
2354 writeByte(kEmptyStream);
2355 writeNumber(((
unsigned)emptyStreamVector.
size() + 7) / 8);
2356 writeBoolVector(emptyStreamVector);
2358 QList<bool> emptyFileVector;
2359 QList<bool> antiVector;
2360 int numEmptyFiles = 0;
2361 int numAntiItems = 0;
2362 for (
int i = 0; i < fileInfos.
size(); i++) {
2363 const FileInfo *file = fileInfos.
at(i);
2364 if (!file->hasStream) {
2365 emptyFileVector.
append(!file->isDir);
2368 bool isAnti = (i < this->isAnti.size() && this->isAnti[i]);
2369 antiVector.
append(isAnti);
2377 if (numEmptyFiles > 0) {
2378 writeByte(kEmptyFile);
2379 writeNumber(((
unsigned)emptyFileVector.
size() + 7) / 8);
2380 writeBoolVector(emptyFileVector);
2383 if (numAntiItems > 0) {
2385 writeNumber(((
unsigned)antiVector.
size() + 7) / 8);
2386 writeBoolVector(antiVector);
2395 size_t namesDataSize = 0;
2396 for (
int i = 0; i < fileInfos.
size(); i++) {
2397 const QString &
name = fileInfos.
at(i)->path;
2402 if (numDefined > 0) {
2407 writeNumber(namesDataSize);
2409 for (
int i = 0; i < fileInfos.
size(); i++) {
2410 const QString &
name = fileInfos.
at(i)->path;
2413 writeByte((
unsigned char)c);
2414 writeByte((
unsigned char)(c >> 8));
2423 writeUInt64DefVector(mTimes, mTimesDefined, kMTime);
2425 writeUInt64DefVector(startPositions, startPositionsDefined, kStartPos);
2429 QList<bool> boolVector;
2432 for (
int i = 0; i < fileInfos.
size(); i++) {
2433 bool defined = fileInfos.
at(i)->attribDefined;
2434 boolVector.
append(defined);
2440 if (numDefined > 0) {
2441 writeAlignedBoolHeader(boolVector, numDefined, kAttributes, 4);
2442 for (
int i = 0; i < fileInfos.
size(); i++) {
2443 const FileInfo *file = fileInfos.
at(i);
2444 if (file->attribDefined) {
2445 writeUInt32(file->attributes);
2455static void setUInt32(
unsigned char *p, quint32 d)
2457 for (
int i = 0; i < 4; i++, d >>= 8) {
2462static void setUInt64(
unsigned char *p, quint64 d)
2464 for (
int i = 0; i < 8; i++, d >>= 8) {
2465 p[i] = (
unsigned char)d;
2469void K7Zip::K7ZipPrivate::writeStartHeader(
const quint64 nextHeaderSize,
const quint32 nextHeaderCRC,
const quint64 nextHeaderOffset)
2471 unsigned char buf[24];
2472 setUInt64(buf + 4, nextHeaderOffset);
2473 setUInt64(buf + 12, nextHeaderSize);
2474 setUInt32(buf + 20, nextHeaderCRC);
2475 setUInt32(buf, crc32(0, (Bytef *)(buf + 4), 20));
2479void K7Zip::K7ZipPrivate::writeSignature()
2481 unsigned char buf[8];
2482 memcpy(buf, k7zip_signature, 6);
2503 qint64 n = dev->
read(header, 32);
2509 for (
int i = 0; i < 6; ++i) {
2510 if ((
unsigned char)header[i] != k7zip_signature[i]) {
2517 int major = header[6];
2518 int minor = header[7];
2526 quint32 startHeaderCRC = GetUi32(header + 8);
2527 quint64 nextHeaderOffset = GetUi64(header + 12);
2528 quint64 nextHeaderSize = GetUi64(header + 20);
2529 quint32 nextHeaderCRC = GetUi32(header + 28);
2531 quint32 crc = crc32(0, (Bytef *)(header + 0xC), 20);
2533 if (crc != startHeaderCRC) {
2538 if (nextHeaderSize == 0) {
2542 if (nextHeaderSize > (quint64)0xFFFFFFFF) {
2547 if ((qint64)nextHeaderOffset < 0) {
2552 dev->
seek(nextHeaderOffset + 32);
2555 inBuffer.
resize(nextHeaderSize);
2558 if (n != (qint64)nextHeaderSize) {
2559 setErrorString(tr(
"Failed read next header size; should read %1, read %2").arg(nextHeaderSize).arg(n));
2562 d->buffer = inBuffer.
data();
2564 d->end = nextHeaderSize;
2566 d->headerSize = 32 + nextHeaderSize;
2569 crc = crc32(0, (Bytef *)(d->buffer), (quint32)nextHeaderSize);
2571 if (crc != nextHeaderCRC) {
2576 int type = d->readByte();
2578 if (type != kHeader) {
2579 if (type != kEncodedHeader) {
2584 decodedData = d->readAndDecodePackedStreams();
2586 int external = d->readByte();
2587 if (external != 0) {
2588 int dataIndex = (int)d->readNumber();
2589 if (dataIndex < 0) {
2594 d->end = decodedData.
size();
2602 type = d->readByte();
2603 if (type != kHeader) {
2610 type = d->readByte();
2612 if (type == kArchiveProperties) {
2618 if (type == kAdditionalStreamsInfo) {
2624 if (type == kMainStreamsInfo) {
2625 if (!d->readMainStreamsInfo()) {
2626 setErrorString(tr(
"Error while reading main streams information"));
2635 type = d->readByte();
2637 for (
int i = 0; i < d->folders.size(); ++i) {
2638 Folder *folder = d->folders.at(i);
2639 d->unpackSizes.
append(folder->getUnpackSize());
2640 d->digestsDefined.append(folder->unpackCRCDefined);
2641 d->digests.append(folder->unpackCRC);
2649 if (type != kFilesInfo) {
2655 int numFiles = d->readNumber();
2656 for (
int i = 0; i < numFiles; ++i) {
2657 d->fileInfos.append(
new FileInfo);
2663 int numEmptyStreams = 0;
2666 quint64 type = d->readByte();
2671 quint64 size = d->readNumber();
2673 size_t ppp = d->pos;
2675 bool addPropIdToList =
true;
2676 bool isKnownType =
true;
2678 if (type > ((quint32)1 << 30)) {
2679 isKnownType =
false;
2682 case kEmptyStream: {
2683 d->readBoolVector(numFiles, emptyStreamVector);
2684 for (
int i = 0; i < emptyStreamVector.
size(); ++i) {
2685 if (emptyStreamVector[i]) {
2693 d->readBoolVector(numEmptyStreams, emptyFileVector);
2696 d->readBoolVector(numEmptyStreams, antiFileVector);
2699 if (!d->readUInt64DefVector(numFiles, d->cTimes, d->cTimesDefined)) {
2704 if (!d->readUInt64DefVector(numFiles, d->aTimes, d->aTimesDefined)) {
2709 if (!d->readUInt64DefVector(numFiles, d->mTimes, d->mTimesDefined)) {
2715 int external = d->readByte();
2716 if (external != 0) {
2717 int dataIndex = d->readNumber();
2718 if (dataIndex < 0 /*|| dataIndex >= dataVector->Size()*/) {
2719 qCDebug(KArchiveLog) <<
"wrong data index";
2726 for (
int i = 0; i < numFiles; i++) {
2727 name = d->readString();
2728 d->fileInfos.
at(i)->path = name;
2734 d->readBoolVector2(numFiles, attributesAreDefined);
2735 int external = d->readByte();
2736 if (external != 0) {
2737 int dataIndex = d->readNumber();
2738 if (dataIndex < 0) {
2739 qCDebug(KArchiveLog) <<
"wrong data index";
2745 for (
int i = 0; i < numFiles; i++) {
2746 FileInfo *fileInfo = d->fileInfos.at(i);
2747 fileInfo->attribDefined = attributesAreDefined[i];
2748 if (fileInfo->attribDefined) {
2749 fileInfo->attributes = d->readUInt32();
2755 if (!d->readUInt64DefVector(numFiles, d->startPositions, d->startPositionsDefined)) {
2761 for (quint64 i = 0; i < size; i++) {
2762 if (d->readByte() != 0) {
2767 addPropIdToList =
false;
2771 addPropIdToList = isKnownType =
false;
2776 if (addPropIdToList) {
2777 d->fileInfoPopIDs.append(type);
2780 d->skipData(d->readNumber());
2783 bool checkRecordsSize = (major > 0 || minor > 2);
2784 if (checkRecordsSize && d->pos - ppp != size) {
2786 "(checkRecordsSize: %1, d->pos - ppp: %2, size: %3)")
2787 .arg(checkRecordsSize)
2794 int emptyFileIndex = 0;
2797 int numAntiItems = 0;
2799 if (emptyStreamVector.
isEmpty()) {
2800 emptyStreamVector.
fill(
false, numFiles);
2803 if (antiFileVector.
isEmpty()) {
2804 antiFileVector.
fill(
false, numEmptyStreams);
2806 if (emptyFileVector.
isEmpty()) {
2807 emptyFileVector.
fill(
false, numEmptyStreams);
2810 for (
int i = 0; i < numEmptyStreams; i++) {
2811 if (antiFileVector[i]) {
2816 d->outData = d->readAndDecodePackedStreams(
false);
2819 int filesWithoutNames = 0;
2827 for (
int i = 0; i < numFiles; i++) {
2828 FileInfo *fileInfo = d->fileInfos.at(i);
2832 if (fileInfo->path.
isEmpty()) {
2834 filesWithoutNames++;
2835 fileInfo->path = QStringLiteral(
"%1_%2").
arg(defaultBaseName).
arg(filesWithoutNames);
2837 fileInfo->path = defaultBaseName;
2842 fileInfo->hasStream = !emptyStreamVector[i];
2843 if (fileInfo->hasStream) {
2844 fileInfo->isDir =
false;
2846 fileInfo->size = d->unpackSizes[sizeIndex];
2847 fileInfo->crc = d->digests[sizeIndex];
2848 fileInfo->crcDefined = d->digestsDefined[sizeIndex];
2851 fileInfo->isDir = !emptyFileVector[emptyFileIndex];
2852 isAnti = antiFileVector[emptyFileIndex];
2855 fileInfo->crcDefined =
false;
2857 if (numAntiItems != 0) {
2858 d->isAnti.append(isAnti);
2862 bool symlink =
false;
2863 if (fileInfo->attributes & FILE_ATTRIBUTE_UNIX_EXTENSION) {
2864 access = fileInfo->attributes >> 16;
2865 if ((access & QT_STAT_MASK) == QT_STAT_LNK) {
2869 if (fileInfo->isDir) {
2870 access = S_IFDIR | 0755;
2877 if (!fileInfo->isDir) {
2879 oldPos += fileInfo->size;
2886 entryName = fileInfo->path;
2888 entryName = fileInfo->path.
mid(index + 1);
2890 Q_ASSERT(!entryName.
isEmpty());
2893 if (d->mTimesDefined.size() > i && d->mTimesDefined[i]) {
2894 mTime = KArchivePrivate::time_tToDateTime(toTimeT(d->mTimes[i]));
2896 mTime = KArchivePrivate::time_tToDateTime(time(
nullptr));
2899 if (fileInfo->isDir) {
2909 e =
new K7ZipFileEntry(
this,
2921 e =
new K7ZipFileEntry(
this, entryName, access, mTime,
rootDir()->user(),
rootDir()->group(), target, 0, 0,
nullptr);
2932 (void)d->addEntryV2(e);
2954 Folder *folder =
new Folder();
2956 folder->unpackSizes.
clear();
2957 folder->unpackSizes.
append(d->outData.size());
2959 Folder::FolderInfo *info =
new Folder::FolderInfo();
2961 info->numInStreams = 1;
2962 info->numOutStreams = 1;
2963 info->methodID = k_LZMA2;
2965 quint32 dictSize = d->outData.size();
2967 const quint32 kMinReduceSize = (1 << 16);
2968 if (dictSize < kMinReduceSize) {
2969 dictSize = kMinReduceSize;
2974 for (dict = 0; dict < 40; dict++) {
2975 if (dictSize <= lzma2_dic_size_from_prop(dict)) {
2979 info->properties.
append(dict);
2981 folder->folderInfos.
append(info);
2982 d->folders.append(folder);
2986 d->createItemsFromEntities(dir,
QString(), data);
2989 folder->unpackCRCDefined =
true;
2990 folder->unpackCRC = crc32(0, (Bytef *)(d->outData.data()), d->outData.size());
2994 if (!d->outData.isEmpty()) {
3005 static_cast<KXzFilter *
>(filter)->init(
QIODevice::WriteOnly, KXzFilter::LZMA2, info->properties);
3007 const int ret = flt.
write(d->outData);
3008 if (ret != d->outData.size()) {
3014 encodedData = inBuffer.
data();
3019 int numUnpackStream = 0;
3020 for (
int i = 0; i < d->fileInfos.size(); ++i) {
3021 if (d->fileInfos.at(i)->hasStream) {
3025 d->numUnpackStreamsInFolders.
append(numUnpackStream);
3027 quint64 headerOffset;
3028 d->writeHeader(headerOffset);
3035 encodedStream = d->encodeStream(packSizes, folders);
3045 d->writeByte(kEncodedHeader);
3048 d->writePackInfo(headerOffset, packSizes, emptyDefined, emptyCrcs);
3049 d->writeUnpackInfo(folders);
3051 for (
int i = 0; i < packSizes.
size(); i++) {
3052 headerOffset += packSizes.
at(i);
3054 qDeleteAll(folders);
3058 quint64 nextHeaderSize = d->header.size();
3059 quint32 nextHeaderCRC = crc32(0, (Bytef *)(d->header.data()), d->header.size());
3060 quint64 nextHeaderOffset = headerOffset;
3063 d->writeSignature();
3064 d->writeStartHeader(nextHeaderSize, nextHeaderCRC, nextHeaderOffset);
3067 device()->
write(d->header.data(), d->header.size());
3074 d->m_currentFile->setSize(size);
3075 d->m_currentFile =
nullptr;
3082 if (!d->m_currentFile) {
3087 if (d->m_currentFile->position() == d->outData.size()) {
3088 d->outData.append(data, size);
3090 d->outData.remove(d->m_currentFile->position(), d->m_currentFile->size());
3091 d->outData.insert(d->m_currentFile->position(), data, size);
3107 setErrorString(tr(
"Application error: 7-Zip file must be open before being written into"));
3108 qCWarning(KArchiveLog) <<
"doPrepareWriting failed: !isOpen()";
3113 setErrorString(tr(
"Application error: attempted to write into non-writable 7-Zip file"));
3114 qCWarning(KArchiveLog) <<
"doPrepareWriting failed: !(mode() & QIODevice::WriteOnly)";
3134 new K7ZipFileEntry(
this,
fileName, perm, mtime, user, group,
QString() , d->outData.
size(), 0 , d->outData);
3138 d->m_entryList << e;
3139 d->m_currentFile = e;
3157 setErrorString(tr(
"Application error: 7-Zip file must be open before being written into"));
3158 qCWarning(KArchiveLog) <<
"doWriteDir failed: !isOpen()";
3179 dirName = name.
mid(i + 1);
3197 setErrorString(tr(
"Application error: 7-Zip file must be open before being written into"));
3198 qCWarning(KArchiveLog) <<
"doWriteSymLink failed: !isOpen()";
3203 setErrorString(tr(
"Application error: attempted to write into non-writable 7-Zip file"));
3204 qCWarning(KArchiveLog) <<
"doWriteSymLink failed: !(mode() & QIODevice::WriteOnly)";
3220 K7ZipFileEntry *e =
new K7ZipFileEntry(
this,
fileName, perm, mtime, user, group, target, 0, 0,
nullptr);
3221 d->outData.append(encodedTarget);
3227 d->m_entryList << e;
3232void K7Zip::virtual_hook(
int id,
void *data)
3234 KArchive::virtual_hook(
id, data);
A class for reading / writing p7zip archives.
bool doWriteDir(const QString &name, const QString &user, const QString &group, mode_t perm, const QDateTime &atime, const QDateTime &mtime, const QDateTime &ctime) override
Reimplemented from KArchive.
bool doWriteData(const char *data, qint64 size) override
Reimplemented from KArchive.
void setPassword(const QString &password)
Sets the password to use for encrypted archives.
bool doFinishWriting(qint64 size) override
Reimplemented from KArchive.
bool openArchive(QIODevice::OpenMode mode) override
Opens the archive for reading.
bool closeArchive() override
Closes the archive.
bool doWriteSymLink(const QString &name, const QString &target, const QString &user, const QString &group, mode_t perm, const QDateTime &atime, const QDateTime &mtime, const QDateTime &ctime) override
Reimplemented from KArchive.
~K7Zip() override
If the archive is still opened, then it will be closed automatically by the destructor.
K7Zip(const QString &filename)
Creates an instance that operates on the given filename using the compression filter associated to gi...
bool passwordNeeded() const
Whether the archive needs a password to be opened.
bool doPrepareWriting(const QString &name, const QString &user, const QString &group, qint64 size, mode_t perm, const QDateTime &atime, const QDateTime &mtime, const QDateTime &ctime) override
Reimplemented from KArchive.
Represents a directory entry in a KArchive.
bool addEntryV2(KArchiveEntry *)
const KArchiveEntry * entry(const QString &name) const
Returns the entry in the archive with the given name.
A base class for entries in an KArchive.
mode_t permissions() const
The permissions and mode flags as returned by the stat() function in st_mode.
QString user() const
User who created the file.
virtual bool isDirectory() const
Checks whether the entry is a directory.
QDateTime date() const
Creation date of the file.
QString group() const
Group of the user who created the file.
QString name() const
Name of the file without path.
QString symLinkTarget() const
Symlink if there is one.
virtual bool isFile() const
Checks whether the entry is a file.
Represents a file entry in a KArchive.
qint64 size() const
Size of the data.
qint64 position() const
Position of the data in the [uncompressed] archive.
QIODevice * device() const
The underlying device.
virtual bool close()
Closes the archive.
virtual KArchiveDirectory * rootDir()
Retrieves or create the root directory.
KArchive(const QString &fileName)
Base constructor (protected since this is a pure virtual class).
const KArchiveDirectory * directory() const
If an archive is opened for reading, then the contents of the archive can be accessed via this functi...
KArchiveDirectory * findOrCreate(const QString &path)
Ensures that path exists, create otherwise.
QIODevice::OpenMode mode() const
Returns the mode in which the archive was opened.
QString fileName() const
The name of the archive file, as passed to the constructor that takes a fileName, or an empty string ...
bool isOpen() const
Checks whether the archive is open.
void setErrorString(const QString &errorStr)
Sets error description.
A class for reading and writing compressed data onto a device (e.g.
static KFilterBase * filterForCompressionType(CompressionType type)
Call this to create the appropriate filter for the CompressionType named type.
void close() override
Close after reading or writing.
bool open(QIODevice::OpenMode mode) override
Open for reading or writing.
This is the base class for compression filters such as gzip and bzip2.
Q_SCRIPTABLE Q_NOREPLY void start()
Type type(const QSqlDatabase &db)
KIOCORE_EXPORT SimpleJob * symlink(const QString &target, const QUrl &dest, JobFlags flags=DefaultFlags)
QString normalize(QStringView str)
QString path(const QString &relativePath)
KIOCORE_EXPORT QString dir(const QString &fileClass)
QString name(StandardAction id)
const QList< QKeySequence > & end()
const QByteArray & data() const const
QByteArray & append(QByteArrayView data)
const char * constData() const const
bool isEmpty() const const
QByteArray mid(qsizetype pos, qsizetype len) const const
void reserve(qsizetype size)
void resize(qsizetype newSize, char c)
qsizetype size() const const
qint64 toSecsSinceEpoch() const const
QString cleanPath(const QString &path)
QString decodeName(const QByteArray &localFileName)
QByteArray encodeName(const QString &fileName)
QString completeBaseName() const const
QByteArray read(qint64 maxSize)
virtual bool seek(qint64 pos)
qint64 write(const QByteArray &data)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
QList< T > & fill(parameter_type value, qsizetype size)
bool isEmpty() const const
void reserve(qsizetype size)
qsizetype size() const const
QString arg(Args &&... args) const const
const QChar at(qsizetype position) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
QString left(qsizetype n) const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
qsizetype size() const const
QByteArray toLatin1() const const
QByteArray toUtf8() const const
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)