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