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 QString name = decodeEntryName(bufferName); 00682 00683 //kdDebug(7040) << "name: " << name << endl; 00684 // only in central header ! see below. 00685 // length of extra attributes 00686 int extralen = (uchar)buffer[31] << 8 | (uchar)buffer[30]; 00687 // length of comment for this file 00688 int commlen = (uchar)buffer[33] << 8 | (uchar)buffer[32]; 00689 // compression method of this file 00690 int cmethod = (uchar)buffer[11] << 8 | (uchar)buffer[10]; 00691 00692 //kdDebug(7040) << "cmethod: " << cmethod << endl; 00693 //kdDebug(7040) << "extralen: " << extralen << endl; 00694 00695 // uncompressed file size 00696 uint ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 | 00697 (uchar)buffer[25] << 8 | (uchar)buffer[24]; 00698 // compressed file size 00699 uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 | 00700 (uchar)buffer[21] << 8 | (uchar)buffer[20]; 00701 00702 // offset of local header 00703 uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 | 00704 (uchar)buffer[43] << 8 | (uchar)buffer[42]; 00705 00706 // some clever people use different extra field lengths 00707 // in the central header and in the local header... funny. 00708 // so we need to get the localextralen to calculate the offset 00709 // from localheaderstart to dataoffset 00710 int localextralen = pfi->extralen; // FIXME: this will not work if 00711 // no local header exists 00712 00713 //kdDebug(7040) << "localextralen: " << localextralen << endl; 00714 00715 // offset, where the real data for uncompression starts 00716 uint dataoffset = localheaderoffset + 30 + localextralen + namelen; //comment only in central header 00717 00718 //kdDebug(7040) << "esize: " << esize << endl; 00719 //kdDebug(7040) << "eoffset: " << eoffset << endl; 00720 //kdDebug(7040) << "csize: " << csize << endl; 00721 00722 int os_madeby = (uchar)buffer[5]; 00723 bool isdir = false; 00724 int access = 0100644; 00725 00726 if (os_madeby == 3) { // good ole unix 00727 access = (uchar)buffer[40] | (uchar)buffer[41] << 8; 00728 } 00729 00730 QString entryName; 00731 00732 if ( name.endsWith( "/" ) ) // Entries with a trailing slash are directories 00733 { 00734 isdir = true; 00735 name = name.left( name.length() - 1 ); 00736 if (os_madeby != 3) access = S_IFDIR | 0755; 00737 else Q_ASSERT(access & S_IFDIR); 00738 } 00739 00740 int pos = name.findRev( '/' ); 00741 if ( pos == -1 ) 00742 entryName = name; 00743 else 00744 entryName = name.mid( pos + 1 ); 00745 Q_ASSERT( !entryName.isEmpty() ); 00746 00747 KArchiveEntry* entry; 00748 if ( isdir ) 00749 { 00750 QString path = QDir::cleanDirPath( name ); 00751 KArchiveEntry* ent = rootDir()->entry( path ); 00752 if ( ent && ent->isDirectory() ) 00753 { 00754 //kdDebug(7040) << "Directory already exists, NOT going to add it again" << endl; 00755 entry = 0L; 00756 } 00757 else 00758 { 00759 entry = new KArchiveDirectory( this, entryName, access, (int)pfi->mtime, rootDir()->user(), rootDir()->group(), QString::null ); 00760 //kdDebug(7040) << "KArchiveDirectory created, entryName= " << entryName << ", name=" << name << endl; 00761 } 00762 } 00763 else 00764 { 00765 QString symlink; 00766 if (S_ISLNK(access)) { 00767 //symlink = QFile::decodeName(pfi->guessed_symlink); 00768 symlink = decodeEntryName(pfi->guessed_symlink); 00769 } 00770 entry = new KZipFileEntry( this, entryName, access, pfi->mtime, 00771 rootDir()->user(), rootDir()->group(), 00772 symlink, name, dataoffset, 00773 ucsize, cmethod, csize ); 00774 static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset ); 00775 //kdDebug(7040) << "KZipFileEntry created, entryName= " << entryName << ", name=" << name << endl; 00776 d->m_fileList.append( static_cast<KZipFileEntry *>( entry ) ); 00777 } 00778 00779 if ( entry ) 00780 { 00781 if ( pos == -1 ) 00782 { 00783 rootDir()->addEntry(entry); 00784 } 00785 else 00786 { 00787 // In some tar files we can find dir/./file => call cleanDirPath 00788 QString path = QDir::cleanDirPath( name.left( pos ) ); 00789 // Ensure container directory exists, create otherwise 00790 KArchiveDirectory * tdir = findOrCreate( path ); 00791 tdir->addEntry(entry); 00792 } 00793 } 00794 00795 //calculate offset to next entry 00796 offset += 46 + commlen + extralen + namelen; 00797 bool b = dev->at(offset); 00798 Q_ASSERT( b ); 00799 if ( !b ) 00800 return false; 00801 } 00802 else if ( startOfFile ) 00803 { 00804 // The file does not start with any ZIP header (e.g. self-extractable ZIP files) 00805 // Therefore we need to find the first PK\003\004 (local header) 00806 kdDebug(7040) << "Try to skip start of file" << endl; 00807 startOfFile = false; 00808 bool foundSignature = false; 00809 00810 while (!foundSignature) 00811 { 00812 n = dev->readBlock( buffer, 1 ); 00813 if (n < 1) 00814 { 00815 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. " << k_funcinfo << endl; 00816 return false; 00817 } 00818 00819 if ( buffer[0] != 'P' ) 00820 continue; 00821 00822 n = dev->readBlock( buffer, 3 ); 00823 if (n < 3) 00824 { 00825 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. " << k_funcinfo << endl; 00826 return false; 00827 } 00828 00829 // We have to detect the magic token for a local header: PK\003\004 00830 /* 00831 * Note: we do not need to check the other magics, if the ZIP file has no 00832 * local header, then it has not any files! 00833 */ 00834 if ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) 00835 { 00836 foundSignature = true; 00837 dev->at( dev->at() - 4 ); // go back 4 bytes, so that the magic bytes can be found... 00838 } 00839 else if ( buffer[0] == 'P' || buffer[1] == 'P' || buffer[2] == 'P' ) 00840 { 00841 // We have another P character so we must go back a little to check if it is a magic 00842 dev->at( dev->at() - 3 ); 00843 } 00844 } 00845 } 00846 else 00847 { 00848 kdWarning(7040) << "Invalid ZIP file. Unrecognized header at offset " << offset << endl; 00849 00850 return false; 00851 } 00852 } 00853 //kdDebug(7040) << "*** done *** " << endl; 00854 return true; 00855 } 00856 00857 bool KZip::closeArchive() 00858 { 00859 if ( ! ( mode() & IO_WriteOnly ) ) 00860 { 00861 //kdDebug(7040) << "closearchive readonly reached." << endl; 00862 return true; 00863 } 00864 00865 kdDebug() << k_funcinfo << "device=" << device() << endl; 00866 //ReadWrite or WriteOnly 00867 //write all central dir file entries 00868 00869 if ( !device() ) // saving aborted 00870 return false; 00871 00872 // to be written at the end of the file... 00873 char buffer[ 22 ]; // first used for 12, then for 22 at the end 00874 uLong crc = crc32(0L, Z_NULL, 0); 00875 00876 Q_LONG centraldiroffset = device()->at(); 00877 //kdDebug(7040) << "closearchive: centraldiroffset: " << centraldiroffset << endl; 00878 Q_LONG atbackup = centraldiroffset; 00879 QPtrListIterator<KZipFileEntry> it( d->m_fileList ); 00880 00881 for ( ; it.current() ; ++it ) 00882 { //set crc and compressed size in each local file header 00883 if ( !device()->at( it.current()->headerStart() + 14 ) ) 00884 return false; 00885 //kdDebug(7040) << "closearchive setcrcandcsize: filename: " 00886 // << it.current()->path() 00887 // << " encoding: "<< it.current()->encoding() << endl; 00888 00889 uLong mycrc = it.current()->crc32(); 00890 buffer[0] = char(mycrc); // crc checksum, at headerStart+14 00891 buffer[1] = char(mycrc >> 8); 00892 buffer[2] = char(mycrc >> 16); 00893 buffer[3] = char(mycrc >> 24); 00894 00895 int mysize1 = it.current()->compressedSize(); 00896 buffer[4] = char(mysize1); // compressed file size, at headerStart+18 00897 buffer[5] = char(mysize1 >> 8); 00898 buffer[6] = char(mysize1 >> 16); 00899 buffer[7] = char(mysize1 >> 24); 00900 00901 int myusize = it.current()->size(); 00902 buffer[8] = char(myusize); // uncompressed file size, at headerStart+22 00903 buffer[9] = char(myusize >> 8); 00904 buffer[10] = char(myusize >> 16); 00905 buffer[11] = char(myusize >> 24); 00906 00907 if ( device()->writeBlock( buffer, 12 ) != 12 ) 00908 return false; 00909 } 00910 device()->at( atbackup ); 00911 00912 for ( it.toFirst(); it.current() ; ++it ) 00913 { 00914 //kdDebug(7040) << "closearchive: filename: " << it.current()->path() 00915 // << " encoding: "<< it.current()->encoding() << endl; 00916 00917 QCString path = QFile::encodeName(it.current()->path()); 00918 00919 const int extra_field_len = 9; 00920 int bufferSize = extra_field_len + path.length() + 46; 00921 char* buffer = new char[ bufferSize ]; 00922 00923 memset(buffer, 0, 46); // zero is a nice default for most header fields 00924 00925 const char head[] = 00926 { 00927 'P', 'K', 1, 2, // central file header signature 00928 0x14, 3, // version made by (3 == UNIX) 00929 0x14, 0 // version needed to extract 00930 }; 00931 00932 // I do not know why memcpy is not working here 00933 //memcpy(buffer, head, sizeof(head)); 00934 qmemmove(buffer, head, sizeof(head)); 00935 00936 buffer[ 10 ] = char(it.current()->encoding()); // compression method 00937 buffer[ 11 ] = char(it.current()->encoding() >> 8); 00938 00939 transformToMsDos( it.current()->datetime(), &buffer[ 12 ] ); 00940 00941 uLong mycrc = it.current()->crc32(); 00942 buffer[ 16 ] = char(mycrc); // crc checksum 00943 buffer[ 17 ] = char(mycrc >> 8); 00944 buffer[ 18 ] = char(mycrc >> 16); 00945 buffer[ 19 ] = char(mycrc >> 24); 00946 00947 int mysize1 = it.current()->compressedSize(); 00948 buffer[ 20 ] = char(mysize1); // compressed file size 00949 buffer[ 21 ] = char(mysize1 >> 8); 00950 buffer[ 22 ] = char(mysize1 >> 16); 00951 buffer[ 23 ] = char(mysize1 >> 24); 00952 00953 int mysize = it.current()->size(); 00954 buffer[ 24 ] = char(mysize); // uncompressed file size 00955 buffer[ 25 ] = char(mysize >> 8); 00956 buffer[ 26 ] = char(mysize >> 16); 00957 buffer[ 27 ] = char(mysize >> 24); 00958 00959 buffer[ 28 ] = char(it.current()->path().length()); // filename length 00960 buffer[ 29 ] = char(it.current()->path().length() >> 8); 00961 00962 buffer[ 30 ] = char(extra_field_len); 00963 buffer[ 31 ] = char(extra_field_len >> 8); 00964 00965 buffer[ 40 ] = char(it.current()->permissions()); 00966 buffer[ 41 ] = char(it.current()->permissions() >> 8); 00967 00968 int myhst = it.current()->headerStart(); 00969 buffer[ 42 ] = char(myhst); //relative offset of local header 00970 buffer[ 43 ] = char(myhst >> 8); 00971 buffer[ 44 ] = char(myhst >> 16); 00972 buffer[ 45 ] = char(myhst >> 24); 00973 00974 // file name 00975 strncpy( buffer + 46, path, path.length() ); 00976 //kdDebug(7040) << "closearchive length to write: " << bufferSize << endl; 00977 00978 // extra field 00979 char *extfield = buffer + 46 + path.length(); 00980 extfield[0] = 'U'; 00981 extfield[1] = 'T'; 00982 extfield[2] = 5; 00983 extfield[3] = 0; 00984 extfield[4] = 1 | 2 | 4; // specify flags from local field 00985 // (unless I misread the spec) 00986 // provide only modification time 00987 unsigned long time = (unsigned long)it.current()->date(); 00988 extfield[5] = char(time); 00989 extfield[6] = char(time >> 8); 00990 extfield[7] = char(time >> 16); 00991 extfield[8] = char(time >> 24); 00992 00993 crc = crc32(crc, (Bytef *)buffer, bufferSize ); 00994 bool ok = ( device()->writeBlock( buffer, bufferSize ) == bufferSize ); 00995 delete[] buffer; 00996 if ( !ok ) 00997 return false; 00998 } 00999 Q_LONG centraldirendoffset = device()->at(); 01000 //kdDebug(7040) << "closearchive: centraldirendoffset: " << centraldirendoffset << endl; 01001 //kdDebug(7040) << "closearchive: device()->at(): " << device()->at() << endl; 01002 01003 //write end of central dir record. 01004 buffer[ 0 ] = 'P'; //end of central dir signature 01005 buffer[ 1 ] = 'K'; 01006 buffer[ 2 ] = 5; 01007 buffer[ 3 ] = 6; 01008 01009 buffer[ 4 ] = 0; // number of this disk 01010 buffer[ 5 ] = 0; 01011 01012 buffer[ 6 ] = 0; // number of disk with start of central dir 01013 buffer[ 7 ] = 0; 01014 01015 int count = d->m_fileList.count(); 01016 //kdDebug(7040) << "number of files (count): " << count << endl; 01017 01018 01019 buffer[ 8 ] = char(count); // total number of entries in central dir of 01020 buffer[ 9 ] = char(count >> 8); // this disk 01021 01022 buffer[ 10 ] = buffer[ 8 ]; // total number of entries in the central dir 01023 buffer[ 11 ] = buffer[ 9 ]; 01024 01025 int cdsize = centraldirendoffset - centraldiroffset; 01026 buffer[ 12 ] = char(cdsize); // size of the central dir 01027 buffer[ 13 ] = char(cdsize >> 8); 01028 buffer[ 14 ] = char(cdsize >> 16); 01029 buffer[ 15 ] = char(cdsize >> 24); 01030 01031 //kdDebug(7040) << "end : centraldiroffset: " << centraldiroffset << endl; 01032 //kdDebug(7040) << "end : centraldirsize: " << cdsize << endl; 01033 01034 buffer[ 16 ] = char(centraldiroffset); // central dir offset 01035 buffer[ 17 ] = char(centraldiroffset >> 8); 01036 buffer[ 18 ] = char(centraldiroffset >> 16); 01037 buffer[ 19 ] = char(centraldiroffset >> 24); 01038 01039 buffer[ 20 ] = 0; //zipfile comment length 01040 buffer[ 21 ] = 0; 01041 01042 if ( device()->writeBlock( buffer, 22 ) != 22 ) 01043 return false; 01044 01045 if ( d->m_saveFile ) { 01046 d->m_saveFile->close(); 01047 setDevice( 0 ); 01048 delete d->m_saveFile; 01049 d->m_saveFile = 0; 01050 } 01051 01052 //kdDebug(7040) << __FILE__" reached." << endl; 01053 return true; 01054 } 01055 01056 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0 01057 bool KZip::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data ) 01058 { 01059 mode_t mode = 0100644; 01060 time_t the_time = time(0); 01061 return KArchive::writeFile( name, user, group, size, mode, the_time, 01062 the_time, the_time, data ); 01063 } 01064 01065 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0 01066 bool KZip::writeFile( const QString& name, const QString& user, 01067 const QString& group, uint size, mode_t perm, 01068 time_t atime, time_t mtime, time_t ctime, 01069 const char* data ) { 01070 return KArchive::writeFile(name, user, group, size, perm, atime, mtime, 01071 ctime, data); 01072 } 01073 01074 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0 01075 bool KZip::prepareWriting( const QString& name, const QString& user, const QString& group, uint size ) 01076 { 01077 mode_t dflt_perm = 0100644; 01078 time_t the_time = time(0); 01079 return prepareWriting(name,user,group,size,dflt_perm, 01080 the_time,the_time,the_time); 01081 } 01082 01083 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0 01084 bool KZip::prepareWriting(const QString& name, const QString& user, 01085 const QString& group, uint size, mode_t perm, 01086 time_t atime, time_t mtime, time_t ctime) { 01087 return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime); 01088 } 01089 01090 bool KZip::prepareWriting_impl(const QString &name, const QString &user, 01091 const QString &group, uint /*size*/, mode_t perm, 01092 time_t atime, time_t mtime, time_t ctime) { 01093 //kdDebug(7040) << "prepareWriting reached." << endl; 01094 if ( !isOpened() ) 01095 { 01096 qWarning( "KZip::writeFile: You must open the zip file before writing to it\n"); 01097 return false; 01098 } 01099 01100 if ( ! ( mode() & IO_WriteOnly ) ) // accept WriteOnly and ReadWrite 01101 { 01102 qWarning( "KZip::writeFile: You must open the zip file for writing\n"); 01103 return false; 01104 } 01105 01106 if ( !device() ) { // aborted 01107 //kdWarning(7040) << "prepareWriting_impl: no device" << endl; 01108 return false; 01109 } 01110 01111 // set right offset in zip. 01112 if ( !device()->at( d->m_offset ) ) { 01113 kdWarning(7040) << "prepareWriting_impl: cannot seek in ZIP file. Disk full?" << endl; 01114 abort(); 01115 return false; 01116 } 01117 01118 // delete entries in the filelist with the same filename as the one we want 01119 // to save, so that we donīt have duplicate file entries when viewing the zip 01120 // with konqi... 01121 // CAUTION: the old file itself is still in the zip and won't be removed !!! 01122 QPtrListIterator<KZipFileEntry> it( d->m_fileList ); 01123 01124 //kdDebug(7040) << "filename to write: " << name <<endl; 01125 for ( ; it.current() ; ++it ) 01126 { 01127 //kdDebug(7040) << "prepfilename: " << it.current()->path() <<endl; 01128 if (name == it.current()->path() ) 01129 { 01130 //kdDebug(7040) << "removing following entry: " << it.current()->path() <<endl; 01131 d->m_fileList.remove(); 01132 } 01133 01134 } 01135 // Find or create parent dir 01136 KArchiveDirectory* parentDir = rootDir(); 01137 QString fileName( name ); 01138 int i = name.findRev( '/' ); 01139 if ( i != -1 ) 01140 { 01141 QString dir = name.left( i ); 01142 fileName = name.mid( i + 1 ); 01143 //kdDebug(7040) << "KZip::prepareWriting ensuring " << dir << " exists. fileName=" << fileName << endl; 01144 parentDir = findOrCreate( dir ); 01145 } 01146 01147 // construct a KZipFileEntry and add it to list 01148 KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, QString::null, 01149 name, device()->at() + 30 + name.length(), // start 01150 0 /*size unknown yet*/, d->m_compression, 0 /*csize unknown yet*/ ); 01151 e->setHeaderStart( device()->at() ); 01152 //kdDebug(7040) << "wrote file start: " << e->position() << " name: " << name << endl; 01153 parentDir->addEntry( e ); 01154 01155 d->m_currentFile = e; 01156 d->m_fileList.append( e ); 01157 01158 int extra_field_len = 0; 01159 if ( d->m_extraField == ModificationTime ) 01160 extra_field_len = 17; // value also used in doneWriting() 01161 01162 // write out zip header 01163 QCString encodedName = QFile::encodeName(name); 01164 int bufferSize = extra_field_len + encodedName.length() + 30; 01165 //kdDebug(7040) << "KZip::prepareWriting bufferSize=" << bufferSize << endl; 01166 char* buffer = new char[ bufferSize ]; 01167 01168 buffer[ 0 ] = 'P'; //local file header signature 01169 buffer[ 1 ] = 'K'; 01170 buffer[ 2 ] = 3; 01171 buffer[ 3 ] = 4; 01172 01173 buffer[ 4 ] = 0x14; // version needed to extract 01174 buffer[ 5 ] = 0; 01175 01176 buffer[ 6 ] = 0; // general purpose bit flag 01177 buffer[ 7 ] = 0; 01178 01179 buffer[ 8 ] = char(e->encoding()); // compression method 01180 buffer[ 9 ] = char(e->encoding() >> 8); 01181 01182 transformToMsDos( e->datetime(), &buffer[ 10 ] ); 01183 01184 buffer[ 14 ] = 'C'; //dummy crc 01185 buffer[ 15 ] = 'R'; 01186 buffer[ 16 ] = 'C'; 01187 buffer[ 17 ] = 'q'; 01188 01189 buffer[ 18 ] = 'C'; //compressed file size 01190 buffer[ 19 ] = 'S'; 01191 buffer[ 20 ] = 'I'; 01192 buffer[ 21 ] = 'Z'; 01193 01194 buffer[ 22 ] = 'U'; //uncompressed file size 01195 buffer[ 23 ] = 'S'; 01196 buffer[ 24 ] = 'I'; 01197 buffer[ 25 ] = 'Z'; 01198 01199 buffer[ 26 ] = (uchar)(encodedName.length()); //filename length 01200 buffer[ 27 ] = (uchar)(encodedName.length() >> 8); 01201 01202 buffer[ 28 ] = (uchar)(extra_field_len); // extra field length 01203 buffer[ 29 ] = (uchar)(extra_field_len >> 8); 01204 01205 // file name 01206 strncpy( buffer + 30, encodedName, encodedName.length() ); 01207 01208 // extra field 01209 if ( d->m_extraField == ModificationTime ) 01210 { 01211 char *extfield = buffer + 30 + encodedName.length(); 01212 // "Extended timestamp" header (0x5455) 01213 extfield[0] = 'U'; 01214 extfield[1] = 'T'; 01215 extfield[2] = 13; // data size 01216 extfield[3] = 0; 01217 extfield[4] = 1 | 2 | 4; // contains mtime, atime, ctime 01218 01219 extfield[5] = char(mtime); 01220 extfield[6] = char(mtime >> 8); 01221 extfield[7] = char(mtime >> 16); 01222 extfield[8] = char(mtime >> 24); 01223 01224 extfield[9] = char(atime); 01225 extfield[10] = char(atime >> 8); 01226 extfield[11] = char(atime >> 16); 01227 extfield[12] = char(atime >> 24); 01228 01229 extfield[13] = char(ctime); 01230 extfield[14] = char(ctime >> 8); 01231 extfield[15] = char(ctime >> 16); 01232 extfield[16] = char(ctime >> 24); 01233 } 01234 01235 // Write header 01236 bool b = (device()->writeBlock( buffer, bufferSize ) == bufferSize ); 01237 d->m_crc = 0L; 01238 delete[] buffer; 01239 01240 Q_ASSERT( b ); 01241 if (!b) { 01242 abort(); 01243 return false; 01244 } 01245 01246 // Prepare device for writing the data 01247 // Either device() if no compression, or a KFilterDev to compress 01248 if ( d->m_compression == 0 ) { 01249 d->m_currentDev = device(); 01250 return true; 01251 } 01252 01253 d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false ); 01254 Q_ASSERT( d->m_currentDev ); 01255 if ( !d->m_currentDev ) { 01256 abort(); 01257 return false; // ouch 01258 } 01259 static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders(); // Just zlib, not gzip 01260 01261 b = d->m_currentDev->open( IO_WriteOnly ); 01262 Q_ASSERT( b ); 01263 return b; 01264 } 01265 01266 bool KZip::doneWriting( uint size ) 01267 { 01268 if ( d->m_currentFile->encoding() == 8 ) { 01269 // Finish 01270 (void)d->m_currentDev->writeBlock( 0, 0 ); 01271 delete d->m_currentDev; 01272 } 01273 // If 0, d->m_currentDev was device() - don't delete ;) 01274 d->m_currentDev = 0L; 01275 01276 Q_ASSERT( d->m_currentFile ); 01277 //kdDebug(7040) << "donewriting reached." << endl; 01278 //kdDebug(7040) << "filename: " << d->m_currentFile->path() << endl; 01279 //kdDebug(7040) << "getpos (at): " << device()->at() << endl; 01280 d->m_currentFile->setSize(size); 01281 int extra_field_len = 0; 01282 if ( d->m_extraField == ModificationTime ) 01283 extra_field_len = 17; // value also used in doneWriting() 01284 01285 int csize = device()->at() - 01286 d->m_currentFile->headerStart() - 30 - 01287 d->m_currentFile->path().length() - extra_field_len; 01288 d->m_currentFile->setCompressedSize(csize); 01289 //kdDebug(7040) << "usize: " << d->m_currentFile->size() << endl; 01290 //kdDebug(7040) << "csize: " << d->m_currentFile->compressedSize() << endl; 01291 //kdDebug(7040) << "headerstart: " << d->m_currentFile->headerStart() << endl; 01292 01293 //kdDebug(7040) << "crc: " << d->m_crc << endl; 01294 d->m_currentFile->setCRC32( d->m_crc ); 01295 01296 d->m_currentFile = 0L; 01297 01298 // update saved offset for appending new files 01299 d->m_offset = device()->at(); 01300 return true; 01301 } 01302 01303 bool KZip::writeSymLink(const QString &name, const QString &target, 01304 const QString &user, const QString &group, 01305 mode_t perm, time_t atime, time_t mtime, time_t ctime) { 01306 return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime); 01307 } 01308 01309 bool KZip::writeSymLink_impl(const QString &name, const QString &target, 01310 const QString &user, const QString &group, 01311 mode_t perm, time_t atime, time_t mtime, time_t ctime) { 01312 01313 // reassure that symlink flag is set, otherwise strange things happen on 01314 // extraction 01315 perm |= S_IFLNK; 01316 Compression c = compression(); 01317 setCompression(NoCompression); // link targets are never compressed 01318 01319 if (!prepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) { 01320 kdWarning() << "KZip::writeFile prepareWriting failed" << endl; 01321 setCompression(c); 01322 return false; 01323 } 01324 01325 QCString symlink_target = QFile::encodeName(target); 01326 if (!writeData(symlink_target, symlink_target.length())) { 01327 kdWarning() << "KZip::writeFile writeData failed" << endl; 01328 setCompression(c); 01329 return false; 01330 } 01331 01332 if (!doneWriting(symlink_target.length())) { 01333 kdWarning() << "KZip::writeFile doneWriting failed" << endl; 01334 setCompression(c); 01335 return false; 01336 } 01337 01338 setCompression(c); 01339 return true; 01340 } 01341 01342 void KZip::virtual_hook( int id, void* data ) 01343 { 01344 switch (id) { 01345 case VIRTUAL_WRITE_DATA: { 01346 WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data); 01347 params->retval = writeData_impl( params->data, params->size ); 01348 break; 01349 } 01350 case VIRTUAL_WRITE_SYMLINK: { 01351 WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data); 01352 params->retval = writeSymLink_impl(*params->name,*params->target, 01353 *params->user,*params->group,params->perm, 01354 params->atime,params->mtime,params->ctime); 01355 break; 01356 } 01357 case VIRTUAL_PREPARE_WRITING: { 01358 PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data); 01359 params->retval = prepareWriting_impl(*params->name,*params->user, 01360 *params->group,params->size,params->perm, 01361 params->atime,params->mtime,params->ctime); 01362 break; 01363 } 01364 default: 01365 KArchive::virtual_hook( id, data ); 01366 }/*end switch*/ 01367 } 01368 01369 // made virtual using virtual_hook 01370 bool KZip::writeData(const char * c, uint i) 01371 { 01372 return KArchive::writeData( c, i ); 01373 } 01374 01375 bool KZip::writeData_impl(const char * c, uint i) 01376 { 01377 Q_ASSERT( d->m_currentFile ); 01378 Q_ASSERT( d->m_currentDev ); 01379 if (!d->m_currentFile || !d->m_currentDev) { 01380 abort(); 01381 return false; 01382 } 01383 01384 // crc to be calculated over uncompressed stuff... 01385 // and they didn't mention it in their docs... 01386 d->m_crc = crc32(d->m_crc, (const Bytef *) c , i); 01387 01388 Q_LONG written = d->m_currentDev->writeBlock( c, i ); 01389 //kdDebug(7040) << "KZip::writeData wrote " << i << " bytes." << endl; 01390 bool ok = written == (Q_LONG)i; 01391 if ( !ok ) 01392 abort(); 01393 return ok; 01394 } 01395 01396 void KZip::setCompression( Compression c ) 01397 { 01398 d->m_compression = ( c == NoCompression ) ? 0 : 8; 01399 } 01400 01401 KZip::Compression KZip::compression() const 01402 { 01403 return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression; 01404 } 01405 01406 void KZip::setExtraField( ExtraField ef ) 01407 { 01408 d->m_extraField = ef; 01409 } 01410 01411 KZip::ExtraField KZip::extraField() const 01412 { 01413 return d->m_extraField; 01414 } 01415 01416 void KZip::abort() 01417 { 01418 if ( d->m_saveFile ) { 01419 d->m_saveFile->abort(); 01420 setDevice( 0 ); 01421 } 01422 } 01423 01424 01426 01427 QByteArray KZipFileEntry::data() const 01428 { 01429 QIODevice* dev = device(); 01430 QByteArray arr; 01431 if ( dev ) { 01432 arr = dev->readAll(); 01433 delete dev; 01434 } 01435 return arr; 01436 } 01437 01438 QIODevice* KZipFileEntry::device() const 01439 { 01440 //kdDebug(7040) << "KZipFileEntry::device creating iodevice limited to pos=" << position() << ", csize=" << compressedSize() << endl; 01441 // Limit the reading to the appropriate part of the underlying device (e.g. file) 01442 KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() ); 01443 if ( encoding() == 0 || compressedSize() == 0 ) // no compression (or even no data) 01444 return limitedDev; 01445 01446 if ( encoding() == 8 ) 01447 { 01448 // On top of that, create a device that uncompresses the zlib data 01449 QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" ); 01450 if ( !filterDev ) 01451 return 0L; // ouch 01452 static_cast<KFilterDev *>(filterDev)->setSkipHeaders(); // Just zlib, not gzip 01453 bool b = filterDev->open( IO_ReadOnly ); 01454 Q_ASSERT( b ); 01455 return filterDev; 01456 } 01457 01458 kdError() << "This zip file contains files compressed with method " 01459 << encoding() <<", this method is currently not supported by KZip," 01460 <<" please use a command-line tool to handle this file." << endl; 01461 return 0L; 01462 }