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