Warning, file /office/calligra/libs/store/KoStore.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002    Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
0003    Copyright (C) 2000-2002 David Faure <faure@kde.org>, Werner Trobin <trobin@kde.org>
0004    Copyright (C) 2010 C. Boemann <cbo@boemann.dk>
0005 
0006    This library is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU Library General Public
0008    License as published by the Free Software Foundation; either
0009    version 2 of the License, or (at your option) any later version.
0010 
0011    This library is distributed in the hope that it will be useful,
0012    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014    Library General Public License for more details.
0015 
0016    You should have received a copy of the GNU Library General Public License
0017    along with this library; see the file COPYING.LIB.  If not, write to
0018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019  * Boston, MA 02110-1301, USA.
0020 */
0021 
0022 #include "KoStore.h"
0023 #include "KoStore_p.h"
0024 
0025 #include "KoTarStore.h"
0026 #include "KoZipStore.h"
0027 #include "KoDirectoryStore.h"
0028 #ifdef QCA2
0029 #include "KoEncryptedStore.h"
0030 #endif
0031 
0032 #include <QBuffer>
0033 #include <QFileInfo>
0034 #include <QFile>
0035 
0036 #include <QUrl>
0037 #include <StoreDebug.h>
0038 
0039 #include <klocalizedstring.h>
0040 #include <kmessagebox.h>
0041 #include <KoNetAccess.h>
0042 
0043 #define DefaultFormat KoStore::Zip
0044 
0045 static KoStore::Backend determineBackend(QIODevice *dev)
0046 {
0047     unsigned char buf[5];
0048     if (dev->read((char *)buf, 4) < 4)
0049         return DefaultFormat; // will create a "bad" store (bad()==true)
0050     if (buf[0] == 0037 && buf[1] == 0213)   // gzip -> tar.gz
0051         return KoStore::Tar;
0052     if (buf[0] == 'P' && buf[1] == 'K' && buf[2] == 3 && buf[3] == 4)
0053         return KoStore::Zip;
0054     return DefaultFormat; // fallback
0055 }
0056 
0057 KoStore* KoStore::createStore(const QString& fileName, Mode mode, const QByteArray & appIdentification, Backend backend, bool writeMimetype)
0058 {
0059     bool automatic = false;
0060     if (backend == Auto) {
0061         automatic = true;
0062         if (mode == KoStore::Write)
0063             backend = DefaultFormat;
0064         else {
0065             QFileInfo inf(fileName);
0066             if (inf.isDir())
0067                 backend = Directory;
0068             else {
0069                 QFile file(fileName);
0070                 if (file.open(QIODevice::ReadOnly))
0071                     backend = determineBackend(&file);
0072                 else
0073                     backend = DefaultFormat; // will create a "bad" store (bad()==true)
0074             }
0075         }
0076     }
0077     switch (backend) {
0078     case Tar:
0079         return new KoTarStore(fileName, mode, appIdentification, writeMimetype);
0080     case Zip:
0081 #ifdef QCA2
0082         if (automatic && mode == Read) {
0083             // When automatically detecting, this might as well be an encrypted file. We'll need to check anyway, so we'll just use the encrypted store.
0084             return new KoEncryptedStore(fileName, Read, appIdentification, writeMimetype);
0085         }
0086 #endif
0087         return new KoZipStore(fileName, mode, appIdentification, writeMimetype);
0088     case Directory:
0089         return new KoDirectoryStore(fileName /* should be a dir name.... */, mode, writeMimetype);
0090 #ifdef QCA2
0091     case Encrypted:
0092         return new KoEncryptedStore(fileName, mode, appIdentification, writeMimetype);
0093 #endif
0094     default:
0095         warnStore << "Unsupported backend requested for KoStore : " << backend;
0096         return 0;
0097     }
0098 }
0099 
0100 KoStore* KoStore::createStore(QIODevice *device, Mode mode, const QByteArray & appIdentification, Backend backend, bool writeMimetype)
0101 {
0102     bool automatic = false;
0103     if (backend == Auto) {
0104         automatic = true;
0105         if (mode == KoStore::Write)
0106             backend = DefaultFormat;
0107         else {
0108             if (device->open(QIODevice::ReadOnly)) {
0109                 backend = determineBackend(device);
0110                 device->close();
0111             }
0112         }
0113     }
0114     switch (backend) {
0115     case Tar:
0116         return new KoTarStore(device, mode, appIdentification, writeMimetype);
0117     case Directory:
0118         errorStore << "Can't create a Directory store for a memory buffer!" << endl;
0119         // fallback
0120     case Zip:
0121 #ifdef QCA2
0122         if (automatic && mode == Read) {
0123             // When automatically detecting, this might as well be an encrypted file. We'll need to check anyway, so we'll just use the encrypted store.
0124             return new KoEncryptedStore(device, Read, appIdentification, writeMimetype);
0125         }
0126 #endif
0127         return new KoZipStore(device, mode, appIdentification, writeMimetype);
0128 #ifdef QCA2
0129     case Encrypted:
0130         return new KoEncryptedStore(device, mode, appIdentification, writeMimetype);
0131 #endif
0132     default:
0133         warnStore << "Unsupported backend requested for KoStore : " << backend;
0134         return 0;
0135     }
0136 }
0137 
0138 KoStore* KoStore::createStore(QWidget* window, const QUrl &url, Mode mode, const QByteArray & appIdentification, Backend backend, bool writeMimetype)
0139 {
0140     const bool automatic = (backend == Auto);
0141     if (url.isLocalFile())
0142         return createStore(url.toLocalFile(), mode,  appIdentification, backend, writeMimetype);
0143 
0144     QString tmpFile;
0145     if (mode == KoStore::Write) {
0146         if (automatic)
0147             backend = DefaultFormat;
0148     } else {
0149         const bool downloaded =
0150             KIO::NetAccess::download(url, tmpFile, window);
0151 
0152         if (!downloaded) {
0153             errorStore << "Could not download file!" << endl;
0154             backend = DefaultFormat; // will create a "bad" store (bad()==true)
0155         } else if (automatic) {
0156             QFile file(tmpFile);
0157             if (file.open(QIODevice::ReadOnly)) {
0158                 backend = determineBackend(&file);
0159                 file.close();
0160             }
0161         }
0162     }
0163     switch (backend) {
0164     case Tar:
0165         return new KoTarStore(window, url, tmpFile, mode, appIdentification);
0166     case Zip:
0167 #ifdef QCA2
0168         if (automatic && mode == Read) {
0169             // When automatically detecting, this might as well be an encrypted file. We'll need to check anyway, so we'll just use the encrypted store.
0170             return new KoEncryptedStore(window, url, tmpFile, Read, appIdentification, writeMimetype);
0171         }
0172 #endif
0173         return new KoZipStore(window, url, tmpFile, mode, appIdentification, writeMimetype);
0174 #ifdef QCA2
0175     case Encrypted:
0176         return new KoEncryptedStore(window, url, tmpFile, mode, appIdentification, writeMimetype);
0177 #endif
0178     default:
0179         warnStore << "Unsupported backend requested for KoStore (QUrl) : " << backend;
0180         KMessageBox::sorry(window,
0181                            i18n("The directory mode is not supported for remote locations."),
0182                            i18n("Calligra Storage"));
0183         return 0;
0184     }
0185 }
0186 
0187 namespace
0188 {
0189 const char ROOTPART[] = "root";
0190 const char MAINNAME[] = "maindoc.xml";
0191 }
0192 
0193 KoStore::KoStore(Mode mode, bool writeMimetype)
0194     : d_ptr(new KoStorePrivate(this, mode, writeMimetype))
0195 {}
0196 
0197 KoStore::~KoStore()
0198 {
0199     Q_D(KoStore);
0200     delete d->stream;
0201     delete d_ptr;
0202 }
0203 
0204 QUrl KoStore::urlOfStore() const
0205 {
0206     Q_D(const KoStore);
0207     if (d->fileMode == KoStorePrivate::RemoteRead || d->fileMode == KoStorePrivate::RemoteWrite) {
0208         return d->url;
0209     } else {
0210         return QUrl(d->localFileName);
0211     }
0212 }
0213 
0214 bool KoStore::open(const QString & _name)
0215 {
0216     Q_D(KoStore);
0217     // This also converts from relative to absolute, i.e. merges the currentPath()
0218     d->fileName = d->toExternalNaming(_name);
0219 
0220     if (d->isOpen) {
0221         warnStore << "Store is already opened, missing close";
0222         return false;
0223     }
0224 
0225     if (d->fileName.length() > 512) {
0226         errorStore << "KoStore: Filename " << d->fileName << " is too long" << endl;
0227         return false;
0228     }
0229 
0230     if (d->mode == Write) {
0231         debugStore << "opening for writing" << d->fileName;
0232         if (d->filesList.contains(d->fileName)) {
0233             warnStore << "KoStore: Duplicate filename" << d->fileName;
0234             return false;
0235         }
0236 
0237         d->filesList.append(d->fileName);
0238 
0239         d->size = 0;
0240         if (!openWrite(d->fileName))
0241             return false;
0242     } else if (d->mode == Read) {
0243         debugStore << "Opening for reading" << d->fileName;
0244         if (!openRead(d->fileName))
0245             return false;
0246     } else
0247         return false;
0248 
0249     d->isOpen = true;
0250     return true;
0251 }
0252 
0253 bool KoStore::isOpen() const
0254 {
0255     Q_D(const KoStore);
0256     return d->isOpen;
0257 }
0258 
0259 bool KoStore::close()
0260 {
0261     Q_D(KoStore);
0262     debugStore << "Closing";
0263 
0264     if (!d->isOpen) {
0265         warnStore << "You must open before closing";
0266         return false;
0267     }
0268 
0269     bool ret = d->mode == Write ? closeWrite() : closeRead();
0270 
0271     delete d->stream;
0272     d->stream = 0;
0273     d->isOpen = false;
0274     return ret;
0275 }
0276 
0277 QIODevice* KoStore::device() const
0278 {
0279     Q_D(const KoStore);
0280     if (!d->isOpen)
0281         warnStore << "You must open before asking for a device";
0282     if (d->mode != Read)
0283         warnStore << "Can not get device from store that is opened for writing";
0284     return d->stream;
0285 }
0286 
0287 QByteArray KoStore::read(qint64 max)
0288 {
0289     Q_D(KoStore);
0290     QByteArray data;
0291 
0292     if (!d->isOpen) {
0293         warnStore << "You must open before reading";
0294         return data;
0295     }
0296     if (d->mode != Read) {
0297         errorStore << "KoStore: Can not read from store that is opened for writing" << endl;
0298         return data;
0299     }
0300 
0301     return d->stream->read(max);
0302 }
0303 
0304 qint64 KoStore::write(const QByteArray& data)
0305 {
0306     return write(data.constData(), data.size());   // see below
0307 }
0308 
0309 qint64 KoStore::read(char *_buffer, qint64 _len)
0310 {
0311     Q_D(KoStore);
0312     if (!d->isOpen) {
0313         errorStore << "KoStore: You must open before reading" << endl;
0314         return -1;
0315     }
0316     if (d->mode != Read) {
0317         errorStore << "KoStore: Can not read from store that is opened for writing" << endl;
0318         return -1;
0319     }
0320 
0321     return d->stream->read(_buffer, _len);
0322 }
0323 
0324 qint64 KoStore::write(const char* _data, qint64 _len)
0325 {
0326     Q_D(KoStore);
0327     if (_len == 0) return 0;
0328 
0329     if (!d->isOpen) {
0330         errorStore << "KoStore: You must open before writing" << endl;
0331         return 0;
0332     }
0333     if (d->mode != Write) {
0334         errorStore << "KoStore: Can not write to store that is opened for reading" << endl;
0335         return 0;
0336     }
0337 
0338     int nwritten = d->stream->write(_data, _len);
0339     Q_ASSERT(nwritten == (int)_len);
0340     d->size += nwritten;
0341 
0342     return nwritten;
0343 }
0344 
0345 qint64 KoStore::size() const
0346 {
0347     Q_D(const KoStore);
0348     if (!d->isOpen) {
0349         warnStore << "You must open before asking for a size";
0350         return static_cast<qint64>(-1);
0351     }
0352     if (d->mode != Read) {
0353         warnStore << "Can not get size from store that is opened for writing";
0354         return static_cast<qint64>(-1);
0355     }
0356     return d->size;
0357 }
0358 
0359 bool KoStore::enterDirectory(const QString &directory)
0360 {
0361     Q_D(KoStore);
0362     //debugStore <<"enterDirectory" << directory;
0363     int pos;
0364     bool success = true;
0365     QString tmp(directory);
0366 
0367     while ((pos = tmp.indexOf('/')) != -1 &&
0368             (success = d->enterDirectoryInternal(tmp.left(pos))))
0369         tmp.remove(0, pos + 1);
0370 
0371     if (success && !tmp.isEmpty())
0372         return d->enterDirectoryInternal(tmp);
0373     return success;
0374 }
0375 
0376 bool KoStore::leaveDirectory()
0377 {
0378     Q_D(KoStore);
0379     if (d->currentPath.isEmpty())
0380         return false;
0381 
0382     d->currentPath.pop_back();
0383 
0384     return enterAbsoluteDirectory(currentPath());
0385 }
0386 
0387 QString KoStore::currentPath() const
0388 {
0389     Q_D(const KoStore);
0390     QString path;
0391     QStringList::ConstIterator it = d->currentPath.begin();
0392     QStringList::ConstIterator end = d->currentPath.end();
0393     for (; it != end; ++it) {
0394         path += *it;
0395         path += '/';
0396     }
0397     return path;
0398 }
0399 
0400 void KoStore::pushDirectory()
0401 {
0402     Q_D(KoStore);
0403     d->directoryStack.push(currentPath());
0404 }
0405 
0406 void KoStore::popDirectory()
0407 {
0408     Q_D(KoStore);
0409     d->currentPath.clear();
0410     enterAbsoluteDirectory(QString());
0411     enterDirectory(d->directoryStack.pop());
0412 }
0413 
0414 bool KoStore::addLocalFile(const QString &fileName, const QString &destName)
0415 {
0416     QFileInfo fi(fileName);
0417     uint size = fi.size();
0418     QFile file(fileName);
0419     if (!file.open(QIODevice::ReadOnly)) {
0420         return false;
0421     }
0422 
0423     if (!open(destName)) {
0424         return false;
0425     }
0426 
0427     QByteArray data;
0428     data.resize(8 * 1024);
0429 
0430     uint total = 0;
0431     for (int block = 0; (block = file.read(data.data(), data.size())) > 0; total += block) {
0432         data.resize(block);
0433         if (write(data) != block)
0434             return false;
0435         data.resize(8*1024);
0436     }
0437     Q_ASSERT(total == size);
0438     if (total != size) {
0439         warnStore << "Did not write enough bytes. Expected: " << size << ", wrote" << total;
0440         return false;
0441     }
0442 
0443     close();
0444     file.close();
0445 
0446     return true;
0447 }
0448 
0449 bool KoStore::addDataToFile(QByteArray &buffer, const QString &destName)
0450 {
0451     QBuffer file(&buffer);
0452     if (!file.open(QIODevice::ReadOnly)) {
0453         return false;
0454     }
0455 
0456     if (!open(destName)) {
0457         return false;
0458     }
0459 
0460     QByteArray data;
0461     data.resize(8 * 1024);
0462 
0463     uint total = 0;
0464     for (int block = 0; (block = file.read(data.data(), data.size())) > 0; total += block) {
0465         data.resize(block);
0466         if (write(data) != block)
0467             return false;
0468         data.resize(8*1024);
0469     }
0470 
0471     close();
0472     file.close();
0473 
0474     return true;
0475 }
0476 
0477 bool KoStore::extractFile(const QString &srcName, const QString &fileName)
0478 {
0479     Q_D(KoStore);
0480     QFile file(fileName);
0481     return d->extractFile(srcName, file);
0482 }
0483 
0484 
0485 bool KoStore::extractFile(const QString &srcName, QByteArray &data)
0486 {
0487     Q_D(KoStore);
0488     QBuffer buffer(&data);
0489     return d->extractFile(srcName, buffer);
0490 }
0491 
0492 bool KoStorePrivate::extractFile(const QString &srcName, QIODevice &buffer)
0493 {
0494     if (!q->open(srcName))
0495         return false;
0496 
0497     if (!buffer.open(QIODevice::WriteOnly)) {
0498         q->close();
0499         return false;
0500     }
0501     // ### This could use KArchive::copy or something, no?
0502 
0503     QByteArray data;
0504     data.resize(8 * 1024);
0505     uint total = 0;
0506     for (int block = 0; (block = q->read(data.data(), data.size())) > 0; total += block) {
0507         buffer.write(data.data(), block);
0508     }
0509 
0510     if (q->size() != static_cast<qint64>(-1))
0511         Q_ASSERT(total == q->size());
0512 
0513     buffer.close();
0514     q->close();
0515 
0516     return true;
0517 }
0518 
0519 bool KoStore::seek(qint64 pos)
0520 {
0521     Q_D(KoStore);
0522     return d->stream->seek(pos);
0523 }
0524 
0525 qint64 KoStore::pos() const
0526 {
0527     Q_D(const KoStore);
0528     return d->stream->pos();
0529 }
0530 
0531 bool KoStore::atEnd() const
0532 {
0533     Q_D(const KoStore);
0534     return d->stream->atEnd();
0535 }
0536 
0537 // See the specification for details of what this function does.
0538 QString KoStorePrivate::toExternalNaming(const QString & _internalNaming) const
0539 {
0540     if (_internalNaming == ROOTPART)
0541         return q->currentPath() + MAINNAME;
0542 
0543     QString intern;
0544     if (_internalNaming.startsWith("tar:/"))     // absolute reference
0545         intern = _internalNaming.mid(5);   // remove protocol
0546     else
0547         intern = q->currentPath() + _internalNaming;
0548 
0549     return intern;
0550 }
0551 
0552 
0553 bool KoStorePrivate::enterDirectoryInternal(const QString &directory)
0554 {
0555     if (q->enterRelativeDirectory(directory)) {
0556         currentPath.append(directory);
0557         return true;
0558     }
0559     return false;
0560 }
0561 
0562 bool KoStore::hasFile(const QString& fileName) const
0563 {
0564     Q_D(const KoStore);
0565     return fileExists(d->toExternalNaming(fileName));
0566 }
0567 
0568 bool KoStore::finalize()
0569 {
0570     Q_D(KoStore);
0571     Q_ASSERT(!d->finalized);   // call this only once!
0572     d->finalized = true;
0573     return doFinalize();
0574 }
0575 
0576 void KoStore::setCompressionEnabled(bool /*e*/)
0577 {
0578 }
0579 
0580 bool KoStore::isEncrypted()
0581 {
0582     return false;
0583 }
0584 
0585 bool KoStore::setPassword(const QString& /*password*/)
0586 {
0587     return false;
0588 }
0589 
0590 QString KoStore::password()
0591 {
0592     return QString();
0593 }
0594 
0595 bool KoStore::bad() const
0596 {
0597     Q_D(const KoStore);
0598     return !d->good;
0599 }
0600 
0601 KoStore::Mode KoStore::mode() const
0602 {
0603     Q_D(const KoStore);
0604     return d->mode;
0605 }
0606 
0607 QStringList KoStore::directoryList() const
0608 {
0609     return QStringList();
0610 }