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 }