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 }