summaryrefslogtreecommitdiffstats
path: root/lib/store/KoStore.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/store/KoStore.cpp')
-rw-r--r--lib/store/KoStore.cpp629
1 files changed, 629 insertions, 0 deletions
diff --git a/lib/store/KoStore.cpp b/lib/store/KoStore.cpp
new file mode 100644
index 00000000..ec97c6a5
--- /dev/null
+++ b/lib/store/KoStore.cpp
@@ -0,0 +1,629 @@
+// -*- c-basic-offset: 2 -*-
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ Copyright (C) 2000-2002 David Faure <faure@kde.org>, Werner Trobin <trobin@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "KoStore.h"
+#include "KoTarStore.h"
+#include "KoZipStore.h"
+#include "KoDirectoryStore.h"
+
+#include <qfileinfo.h>
+#include <qfile.h>
+#include <qdir.h>
+
+#include <kurl.h>
+#include <kdebug.h>
+#include <kdeversion.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kio/netaccess.h>
+
+//#define DefaultFormat KoStore::Tar
+#define DefaultFormat KoStore::Zip
+
+const int KoStore::s_area = 30002;
+
+KoStore::Backend KoStore::determineBackend( QIODevice* dev )
+{
+ unsigned char buf[5];
+ if ( dev->readBlock( (char *)buf, 4 ) < 4 )
+ return DefaultFormat; // will create a "bad" store (bad()==true)
+ if ( buf[0] == 0037 && buf[1] == 0213 ) // gzip -> tar.gz
+ return Tar;
+ if ( buf[0] == 'P' && buf[1] == 'K' && buf[2] == 3 && buf[3] == 4 )
+ return Zip;
+ return DefaultFormat; // fallback
+}
+
+KoStore* KoStore::createStore( const QString& fileName, Mode mode, const QCString & appIdentification, Backend backend )
+{
+ if ( backend == Auto ) {
+ if ( mode == KoStore::Write )
+ backend = DefaultFormat;
+ else
+ {
+ QFileInfo inf( fileName );
+ if ( inf.isDir() )
+ backend = Directory;
+ else
+ {
+ QFile file( fileName );
+ if ( file.open( IO_ReadOnly ) )
+ backend = determineBackend( &file );
+ else
+ backend = DefaultFormat; // will create a "bad" store (bad()==true)
+ }
+ }
+ }
+ switch ( backend )
+ {
+ case Tar:
+ return new KoTarStore( fileName, mode, appIdentification );
+ case Zip:
+ return new KoZipStore( fileName, mode, appIdentification );
+ case Directory:
+ return new KoDirectoryStore( fileName /* should be a dir name.... */, mode );
+ default:
+ kdWarning(s_area) << "Unsupported backend requested for KoStore : " << backend << endl;
+ return 0L;
+ }
+}
+
+KoStore* KoStore::createStore( QIODevice *device, Mode mode, const QCString & appIdentification, Backend backend )
+{
+ if ( backend == Auto )
+ {
+ if ( mode == KoStore::Write )
+ backend = DefaultFormat;
+ else {
+ if ( device->open( IO_ReadOnly ) ) {
+ backend = determineBackend( device );
+ device->close();
+ }
+ }
+ }
+ switch ( backend )
+ {
+ case Tar:
+ return new KoTarStore( device, mode, appIdentification );
+ case Directory:
+ kdError(s_area) << "Can't create a Directory store for a memory buffer!" << endl;
+ // fallback
+ case Zip:
+ return new KoZipStore( device, mode, appIdentification );
+ default:
+ kdWarning(s_area) << "Unsupported backend requested for KoStore : " << backend << endl;
+ return 0L;
+ }
+}
+
+KoStore* KoStore::createStore( QWidget* window, const KURL& url, Mode mode, const QCString & appIdentification, Backend backend )
+{
+ if ( url.isLocalFile() )
+ return createStore(url.path(), mode, appIdentification, backend );
+
+ QString tmpFile;
+ if ( mode == KoStore::Write )
+ {
+ if ( backend == Auto )
+ backend = DefaultFormat;
+ }
+ else
+ {
+ const bool downloaded =
+ KIO::NetAccess::download( url, tmpFile, window );
+
+ if (!downloaded)
+ {
+ kdError(s_area) << "Could not download file!" << endl;
+ backend = DefaultFormat; // will create a "bad" store (bad()==true)
+ }
+ else if ( backend == Auto )
+ {
+ QFile file( tmpFile );
+ if ( file.open( IO_ReadOnly ) )
+ {
+ backend = determineBackend( &file );
+ file.close();
+ }
+ }
+ }
+ switch ( backend )
+ {
+ case Tar:
+ return new KoTarStore( window, url, tmpFile, mode, appIdentification );
+ case Zip:
+ return new KoZipStore( window, url, tmpFile, mode, appIdentification );
+ default:
+ kdWarning(s_area) << "Unsupported backend requested for KoStore (KURL) : " << backend << endl;
+ KMessageBox::sorry( window,
+ i18n("The directory mode is not supported for remote locations."),
+ i18n("KOffice Storage"));
+ return 0L;
+ }
+}
+
+namespace {
+ const char* const ROOTPART = "root";
+ const char* const MAINNAME = "maindoc.xml";
+}
+
+bool KoStore::init( Mode _mode )
+{
+ d = 0;
+ m_bIsOpen = false;
+ m_mode = _mode;
+ m_stream = 0;
+
+ // Assume new style names.
+ m_namingVersion = NAMING_VERSION_2_2;
+ return true;
+}
+
+KoStore::~KoStore()
+{
+ delete m_stream;
+}
+
+bool KoStore::open( const QString & _name )
+{
+ // This also converts from relative to absolute, i.e. merges the currentPath()
+ m_sName = toExternalNaming( _name );
+
+ if ( m_bIsOpen )
+ {
+ kdWarning(s_area) << "KoStore: File is already opened" << endl;
+ //return KIO::ERR_INTERNAL;
+ return false;
+ }
+
+ if ( m_sName.length() > 512 )
+ {
+ kdError(s_area) << "KoStore: Filename " << m_sName << " is too long" << endl;
+ //return KIO::ERR_MALFORMED_URL;
+ return false;
+ }
+
+ if ( m_mode == Write )
+ {
+ kdDebug(s_area) << "KoStore: opening for writing '" << m_sName << "'" << endl;
+ if ( m_strFiles.findIndex( m_sName ) != -1 ) // just check if it's there
+ {
+ kdWarning(s_area) << "KoStore: Duplicate filename " << m_sName << endl;
+ //return KIO::ERR_FILE_ALREADY_EXIST;
+ return false;
+ }
+
+ m_strFiles.append( m_sName );
+
+ m_iSize = 0;
+ if ( !openWrite( m_sName ) )
+ return false;
+ }
+ else if ( m_mode == Read )
+ {
+ kdDebug(s_area) << "Opening for reading '" << m_sName << "'" << endl;
+ if ( !openRead( m_sName ) )
+ return false;
+ }
+ else
+ //return KIO::ERR_UNSUPPORTED_ACTION;
+ return false;
+
+ m_bIsOpen = true;
+ return true;
+}
+
+bool KoStore::isOpen() const
+{
+ return m_bIsOpen;
+}
+
+bool KoStore::close()
+{
+ kdDebug(s_area) << "KoStore: Closing" << endl;
+
+ if ( !m_bIsOpen )
+ {
+ kdWarning(s_area) << "KoStore: You must open before closing" << endl;
+ //return KIO::ERR_INTERNAL;
+ return false;
+ }
+
+ bool ret = m_mode == Write ? closeWrite() : closeRead();
+
+ delete m_stream;
+ m_stream = 0L;
+ m_bIsOpen = false;
+ return ret;
+}
+
+QIODevice* KoStore::device() const
+{
+ if ( !m_bIsOpen )
+ kdWarning(s_area) << "KoStore: You must open before asking for a device" << endl;
+ if ( m_mode != Read )
+ kdWarning(s_area) << "KoStore: Can not get device from store that is opened for writing" << endl;
+ return m_stream;
+}
+
+QByteArray KoStore::read( unsigned long int max )
+{
+ QByteArray data; // Data is a QArray<char>
+
+ if ( !m_bIsOpen )
+ {
+ kdWarning(s_area) << "KoStore: You must open before reading" << endl;
+ data.resize( 0 );
+ return data;
+ }
+ if ( m_mode != Read )
+ {
+ kdError(s_area) << "KoStore: Can not read from store that is opened for writing" << endl;
+ data.resize( 0 );
+ return data;
+ }
+
+ if ( m_stream->atEnd() )
+ {
+ data.resize( 0 );
+ return data;
+ }
+
+ if ( max > m_iSize - m_stream->at() )
+ max = m_iSize - m_stream->at();
+ if ( max == 0 )
+ {
+ data.resize( 0 );
+ return data;
+ }
+
+ char *p = new char[ max ];
+ m_stream->readBlock( p, max );
+
+ data.setRawData( p, max );
+ return data;
+}
+
+Q_LONG KoStore::write( const QByteArray& data )
+{
+ return write( data.data(), data.size() ); // see below
+}
+
+Q_LONG KoStore::read( char *_buffer, Q_ULONG _len )
+{
+ if ( !m_bIsOpen )
+ {
+ kdError(s_area) << "KoStore: You must open before reading" << endl;
+ return -1;
+ }
+ if ( m_mode != Read )
+ {
+ kdError(s_area) << "KoStore: Can not read from store that is opened for writing" << endl;
+ return -1;
+ }
+
+ if ( m_stream->atEnd() )
+ return 0;
+
+ if ( _len > m_iSize - m_stream->at() )
+ _len = m_iSize - m_stream->at();
+ if ( _len == 0 )
+ return 0;
+
+ return m_stream->readBlock( _buffer, _len );
+}
+
+Q_LONG KoStore::write( const char* _data, Q_ULONG _len )
+{
+ if ( _len == 0L ) return 0;
+
+ if ( !m_bIsOpen )
+ {
+ kdError(s_area) << "KoStore: You must open before writing" << endl;
+ return 0L;
+ }
+ if ( m_mode != Write )
+ {
+ kdError(s_area) << "KoStore: Can not write to store that is opened for reading" << endl;
+ return 0L;
+ }
+
+ int nwritten = m_stream->writeBlock( _data, _len );
+ Q_ASSERT( nwritten == (int)_len );
+ m_iSize += nwritten;
+
+ return nwritten;
+}
+
+QIODevice::Offset KoStore::size() const
+{
+ if ( !m_bIsOpen )
+ {
+ kdWarning(s_area) << "KoStore: You must open before asking for a size" << endl;
+ return static_cast<QIODevice::Offset>(-1);
+ }
+ if ( m_mode != Read )
+ {
+ kdWarning(s_area) << "KoStore: Can not get size from store that is opened for writing" << endl;
+ return static_cast<QIODevice::Offset>(-1);
+ }
+ return m_iSize;
+}
+
+bool KoStore::enterDirectory( const QString& directory )
+{
+ //kdDebug(s_area) << "KoStore::enterDirectory " << directory << endl;
+ int pos;
+ bool success = true;
+ QString tmp( directory );
+
+ while ( ( pos = tmp.find( '/' ) ) != -1 &&
+ ( success = enterDirectoryInternal( tmp.left( pos ) ) ) )
+ tmp = tmp.mid( pos + 1 );
+
+ if ( success && !tmp.isEmpty() )
+ return enterDirectoryInternal( tmp );
+ return success;
+}
+
+bool KoStore::leaveDirectory()
+{
+ if ( m_currentPath.isEmpty() )
+ return false;
+
+ m_currentPath.pop_back();
+
+ return enterAbsoluteDirectory( expandEncodedDirectory( currentPath() ) );
+}
+
+QString KoStore::currentDirectory() const
+{
+ return expandEncodedDirectory( currentPath() );
+}
+
+QString KoStore::currentPath() const
+{
+ QString path;
+ QStringList::ConstIterator it = m_currentPath.begin();
+ QStringList::ConstIterator end = m_currentPath.end();
+ for ( ; it != end; ++it ) {
+ path += *it;
+ path += '/';
+ }
+ return path;
+}
+
+void KoStore::pushDirectory()
+{
+ m_directoryStack.push( currentPath() );
+}
+
+void KoStore::popDirectory()
+{
+ m_currentPath.clear();
+ enterAbsoluteDirectory( QString::null );
+ enterDirectory( m_directoryStack.pop() );
+}
+
+bool KoStore::addLocalFile( const QString &fileName, const QString &destName )
+{
+ QFileInfo fi( fileName );
+ uint size = fi.size();
+ QFile file( fileName );
+ if ( !file.open( IO_ReadOnly ))
+ {
+ return false;
+ }
+
+ if ( !open ( destName ) )
+ {
+ return false;
+ }
+
+ QByteArray data ( 8 * 1024 );
+
+ uint total = 0;
+ for ( int block = 0; ( block = file.readBlock ( data.data(), data.size() ) ) > 0; total += block )
+ {
+ data.resize(block);
+ if ( write( data ) != block )
+ return false;
+ data.resize(8*1024);
+ }
+ Q_ASSERT( total == size );
+
+ close();
+ file.close();
+
+ return true;
+}
+
+bool KoStore::extractFile ( const QString &srcName, const QString &fileName )
+{
+ if ( !open ( srcName ) )
+ return false;
+
+ QFile file( fileName );
+
+ if( !file.open ( IO_WriteOnly ) )
+ {
+ close();
+ return false;
+ }
+
+ QByteArray data ( 8 * 1024 );
+ uint total = 0;
+ for( int block = 0; ( block = read ( data.data(), data.size() ) ) > 0; total += block )
+ {
+ file.writeBlock ( data.data(), block );
+ }
+
+ if( size() != static_cast<QIODevice::Offset>(-1) )
+ Q_ASSERT( total == size() );
+
+ file.close();
+ close();
+
+ return true;
+}
+
+QStringList KoStore::addLocalDirectory( const QString &dirPath, const QString &destName )
+{
+ QString dot = ".";
+ QString dotdot = "..";
+ QStringList content;
+
+ QDir dir(dirPath);
+ if ( !dir.exists() )
+ return 0;
+
+ QStringList files = dir.entryList();
+ for ( QStringList::Iterator it = files.begin(); it != files.end(); ++it )
+ {
+ if ( *it != dot && *it != dotdot )
+ {
+ QString currentFile = dirPath + "/" + *it;
+ QString dest = destName.isEmpty() ? *it : (destName + "/" + *it);
+
+ QFileInfo fi ( currentFile );
+ if ( fi.isFile() )
+ {
+ addLocalFile ( currentFile, dest );
+ content.append(dest);
+ }
+ else if ( fi.isDir() )
+ {
+ content += addLocalDirectory ( currentFile, dest );
+ }
+ }
+ }
+
+ return content;
+}
+
+
+bool KoStore::at( QIODevice::Offset pos )
+{
+ return m_stream->at( pos );
+}
+
+QIODevice::Offset KoStore::at() const
+{
+ return m_stream->at();
+}
+
+bool KoStore::atEnd() const
+{
+ return m_stream->atEnd();
+}
+
+// See the specification for details of what this function does.
+QString KoStore::toExternalNaming( const QString & _internalNaming ) const
+{
+ if ( _internalNaming == ROOTPART )
+ return expandEncodedDirectory( currentPath() ) + MAINNAME;
+
+ QString intern;
+ if ( _internalNaming.startsWith( "tar:/" ) ) // absolute reference
+ intern = _internalNaming.mid( 5 ); // remove protocol
+ else
+ intern = currentPath() + _internalNaming;
+
+ return expandEncodedPath( intern );
+}
+
+QString KoStore::expandEncodedPath( QString intern ) const
+{
+ if ( m_namingVersion == NAMING_VERSION_RAW )
+ return intern;
+
+ QString result;
+ int pos;
+
+ if ( ( pos = intern.findRev( '/', -1 ) ) != -1 ) {
+ result = expandEncodedDirectory( intern.left( pos ) ) + '/';
+ intern = intern.mid( pos + 1 );
+ }
+
+ // Now process the filename. If the first character is numeric, we have
+ // a main document.
+ if ( QChar(intern.at(0)).isDigit() )
+ {
+ // If this is the first part name, check if we have a store with
+ // old-style names.
+ if ( ( m_namingVersion == NAMING_VERSION_2_2 ) &&
+ ( m_mode == Read ) &&
+ ( fileExists( result + "part" + intern + ".xml" ) ) )
+ m_namingVersion = NAMING_VERSION_2_1;
+
+ if ( m_namingVersion == NAMING_VERSION_2_1 )
+ result = result + "part" + intern + ".xml";
+ else
+ result = result + "part" + intern + "/" + MAINNAME;
+ }
+ else
+ result += intern;
+ return result;
+}
+
+QString KoStore::expandEncodedDirectory( QString intern ) const
+{
+ if ( m_namingVersion == NAMING_VERSION_RAW )
+ return intern;
+
+ QString result;
+ int pos;
+ while ( ( pos = intern.find( '/' ) ) != -1 ) {
+ if ( QChar(intern.at(0)).isDigit() )
+ result += "part";
+ result += intern.left( pos + 1 ); // copy numbers (or "pictures") + "/"
+ intern = intern.mid( pos + 1 ); // remove the dir we just processed
+ }
+
+ if ( QChar(intern.at(0)).isDigit() )
+ result += "part";
+ result += intern;
+ return result;
+}
+
+bool KoStore::enterDirectoryInternal( const QString& directory )
+{
+ if ( enterRelativeDirectory( expandEncodedDirectory( directory ) ) )
+ {
+ m_currentPath.append( directory );
+ return true;
+ }
+ return false;
+}
+
+void KoStore::disallowNameExpansion( void )
+{
+ m_namingVersion = NAMING_VERSION_RAW;
+}
+
+bool KoStore::hasFile( const QString& fileName ) const
+{
+ return fileExists( toExternalNaming( currentPath() + fileName ) );
+}