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