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 <tqasciidict.h> 00048 #include <tqfile.h> 00049 #include <tqdir.h> 00050 #include <tqdatetime.h> 00051 #include <tqptrlist.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 TQDateTime& dt, char* buffer) 00060 { 00061 if ( dt.isValid() ) 00062 { 00063 const TQ_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 TQ_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 TQ_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 TQTime qt(h, m, s); 00095 00096 TQ_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 TQDate qd(y, o, d); 00101 00102 TQDateTime 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 // TQCString 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 TQCString 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 TQIODevice* m_currentDev; // filterdev used to write to the above file 00325 TQPtrList<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 TQString& 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( TQIODevice * dev ) 00348 : KArchive( dev ) 00349 { 00350 //kdDebug(7040) << "KZip::KZip( TQIODevice * 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 TQFile) 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( TQT_TQIODEVICE(d->m_saveFile->file()) ); 00389 } 00390 return true; 00391 case IO_ReadOnly: 00392 case IO_ReadWrite: 00393 { 00394 // ReadWrite mode still uses TQFile for now; we'd need to copy to the tempfile, in fact. 00395 if ( !m_filename.isEmpty() ) { 00396 setDevice( TQT_TQIODEVICE(new TQFile( 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 TQIODevice* 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 TQAsciiDict<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 TQ_LONG compr_size = (uchar)buffer[12] | (uchar)buffer[13] << 8 00466 | (uchar)buffer[14] << 16 | (uchar)buffer[15] << 24; 00467 TQ_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 TQCString 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 > (TQ_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 TQCString 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 TQString name( TQFile::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 TQString 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 TQString path = TQDir::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(), TQString::null ); 00759 //kdDebug(7040) << "KArchiveDirectory created, entryName= " << entryName << ", name=" << name << endl; 00760 } 00761 } 00762 else 00763 { 00764 TQString symlink; 00765 if (S_ISLNK(access)) { 00766 symlink = TQFile::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 TQString path = TQDir::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 TQ_LONG centraldiroffset = device()->at(); 00875 //kdDebug(7040) << "closearchive: centraldiroffset: " << centraldiroffset << endl; 00876 TQ_LONG atbackup = centraldiroffset; 00877 TQPtrListIterator<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 TQCString path = TQFile::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 tqmemmove(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 TQ_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 TQString& name, const TQString& user, const TQString& 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 TQString dirName = name; 01060 if (!name.endsWith("/")) { 01061 dirName = dirName.append('/'); 01062 } 01063 01064 mode_t perm = 040755; 01065 time_t the_time = time(0); 01066 return writeFile(dirName, user, group, 0, perm, the_time, the_time, the_time, 0); 01067 } 01068 01069 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0 01070 bool KZip::writeFile( const TQString& name, const TQString& user, const TQString& group, uint size, const char* data ) 01071 { 01072 mode_t mode = 0100644; 01073 time_t the_time = time(0); 01074 return KArchive::writeFile( name, user, group, size, mode, the_time, 01075 the_time, the_time, data ); 01076 } 01077 01078 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0 01079 bool KZip::writeFile( const TQString& name, const TQString& user, 01080 const TQString& group, uint size, mode_t perm, 01081 time_t atime, time_t mtime, time_t ctime, 01082 const char* data ) { 01083 return KArchive::writeFile(name, user, group, size, perm, atime, mtime, 01084 ctime, data); 01085 } 01086 01087 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0 01088 bool KZip::prepareWriting( const TQString& name, const TQString& user, const TQString& group, uint size ) 01089 { 01090 mode_t dflt_perm = 0100644; 01091 time_t the_time = time(0); 01092 return prepareWriting(name,user,group,size,dflt_perm, 01093 the_time,the_time,the_time); 01094 } 01095 01096 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0 01097 bool KZip::prepareWriting(const TQString& name, const TQString& user, 01098 const TQString& group, uint size, mode_t perm, 01099 time_t atime, time_t mtime, time_t ctime) { 01100 return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime); 01101 } 01102 01103 bool KZip::prepareWriting_impl(const TQString &name, const TQString &user, 01104 const TQString &group, uint /*size*/, mode_t perm, 01105 time_t atime, time_t mtime, time_t ctime) { 01106 //kdDebug(7040) << "prepareWriting reached." << endl; 01107 if ( !isOpened() ) 01108 { 01109 qWarning( "KZip::writeFile: You must open the zip file before writing to it\n"); 01110 return false; 01111 } 01112 01113 if ( ! ( mode() & IO_WriteOnly ) ) // accept WriteOnly and ReadWrite 01114 { 01115 qWarning( "KZip::writeFile: You must open the zip file for writing\n"); 01116 return false; 01117 } 01118 01119 if ( !device() ) { // aborted 01120 //kdWarning(7040) << "prepareWriting_impl: no device" << endl; 01121 return false; 01122 } 01123 01124 // set right offset in zip. 01125 if ( !device()->at( d->m_offset ) ) { 01126 kdWarning(7040) << "prepareWriting_impl: cannot seek in ZIP file. Disk full?" << endl; 01127 abort(); 01128 return false; 01129 } 01130 01131 // delete entries in the filelist with the same filename as the one we want 01132 // to save, so that we don�t have duplicate file entries when viewing the zip 01133 // with konqi... 01134 // CAUTION: the old file itself is still in the zip and won't be removed !!! 01135 TQPtrListIterator<KZipFileEntry> it( d->m_fileList ); 01136 01137 //kdDebug(7040) << "filename to write: " << name <<endl; 01138 for ( ; it.current() ; ++it ) 01139 { 01140 //kdDebug(7040) << "prepfilename: " << it.current()->path() <<endl; 01141 if (name == it.current()->path() ) 01142 { 01143 //kdDebug(7040) << "removing following entry: " << it.current()->path() <<endl; 01144 d->m_fileList.remove(); 01145 } 01146 01147 } 01148 // Find or create parent dir 01149 KArchiveDirectory* parentDir = rootDir(); 01150 TQString fileName( name ); 01151 int i = name.findRev( '/' ); 01152 if ( i != -1 ) 01153 { 01154 TQString dir = name.left( i ); 01155 fileName = name.mid( i + 1 ); 01156 //kdDebug(7040) << "KZip::prepareWriting ensuring " << dir << " exists. fileName=" << fileName << endl; 01157 parentDir = findOrCreate( dir ); 01158 } 01159 01160 // construct a KZipFileEntry and add it to list 01161 KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, TQString::null, 01162 name, device()->at() + 30 + name.length(), // start 01163 0 /*size unknown yet*/, d->m_compression, 0 /*csize unknown yet*/ ); 01164 e->setHeaderStart( device()->at() ); 01165 //kdDebug(7040) << "wrote file start: " << e->position() << " name: " << name << endl; 01166 parentDir->addEntry( e ); 01167 01168 d->m_currentFile = e; 01169 d->m_fileList.append( e ); 01170 01171 int extra_field_len = 0; 01172 if ( d->m_extraField == ModificationTime ) 01173 extra_field_len = 17; // value also used in doneWriting() 01174 01175 // write out zip header 01176 TQCString encodedName = TQFile::encodeName(name); 01177 int bufferSize = extra_field_len + encodedName.length() + 30; 01178 //kdDebug(7040) << "KZip::prepareWriting bufferSize=" << bufferSize << endl; 01179 char* buffer = new char[ bufferSize ]; 01180 01181 buffer[ 0 ] = 'P'; //local file header signature 01182 buffer[ 1 ] = 'K'; 01183 buffer[ 2 ] = 3; 01184 buffer[ 3 ] = 4; 01185 01186 buffer[ 4 ] = 0x14; // version needed to extract 01187 buffer[ 5 ] = 0; 01188 01189 buffer[ 6 ] = 0; // general purpose bit flag 01190 buffer[ 7 ] = 0; 01191 01192 buffer[ 8 ] = char(e->encoding()); // compression method 01193 buffer[ 9 ] = char(e->encoding() >> 8); 01194 01195 transformToMsDos( e->datetime(), &buffer[ 10 ] ); 01196 01197 buffer[ 14 ] = 'C'; //dummy crc 01198 buffer[ 15 ] = 'R'; 01199 buffer[ 16 ] = 'C'; 01200 buffer[ 17 ] = 'q'; 01201 01202 buffer[ 18 ] = 'C'; //compressed file size 01203 buffer[ 19 ] = 'S'; 01204 buffer[ 20 ] = 'I'; 01205 buffer[ 21 ] = 'Z'; 01206 01207 buffer[ 22 ] = 'U'; //uncompressed file size 01208 buffer[ 23 ] = 'S'; 01209 buffer[ 24 ] = 'I'; 01210 buffer[ 25 ] = 'Z'; 01211 01212 buffer[ 26 ] = (uchar)(encodedName.length()); //filename length 01213 buffer[ 27 ] = (uchar)(encodedName.length() >> 8); 01214 01215 buffer[ 28 ] = (uchar)(extra_field_len); // extra field length 01216 buffer[ 29 ] = (uchar)(extra_field_len >> 8); 01217 01218 // file name 01219 strncpy( buffer + 30, encodedName, encodedName.length() ); 01220 01221 // extra field 01222 if ( d->m_extraField == ModificationTime ) 01223 { 01224 char *extfield = buffer + 30 + encodedName.length(); 01225 // "Extended timestamp" header (0x5455) 01226 extfield[0] = 'U'; 01227 extfield[1] = 'T'; 01228 extfield[2] = 13; // data size 01229 extfield[3] = 0; 01230 extfield[4] = 1 | 2 | 4; // contains mtime, atime, ctime 01231 01232 extfield[5] = char(mtime); 01233 extfield[6] = char(mtime >> 8); 01234 extfield[7] = char(mtime >> 16); 01235 extfield[8] = char(mtime >> 24); 01236 01237 extfield[9] = char(atime); 01238 extfield[10] = char(atime >> 8); 01239 extfield[11] = char(atime >> 16); 01240 extfield[12] = char(atime >> 24); 01241 01242 extfield[13] = char(ctime); 01243 extfield[14] = char(ctime >> 8); 01244 extfield[15] = char(ctime >> 16); 01245 extfield[16] = char(ctime >> 24); 01246 } 01247 01248 // Write header 01249 bool b = (device()->writeBlock( buffer, bufferSize ) == bufferSize ); 01250 d->m_crc = 0L; 01251 delete[] buffer; 01252 01253 Q_ASSERT( b ); 01254 if (!b) { 01255 abort(); 01256 return false; 01257 } 01258 01259 // Prepare device for writing the data 01260 // Either device() if no compression, or a KFilterDev to compress 01261 if ( d->m_compression == 0 ) { 01262 d->m_currentDev = device(); 01263 return true; 01264 } 01265 01266 d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false ); 01267 Q_ASSERT( d->m_currentDev ); 01268 if ( !d->m_currentDev ) { 01269 abort(); 01270 return false; // ouch 01271 } 01272 static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders(); // Just zlib, not gzip 01273 01274 b = d->m_currentDev->open( IO_WriteOnly ); 01275 Q_ASSERT( b ); 01276 return b; 01277 } 01278 01279 bool KZip::doneWriting( uint size ) 01280 { 01281 if ( d->m_currentFile->encoding() == 8 ) { 01282 // Finish 01283 (void)d->m_currentDev->writeBlock( 0, 0 ); 01284 delete d->m_currentDev; 01285 } 01286 // If 0, d->m_currentDev was device() - don't delete ;) 01287 d->m_currentDev = 0L; 01288 01289 Q_ASSERT( d->m_currentFile ); 01290 //kdDebug(7040) << "donewriting reached." << endl; 01291 //kdDebug(7040) << "filename: " << d->m_currentFile->path() << endl; 01292 //kdDebug(7040) << "getpos (at): " << device()->at() << endl; 01293 d->m_currentFile->setSize(size); 01294 int extra_field_len = 0; 01295 if ( d->m_extraField == ModificationTime ) 01296 extra_field_len = 17; // value also used in doneWriting() 01297 01298 int csize = device()->at() - 01299 d->m_currentFile->headerStart() - 30 - 01300 d->m_currentFile->path().length() - extra_field_len; 01301 d->m_currentFile->setCompressedSize(csize); 01302 //kdDebug(7040) << "usize: " << d->m_currentFile->size() << endl; 01303 //kdDebug(7040) << "csize: " << d->m_currentFile->compressedSize() << endl; 01304 //kdDebug(7040) << "headerstart: " << d->m_currentFile->headerStart() << endl; 01305 01306 //kdDebug(7040) << "crc: " << d->m_crc << endl; 01307 d->m_currentFile->setCRC32( d->m_crc ); 01308 01309 d->m_currentFile = 0L; 01310 01311 // update saved offset for appending new files 01312 d->m_offset = device()->at(); 01313 return true; 01314 } 01315 01316 bool KZip::writeSymLink(const TQString &name, const TQString &target, 01317 const TQString &user, const TQString &group, 01318 mode_t perm, time_t atime, time_t mtime, time_t ctime) { 01319 return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime); 01320 } 01321 01322 bool KZip::writeSymLink_impl(const TQString &name, const TQString &target, 01323 const TQString &user, const TQString &group, 01324 mode_t perm, time_t atime, time_t mtime, time_t ctime) { 01325 01326 // reassure that symlink flag is set, otherwise strange things happen on 01327 // extraction 01328 perm |= S_IFLNK; 01329 Compression c = compression(); 01330 setCompression(NoCompression); // link targets are never compressed 01331 01332 if (!prepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) { 01333 kdWarning() << "KZip::writeFile prepareWriting failed" << endl; 01334 setCompression(c); 01335 return false; 01336 } 01337 01338 TQCString symlink_target = TQFile::encodeName(target); 01339 if (!writeData(symlink_target, symlink_target.length())) { 01340 kdWarning() << "KZip::writeFile writeData failed" << endl; 01341 setCompression(c); 01342 return false; 01343 } 01344 01345 if (!doneWriting(symlink_target.length())) { 01346 kdWarning() << "KZip::writeFile doneWriting failed" << endl; 01347 setCompression(c); 01348 return false; 01349 } 01350 01351 setCompression(c); 01352 return true; 01353 } 01354 01355 void KZip::virtual_hook( int id, void* data ) 01356 { 01357 switch (id) { 01358 case VIRTUAL_WRITE_DATA: { 01359 WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data); 01360 params->retval = writeData_impl( params->data, params->size ); 01361 break; 01362 } 01363 case VIRTUAL_WRITE_SYMLINK: { 01364 WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data); 01365 params->retval = writeSymLink_impl(*params->name,*params->target, 01366 *params->user,*params->group,params->perm, 01367 params->atime,params->mtime,params->ctime); 01368 break; 01369 } 01370 case VIRTUAL_PREPARE_WRITING: { 01371 PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data); 01372 params->retval = prepareWriting_impl(*params->name,*params->user, 01373 *params->group,params->size,params->perm, 01374 params->atime,params->mtime,params->ctime); 01375 break; 01376 } 01377 default: 01378 KArchive::virtual_hook( id, data ); 01379 }/*end switch*/ 01380 } 01381 01382 // made virtual using virtual_hook 01383 bool KZip::writeData(const char * c, uint i) 01384 { 01385 return KArchive::writeData( c, i ); 01386 } 01387 01388 bool KZip::writeData_impl(const char * c, uint i) 01389 { 01390 Q_ASSERT( d->m_currentFile ); 01391 Q_ASSERT( d->m_currentDev ); 01392 if (!d->m_currentFile || !d->m_currentDev) { 01393 abort(); 01394 return false; 01395 } 01396 01397 // crc to be calculated over uncompressed stuff... 01398 // and they didn't mention it in their docs... 01399 d->m_crc = crc32(d->m_crc, (const Bytef *) c , i); 01400 01401 TQ_LONG written = d->m_currentDev->writeBlock( c, i ); 01402 //kdDebug(7040) << "KZip::writeData wrote " << i << " bytes." << endl; 01403 bool ok = written == (TQ_LONG)i; 01404 if ( !ok ) 01405 abort(); 01406 return ok; 01407 } 01408 01409 void KZip::setCompression( Compression c ) 01410 { 01411 d->m_compression = ( c == NoCompression ) ? 0 : 8; 01412 } 01413 01414 KZip::Compression KZip::compression() const 01415 { 01416 return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression; 01417 } 01418 01419 void KZip::setExtraField( ExtraField ef ) 01420 { 01421 d->m_extraField = ef; 01422 } 01423 01424 KZip::ExtraField KZip::extraField() const 01425 { 01426 return d->m_extraField; 01427 } 01428 01429 void KZip::abort() 01430 { 01431 if ( d->m_saveFile ) { 01432 d->m_saveFile->abort(); 01433 setDevice( 0 ); 01434 } 01435 } 01436 01437 01439 01440 TQByteArray KZipFileEntry::data() const 01441 { 01442 TQIODevice* dev = device(); 01443 TQByteArray arr; 01444 if ( dev ) { 01445 arr = dev->readAll(); 01446 delete dev; 01447 } 01448 return arr; 01449 } 01450 01451 TQIODevice* KZipFileEntry::device() const 01452 { 01453 //kdDebug(7040) << "KZipFileEntry::device creating iodevice limited to pos=" << position() << ", csize=" << compressedSize() << endl; 01454 // Limit the reading to the appropriate part of the underlying device (e.g. file) 01455 KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() ); 01456 if ( encoding() == 0 || compressedSize() == 0 ) // no compression (or even no data) 01457 return limitedDev; 01458 01459 if ( encoding() == 8 ) 01460 { 01461 // On top of that, create a device that uncompresses the zlib data 01462 TQIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" ); 01463 if ( !filterDev ) 01464 return 0L; // ouch 01465 static_cast<KFilterDev *>(filterDev)->setSkipHeaders(); // Just zlib, not gzip 01466 bool b = filterDev->open( IO_ReadOnly ); 01467 Q_ASSERT( b ); 01468 return filterDev; 01469 } 01470 01471 kdError() << "This zip file contains files compressed with method " 01472 << encoding() <<", this method is currently not supported by KZip," 01473 <<" please use a command-line tool to handle this file." << endl; 01474 return 0L; 01475 }