1 /*
2 SPDX-FileCopyrightText: 2009 Raphael Kubo da Costa <rakuco@FreeBSD.org>
3
4 SPDX-License-Identifier: BSD-2-Clause
5 */
6
7 #include "singlefileplugin.h"
8 #include "ark_debug.h"
9 #include "queries.h"
10
11 #include <QFile>
12 #include <QFileInfo>
13
14 #include <karchive_version.h>
15 #if KARCHIVE_VERSION >= QT_VERSION_CHECK(5, 85, 0)
16 #include <KCompressionDevice>
17 #else
18 #include <KFilterDev>
19 #endif
20 #include <KLocalizedString>
21
LibSingleFileInterface(QObject * parent,const QVariantList & args)22 LibSingleFileInterface::LibSingleFileInterface(QObject *parent, const QVariantList & args)
23 : Kerfuffle::ReadOnlyArchiveInterface(parent, args)
24 {
25 qCDebug(ARK) << "Loaded singlefile plugin";
26 }
27
~LibSingleFileInterface()28 LibSingleFileInterface::~LibSingleFileInterface()
29 {
30 }
31
extractFiles(const QVector<Kerfuffle::Archive::Entry * > & files,const QString & destinationDirectory,const Kerfuffle::ExtractionOptions & options)32 bool LibSingleFileInterface::extractFiles(const QVector<Kerfuffle::Archive::Entry*> &files, const QString &destinationDirectory, const Kerfuffle::ExtractionOptions &options)
33 {
34 Q_UNUSED(files)
35 Q_UNUSED(options)
36
37 QString outputFileName = destinationDirectory;
38 if (!destinationDirectory.endsWith(QLatin1Char('/'))) {
39 outputFileName += QLatin1Char('/');
40 }
41 outputFileName += uncompressedFileName();
42
43 outputFileName = overwriteFileName(outputFileName);
44 if (outputFileName.isEmpty()) {
45 return true;
46 }
47
48 qCDebug(ARK) << "Extracting to" << outputFileName;
49
50 QFile outputFile(outputFileName);
51 if (!outputFile.open(QIODevice::WriteOnly)) {
52 qCCritical(ARK) << "Failed to open output file" << outputFile.errorString();
53 Q_EMIT error(xi18nc("@info", "Ark could not extract <filename>%1</filename>.", outputFile.fileName()));
54
55 return false;
56 }
57
58 #if KARCHIVE_VERSION >= QT_VERSION_CHECK(5, 85, 0)
59 KCompressionDevice *device = new KCompressionDevice(filename(), KCompressionDevice::compressionTypeForMimeType(m_mimeType));
60 #else
61 KCompressionDevice *device = new KCompressionDevice(filename(), KFilterDev::compressionTypeForMimeType(m_mimeType));
62 #endif
63 if (!device) {
64 qCCritical(ARK) << "Could not create KCompressionDevice";
65 Q_EMIT error(xi18nc("@info", "Ark could not open <filename>%1</filename> for extraction.", filename()));
66
67 return false;
68 }
69
70 device->open(QIODevice::ReadOnly);
71
72 qint64 bytesRead;
73 QByteArray dataChunk(1024*16, '\0'); // 16Kb
74
75 while (true) {
76 bytesRead = device->read(dataChunk.data(), dataChunk.size());
77
78 if (bytesRead == -1) {
79 Q_EMIT error(xi18nc("@info", "There was an error while reading <filename>%1</filename> during extraction.", filename()));
80 break;
81 } else if (bytesRead == 0) {
82 break;
83 }
84
85 outputFile.write(dataChunk.data(), bytesRead);
86 }
87
88 delete device;
89
90 return true;
91 }
92
list()93 bool LibSingleFileInterface::list()
94 {
95 qCDebug(ARK) << "Listing archive contents";
96
97 Kerfuffle::Archive::Entry *e = new Kerfuffle::Archive::Entry();
98 connect(this, &QObject::destroyed, e, &QObject::deleteLater);
99 e->setProperty("fullPath", uncompressedFileName());
100 e->setProperty("compressedSize", QFileInfo(filename()).size());
101 Q_EMIT entry(e);
102
103 return true;
104 }
105
overwriteFileName(QString & filename)106 QString LibSingleFileInterface::overwriteFileName(QString& filename)
107 {
108 QString newFileName(filename);
109
110 while (QFile::exists(newFileName)) {
111 Kerfuffle::OverwriteQuery query(newFileName);
112
113 query.setMultiMode(false);
114 Q_EMIT userQuery(&query);
115 query.waitForResponse();
116
117 if ((query.responseCancelled()) || (query.responseSkip())) {
118 return QString();
119 } else if (query.responseOverwrite()) {
120 break;
121 } else if (query.responseRename()) {
122 newFileName = query.newFilename();
123 }
124 }
125
126 return newFileName;
127 }
128
uncompressedFileName() const129 const QString LibSingleFileInterface::uncompressedFileName() const
130 {
131 QString uncompressedName(QFileInfo(filename()).fileName());
132
133 // Bug 252701: For .svgz just remove the terminal "z".
134 if (uncompressedName.endsWith(QLatin1String(".svgz"), Qt::CaseInsensitive)) {
135 uncompressedName.chop(1);
136 return uncompressedName;
137 }
138
139 for (const QString & extension : std::as_const(m_possibleExtensions)) {
140 qCDebug(ARK) << extension;
141
142 if (uncompressedName.endsWith(extension, Qt::CaseInsensitive)) {
143 uncompressedName.chop(extension.size());
144 return uncompressedName;
145 }
146 }
147
148 return uncompressedName + QStringLiteral( ".uncompressed" );
149 }
150
testArchive()151 bool LibSingleFileInterface::testArchive()
152 {
153 return false;
154 }
155
156