00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <stdio.h>
00023 #include <assert.h>
00024 #include <stdlib.h>
00025
00026 #include "KoStore.h"
00027 #include "KoTarStore.h"
00028 #include "KoZipStore.h"
00029 #include "KoDirectoryStore.h"
00030 #ifdef QCA2
00031 #include "KoEncryptedStore.h"
00032 #endif
00033
00034 #include <QBuffer>
00035 #include <QFileInfo>
00036 #include <QFile>
00037 #include <QDir>
00038
00039 #include <kurl.h>
00040 #include <kdebug.h>
00041 #include <kdeversion.h>
00042 #include <klocale.h>
00043 #include <kmessagebox.h>
00044 #include <kio/netaccess.h>
00045
00046
00047 #define DefaultFormat KoStore::Zip
00048
00049 const int KoStore::s_area = 30002;
00050
00051 KoStore::Backend KoStore::determineBackend( QIODevice* dev )
00052 {
00053 unsigned char buf[5];
00054 if ( dev->read( (char *)buf, 4 ) < 4 )
00055 return DefaultFormat;
00056 if ( buf[0] == 0037 && buf[1] == 0213 )
00057 return Tar;
00058 if ( buf[0] == 'P' && buf[1] == 'K' && buf[2] == 3 && buf[3] == 4 )
00059 return Zip;
00060 return DefaultFormat;
00061 }
00062
00063 KoStore* KoStore::createStore( const QString& fileName, Mode mode, const QByteArray & appIdentification, Backend backend )
00064 {
00065 bool automatic = false;
00066 if ( backend == Auto ) {
00067 automatic = true;
00068 if ( mode == KoStore::Write )
00069 backend = DefaultFormat;
00070 else
00071 {
00072 QFileInfo inf( fileName );
00073 if ( inf.isDir() )
00074 backend = Directory;
00075 else
00076 {
00077 QFile file( fileName );
00078 if ( file.open( QIODevice::ReadOnly ) )
00079 backend = determineBackend( &file );
00080 else
00081 backend = DefaultFormat;
00082 }
00083 }
00084 }
00085 switch ( backend )
00086 {
00087 case Tar:
00088 return new KoTarStore( fileName, mode, appIdentification );
00089 case Zip:
00090 #ifdef QCA2
00091 if( automatic && mode == Read ) {
00092
00093 return KoEncryptedStore::createEncryptedStoreReader( fileName, appIdentification );
00094 }
00095 #endif
00096 return new KoZipStore( fileName, mode, appIdentification );
00097 case Directory:
00098 return new KoDirectoryStore( fileName , mode );
00099 #ifdef QCA2
00100 case Encrypted:
00101 return new KoEncryptedStore( fileName, mode, appIdentification );
00102 #endif
00103 default:
00104 kWarning(s_area) << "Unsupported backend requested for KoStore : " << backend << endl;
00105 return 0L;
00106 }
00107 }
00108
00109 KoStore* KoStore::createStore( QIODevice *device, Mode mode, const QByteArray & appIdentification, Backend backend )
00110 {
00111 bool automatic = false;
00112 if ( backend == Auto )
00113 {
00114 automatic = true;
00115 if ( mode == KoStore::Write )
00116 backend = DefaultFormat;
00117 else {
00118 if ( device->open( QIODevice::ReadOnly ) ) {
00119 backend = determineBackend( device );
00120 device->close();
00121 }
00122 }
00123 }
00124 switch ( backend )
00125 {
00126 case Tar:
00127 return new KoTarStore( device, mode, appIdentification );
00128 case Directory:
00129 kError(s_area) << "Can't create a Directory store for a memory buffer!" << endl;
00130
00131 case Zip:
00132 #ifdef QCA2
00133 if( automatic && mode == Read ) {
00134
00135 return KoEncryptedStore::createEncryptedStoreReader( device, appIdentification );
00136 }
00137 #endif
00138 return new KoZipStore( device, mode, appIdentification );
00139 #ifdef QCA2
00140 case Encrypted:
00141 return new KoEncryptedStore( device, mode, appIdentification );
00142 #endif
00143 default:
00144 kWarning(s_area) << "Unsupported backend requested for KoStore : " << backend << endl;
00145 return 0L;
00146 }
00147 }
00148
00149 KoStore* KoStore::createStore( QWidget* window, const KUrl& url, Mode mode, const QByteArray & appIdentification, Backend backend )
00150 {
00151 const bool automatic = ( backend == Auto );
00152 if ( url.isLocalFile() )
00153 return createStore(url.path(), mode, appIdentification, backend );
00154
00155 QString tmpFile;
00156 if ( mode == KoStore::Write )
00157 {
00158 if ( automatic )
00159 backend = DefaultFormat;
00160 }
00161 else
00162 {
00163 const bool downloaded =
00164 KIO::NetAccess::download( url, tmpFile, window );
00165
00166 if (!downloaded)
00167 {
00168 kError(s_area) << "Could not download file!" << endl;
00169 backend = DefaultFormat;
00170 }
00171 else if ( automatic )
00172 {
00173 QFile file( tmpFile );
00174 if ( file.open( QIODevice::ReadOnly ) )
00175 {
00176 backend = determineBackend( &file );
00177 file.close();
00178 }
00179 }
00180 }
00181 switch ( backend )
00182 {
00183 case Tar:
00184 return new KoTarStore( window, url, tmpFile, mode, appIdentification );
00185 case Zip:
00186 #ifdef QCA2
00187 if( automatic && mode == Read ) {
00188
00189 return KoEncryptedStore::createEncryptedStoreReader( window, url, tmpFile, appIdentification );
00190 }
00191 #endif
00192 return new KoZipStore( window, url, tmpFile, mode, appIdentification );
00193 #ifdef QCA2
00194 case Encrypted:
00195 return new KoEncryptedStore( window, url, tmpFile, mode, appIdentification );
00196 #endif
00197 default:
00198 kWarning(s_area) << "Unsupported backend requested for KoStore (KUrl) : " << backend << endl;
00199 KMessageBox::sorry( window,
00200 i18n("The directory mode is not supported for remote locations."),
00201 i18n("KOffice Storage"));
00202 return 0L;
00203 }
00204 }
00205
00206 namespace {
00207 const char* const ROOTPART = "root";
00208 const char* const MAINNAME = "maindoc.xml";
00209 }
00210
00211 bool KoStore::init( Mode _mode )
00212 {
00213 d = 0;
00214 m_bIsOpen = false;
00215 m_mode = _mode;
00216 m_stream = 0;
00217
00218
00219 m_namingVersion = NAMING_VERSION_2_2;
00220 return true;
00221 }
00222
00223 KoStore::~KoStore()
00224 {
00225 delete m_stream;
00226 }
00227
00228 bool KoStore::open( const QString & _name )
00229 {
00230
00231 m_sName = toExternalNaming( _name );
00232
00233 if ( m_bIsOpen )
00234 {
00235 kWarning(s_area) << "KoStore: File is already opened" << endl;
00236
00237 return false;
00238 }
00239
00240 if ( m_sName.length() > 512 )
00241 {
00242 kError(s_area) << "KoStore: Filename " << m_sName << " is too long" << endl;
00243
00244 return false;
00245 }
00246
00247 if ( m_mode == Write )
00248 {
00249 kDebug(s_area) << "KoStore: opening for writing '" << m_sName << "'" << endl;
00250 if ( m_strFiles.contains( m_sName ) )
00251 {
00252 kWarning(s_area) << "KoStore: Duplicate filename " << m_sName << endl;
00253
00254 return false;
00255 }
00256
00257 m_strFiles.append( m_sName );
00258
00259 m_iSize = 0;
00260 if ( !openWrite( m_sName ) )
00261 return false;
00262 }
00263 else if ( m_mode == Read )
00264 {
00265 kDebug(s_area) << "Opening for reading '" << m_sName << "'" << endl;
00266 if ( !openRead( m_sName ) )
00267 return false;
00268 }
00269 else
00270
00271 return false;
00272
00273 m_bIsOpen = true;
00274 return true;
00275 }
00276
00277 bool KoStore::isOpen() const
00278 {
00279 return m_bIsOpen;
00280 }
00281
00282 bool KoStore::close()
00283 {
00284 kDebug(s_area) << "KoStore: Closing" << endl;
00285
00286 if ( !m_bIsOpen )
00287 {
00288 kWarning(s_area) << "KoStore: You must open before closing" << endl;
00289
00290 return false;
00291 }
00292
00293 bool ret = m_mode == Write ? closeWrite() : closeRead();
00294
00295 delete m_stream;
00296 m_stream = 0L;
00297 m_bIsOpen = false;
00298 return ret;
00299 }
00300
00301 QIODevice* KoStore::device() const
00302 {
00303 if ( !m_bIsOpen )
00304 kWarning(s_area) << "KoStore: You must open before asking for a device" << endl;
00305 if ( m_mode != Read )
00306 kWarning(s_area) << "KoStore: Can not get device from store that is opened for writing" << endl;
00307 return m_stream;
00308 }
00309
00310 QByteArray KoStore::read( qint64 max )
00311 {
00312 QByteArray data;
00313
00314 if ( !m_bIsOpen )
00315 {
00316 kWarning(s_area) << "KoStore: You must open before reading" << endl;
00317 return data;
00318 }
00319 if ( m_mode != Read )
00320 {
00321 kError(s_area) << "KoStore: Can not read from store that is opened for writing" << endl;
00322 return data;
00323 }
00324
00325 if ( m_stream->atEnd() )
00326 {
00327 return data;
00328 }
00329
00330 if ( max > m_iSize - m_stream->pos() )
00331 max = m_iSize - m_stream->pos();
00332 if ( max == 0 )
00333 {
00334 return data;
00335 }
00336
00337 data = m_stream->read( max );
00338
00339 return data;
00340 }
00341
00342 qint64 KoStore::write( const QByteArray& data )
00343 {
00344 return write( data.data(), data.size() );
00345 }
00346
00347 qint64 KoStore::read( char *_buffer, qint64 _len )
00348 {
00349 if ( !m_bIsOpen )
00350 {
00351 kError(s_area) << "KoStore: You must open before reading" << endl;
00352 return -1;
00353 }
00354 if ( m_mode != Read )
00355 {
00356 kError(s_area) << "KoStore: Can not read from store that is opened for writing" << endl;
00357 return -1;
00358 }
00359
00360 if ( m_stream->atEnd() )
00361 return 0;
00362
00363 if ( _len > m_iSize - m_stream->pos() )
00364 _len = m_iSize - m_stream->pos();
00365 if ( _len == 0 )
00366 return 0;
00367
00368 return m_stream->read( _buffer, _len );
00369 }
00370
00371 qint64 KoStore::write( const char* _data, qint64 _len )
00372 {
00373 if ( _len == 0L ) return 0;
00374
00375 if ( !m_bIsOpen )
00376 {
00377 kError(s_area) << "KoStore: You must open before writing" << endl;
00378 return 0L;
00379 }
00380 if ( m_mode != Write )
00381 {
00382 kError(s_area) << "KoStore: Can not write to store that is opened for reading" << endl;
00383 return 0L;
00384 }
00385
00386 int nwritten = m_stream->write( _data, _len );
00387 Q_ASSERT( nwritten == (int)_len );
00388 m_iSize += nwritten;
00389
00390 return nwritten;
00391 }
00392
00393 qint64 KoStore::size() const
00394 {
00395 if ( !m_bIsOpen )
00396 {
00397 kWarning(s_area) << "KoStore: You must open before asking for a size" << endl;
00398 return static_cast<qint64>(-1);
00399 }
00400 if ( m_mode != Read )
00401 {
00402 kWarning(s_area) << "KoStore: Can not get size from store that is opened for writing" << endl;
00403 return static_cast<qint64>(-1);
00404 }
00405 return m_iSize;
00406 }
00407
00408 bool KoStore::enterDirectory( const QString& directory )
00409 {
00410
00411 int pos;
00412 bool success = true;
00413 QString tmp( directory );
00414
00415 while ( ( pos = tmp.indexOf( '/' ) ) != -1 &&
00416 ( success = enterDirectoryInternal( tmp.left( pos ) ) ) )
00417 tmp = tmp.mid( pos + 1 );
00418
00419 if ( success && !tmp.isEmpty() )
00420 return enterDirectoryInternal( tmp );
00421 return success;
00422 }
00423
00424 bool KoStore::leaveDirectory()
00425 {
00426 if ( m_currentPath.isEmpty() )
00427 return false;
00428
00429 m_currentPath.pop_back();
00430
00431 return enterAbsoluteDirectory( expandEncodedDirectory( currentPath() ) );
00432 }
00433
00434 QString KoStore::currentDirectory() const
00435 {
00436 return expandEncodedDirectory( currentPath() );
00437 }
00438
00439 QString KoStore::currentPath() const
00440 {
00441 QString path;
00442 QStringList::ConstIterator it = m_currentPath.begin();
00443 QStringList::ConstIterator end = m_currentPath.end();
00444 for ( ; it != end; ++it ) {
00445 path += *it;
00446 path += '/';
00447 }
00448 return path;
00449 }
00450
00451 void KoStore::pushDirectory()
00452 {
00453 m_directoryStack.push( currentPath() );
00454 }
00455
00456 void KoStore::popDirectory()
00457 {
00458 m_currentPath.clear();
00459 enterAbsoluteDirectory( QString::null );
00460 enterDirectory( m_directoryStack.pop() );
00461 }
00462
00463 bool KoStore::addLocalFile( const QString &fileName, const QString &destName )
00464 {
00465 QFileInfo fi( fileName );
00466 uint size = fi.size();
00467 QFile file( fileName );
00468 if ( !file.open( QIODevice::ReadOnly ))
00469 {
00470 return false;
00471 }
00472
00473 if ( !open ( destName ) )
00474 {
00475 return false;
00476 }
00477
00478 QByteArray data;
00479 data.resize( 8 * 1024 );
00480
00481 uint total = 0;
00482 for ( int block = 0; ( block = file.read( data.data(), data.size() ) ) > 0; total += block )
00483 {
00484 data.resize(block);
00485 if ( write( data ) != block )
00486 return false;
00487 data.resize(8*1024);
00488 }
00489 Q_ASSERT( total == size );
00490
00491 close();
00492 file.close();
00493
00494 return true;
00495 }
00496
00497 bool KoStore::addDataToFile( QByteArray &buffer, const QString &destName )
00498 {
00499 QBuffer file( &buffer );
00500 if ( !file.open( QIODevice::ReadOnly ))
00501 {
00502 return false;
00503 }
00504
00505 if ( !open ( destName ) )
00506 {
00507 return false;
00508 }
00509
00510 QByteArray data;
00511 data.resize( 8 * 1024 );
00512
00513 uint total = 0;
00514 for ( int block = 0; ( block = file.read( data.data(), data.size() ) ) > 0; total += block )
00515 {
00516 data.resize(block);
00517 if ( write( data ) != block )
00518 return false;
00519 data.resize(8*1024);
00520 }
00521
00522 close();
00523 file.close();
00524
00525 return true;
00526 }
00527
00528 bool KoStore::extractFile ( const QString &srcName, const QString &fileName )
00529 {
00530 QFile file( fileName );
00531 return extractFile( srcName, file );
00532 }
00533
00534
00535 bool KoStore::extractFile( const QString &srcName, QByteArray &data )
00536 {
00537 QBuffer buffer( &data );
00538 return extractFile( srcName, buffer );
00539 }
00540
00541 bool KoStore::extractFile( const QString &srcName, QIODevice &buffer )
00542 {
00543 if ( !open ( srcName ) )
00544 return false;
00545
00546 if( !buffer.open ( QIODevice::WriteOnly ) )
00547 {
00548 close();
00549 return false;
00550 }
00551
00552
00553 QByteArray data;
00554 data.resize( 8 * 1024 );
00555 uint total = 0;
00556 for( int block = 0; ( block = read( data.data(), data.size() ) ) > 0; total += block )
00557 {
00558 buffer.write( data.data(), block );
00559 }
00560
00561 if( size() != static_cast<qint64>(-1) )
00562 Q_ASSERT( total == size() );
00563
00564 buffer.close();
00565 close();
00566
00567 return true;
00568 }
00569
00570
00571 QStringList KoStore::addLocalDirectory( const QString &dirPath, const QString &destName )
00572 {
00573 QString dot = ".";
00574 QString dotdot = "..";
00575 QStringList content;
00576
00577 QDir dir(dirPath);
00578 if ( !dir.exists() )
00579 return QStringList();
00580
00581 QStringList files = dir.entryList();
00582 for ( QStringList::Iterator it = files.begin(); it != files.end(); ++it )
00583 {
00584 if ( *it != dot && *it != dotdot )
00585 {
00586 QString currentFile = dirPath + '/' + *it;
00587 QString dest = destName.isEmpty() ? *it : (destName + '/' + *it);
00588
00589 QFileInfo fi ( currentFile );
00590 if ( fi.isFile() )
00591 {
00592 addLocalFile ( currentFile, dest );
00593 content.append(dest);
00594 }
00595 else if ( fi.isDir() )
00596 {
00597 content += addLocalDirectory ( currentFile, dest );
00598 }
00599 }
00600 }
00601
00602 return content;
00603 }
00604
00605
00606 bool KoStore::seek( qint64 pos )
00607 {
00608 return m_stream->seek( pos );
00609 }
00610
00611 qint64 KoStore::pos() const
00612 {
00613 return m_stream->pos();
00614 }
00615
00616 bool KoStore::atEnd() const
00617 {
00618 return m_stream->atEnd();
00619 }
00620
00621
00622 QString KoStore::toExternalNaming( const QString & _internalNaming ) const
00623 {
00624 if ( _internalNaming == ROOTPART )
00625 return expandEncodedDirectory( currentPath() ) + MAINNAME;
00626
00627 QString intern;
00628 if ( _internalNaming.startsWith( "tar:/" ) )
00629 intern = _internalNaming.mid( 5 );
00630 else
00631 intern = currentPath() + _internalNaming;
00632
00633 return expandEncodedPath( intern );
00634 }
00635
00636 QString KoStore::expandEncodedPath( QString intern ) const
00637 {
00638 if ( m_namingVersion == NAMING_VERSION_RAW )
00639 return intern;
00640
00641 QString result;
00642 int pos;
00643
00644 if ( ( pos = intern.lastIndexOf( '/', -1 ) ) != -1 ) {
00645 result = expandEncodedDirectory( intern.left( pos ) ) + '/';
00646 intern = intern.mid( pos + 1 );
00647 }
00648
00649
00650
00651 if ( QChar(intern.at(0)).isDigit() )
00652 {
00653
00654
00655 if ( ( m_namingVersion == NAMING_VERSION_2_2 ) &&
00656 ( m_mode == Read ) &&
00657 ( fileExists( result + "part" + intern + ".xml" ) ) )
00658 m_namingVersion = NAMING_VERSION_2_1;
00659
00660 if ( m_namingVersion == NAMING_VERSION_2_1 )
00661 result = result + "part" + intern + ".xml";
00662 else
00663 result = result + "part" + intern + '/' + MAINNAME;
00664 }
00665 else
00666 result += intern;
00667 return result;
00668 }
00669
00670 QString KoStore::expandEncodedDirectory( QString intern ) const
00671 {
00672 if ( m_namingVersion == NAMING_VERSION_RAW )
00673 return intern;
00674
00675 QString result;
00676 int pos;
00677 while ( ( pos = intern.indexOf( '/' ) ) != -1 ) {
00678 if ( QChar(intern.at(0)).isDigit() )
00679 result += "part";
00680 result += intern.left( pos + 1 );
00681 intern = intern.mid( pos + 1 );
00682 }
00683
00684 if ( !intern.isEmpty() && QChar(intern.at(0)).isDigit() )
00685 result += "part";
00686 result += intern;
00687 return result;
00688 }
00689
00690 bool KoStore::enterDirectoryInternal( const QString& directory )
00691 {
00692 if ( enterRelativeDirectory( expandEncodedDirectory( directory ) ) )
00693 {
00694 m_currentPath.append( directory );
00695 return true;
00696 }
00697 return false;
00698 }
00699
00700 void KoStore::disallowNameExpansion( void )
00701 {
00702 m_namingVersion = NAMING_VERSION_RAW;
00703 }
00704
00705 bool KoStore::hasFile( const QString& fileName ) const
00706 {
00707 return fileExists( toExternalNaming( currentPath() + fileName ) );
00708 }