22
24
#include <stdlib.h>
24
26
#include "koStore.h"
27
#include "koTarStore.h"
28
#include "koZipStore.h"
29
#include "koDirectoryStore.h"
27
31
#include <kdebug.h>
32
#define ROOTPART "root"
33
#define MAINNAME "maindoc.xml"
35
KoStore::KoStore( const QString & _filename, Mode _mode, const QCString & appIdentification )
33
#include <qfileinfo.h>
37
//#define DefaultFormat KoStore::Tar
38
#define DefaultFormat KoStore::Zip
40
const int KoStore::s_area = 30002;
42
KoStore::Backend KoStore::determineBackend( QIODevice* dev )
45
if ( dev->readBlock( (char *)buf, 4 ) < 4 )
46
return DefaultFormat; // will create a "bad" store (bad()==true)
47
if ( buf[0] == 0037 && buf[1] == 0213 ) // gzip -> tar.gz
49
if ( buf[0] == 'P' && buf[1] == 'K' && buf[2] == 3 && buf[3] == 4 )
51
return DefaultFormat; // fallback
54
KoStore* KoStore::createStore( const QString& fileName, Mode mode, const QCString & appIdentification, Backend backend )
56
if ( backend == Auto ) {
57
if ( mode == KoStore::Write )
58
backend = DefaultFormat;
61
QFileInfo inf( fileName );
66
QFile file( fileName );
67
if ( file.open( IO_ReadOnly ) )
68
backend = determineBackend( &file );
70
backend = DefaultFormat; // will create a "bad" store (bad()==true)
77
return new KoTarStore( fileName, mode, appIdentification );
79
return new KoZipStore( fileName, mode, appIdentification );
81
return new KoDirectoryStore( fileName /* should be a dir name.... */, mode );
83
kdWarning(s_area) << "Unsupported backend requested for KoStore : " << backend << endl;
88
KoStore* KoStore::createStore( QIODevice *device, Mode mode, const QCString & appIdentification, Backend backend )
90
if ( backend == Auto )
92
if ( mode == KoStore::Write )
93
backend = DefaultFormat;
95
if ( device->open( IO_ReadOnly ) ) {
96
backend = determineBackend( device );
104
return new KoTarStore( device, mode, appIdentification );
106
kdError(s_area) << "Can't create a Directory store for a memory buffer!" << endl;
109
return new KoZipStore( device, mode, appIdentification );
111
kdWarning(s_area) << "Unsupported backend requested for KoStore : " << backend << endl;
117
const char* const ROOTPART = "root";
118
const char* const MAINNAME = "maindoc.xml";
121
bool KoStore::init( Mode _mode )
37
124
m_bIsOpen = false;
41
kdDebug(s_area) << "KoStore Constructor filename = " << _filename
42
<< " mode = " << int(_mode) << endl;
44
#if KDE_VERSION >= 220 // we have the new KTar
45
m_pTar = new KTarGz( _filename, "application/x-gzip" );
47
m_pTar = new KTarGz( _filename );
50
m_bGood = m_pTar->open( _mode == Write ? IO_WriteOnly : IO_ReadOnly );
52
if ( m_bGood && _mode == Read )
53
m_bGood = m_pTar->directory() != 0;
55
#if KDE_VERSION >= 220 // we have the new KTar
56
if ( m_bGood && _mode == Write )
57
m_pTar->setOrigFileName( appIdentification );
60
128
// Assume new style names.
61
129
m_namingVersion = NAMING_VERSION_2_2;
64
133
KoStore::~KoStore()
72
// See the specification for details of what this function does.
73
QString KoStore::toExternalNaming( const QString & _internalNaming )
75
// "root" is the main document, let's save it as "maindoc.xml"
76
if (_internalNaming == ROOTPART)
79
if ( _internalNaming.left(5) == "tar:/" )
81
QString intern( _internalNaming.mid( 5 ) ); // remove protocol
84
while ( ( pos = intern.find( '/' ) ) != -1 ) {
85
if ( QChar(intern.at(0)).isDigit() )
87
result += intern.left( pos + 1 ); // copy numbers (or "pictures") + "/"
88
intern = intern.mid( pos + 1 ); // remove the dir we just processed
91
// Now process the filename. If the first character is numeric, we have
93
if ( QChar(intern.at(0)).isDigit() )
95
// If this is the first part name, check if we have a store with
97
if ( ( m_namingVersion == NAMING_VERSION_2_2 ) &&
99
( m_pTar->directory()->entry( result + "part" + intern + ".xml" ) ) )
101
m_namingVersion = NAMING_VERSION_2_1;
103
if ( m_namingVersion == NAMING_VERSION_2_1)
105
result = result + "part" + intern + ".xml";
109
result = result + "part" + intern + "/" + MAINNAME;
120
return _internalNaming;
123
138
bool KoStore::open( const QString & _name )
140
// This also converts from relative to absolute, i.e. merges the currentPath()
125
141
m_sName = toExternalNaming( _name );
129
145
kdWarning(s_area) << "KoStore: File is already opened" << endl;
146
//return KIO::ERR_INTERNAL;
133
150
if ( m_sName.length() > 512 )
135
152
kdError(s_area) << "KoStore: Filename " << m_sName << " is too long" << endl;
153
//return KIO::ERR_MALFORMED_URL;
139
157
if ( m_mode == Write )
142
160
if ( m_strFiles.findIndex( m_sName ) != -1 ) // just check if it's there
144
162
kdWarning(s_area) << "KoStore: Duplicate filename " << m_sName << endl;
163
//return KIO::ERR_FILE_ALREADY_EXIST;
148
167
m_strFiles.append( m_sName );
170
if ( !openWrite( m_sName ) )
151
173
else if ( m_mode == Read )
153
175
kdDebug(s_area) << "Opening for reading '" << m_sName << "'" << endl;
155
const KTarEntry * entry = m_pTar->directory()->entry( m_sName );
158
kdWarning(s_area) << "Unknown filename " << m_sName << endl;
161
if ( entry->isDirectory() )
163
kdWarning(s_area) << m_sName << " is a directory !" << endl;
166
KTarFile * f = (KTarFile *) entry;
167
m_byteArray = f->data();
168
// warning, m_byteArray can be bigger than f->data().size() (if a previous file was bigger)
169
// this is why we never use m_byteArray.size()
176
if ( !openRead( m_sName ) )
180
//return KIO::ERR_UNSUPPORTED_ACTION;
175
m_stream = new QBuffer( m_byteArray );
176
m_stream->open( (m_mode == Write) ? IO_WriteOnly : IO_ReadOnly );
177
183
m_bIsOpen = true;
182
void KoStore::close()
187
bool KoStore::isOpen() const
192
bool KoStore::close()
184
194
kdDebug(s_area) << "KoStore: Closing" << endl;
186
196
if ( !m_bIsOpen )
188
198
kdWarning(s_area) << "KoStore: You must open before closing" << endl;
192
if ( m_mode == Write )
194
// write the whole bytearray at once into the tar file
196
kdDebug(s_area) << "Writing file " << m_sName << " into TAR archive. size "
198
m_pTar->writeFile( m_sName , "user", "group", m_iSize, m_byteArray.data() );
199
//return KIO::ERR_INTERNAL;
203
bool ret = m_mode == Write ? closeWrite() : closeRead();
203
207
m_bIsOpen = false;
211
QIODevice* KoStore::device() const
214
kdWarning(s_area) << "KoStore: You must open before asking for a device" << endl;
215
if ( m_mode != Read )
216
kdWarning(s_area) << "KoStore: Can not get device from store that is opened for writing" << endl;
206
220
QByteArray KoStore::read( unsigned long int max )
265
m_stream->readBlock( _buffer, _len );
270
bool KoStore::embed( const QString &dest, KoStore &store, const QString &src )
272
if ( dest == ROOTPART )
274
kdError(s_area) << "KoStore: cannot embed root part" << endl;
278
// Find the destination directory corresponding to the part to be embedded.
282
destDir = toExternalNaming( dest );
283
if ( destDir.mid( destDir.length() - sizeof(MAINNAME) + 1 ) == MAINNAME )
285
destDir = destDir.left( destDir.length() - sizeof(MAINNAME) + 1 );
289
kdError(s_area) << "KoStore: cannot embed to a part called " << destDir << endl;
293
// Find the source directory corresponding to the part to be embedded.
297
srcDir = store.toExternalNaming( src );
298
if ( srcDir.mid( srcDir.length() - sizeof(MAINNAME) + 1 ) == MAINNAME )
300
srcDir = srcDir.left( srcDir.length() - sizeof(MAINNAME) + 1 );
304
kdError(s_area) << "KoStore: cannot embed from a part called " << srcDir << endl;
308
// Now recurse into the embedded part, addings its top level contents to our tar.
310
kdDebug(s_area) << "KoStore: embedding " << srcDir << " in " << destDir << endl;
311
const KTarEntry *entry;
312
if ( src == ROOTPART )
314
entry = store.m_pTar->directory();
318
entry = store.m_pTar->directory()->entry( srcDir );
320
QStringList entries = dynamic_cast<const KTarDirectory *>( entry )->entries();
323
for ( i = 0; i < entries.count(); i++ )
325
if ( store.m_pTar->directory()->entry( srcDir + entries[i] )->isDirectory() )
327
// Recurse to get the files in the next level down.
329
if ( embed( destDir + entries[i] + "/" + MAINNAME,
331
srcDir + entries[i] + "/" + MAINNAME ) )
333
kdDebug(s_area) << "KoStore: embedded " << srcDir << " in " << destDir << endl;
342
kdDebug(s_area) << "KoStore: is file " << endl;
343
if ( ( open( destDir + entries[i] ) && store.open( srcDir + entries[i] ) ) )
345
kdDebug(s_area) << "KoStore: embedding file " << entries[i] << endl;
346
long length = store.size();
347
write( store.read( length ));
357
return i == entries.count();
360
long KoStore::size() const
364
kdWarning(s_area) << "KoStore: You must open before asking for a size" << endl;
367
if ( m_mode != Read )
369
kdWarning(s_area) << "KoStore: Can not get size from store that is opened for writing" << endl;
372
return (long) m_iSize;
375
bool KoStore::write( const QByteArray& data )
377
unsigned int len = data.size();
378
if ( len == 0L ) return true; // nothing to do
379
return write( data.data(), len ); // see below
382
bool KoStore::write( const char* _data, unsigned long _len )
384
if ( _len == 0L ) return true;
284
return m_stream->readBlock( _buffer, _len );
287
Q_LONG KoStore::write( const char* _data, Q_ULONG _len )
289
if ( _len == 0L ) return 0;
386
291
if ( !m_bIsOpen )
397
m_stream->writeBlock( _data, _len );
403
bool KoStore::at( int pos )
302
int nwritten = m_stream->writeBlock( _data, _len );
303
Q_ASSERT( nwritten == (int)_len );
309
QIODevice::Offset KoStore::size() const
313
kdWarning(s_area) << "KoStore: You must open before asking for a size" << endl;
314
return static_cast<QIODevice::Offset>(-1);
316
if ( m_mode != Read )
318
kdWarning(s_area) << "KoStore: Can not get size from store that is opened for writing" << endl;
319
return static_cast<QIODevice::Offset>(-1);
324
bool KoStore::enterDirectory( const QString& directory )
326
//kdDebug(s_area) << "KoStore::enterDirectory " << directory << endl;
329
QString tmp( directory );
331
while ( ( pos = tmp.find( '/' ) ) != -1 &&
332
( success = enterDirectoryInternal( tmp.left( pos ) ) ) )
333
tmp = tmp.mid( pos + 1 );
335
if ( success && !tmp.isEmpty() )
336
return enterDirectoryInternal( tmp );
340
bool KoStore::leaveDirectory()
342
if ( m_currentPath.isEmpty() )
345
m_currentPath.pop_back();
347
return enterAbsoluteDirectory( expandEncodedDirectory( currentPath() ) );
350
QString KoStore::currentPath() const
353
QStringList::ConstIterator it = m_currentPath.begin();
354
QStringList::ConstIterator end = m_currentPath.end();
355
for ( ; it != end; ++it ) {
362
void KoStore::pushDirectory()
364
m_directoryStack.push( currentPath() );
367
void KoStore::popDirectory()
369
m_currentPath.clear();
370
enterAbsoluteDirectory( QString::null );
371
enterDirectory( m_directoryStack.pop() );
374
bool KoStore::addLocalFile( const QString &fileName, const QString &destName )
376
QFileInfo fi( fileName );
377
uint size = fi.size();
378
QFile file( fileName );
379
if ( !file.open( IO_ReadOnly ))
384
if ( !open ( destName ) )
389
QByteArray data ( 8 * 1024 );
392
for ( int block = 0; ( block = file.readBlock ( data.data(), data.size() ) ) > 0; total += block )
395
if ( write( data ) != block )
399
Q_ASSERT( total == size );
407
bool KoStore::extractFile ( const QString &srcName, const QString &fileName )
409
if ( !open ( srcName ) )
412
QFile file( fileName );
414
if( !file.open ( IO_WriteOnly ) )
420
QByteArray data ( 8 * 1024 );
422
for( int block = 0; ( block = read ( data.data(), data.size() ) ) > 0; total += block )
424
file.writeBlock ( data.data(), block );
427
if( size() != static_cast<QIODevice::Offset>(-1) )
428
Q_ASSERT( total == size() );
436
QStringList KoStore::addLocalDirectory( const QString &dirPath, const QString &destName )
439
QString dotdot = "..";
446
QStringList files = dir.entryList();
447
for ( QStringList::Iterator it = files.begin(); it != files.end(); ++it )
449
if ( *it != dot && *it != dotdot )
451
QString currentFile = dirPath + "/" + *it;
452
QString dest = destName.isEmpty() ? *it : (destName + "/" + *it);
454
QFileInfo fi ( currentFile );
457
addLocalFile ( currentFile, dest );
458
content.append(dest);
460
else if ( fi.isDir() )
462
content += addLocalDirectory ( currentFile, dest );
471
bool KoStore::at( QIODevice::Offset pos )
405
473
return m_stream->at( pos );
408
int KoStore::at() const
476
QIODevice::Offset KoStore::at() const
410
478
return m_stream->at();
415
483
return m_stream->atEnd();
486
// See the specification for details of what this function does.
487
QString KoStore::toExternalNaming( const QString & _internalNaming )
489
if ( _internalNaming == ROOTPART )
490
return expandEncodedDirectory( currentPath() ) + MAINNAME;
493
if ( _internalNaming.startsWith( "tar:/" ) ) // absolute reference
494
intern = _internalNaming.mid( 5 ); // remove protocol
496
intern = currentPath() + _internalNaming;
498
return expandEncodedPath( intern );
501
QString KoStore::expandEncodedPath( QString intern )
506
if ( ( pos = intern.findRev( '/', -1 ) ) != -1 ) {
507
result = expandEncodedDirectory( intern.left( pos ) ) + '/';
508
intern = intern.mid( pos + 1 );
511
// Now process the filename. If the first character is numeric, we have
513
if ( QChar(intern.at(0)).isDigit() )
515
// If this is the first part name, check if we have a store with
517
if ( ( m_namingVersion == NAMING_VERSION_2_2 ) &&
518
( m_mode == Read ) &&
519
( fileExists( result + "part" + intern + ".xml" ) ) )
520
m_namingVersion = NAMING_VERSION_2_1;
522
if ( m_namingVersion == NAMING_VERSION_2_1 )
523
result = result + "part" + intern + ".xml";
525
result = result + "part" + intern + "/" + MAINNAME;
532
QString KoStore::expandEncodedDirectory( QString intern )
536
while ( ( pos = intern.find( '/' ) ) != -1 ) {
537
if ( QChar(intern.at(0)).isDigit() )
539
result += intern.left( pos + 1 ); // copy numbers (or "pictures") + "/"
540
intern = intern.mid( pos + 1 ); // remove the dir we just processed
543
if ( QChar(intern.at(0)).isDigit() )
549
bool KoStore::enterDirectoryInternal( const QString& directory )
551
if ( enterRelativeDirectory( expandEncodedDirectory( directory ) ) )
553
m_currentPath.append( directory );