kio Library API Documentation

kzip.cpp

00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2000 David Faure <faure@kde.org> 00003 Copyright (C) 2002 Holger Schroeder <holger-kde@holgis.net> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License version 2 as published by the Free Software Foundation. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00017 Boston, MA 02111-1307, USA. 00018 */ 00019 00020 /* 00021 This class implements a kioslave to access ZIP files from KDE. 00022 you can use it in IO_ReadOnly or in IO_WriteOnly mode, and it 00023 behaves just as expected (i hope ;-) ). 00024 It can also be used in IO_ReadWrite mode, in this case one can 00025 append files to an existing zip archive. when you append new files, which 00026 are not yet in the zip, it works as expected, they are appended at the end. 00027 when you append a file, which is already in the file, the reference to the 00028 old file is dropped and the new one is added to the zip. but the 00029 old data from the file itself is not deleted, it is still in the 00030 zipfile. so when you want to have a small and garbagefree zipfile, 00031 just read the contents of the appended zipfile and write it to a new one 00032 in IO_WriteOnly mode. especially take care of this, when you don't want 00033 to leak information of how intermediate versions of files in the zip 00034 were looking. 00035 For more information on the zip fileformat go to 00036 http://www.pkware.com/support/appnote.html . 00037 00038 */ 00039 00040 #include <qasciidict.h> 00041 #include <qfile.h> 00042 #include <qdir.h> 00043 #include <time.h> 00044 #include <string.h> 00045 #include <qdatetime.h> 00046 #include <kdebug.h> 00047 #include <qptrlist.h> 00048 #include <kmimetype.h> 00049 #include <zlib.h> 00050 00051 #include "kfilterdev.h" 00052 #include "kzip.h" 00053 #include "klimitediodevice.h" 00054 00055 const int max_path_len = 4095; // maximum number of character a path may contain 00056 00057 static void transformToMsDos(const QDateTime& dt, char* buffer) 00058 { 00059 if ( dt.isValid() ) 00060 { 00061 const Q_UINT16 time = 00062 ( dt.time().hour() << 11 ) // 5 bit hour 00063 | ( dt.time().minute() << 5 ) // 6 bit minute 00064 | ( dt.time().second() >> 1 ); // 5 bit double seconds 00065 00066 buffer[0] = char(time); 00067 buffer[1] = char(time >> 8); 00068 00069 const Q_UINT16 date = 00070 ( ( dt.date().year() - 1980 ) << 9 ) // 7 bit year 1980-based 00071 | ( dt.date().month() << 5 ) // 4 bit month 00072 | ( dt.date().day() ); // 5 bit day 00073 00074 buffer[2] = char(date); 00075 buffer[3] = char(date >> 8); 00076 } 00077 else // !dt.isValid(), assume 1980-01-01 midnight 00078 { 00079 buffer[0] = 0; 00080 buffer[1] = 0; 00081 buffer[2] = 33; 00082 buffer[3] = 0; 00083 } 00084 } 00085 00086 // == parsing routines for zip headers 00087 00089 struct ParseFileInfo { 00090 // file related info 00091 // QCString name; // filename 00092 mode_t perm; // permissions of this file 00093 time_t atime; // last access time (UNIX format) 00094 time_t mtime; // modification time (UNIX format) 00095 time_t ctime; // creation time (UNIX format) 00096 int uid; // user id (-1 if not specified) 00097 int gid; // group id (-1 if not specified) 00098 QCString guessed_symlink; // guessed symlink target 00099 int extralen; // length of extra field 00100 00101 // parsing related info 00102 bool exttimestamp_seen; // true if extended timestamp extra field 00103 // has been parsed 00104 bool newinfounix_seen; // true if Info-ZIP Unix New extra field has 00105 // been parsed 00106 00107 ParseFileInfo() : perm(0100644), uid(-1), gid(-1), extralen(0), 00108 exttimestamp_seen(false), newinfounix_seen(false) { 00109 ctime = mtime = atime = time(0); 00110 } 00111 }; 00112 00121 static bool parseExtTimestamp(const char *buffer, int size, bool islocal, 00122 ParseFileInfo &pfi) { 00123 if (size < 1) { 00124 kdDebug(7040) << "premature end of extended timestamp (#1)" << endl; 00125 return false; 00126 }/*end if*/ 00127 int flags = *buffer; // read flags 00128 buffer += 1; 00129 00130 if (flags & 1) { // contains modification time 00131 if (size < 5) { 00132 kdDebug(7040) << "premature end of extended timestamp (#2)" << endl; 00133 return false; 00134 }/*end if*/ 00135 pfi.mtime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8 00136 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24); 00137 }/*end if*/ 00138 buffer += 4; 00139 // central extended field cannot contain more than the modification time 00140 // even if other flags are set 00141 if (!islocal) { 00142 pfi.exttimestamp_seen = true; 00143 return true; 00144 }/*end if*/ 00145 00146 if (flags & 2) { // contains last access time 00147 if (size < 9) { 00148 kdDebug(7040) << "premature end of extended timestamp (#3)" << endl; 00149 return false; 00150 }/*end if*/ 00151 pfi.atime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8 00152 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24); 00153 }/*end if*/ 00154 buffer += 4; 00155 00156 if (flags & 4) { // contains creation time 00157 if (size < 13) { 00158 kdDebug(7040) << "premature end of extended timestamp (#4)" << endl; 00159 return false; 00160 }/*end if*/ 00161 pfi.ctime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8 00162 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24); 00163 }/*end if*/ 00164 buffer += 4; 00165 00166 pfi.exttimestamp_seen = true; 00167 return true; 00168 } 00169 00178 static bool parseInfoZipUnixOld(const char *buffer, int size, bool islocal, 00179 ParseFileInfo &pfi) { 00180 // spec mandates to omit this field if one of the newer fields are available 00181 if (pfi.exttimestamp_seen || pfi.newinfounix_seen) return true; 00182 00183 if (size < 8) { 00184 kdDebug(7040) << "premature end of Info-ZIP unix extra field old" << endl; 00185 return false; 00186 }/*end if*/ 00187 00188 pfi.atime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8 00189 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24); 00190 buffer += 4; 00191 pfi.mtime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8 00192 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24); 00193 buffer += 4; 00194 if (islocal && size >= 12) { 00195 pfi.uid = (uchar)buffer[0] | (uchar)buffer[1] << 8; 00196 buffer += 2; 00197 pfi.gid = (uchar)buffer[0] | (uchar)buffer[1] << 8; 00198 buffer += 2; 00199 }/*end if*/ 00200 return true; 00201 } 00202 00203 #if 0 // not needed yet 00204 00212 static bool parseInfoZipUnixNew(const char *buffer, int size, bool islocal, 00213 ParseFileInfo &pfi) { 00214 if (!islocal) { // contains nothing in central field 00215 pfi.newinfounix = true; 00216 return true; 00217 }/*end if*/ 00218 00219 if (size < 4) { 00220 kdDebug(7040) << "premature end of Info-ZIP unix extra field new" << endl; 00221 return false; 00222 }/*end if*/ 00223 00224 pfi.uid = (uchar)buffer[0] | (uchar)buffer[1] << 8; 00225 buffer += 2; 00226 pfi.gid = (uchar)buffer[0] | (uchar)buffer[1] << 8; 00227 buffer += 2; 00228 00229 pfi.newinfounix = true; 00230 return true; 00231 } 00232 #endif 00233 00242 static bool parseExtraField(const char *buffer, int size, bool islocal, 00243 ParseFileInfo &pfi) { 00244 // extra field in central directory doesn't contain useful data, so we 00245 // don't bother parsing it 00246 if (!islocal) return true; 00247 00248 while (size >= 4) { // as long as a potential extra field can be read 00249 int magic = (uchar)buffer[0] | (uchar)buffer[1] << 8; 00250 buffer += 2; 00251 int fieldsize = (uchar)buffer[0] | (uchar)buffer[1] << 8; 00252 buffer += 2; 00253 size -= 4; 00254 00255 if (fieldsize > size) { 00256 //kdDebug(7040) << "fieldsize: " << fieldsize << " size: " << size << endl; 00257 kdDebug(7040) << "premature end of extra fields reached" << endl; 00258 break; 00259 }/*end if*/ 00260 00261 switch (magic) { 00262 case 0x5455: // extended timestamp 00263 if (!parseExtTimestamp(buffer, fieldsize, islocal, pfi)) return false; 00264 break; 00265 case 0x5855: // old Info-ZIP unix extra field 00266 if (!parseInfoZipUnixOld(buffer, fieldsize, islocal, pfi)) return false; 00267 break; 00268 #if 0 // not needed yet 00269 case 0x7855: // new Info-ZIP unix extra field 00270 if (!parseInfoZipUnixNew(buffer, fieldsize, islocal, pfi)) return false; 00271 break; 00272 #endif 00273 default: 00274 /* ignore everything else */; 00275 }/*end switch*/ 00276 00277 buffer += fieldsize; 00278 size -= fieldsize; 00279 }/*wend*/ 00280 return true; 00281 } 00282 00286 00287 class KZip::KZipPrivate 00288 { 00289 public: 00290 KZipPrivate() 00291 : m_crc( 0 ), 00292 m_currentFile( 0L ), 00293 m_currentDev( 0L ), 00294 m_compression( 8 ), 00295 m_extraField( KZip::NoExtraField ), 00296 m_offset( 0L ) { } 00297 00298 unsigned long m_crc; // checksum 00299 KZipFileEntry* m_currentFile; // file currently being written 00300 QIODevice* m_currentDev; // filterdev used to write to the above file 00301 QPtrList<KZipFileEntry> m_fileList; // flat list of all files, for the index (saves a recursive method ;) 00302 int m_compression; 00303 KZip::ExtraField m_extraField; 00304 unsigned int m_offset; // holds the offset of the place in the zip, 00305 // where new data can be appended. after openarchive it points to 0, when in 00306 // writeonly mode, or it points to the beginning of the central directory. 00307 // each call to writefile updates this value. 00308 }; 00309 00310 KZip::KZip( const QString& filename ) 00311 : KArchive( 0L ) 00312 { 00313 //kdDebug(7040) << "KZip(filename) reached." << endl; 00314 m_filename = filename; 00315 d = new KZipPrivate; 00316 setDevice( new QFile( filename ) ); 00317 } 00318 00319 KZip::KZip( QIODevice * dev ) 00320 : KArchive( dev ) 00321 { 00322 //kdDebug(7040) << "KZip::KZip( QIODevice * dev) reached." << endl; 00323 d = new KZipPrivate; 00324 } 00325 00326 KZip::~KZip() 00327 { 00328 // mjarrett: Closes to prevent ~KArchive from aborting w/o device 00329 //kdDebug(7040) << "~KZip reached." << endl; 00330 if( isOpened() ) 00331 close(); 00332 if ( !m_filename.isEmpty() ) 00333 delete device(); // we created it ourselves 00334 delete d; 00335 } 00336 00337 bool KZip::openArchive( int mode ) 00338 { 00339 //kdDebug(7040) << "openarchive reached." << endl; 00340 d->m_fileList.clear(); 00341 00342 if ( mode == IO_WriteOnly ) 00343 return true; 00344 if ( mode != IO_ReadOnly && mode != IO_ReadWrite ) 00345 { 00346 kdWarning(7040) << "Unsupported mode " << mode << endl; 00347 return false; 00348 } 00349 00350 char buffer[47]; 00351 00352 // Check that it's a valid ZIP file 00353 // KArchive::open() opened the underlying device already. 00354 QIODevice* dev = device(); 00355 00356 uint offset = 0; // holds offset, where we read 00357 int n; 00358 00359 // contains information gathered from the local file headers 00360 QAsciiDict<ParseFileInfo> pfi_map(1009, true /*case sensitive */, true /*copy keys*/); 00361 pfi_map.setAutoDelete(true); 00362 00363 for (;;) // repeat until 'end of entries' signature is reached 00364 { 00365 n = dev->readBlock( buffer, 4 ); 00366 00367 if (n < 4) 00368 { 00369 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#1)" << endl; 00370 00371 return false; 00372 } 00373 00374 if ( !memcmp( buffer, "PK\5\6", 4 ) ) // 'end of entries' 00375 break; 00376 00377 if ( !memcmp( buffer, "PK\3\4", 4 ) ) // local file header 00378 { 00379 dev->at( dev->at() + 2 ); // skip 'version needed to extract' 00380 00381 // read static header stuff 00382 n = dev->readBlock( buffer, 24 ); 00383 if (n < 24) { 00384 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#4)" << endl; 00385 return false; 00386 } 00387 00388 int gpf = (uchar)buffer[0]; // "general purpose flag" not "general protection fault" ;-) 00389 int compression_mode = (uchar)buffer[2] | (uchar)buffer[3] << 8; 00390 Q_LONG compr_size = (uchar)buffer[12] | (uchar)buffer[13] << 8 00391 | (uchar)buffer[14] << 16 | (uchar)buffer[15] << 24; 00392 Q_LONG uncomp_size = (uchar)buffer[16] | (uchar)buffer[17] << 8 00393 | (uchar)buffer[18] << 16 | (uchar)buffer[19] << 24; 00394 int namelen = (uchar)buffer[20] | (uchar)buffer[21] << 8; 00395 int extralen = (uchar)buffer[22] | (uchar)buffer[23] << 8; 00396 00397 // read filename 00398 QCString filename(namelen + 1); 00399 n = dev->readBlock(filename.data(), namelen); 00400 if ( n < namelen ) { 00401 kdWarning(7040) << "Invalid ZIP file. Name not completely read (#2)" << endl; 00402 return false; 00403 } 00404 00405 ParseFileInfo *pfi = new ParseFileInfo(); 00406 pfi_map.insert(filename.data(), pfi); 00407 00408 // read and parse the beginning of the extra field, 00409 // skip rest of extra field in case it is too long 00410 unsigned int extraFieldEnd = dev->at() + extralen; 00411 pfi->extralen = extralen; 00412 int handledextralen = QMIN(extralen, (int)sizeof buffer); 00413 n = dev->readBlock(buffer, handledextralen); 00414 // no error msg necessary as we deliberately truncate the extra field 00415 if (!parseExtraField(buffer, handledextralen, true, *pfi)) 00416 return false; 00417 00418 // jump to end of extra field 00419 dev->at( extraFieldEnd ); 00420 00421 // we have to take care of the 'general purpose bit flag'. 00422 // if bit 3 is set, the header doesn't contain the length of 00423 // the file and we look for the signature 'PK\7\8' or another 00424 // PKxx signature. 00425 if ( gpf & 8 ) 00426 { 00427 bool foundSignature = false; 00428 00429 while (!foundSignature) 00430 { 00431 n = dev->readBlock( buffer, 1 ); 00432 if (n < 1) 00433 { 00434 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)" << endl; 00435 return false; 00436 } 00437 00438 if ( buffer[0] != 'P' ) 00439 continue; 00440 00441 n = dev->readBlock( buffer, 3 ); 00442 if (n < 3) 00443 { 00444 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)" << endl; 00445 return false; 00446 } 00447 00448 // we have to detect three magic tokens here: 00449 // PK34 for the next local header in case there is no data descriptor 00450 // PK12 for the central header in case there is no data descriptor 00451 // PK78 for the data descriptor in case it is following the compressed data 00452 00453 if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 ) 00454 { 00455 foundSignature = true; 00456 dev->at( dev->at() + 12 ); // skip the 'data_descriptor' 00457 } 00458 else if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 ) 00459 || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) ) 00460 { 00461 foundSignature = true; 00462 dev->at( dev->at() - 4 ); // go back 4 bytes, so that the magic bytes can be found 00463 // in the next cycle 00464 } 00465 } 00466 } 00467 else // local header contains the compressed and uncompressed size 00468 { 00469 // check if this could be a symbolic link 00470 if (compression_mode == NoCompression 00471 && uncomp_size <= max_path_len 00472 && uncomp_size > 0) { 00473 // read content and store it 00474 pfi->guessed_symlink.resize(uncomp_size + 1); 00475 n = dev->readBlock(pfi->guessed_symlink.data(), uncomp_size); 00476 if (n < uncomp_size) { 00477 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#5)" << endl; 00478 return false; 00479 } 00480 } 00481 else // normal file 00482 { 00483 if ( dev->size() > 0 && compr_size > (Q_LONG)dev->size() ) 00484 { 00485 // here we cannot trust the compressed size, so scan through the compressed 00486 // data to find the next header 00487 bool foundSignature = false; 00488 00489 while (!foundSignature) 00490 { 00491 n = dev->readBlock( buffer, 1 ); 00492 if (n < 1) 00493 { 00494 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#6)" << endl; 00495 return false; 00496 } 00497 00498 if ( buffer[0] != 'P' ) 00499 continue; 00500 00501 n = dev->readBlock( buffer, 3 ); 00502 if (n < 3) 00503 { 00504 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#7)" << endl; 00505 return false; 00506 } 00507 00508 // we have to detect three magic tokens here: 00509 // PK34 for the next local header in case there is no data descriptor 00510 // PK12 for the central header in case there is no data descriptor 00511 // PK78 for the data descriptor in case it is following the compressed data 00512 00513 if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 ) 00514 { 00515 foundSignature = true; 00516 dev->at( dev->at() + 12 ); // skip the 'data_descriptor' 00517 } 00518 else if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 ) 00519 || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) ) 00520 { 00521 foundSignature = true; 00522 dev->at( dev->at() - 4 ); 00523 // go back 4 bytes, so that the magic bytes can be found 00524 // in the next cycle... 00525 } 00526 } 00527 } 00528 else 00529 { 00530 dev->at( dev->at() + compr_size ); 00531 } 00532 } 00533 // here we calculate the length of the file in the zip 00534 // with headers and jump to the next header. 00535 uint skip = compr_size + namelen + extralen; 00536 offset += 30 + skip; 00537 } 00538 } 00539 else if ( !memcmp( buffer, "PK\1\2", 4 ) ) // central block 00540 { 00541 00542 // so we reached the central header at the end of the zip file 00543 // here we get all interesting data out of the central header 00544 // of a file 00545 offset = dev->at() - 4; 00546 00547 //set offset for appending new files 00548 if ( d->m_offset == 0L ) d->m_offset = offset; 00549 00550 n = dev->readBlock( buffer + 4, 42 ); 00551 if (n < 42) { 00552 kdWarning(7040) << "Invalid ZIP file, central entry too short" << endl; // not long enough for valid entry 00553 return false; 00554 } 00555 // length of the filename (well, pathname indeed) 00556 int namelen = (uchar)buffer[29] << 8 | (uchar)buffer[28]; 00557 QCString bufferName( namelen + 1 ); 00558 n = dev->readBlock( bufferName.data(), namelen ); 00559 if ( n < namelen ) 00560 kdWarning(7040) << "Invalid ZIP file. Name not completely read" << endl; 00561 00562 ParseFileInfo *pfi = pfi_map[bufferName]; 00563 if (!pfi) { // can that happen? 00564 pfi_map.insert(bufferName.data(), pfi = new ParseFileInfo()); 00565 } 00566 QString name( QFile::decodeName(bufferName) ); 00567 00568 //kdDebug(7040) << "name: " << name << endl; 00569 // only in central header ! see below. 00570 // length of extra attributes 00571 int extralen = (uchar)buffer[31] << 8 | (uchar)buffer[30]; 00572 // length of comment for this file 00573 int commlen = (uchar)buffer[33] << 8 | (uchar)buffer[32]; 00574 // compression method of this file 00575 int cmethod = (uchar)buffer[11] << 8 | (uchar)buffer[10]; 00576 00577 //kdDebug(7040) << "cmethod: " << cmethod << endl; 00578 //kdDebug(7040) << "extralen: " << extralen << endl; 00579 00580 // uncompressed file size 00581 uint ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 | 00582 (uchar)buffer[25] << 8 | (uchar)buffer[24]; 00583 // compressed file size 00584 uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 | 00585 (uchar)buffer[21] << 8 | (uchar)buffer[20]; 00586 00587 // offset of local header 00588 uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 | 00589 (uchar)buffer[43] << 8 | (uchar)buffer[42]; 00590 00591 // some clever people use different extra field lengths 00592 // in the central header and in the local header... funny. 00593 // so we need to get the localextralen to calculate the offset 00594 // from localheaderstart to dataoffset 00595 int localextralen = pfi->extralen; // FIXME: this will not work if 00596 // no local header exists 00597 00598 //kdDebug(7040) << "localextralen: " << localextralen << endl; 00599 00600 // offset, where the real data for uncompression starts 00601 uint dataoffset = localheaderoffset + 30 + localextralen + namelen; //comment only in central header 00602 00603 //kdDebug(7040) << "esize: " << esize << endl; 00604 //kdDebug(7040) << "eoffset: " << eoffset << endl; 00605 //kdDebug(7040) << "csize: " << csize << endl; 00606 00607 int os_madeby = (uchar)buffer[5]; 00608 bool isdir = false; 00609 int access = 0100644; 00610 00611 if (os_madeby == 3) { // good ole unix 00612 access = (uchar)buffer[40] | (uchar)buffer[41] << 8; 00613 } 00614 00615 QString entryName; 00616 00617 if ( name.endsWith( "/" ) ) // Entries with a trailing slash are directories 00618 { 00619 isdir = true; 00620 name = name.left( name.length() - 1 ); 00621 if (os_madeby != 3) access = S_IFDIR | 0755; 00622 else Q_ASSERT(access & S_IFDIR); 00623 } 00624 00625 int pos = name.findRev( '/' ); 00626 if ( pos == -1 ) 00627 entryName = name; 00628 else 00629 entryName = name.mid( pos + 1 ); 00630 Q_ASSERT( !entryName.isEmpty() ); 00631 00632 KArchiveEntry* entry; 00633 if ( isdir ) 00634 { 00635 QString path = QDir::cleanDirPath( name ); 00636 KArchiveEntry* ent = rootDir()->entry( path ); 00637 if ( ent && ent->isDirectory() ) 00638 { 00639 //kdDebug(7040) << "Directory already exists, NOT going to add it again" << endl; 00640 entry = 0L; 00641 } 00642 else 00643 { 00644 entry = new KArchiveDirectory( this, entryName, access, (int)pfi->mtime, rootDir()->user(), rootDir()->group(), QString::null ); 00645 //kdDebug(7040) << "KArchiveDirectory created, entryName= " << entryName << ", name=" << name << endl; 00646 } 00647 } 00648 else 00649 { 00650 QString symlink; 00651 if (S_ISLNK(access)) { 00652 symlink = QFile::decodeName(pfi->guessed_symlink); 00653 } 00654 entry = new KZipFileEntry( this, entryName, access, pfi->mtime, 00655 rootDir()->user(), rootDir()->group(), 00656 symlink, name, dataoffset, 00657 ucsize, cmethod, csize ); 00658 static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset ); 00659 //kdDebug(7040) << "KZipFileEntry created, entryName= " << entryName << ", name=" << name << endl; 00660 d->m_fileList.append( static_cast<KZipFileEntry *>( entry ) ); 00661 } 00662 00663 if ( entry ) 00664 { 00665 if ( pos == -1 ) 00666 { 00667 rootDir()->addEntry(entry); 00668 } 00669 else 00670 { 00671 // In some tar files we can find dir/./file => call cleanDirPath 00672 QString path = QDir::cleanDirPath( name.left( pos ) ); 00673 // Ensure container directory exists, create otherwise 00674 KArchiveDirectory * tdir = findOrCreate( path ); 00675 tdir->addEntry(entry); 00676 } 00677 } 00678 00679 //calculate offset to next entry 00680 offset += 46 + commlen + extralen + namelen; 00681 bool b = dev->at(offset); 00682 Q_ASSERT( b ); 00683 if ( !b ) 00684 return false; 00685 } 00686 else 00687 { 00688 kdWarning(7040) << "Invalid ZIP file. Unrecognized header at offset " << offset << endl; 00689 00690 return false; 00691 } 00692 } 00693 //kdDebug(7040) << "*** done *** " << endl; 00694 return true; 00695 } 00696 00697 bool KZip::closeArchive() 00698 { 00699 if ( ! ( mode() & IO_WriteOnly ) ) 00700 { 00701 //kdDebug(7040) << "closearchive readonly reached." << endl; 00702 return true; 00703 } 00704 //ReadWrite or WriteOnly 00705 //write all central dir file entries 00706 00707 // to be written at the end of the file... 00708 char buffer[ 22 ]; // first used for 12, then for 22 at the end 00709 uLong crc = crc32(0L, Z_NULL, 0); 00710 00711 Q_LONG centraldiroffset = device()->at(); 00712 //kdDebug(7040) << "closearchive: centraldiroffset: " << centraldiroffset << endl; 00713 Q_LONG atbackup = centraldiroffset; 00714 QPtrListIterator<KZipFileEntry> it( d->m_fileList ); 00715 00716 for ( ; it.current() ; ++it ) 00717 { //set crc and compressed size in each local file header 00718 if ( !device()->at( it.current()->headerStart() + 14 ) ) 00719 return false; 00720 //kdDebug(7040) << "closearchive setcrcandcsize: filename: " 00721 // << it.current()->path() 00722 // << " encoding: "<< it.current()->encoding() << endl; 00723 00724 uLong mycrc = it.current()->crc32(); 00725 buffer[0] = char(mycrc); // crc checksum, at headerStart+14 00726 buffer[1] = char(mycrc >> 8); 00727 buffer[2] = char(mycrc >> 16); 00728 buffer[3] = char(mycrc >> 24); 00729 00730 int mysize1 = it.current()->compressedSize(); 00731 buffer[4] = char(mysize1); // compressed file size, at headerStart+18 00732 buffer[5] = char(mysize1 >> 8); 00733 buffer[6] = char(mysize1 >> 16); 00734 buffer[7] = char(mysize1 >> 24); 00735 00736 int myusize = it.current()->size(); 00737 buffer[8] = char(myusize); // uncompressed file size, at headerStart+22 00738 buffer[9] = char(myusize >> 8); 00739 buffer[10] = char(myusize >> 16); 00740 buffer[11] = char(myusize >> 24); 00741 00742 if ( device()->writeBlock( buffer, 12 ) != 12 ) 00743 return false; 00744 } 00745 device()->at( atbackup ); 00746 00747 for ( it.toFirst(); it.current() ; ++it ) 00748 { 00749 //kdDebug(7040) << "closearchive: filename: " << it.current()->path() 00750 // << " encoding: "<< it.current()->encoding() << endl; 00751 00752 QCString path = QFile::encodeName(it.current()->path()); 00753 00754 const int extra_field_len = 9; 00755 int bufferSize = extra_field_len + path.length() + 46; 00756 char* buffer = new char[ bufferSize ]; 00757 00758 memset(buffer, 0, 46); // zero is a nice default for most header fields 00759 00760 const char head[] = 00761 { 00762 'P', 'K', 1, 2, // central file header signature 00763 0x14, 3, // version made by (3 == UNIX) 00764 0x14, 0 // version needed to extract 00765 }; 00766 00767 // I do not know why memcpy is not working here 00768 //memcpy(buffer, head, sizeof(head)); 00769 qmemmove(buffer, head, sizeof(head)); 00770 00771 buffer[ 10 ] = char(it.current()->encoding()); // compression method 00772 buffer[ 11 ] = char(it.current()->encoding() >> 8); 00773 00774 transformToMsDos( it.current()->datetime(), &buffer[ 12 ] ); 00775 00776 uLong mycrc = it.current()->crc32(); 00777 buffer[ 16 ] = char(mycrc); // crc checksum 00778 buffer[ 17 ] = char(mycrc >> 8); 00779 buffer[ 18 ] = char(mycrc >> 16); 00780 buffer[ 19 ] = char(mycrc >> 24); 00781 00782 int mysize1 = it.current()->compressedSize(); 00783 buffer[ 20 ] = char(mysize1); // compressed file size 00784 buffer[ 21 ] = char(mysize1 >> 8); 00785 buffer[ 22 ] = char(mysize1 >> 16); 00786 buffer[ 23 ] = char(mysize1 >> 24); 00787 00788 int mysize = it.current()->size(); 00789 buffer[ 24 ] = char(mysize); // uncompressed file size 00790 buffer[ 25 ] = char(mysize >> 8); 00791 buffer[ 26 ] = char(mysize >> 16); 00792 buffer[ 27 ] = char(mysize >> 24); 00793 00794 buffer[ 28 ] = char(it.current()->path().length()); // filename length 00795 buffer[ 29 ] = char(it.current()->path().length() >> 8); 00796 00797 buffer[ 30 ] = char(extra_field_len); 00798 buffer[ 31 ] = char(extra_field_len >> 8); 00799 00800 buffer[ 40 ] = char(it.current()->permissions()); 00801 buffer[ 41 ] = char(it.current()->permissions() >> 8); 00802 00803 int myhst = it.current()->headerStart(); 00804 buffer[ 42 ] = char(myhst); //relative offset of local header 00805 buffer[ 43 ] = char(myhst >> 8); 00806 buffer[ 44 ] = char(myhst >> 16); 00807 buffer[ 45 ] = char(myhst >> 24); 00808 00809 // file name 00810 strncpy( buffer + 46, path, path.length() ); 00811 //kdDebug(7040) << "closearchive length to write: " << bufferSize << endl; 00812 00813 // extra field 00814 char *extfield = buffer + 46 + path.length(); 00815 extfield[0] = 'U'; 00816 extfield[1] = 'T'; 00817 extfield[2] = 5; 00818 extfield[3] = 0; 00819 extfield[4] = 1 | 2 | 4; // specify flags from local field 00820 // (unless I misread the spec) 00821 // provide only modification time 00822 unsigned long time = (unsigned long)it.current()->date(); 00823 extfield[5] = char(time); 00824 extfield[6] = char(time >> 8); 00825 extfield[7] = char(time >> 16); 00826 extfield[8] = char(time >> 24); 00827 00828 crc = crc32(crc, (Bytef *)buffer, bufferSize ); 00829 bool ok = ( device()->writeBlock( buffer, bufferSize ) == bufferSize ); 00830 delete[] buffer; 00831 if ( !ok ) 00832 return false; 00833 } 00834 Q_LONG centraldirendoffset = device()->at(); 00835 //kdDebug(7040) << "closearchive: centraldirendoffset: " << centraldirendoffset << endl; 00836 //kdDebug(7040) << "closearchive: device()->at(): " << device()->at() << endl; 00837 00838 //write end of central dir record. 00839 buffer[ 0 ] = 'P'; //end of central dir signature 00840 buffer[ 1 ] = 'K'; 00841 buffer[ 2 ] = 5; 00842 buffer[ 3 ] = 6; 00843 00844 buffer[ 4 ] = 0; // number of this disk 00845 buffer[ 5 ] = 0; 00846 00847 buffer[ 6 ] = 0; // number of disk with start of central dir 00848 buffer[ 7 ] = 0; 00849 00850 int count = d->m_fileList.count(); 00851 //kdDebug(7040) << "number of files (count): " << count << endl; 00852 00853 00854 buffer[ 8 ] = char(count); // total number of entries in central dir of 00855 buffer[ 9 ] = char(count >> 8); // this disk 00856 00857 buffer[ 10 ] = buffer[ 8 ]; // total number of entries in the central dir 00858 buffer[ 11 ] = buffer[ 9 ]; 00859 00860 int cdsize = centraldirendoffset - centraldiroffset; 00861 buffer[ 12 ] = char(cdsize); // size of the central dir 00862 buffer[ 13 ] = char(cdsize >> 8); 00863 buffer[ 14 ] = char(cdsize >> 16); 00864 buffer[ 15 ] = char(cdsize >> 24); 00865 00866 //kdDebug(7040) << "end : centraldiroffset: " << centraldiroffset << endl; 00867 //kdDebug(7040) << "end : centraldirsize: " << cdsize << endl; 00868 00869 buffer[ 16 ] = char(centraldiroffset); // central dir offset 00870 buffer[ 17 ] = char(centraldiroffset >> 8); 00871 buffer[ 18 ] = char(centraldiroffset >> 16); 00872 buffer[ 19 ] = char(centraldiroffset >> 24); 00873 00874 buffer[ 20 ] = 0; //zipfile comment length 00875 buffer[ 21 ] = 0; 00876 00877 if ( device()->writeBlock( buffer, 22 ) != 22 ) 00878 return false; 00879 00880 //kdDebug(7040) << "kzip.cpp reached." << endl; 00881 return true; 00882 } 00883 00884 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0 00885 bool KZip::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data ) 00886 { 00887 mode_t mode = 0100644; 00888 time_t the_time = time(0); 00889 return KArchive::writeFile( name, user, group, size, mode, the_time, 00890 the_time, the_time, data ); 00891 } 00892 00893 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0 00894 bool KZip::writeFile( const QString& name, const QString& user, 00895 const QString& group, uint size, mode_t perm, 00896 time_t atime, time_t mtime, time_t ctime, 00897 const char* data ) { 00898 return KArchive::writeFile(name, user, group, size, perm, atime, mtime, 00899 ctime, data); 00900 } 00901 00902 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0 00903 bool KZip::prepareWriting( const QString& name, const QString& user, const QString& group, uint size ) 00904 { 00905 mode_t dflt_perm = 0100644; 00906 time_t the_time = time(0); 00907 return prepareWriting(name,user,group,size,dflt_perm, 00908 the_time,the_time,the_time); 00909 } 00910 00911 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0 00912 bool KZip::prepareWriting(const QString& name, const QString& user, 00913 const QString& group, uint size, mode_t perm, 00914 time_t atime, time_t mtime, time_t ctime) { 00915 return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime); 00916 } 00917 00918 bool KZip::prepareWriting_impl(const QString &name, const QString &user, 00919 const QString &group, uint /*size*/, mode_t perm, 00920 time_t atime, time_t mtime, time_t ctime) { 00921 //kdDebug(7040) << "prepareWriting reached." << endl; 00922 if ( !isOpened() ) 00923 { 00924 qWarning( "KZip::writeFile: You must open the zip file before writing to it\n"); 00925 return false; 00926 } 00927 00928 if ( ! ( mode() & IO_WriteOnly ) ) // accept WriteOnly and ReadWrite 00929 { 00930 qWarning( "KZip::writeFile: You must open the zip file for writing\n"); 00931 return false; 00932 } 00933 00934 // set right offset in zip. 00935 if ( !device()->at( d->m_offset ) ) { 00936 kdWarning(7040) << "prepareWriting_impl: cannot seek in ZIP file. Disk full?" << endl; 00937 return false; 00938 } 00939 00940 // delete entries in the filelist with the same filename as the one we want 00941 // to save, so that we donīt have duplicate file entries when viewing the zip 00942 // with konqi... 00943 // CAUTION: the old file itself is still in the zip and won't be removed !!! 00944 QPtrListIterator<KZipFileEntry> it( d->m_fileList ); 00945 00946 //kdDebug(7040) << "filename to write: " << name <<endl; 00947 for ( ; it.current() ; ++it ) 00948 { 00949 //kdDebug(7040) << "prepfilename: " << it.current()->path() <<endl; 00950 if (name == it.current()->path() ) 00951 { 00952 //kdDebug(7040) << "removing following entry: " << it.current()->path() <<endl; 00953 d->m_fileList.remove(); 00954 } 00955 00956 } 00957 // Find or create parent dir 00958 KArchiveDirectory* parentDir = rootDir(); 00959 QString fileName( name ); 00960 int i = name.findRev( '/' ); 00961 if ( i != -1 ) 00962 { 00963 QString dir = name.left( i ); 00964 fileName = name.mid( i + 1 ); 00965 //kdDebug(7040) << "KZip::prepareWriting ensuring " << dir << " exists. fileName=" << fileName << endl; 00966 parentDir = findOrCreate( dir ); 00967 } 00968 00969 // construct a KZipFileEntry and add it to list 00970 KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, QString::null, 00971 name, device()->at() + 30 + name.length(), // start 00972 0 /*size unknown yet*/, d->m_compression, 0 /*csize unknown yet*/ ); 00973 e->setHeaderStart( device()->at() ); 00974 //kdDebug(7040) << "wrote file start: " << e->position() << " name: " << name << endl; 00975 parentDir->addEntry( e ); 00976 00977 d->m_currentFile = e; 00978 d->m_fileList.append( e ); 00979 00980 int extra_field_len = 0; 00981 if ( d->m_extraField == ModificationTime ) 00982 extra_field_len = 17; // value also used in doneWriting() 00983 00984 // write out zip header 00985 QCString encodedName = QFile::encodeName(name); 00986 int bufferSize = extra_field_len + encodedName.length() + 30; 00987 //kdDebug(7040) << "KZip::prepareWriting bufferSize=" << bufferSize << endl; 00988 char* buffer = new char[ bufferSize ]; 00989 00990 buffer[ 0 ] = 'P'; //local file header signature 00991 buffer[ 1 ] = 'K'; 00992 buffer[ 2 ] = 3; 00993 buffer[ 3 ] = 4; 00994 00995 buffer[ 4 ] = 0x14; // version needed to extract 00996 buffer[ 5 ] = 0; 00997 00998 buffer[ 6 ] = 0; // general purpose bit flag 00999 buffer[ 7 ] = 0; 01000 01001 buffer[ 8 ] = char(e->encoding()); // compression method 01002 buffer[ 9 ] = char(e->encoding() >> 8); 01003 01004 transformToMsDos( e->datetime(), &buffer[ 10 ] ); 01005 01006 buffer[ 14 ] = 'C'; //dummy crc 01007 buffer[ 15 ] = 'R'; 01008 buffer[ 16 ] = 'C'; 01009 buffer[ 17 ] = 'q'; 01010 01011 buffer[ 18 ] = 'C'; //compressed file size 01012 buffer[ 19 ] = 'S'; 01013 buffer[ 20 ] = 'I'; 01014 buffer[ 21 ] = 'Z'; 01015 01016 buffer[ 22 ] = 'U'; //uncompressed file size 01017 buffer[ 23 ] = 'S'; 01018 buffer[ 24 ] = 'I'; 01019 buffer[ 25 ] = 'Z'; 01020 01021 buffer[ 26 ] = (uchar)(encodedName.length()); //filename length 01022 buffer[ 27 ] = (uchar)(encodedName.length() >> 8); 01023 01024 buffer[ 28 ] = (uchar)(extra_field_len); // extra field length 01025 buffer[ 29 ] = (uchar)(extra_field_len >> 8); 01026 01027 // file name 01028 strncpy( buffer + 30, encodedName, encodedName.length() ); 01029 01030 // extra field 01031 if ( d->m_extraField == ModificationTime ) 01032 { 01033 char *extfield = buffer + 30 + encodedName.length(); 01034 // "Extended timestamp" header (0x5455) 01035 extfield[0] = 'U'; 01036 extfield[1] = 'T'; 01037 extfield[2] = 13; // data size 01038 extfield[3] = 0; 01039 extfield[4] = 1 | 2 | 4; // contains mtime, atime, ctime 01040 01041 extfield[5] = char(mtime); 01042 extfield[6] = char(mtime >> 8); 01043 extfield[7] = char(mtime >> 16); 01044 extfield[8] = char(mtime >> 24); 01045 01046 extfield[9] = char(atime); 01047 extfield[10] = char(atime >> 8); 01048 extfield[11] = char(atime >> 16); 01049 extfield[12] = char(atime >> 24); 01050 01051 extfield[13] = char(ctime); 01052 extfield[14] = char(ctime >> 8); 01053 extfield[15] = char(ctime >> 16); 01054 extfield[16] = char(ctime >> 24); 01055 } 01056 01057 // Write header 01058 bool b = (device()->writeBlock( buffer, bufferSize ) == bufferSize ); 01059 d->m_crc = 0L; 01060 delete[] buffer; 01061 01062 Q_ASSERT( b ); 01063 if (!b) 01064 return false; 01065 01066 // Prepare device for writing the data 01067 // Either device() if no compression, or a KFilterDev to compress 01068 if ( d->m_compression == 0 ) { 01069 d->m_currentDev = device(); 01070 return true; 01071 } 01072 01073 d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false ); 01074 Q_ASSERT( d->m_currentDev ); 01075 if ( !d->m_currentDev ) 01076 return false; // ouch 01077 static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders(); // Just zlib, not gzip 01078 01079 b = d->m_currentDev->open( IO_WriteOnly ); 01080 Q_ASSERT( b ); 01081 return b; 01082 } 01083 01084 bool KZip::doneWriting( uint size ) 01085 { 01086 if ( d->m_currentFile->encoding() == 8 ) { 01087 // Finish 01088 (void)d->m_currentDev->writeBlock( 0, 0 ); 01089 delete d->m_currentDev; 01090 } 01091 // If 0, d->m_currentDev was device() - don't delete ;) 01092 d->m_currentDev = 0L; 01093 01094 Q_ASSERT( d->m_currentFile ); 01095 //kdDebug(7040) << "donewriting reached." << endl; 01096 //kdDebug(7040) << "filename: " << d->m_currentFile->path() << endl; 01097 //kdDebug(7040) << "getpos (at): " << device()->at() << endl; 01098 d->m_currentFile->setSize(size); 01099 int extra_field_len = 0; 01100 if ( d->m_extraField == ModificationTime ) 01101 extra_field_len = 17; // value also used in doneWriting() 01102 01103 int csize = device()->at() - 01104 d->m_currentFile->headerStart() - 30 - 01105 d->m_currentFile->path().length() - extra_field_len; 01106 d->m_currentFile->setCompressedSize(csize); 01107 //kdDebug(7040) << "usize: " << d->m_currentFile->size() << endl; 01108 //kdDebug(7040) << "csize: " << d->m_currentFile->compressedSize() << endl; 01109 //kdDebug(7040) << "headerstart: " << d->m_currentFile->headerStart() << endl; 01110 01111 //kdDebug(7040) << "crc: " << d->m_crc << endl; 01112 d->m_currentFile->setCRC32( d->m_crc ); 01113 01114 d->m_currentFile = 0L; 01115 01116 // update saved offset for appending new files 01117 d->m_offset = device()->at(); 01118 return true; 01119 } 01120 01121 bool KZip::writeSymLink(const QString &name, const QString &target, 01122 const QString &user, const QString &group, 01123 mode_t perm, time_t atime, time_t mtime, time_t ctime) { 01124 return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime); 01125 } 01126 01127 bool KZip::writeSymLink_impl(const QString &name, const QString &target, 01128 const QString &user, const QString &group, 01129 mode_t perm, time_t atime, time_t mtime, time_t ctime) { 01130 01131 // reassure that symlink flag is set, otherwise strange things happen on 01132 // extraction 01133 perm |= S_IFLNK; 01134 Compression c = compression(); 01135 setCompression(NoCompression); // link targets are never compressed 01136 01137 if (!prepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) { 01138 kdWarning() << "KZip::writeFile prepareWriting failed" << endl; 01139 setCompression(c); 01140 return false; 01141 } 01142 01143 QCString symlink_target = QFile::encodeName(target); 01144 if (!writeData(symlink_target, symlink_target.length())) { 01145 kdWarning() << "KZip::writeFile writeData failed" << endl; 01146 setCompression(c); 01147 return false; 01148 } 01149 01150 if (!doneWriting(symlink_target.length())) { 01151 kdWarning() << "KZip::writeFile doneWriting failed" << endl; 01152 setCompression(c); 01153 return false; 01154 } 01155 01156 setCompression(c); 01157 return true; 01158 } 01159 01160 void KZip::virtual_hook( int id, void* data ) 01161 { 01162 switch (id) { 01163 case VIRTUAL_WRITE_DATA: { 01164 WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data); 01165 params->retval = writeData_impl( params->data, params->size ); 01166 break; 01167 } 01168 case VIRTUAL_WRITE_SYMLINK: { 01169 WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data); 01170 params->retval = writeSymLink_impl(*params->name,*params->target, 01171 *params->user,*params->group,params->perm, 01172 params->atime,params->mtime,params->ctime); 01173 break; 01174 } 01175 case VIRTUAL_PREPARE_WRITING: { 01176 PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data); 01177 params->retval = prepareWriting_impl(*params->name,*params->user, 01178 *params->group,params->size,params->perm, 01179 params->atime,params->mtime,params->ctime); 01180 break; 01181 } 01182 default: 01183 KArchive::virtual_hook( id, data ); 01184 }/*end switch*/ 01185 } 01186 01187 // made virtual using virtual_hook 01188 bool KZip::writeData(const char * c, uint i) 01189 { 01190 return KArchive::writeData( c, i ); 01191 } 01192 01193 bool KZip::writeData_impl(const char * c, uint i) 01194 { 01195 Q_ASSERT( d->m_currentFile ); 01196 Q_ASSERT( d->m_currentDev ); 01197 if (!d->m_currentFile || !d->m_currentDev) 01198 return false; 01199 01200 // crc to be calculated over uncompressed stuff... 01201 // and they didn't mention it in their docs... 01202 d->m_crc = crc32(d->m_crc, (const Bytef *) c , i); 01203 01204 Q_LONG written = d->m_currentDev->writeBlock( c, i ); 01205 //kdDebug(7040) << "KZip::writeData wrote " << i << " bytes." << endl; 01206 return written == (Q_LONG)i; 01207 } 01208 01209 void KZip::setCompression( Compression c ) 01210 { 01211 d->m_compression = ( c == NoCompression ) ? 0 : 8; 01212 } 01213 01214 KZip::Compression KZip::compression() const 01215 { 01216 return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression; 01217 } 01218 01219 void KZip::setExtraField( ExtraField ef ) 01220 { 01221 d->m_extraField = ef; 01222 } 01223 01224 KZip::ExtraField KZip::extraField() const 01225 { 01226 return d->m_extraField; 01227 } 01228 01230 01231 QByteArray KZipFileEntry::data() const 01232 { 01233 QIODevice* dev = device(); 01234 QByteArray arr; 01235 if ( dev ) { 01236 arr = dev->readAll(); 01237 delete dev; 01238 } 01239 return arr; 01240 } 01241 01242 QIODevice* KZipFileEntry::device() const 01243 { 01244 //kdDebug(7040) << "KZipFileEntry::device creating iodevice limited to pos=" << position() << ", csize=" << compressedSize() << endl; 01245 // Limit the reading to the appropriate part of the underlying device (e.g. file) 01246 KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() ); 01247 if ( encoding() == 0 || compressedSize() == 0 ) // no compression (or even no data) 01248 return limitedDev; 01249 01250 if ( encoding() == 8 ) 01251 { 01252 // On top of that, create a device that uncompresses the zlib data 01253 QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" ); 01254 if ( !filterDev ) 01255 return 0L; // ouch 01256 static_cast<KFilterDev *>(filterDev)->setSkipHeaders(); // Just zlib, not gzip 01257 bool b = filterDev->open( IO_ReadOnly ); 01258 Q_ASSERT( b ); 01259 return filterDev; 01260 } 01261 01262 kdError() << "This zip file contains files compressed with method " 01263 << encoding() <<", this method is currently not supported by KZip," 01264 <<" please use a command-line tool to handle this file." << endl; 01265 return 0L; 01266 } 01267
KDE Logo
This file is part of the documentation for kio Library Version 3.3.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Oct 17 11:29:30 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003