File indexing completed on 2024-05-12 15:59:59
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 1998, 1999 Torben Weis <weis@kde.org> 0003 SPDX-FileCopyrightText: 2000-2002 David Faure <faure@kde.org>, Werner Trobin <trobin@kde.org> 0004 SPDX-FileCopyrightText: 2010 C. Boemann <cbo@boemann.dk> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "KoStore.h" 0010 #include "KoStore_p.h" 0011 0012 #include "KoQuaZipStore.h" 0013 #include "KoDirectoryStore.h" 0014 0015 #include <QBuffer> 0016 #include <QFileInfo> 0017 #include <QFile> 0018 0019 #include <QUrl> 0020 #include <StoreDebug.h> 0021 0022 #include <KConfig> 0023 #include <KSharedConfig> 0024 #include <KConfigGroup> 0025 0026 0027 #define DefaultFormat KoStore::Zip 0028 0029 static KoStore::Backend determineBackend(QIODevice *dev) 0030 { 0031 unsigned char buf[5]; 0032 if (dev->read((char *)buf, 4) < 4) 0033 return DefaultFormat; // will create a "bad" store (bad()==true) 0034 if (buf[0] == 'P' && buf[1] == 'K' && buf[2] == 3 && buf[3] == 4) 0035 return KoStore::Zip; 0036 return DefaultFormat; // fallback 0037 } 0038 0039 KoStore* KoStore::createStore(const QString& fileName, Mode mode, const QByteArray & appIdentification, Backend backend, bool writeMimetype) 0040 { 0041 if (backend == Auto) { 0042 if (mode == KoStore::Write) 0043 backend = DefaultFormat; 0044 else { 0045 QFileInfo inf(fileName); 0046 if (inf.isDir()) 0047 backend = Directory; 0048 else { 0049 QFile file(fileName); 0050 if (file.open(QIODevice::ReadOnly)) 0051 backend = determineBackend(&file); 0052 else 0053 backend = DefaultFormat; // will create a "bad" store (bad()==true) 0054 } 0055 } 0056 } 0057 switch (backend) { 0058 case Zip: 0059 return new KoQuaZipStore(fileName, mode, appIdentification, writeMimetype); 0060 case Directory: 0061 return new KoDirectoryStore(fileName /* should be a dir name.... */, mode, writeMimetype); 0062 default: 0063 warnStore << "Unsupported backend requested for KoStore : " << backend; 0064 return 0; 0065 } 0066 } 0067 0068 KoStore* KoStore::createStore(QIODevice *device, Mode mode, const QByteArray & appIdentification, Backend backend, bool writeMimetype) 0069 { 0070 if (backend == Auto) { 0071 if (mode == KoStore::Write) 0072 backend = DefaultFormat; 0073 else { 0074 if (device->open(QIODevice::ReadOnly)) { 0075 backend = determineBackend(device); 0076 device->close(); 0077 } 0078 } 0079 } 0080 switch (backend) { 0081 case Directory: 0082 errorStore << "Can't create a Directory store for a memory buffer!" << endl; 0083 return 0; 0084 case Zip: 0085 return new KoQuaZipStore(device, mode, appIdentification, writeMimetype); 0086 default: 0087 warnStore << "Unsupported backend requested for KoStore : " << backend; 0088 return 0; 0089 } 0090 } 0091 0092 namespace 0093 { 0094 const char ROOTPART[] = "root"; 0095 const char MAINNAME[] = "maindoc.xml"; 0096 } 0097 0098 KoStore::KoStore(Mode mode, bool writeMimetype) 0099 : d_ptr(new KoStorePrivate(this, mode, writeMimetype)) 0100 {} 0101 0102 KoStore::~KoStore() 0103 { 0104 Q_D(KoStore); 0105 delete d->stream; 0106 delete d_ptr; 0107 } 0108 0109 bool KoStore::open(const QString & _name) 0110 { 0111 Q_D(KoStore); 0112 // This also converts from relative to absolute, i.e. merges the currentPath() 0113 d->fileName = d->toExternalNaming(_name); 0114 0115 debugStore << "KOStore" << _name << d->fileName; 0116 0117 if (d->isOpen) { 0118 warnStore << "Store is already opened, missing close"; 0119 return false; 0120 } 0121 0122 if (d->fileName.length() > 512) { 0123 errorStore << "KoStore: Filename " << d->fileName << " is too long" << endl; 0124 return false; 0125 } 0126 0127 if (d->mode == Write) { 0128 debugStore << "opening for writing" << d->fileName; 0129 if (d->filesList.contains(d->fileName)) { 0130 warnStore << "KoStore: Duplicate filename" << d->fileName; 0131 return false; 0132 } 0133 0134 d->filesList.append(d->fileName); 0135 0136 d->size = 0; 0137 if (!openWrite(d->fileName)) 0138 return false; 0139 } else if (d->mode == Read) { 0140 debugStore << "Opening for reading" << d->fileName; 0141 if (!openRead(d->fileName)) 0142 return false; 0143 } else 0144 return false; 0145 0146 d->isOpen = true; 0147 return true; 0148 } 0149 0150 bool KoStore::isOpen() const 0151 { 0152 Q_D(const KoStore); 0153 return d->isOpen; 0154 } 0155 0156 bool KoStore::close() 0157 { 0158 Q_D(KoStore); 0159 if (!d->isOpen) { 0160 warnStore << "You must open before closing"; 0161 return false; 0162 } 0163 0164 bool ret = d->mode == Write ? closeWrite() : closeRead(); 0165 delete d->stream; 0166 d->stream = 0; 0167 d->isOpen = false; 0168 return ret; 0169 } 0170 0171 QIODevice* KoStore::device() const 0172 { 0173 Q_D(const KoStore); 0174 if (!d->isOpen) 0175 warnStore << "You must open before asking for a device"; 0176 if (d->mode != Read) 0177 warnStore << "Can not get device from store that is opened for writing"; 0178 return d->stream; 0179 } 0180 0181 QByteArray KoStore::read(qint64 max) 0182 { 0183 Q_D(KoStore); 0184 QByteArray data; 0185 0186 if (!d->isOpen) { 0187 warnStore << "You must open before reading"; 0188 return data; 0189 } 0190 if (d->mode != Read) { 0191 errorStore << "KoStore: Can not read from store that is opened for writing" << endl; 0192 return data; 0193 } 0194 0195 return d->stream->read(max); 0196 } 0197 0198 qint64 KoStore::write(const QByteArray& data) 0199 { 0200 return write(data.constData(), data.size()); // see below 0201 } 0202 0203 qint64 KoStore::read(char *_buffer, qint64 _len) 0204 { 0205 Q_D(KoStore); 0206 if (!d->isOpen) { 0207 errorStore << "KoStore: You must open before reading" << endl; 0208 return -1; 0209 } 0210 if (d->mode != Read) { 0211 errorStore << "KoStore: Can not read from store that is opened for writing" << endl; 0212 return -1; 0213 } 0214 0215 return d->stream->read(_buffer, _len); 0216 } 0217 0218 qint64 KoStore::write(const char* _data, qint64 _len) 0219 { 0220 Q_D(KoStore); 0221 if (_len == 0) return 0; 0222 0223 if (!d->isOpen) { 0224 errorStore << "KoStore: You must open before writing" << endl; 0225 return 0; 0226 } 0227 if (d->mode != Write) { 0228 errorStore << "KoStore: Can not write to store that is opened for reading" << endl; 0229 return 0; 0230 } 0231 0232 int nwritten = d->stream->write(_data, _len); 0233 Q_ASSERT(nwritten == (int)_len); 0234 d->size += nwritten; 0235 0236 return nwritten; 0237 } 0238 0239 qint64 KoStore::size() const 0240 { 0241 Q_D(const KoStore); 0242 if (!d->isOpen) { 0243 warnStore << "You must open before asking for a size"; 0244 return static_cast<qint64>(-1); 0245 } 0246 if (d->mode != Read) { 0247 warnStore << "Can not get size from store that is opened for writing"; 0248 return static_cast<qint64>(-1); 0249 } 0250 return d->size; 0251 } 0252 0253 bool KoStore::enterDirectory(const QString &directory) 0254 { 0255 Q_D(KoStore); 0256 //debugStore <<"enterDirectory" << directory; 0257 int pos; 0258 bool success = true; 0259 QString tmp(directory); 0260 0261 while ((pos = tmp.indexOf('/')) != -1 && 0262 (success = d->enterDirectoryInternal(tmp.left(pos)))) 0263 tmp.remove(0, pos + 1); 0264 0265 if (success && !tmp.isEmpty()) 0266 return d->enterDirectoryInternal(tmp); 0267 return success; 0268 } 0269 0270 bool KoStore::leaveDirectory() 0271 { 0272 Q_D(KoStore); 0273 if (d->currentPath.isEmpty()) 0274 return false; 0275 0276 d->currentPath.pop_back(); 0277 0278 return enterAbsoluteDirectory(currentPath()); 0279 } 0280 0281 QString KoStore::currentPath() const 0282 { 0283 Q_D(const KoStore); 0284 QString path; 0285 QStringList::ConstIterator it = d->currentPath.begin(); 0286 QStringList::ConstIterator end = d->currentPath.end(); 0287 for (; it != end; ++it) { 0288 path += *it; 0289 path += '/'; 0290 } 0291 return path; 0292 } 0293 0294 void KoStore::pushDirectory() 0295 { 0296 Q_D(KoStore); 0297 d->directoryStack.push(currentPath()); 0298 } 0299 0300 void KoStore::popDirectory() 0301 { 0302 Q_D(KoStore); 0303 d->currentPath.clear(); 0304 enterAbsoluteDirectory(QString()); 0305 enterDirectory(d->directoryStack.pop()); 0306 } 0307 0308 bool KoStore::extractFile(const QString &srcName, QByteArray &data) 0309 { 0310 Q_D(KoStore); 0311 QBuffer buffer(&data); 0312 return d->extractFile(srcName, buffer); 0313 } 0314 0315 bool KoStorePrivate::extractFile(const QString &srcName, QIODevice &buffer) 0316 { 0317 if (!q->open(srcName)) 0318 return false; 0319 0320 if (!buffer.open(QIODevice::WriteOnly)) { 0321 q->close(); 0322 return false; 0323 } 0324 0325 QByteArray data; 0326 data.resize(8 * 1024); 0327 uint total = 0; 0328 for (int block = 0; (block = q->read(data.data(), data.size())) > 0; total += block) { 0329 buffer.write(data.data(), block); 0330 } 0331 0332 if (q->size() != static_cast<qint64>(-1)) 0333 Q_ASSERT(total == q->size()); 0334 0335 buffer.close(); 0336 q->close(); 0337 0338 return true; 0339 } 0340 0341 bool KoStore::seek(qint64 pos) 0342 { 0343 Q_D(KoStore); 0344 return d->stream->seek(pos); 0345 } 0346 0347 qint64 KoStore::pos() const 0348 { 0349 Q_D(const KoStore); 0350 return d->stream->pos(); 0351 } 0352 0353 bool KoStore::atEnd() const 0354 { 0355 Q_D(const KoStore); 0356 return d->stream->atEnd(); 0357 } 0358 0359 // See the specification for details of what this function does. 0360 QString KoStorePrivate::toExternalNaming(const QString & _internalNaming) const 0361 { 0362 if (_internalNaming == ROOTPART) 0363 return q->currentPath() + MAINNAME; 0364 0365 QString intern; 0366 if (_internalNaming.startsWith("tar:/")) // absolute reference 0367 intern = _internalNaming.mid(5); // remove protocol 0368 else 0369 intern = q->currentPath() + _internalNaming; 0370 0371 return intern; 0372 } 0373 0374 0375 bool KoStorePrivate::enterDirectoryInternal(const QString &directory) 0376 { 0377 if (q->enterRelativeDirectory(directory)) { 0378 currentPath.append(directory); 0379 return true; 0380 } 0381 return false; 0382 } 0383 0384 bool KoStore::hasFile(const QString& fileName) const 0385 { 0386 Q_D(const KoStore); 0387 return fileExists(d->toExternalNaming(fileName)); 0388 } 0389 0390 bool KoStore::hasDirectory(const QString &directoryName) 0391 { 0392 return enterAbsoluteDirectory(directoryName); 0393 } 0394 0395 bool KoStore::finalize() 0396 { 0397 Q_D(KoStore); 0398 Q_ASSERT(!d->finalized); // call this only once! 0399 d->finalized = true; 0400 return doFinalize(); 0401 } 0402 0403 void KoStore::setCompressionEnabled(bool /*e*/) 0404 { 0405 } 0406 0407 void KoStore::setSubstitution(const QString &name, const QString &substitution) 0408 { 0409 Q_D(KoStore); 0410 d->substituteThis = name; 0411 d->substituteWith = substitution; 0412 } 0413 0414 bool KoStore::bad() const 0415 { 0416 Q_D(const KoStore); 0417 return !d->good; 0418 } 0419 0420 KoStore::Mode KoStore::mode() const 0421 { 0422 Q_D(const KoStore); 0423 return d->mode; 0424 } 0425 0426 QStringList KoStore::directoryList() const 0427 { 0428 return QStringList(); 0429 }