• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdeio/tdeio
 

tdeio/tdeio

  • tdeio
  • tdeio
kzip.cpp
1/* This file is part of the KDE libraries
2 Copyright (C) 2000 David Faure <faure@kde.org>
3 Copyright (C) 2002 Holger Schroeder <holger-kde@holgis.net>
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 version 2 as published by the Free Software Foundation.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*/
19
20/*
21 This class implements a tdeioslave to access ZIP files from KDE.
22 you can use it in IO_ReadOnly or in IO_WriteOnly mode, and it
23 behaves just as expected (i hope ;-) ).
24 It can also be used in IO_ReadWrite mode, in this case one can
25 append files to an existing zip archive. when you append new files, which
26 are not yet in the zip, it works as expected, they are appended at the end.
27 when you append a file, which is already in the file, the reference to the
28 old file is dropped and the new one is added to the zip. but the
29 old data from the file itself is not deleted, it is still in the
30 zipfile. so when you want to have a small and garbagefree zipfile,
31 just read the contents of the appended zipfile and write it to a new one
32 in IO_WriteOnly mode. especially take care of this, when you don't want
33 to leak information of how intermediate versions of files in the zip
34 were looking.
35 For more information on the zip fileformat go to
36 http://www.pkware.com/support/appnote.html .
37
38*/
39
40#include "kzip.h"
41#include "kfilterdev.h"
42#include "klimitediodevice.h"
43#include <kmimetype.h>
44#include <ksavefile.h>
45#include <kdebug.h>
46
47#include <tqasciidict.h>
48#include <tqfile.h>
49#include <tqdir.h>
50#include <tqdatetime.h>
51#include <tqptrlist.h>
52
53#include <zlib.h>
54#include <time.h>
55#include <string.h>
56
57const int max_path_len = 4095; // maximum number of character a path may contain
58
59static void transformToMsDos(const TQDateTime& dt, char* buffer)
60{
61 if ( dt.isValid() )
62 {
63 const TQ_UINT16 time =
64 ( dt.time().hour() << 11 ) // 5 bit hour
65 | ( dt.time().minute() << 5 ) // 6 bit minute
66 | ( dt.time().second() >> 1 ); // 5 bit double seconds
67
68 buffer[0] = char(time);
69 buffer[1] = char(time >> 8);
70
71 const TQ_UINT16 date =
72 ( ( dt.date().year() - 1980 ) << 9 ) // 7 bit year 1980-based
73 | ( dt.date().month() << 5 ) // 4 bit month
74 | ( dt.date().day() ); // 5 bit day
75
76 buffer[2] = char(date);
77 buffer[3] = char(date >> 8);
78 }
79 else // !dt.isValid(), assume 1980-01-01 midnight
80 {
81 buffer[0] = 0;
82 buffer[1] = 0;
83 buffer[2] = 33;
84 buffer[3] = 0;
85 }
86}
87
88static time_t transformFromMsDos(const char* buffer)
89{
90 TQ_UINT16 time = (uchar)buffer[0] | ( (uchar)buffer[1] << 8 );
91 int h = time >> 11;
92 int m = ( time & 0x7ff ) >> 5;
93 int s = ( time & 0x1f ) * 2 ;
94 TQTime qt(h, m, s);
95
96 TQ_UINT16 date = (uchar)buffer[2] | ( (uchar)buffer[3] << 8 );
97 int y = ( date >> 9 ) + 1980;
98 int o = ( date & 0x1ff ) >> 5;
99 int d = ( date & 0x1f );
100 TQDate qd(y, o, d);
101
102 TQDateTime dt( qd, qt );
103 return dt.toTime_t();
104}
105
106// == parsing routines for zip headers
107
109struct ParseFileInfo {
110 // file related info
111// TQCString name; // filename
112 mode_t perm; // permissions of this file
113 time_t atime; // last access time (UNIX format)
114 time_t mtime; // modification time (UNIX format)
115 time_t ctime; // creation time (UNIX format)
116 int uid; // user id (-1 if not specified)
117 int gid; // group id (-1 if not specified)
118 TQCString guessed_symlink; // guessed symlink target
119 int extralen; // length of extra field
120
121 // parsing related info
122 bool exttimestamp_seen; // true if extended timestamp extra field
123 // has been parsed
124 bool newinfounix_seen; // true if Info-ZIP Unix New extra field has
125 // been parsed
126
127 ParseFileInfo() : perm(0100644), uid(-1), gid(-1), extralen(0),
128 exttimestamp_seen(false), newinfounix_seen(false) {
129 ctime = mtime = atime = time(0);
130 }
131};
132
141static bool parseExtTimestamp(const char *buffer, int size, bool islocal,
142 ParseFileInfo &pfi) {
143 if (size < 1) {
144 kdDebug(7040) << "premature end of extended timestamp (#1)" << endl;
145 return false;
146 }/*end if*/
147 int flags = *buffer; // read flags
148 buffer += 1;
149 size -= 1;
150
151 if (flags & 1) { // contains modification time
152 if (size < 4) {
153 kdDebug(7040) << "premature end of extended timestamp (#2)" << endl;
154 return false;
155 }/*end if*/
156 pfi.mtime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
157 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
158 buffer += 4;
159 size -= 4;
160 }/*end if*/
161 // central extended field cannot contain more than the modification time
162 // even if other flags are set
163 if (!islocal) {
164 pfi.exttimestamp_seen = true;
165 return true;
166 }/*end if*/
167
168 if (flags & 2) { // contains last access time
169 if (size < 4) {
170 kdDebug(7040) << "premature end of extended timestamp (#3)" << endl;
171 return true;
172 }/*end if*/
173 pfi.atime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
174 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
175 buffer += 4;
176 size -= 4;
177 }/*end if*/
178
179 if (flags & 4) { // contains creation time
180 if (size < 4) {
181 kdDebug(7040) << "premature end of extended timestamp (#4)" << endl;
182 return true;
183 }/*end if*/
184 pfi.ctime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
185 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
186 buffer += 4;
187 }/*end if*/
188
189 pfi.exttimestamp_seen = true;
190 return true;
191}
192
201static bool parseInfoZipUnixOld(const char *buffer, int size, bool islocal,
202 ParseFileInfo &pfi) {
203 // spec mandates to omit this field if one of the newer fields are available
204 if (pfi.exttimestamp_seen || pfi.newinfounix_seen) return true;
205
206 if (size < 8) {
207 kdDebug(7040) << "premature end of Info-ZIP unix extra field old" << endl;
208 return false;
209 }/*end if*/
210
211 pfi.atime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
212 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
213 buffer += 4;
214 pfi.mtime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
215 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
216 buffer += 4;
217 if (islocal && size >= 12) {
218 pfi.uid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
219 buffer += 2;
220 pfi.gid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
221 buffer += 2;
222 }/*end if*/
223 return true;
224}
225
226#if 0 // not needed yet
235static bool parseInfoZipUnixNew(const char *buffer, int size, bool islocal,
236 ParseFileInfo &pfi) {
237 if (!islocal) { // contains nothing in central field
238 pfi.newinfounix = true;
239 return true;
240 }/*end if*/
241
242 if (size < 4) {
243 kdDebug(7040) << "premature end of Info-ZIP unix extra field new" << endl;
244 return false;
245 }/*end if*/
246
247 pfi.uid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
248 buffer += 2;
249 pfi.gid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
250 buffer += 2;
251
252 pfi.newinfounix = true;
253 return true;
254}
255#endif
256
265static bool parseExtraField(const char *buffer, int size, bool islocal,
266 ParseFileInfo &pfi) {
267 // extra field in central directory doesn't contain useful data, so we
268 // don't bother parsing it
269 if (!islocal) return true;
270
271 while (size >= 4) { // as long as a potential extra field can be read
272 int magic = (uchar)buffer[0] | (uchar)buffer[1] << 8;
273 buffer += 2;
274 int fieldsize = (uchar)buffer[0] | (uchar)buffer[1] << 8;
275 buffer += 2;
276 size -= 4;
277
278 if (fieldsize > size) {
279 //kdDebug(7040) << "fieldsize: " << fieldsize << " size: " << size << endl;
280 kdDebug(7040) << "premature end of extra fields reached" << endl;
281 break;
282 }/*end if*/
283
284 switch (magic) {
285 case 0x5455: // extended timestamp
286 if (!parseExtTimestamp(buffer, fieldsize, islocal, pfi)) return false;
287 break;
288 case 0x5855: // old Info-ZIP unix extra field
289 if (!parseInfoZipUnixOld(buffer, fieldsize, islocal, pfi)) return false;
290 break;
291#if 0 // not needed yet
292 case 0x7855: // new Info-ZIP unix extra field
293 if (!parseInfoZipUnixNew(buffer, fieldsize, islocal, pfi)) return false;
294 break;
295#endif
296 default:
297 /* ignore everything else */;
298 }/*end switch*/
299
300 buffer += fieldsize;
301 size -= fieldsize;
302 }/*wend*/
303 return true;
304}
305
309
310class KZip::KZipPrivate
311{
312public:
313 KZipPrivate()
314 : m_crc( 0 ),
315 m_currentFile( 0L ),
316 m_currentDev( 0L ),
317 m_compression( 8 ),
318 m_extraField( KZip::NoExtraField ),
319 m_offset( 0L ),
320 m_saveFile( 0 ) {}
321
322 unsigned long m_crc; // checksum
323 KZipFileEntry* m_currentFile; // file currently being written
324 TQIODevice* m_currentDev; // filterdev used to write to the above file
325 TQPtrList<KZipFileEntry> m_fileList; // flat list of all files, for the index (saves a recursive method ;)
326 int m_compression;
327 KZip::ExtraField m_extraField;
328 unsigned int m_offset; // holds the offset of the place in the zip,
329 // where new data can be appended. after openarchive it points to 0, when in
330 // writeonly mode, or it points to the beginning of the central directory.
331 // each call to writefile updates this value.
332 KSaveFile* m_saveFile;
333};
334
335KZip::KZip( const TQString& filename )
336 : KArchive( 0L )
337{
338 //kdDebug(7040) << "KZip(filename) reached." << endl;
339 Q_ASSERT( !filename.isEmpty() );
340 m_filename = filename;
341 d = new KZipPrivate;
342 // unusual: this ctor leaves the device set to 0.
343 // This is for the use of KSaveFile, see openArchive.
344 // KDE4: move KSaveFile support to base class, KArchive.
345}
346
347KZip::KZip( TQIODevice * dev )
348 : KArchive( dev )
349{
350 //kdDebug(7040) << "KZip::KZip( TQIODevice * dev) reached." << endl;
351 d = new KZipPrivate;
352}
353
354KZip::~KZip()
355{
356 // mjarrett: Closes to prevent ~KArchive from aborting w/o device
357 //kdDebug(7040) << "~KZip reached." << endl;
358 if( isOpened() )
359 close();
360 if ( !m_filename.isEmpty() ) { // we created the device ourselves
361 if ( d->m_saveFile ) // writing mode
362 delete d->m_saveFile;
363 else // reading mode
364 delete device(); // (the TQFile)
365 }
366 delete d;
367}
368
369bool KZip::openArchive( int mode )
370{
371 //kdDebug(7040) << "openarchive reached." << endl;
372 d->m_fileList.clear();
373
374 switch ( mode ) {
375 case IO_WriteOnly:
376 // The use of KSaveFile can't be done in the ctor (no mode known yet)
377 // Ideally we would reimplement open() and do it there (BIC)
378 if ( !m_filename.isEmpty() ) {
379 kdDebug(7040) << "Writing to a file using KSaveFile" << endl;
380 d->m_saveFile = new KSaveFile( m_filename );
381 if ( d->m_saveFile->status() != 0 ) {
382 kdWarning(7040) << "KSaveFile creation for " << m_filename << " failed, " << strerror( d->m_saveFile->status() ) << endl;
383 delete d->m_saveFile;
384 d->m_saveFile = 0;
385 return false;
386 }
387 Q_ASSERT( d->m_saveFile->file() );
388 setDevice( d->m_saveFile->file() );
389 }
390 return true;
391 case IO_ReadOnly:
392 case IO_ReadWrite:
393 {
394 // ReadWrite mode still uses TQFile for now; we'd need to copy to the tempfile, in fact.
395 if ( !m_filename.isEmpty() ) {
396 setDevice( new TQFile( m_filename ) );
397 if ( !device()->open( mode ) )
398 return false;
399 }
400 break; // continued below
401 }
402 default:
403 kdWarning(7040) << "Unsupported mode " << mode << endl;
404 return false;
405 }
406
407 char buffer[47];
408
409 // Check that it's a valid ZIP file
410 // the above code opened the underlying device already.
411 TQIODevice* dev = device();
412
413 if (!dev) {
414 return false;
415 }
416
417 uint offset = 0; // holds offset, where we read
418 int n;
419
420 // contains information gathered from the local file headers
421 TQAsciiDict<ParseFileInfo> pfi_map(1009, true /*case sensitive */, true /*copy keys*/);
422 pfi_map.setAutoDelete(true);
423
424 // We set a bool for knowing if we are allowed to skip the start of the file
425 bool startOfFile = true;
426
427 for (;;) // repeat until 'end of entries' signature is reached
428 {
429kdDebug(7040) << "loop starts" << endl;
430kdDebug(7040) << "dev->at() now : " << dev->at() << endl;
431 n = dev->readBlock( buffer, 4 );
432
433 if (n < 4)
434 {
435 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#1)" << endl;
436
437 return false;
438 }
439
440 if ( !memcmp( buffer, "PK\5\6", 4 ) ) // 'end of entries'
441 {
442 kdDebug(7040) << "PK56 found end of archive" << endl;
443 startOfFile = false;
444 break;
445 }
446
447 if ( !memcmp( buffer, "PK\3\4", 4 ) ) // local file header
448 {
449 kdDebug(7040) << "PK34 found local file header" << endl;
450 startOfFile = false;
451 // can this fail ???
452 dev->at( dev->at() + 2 ); // skip 'version needed to extract'
453
454 // read static header stuff
455 n = dev->readBlock( buffer, 24 );
456 if (n < 24) {
457 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#4)" << endl;
458 return false;
459 }
460
461 int gpf = (uchar)buffer[0]; // "general purpose flag" not "general protection fault" ;-)
462 int compression_mode = (uchar)buffer[2] | (uchar)buffer[3] << 8;
463 time_t mtime = transformFromMsDos( buffer+4 );
464
465 TQ_LONG compr_size = (uchar)buffer[12] | (uchar)buffer[13] << 8
466 | (uchar)buffer[14] << 16 | (uchar)buffer[15] << 24;
467 TQ_LONG uncomp_size = (uchar)buffer[16] | (uchar)buffer[17] << 8
468 | (uchar)buffer[18] << 16 | (uchar)buffer[19] << 24;
469 int namelen = (uchar)buffer[20] | (uchar)buffer[21] << 8;
470 int extralen = (uchar)buffer[22] | (uchar)buffer[23] << 8;
471
472 kdDebug(7040) << "general purpose bit flag: " << gpf << endl;
473 kdDebug(7040) << "compressed size: " << compr_size << endl;
474 kdDebug(7040) << "uncompressed size: " << uncomp_size << endl;
475 kdDebug(7040) << "namelen: " << namelen << endl;
476 kdDebug(7040) << "extralen: " << extralen << endl;
477 kdDebug(7040) << "archive size: " << dev->size() << endl;
478
479 // read filename
480 TQCString filename(namelen + 1);
481 n = dev->readBlock(filename.data(), namelen);
482 if ( n < namelen ) {
483 kdWarning(7040) << "Invalid ZIP file. Name not completely read (#2)" << endl;
484 return false;
485 }
486
487 ParseFileInfo *pfi = new ParseFileInfo();
488 pfi->mtime = mtime;
489 pfi_map.insert(filename.data(), pfi);
490
491 // read and parse the beginning of the extra field,
492 // skip rest of extra field in case it is too long
493 unsigned int extraFieldEnd = dev->at() + extralen;
494 pfi->extralen = extralen;
495 int handledextralen = TQMIN(extralen, (int)sizeof buffer);
496
497 kdDebug(7040) << "handledextralen: " << handledextralen << endl;
498
499 n = dev->readBlock(buffer, handledextralen);
500 // no error msg necessary as we deliberately truncate the extra field
501 if (!parseExtraField(buffer, handledextralen, true, *pfi))
502 {
503 kdWarning(7040) << "Invalid ZIP File. Broken ExtraField." << endl;
504 return false;
505 }
506
507 // jump to end of extra field
508 dev->at( extraFieldEnd );
509
510 // we have to take care of the 'general purpose bit flag'.
511 // if bit 3 is set, the header doesn't contain the length of
512 // the file and we look for the signature 'PK\7\8'.
513 if ( gpf & 8 )
514 {
515 // here we have to read through the compressed data to find
516 // the next PKxx
517 kdDebug(7040) << "trying to seek for next PK78" << endl;
518 bool foundSignature = false;
519
520 while (!foundSignature)
521 {
522 n = dev->readBlock( buffer, 1 );
523 if (n < 1)
524 {
525 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)" << endl;
526 return false;
527 }
528
529 if ( buffer[0] != 'P' )
530 continue;
531
532 n = dev->readBlock( buffer, 3 );
533 if (n < 3)
534 {
535 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)" << endl;
536 return false;
537 }
538
539 // we have to detect three magic tokens here:
540 // PK34 for the next local header in case there is no data descriptor
541 // PK12 for the central header in case there is no data descriptor
542 // PK78 for the data descriptor in case it is following the compressed data
543
544 if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
545 {
546 foundSignature = true;
547 dev->at( dev->at() + 12 ); // skip the 'data_descriptor'
548 }
549 else if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 )
550 || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) )
551 {
552 foundSignature = true;
553 dev->at( dev->at() - 4 ); // go back 4 bytes, so that the magic bytes can be found...
554 }
555 else if ( buffer[0] == 'P' || buffer[1] == 'P' || buffer[2] == 'P' )
556 {
557 // We have another P character so we must go back a little to check if it is a magic
558 dev->at( dev->at() - 3 );
559 }
560
561 }
562 }
563 else
564 {
565 // here we skip the compressed data and jump to the next header
566 kdDebug(7040) << "general purpose bit flag indicates, that local file header contains valid size" << endl;
567 // check if this could be a symbolic link
568 if (compression_mode == NoCompression
569 && uncomp_size <= max_path_len
570 && uncomp_size > 0) {
571 // read content and store it
572 pfi->guessed_symlink.resize(uncomp_size + 1);
573 kdDebug(7040) << "guessed symlink size: " << uncomp_size << endl;
574 n = dev->readBlock(pfi->guessed_symlink.data(), uncomp_size);
575 if (n < uncomp_size) {
576 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#5)" << endl;
577 return false;
578 }
579 } else {
580
581 if ( compr_size > (TQ_LONG)dev->size() )
582 {
583 // here we cannot trust the compressed size, so scan through the compressed
584 // data to find the next header
585 bool foundSignature = false;
586
587 while (!foundSignature)
588 {
589 n = dev->readBlock( buffer, 1 );
590 if (n < 1)
591 {
592 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)" << endl;
593 return false;
594 }
595
596 if ( buffer[0] != 'P' )
597 continue;
598
599 n = dev->readBlock( buffer, 3 );
600 if (n < 3)
601 {
602 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)" << endl;
603 return false;
604 }
605
606 // we have to detect three magic tokens here:
607 // PK34 for the next local header in case there is no data descriptor
608 // PK12 for the central header in case there is no data descriptor
609 // PK78 for the data descriptor in case it is following the compressed data
610
611 if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
612 {
613 foundSignature = true;
614 dev->at( dev->at() + 12 ); // skip the 'data_descriptor'
615 }
616
617 if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 )
618 || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) )
619 {
620 foundSignature = true;
621 dev->at( dev->at() - 4 );
622 // go back 4 bytes, so that the magic bytes can be found
623 // in the next cycle...
624 }
625 }
626 }
627 else
628 {
629// kdDebug(7040) << "before interesting dev->at(): " << dev->at() << endl;
630 bool success;
631 success = dev->at( dev->at() + compr_size ); // can this fail ???
632/* kdDebug(7040) << "after interesting dev->at(): " << dev->at() << endl;
633 if ( success )
634 kdDebug(7040) << "dev->at was successful... " << endl;
635 else
636 kdDebug(7040) << "dev->at failed... " << endl;*/
637 }
638
639 }
640
641// not needed any more
642/* // here we calculate the length of the file in the zip
643 // with headers and jump to the next header.
644 uint skip = compr_size + namelen + extralen;
645 offset += 30 + skip;*/
646 }
647 }
648 else if ( !memcmp( buffer, "PK\1\2", 4 ) ) // central block
649 {
650 kdDebug(7040) << "PK12 found central block" << endl;
651 startOfFile = false;
652
653 // so we reached the central header at the end of the zip file
654 // here we get all interesting data out of the central header
655 // of a file
656 offset = dev->at() - 4;
657
658 //set offset for appending new files
659 if ( d->m_offset == 0L ) d->m_offset = offset;
660
661 n = dev->readBlock( buffer + 4, 42 );
662 if (n < 42) {
663 kdWarning(7040) << "Invalid ZIP file, central entry too short" << endl; // not long enough for valid entry
664 return false;
665 }
666
667 //int gpf = (uchar)buffer[9] << 8 | (uchar)buffer[10];
668 //kdDebug() << "general purpose flag=" << gpf << endl;
669 // length of the filename (well, pathname indeed)
670 int namelen = (uchar)buffer[29] << 8 | (uchar)buffer[28];
671 TQCString bufferName( namelen + 1 );
672 n = dev->readBlock( bufferName.data(), namelen );
673 if ( n < namelen )
674 kdWarning(7040) << "Invalid ZIP file. Name not completely read" << endl;
675
676 ParseFileInfo *pfi = pfi_map[bufferName];
677 if (!pfi) { // can that happen?
678 pfi_map.insert(bufferName.data(), pfi = new ParseFileInfo());
679 }
680 TQString name( TQFile::decodeName(bufferName) );
681
682 //kdDebug(7040) << "name: " << name << endl;
683 // only in central header ! see below.
684 // length of extra attributes
685 int extralen = (uchar)buffer[31] << 8 | (uchar)buffer[30];
686 // length of comment for this file
687 int commlen = (uchar)buffer[33] << 8 | (uchar)buffer[32];
688 // compression method of this file
689 int cmethod = (uchar)buffer[11] << 8 | (uchar)buffer[10];
690
691 //kdDebug(7040) << "cmethod: " << cmethod << endl;
692 //kdDebug(7040) << "extralen: " << extralen << endl;
693
694 // uncompressed file size
695 uint ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 |
696 (uchar)buffer[25] << 8 | (uchar)buffer[24];
697 // compressed file size
698 uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 |
699 (uchar)buffer[21] << 8 | (uchar)buffer[20];
700
701 // offset of local header
702 uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 |
703 (uchar)buffer[43] << 8 | (uchar)buffer[42];
704
705 // some clever people use different extra field lengths
706 // in the central header and in the local header... funny.
707 // so we need to get the localextralen to calculate the offset
708 // from localheaderstart to dataoffset
709 int localextralen = pfi->extralen; // FIXME: this will not work if
710 // no local header exists
711
712 //kdDebug(7040) << "localextralen: " << localextralen << endl;
713
714 // offset, where the real data for uncompression starts
715 uint dataoffset = localheaderoffset + 30 + localextralen + namelen; //comment only in central header
716
717 //kdDebug(7040) << "esize: " << esize << endl;
718 //kdDebug(7040) << "eoffset: " << eoffset << endl;
719 //kdDebug(7040) << "csize: " << csize << endl;
720
721 int os_madeby = (uchar)buffer[5];
722 bool isdir = false;
723 int access = 0100644;
724
725 if (os_madeby == 3) { // good ole unix
726 access = (uchar)buffer[40] | (uchar)buffer[41] << 8;
727 }
728
729 TQString entryName;
730
731 if ( name.endsWith( "/" ) ) // Entries with a trailing slash are directories
732 {
733 isdir = true;
734 name = name.left( name.length() - 1 );
735 if (os_madeby != 3) access = S_IFDIR | 0755;
736 else Q_ASSERT(access & S_IFDIR);
737 }
738
739 int pos = name.findRev( '/' );
740 if ( pos == -1 )
741 entryName = name;
742 else
743 entryName = name.mid( pos + 1 );
744 Q_ASSERT( !entryName.isEmpty() );
745
746 KArchiveEntry* entry;
747 if ( isdir )
748 {
749 TQString path = TQDir::cleanDirPath( name );
750 KArchiveEntry* ent = rootDir()->entry( path );
751 if ( ent && ent->isDirectory() )
752 {
753 //kdDebug(7040) << "Directory already exists, NOT going to add it again" << endl;
754 entry = 0L;
755 }
756 else
757 {
758 entry = new KArchiveDirectory( this, entryName, access, (int)pfi->mtime, rootDir()->user(), rootDir()->group(), TQString::null );
759 //kdDebug(7040) << "KArchiveDirectory created, entryName= " << entryName << ", name=" << name << endl;
760 }
761 }
762 else
763 {
764 TQString symlink;
765 if (S_ISLNK(access)) {
766 symlink = TQFile::decodeName(pfi->guessed_symlink);
767 }
768 entry = new KZipFileEntry( this, entryName, access, pfi->mtime,
769 rootDir()->user(), rootDir()->group(),
770 symlink, name, dataoffset,
771 ucsize, cmethod, csize );
772 static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset );
773 //kdDebug(7040) << "KZipFileEntry created, entryName= " << entryName << ", name=" << name << endl;
774 d->m_fileList.append( static_cast<KZipFileEntry *>( entry ) );
775 }
776
777 if ( entry )
778 {
779 if ( pos == -1 )
780 {
781 rootDir()->addEntry(entry);
782 }
783 else
784 {
785 // In some tar files we can find dir/./file => call cleanDirPath
786 TQString path = TQDir::cleanDirPath( name.left( pos ) );
787 // Ensure container directory exists, create otherwise
788 KArchiveDirectory * tdir = findOrCreate( path );
789 tdir->addEntry(entry);
790 }
791 }
792
793 //calculate offset to next entry
794 offset += 46 + commlen + extralen + namelen;
795 bool b = dev->at(offset);
796 Q_ASSERT( b );
797 if ( !b )
798 return false;
799 }
800 else if ( startOfFile )
801 {
802 // The file does not start with any ZIP header (e.g. self-extractable ZIP files)
803 // Therefore we need to find the first PK\003\004 (local header)
804 kdDebug(7040) << "Try to skip start of file" << endl;
805 startOfFile = false;
806 bool foundSignature = false;
807
808 while (!foundSignature)
809 {
810 n = dev->readBlock( buffer, 1 );
811 if (n < 1)
812 {
813 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. " << k_funcinfo << endl;
814 return false;
815 }
816
817 if ( buffer[0] != 'P' )
818 continue;
819
820 n = dev->readBlock( buffer, 3 );
821 if (n < 3)
822 {
823 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. " << k_funcinfo << endl;
824 return false;
825 }
826
827 // We have to detect the magic token for a local header: PK\003\004
828 /*
829 * Note: we do not need to check the other magics, if the ZIP file has no
830 * local header, then it has not any files!
831 */
832 if ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 )
833 {
834 foundSignature = true;
835 dev->at( dev->at() - 4 ); // go back 4 bytes, so that the magic bytes can be found...
836 }
837 else if ( buffer[0] == 'P' || buffer[1] == 'P' || buffer[2] == 'P' )
838 {
839 // We have another P character so we must go back a little to check if it is a magic
840 dev->at( dev->at() - 3 );
841 }
842 }
843 }
844 else
845 {
846 kdWarning(7040) << "Invalid ZIP file. Unrecognized header at offset " << offset << endl;
847
848 return false;
849 }
850 }
851 //kdDebug(7040) << "*** done *** " << endl;
852 return true;
853}
854
855bool KZip::closeArchive()
856{
857 if ( ! ( mode() & IO_WriteOnly ) )
858 {
859 //kdDebug(7040) << "closearchive readonly reached." << endl;
860 return true;
861 }
862
863 kdDebug() << k_funcinfo << "device=" << device() << endl;
864 //ReadWrite or WriteOnly
865 //write all central dir file entries
866
867 if ( !device() ) // saving aborted
868 return false;
869
870 // to be written at the end of the file...
871 char buffer[ 22 ]; // first used for 12, then for 22 at the end
872 uLong crc = crc32(0L, Z_NULL, 0);
873
874 TQ_LONG centraldiroffset = device()->at();
875 //kdDebug(7040) << "closearchive: centraldiroffset: " << centraldiroffset << endl;
876 TQ_LONG atbackup = centraldiroffset;
877 TQPtrListIterator<KZipFileEntry> it( d->m_fileList );
878
879 for ( ; it.current() ; ++it )
880 { //set crc and compressed size in each local file header
881 if ( !device()->at( it.current()->headerStart() + 14 ) )
882 return false;
883 //kdDebug(7040) << "closearchive setcrcandcsize: filename: "
884 // << it.current()->path()
885 // << " encoding: "<< it.current()->encoding() << endl;
886
887 uLong mycrc = it.current()->crc32();
888 buffer[0] = char(mycrc); // crc checksum, at headerStart+14
889 buffer[1] = char(mycrc >> 8);
890 buffer[2] = char(mycrc >> 16);
891 buffer[3] = char(mycrc >> 24);
892
893 int mysize1 = it.current()->compressedSize();
894 buffer[4] = char(mysize1); // compressed file size, at headerStart+18
895 buffer[5] = char(mysize1 >> 8);
896 buffer[6] = char(mysize1 >> 16);
897 buffer[7] = char(mysize1 >> 24);
898
899 int myusize = it.current()->size();
900 buffer[8] = char(myusize); // uncompressed file size, at headerStart+22
901 buffer[9] = char(myusize >> 8);
902 buffer[10] = char(myusize >> 16);
903 buffer[11] = char(myusize >> 24);
904
905 if ( device()->writeBlock( buffer, 12 ) != 12 )
906 return false;
907 }
908 device()->at( atbackup );
909
910 for ( it.toFirst(); it.current() ; ++it )
911 {
912 //kdDebug(7040) << "closearchive: filename: " << it.current()->path()
913 // << " encoding: "<< it.current()->encoding() << endl;
914
915 TQCString path = TQFile::encodeName(it.current()->path());
916
917 const int extra_field_len = 9;
918 int bufferSize = extra_field_len + path.length() + 46;
919 char* buffer = new char[ bufferSize ];
920
921 memset(buffer, 0, 46); // zero is a nice default for most header fields
922
923 const char head[] =
924 {
925 'P', 'K', 1, 2, // central file header signature
926 0x14, 3, // version made by (3 == UNIX)
927 0x14, 0 // version needed to extract
928 };
929
930 // I do not know why memcpy is not working here
931 //memcpy(buffer, head, sizeof(head));
932 tqmemmove(buffer, head, sizeof(head));
933
934 buffer[ 10 ] = char(it.current()->encoding()); // compression method
935 buffer[ 11 ] = char(it.current()->encoding() >> 8);
936
937 transformToMsDos( it.current()->datetime(), &buffer[ 12 ] );
938
939 uLong mycrc = it.current()->crc32();
940 buffer[ 16 ] = char(mycrc); // crc checksum
941 buffer[ 17 ] = char(mycrc >> 8);
942 buffer[ 18 ] = char(mycrc >> 16);
943 buffer[ 19 ] = char(mycrc >> 24);
944
945 int mysize1 = it.current()->compressedSize();
946 buffer[ 20 ] = char(mysize1); // compressed file size
947 buffer[ 21 ] = char(mysize1 >> 8);
948 buffer[ 22 ] = char(mysize1 >> 16);
949 buffer[ 23 ] = char(mysize1 >> 24);
950
951 int mysize = it.current()->size();
952 buffer[ 24 ] = char(mysize); // uncompressed file size
953 buffer[ 25 ] = char(mysize >> 8);
954 buffer[ 26 ] = char(mysize >> 16);
955 buffer[ 27 ] = char(mysize >> 24);
956
957 buffer[ 28 ] = char(it.current()->path().length()); // filename length
958 buffer[ 29 ] = char(it.current()->path().length() >> 8);
959
960 buffer[ 30 ] = char(extra_field_len);
961 buffer[ 31 ] = char(extra_field_len >> 8);
962
963 buffer[ 40 ] = char(it.current()->permissions());
964 buffer[ 41 ] = char(it.current()->permissions() >> 8);
965
966 int myhst = it.current()->headerStart();
967 buffer[ 42 ] = char(myhst); //relative offset of local header
968 buffer[ 43 ] = char(myhst >> 8);
969 buffer[ 44 ] = char(myhst >> 16);
970 buffer[ 45 ] = char(myhst >> 24);
971
972 // file name
973 strncpy( buffer + 46, path, path.length() );
974 //kdDebug(7040) << "closearchive length to write: " << bufferSize << endl;
975
976 // extra field
977 char *extfield = buffer + 46 + path.length();
978 extfield[0] = 'U';
979 extfield[1] = 'T';
980 extfield[2] = 5;
981 extfield[3] = 0;
982 extfield[4] = 1 | 2 | 4; // specify flags from local field
983 // (unless I misread the spec)
984 // provide only modification time
985 unsigned long time = (unsigned long)it.current()->date();
986 extfield[5] = char(time);
987 extfield[6] = char(time >> 8);
988 extfield[7] = char(time >> 16);
989 extfield[8] = char(time >> 24);
990
991 crc = crc32(crc, (Bytef *)buffer, bufferSize );
992 bool ok = ( device()->writeBlock( buffer, bufferSize ) == bufferSize );
993 delete[] buffer;
994 if ( !ok )
995 return false;
996 }
997 TQ_LONG centraldirendoffset = device()->at();
998 //kdDebug(7040) << "closearchive: centraldirendoffset: " << centraldirendoffset << endl;
999 //kdDebug(7040) << "closearchive: device()->at(): " << device()->at() << endl;
1000
1001 //write end of central dir record.
1002 buffer[ 0 ] = 'P'; //end of central dir signature
1003 buffer[ 1 ] = 'K';
1004 buffer[ 2 ] = 5;
1005 buffer[ 3 ] = 6;
1006
1007 buffer[ 4 ] = 0; // number of this disk
1008 buffer[ 5 ] = 0;
1009
1010 buffer[ 6 ] = 0; // number of disk with start of central dir
1011 buffer[ 7 ] = 0;
1012
1013 int count = d->m_fileList.count();
1014 //kdDebug(7040) << "number of files (count): " << count << endl;
1015
1016
1017 buffer[ 8 ] = char(count); // total number of entries in central dir of
1018 buffer[ 9 ] = char(count >> 8); // this disk
1019
1020 buffer[ 10 ] = buffer[ 8 ]; // total number of entries in the central dir
1021 buffer[ 11 ] = buffer[ 9 ];
1022
1023 int cdsize = centraldirendoffset - centraldiroffset;
1024 buffer[ 12 ] = char(cdsize); // size of the central dir
1025 buffer[ 13 ] = char(cdsize >> 8);
1026 buffer[ 14 ] = char(cdsize >> 16);
1027 buffer[ 15 ] = char(cdsize >> 24);
1028
1029 //kdDebug(7040) << "end : centraldiroffset: " << centraldiroffset << endl;
1030 //kdDebug(7040) << "end : centraldirsize: " << cdsize << endl;
1031
1032 buffer[ 16 ] = char(centraldiroffset); // central dir offset
1033 buffer[ 17 ] = char(centraldiroffset >> 8);
1034 buffer[ 18 ] = char(centraldiroffset >> 16);
1035 buffer[ 19 ] = char(centraldiroffset >> 24);
1036
1037 buffer[ 20 ] = 0; //zipfile comment length
1038 buffer[ 21 ] = 0;
1039
1040 if ( device()->writeBlock( buffer, 22 ) != 22 )
1041 return false;
1042
1043 if ( d->m_saveFile ) {
1044 d->m_saveFile->close();
1045 setDevice( 0 );
1046 delete d->m_saveFile;
1047 d->m_saveFile = 0;
1048 }
1049
1050 //kdDebug(7040) << __FILE__" reached." << endl;
1051 return true;
1052}
1053
1054bool KZip::writeDir(const TQString& name, const TQString& user, const TQString& group)
1055{
1056 // Zip files have no explicit directories, they are implicitly created during extraction time
1057 // when file entries have paths in them.
1058 // However, to support empty directories, we must create a dummy file entry which ends with '/'.
1059 TQString dirName = name;
1060 if (!name.endsWith("/")) {
1061 dirName = dirName.append('/');
1062 }
1063
1064 mode_t perm = 040755;
1065 time_t the_time = time(0);
1066 return writeFile(dirName, user, group, 0, perm, the_time, the_time, the_time, 0);
1067}
1068
1069// Doesn't need to be reimplemented anymore. Remove for KDE-4.0
1070bool KZip::writeFile( const TQString& name, const TQString& user, const TQString& group, uint size, const char* data )
1071{
1072 mode_t mode = 0100644;
1073 time_t the_time = time(0);
1074 return KArchive::writeFile( name, user, group, size, mode, the_time,
1075 the_time, the_time, data );
1076}
1077
1078// Doesn't need to be reimplemented anymore. Remove for KDE-4.0
1079bool KZip::writeFile( const TQString& name, const TQString& user,
1080 const TQString& group, uint size, mode_t perm,
1081 time_t atime, time_t mtime, time_t ctime,
1082 const char* data ) {
1083 return KArchive::writeFile(name, user, group, size, perm, atime, mtime,
1084 ctime, data);
1085}
1086
1087// Doesn't need to be reimplemented anymore. Remove for KDE-4.0
1088bool KZip::prepareWriting( const TQString& name, const TQString& user, const TQString& group, uint size )
1089{
1090 mode_t dflt_perm = 0100644;
1091 time_t the_time = time(0);
1092 return prepareWriting(name,user,group,size,dflt_perm,
1093 the_time,the_time,the_time);
1094}
1095
1096// Doesn't need to be reimplemented anymore. Remove for KDE-4.0
1097bool KZip::prepareWriting(const TQString& name, const TQString& user,
1098 const TQString& group, uint size, mode_t perm,
1099 time_t atime, time_t mtime, time_t ctime) {
1100 return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime);
1101}
1102
1103bool KZip::prepareWriting_impl(const TQString &name, const TQString &user,
1104 const TQString &group, uint /*size*/, mode_t perm,
1105 time_t atime, time_t mtime, time_t ctime) {
1106 //kdDebug(7040) << "prepareWriting reached." << endl;
1107 if ( !isOpened() )
1108 {
1109 tqWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
1110 return false;
1111 }
1112
1113 if ( ! ( mode() & IO_WriteOnly ) ) // accept WriteOnly and ReadWrite
1114 {
1115 tqWarning( "KZip::writeFile: You must open the zip file for writing\n");
1116 return false;
1117 }
1118
1119 if ( !device() ) { // aborted
1120 //kdWarning(7040) << "prepareWriting_impl: no device" << endl;
1121 return false;
1122 }
1123
1124 // set right offset in zip.
1125 if ( !device()->at( d->m_offset ) ) {
1126 kdWarning(7040) << "prepareWriting_impl: cannot seek in ZIP file. Disk full?" << endl;
1127 abort();
1128 return false;
1129 }
1130
1131 // delete entries in the filelist with the same filename as the one we want
1132 // to save, so that we don�t have duplicate file entries when viewing the zip
1133 // with konqi...
1134 // CAUTION: the old file itself is still in the zip and won't be removed !!!
1135 TQPtrListIterator<KZipFileEntry> it( d->m_fileList );
1136
1137 //kdDebug(7040) << "filename to write: " << name <<endl;
1138 for ( ; it.current() ; ++it )
1139 {
1140 //kdDebug(7040) << "prepfilename: " << it.current()->path() <<endl;
1141 if (name == it.current()->path() )
1142 {
1143 //kdDebug(7040) << "removing following entry: " << it.current()->path() <<endl;
1144 d->m_fileList.remove();
1145 }
1146
1147 }
1148 // Find or create parent dir
1149 KArchiveDirectory* parentDir = rootDir();
1150 TQString fileName( name );
1151 int i = name.findRev( '/' );
1152 if ( i != -1 )
1153 {
1154 TQString dir = name.left( i );
1155 fileName = name.mid( i + 1 );
1156 //kdDebug(7040) << "KZip::prepareWriting ensuring " << dir << " exists. fileName=" << fileName << endl;
1157 parentDir = findOrCreate( dir );
1158 }
1159
1160 // construct a KZipFileEntry and add it to list
1161 KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, TQString::null,
1162 name, device()->at() + 30 + name.length(), // start
1163 0 /*size unknown yet*/, d->m_compression, 0 /*csize unknown yet*/ );
1164 e->setHeaderStart( device()->at() );
1165 //kdDebug(7040) << "wrote file start: " << e->position() << " name: " << name << endl;
1166 parentDir->addEntry( e );
1167
1168 d->m_currentFile = e;
1169 d->m_fileList.append( e );
1170
1171 int extra_field_len = 0;
1172 if ( d->m_extraField == ModificationTime )
1173 extra_field_len = 17; // value also used in doneWriting()
1174
1175 // write out zip header
1176 TQCString encodedName = TQFile::encodeName(name);
1177 int bufferSize = extra_field_len + encodedName.length() + 30;
1178 //kdDebug(7040) << "KZip::prepareWriting bufferSize=" << bufferSize << endl;
1179 char* buffer = new char[ bufferSize ];
1180
1181 buffer[ 0 ] = 'P'; //local file header signature
1182 buffer[ 1 ] = 'K';
1183 buffer[ 2 ] = 3;
1184 buffer[ 3 ] = 4;
1185
1186 buffer[ 4 ] = 0x14; // version needed to extract
1187 buffer[ 5 ] = 0;
1188
1189 buffer[ 6 ] = 0; // general purpose bit flag
1190 buffer[ 7 ] = 0;
1191
1192 buffer[ 8 ] = char(e->encoding()); // compression method
1193 buffer[ 9 ] = char(e->encoding() >> 8);
1194
1195 transformToMsDos( e->datetime(), &buffer[ 10 ] );
1196
1197 buffer[ 14 ] = 'C'; //dummy crc
1198 buffer[ 15 ] = 'R';
1199 buffer[ 16 ] = 'C';
1200 buffer[ 17 ] = 'q';
1201
1202 buffer[ 18 ] = 'C'; //compressed file size
1203 buffer[ 19 ] = 'S';
1204 buffer[ 20 ] = 'I';
1205 buffer[ 21 ] = 'Z';
1206
1207 buffer[ 22 ] = 'U'; //uncompressed file size
1208 buffer[ 23 ] = 'S';
1209 buffer[ 24 ] = 'I';
1210 buffer[ 25 ] = 'Z';
1211
1212 buffer[ 26 ] = (uchar)(encodedName.length()); //filename length
1213 buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
1214
1215 buffer[ 28 ] = (uchar)(extra_field_len); // extra field length
1216 buffer[ 29 ] = (uchar)(extra_field_len >> 8);
1217
1218 // file name
1219 strncpy( buffer + 30, encodedName, encodedName.length() );
1220
1221 // extra field
1222 if ( d->m_extraField == ModificationTime )
1223 {
1224 char *extfield = buffer + 30 + encodedName.length();
1225 // "Extended timestamp" header (0x5455)
1226 extfield[0] = 'U';
1227 extfield[1] = 'T';
1228 extfield[2] = 13; // data size
1229 extfield[3] = 0;
1230 extfield[4] = 1 | 2 | 4; // contains mtime, atime, ctime
1231
1232 extfield[5] = char(mtime);
1233 extfield[6] = char(mtime >> 8);
1234 extfield[7] = char(mtime >> 16);
1235 extfield[8] = char(mtime >> 24);
1236
1237 extfield[9] = char(atime);
1238 extfield[10] = char(atime >> 8);
1239 extfield[11] = char(atime >> 16);
1240 extfield[12] = char(atime >> 24);
1241
1242 extfield[13] = char(ctime);
1243 extfield[14] = char(ctime >> 8);
1244 extfield[15] = char(ctime >> 16);
1245 extfield[16] = char(ctime >> 24);
1246 }
1247
1248 // Write header
1249 bool b = (device()->writeBlock( buffer, bufferSize ) == bufferSize );
1250 d->m_crc = 0L;
1251 delete[] buffer;
1252
1253 Q_ASSERT( b );
1254 if (!b) {
1255 abort();
1256 return false;
1257 }
1258
1259 // Prepare device for writing the data
1260 // Either device() if no compression, or a KFilterDev to compress
1261 if ( d->m_compression == 0 ) {
1262 d->m_currentDev = device();
1263 return true;
1264 }
1265
1266 d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
1267 Q_ASSERT( d->m_currentDev );
1268 if ( !d->m_currentDev ) {
1269 abort();
1270 return false; // ouch
1271 }
1272 static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders(); // Just zlib, not gzip
1273
1274 b = d->m_currentDev->open( IO_WriteOnly );
1275 Q_ASSERT( b );
1276 return b;
1277}
1278
1279bool KZip::doneWriting( uint size )
1280{
1281 if ( d->m_currentFile->encoding() == 8 ) {
1282 // Finish
1283 (void)d->m_currentDev->writeBlock( 0, 0 );
1284 delete d->m_currentDev;
1285 }
1286 // If 0, d->m_currentDev was device() - don't delete ;)
1287 d->m_currentDev = 0L;
1288
1289 Q_ASSERT( d->m_currentFile );
1290 //kdDebug(7040) << "donewriting reached." << endl;
1291 //kdDebug(7040) << "filename: " << d->m_currentFile->path() << endl;
1292 //kdDebug(7040) << "getpos (at): " << device()->at() << endl;
1293 d->m_currentFile->setSize(size);
1294 int extra_field_len = 0;
1295 if ( d->m_extraField == ModificationTime )
1296 extra_field_len = 17; // value also used in doneWriting()
1297
1298 int csize = device()->at() -
1299 d->m_currentFile->headerStart() - 30 -
1300 d->m_currentFile->path().length() - extra_field_len;
1301 d->m_currentFile->setCompressedSize(csize);
1302 //kdDebug(7040) << "usize: " << d->m_currentFile->size() << endl;
1303 //kdDebug(7040) << "csize: " << d->m_currentFile->compressedSize() << endl;
1304 //kdDebug(7040) << "headerstart: " << d->m_currentFile->headerStart() << endl;
1305
1306 //kdDebug(7040) << "crc: " << d->m_crc << endl;
1307 d->m_currentFile->setCRC32( d->m_crc );
1308
1309 d->m_currentFile = 0L;
1310
1311 // update saved offset for appending new files
1312 d->m_offset = device()->at();
1313 return true;
1314}
1315
1316bool KZip::writeSymLink(const TQString &name, const TQString &target,
1317 const TQString &user, const TQString &group,
1318 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
1319 return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime);
1320}
1321
1322bool KZip::writeSymLink_impl(const TQString &name, const TQString &target,
1323 const TQString &user, const TQString &group,
1324 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
1325
1326 // reassure that symlink flag is set, otherwise strange things happen on
1327 // extraction
1328 perm |= S_IFLNK;
1329 Compression c = compression();
1330 setCompression(NoCompression); // link targets are never compressed
1331
1332 if (!prepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) {
1333 kdWarning() << "KZip::writeFile prepareWriting failed" << endl;
1334 setCompression(c);
1335 return false;
1336 }
1337
1338 TQCString symlink_target = TQFile::encodeName(target);
1339 if (!writeData(symlink_target, symlink_target.length())) {
1340 kdWarning() << "KZip::writeFile writeData failed" << endl;
1341 setCompression(c);
1342 return false;
1343 }
1344
1345 if (!doneWriting(symlink_target.length())) {
1346 kdWarning() << "KZip::writeFile doneWriting failed" << endl;
1347 setCompression(c);
1348 return false;
1349 }
1350
1351 setCompression(c);
1352 return true;
1353}
1354
1355void KZip::virtual_hook( int id, void* data )
1356{
1357 switch (id) {
1358 case VIRTUAL_WRITE_DATA: {
1359 WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data);
1360 params->retval = writeData_impl( params->data, params->size );
1361 break;
1362 }
1363 case VIRTUAL_WRITE_SYMLINK: {
1364 WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
1365 params->retval = writeSymLink_impl(*params->name,*params->target,
1366 *params->user,*params->group,params->perm,
1367 params->atime,params->mtime,params->ctime);
1368 break;
1369 }
1370 case VIRTUAL_PREPARE_WRITING: {
1371 PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
1372 params->retval = prepareWriting_impl(*params->name,*params->user,
1373 *params->group,params->size,params->perm,
1374 params->atime,params->mtime,params->ctime);
1375 break;
1376 }
1377 default:
1378 KArchive::virtual_hook( id, data );
1379 }/*end switch*/
1380}
1381
1382// made virtual using virtual_hook
1383bool KZip::writeData(const char * c, uint i)
1384{
1385 return KArchive::writeData( c, i );
1386}
1387
1388bool KZip::writeData_impl(const char * c, uint i)
1389{
1390 Q_ASSERT( d->m_currentFile );
1391 Q_ASSERT( d->m_currentDev );
1392 if (!d->m_currentFile || !d->m_currentDev) {
1393 abort();
1394 return false;
1395 }
1396
1397 // crc to be calculated over uncompressed stuff...
1398 // and they didn't mention it in their docs...
1399 d->m_crc = crc32(d->m_crc, (const Bytef *) c , i);
1400
1401 TQ_LONG written = d->m_currentDev->writeBlock( c, i );
1402 //kdDebug(7040) << "KZip::writeData wrote " << i << " bytes." << endl;
1403 bool ok = written == (TQ_LONG)i;
1404 if ( !ok )
1405 abort();
1406 return ok;
1407}
1408
1409void KZip::setCompression( Compression c )
1410{
1411 d->m_compression = ( c == NoCompression ) ? 0 : 8;
1412}
1413
1414KZip::Compression KZip::compression() const
1415{
1416 return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
1417}
1418
1419void KZip::setExtraField( ExtraField ef )
1420{
1421 d->m_extraField = ef;
1422}
1423
1424KZip::ExtraField KZip::extraField() const
1425{
1426 return d->m_extraField;
1427}
1428
1429void KZip::abort()
1430{
1431 if ( d->m_saveFile ) {
1432 d->m_saveFile->abort();
1433 setDevice( 0 );
1434 }
1435}
1436
1437
1439
1440TQByteArray KZipFileEntry::data() const
1441{
1442 TQIODevice* dev = device();
1443 TQByteArray arr;
1444 if ( dev ) {
1445 arr = dev->readAll();
1446 delete dev;
1447 }
1448 return arr;
1449}
1450
1451TQIODevice* KZipFileEntry::device() const
1452{
1453 //kdDebug(7040) << "KZipFileEntry::device creating iodevice limited to pos=" << position() << ", csize=" << compressedSize() << endl;
1454 // Limit the reading to the appropriate part of the underlying device (e.g. file)
1455 KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
1456 if ( encoding() == 0 || compressedSize() == 0 ) // no compression (or even no data)
1457 return limitedDev;
1458
1459 if ( encoding() == 8 )
1460 {
1461 // On top of that, create a device that uncompresses the zlib data
1462 TQIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
1463 if ( !filterDev )
1464 return 0L; // ouch
1465 static_cast<KFilterDev *>(filterDev)->setSkipHeaders(); // Just zlib, not gzip
1466 bool b = filterDev->open( IO_ReadOnly );
1467 Q_ASSERT( b );
1468 return filterDev;
1469 }
1470
1471 kdError() << "This zip file contains files compressed with method "
1472 << encoding() <<", this method is currently not supported by KZip,"
1473 <<" please use a command-line tool to handle this file." << endl;
1474 return 0L;
1475}
KArchiveDirectory
Represents a directory entry in a KArchive.
Definition: karchive.h:574
KArchiveDirectory::entry
KArchiveEntry * entry(TQString name)
Returns the entry with the given name.
Definition: karchive.cpp:547
KArchiveEntry
A base class for entries in an KArchive.
Definition: karchive.h:396
KArchiveEntry::isDirectory
virtual bool isDirectory() const
Checks whether the entry is a directory.
Definition: karchive.h:464
KArchive
KArchive is a base class for reading and writing archives.
Definition: karchive.h:43
KArchive::rootDir
virtual KArchiveDirectory * rootDir()
Retrieves or create the root directory.
Definition: karchive.cpp:368
KArchive::open
virtual bool open(int mode)
Opens the archive for reading or writing.
Definition: karchive.cpp:91
KArchive::findOrCreate
KArchiveDirectory * findOrCreate(const TQString &path)
Ensures that path exists, create otherwise.
Definition: karchive.cpp:383
KArchive::close
virtual void close()
Closes the archive.
Definition: karchive.cpp:108
KArchive::mode
int mode() const
Returns the mode in which the archive was opened.
Definition: karchive.h:90
KArchive::device
TQIODevice * device() const
The underlying device.
Definition: karchive.h:96
KArchive::isOpened
bool isOpened() const
Checks whether the archive is open.
Definition: karchive.h:83
KArchive::writeData
bool writeData(const char *data, uint size)
Write data into the current file - to be called after calling prepareWriting.
Definition: karchive.cpp:353
KArchive::writeSymLink
bool writeSymLink(const TQString &name, const TQString &target, const TQString &user, const TQString &group, mode_t perm, time_t atime, time_t mtime, time_t ctime)
Writes a symbolic link to the archive if the archive must be opened for writing.
Definition: karchive.cpp:327
KArchive::prepareWriting
virtual bool prepareWriting(const TQString &name, const TQString &user, const TQString &group, uint size)=0
Here's another way of writing a file into an archive: Call prepareWriting, then call writeData() as m...
KArchive::writeFile
virtual bool writeFile(const TQString &name, const TQString &user, const TQString &group, uint size, const char *data)
If an archive is opened for writing then you can add a new file using this function.
Definition: karchive.cpp:228
KFilterDev
A class for reading and writing compressed data onto a device (e.g.
Definition: kfilterdev.h:37
KFilterDev::open
virtual bool open(int mode)
Open for reading or writing.
Definition: kfilterdev.cpp:119
KFilterDev::device
static TQIODevice * device(TQIODevice *inDevice, const TQString &mimetype)
Creates an i/o device that is able to read from the TQIODevice inDevice, whether the data is compress...
Definition: kfilterdev.cpp:101
KLimitedIODevice
A readonly device that reads from an underlying device from a given point to another (e....
Definition: klimitediodevice.h:32
KZip
This class implements a tdeioslave to access zip files from KDE.
Definition: kzip.h:54
KZip::writeDir
virtual bool writeDir(const TQString &name, const TQString &user, const TQString &group)
If an archive is opened for writing then you can add new directories using this function.
Definition: kzip.cpp:1054
KZip::fileName
TQString fileName()
The name of the zip file, as passed to the constructor.
Definition: kzip.h:84
KZip::setCompression
void setCompression(Compression c)
Call this before writeFile or prepareWriting, to define whether the next files to be written should b...
Definition: kzip.cpp:1409
KZip::ExtraField
ExtraField
Describes the contents of the "extra field" for a given file in the Zip archive.
Definition: kzip.h:89
KZip::ModificationTime
@ ModificationTime
Modification time ("extended timestamp" header)
Definition: kzip.h:90
KZip::NoExtraField
@ NoExtraField
No extra field.
Definition: kzip.h:89
KZip::writeData
bool writeData(const char *data, uint size)
Write data to a file that has been created using prepareWriting().
Definition: kzip.cpp:1383
KZip::closeArchive
virtual bool closeArchive()
Closes the archive.
Definition: kzip.cpp:855
KZip::prepareWriting
virtual bool prepareWriting(const TQString &name, const TQString &user, const TQString &group, uint size)
Alternative method for writing: call prepareWriting(), then feed the data in small chunks using write...
Definition: kzip.cpp:1088
KZip::setExtraField
void setExtraField(ExtraField ef)
Call this before writeFile or prepareWriting, to define what the next file to be written should have ...
Definition: kzip.cpp:1419
KZip::writeFile
virtual bool writeFile(const TQString &name, const TQString &user, const TQString &group, uint size, const char *data)
If an archive is opened for writing then you can add a new file using this function.
Definition: kzip.cpp:1070
KZip::~KZip
virtual ~KZip()
If the zip file is still opened, then it will be closed automatically by the destructor.
Definition: kzip.cpp:354
KZip::openArchive
virtual bool openArchive(int mode)
Opens the archive for reading.
Definition: kzip.cpp:369
KZip::Compression
Compression
Describes the compression type for a given file in the Zip archive.
Definition: kzip.h:112
KZip::DeflateCompression
@ DeflateCompression
Deflate compression method.
Definition: kzip.h:113
KZip::NoCompression
@ NoCompression
Uncompressed.
Definition: kzip.h:112
KZip::compression
Compression compression() const
The current compression mode that will be used for new files.
Definition: kzip.cpp:1414
KZip::KZip
KZip(const TQString &filename)
Creates an instance that operates on the given filename.
Definition: kzip.cpp:335
KZip::doneWriting
virtual bool doneWriting(uint size)
Write data to a file that has been created using prepareWriting().
Definition: kzip.cpp:1279
KZip::extraField
ExtraField extraField() const
The current type of "extra field" that will be used for new files.
Definition: kzip.cpp:1424

tdeio/tdeio

Skip menu "tdeio/tdeio"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

tdeio/tdeio

Skip menu "tdeio/tdeio"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdeio/tdeio by doxygen 1.9.4
This website is maintained by Timothy Pearson.