Krita Source Code Documentation
Loading...
Searching...
No Matches
KoQuaZipStore.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2019 Boudewijn Rempt <boud@valdyas.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6#include "KoQuaZipStore.h"
7#include "KoStore_p.h"
8
9#include <StoreDebug.h>
10
11#include <zlib.h>
12#include <quazip.h>
13#include <quazipfile.h>
14#include <quazipdir.h>
15#include <quazipfileinfo.h>
16#include <quazipnewinfo.h>
17
18#include <QTemporaryFile>
19#include <QTextCodec>
20#include <QByteArray>
21#include <QBuffer>
22
23#include <KConfig>
24#include <KSharedConfig>
25#include <KConfigGroup>
26
28
31
32 QuaZip *archive {0};
33 QuaZipFile *currentFile {0};
35 bool directoryListCached {false};
36 int compressionLevel {Z_DEFAULT_COMPRESSION};
37 bool usingSaveFile {false};
38 QByteArray cache;
39 QBuffer buffer;
40};
41
42
43KoQuaZipStore::KoQuaZipStore(const QString &_filename, KoStore::Mode _mode, const QByteArray &appIdentification, bool writeMimetype)
44 : KoStore(_mode, writeMimetype)
45 , dd(new Private())
46{
47 Q_D(KoStore);
48 d->localFileName = _filename;
49 dd->archive = new QuaZip(_filename);
50 init(appIdentification);
51
52}
53
54KoQuaZipStore::KoQuaZipStore(QIODevice *dev, KoStore::Mode _mode, const QByteArray &appIdentification, bool writeMimetype)
55 : KoStore(_mode, writeMimetype)
56 , dd(new Private())
57{
58 dd->archive = new QuaZip(dev);
59 init(appIdentification);
60}
61
63{
64 Q_D(KoStore);
65
66 if (d->good && dd->currentFile && dd->currentFile->isOpen()) {
67 dd->currentFile->close();
68 }
69
70 if (!d->finalized) {
71 finalize();
72 }
73
74 delete dd->archive;
75
76 if (dd->currentFile) {
77 delete dd->currentFile;
78 }
79}
80
82{
83
84 if (enabled) {
85 dd->compressionLevel = Z_DEFAULT_COMPRESSION;
86 }
87 else {
88 dd->compressionLevel = Z_NO_COMPRESSION;
89 }
90}
91
92qint64 KoQuaZipStore::write(const char *_data, qint64 _len)
93{
94 Q_D(KoStore);
95 if (_len == 0) return 0;
96
97 if (!d->isOpen) {
98 errorStore << "KoStore: You must open before writing" << Qt::endl;
99 return 0;
100 }
101
102 if (d->mode != Write) {
103 errorStore << "KoStore: Can not write to store that is opened for reading" << Qt::endl;
104 return 0;
105 }
106
107 qint64 nwritten = dd->buffer.write(_data, _len);
108 d->size += nwritten;
109 return nwritten;
110}
111
113{
114 // If in Read mode, we can assume the directory listing won't change between invocations.
115 if(mode() == Read) {
116 if(!dd->directoryListCached) {
117 dd->directoryListCache = dd->archive->getFileNameList();
118 dd->directoryListCached = true;
119 }
120 return dd->directoryListCache;
121 }
122 else {
123 return dd->archive->getFileNameList();
124 }
125}
126
127void KoQuaZipStore::init(const QByteArray &appIdentification)
128{
129 Q_D(KoStore);
130
131 bool enableZip64 = false;
132 if (appIdentification == "application/x-krita") {
133 enableZip64 = KSharedConfig::openConfig()->group("").readEntry<bool>("UseZip64", false);
134 }
135
136 dd->archive->setDataDescriptorWritingEnabled(false);
137 dd->archive->setZip64Enabled(enableZip64);
138 dd->archive->setFileNameCodec("UTF-8");
139 dd->usingSaveFile = dd->archive->getIoDevice() && dd->archive->getIoDevice()->inherits("QSaveFile");
140 dd->archive->setAutoClose(!dd->usingSaveFile);
141
142 d->good = dd->archive->open(d->mode == Write ? QuaZip::mdCreate : QuaZip::mdUnzip);
143
144 if (!d->good) {
145 return;
146 }
147
148 if (d->mode == Write) {
149 if (d->writeMimetype) {
150 QuaZipFile f(dd->archive);
151 QuaZipNewInfo newInfo("mimetype");
152 newInfo.setPermissions(QFileDevice::ReadOwner | QFileDevice::ReadGroup | QFileDevice::ReadOther);
153 if (!f.open(QIODevice::WriteOnly, newInfo, 0, 0, 0, Z_NO_COMPRESSION)) {
154 d->good = false;
155 return;
156 }
157 f.write(appIdentification);
158 f.close();
159 }
160 }
161 else {
162 debugStore << dd->archive->getEntriesCount() << directoryList();
163 d->good = dd->archive->getEntriesCount();
164 }
165}
166
168{
169 Q_D(KoStore);
170
171 d->stream = 0;
172 if (d->good && !dd->usingSaveFile) {
173 dd->archive->close();
174 }
175 return dd->archive->getZipError() == ZIP_OK;
176
177}
178
179bool KoQuaZipStore::openWrite(const QString &name)
180{
181 Q_D(KoStore);
182 QString fixedPath = name;
183 fixedPath.replace("//", "/");
184
185 delete d->stream;
186 d->stream = 0; // Not used when writing
187
188 delete dd->currentFile;
189 dd->currentFile = new QuaZipFile(dd->archive);
190 QuaZipNewInfo newInfo(fixedPath);
191 newInfo.setPermissions(QFileDevice::ReadOwner | QFileDevice::ReadGroup | QFileDevice::ReadOther);
192 bool r = dd->currentFile->open(QIODevice::WriteOnly, newInfo, 0, 0, Z_DEFLATED, dd->compressionLevel);
193 if (!r) {
194 qWarning() << "Could not open" << name << dd->currentFile->getZipError();
195 }
196
197 dd->cache = QByteArray();
198 dd->buffer.setBuffer(&dd->cache);
199 dd->buffer.open(QBuffer::WriteOnly);
200
201 return r;
202}
203
204bool KoQuaZipStore::openRead(const QString &name)
205{
206 Q_D(KoStore);
207
208 QString fixedPath = name;
209 fixedPath.replace("//", "/");
210
211 delete d->stream;
212 d->stream = 0;
213 delete dd->currentFile;
214 dd->currentFile = 0;
215
216 if (!currentPath().isEmpty() && !fixedPath.startsWith(currentPath())) {
217 fixedPath = currentPath() + '/' + fixedPath;
218 }
219
220 if (!d->substituteThis.isEmpty()) {
221 fixedPath = fixedPath.replace(d->substituteThis, d->substituteWith);
222 }
223
224 if (!dd->archive->setCurrentFile(fixedPath)) {
225 qWarning() << "\t\tCould not set current file" << dd->archive->getZipError() << fixedPath;
226 return false;
227 }
228
229 dd->currentFile = new QuaZipFile(dd->archive);
230 if (!dd->currentFile->open(QIODevice::ReadOnly)) {
231 qWarning() << "\t\t\tBut could not open!!!" << dd->archive->getZipError();
232 return false;
233 }
234 d->stream = dd->currentFile;
235 d->size = dd->currentFile->size();
236 return true;
237}
238
240{
241 Q_D(KoStore);
242
243 bool r = true;
244 if (dd->currentFile->write(dd->cache) != dd->cache.size()) {
245 // write() returns number of bytes written, or -1 in case of error
246 // let's allow write 0 bytes in the cache, when needed
247 qWarning() << "Could not write buffer to the file";
248 r = false;
249 }
250 dd->buffer.close();
251 dd->currentFile->close();
252 d->stream = 0;
253 return (r && dd->currentFile->getZipError() == ZIP_OK);
254}
255
257{
258 Q_D(KoStore);
259 d->stream = 0;
260 return true;
261}
262
263bool KoQuaZipStore::enterRelativeDirectory(const QString & /*path*/)
264{
265 return true;
266}
267
269{
270 QString fixedPath = path;
271 fixedPath.replace("//", "/");
272
273 if (fixedPath.isEmpty()) {
274 fixedPath = "/";
275 }
276
277 QuaZipDir currentDir (dd->archive, fixedPath);
278
279 return currentDir.exists();
280}
281
282bool KoQuaZipStore::fileExists(const QString &absPath) const
283{
284 Q_D(const KoStore);
285
286 QString fixedPath = absPath;
287 fixedPath.replace("//", "/");
288
289 if (!d->substituteThis.isEmpty()) {
290 fixedPath = fixedPath.replace(d->substituteThis, d->substituteWith);
291 }
292
293 return directoryList().contains(fixedPath);
294}
#define debugStore
Definition StoreDebug.h:15
#define errorStore
Definition StoreDebug.h:17
KoQuaZipStore(const QString &_filename, Mode _mode, const QByteArray &appIdentification, bool writeMimetype=true)
QStringList directoryList() const override
bool enterRelativeDirectory(const QString &dirName) override
bool openRead(const QString &name) override
bool fileExists(const QString &absPath) const override
void init(const QByteArray &appIdentification)
bool closeRead() override
bool doFinalize() override
~KoQuaZipStore() override
void setCompressionEnabled(bool enabled) override
bool closeWrite() override
qint64 write(const char *_data, qint64 _len) override
bool enterAbsoluteDirectory(const QString &path) override
const QScopedPointer< Private > dd
bool openWrite(const QString &name) override
@ Read
Definition KoStore.h:29
@ Write
Definition KoStore.h:29
Mode mode() const
Definition KoStore.cpp:420
QString currentPath() const
Definition KoStore.cpp:281
bool finalize()
Definition KoStore.cpp:395