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