diff options
Diffstat (limited to 'lib/store/KoStore.cpp')
-rw-r--r-- | lib/store/KoStore.cpp | 629 |
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 ) ); +} |