lib Library API Documentation

koStore.cc

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 <david@mandrakesoft.com>, 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., 59 Temple Place - Suite 330,
00019    Boston, MA 02111-1307, 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 
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 //#define DefaultFormat KoStore::Tar
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; // will create a "bad" store (bad()==true)
00052     if ( buf[0] == 0037 && buf[1] == 0213 ) // gzip -> tar.gz
00053       return Tar;
00054     if ( buf[0] == 'P' && buf[1] == 'K' && buf[2] == 3 && buf[3] == 4 )
00055       return Zip;
00056     return DefaultFormat; // fallback
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; // will create a "bad" store (bad()==true)
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 /* should be a dir name.... */, 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     // fallback
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; // will create a "bad" store (bad()==true)
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   // Assume new style names.
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   // This also converts from relative to absolute, i.e. merges the currentPath()
00192   m_sName = toExternalNaming( _name );
00193 
00194   if ( m_bIsOpen )
00195   {
00196     kdWarning(s_area) << "KoStore: File is already opened" << endl;
00197     //return KIO::ERR_INTERNAL;
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       //return KIO::ERR_MALFORMED_URL;
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 ) // just check if it's there
00212     {
00213       kdWarning(s_area) << "KoStore: Duplicate filename " << m_sName << endl;
00214       //return KIO::ERR_FILE_ALREADY_EXIST;
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     //return KIO::ERR_UNSUPPORTED_ACTION;
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     //return KIO::ERR_INTERNAL;
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; // Data is a QArray<char>
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() ); // see below
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   //kdDebug(s_area) << "KoStore::enterDirectory " << directory << endl;
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 // See the specification for details of what this function does.
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:/" ) ) // absolute reference
00550     intern = _internalNaming.mid( 5 ); // remove protocol
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   // Now process the filename. If the first character is numeric, we have
00571   // a main document.
00572   if ( QChar(intern.at(0)).isDigit() )
00573   {
00574     // If this is the first part name, check if we have a store with
00575     // old-style names.
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 ); // copy numbers (or "pictures") + "/"
00602     intern = intern.mid( pos + 1 ); // remove the dir we just processed
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 }
KDE Logo
This file is part of the documentation for lib Library Version 1.4.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Feb 13 09:40:07 2006 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003