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