00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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 <kdebug.h>
00032
00033
#include <qfileinfo.h>
00034
#include <qfile.h>
00035
#include <qdir.h>
00036
00037
00038
#define DefaultFormat KoStore::Zip
00039
00040
const int KoStore::s_area = 30002;
00041
00042 KoStore::Backend KoStore::determineBackend(
QIODevice* dev )
00043 {
00044
unsigned char buf[5];
00045
if ( dev->readBlock( (
char *)buf, 4 ) < 4 )
00046
return DefaultFormat;
00047
if ( buf[0] == 0037 && buf[1] == 0213 )
00048
return Tar;
00049
if ( buf[0] ==
'P' && buf[1] ==
'K' && buf[2] == 3 && buf[3] == 4 )
00050
return Zip;
00051
return DefaultFormat;
00052 }
00053
00054 KoStore*
KoStore::createStore(
const QString& fileName, Mode mode,
const QCString & appIdentification, Backend backend )
00055 {
00056
if ( backend == Auto ) {
00057
if ( mode == KoStore::Write )
00058 backend = DefaultFormat;
00059
else
00060 {
00061
QFileInfo inf( fileName );
00062
if ( inf.isDir() )
00063 backend = Directory;
00064
else
00065 {
00066
QFile file( fileName );
00067
if ( file.open( IO_ReadOnly ) )
00068 backend = determineBackend( &file );
00069
else
00070 backend = DefaultFormat;
00071 }
00072 }
00073 }
00074
switch ( backend )
00075 {
00076
case Tar:
00077
return new KoTarStore( fileName, mode, appIdentification );
00078
case Zip:
00079
return new KoZipStore( fileName, mode, appIdentification );
00080
case Directory:
00081
return new KoDirectoryStore( fileName , mode );
00082
default:
00083 kdWarning(s_area) <<
"Unsupported backend requested for KoStore : " << backend << endl;
00084
return 0L;
00085 }
00086 }
00087
00088 KoStore*
KoStore::createStore(
QIODevice *device, Mode mode,
const QCString & appIdentification, Backend backend )
00089 {
00090
if ( backend == Auto )
00091 {
00092
if ( mode == KoStore::Write )
00093 backend = DefaultFormat;
00094
else {
00095
if ( device->open( IO_ReadOnly ) ) {
00096 backend = determineBackend( device );
00097 device->close();
00098 }
00099 }
00100 }
00101
switch ( backend )
00102 {
00103
case Tar:
00104
return new KoTarStore( device, mode, appIdentification );
00105
case Directory:
00106 kdError(s_area) <<
"Can't create a Directory store for a memory buffer!" << endl;
00107
00108
case Zip:
00109
return new KoZipStore( device, mode, appIdentification );
00110
default:
00111 kdWarning(s_area) <<
"Unsupported backend requested for KoStore : " << backend << endl;
00112
return 0L;
00113 }
00114 }
00115
00116
namespace {
00117
const char*
const ROOTPART =
"root";
00118
const char*
const MAINNAME =
"maindoc.xml";
00119 }
00120
00121 bool KoStore::init( Mode _mode )
00122 {
00123 d = 0;
00124 m_bIsOpen =
false;
00125 m_mode = _mode;
00126
m_stream = 0;
00127
00128
00129 m_namingVersion = NAMING_VERSION_2_2;
00130
return true;
00131 }
00132
00133 KoStore::~KoStore()
00134 {
00135
delete m_stream;
00136 }
00137
00138 bool KoStore::open(
const QString & _name )
00139 {
00140
00141
m_sName = toExternalNaming( _name );
00142
00143
if ( m_bIsOpen )
00144 {
00145 kdWarning(s_area) <<
"KoStore: File is already opened" << endl;
00146
00147
return false;
00148 }
00149
00150
if (
m_sName.length() > 512 )
00151 {
00152 kdError(s_area) <<
"KoStore: Filename " <<
m_sName <<
" is too long" << endl;
00153
00154
return false;
00155 }
00156
00157
if ( m_mode == Write )
00158 {
00159 kdDebug(s_area) <<
"KoStore: opening for writing '" <<
m_sName <<
"'" << endl;
00160
if (
m_strFiles.findIndex(
m_sName ) != -1 )
00161 {
00162 kdWarning(s_area) <<
"KoStore: Duplicate filename " <<
m_sName << endl;
00163
00164
return false;
00165 }
00166
00167
m_strFiles.append(
m_sName );
00168
00169
m_iSize = 0;
00170
if ( !
openWrite(
m_sName ) )
00171
return false;
00172 }
00173
else if ( m_mode == Read )
00174 {
00175 kdDebug(s_area) <<
"Opening for reading '" <<
m_sName <<
"'" << endl;
00176
if ( !
openRead(
m_sName ) )
00177
return false;
00178 }
00179
else
00180
00181
return false;
00182
00183 m_bIsOpen =
true;
00184
return true;
00185 }
00186
00187 bool KoStore::isOpen()
const
00188
{
00189
return m_bIsOpen;
00190 }
00191
00192 bool KoStore::close()
00193 {
00194 kdDebug(s_area) <<
"KoStore: Closing" << endl;
00195
00196
if ( !m_bIsOpen )
00197 {
00198 kdWarning(s_area) <<
"KoStore: You must open before closing" << endl;
00199
00200
return false;
00201 }
00202
00203
bool ret = m_mode == Write ?
closeWrite() :
closeRead();
00204
00205
delete m_stream;
00206
m_stream = 0L;
00207 m_bIsOpen =
false;
00208
return ret;
00209 }
00210
00211 QIODevice*
KoStore::device()
const
00212
{
00213
if ( !m_bIsOpen )
00214 kdWarning(s_area) <<
"KoStore: You must open before asking for a device" << endl;
00215
if ( m_mode != Read )
00216 kdWarning(s_area) <<
"KoStore: Can not get device from store that is opened for writing" << endl;
00217
return m_stream;
00218 }
00219
00220
QByteArray KoStore::read(
unsigned long int max )
00221 {
00222
QByteArray data;
00223
00224
if ( !m_bIsOpen )
00225 {
00226 kdWarning(s_area) <<
"KoStore: You must open before reading" << endl;
00227 data.resize( 0 );
00228
return data;
00229 }
00230
if ( m_mode != Read )
00231 {
00232 kdError(s_area) <<
"KoStore: Can not read from store that is opened for writing" << endl;
00233 data.resize( 0 );
00234
return data;
00235 }
00236
00237
if (
m_stream->atEnd() )
00238 {
00239 data.resize( 0 );
00240
return data;
00241 }
00242
00243
if ( max >
m_iSize -
m_stream->at() )
00244 max =
m_iSize -
m_stream->at();
00245
if ( max == 0 )
00246 {
00247 data.resize( 0 );
00248
return data;
00249 }
00250
00251
char *p =
new char[ max ];
00252
m_stream->readBlock( p, max );
00253
00254 data.setRawData( p, max );
00255
return data;
00256 }
00257
00258 Q_LONG
KoStore::write(
const QByteArray& data )
00259 {
00260
return write( data.data(), data.size() );
00261 }
00262
00263 Q_LONG
KoStore::read(
char *_buffer, Q_ULONG _len )
00264 {
00265
if ( !m_bIsOpen )
00266 {
00267 kdError(s_area) <<
"KoStore: You must open before reading" << endl;
00268
return -1;
00269 }
00270
if ( m_mode != Read )
00271 {
00272 kdError(s_area) <<
"KoStore: Can not read from store that is opened for writing" << endl;
00273
return -1;
00274 }
00275
00276
if (
m_stream->atEnd() )
00277
return 0;
00278
00279
if ( _len >
m_iSize -
m_stream->at() )
00280 _len =
m_iSize -
m_stream->at();
00281
if ( _len == 0 )
00282
return 0;
00283
00284
return m_stream->readBlock( _buffer, _len );
00285 }
00286
00287 Q_LONG
KoStore::write(
const char* _data, Q_ULONG _len )
00288 {
00289
if ( _len == 0L )
return 0;
00290
00291
if ( !m_bIsOpen )
00292 {
00293 kdError(s_area) <<
"KoStore: You must open before writing" << endl;
00294
return 0L;
00295 }
00296
if ( m_mode != Write )
00297 {
00298 kdError(s_area) <<
"KoStore: Can not write to store that is opened for reading" << endl;
00299
return 0L;
00300 }
00301
00302
int nwritten =
m_stream->writeBlock( _data, _len );
00303 Q_ASSERT( nwritten == (
int)_len );
00304
m_iSize += nwritten;
00305
00306
return nwritten;
00307 }
00308
00309 QIODevice::Offset
KoStore::size()
const
00310
{
00311
if ( !m_bIsOpen )
00312 {
00313 kdWarning(s_area) <<
"KoStore: You must open before asking for a size" << endl;
00314
return static_cast<QIODevice::Offset>(-1);
00315 }
00316
if ( m_mode != Read )
00317 {
00318 kdWarning(s_area) <<
"KoStore: Can not get size from store that is opened for writing" << endl;
00319
return static_cast<QIODevice::Offset>(-1);
00320 }
00321
return m_iSize;
00322 }
00323
00324 bool KoStore::enterDirectory(
const QString& directory )
00325 {
00326
00327
int pos;
00328
bool success =
true;
00329
QString tmp( directory );
00330
00331
while ( ( pos = tmp.find(
'/' ) ) != -1 &&
00332 ( success = enterDirectoryInternal( tmp.left( pos ) ) ) )
00333 tmp = tmp.mid( pos + 1 );
00334
00335
if ( success && !tmp.isEmpty() )
00336
return enterDirectoryInternal( tmp );
00337
return success;
00338 }
00339
00340 bool KoStore::leaveDirectory()
00341 {
00342
if (
m_currentPath.isEmpty() )
00343
return false;
00344
00345
m_currentPath.pop_back();
00346
00347
return enterAbsoluteDirectory( expandEncodedDirectory(
currentPath() ) );
00348 }
00349
00350 QString KoStore::currentPath()
const
00351
{
00352
QString path;
00353 QStringList::ConstIterator it =
m_currentPath.begin();
00354 QStringList::ConstIterator end =
m_currentPath.end();
00355
for ( ; it != end; ++it ) {
00356 path += *it;
00357 path +=
'/';
00358 }
00359
return path;
00360 }
00361
00362 void KoStore::pushDirectory()
00363 {
00364
m_directoryStack.push(
currentPath() );
00365 }
00366
00367 void KoStore::popDirectory()
00368 {
00369
m_currentPath.clear();
00370
enterAbsoluteDirectory( QString::null );
00371
enterDirectory(
m_directoryStack.pop() );
00372 }
00373
00374 bool KoStore::addLocalFile(
const QString &fileName,
const QString &destName )
00375 {
00376
QFileInfo fi( fileName );
00377 uint
size = fi.size();
00378
QFile file( fileName );
00379
if ( !file.open( IO_ReadOnly ))
00380 {
00381
return false;
00382 }
00383
00384
if ( !
open ( destName ) )
00385 {
00386
return false;
00387 }
00388
00389
QByteArray data ( 8 * 1024 );
00390
00391 uint total = 0;
00392
for (
int block = 0; ( block = file.readBlock ( data.data(), data.size() ) ) > 0; total += block )
00393 {
00394 data.resize(block);
00395
if (
write( data ) != block )
00396
return false;
00397 data.resize(8*1024);
00398 }
00399 Q_ASSERT( total == size );
00400
00401
close();
00402 file.close();
00403
00404
return true;
00405 }
00406
00407 bool KoStore::extractFile (
const QString &srcName,
const QString &fileName )
00408 {
00409
if ( !
open ( srcName ) )
00410
return false;
00411
00412
QFile file( fileName );
00413
00414
if( !file.open ( IO_WriteOnly ) )
00415 {
00416
close();
00417
return false;
00418 }
00419
00420
QByteArray data ( 8 * 1024 );
00421 uint total = 0;
00422
for(
int block = 0; ( block =
read ( data.data(), data.size() ) ) > 0; total += block )
00423 {
00424 file.writeBlock ( data.data(), block );
00425 }
00426
00427
if(
size() != static_cast<QIODevice::Offset>(-1) )
00428 Q_ASSERT( total ==
size() );
00429
00430 file.close();
00431
close();
00432
00433
return true;
00434 }
00435
00436 QStringList KoStore::addLocalDirectory(
const QString &dirPath,
const QString &destName )
00437 {
00438
QString dot =
".";
00439
QString dotdot =
"..";
00440
QStringList content;
00441
00442
QDir dir(dirPath);
00443
if ( !dir.exists() )
00444
return 0;
00445
00446
QStringList files = dir.entryList();
00447
for ( QStringList::Iterator it = files.begin(); it != files.end(); ++it )
00448 {
00449
if ( *it != dot && *it != dotdot )
00450 {
00451
QString currentFile = dirPath +
"/" + *it;
00452
QString dest = destName.isEmpty() ? *it : (destName +
"/" + *it);
00453
00454
QFileInfo fi ( currentFile );
00455
if ( fi.isFile() )
00456 {
00457
addLocalFile ( currentFile, dest );
00458 content.append(dest);
00459 }
00460
else if ( fi.isDir() )
00461 {
00462 content +=
addLocalDirectory ( currentFile, dest );
00463 }
00464 }
00465 }
00466
00467
return content;
00468 }
00469
00470
00471 bool KoStore::at( QIODevice::Offset pos )
00472 {
00473
return m_stream->at( pos );
00474 }
00475
00476 QIODevice::Offset
KoStore::at()
const
00477
{
00478
return m_stream->at();
00479 }
00480
00481 bool KoStore::atEnd()
const
00482
{
00483
return m_stream->atEnd();
00484 }
00485
00486
00487
QString KoStore::toExternalNaming(
const QString & _internalNaming )
00488 {
00489
if ( _internalNaming == ROOTPART )
00490
return expandEncodedDirectory(
currentPath() ) + MAINNAME;
00491
00492
QString intern;
00493
if ( _internalNaming.startsWith(
"tar:/" ) )
00494 intern = _internalNaming.mid( 5 );
00495
else
00496 intern =
currentPath() + _internalNaming;
00497
00498
return expandEncodedPath( intern );
00499 }
00500
00501
QString KoStore::expandEncodedPath(
QString intern )
00502 {
00503
QString result;
00504
int pos;
00505
00506
if ( ( pos = intern.findRev(
'/', -1 ) ) != -1 ) {
00507 result = expandEncodedDirectory( intern.left( pos ) ) +
'/';
00508 intern = intern.mid( pos + 1 );
00509 }
00510
00511
00512
00513
if (
QChar(intern.at(0)).isDigit() )
00514 {
00515
00516
00517
if ( ( m_namingVersion == NAMING_VERSION_2_2 ) &&
00518 ( m_mode == Read ) &&
00519 ( fileExists( result +
"part" + intern +
".xml" ) ) )
00520 m_namingVersion = NAMING_VERSION_2_1;
00521
00522
if ( m_namingVersion == NAMING_VERSION_2_1 )
00523 result = result +
"part" + intern +
".xml";
00524
else
00525 result = result +
"part" + intern +
"/" + MAINNAME;
00526 }
00527
else
00528 result += intern;
00529
return result;
00530 }
00531
00532
QString KoStore::expandEncodedDirectory(
QString intern )
00533 {
00534
QString result;
00535
int pos;
00536
while ( ( pos = intern.find(
'/' ) ) != -1 ) {
00537
if (
QChar(intern.at(0)).isDigit() )
00538 result +=
"part";
00539 result += intern.left( pos + 1 );
00540 intern = intern.mid( pos + 1 );
00541 }
00542
00543
if (
QChar(intern.at(0)).isDigit() )
00544 result +=
"part";
00545 result += intern;
00546
return result;
00547 }
00548
00549
bool KoStore::enterDirectoryInternal(
const QString& directory )
00550 {
00551
if (
enterRelativeDirectory( expandEncodedDirectory( directory ) ) )
00552 {
00553
m_currentPath.append( directory );
00554
return true;
00555 }
00556
return false;
00557 }