lib

KoStore.cpp
1/* This file is part of the KDE project
2 Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
3 Copyright (C) 2000-2002 David Faure <faure@kde.org>, Werner Trobin <trobin@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include <stdio.h>
22#include <assert.h>
23#include <stdlib.h>
24
25#include "KoStore.h"
26#include "KoTarStore.h"
27#include "KoZipStore.h"
28#include "KoDirectoryStore.h"
29
30#include <tqfileinfo.h>
31#include <tqfile.h>
32#include <tqdir.h>
33
34#include <kurl.h>
35#include <kdebug.h>
36#include <tdeversion.h>
37#include <tdelocale.h>
38#include <tdemessagebox.h>
39#include <tdeio/netaccess.h>
40
41//#define DefaultFormat KoStore::Tar
42#define DefaultFormat KoStore::Zip
43
44const int KoStore::s_area = 30002;
45
46KoStore::Backend KoStore::determineBackend( TQIODevice* dev )
47{
48 unsigned char buf[5];
49 if ( dev->readBlock( (char *)buf, 4 ) < 4 )
50 return DefaultFormat; // will create a "bad" store (bad()==true)
51 if ( buf[0] == 0037 && buf[1] == 0213 ) // gzip -> tar.gz
52 return Tar;
53 if ( buf[0] == 'P' && buf[1] == 'K' && buf[2] == 3 && buf[3] == 4 )
54 return Zip;
55 return DefaultFormat; // fallback
56}
57
58KoStore* KoStore::createStore( const TQString& fileName, Mode mode, const TQCString & appIdentification, Backend backend )
59{
60 if ( backend == Auto ) {
61 if ( mode == KoStore::Write )
62 backend = DefaultFormat;
63 else
64 {
65 TQFileInfo inf( fileName );
66 if ( inf.isDir() )
67 backend = Directory;
68 else
69 {
70 TQFile file( fileName );
71 if ( file.open( IO_ReadOnly ) )
72 backend = determineBackend( &file );
73 else
74 backend = DefaultFormat; // will create a "bad" store (bad()==true)
75 }
76 }
77 }
78 switch ( backend )
79 {
80 case Tar:
81 return new KoTarStore( fileName, mode, appIdentification );
82 case Zip:
83 return new KoZipStore( fileName, mode, appIdentification );
84 case Directory:
85 return new KoDirectoryStore( fileName /* should be a dir name.... */, mode );
86 default:
87 kdWarning(s_area) << "Unsupported backend requested for KoStore : " << backend << endl;
88 return 0L;
89 }
90}
91
92KoStore* KoStore::createStore( TQIODevice *device, Mode mode, const TQCString & appIdentification, Backend backend )
93{
94 if ( backend == Auto )
95 {
96 if ( mode == KoStore::Write )
97 backend = DefaultFormat;
98 else {
99 if ( device->open( IO_ReadOnly ) ) {
100 backend = determineBackend( device );
101 device->close();
102 }
103 }
104 }
105 switch ( backend )
106 {
107 case Tar:
108 return new KoTarStore( device, mode, appIdentification );
109 case Directory:
110 kdError(s_area) << "Can't create a Directory store for a memory buffer!" << endl;
111 // fallback
112 case Zip:
113 return new KoZipStore( device, mode, appIdentification );
114 default:
115 kdWarning(s_area) << "Unsupported backend requested for KoStore : " << backend << endl;
116 return 0L;
117 }
118}
119
120KoStore* KoStore::createStore( TQWidget* window, const KURL& url, Mode mode, const TQCString & appIdentification, Backend backend )
121{
122 if ( url.isLocalFile() )
123 return createStore(url.path(), mode, appIdentification, backend );
124
125 TQString tmpFile;
126 if ( mode == KoStore::Write )
127 {
128 if ( backend == Auto )
129 backend = DefaultFormat;
130 }
131 else
132 {
133 const bool downloaded =
134 TDEIO::NetAccess::download( url, tmpFile, window );
135
136 if (!downloaded)
137 {
138 kdError(s_area) << "Could not download file!" << endl;
139 backend = DefaultFormat; // will create a "bad" store (bad()==true)
140 }
141 else if ( backend == Auto )
142 {
143 TQFile file( tmpFile );
144 if ( file.open( IO_ReadOnly ) )
145 {
146 backend = determineBackend( &file );
147 file.close();
148 }
149 }
150 }
151 switch ( backend )
152 {
153 case Tar:
154 return new KoTarStore( window, url, tmpFile, mode, appIdentification );
155 case Zip:
156 return new KoZipStore( window, url, tmpFile, mode, appIdentification );
157 default:
158 kdWarning(s_area) << "Unsupported backend requested for KoStore (KURL) : " << backend << endl;
159 KMessageBox::sorry( window,
160 i18n("The directory mode is not supported for remote locations."),
161 i18n("KOffice Storage"));
162 return 0L;
163 }
164}
165
166namespace {
167 const char* const ROOTPART = "root";
168 const char* const MAINNAME = "maindoc.xml";
169}
170
171bool KoStore::init( Mode _mode )
172{
173 d = 0;
174 m_bIsOpen = false;
175 m_mode = _mode;
176 m_stream = 0;
177
178 // Assume new style names.
179 m_namingVersion = NAMING_VERSION_2_2;
180 return true;
181}
182
184{
185 delete m_stream;
186}
187
188bool KoStore::open( const TQString & _name )
189{
190 // This also converts from relative to absolute, i.e. merges the currentPath()
191 m_sName = toExternalNaming( _name );
192
193 if ( m_bIsOpen )
194 {
195 kdWarning(s_area) << "KoStore: File is already opened" << endl;
196 //return TDEIO::ERR_INTERNAL;
197 return false;
198 }
199
200 if ( m_sName.length() > 512 )
201 {
202 kdError(s_area) << "KoStore: Filename " << m_sName << " is too long" << endl;
203 //return TDEIO::ERR_MALFORMED_URL;
204 return false;
205 }
206
207 if ( m_mode == Write )
208 {
209 kdDebug(s_area) << "KoStore: opening for writing '" << m_sName << "'" << endl;
210 if ( m_strFiles.findIndex( m_sName ) != -1 ) // just check if it's there
211 {
212 kdWarning(s_area) << "KoStore: Duplicate filename " << m_sName << endl;
213 //return TDEIO::ERR_FILE_ALREADY_EXIST;
214 return false;
215 }
216
217 m_strFiles.append( m_sName );
218
219 m_iSize = 0;
220 if ( !openWrite( m_sName ) )
221 return false;
222 }
223 else if ( m_mode == Read )
224 {
225 kdDebug(s_area) << "Opening for reading '" << m_sName << "'" << endl;
226 if ( !openRead( m_sName ) )
227 return false;
228 }
229 else
230 //return TDEIO::ERR_UNSUPPORTED_ACTION;
231 return false;
232
233 m_bIsOpen = true;
234 return true;
235}
236
237bool KoStore::isOpen() const
238{
239 return m_bIsOpen;
240}
241
243{
244 kdDebug(s_area) << "KoStore: Closing" << endl;
245
246 if ( !m_bIsOpen )
247 {
248 kdWarning(s_area) << "KoStore: You must open before closing" << endl;
249 //return TDEIO::ERR_INTERNAL;
250 return false;
251 }
252
253 bool ret = m_mode == Write ? closeWrite() : closeRead();
254
255 delete m_stream;
256 m_stream = 0L;
257 m_bIsOpen = false;
258 return ret;
259}
260
261TQIODevice* KoStore::device() const
262{
263 if ( !m_bIsOpen )
264 kdWarning(s_area) << "KoStore: You must open before asking for a device" << endl;
265 if ( m_mode != Read )
266 kdWarning(s_area) << "KoStore: Can not get device from store that is opened for writing" << endl;
267 return m_stream;
268}
269
270TQByteArray KoStore::read( unsigned long int max )
271{
272 TQByteArray data;
273
274 if ( !m_bIsOpen )
275 {
276 kdWarning(s_area) << "KoStore: You must open before reading" << endl;
277 data.resize( 0 );
278 return data;
279 }
280 if ( m_mode != Read )
281 {
282 kdError(s_area) << "KoStore: Can not read from store that is opened for writing" << endl;
283 data.resize( 0 );
284 return data;
285 }
286
287 if ( m_stream->atEnd() )
288 {
289 data.resize( 0 );
290 return data;
291 }
292
293 if ( max > m_iSize - m_stream->at() )
294 max = m_iSize - m_stream->at();
295 if ( max == 0 )
296 {
297 data.resize( 0 );
298 return data;
299 }
300
301 char *p = new char[ max ];
302 m_stream->readBlock( p, max );
303
304 data.setRawData( p, max );
305 return data;
306}
307
308TQ_LONG KoStore::write( const TQByteArray& data )
309{
310 return write( data.data(), data.size() ); // see below
311}
312
313TQ_LONG KoStore::read( char *_buffer, TQ_ULONG _len )
314{
315 if ( !m_bIsOpen )
316 {
317 kdError(s_area) << "KoStore: You must open before reading" << endl;
318 return -1;
319 }
320 if ( m_mode != Read )
321 {
322 kdError(s_area) << "KoStore: Can not read from store that is opened for writing" << endl;
323 return -1;
324 }
325
326 if ( m_stream->atEnd() )
327 return 0;
328
329 if ( _len > m_iSize - m_stream->at() )
330 _len = m_iSize - m_stream->at();
331 if ( _len == 0 )
332 return 0;
333
334 return m_stream->readBlock( _buffer, _len );
335}
336
337TQ_LONG KoStore::write( const char* _data, TQ_ULONG _len )
338{
339 if ( _len == 0L ) return 0;
340
341 if ( !m_bIsOpen )
342 {
343 kdError(s_area) << "KoStore: You must open before writing" << endl;
344 return 0L;
345 }
346 if ( m_mode != Write )
347 {
348 kdError(s_area) << "KoStore: Can not write to store that is opened for reading" << endl;
349 return 0L;
350 }
351
352 int nwritten = m_stream->writeBlock( _data, _len );
353 Q_ASSERT( nwritten == (int)_len );
354 m_iSize += nwritten;
355
356 return nwritten;
357}
358
359TQIODevice::Offset KoStore::size() const
360{
361 if ( !m_bIsOpen )
362 {
363 kdWarning(s_area) << "KoStore: You must open before asking for a size" << endl;
364 return static_cast<TQIODevice::Offset>(-1);
365 }
366 if ( m_mode != Read )
367 {
368 kdWarning(s_area) << "KoStore: Can not get size from store that is opened for writing" << endl;
369 return static_cast<TQIODevice::Offset>(-1);
370 }
371 return m_iSize;
372}
373
374bool KoStore::enterDirectory( const TQString& directory )
375{
376 //kdDebug(s_area) << "KoStore::enterDirectory " << directory << endl;
377 int pos;
378 bool success = true;
379 TQString tmp( directory );
380
381 while ( ( pos = tmp.find( '/' ) ) != -1 &&
382 ( success = enterDirectoryInternal( tmp.left( pos ) ) ) )
383 tmp = tmp.mid( pos + 1 );
384
385 if ( success && !tmp.isEmpty() )
386 return enterDirectoryInternal( tmp );
387 return success;
388}
389
391{
392 if ( m_currentPath.isEmpty() )
393 return false;
394
395 m_currentPath.pop_back();
396
397 return enterAbsoluteDirectory( expandEncodedDirectory( currentPath() ) );
398}
399
401{
402 return expandEncodedDirectory( currentPath() );
403}
404
405TQString KoStore::currentPath() const
406{
407 TQString path;
408 TQStringList::ConstIterator it = m_currentPath.begin();
409 TQStringList::ConstIterator end = m_currentPath.end();
410 for ( ; it != end; ++it ) {
411 path += *it;
412 path += '/';
413 }
414 return path;
415}
416
421
423{
424 m_currentPath.clear();
425 enterAbsoluteDirectory( TQString() );
427}
428
429bool KoStore::addLocalFile( const TQString &fileName, const TQString &destName )
430{
431 TQFileInfo fi( fileName );
432 uint size = fi.size();
433 TQFile file( fileName );
434 if ( !file.open( IO_ReadOnly ))
435 {
436 return false;
437 }
438
439 if ( !open ( destName ) )
440 {
441 return false;
442 }
443
444 TQByteArray data ( 8 * 1024 );
445
446 uint total = 0;
447 for ( int block = 0; ( block = file.readBlock ( data.data(), data.size() ) ) > 0; total += block )
448 {
449 data.resize(block);
450 if ( write( data ) != block )
451 return false;
452 data.resize(8*1024);
453 }
454 Q_ASSERT( total == size );
455
456 close();
457 file.close();
458
459 return true;
460}
461
462bool KoStore::extractFile ( const TQString &srcName, const TQString &fileName )
463{
464 if ( !open ( srcName ) )
465 return false;
466
467 TQFile file( fileName );
468
469 if( !file.open ( IO_WriteOnly ) )
470 {
471 close();
472 return false;
473 }
474
475 TQByteArray data ( 8 * 1024 );
476 uint total = 0;
477 for( int block = 0; ( block = read ( data.data(), data.size() ) ) > 0; total += block )
478 {
479 file.writeBlock ( data.data(), block );
480 }
481
482 if( size() != static_cast<TQIODevice::Offset>(-1) )
483 Q_ASSERT( total == size() );
484
485 file.close();
486 close();
487
488 return true;
489}
490
491TQStringList KoStore::addLocalDirectory( const TQString &dirPath, const TQString &destName )
492{
493 TQString dot = ".";
494 TQString dotdot = "..";
495 TQStringList content;
496
497 TQDir dir(dirPath);
498 if ( !dir.exists() )
499 return 0;
500
501 TQStringList files = dir.entryList();
502 for ( TQStringList::Iterator it = files.begin(); it != files.end(); ++it )
503 {
504 if ( *it != dot && *it != dotdot )
505 {
506 TQString currentFile = dirPath + "/" + *it;
507 TQString dest = destName.isEmpty() ? *it : (destName + "/" + *it);
508
509 TQFileInfo fi ( currentFile );
510 if ( fi.isFile() )
511 {
512 addLocalFile ( currentFile, dest );
513 content.append(dest);
514 }
515 else if ( fi.isDir() )
516 {
517 content += addLocalDirectory ( currentFile, dest );
518 }
519 }
520 }
521
522 return content;
523}
524
525
526bool KoStore::at( TQIODevice::Offset pos )
527{
528 return m_stream->at( pos );
529}
530
531TQIODevice::Offset KoStore::at() const
532{
533 return m_stream->at();
534}
535
536bool KoStore::atEnd() const
537{
538 return m_stream->atEnd();
539}
540
541// See the specification for details of what this function does.
542TQString KoStore::toExternalNaming( const TQString & _internalNaming ) const
543{
544 if ( _internalNaming == ROOTPART )
545 return expandEncodedDirectory( currentPath() ) + MAINNAME;
546
547 TQString intern;
548 if ( _internalNaming.startsWith( "tar:/" ) ) // absolute reference
549 intern = _internalNaming.mid( 5 ); // remove protocol
550 else
551 intern = currentPath() + _internalNaming;
552
553 return expandEncodedPath( intern );
554}
555
556TQString KoStore::expandEncodedPath( TQString intern ) const
557{
558 if ( m_namingVersion == NAMING_VERSION_RAW )
559 return intern;
560
561 TQString result;
562 int pos;
563
564 if ( ( pos = intern.findRev( '/', -1 ) ) != -1 ) {
565 result = expandEncodedDirectory( intern.left( pos ) ) + '/';
566 intern = intern.mid( pos + 1 );
567 }
568
569 // Now process the filename. If the first character is numeric, we have
570 // a main document.
571 if ( TQChar(intern.at(0)).isDigit() )
572 {
573 // If this is the first part name, check if we have a store with
574 // old-style names.
575 if ( ( m_namingVersion == NAMING_VERSION_2_2 ) &&
576 ( m_mode == Read ) &&
577 ( fileExists( result + "part" + intern + ".xml" ) ) )
578 m_namingVersion = NAMING_VERSION_2_1;
579
580 if ( m_namingVersion == NAMING_VERSION_2_1 )
581 result = result + "part" + intern + ".xml";
582 else
583 result = result + "part" + intern + "/" + MAINNAME;
584 }
585 else
586 result += intern;
587 return result;
588}
589
590TQString KoStore::expandEncodedDirectory( TQString intern ) const
591{
592 if ( m_namingVersion == NAMING_VERSION_RAW )
593 return intern;
594
595 TQString result;
596 int pos;
597 while ( ( pos = intern.find( '/' ) ) != -1 ) {
598 if ( TQChar(intern.at(0)).isDigit() )
599 result += "part";
600 result += intern.left( pos + 1 ); // copy numbers (or "pictures") + "/"
601 intern = intern.mid( pos + 1 ); // remove the dir we just processed
602 }
603
604 if ( TQChar(intern.at(0)).isDigit() )
605 result += "part";
606 result += intern;
607 return result;
608}
609
610bool KoStore::enterDirectoryInternal( const TQString& directory )
611{
612 if ( enterRelativeDirectory( expandEncodedDirectory( directory ) ) )
613 {
614 m_currentPath.append( directory );
615 return true;
616 }
617 return false;
618}
619
621{
622 m_namingVersion = NAMING_VERSION_RAW;
623}
624
625bool KoStore::hasFile( const TQString& fileName ) const
626{
627 return fileExists( toExternalNaming( currentPath() + fileName ) );
628}
static void sorry(TQWidget *parent, const TQString &text, const TQString &caption=TQString::null, int options=Notify)
Saves and loads KOffice documents using various backends.
Definition KoStore.h:40
bool close()
Close the file inside the store.
Definition KoStore.cpp:242
virtual bool closeRead()=0
virtual bool openRead(const TQString &name)=0
Open the file name in the store, for reading.
bool isOpen() const
Check whether a file inside the store is currently opened with open(), ready to be read or written.
Definition KoStore.cpp:237
virtual bool closeWrite()=0
bool hasFile(const TQString &fileName) const
Definition KoStore.cpp:625
virtual bool openWrite(const TQString &name)=0
Open the file name in the store, for writing On success, this method must set m_stream to a stream in...
TQStringList m_currentPath
The "current directory" (path)
Definition KoStore.h:352
void pushDirectory()
Stacks the current directory.
Definition KoStore.cpp:417
void popDirectory()
Restores the previously pushed directory.
Definition KoStore.cpp:422
bool leaveDirectory()
Leaves a directory.
Definition KoStore.cpp:390
TQIODevice * device() const
Get a device for reading a file from the store directly (slightly faster than read() calls) You need ...
Definition KoStore.cpp:261
virtual bool fileExists(const TQString &absPath) const =0
Check if a file exists inside the store.
TQString m_sName
Current filename (between an open() and a close())
Definition KoStore.h:358
bool open(const TQString &name)
Open a new file inside the store.
Definition KoStore.cpp:188
TQIODevice::Offset m_iSize
Current size of the file named m_sName.
Definition KoStore.h:360
TQIODevice::Offset size() const
Definition KoStore.cpp:359
virtual bool init(Mode mode)
Init store - called by constructor.
Definition KoStore.cpp:171
bool extractFile(const TQString &srcName, const TQString &fileName)
Extracts a file out of the store.
Definition KoStore.cpp:462
TQString currentPath() const
Returns the current path including a trailing slash.
Definition KoStore.cpp:405
TQStringList addLocalDirectory(const TQString &dirPath, const TQString &dest)
Imports a local directory.
Definition KoStore.cpp:491
bool at(TQIODevice::Offset pos)
See TQIODevice.
Definition KoStore.cpp:526
virtual bool enterRelativeDirectory(const TQString &dirName)=0
Enter a subdirectory of the current directory.
virtual bool enterAbsoluteDirectory(const TQString &path)=0
Enter a directory where we've been before.
TQ_LONG write(const TQByteArray &_data)
Write data into the currently opened file.
Definition KoStore.cpp:308
static KoStore * createStore(const TQString &fileName, Mode mode, const TQCString &appIdentification="", Backend backend=Auto)
Open a store (i.e.
Definition KoStore.cpp:58
void disallowNameExpansion(void)
Do not expand file and directory names Useful when using KoStore on non-KOffice files.
Definition KoStore.cpp:620
bool enterDirectory(const TQString &directory)
Enters one or multiple directories.
Definition KoStore.cpp:374
virtual ~KoStore()
Destroys the store (i.e.
Definition KoStore.cpp:183
Mode mode() const
Definition KoStore.h:163
TQString currentDirectory() const
Returns the current directory.
Definition KoStore.cpp:400
TQByteArray read(unsigned long int max)
Read data from the currently opened file.
Definition KoStore.cpp:270
bool addLocalFile(const TQString &fileName, const TQString &destName)
Imports a local file into a store.
Definition KoStore.cpp:429
TQValueStack< TQString > m_directoryStack
Used to push/pop directories to make it easy to save/restore the state.
Definition KoStore.h:355
TQIODevice * m_stream
The stream for the current read or write operation.
Definition KoStore.h:363
TQStringList m_strFiles
Store the filenames (with full path inside the archive) when writing, to avoid duplicates.
Definition KoStore.h:349