Warning, file /multimedia/k3b/src/projects/kostore/KoStore.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 // -*- c-basic-offset: 2 -*-
0002 /* This file is part of the KDE project
0003    SPDX-FileCopyrightText: 1998, 1999 Torben Weis <weis@kde.org>
0004    SPDX-FileCopyrightText: 2000-2002 David Faure <faure@kde.org>, Werner Trobin <trobin@kde.org>
0005 
0006    SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "KoStore.h"
0010 //#include "KoTarStore.h"
0011 #include "KoZipStore.h"
0012 //#include "KoDirectoryStore.h"
0013 #ifdef QCA2
0014 #include "KoEncryptedStore.h"
0015 #endif
0016 #include "kostore_log.h"
0017 
0018 #include <QBuffer>
0019 #include <QDir>
0020 #include <QFileInfo>
0021 #include <QFile>
0022 #include <QIODevice>
0023 #include <QTemporaryFile>
0024 #include <QUrl>
0025 
0026 #include <KLocalizedString>
0027 #include <KIO/StoredTransferJob>
0028 #include <KJobWidgets>
0029 #include <KMessageBox>
0030 
0031 #define DefaultFormat KoStore::Zip
0032 
0033 KoStore::Backend KoStore::determineBackend( QIODevice* dev )
0034 {
0035     unsigned char buf[5];
0036     if ( dev->read( (char *)buf, 4 ) < 4 )
0037       return DefaultFormat; // will create a "bad" store (bad()==true)
0038     //if ( buf[0] == 0037 && buf[1] == 0213 ) // gzip -> tar.gz
0039       //return Tar;
0040     if ( buf[0] == 'P' && buf[1] == 'K' && buf[2] == 3 && buf[3] == 4 )
0041       return Zip;
0042     return DefaultFormat; // fallback
0043 }
0044 
0045 KoStore* KoStore::createStore( const QString& fileName, Mode mode, const QByteArray & appIdentification, Backend backend )
0046 {
0047   bool automatic = false;
0048   if ( backend == Auto ) {
0049     automatic = true;
0050     if ( mode == KoStore::Write )
0051       backend = DefaultFormat;
0052     else
0053     {
0054       QFileInfo inf( fileName );
0055       if ( inf.isDir() )
0056         backend = Directory;
0057       else
0058       {
0059         QFile file( fileName );
0060         if ( file.open( QIODevice::ReadOnly ) )
0061           backend = determineBackend( &file );
0062         else
0063           backend = DefaultFormat; // will create a "bad" store (bad()==true)
0064       }
0065     }
0066   }
0067   switch ( backend )
0068   {
0069 //  case Tar:
0070   //  return new KoTarStore( fileName, mode, appIdentification );
0071   case Zip:
0072 #ifdef QCA2
0073     if( automatic && mode == Read ) {
0074         // 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.
0075         return new KoEncryptedStore( fileName, Read, appIdentification );
0076     }
0077 #else
0078     Q_UNUSED( automatic )
0079 #endif
0080     return new KoZipStore( fileName, mode, appIdentification );
0081 //  case Directory:
0082 //    return new KoDirectoryStore( fileName /* should be a dir name.... */, mode );
0083 #ifdef QCA2
0084   case Encrypted:
0085     return new KoEncryptedStore( fileName, mode, appIdentification );
0086 #endif
0087   default:
0088     qCWarning(K3B_KOSTORE_LOG) << "Unsupported backend requested for KoStore : " << backend;
0089     return 0L;
0090   }
0091 }
0092 
0093 KoStore* KoStore::createStore( QIODevice *device, Mode mode, const QByteArray & appIdentification, Backend backend )
0094 {
0095   bool automatic = false;
0096   if ( backend == Auto )
0097   {
0098     automatic = true;
0099     if ( mode == KoStore::Write )
0100       backend = DefaultFormat;
0101     else {
0102       if ( device->open( QIODevice::ReadOnly ) ) {
0103         backend = determineBackend( device );
0104         device->close();
0105       }
0106     }
0107   }
0108   switch ( backend )
0109   {
0110   //case Tar:
0111     //return new KoTarStore( device, mode, appIdentification );
0112   case Directory:
0113     qCCritical(K3B_KOSTORE_LOG) << "Can't create a Directory store for a memory buffer!" << Qt::endl;
0114     // fallback
0115     Q_FALLTHROUGH();
0116   case Zip:
0117 #ifdef QCA2
0118     if( automatic && mode == Read ) {
0119         // 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.
0120         return new KoEncryptedStore( device, Read, appIdentification );
0121     }
0122 #else
0123     Q_UNUSED( automatic )
0124 #endif
0125     return new KoZipStore( device, mode, appIdentification );
0126 #ifdef QCA2
0127   case Encrypted:
0128     return new KoEncryptedStore( device, mode, appIdentification );
0129 #endif
0130   default:
0131     qCWarning(K3B_KOSTORE_LOG) << "Unsupported backend requested for KoStore : " << backend;
0132     return 0L;
0133   }
0134 }
0135 
0136 KoStore* KoStore::createStore( QWidget* window, const QUrl& url, Mode mode, const QByteArray & appIdentification, Backend backend )
0137 {
0138   const bool automatic = ( backend == Auto );
0139   if ( url.isLocalFile() )
0140     return createStore(url.toLocalFile(), mode,  appIdentification, backend );
0141 
0142   QTemporaryFile tmpFile;
0143   if ( mode == KoStore::Write )
0144   {
0145     if ( automatic )
0146       backend = DefaultFormat;
0147   }
0148   else
0149   {
0150     KIO::StoredTransferJob* transferJob = KIO::storedGet( url );
0151     KJobWidgets::setWindow( transferJob, window );
0152     bool downloaded = true;
0153     QObject::connect( transferJob, &KJob::result, [&](KJob*) {
0154       if( transferJob->error() != KJob::NoError ) {
0155         tmpFile.open();
0156         tmpFile.write( transferJob->data() );
0157         tmpFile.close();
0158       } else {
0159         downloaded = false;
0160       }
0161     } );
0162 
0163     if (!downloaded)
0164     {
0165       qCCritical(K3B_KOSTORE_LOG) << "Could not download file!" << Qt::endl;
0166       backend = DefaultFormat; // will create a "bad" store (bad()==true)
0167     }
0168     else if ( automatic )
0169     {
0170       if ( tmpFile.open() )
0171       {
0172         backend = determineBackend( &tmpFile );
0173         tmpFile.close();
0174       }
0175     }
0176   }
0177   switch ( backend )
0178   {
0179   //case Tar:
0180     //return new KoTarStore( &tmpFile, mode, appIdentification );
0181   case Zip:
0182 #ifdef QCA2
0183     if( automatic && mode == Read ) {
0184         // 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.
0185         return new KoEncryptedStore( &tmpFile, Read, appIdentification );
0186     }
0187 #endif
0188     return new KoZipStore( &tmpFile, mode, appIdentification );
0189 #ifdef QCA2
0190   case Encrypted:
0191     return new KoEncryptedStore( &tmpFile, mode, appIdentification );
0192 #endif
0193   default:
0194     qCWarning(K3B_KOSTORE_LOG) << "Unsupported backend requested for KoStore (QUrl) : " << backend;
0195     KMessageBox::error( window,
0196         i18n("The directory mode is not supported for remote locations."),
0197         i18n("KOffice Storage"));
0198     return 0L;
0199   }
0200 }
0201 
0202 namespace {
0203   const char* const ROOTPART = "root";
0204   const char* const MAINNAME = "maindoc.xml";
0205 }
0206 
0207 KoStore::KoStore()
0208 {
0209 }
0210 
0211 bool KoStore::init( Mode _mode )
0212 {
0213   m_bIsOpen = false;
0214   m_mode = _mode;
0215   m_stream = 0;
0216   m_bFinalized = false;
0217 
0218   // Assume new style names.
0219   m_namingVersion = NAMING_VERSION_2_2;
0220   return true;
0221 }
0222 
0223 KoStore::~KoStore()
0224 {
0225   delete m_stream;
0226 }
0227 
0228 bool KoStore::open( const QString & _name )
0229 {
0230   // This also converts from relative to absolute, i.e. merges the currentPath()
0231   m_sName = toExternalNaming( _name );
0232 
0233   if ( m_bIsOpen )
0234   {
0235     qCWarning(K3B_KOSTORE_LOG) << "KoStore: File is already opened";
0236     //return KIO::ERR_INTERNAL;
0237     return false;
0238   }
0239 
0240   if ( m_sName.length() > 512 )
0241   {
0242       qCCritical(K3B_KOSTORE_LOG) << "KoStore: Filename " << m_sName << " is too long" << Qt::endl;
0243       //return KIO::ERR_MALFORMED_URL;
0244       return false;
0245   }
0246 
0247   if ( m_mode == Write )
0248   {
0249     qCDebug(K3B_KOSTORE_LOG) <<"KoStore: opening for writing '" << m_sName <<"'";
0250     if ( m_strFiles.contains( m_sName ) )
0251     {
0252       qCWarning(K3B_KOSTORE_LOG) << "KoStore: Duplicate filename " << m_sName;
0253       //return KIO::ERR_FILE_ALREADY_EXIST;
0254       return false;
0255     }
0256 
0257     m_strFiles.append( m_sName );
0258 
0259     m_iSize = 0;
0260     if ( !openWrite( m_sName ) )
0261       return false;
0262   }
0263   else if ( m_mode == Read )
0264   {
0265     qCDebug(K3B_KOSTORE_LOG) <<"Opening for reading '" << m_sName <<"'";
0266     if ( !openRead( m_sName ) )
0267       return false;
0268   }
0269   else
0270     //return KIO::ERR_UNSUPPORTED_ACTION;
0271     return false;
0272 
0273   m_bIsOpen = true;
0274   return true;
0275 }
0276 
0277 bool KoStore::isOpen() const
0278 {
0279   return m_bIsOpen;
0280 }
0281 
0282 bool KoStore::close()
0283 {
0284   qCDebug(K3B_KOSTORE_LOG) <<"KoStore: Closing";
0285 
0286   if ( !m_bIsOpen )
0287   {
0288     qCWarning(K3B_KOSTORE_LOG) << "KoStore: You must open before closing";
0289     //return KIO::ERR_INTERNAL;
0290     return false;
0291   }
0292 
0293   bool ret = m_mode == Write ? closeWrite() : closeRead();
0294 
0295   delete m_stream;
0296   m_stream = 0L;
0297   m_bIsOpen = false;
0298   return ret;
0299 }
0300 
0301 QIODevice* KoStore::device() const
0302 {
0303   if ( !m_bIsOpen )
0304     qCWarning(K3B_KOSTORE_LOG) << "KoStore: You must open before asking for a device";
0305   if ( m_mode != Read )
0306     qCWarning(K3B_KOSTORE_LOG) << "KoStore: Can not get device from store that is opened for writing";
0307   return m_stream;
0308 }
0309 
0310 QByteArray KoStore::read( qint64 max )
0311 {
0312   QByteArray data;
0313 
0314   if ( !m_bIsOpen )
0315   {
0316     qCWarning(K3B_KOSTORE_LOG) << "KoStore: You must open before reading";
0317     return data;
0318   }
0319   if ( m_mode != Read )
0320   {
0321     qCCritical(K3B_KOSTORE_LOG) << "KoStore: Can not read from store that is opened for writing" << Qt::endl;
0322     return data;
0323   }
0324 
0325   return m_stream->read( max );
0326 }
0327 
0328 qint64 KoStore::write( const QByteArray& data )
0329 {
0330   return write( data.data(), data.size() ); // see below
0331 }
0332 
0333 qint64 KoStore::read( char *_buffer, qint64 _len )
0334 {
0335   if ( !m_bIsOpen )
0336   {
0337     qCCritical(K3B_KOSTORE_LOG) << "KoStore: You must open before reading" << Qt::endl;
0338     return -1;
0339   }
0340   if ( m_mode != Read )
0341   {
0342     qCCritical(K3B_KOSTORE_LOG) << "KoStore: Can not read from store that is opened for writing" << Qt::endl;
0343     return -1;
0344   }
0345 
0346   return m_stream->read( _buffer, _len );
0347 }
0348 
0349 qint64 KoStore::write( const char* _data, qint64 _len )
0350 {
0351   if ( _len == 0L ) return 0;
0352 
0353   if ( !m_bIsOpen )
0354   {
0355     qCCritical(K3B_KOSTORE_LOG) << "KoStore: You must open before writing" << Qt::endl;
0356     return 0L;
0357   }
0358   if ( m_mode != Write  )
0359   {
0360     qCCritical(K3B_KOSTORE_LOG) << "KoStore: Can not write to store that is opened for reading" << Qt::endl;
0361     return 0L;
0362   }
0363 
0364   int nwritten = m_stream->write( _data, _len );
0365   Q_ASSERT( nwritten == (int)_len );
0366   m_iSize += nwritten;
0367 
0368   return nwritten;
0369 }
0370 
0371 qint64 KoStore::size() const
0372 {
0373   if ( !m_bIsOpen )
0374   {
0375     qCWarning(K3B_KOSTORE_LOG) << "KoStore: You must open before asking for a size";
0376     return static_cast<qint64>(-1);
0377   }
0378   if ( m_mode != Read )
0379   {
0380     qCWarning(K3B_KOSTORE_LOG) << "KoStore: Can not get size from store that is opened for writing";
0381     return static_cast<qint64>(-1);
0382   }
0383   return m_iSize;
0384 }
0385 
0386 bool KoStore::enterDirectory( const QString& directory )
0387 {
0388   //qCDebug(K3B_KOSTORE_LOG) <<"KoStore::enterDirectory" << directory;
0389   int pos;
0390   bool success = true;
0391   QString tmp( directory );
0392 
0393   while ( ( pos = tmp.indexOf( '/' ) ) != -1 &&
0394           ( success = enterDirectoryInternal( tmp.left( pos ) ) ) )
0395           tmp = tmp.mid( pos + 1 );
0396 
0397   if ( success && !tmp.isEmpty() )
0398     return enterDirectoryInternal( tmp );
0399   return success;
0400 }
0401 
0402 bool KoStore::leaveDirectory()
0403 {
0404   if ( m_currentPath.isEmpty() )
0405     return false;
0406 
0407   m_currentPath.pop_back();
0408 
0409   return enterAbsoluteDirectory( expandEncodedDirectory( currentPath() ) );
0410 }
0411 
0412 QString KoStore::currentDirectory() const
0413 {
0414   return expandEncodedDirectory( currentPath() );
0415 }
0416 
0417 QString KoStore::currentPath() const
0418 {
0419   QString path;
0420   QStringList::ConstIterator it = m_currentPath.begin();
0421   QStringList::ConstIterator end = m_currentPath.end();
0422   for ( ; it != end; ++it ) {
0423     path += *it;
0424     path += '/';
0425   }
0426   return path;
0427 }
0428 
0429 void KoStore::pushDirectory()
0430 {
0431   m_directoryStack.push( currentPath() );
0432 }
0433 
0434 void KoStore::popDirectory()
0435 {
0436   m_currentPath.clear();
0437   enterAbsoluteDirectory( QString() );
0438   enterDirectory( m_directoryStack.pop() );
0439 }
0440 
0441 bool KoStore::addLocalFile( const QString &fileName, const QString &destName )
0442 {
0443   QFileInfo fi( fileName );
0444   uint size = fi.size();
0445   QFile file( fileName );
0446   if ( !file.open( QIODevice::ReadOnly ))
0447   {
0448     return false;
0449   }
0450 
0451   if ( !open ( destName ) )
0452   {
0453     return false;
0454   }
0455 
0456   QByteArray data;
0457   data.resize( 8 * 1024 );
0458 
0459   uint total = 0;
0460   for ( int block = 0; ( block = file.read( data.data(), data.size() ) ) > 0; total += block )
0461   {
0462     data.resize(block);
0463     if ( write( data ) != block )
0464       return false;
0465     data.resize(8*1024);
0466   }
0467   Q_ASSERT( total == size );
0468 
0469   close();
0470   file.close();
0471 
0472   return true;
0473 }
0474 
0475 bool KoStore::addDataToFile( QByteArray &buffer, const QString &destName )
0476 {
0477   QBuffer file( &buffer );
0478   if ( !file.open( QIODevice::ReadOnly ))
0479   {
0480     return false;
0481   }
0482 
0483   if ( !open ( destName ) )
0484   {
0485     return false;
0486   }
0487 
0488   QByteArray data;
0489   data.resize( 8 * 1024 );
0490 
0491   uint total = 0;
0492   for ( int block = 0; ( block = file.read( data.data(), data.size() ) ) > 0; total += block )
0493   {
0494     data.resize(block);
0495     if ( write( data ) != block )
0496       return false;
0497     data.resize(8*1024);
0498   }
0499 
0500   close();
0501   file.close();
0502 
0503   return true;
0504 }
0505 
0506 bool KoStore::extractFile ( const QString &srcName, const QString &fileName )
0507 {
0508   QFile file( fileName );
0509   return extractFile( srcName, file );
0510 }
0511 
0512 
0513 bool KoStore::extractFile( const QString &srcName, QByteArray &data )
0514 {
0515   QBuffer buffer( &data );
0516   return extractFile( srcName, buffer );
0517 }
0518 
0519 bool KoStore::extractFile( const QString &srcName, QIODevice &buffer )
0520 {
0521   if ( !open ( srcName ) )
0522      return false;
0523 
0524   if( !buffer.open ( QIODevice::WriteOnly ) )
0525   {
0526     close();
0527     return false;
0528   }
0529   // ### This could use KArchive::copy or something, no?
0530 
0531   QByteArray data;
0532   data.resize( 8 * 1024 );
0533   uint total = 0;
0534   for( int block = 0; ( block = read( data.data(), data.size() ) ) > 0; total += block )
0535   {
0536     buffer.write( data.data(), block );
0537   }
0538 
0539   if( size() != static_cast<qint64>(-1) )
0540     Q_ASSERT( total == size() );
0541 
0542   buffer.close();
0543   close();
0544 
0545   return true;
0546 }
0547 
0548 
0549 QStringList KoStore::addLocalDirectory( const QString &dirPath, const QString &destName )
0550 {
0551   QString dot(".");
0552   QString dotdot("..");
0553   QStringList content;
0554 
0555   QDir dir(dirPath);
0556   if ( !dir.exists() )
0557     return QStringList();
0558 
0559   QStringList files = dir.entryList();
0560   for ( QStringList::Iterator it = files.begin(); it != files.end(); ++it )
0561   {
0562      if ( *it != dot && *it != dotdot )
0563      {
0564         QString currentFile = dirPath + '/' + *it;
0565         QString dest = destName.isEmpty() ? *it : (destName + '/' + *it);
0566 
0567         QFileInfo fi ( currentFile );
0568         if ( fi.isFile() )
0569         {
0570           addLocalFile ( currentFile, dest );
0571           content.append(dest);
0572         }
0573         else if ( fi.isDir() )
0574         {
0575           content += addLocalDirectory ( currentFile, dest );
0576         }
0577      }
0578   }
0579 
0580   return content;
0581 }
0582 
0583 
0584 bool KoStore::seek( qint64 pos )
0585 {
0586   return m_stream->seek( pos );
0587 }
0588 
0589 qint64 KoStore::pos() const
0590 {
0591   return m_stream->pos();
0592 }
0593 
0594 bool KoStore::atEnd() const
0595 {
0596   return m_stream->atEnd();
0597 }
0598 
0599 // See the specification for details of what this function does.
0600 QString KoStore::toExternalNaming( const QString & _internalNaming ) const
0601 {
0602   if ( _internalNaming == ROOTPART )
0603     return expandEncodedDirectory( currentPath() ) + MAINNAME;
0604 
0605   QString intern;
0606   if ( _internalNaming.startsWith( "tar:/" ) ) // absolute reference
0607     intern = _internalNaming.mid( 5 ); // remove protocol
0608   else
0609     intern = currentPath() + _internalNaming;
0610 
0611   return expandEncodedPath( intern );
0612 }
0613 
0614 QString KoStore::expandEncodedPath( const QString& _intern ) const
0615 {
0616   QString intern = _intern;
0617 
0618   if ( m_namingVersion == NAMING_VERSION_RAW )
0619     return intern;
0620 
0621   QString result;
0622   int pos;
0623 
0624   if ( ( pos = intern.lastIndexOf( '/', -1 ) ) != -1 ) {
0625     result = expandEncodedDirectory( intern.left( pos ) ) + '/';
0626     intern = intern.mid( pos + 1 );
0627   }
0628 
0629   // Now process the filename. If the first character is numeric, we have
0630   // a main document.
0631   if ( QChar(intern.at(0)).isDigit() )
0632   {
0633     // If this is the first part name, check if we have a store with
0634     // old-style names.
0635     if ( ( m_namingVersion == NAMING_VERSION_2_2 ) &&
0636          ( m_mode == Read ) &&
0637          ( fileExists( result + "part" + intern + ".xml" ) ) )
0638       m_namingVersion = NAMING_VERSION_2_1;
0639 
0640     if ( m_namingVersion == NAMING_VERSION_2_1 )
0641       result = result + "part" + intern + ".xml";
0642     else
0643       result = result + "part" + intern + '/' + MAINNAME;
0644   }
0645   else
0646     result += intern;
0647   return result;
0648 }
0649 
0650 QString KoStore::expandEncodedDirectory( const QString& _intern ) const
0651 {
0652   QString intern = _intern;
0653 
0654   if ( m_namingVersion == NAMING_VERSION_RAW )
0655     return intern;
0656 
0657   QString result;
0658   int pos;
0659   while ( ( pos = intern.indexOf( '/' ) ) != -1 ) {
0660     if ( QChar(intern.at(0)).isDigit() )
0661       result += "part";
0662     result += intern.left( pos + 1 ); // copy numbers (or "pictures") + "/"
0663     intern = intern.mid( pos + 1 ); // remove the dir we just processed
0664   }
0665 
0666   if ( !intern.isEmpty() && QChar(intern.at(0)).isDigit() )
0667     result += "part";
0668   result += intern;
0669   return result;
0670 }
0671 
0672 bool KoStore::enterDirectoryInternal( const QString& directory )
0673 {
0674     if ( enterRelativeDirectory( expandEncodedDirectory( directory ) ) )
0675     {
0676       m_currentPath.append( directory );
0677       return true;
0678     }
0679     return false;
0680 }
0681 
0682 void KoStore::disallowNameExpansion( void )
0683 {
0684     m_namingVersion = NAMING_VERSION_RAW;
0685 }
0686 
0687 bool KoStore::hasFile( const QString& fileName ) const
0688 {
0689   return fileExists( toExternalNaming( currentPath() + fileName ) );
0690 }
0691 
0692 bool KoStore::finalize()
0693 {
0694   Q_ASSERT( !m_bFinalized ); // call this only once!
0695   m_bFinalized = true;
0696   return doFinalize();
0697 }
0698 
0699 bool KoStore::isEncrypted()
0700 {
0701     return false;
0702 }
0703 
0704 bool KoStore::setPassword( const QString& /*password*/ )
0705 {
0706     return false;
0707 }
0708 
0709 QString KoStore::password( )
0710 {
0711     return QString();
0712 }