F:/KPlato/koffice/libs/store/KoStore.cpp

Aller à la documentation de ce fichier.
00001 // -*- c-basic-offset: 2 -*-
00002 /* This file is part of the KDE project
00003    Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
00004    Copyright (C) 2000-2002 David Faure <faure@kde.org>, Werner Trobin <trobin@kde.org>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License as published by the Free Software Foundation; either
00009    version 2 of the License, or (at your option) any later version.
00010 
00011    This library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this library; see the file COPYING.LIB.  If not, write to
00018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019  * Boston, MA 02110-1301, USA.
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 //#define DefaultFormat KoStore::Tar
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; // will create a "bad" store (bad()==true)
00056     if ( buf[0] == 0037 && buf[1] == 0213 ) // gzip -> tar.gz
00057       return Tar;
00058     if ( buf[0] == 'P' && buf[1] == 'K' && buf[2] == 3 && buf[3] == 4 )
00059       return Zip;
00060     return DefaultFormat; // fallback
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; // will create a "bad" store (bad()==true)
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         // Determines if the ZIP-store is encrypted and gives us the fastest reader that can read the store
00093         return KoEncryptedStore::createEncryptedStoreReader( fileName, appIdentification );
00094     }
00095 #endif
00096     return new KoZipStore( fileName, mode, appIdentification );
00097   case Directory:
00098     return new KoDirectoryStore( fileName /* should be a dir name.... */, 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     // fallback
00131   case Zip:
00132 #ifdef QCA2
00133     if( automatic && mode == Read ) {
00134         // Determines if the ZIP-store is encrypted and gives us the fastest reader that can read the store
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; // will create a "bad" store (bad()==true)
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         // Determines if the ZIP store is encrypted and gives the fastest reader that can read the store
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   // Assume new style names.
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   // This also converts from relative to absolute, i.e. merges the currentPath()
00231   m_sName = toExternalNaming( _name );
00232 
00233   if ( m_bIsOpen )
00234   {
00235     kWarning(s_area) << "KoStore: File is already opened" << endl;
00236     //return KIO::ERR_INTERNAL;
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       //return KIO::ERR_MALFORMED_URL;
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       //return KIO::ERR_FILE_ALREADY_EXIST;
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     //return KIO::ERR_UNSUPPORTED_ACTION;
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     //return KIO::ERR_INTERNAL;
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() ); // see below
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   //kDebug(s_area) << "KoStore::enterDirectory " << directory << endl;
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   // ### This could use KArchive::copy or something, no?
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 // See the specification for details of what this function does.
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:/" ) ) // absolute reference
00629     intern = _internalNaming.mid( 5 ); // remove protocol
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   // Now process the filename. If the first character is numeric, we have
00650   // a main document.
00651   if ( QChar(intern.at(0)).isDigit() )
00652   {
00653     // If this is the first part name, check if we have a store with
00654     // old-style names.
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 ); // copy numbers (or "pictures") + "/"
00681     intern = intern.mid( pos + 1 ); // remove the dir we just processed
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 }

Généré le Wed Nov 22 23:41:14 2006 pour KPlato par  doxygen 1.5.1-p1