• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.14.38 API Reference
  • KDE Home
  • Contact Us
 

KDECore

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

KDE's Doxygen guidelines are available online.

KDECore

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

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal