kio Library API Documentation

kzip.cpp

00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2000 David Faure <faure@kde.org> 00003 Copyright (C) 2002 Holger Schroeder <holger-kde@holgis.net> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License version 2 as published by the Free Software Foundation. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00017 Boston, MA 02111-1307, USA. 00018 */ 00019 00020 /* 00021 This class implements a kioslave to access ZIP files from KDE. 00022 you can use it in IO_ReadOnly or in IO_WriteOnly mode, and it 00023 behaves just as expected (i hope ;-) ). 00024 It can also be used in IO_ReadWrite mode, in this case one can 00025 append files to an existing zip archive. when you append new files, which 00026 are not yet in the zip, it works as expected, they are appended at the end. 00027 when you append a file, which is already in the file, the reference to the 00028 old file is dropped and the new one is added to the zip. but the 00029 old data from the file itself is not deleted, it is still in the 00030 zipfile. so when you want to have a small and garbagefree zipfile, 00031 just read the contents of the appended zipfile and write it to a new one 00032 in IO_WriteOnly mode. especially take care of this, when you don't want 00033 to leak information of how intermediate versions of files in the zip 00034 were looking. 00035 For more information on the zip fileformat go to 00036 http://www.pkware.com/support/appnote.html . 00037 00038 */ 00039 00040 #include "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 // KArchive::open() 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 for (;;) // repeat until 'end of entries' signature is reached 00425 { 00426 kdDebug(7040) << "loop starts" << endl; 00427 kdDebug(7040) << "dev->at() now : " << dev->at() << endl; 00428 n = dev->readBlock( buffer, 4 ); 00429 00430 if (n < 4) 00431 { 00432 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#1)" << endl; 00433 00434 return false; 00435 } 00436 00437 if ( !memcmp( buffer, "PK\5\6", 4 ) ) // 'end of entries' 00438 { 00439 kdDebug(7040) << "PK56 found end of archive" << endl; 00440 break; 00441 } 00442 00443 if ( !memcmp( buffer, "PK\3\4", 4 ) ) // local file header 00444 { 00445 kdDebug(7040) << "PK34 found local file header" << endl; 00446 // can this fail ??? 00447 dev->at( dev->at() + 2 ); // skip 'version needed to extract' 00448 00449 // read static header stuff 00450 n = dev->readBlock( buffer, 24 ); 00451 if (n < 24) { 00452 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#4)" << endl; 00453 return false; 00454 } 00455 00456 int gpf = (uchar)buffer[0]; // "general purpose flag" not "general protection fault" ;-) 00457 int compression_mode = (uchar)buffer[2] | (uchar)buffer[3] << 8; 00458 time_t mtime = transformFromMsDos( buffer+4 ); 00459 00460 Q_LONG compr_size = (uchar)buffer[12] | (uchar)buffer[13] << 8 00461 | (uchar)buffer[14] << 16 | (uchar)buffer[15] << 24; 00462 Q_LONG uncomp_size = (uchar)buffer[16] | (uchar)buffer[17] << 8 00463 | (uchar)buffer[18] << 16 | (uchar)buffer[19] << 24; 00464 int namelen = (uchar)buffer[20] | (uchar)buffer[21] << 8; 00465 int extralen = (uchar)buffer[22] | (uchar)buffer[23] << 8; 00466 00467 kdDebug(7040) << "general purpose bit flag: " << gpf << endl; 00468 kdDebug(7040) << "compressed size: " << compr_size << endl; 00469 kdDebug(7040) << "uncompressed size: " << uncomp_size << endl; 00470 kdDebug(7040) << "namelen: " << namelen << endl; 00471 kdDebug(7040) << "extralen: " << extralen << endl; 00472 kdDebug(7040) << "archive size: " << dev->size() << endl; 00473 00474 // read filename 00475 QCString filename(namelen + 1); 00476 n = dev->readBlock(filename.data(), namelen); 00477 if ( n < namelen ) { 00478 kdWarning(7040) << "Invalid ZIP file. Name not completely read (#2)" << endl; 00479 return false; 00480 } 00481 00482 ParseFileInfo *pfi = new ParseFileInfo(); 00483 pfi->mtime = mtime; 00484 pfi_map.insert(filename.data(), pfi); 00485 00486 // read and parse the beginning of the extra field, 00487 // skip rest of extra field in case it is too long 00488 unsigned int extraFieldEnd = dev->at() + extralen; 00489 pfi->extralen = extralen; 00490 int handledextralen = QMIN(extralen, (int)sizeof buffer); 00491 00492 kdDebug(7040) << "handledextralen: " << handledextralen << endl; 00493 00494 n = dev->readBlock(buffer, handledextralen); 00495 // no error msg necessary as we deliberately truncate the extra field 00496 if (!parseExtraField(buffer, handledextralen, true, *pfi)) 00497 { 00498 kdWarning(7040) << "Invalid ZIP File. Broken ExtraField." << endl; 00499 return false; 00500 } 00501 00502 // jump to end of extra field 00503 dev->at( extraFieldEnd ); 00504 00505 // we have to take care of the 'general purpose bit flag'. 00506 // if bit 3 is set, the header doesn't contain the length of 00507 // the file and we look for the signature 'PK\7\8'. 00508 if ( gpf & 8 ) 00509 { 00510 // here we have to read through the compressed data to find 00511 // the next PKxx 00512 kdDebug(7040) << "trying to seek for next PK78" << endl; 00513 bool foundSignature = false; 00514 00515 while (!foundSignature) 00516 { 00517 n = dev->readBlock( buffer, 1 ); 00518 if (n < 1) 00519 { 00520 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)" << endl; 00521 return false; 00522 } 00523 00524 if ( buffer[0] != 'P' ) 00525 continue; 00526 00527 n = dev->readBlock( buffer, 3 ); 00528 if (n < 3) 00529 { 00530 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)" << endl; 00531 return false; 00532 } 00533 00534 // we have to detect three magic tokens here: 00535 // PK34 for the next local header in case there is no data descriptor 00536 // PK12 for the central header in case there is no data descriptor 00537 // PK78 for the data descriptor in case it is following the compressed data 00538 00539 if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 ) 00540 { 00541 foundSignature = true; 00542 dev->at( dev->at() + 12 ); // skip the 'data_descriptor' 00543 } 00544 00545 if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 ) 00546 || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) ) 00547 { 00548 foundSignature = true; 00549 dev->at( dev->at() - 4 ); // go back 4 bytes, so that the magic bytes can be found... 00550 } 00551 00552 } 00553 } 00554 else 00555 { 00556 // here we skip the compressed data and jump to the next header 00557 kdDebug(7040) << "general purpose bit flag indicates, that local file header contains valid size" << endl; 00558 // check if this could be a symbolic link 00559 if (compression_mode == NoCompression 00560 && uncomp_size <= max_path_len 00561 && uncomp_size > 0) { 00562 // read content and store it 00563 pfi->guessed_symlink.resize(uncomp_size + 1); 00564 kdDebug(7040) << "guessed symlink size: " << uncomp_size << endl; 00565 n = dev->readBlock(pfi->guessed_symlink.data(), uncomp_size); 00566 if (n < uncomp_size) { 00567 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#5)" << endl; 00568 return false; 00569 } 00570 } else { 00571 00572 if ( compr_size > (Q_LONG)dev->size() ) 00573 { 00574 // here we cannot trust the compressed size, so scan through the compressed 00575 // data to find the next header 00576 bool foundSignature = false; 00577 00578 while (!foundSignature) 00579 { 00580 n = dev->readBlock( buffer, 1 ); 00581 if (n < 1) 00582 { 00583 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)" << endl; 00584 return false; 00585 } 00586 00587 if ( buffer[0] != 'P' ) 00588 continue; 00589 00590 n = dev->readBlock( buffer, 3 ); 00591 if (n < 3) 00592 { 00593 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)" << endl; 00594 return false; 00595 } 00596 00597 // we have to detect three magic tokens here: 00598 // PK34 for the next local header in case there is no data descriptor 00599 // PK12 for the central header in case there is no data descriptor 00600 // PK78 for the data descriptor in case it is following the compressed data 00601 00602 if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 ) 00603 { 00604 foundSignature = true; 00605 dev->at( dev->at() + 12 ); // skip the 'data_descriptor' 00606 } 00607 00608 if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 ) 00609 || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) ) 00610 { 00611 foundSignature = true; 00612 dev->at( dev->at() - 4 ); 00613 // go back 4 bytes, so that the magic bytes can be found 00614 // in the next cycle... 00615 } 00616 } 00617 } 00618 else 00619 { 00620 // kdDebug(7040) << "before interesting dev->at(): " << dev->at() << endl; 00621 bool success; 00622 success = dev->at( dev->at() + compr_size ); // can this fail ??? 00623 /* kdDebug(7040) << "after interesting dev->at(): " << dev->at() << endl; 00624 if ( success ) 00625 kdDebug(7040) << "dev->at was successful... " << endl; 00626 else 00627 kdDebug(7040) << "dev->at failed... " << endl;*/ 00628 } 00629 00630 } 00631 00632 // not needed any more 00633 /* // here we calculate the length of the file in the zip 00634 // with headers and jump to the next header. 00635 uint skip = compr_size + namelen + extralen; 00636 offset += 30 + skip;*/ 00637 } 00638 } 00639 else if ( !memcmp( buffer, "PK\1\2", 4 ) ) // central block 00640 { 00641 kdDebug(7040) << "PK12 found central block" << endl; 00642 00643 // so we reached the central header at the end of the zip file 00644 // here we get all interesting data out of the central header 00645 // of a file 00646 offset = dev->at() - 4; 00647 00648 //set offset for appending new files 00649 if ( d->m_offset == 0L ) d->m_offset = offset; 00650 00651 n = dev->readBlock( buffer + 4, 42 ); 00652 if (n < 42) { 00653 kdWarning(7040) << "Invalid ZIP file, central entry too short" << endl; // not long enough for valid entry 00654 return false; 00655 } 00656 00657 //int gpf = (uchar)buffer[9] << 8 | (uchar)buffer[10]; 00658 //kdDebug() << "general purpose flag=" << gpf << endl; 00659 // length of the filename (well, pathname indeed) 00660 int namelen = (uchar)buffer[29] << 8 | (uchar)buffer[28]; 00661 QCString bufferName( namelen + 1 ); 00662 n = dev->readBlock( bufferName.data(), namelen ); 00663 if ( n < namelen ) 00664 kdWarning(7040) << "Invalid ZIP file. Name not completely read" << endl; 00665 00666 ParseFileInfo *pfi = pfi_map[bufferName]; 00667 if (!pfi) { // can that happen? 00668 pfi_map.insert(bufferName.data(), pfi = new ParseFileInfo()); 00669 } 00670 QString name( QFile::decodeName(bufferName) ); 00671 00672 //kdDebug(7040) << "name: " << name << endl; 00673 // only in central header ! see below. 00674 // length of extra attributes 00675 int extralen = (uchar)buffer[31] << 8 | (uchar)buffer[30]; 00676 // length of comment for this file 00677 int commlen = (uchar)buffer[33] << 8 | (uchar)buffer[32]; 00678 // compression method of this file 00679 int cmethod = (uchar)buffer[11] << 8 | (uchar)buffer[10]; 00680 00681 //kdDebug(7040) << "cmethod: " << cmethod << endl; 00682 //kdDebug(7040) << "extralen: " << extralen << endl; 00683 00684 // uncompressed file size 00685 uint ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 | 00686 (uchar)buffer[25] << 8 | (uchar)buffer[24]; 00687 // compressed file size 00688 uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 | 00689 (uchar)buffer[21] << 8 | (uchar)buffer[20]; 00690 00691 // offset of local header 00692 uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 | 00693 (uchar)buffer[43] << 8 | (uchar)buffer[42]; 00694 00695 // some clever people use different extra field lengths 00696 // in the central header and in the local header... funny. 00697 // so we need to get the localextralen to calculate the offset 00698 // from localheaderstart to dataoffset 00699 int localextralen = pfi->extralen; // FIXME: this will not work if 00700 // no local header exists 00701 00702 //kdDebug(7040) << "localextralen: " << localextralen << endl; 00703 00704 // offset, where the real data for uncompression starts 00705 uint dataoffset = localheaderoffset + 30 + localextralen + namelen; //comment only in central header 00706 00707 //kdDebug(7040) << "esize: " << esize << endl; 00708 //kdDebug(7040) << "eoffset: " << eoffset << endl; 00709 //kdDebug(7040) << "csize: " << csize << endl; 00710 00711 int os_madeby = (uchar)buffer[5]; 00712 bool isdir = false; 00713 int access = 0100644; 00714 00715 if (os_madeby == 3) { // good ole unix 00716 access = (uchar)buffer[40] | (uchar)buffer[41] << 8; 00717 } 00718 00719 QString entryName; 00720 00721 if ( name.endsWith( "/" ) ) // Entries with a trailing slash are directories 00722 { 00723 isdir = true; 00724 name = name.left( name.length() - 1 ); 00725 if (os_madeby != 3) access = S_IFDIR | 0755; 00726 else Q_ASSERT(access & S_IFDIR); 00727 } 00728 00729 int pos = name.findRev( '/' ); 00730 if ( pos == -1 ) 00731 entryName = name; 00732 else 00733 entryName = name.mid( pos + 1 ); 00734 Q_ASSERT( !entryName.isEmpty() ); 00735 00736 KArchiveEntry* entry; 00737 if ( isdir ) 00738 { 00739 QString path = QDir::cleanDirPath( name ); 00740 KArchiveEntry* ent = rootDir()->entry( path ); 00741 if ( ent && ent->isDirectory() ) 00742 { 00743 //kdDebug(7040) << "Directory already exists, NOT going to add it again" << endl; 00744 entry = 0L; 00745 } 00746 else 00747 { 00748 entry = new KArchiveDirectory( this, entryName, access, (int)pfi->mtime, rootDir()->user(), rootDir()->group(), QString::null ); 00749 //kdDebug(7040) << "KArchiveDirectory created, entryName= " << entryName << ", name=" << name << endl; 00750 } 00751 } 00752 else 00753 { 00754 QString symlink; 00755 if (S_ISLNK(access)) { 00756 symlink = QFile::decodeName(pfi->guessed_symlink); 00757 } 00758 entry = new KZipFileEntry( this, entryName, access, pfi->mtime, 00759 rootDir()->user(), rootDir()->group(), 00760 symlink, name, dataoffset, 00761 ucsize, cmethod, csize ); 00762 static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset ); 00763 //kdDebug(7040) << "KZipFileEntry created, entryName= " << entryName << ", name=" << name << endl; 00764 d->m_fileList.append( static_cast<KZipFileEntry *>( entry ) ); 00765 } 00766 00767 if ( entry ) 00768 { 00769 if ( pos == -1 ) 00770 { 00771 rootDir()->addEntry(entry); 00772 } 00773 else 00774 { 00775 // In some tar files we can find dir/./file => call cleanDirPath 00776 QString path = QDir::cleanDirPath( name.left( pos ) ); 00777 // Ensure container directory exists, create otherwise 00778 KArchiveDirectory * tdir = findOrCreate( path ); 00779 tdir->addEntry(entry); 00780 } 00781 } 00782 00783 //calculate offset to next entry 00784 offset += 46 + commlen + extralen + namelen; 00785 bool b = dev->at(offset); 00786 Q_ASSERT( b ); 00787 if ( !b ) 00788 return false; 00789 } 00790 else 00791 { 00792 kdWarning(7040) << "Invalid ZIP file. Unrecognized header at offset " << offset << endl; 00793 00794 return false; 00795 } 00796 } 00797 //kdDebug(7040) << "*** done *** " << endl; 00798 return true; 00799 } 00800 00801 bool KZip::closeArchive() 00802 { 00803 if ( ! ( mode() & IO_WriteOnly ) ) 00804 { 00805 //kdDebug(7040) << "closearchive readonly reached." << endl; 00806 return true; 00807 } 00808 00809 kdDebug() << k_funcinfo << "device=" << device() << endl; 00810 //ReadWrite or WriteOnly 00811 //write all central dir file entries 00812 00813 if ( !device() ) // saving aborted 00814 return false; 00815 00816 // to be written at the end of the file... 00817 char buffer[ 22 ]; // first used for 12, then for 22 at the end 00818 uLong crc = crc32(0L, Z_NULL, 0); 00819 00820 Q_LONG centraldiroffset = device()->at(); 00821 //kdDebug(7040) << "closearchive: centraldiroffset: " << centraldiroffset << endl; 00822 Q_LONG atbackup = centraldiroffset; 00823 QPtrListIterator<KZipFileEntry> it( d->m_fileList ); 00824 00825 for ( ; it.current() ; ++it ) 00826 { //set crc and compressed size in each local file header 00827 if ( !device()->at( it.current()->headerStart() + 14 ) ) 00828 return false; 00829 //kdDebug(7040) << "closearchive setcrcandcsize: filename: " 00830 // << it.current()->path() 00831 // << " encoding: "<< it.current()->encoding() << endl; 00832 00833 uLong mycrc = it.current()->crc32(); 00834 buffer[0] = char(mycrc); // crc checksum, at headerStart+14 00835 buffer[1] = char(mycrc >> 8); 00836 buffer[2] = char(mycrc >> 16); 00837 buffer[3] = char(mycrc >> 24); 00838 00839 int mysize1 = it.current()->compressedSize(); 00840 buffer[4] = char(mysize1); // compressed file size, at headerStart+18 00841 buffer[5] = char(mysize1 >> 8); 00842 buffer[6] = char(mysize1 >> 16); 00843 buffer[7] = char(mysize1 >> 24); 00844 00845 int myusize = it.current()->size(); 00846 buffer[8] = char(myusize); // uncompressed file size, at headerStart+22 00847 buffer[9] = char(myusize >> 8); 00848 buffer[10] = char(myusize >> 16); 00849 buffer[11] = char(myusize >> 24); 00850 00851 if ( device()->writeBlock( buffer, 12 ) != 12 ) 00852 return false; 00853 } 00854 device()->at( atbackup ); 00855 00856 for ( it.toFirst(); it.current() ; ++it ) 00857 { 00858 //kdDebug(7040) << "closearchive: filename: " << it.current()->path() 00859 // << " encoding: "<< it.current()->encoding() << endl; 00860 00861 QCString path = QFile::encodeName(it.current()->path()); 00862 00863 const int extra_field_len = 9; 00864 int bufferSize = extra_field_len + path.length() + 46; 00865 char* buffer = new char[ bufferSize ]; 00866 00867 memset(buffer, 0, 46); // zero is a nice default for most header fields 00868 00869 const char head[] = 00870 { 00871 'P', 'K', 1, 2, // central file header signature 00872 0x14, 3, // version made by (3 == UNIX) 00873 0x14, 0 // version needed to extract 00874 }; 00875 00876 // I do not know why memcpy is not working here 00877 //memcpy(buffer, head, sizeof(head)); 00878 qmemmove(buffer, head, sizeof(head)); 00879 00880 buffer[ 10 ] = char(it.current()->encoding()); // compression method 00881 buffer[ 11 ] = char(it.current()->encoding() >> 8); 00882 00883 transformToMsDos( it.current()->datetime(), &buffer[ 12 ] ); 00884 00885 uLong mycrc = it.current()->crc32(); 00886 buffer[ 16 ] = char(mycrc); // crc checksum 00887 buffer[ 17 ] = char(mycrc >> 8); 00888 buffer[ 18 ] = char(mycrc >> 16); 00889 buffer[ 19 ] = char(mycrc >> 24); 00890 00891 int mysize1 = it.current()->compressedSize(); 00892 buffer[ 20 ] = char(mysize1); // compressed file size 00893 buffer[ 21 ] = char(mysize1 >> 8); 00894 buffer[ 22 ] = char(mysize1 >> 16); 00895 buffer[ 23 ] = char(mysize1 >> 24); 00896 00897 int mysize = it.current()->size(); 00898 buffer[ 24 ] = char(mysize); // uncompressed file size 00899 buffer[ 25 ] = char(mysize >> 8); 00900 buffer[ 26 ] = char(mysize >> 16); 00901 buffer[ 27 ] = char(mysize >> 24); 00902 00903 buffer[ 28 ] = char(it.current()->path().length()); // filename length 00904 buffer[ 29 ] = char(it.current()->path().length() >> 8); 00905 00906 buffer[ 30 ] = char(extra_field_len); 00907 buffer[ 31 ] = char(extra_field_len >> 8); 00908 00909 buffer[ 40 ] = char(it.current()->permissions()); 00910 buffer[ 41 ] = char(it.current()->permissions() >> 8); 00911 00912 int myhst = it.current()->headerStart(); 00913 buffer[ 42 ] = char(myhst); //relative offset of local header 00914 buffer[ 43 ] = char(myhst >> 8); 00915 buffer[ 44 ] = char(myhst >> 16); 00916 buffer[ 45 ] = char(myhst >> 24); 00917 00918 // file name 00919 strncpy( buffer + 46, path, path.length() ); 00920 //kdDebug(7040) << "closearchive length to write: " << bufferSize << endl; 00921 00922 // extra field 00923 char *extfield = buffer + 46 + path.length(); 00924 extfield[0] = 'U'; 00925 extfield[1] = 'T'; 00926 extfield[2] = 5; 00927 extfield[3] = 0; 00928 extfield[4] = 1 | 2 | 4; // specify flags from local field 00929 // (unless I misread the spec) 00930 // provide only modification time 00931 unsigned long time = (unsigned long)it.current()->date(); 00932 extfield[5] = char(time); 00933 extfield[6] = char(time >> 8); 00934 extfield[7] = char(time >> 16); 00935 extfield[8] = char(time >> 24); 00936 00937 crc = crc32(crc, (Bytef *)buffer, bufferSize ); 00938 bool ok = ( device()->writeBlock( buffer, bufferSize ) == bufferSize ); 00939 delete[] buffer; 00940 if ( !ok ) 00941 return false; 00942 } 00943 Q_LONG centraldirendoffset = device()->at(); 00944 //kdDebug(7040) << "closearchive: centraldirendoffset: " << centraldirendoffset << endl; 00945 //kdDebug(7040) << "closearchive: device()->at(): " << device()->at() << endl; 00946 00947 //write end of central dir record. 00948 buffer[ 0 ] = 'P'; //end of central dir signature 00949 buffer[ 1 ] = 'K'; 00950 buffer[ 2 ] = 5; 00951 buffer[ 3 ] = 6; 00952 00953 buffer[ 4 ] = 0; // number of this disk 00954 buffer[ 5 ] = 0; 00955 00956 buffer[ 6 ] = 0; // number of disk with start of central dir 00957 buffer[ 7 ] = 0; 00958 00959 int count = d->m_fileList.count(); 00960 //kdDebug(7040) << "number of files (count): " << count << endl; 00961 00962 00963 buffer[ 8 ] = char(count); // total number of entries in central dir of 00964 buffer[ 9 ] = char(count >> 8); // this disk 00965 00966 buffer[ 10 ] = buffer[ 8 ]; // total number of entries in the central dir 00967 buffer[ 11 ] = buffer[ 9 ]; 00968 00969 int cdsize = centraldirendoffset - centraldiroffset; 00970 buffer[ 12 ] = char(cdsize); // size of the central dir 00971 buffer[ 13 ] = char(cdsize >> 8); 00972 buffer[ 14 ] = char(cdsize >> 16); 00973 buffer[ 15 ] = char(cdsize >> 24); 00974 00975 //kdDebug(7040) << "end : centraldiroffset: " << centraldiroffset << endl; 00976 //kdDebug(7040) << "end : centraldirsize: " << cdsize << endl; 00977 00978 buffer[ 16 ] = char(centraldiroffset); // central dir offset 00979 buffer[ 17 ] = char(centraldiroffset >> 8); 00980 buffer[ 18 ] = char(centraldiroffset >> 16); 00981 buffer[ 19 ] = char(centraldiroffset >> 24); 00982 00983 buffer[ 20 ] = 0; //zipfile comment length 00984 buffer[ 21 ] = 0; 00985 00986 if ( device()->writeBlock( buffer, 22 ) != 22 ) 00987 return false; 00988 00989 if ( d->m_saveFile ) { 00990 d->m_saveFile->close(); 00991 setDevice( 0 ); 00992 delete d->m_saveFile; 00993 d->m_saveFile = 0; 00994 } 00995 00996 //kdDebug(7040) << __FILE__" reached." << endl; 00997 return true; 00998 } 00999 01000 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0 01001 bool KZip::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data ) 01002 { 01003 mode_t mode = 0100644; 01004 time_t the_time = time(0); 01005 return KArchive::writeFile( name, user, group, size, mode, the_time, 01006 the_time, the_time, data ); 01007 } 01008 01009 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0 01010 bool KZip::writeFile( const QString& name, const QString& user, 01011 const QString& group, uint size, mode_t perm, 01012 time_t atime, time_t mtime, time_t ctime, 01013 const char* data ) { 01014 return KArchive::writeFile(name, user, group, size, perm, atime, mtime, 01015 ctime, data); 01016 } 01017 01018 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0 01019 bool KZip::prepareWriting( const QString& name, const QString& user, const QString& group, uint size ) 01020 { 01021 mode_t dflt_perm = 0100644; 01022 time_t the_time = time(0); 01023 return prepareWriting(name,user,group,size,dflt_perm, 01024 the_time,the_time,the_time); 01025 } 01026 01027 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0 01028 bool KZip::prepareWriting(const QString& name, const QString& user, 01029 const QString& group, uint size, mode_t perm, 01030 time_t atime, time_t mtime, time_t ctime) { 01031 return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime); 01032 } 01033 01034 bool KZip::prepareWriting_impl(const QString &name, const QString &user, 01035 const QString &group, uint /*size*/, mode_t perm, 01036 time_t atime, time_t mtime, time_t ctime) { 01037 //kdDebug(7040) << "prepareWriting reached." << endl; 01038 if ( !isOpened() ) 01039 { 01040 qWarning( "KZip::writeFile: You must open the zip file before writing to it\n"); 01041 return false; 01042 } 01043 01044 if ( ! ( mode() & IO_WriteOnly ) ) // accept WriteOnly and ReadWrite 01045 { 01046 qWarning( "KZip::writeFile: You must open the zip file for writing\n"); 01047 return false; 01048 } 01049 01050 if ( !device() ) { // aborted 01051 //kdWarning(7040) << "prepareWriting_impl: no device" << endl; 01052 return false; 01053 } 01054 01055 // set right offset in zip. 01056 if ( !device()->at( d->m_offset ) ) { 01057 kdWarning(7040) << "prepareWriting_impl: cannot seek in ZIP file. Disk full?" << endl; 01058 abort(); 01059 return false; 01060 } 01061 01062 // delete entries in the filelist with the same filename as the one we want 01063 // to save, so that we donīt have duplicate file entries when viewing the zip 01064 // with konqi... 01065 // CAUTION: the old file itself is still in the zip and won't be removed !!! 01066 QPtrListIterator<KZipFileEntry> it( d->m_fileList ); 01067 01068 //kdDebug(7040) << "filename to write: " << name <<endl; 01069 for ( ; it.current() ; ++it ) 01070 { 01071 //kdDebug(7040) << "prepfilename: " << it.current()->path() <<endl; 01072 if (name == it.current()->path() ) 01073 { 01074 //kdDebug(7040) << "removing following entry: " << it.current()->path() <<endl; 01075 d->m_fileList.remove(); 01076 } 01077 01078 } 01079 // Find or create parent dir 01080 KArchiveDirectory* parentDir = rootDir(); 01081 QString fileName( name ); 01082 int i = name.findRev( '/' ); 01083 if ( i != -1 ) 01084 { 01085 QString dir = name.left( i ); 01086 fileName = name.mid( i + 1 ); 01087 //kdDebug(7040) << "KZip::prepareWriting ensuring " << dir << " exists. fileName=" << fileName << endl; 01088 parentDir = findOrCreate( dir ); 01089 } 01090 01091 // construct a KZipFileEntry and add it to list 01092 KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, QString::null, 01093 name, device()->at() + 30 + name.length(), // start 01094 0 /*size unknown yet*/, d->m_compression, 0 /*csize unknown yet*/ ); 01095 e->setHeaderStart( device()->at() ); 01096 //kdDebug(7040) << "wrote file start: " << e->position() << " name: " << name << endl; 01097 parentDir->addEntry( e ); 01098 01099 d->m_currentFile = e; 01100 d->m_fileList.append( e ); 01101 01102 int extra_field_len = 0; 01103 if ( d->m_extraField == ModificationTime ) 01104 extra_field_len = 17; // value also used in doneWriting() 01105 01106 // write out zip header 01107 QCString encodedName = QFile::encodeName(name); 01108 int bufferSize = extra_field_len + encodedName.length() + 30; 01109 //kdDebug(7040) << "KZip::prepareWriting bufferSize=" << bufferSize << endl; 01110 char* buffer = new char[ bufferSize ]; 01111 01112 buffer[ 0 ] = 'P'; //local file header signature 01113 buffer[ 1 ] = 'K'; 01114 buffer[ 2 ] = 3; 01115 buffer[ 3 ] = 4; 01116 01117 buffer[ 4 ] = 0x14; // version needed to extract 01118 buffer[ 5 ] = 0; 01119 01120 buffer[ 6 ] = 0; // general purpose bit flag 01121 buffer[ 7 ] = 0; 01122 01123 buffer[ 8 ] = char(e->encoding()); // compression method 01124 buffer[ 9 ] = char(e->encoding() >> 8); 01125 01126 transformToMsDos( e->datetime(), &buffer[ 10 ] ); 01127 01128 buffer[ 14 ] = 'C'; //dummy crc 01129 buffer[ 15 ] = 'R'; 01130 buffer[ 16 ] = 'C'; 01131 buffer[ 17 ] = 'q'; 01132 01133 buffer[ 18 ] = 'C'; //compressed file size 01134 buffer[ 19 ] = 'S'; 01135 buffer[ 20 ] = 'I'; 01136 buffer[ 21 ] = 'Z'; 01137 01138 buffer[ 22 ] = 'U'; //uncompressed file size 01139 buffer[ 23 ] = 'S'; 01140 buffer[ 24 ] = 'I'; 01141 buffer[ 25 ] = 'Z'; 01142 01143 buffer[ 26 ] = (uchar)(encodedName.length()); //filename length 01144 buffer[ 27 ] = (uchar)(encodedName.length() >> 8); 01145 01146 buffer[ 28 ] = (uchar)(extra_field_len); // extra field length 01147 buffer[ 29 ] = (uchar)(extra_field_len >> 8); 01148 01149 // file name 01150 strncpy( buffer + 30, encodedName, encodedName.length() ); 01151 01152 // extra field 01153 if ( d->m_extraField == ModificationTime ) 01154 { 01155 char *extfield = buffer + 30 + encodedName.length(); 01156 // "Extended timestamp" header (0x5455) 01157 extfield[0] = 'U'; 01158 extfield[1] = 'T'; 01159 extfield[2] = 13; // data size 01160 extfield[3] = 0; 01161 extfield[4] = 1 | 2 | 4; // contains mtime, atime, ctime 01162 01163 extfield[5] = char(mtime); 01164 extfield[6] = char(mtime >> 8); 01165 extfield[7] = char(mtime >> 16); 01166 extfield[8] = char(mtime >> 24); 01167 01168 extfield[9] = char(atime); 01169 extfield[10] = char(atime >> 8); 01170 extfield[11] = char(atime >> 16); 01171 extfield[12] = char(atime >> 24); 01172 01173 extfield[13] = char(ctime); 01174 extfield[14] = char(ctime >> 8); 01175 extfield[15] = char(ctime >> 16); 01176 extfield[16] = char(ctime >> 24); 01177 } 01178 01179 // Write header 01180 bool b = (device()->writeBlock( buffer, bufferSize ) == bufferSize ); 01181 d->m_crc = 0L; 01182 delete[] buffer; 01183 01184 Q_ASSERT( b ); 01185 if (!b) { 01186 abort(); 01187 return false; 01188 } 01189 01190 // Prepare device for writing the data 01191 // Either device() if no compression, or a KFilterDev to compress 01192 if ( d->m_compression == 0 ) { 01193 d->m_currentDev = device(); 01194 return true; 01195 } 01196 01197 d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false ); 01198 Q_ASSERT( d->m_currentDev ); 01199 if ( !d->m_currentDev ) { 01200 abort(); 01201 return false; // ouch 01202 } 01203 static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders(); // Just zlib, not gzip 01204 01205 b = d->m_currentDev->open( IO_WriteOnly ); 01206 Q_ASSERT( b ); 01207 return b; 01208 } 01209 01210 bool KZip::doneWriting( uint size ) 01211 { 01212 if ( d->m_currentFile->encoding() == 8 ) { 01213 // Finish 01214 (void)d->m_currentDev->writeBlock( 0, 0 ); 01215 delete d->m_currentDev; 01216 } 01217 // If 0, d->m_currentDev was device() - don't delete ;) 01218 d->m_currentDev = 0L; 01219 01220 Q_ASSERT( d->m_currentFile ); 01221 //kdDebug(7040) << "donewriting reached." << endl; 01222 //kdDebug(7040) << "filename: " << d->m_currentFile->path() << endl; 01223 //kdDebug(7040) << "getpos (at): " << device()->at() << endl; 01224 d->m_currentFile->setSize(size); 01225 int extra_field_len = 0; 01226 if ( d->m_extraField == ModificationTime ) 01227 extra_field_len = 17; // value also used in doneWriting() 01228 01229 int csize = device()->at() - 01230 d->m_currentFile->headerStart() - 30 - 01231 d->m_currentFile->path().length() - extra_field_len; 01232 d->m_currentFile->setCompressedSize(csize); 01233 //kdDebug(7040) << "usize: " << d->m_currentFile->size() << endl; 01234 //kdDebug(7040) << "csize: " << d->m_currentFile->compressedSize() << endl; 01235 //kdDebug(7040) << "headerstart: " << d->m_currentFile->headerStart() << endl; 01236 01237 //kdDebug(7040) << "crc: " << d->m_crc << endl; 01238 d->m_currentFile->setCRC32( d->m_crc ); 01239 01240 d->m_currentFile = 0L; 01241 01242 // update saved offset for appending new files 01243 d->m_offset = device()->at(); 01244 return true; 01245 } 01246 01247 bool KZip::writeSymLink(const QString &name, const QString &target, 01248 const QString &user, const QString &group, 01249 mode_t perm, time_t atime, time_t mtime, time_t ctime) { 01250 return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime); 01251 } 01252 01253 bool KZip::writeSymLink_impl(const QString &name, const QString &target, 01254 const QString &user, const QString &group, 01255 mode_t perm, time_t atime, time_t mtime, time_t ctime) { 01256 01257 // reassure that symlink flag is set, otherwise strange things happen on 01258 // extraction 01259 perm |= S_IFLNK; 01260 Compression c = compression(); 01261 setCompression(NoCompression); // link targets are never compressed 01262 01263 if (!prepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) { 01264 kdWarning() << "KZip::writeFile prepareWriting failed" << endl; 01265 setCompression(c); 01266 return false; 01267 } 01268 01269 QCString symlink_target = QFile::encodeName(target); 01270 if (!writeData(symlink_target, symlink_target.length())) { 01271 kdWarning() << "KZip::writeFile writeData failed" << endl; 01272 setCompression(c); 01273 return false; 01274 } 01275 01276 if (!doneWriting(symlink_target.length())) { 01277 kdWarning() << "KZip::writeFile doneWriting failed" << endl; 01278 setCompression(c); 01279 return false; 01280 } 01281 01282 setCompression(c); 01283 return true; 01284 } 01285 01286 void KZip::virtual_hook( int id, void* data ) 01287 { 01288 switch (id) { 01289 case VIRTUAL_WRITE_DATA: { 01290 WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data); 01291 params->retval = writeData_impl( params->data, params->size ); 01292 break; 01293 } 01294 case VIRTUAL_WRITE_SYMLINK: { 01295 WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data); 01296 params->retval = writeSymLink_impl(*params->name,*params->target, 01297 *params->user,*params->group,params->perm, 01298 params->atime,params->mtime,params->ctime); 01299 break; 01300 } 01301 case VIRTUAL_PREPARE_WRITING: { 01302 PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data); 01303 params->retval = prepareWriting_impl(*params->name,*params->user, 01304 *params->group,params->size,params->perm, 01305 params->atime,params->mtime,params->ctime); 01306 break; 01307 } 01308 default: 01309 KArchive::virtual_hook( id, data ); 01310 }/*end switch*/ 01311 } 01312 01313 // made virtual using virtual_hook 01314 bool KZip::writeData(const char * c, uint i) 01315 { 01316 return KArchive::writeData( c, i ); 01317 } 01318 01319 bool KZip::writeData_impl(const char * c, uint i) 01320 { 01321 Q_ASSERT( d->m_currentFile ); 01322 Q_ASSERT( d->m_currentDev ); 01323 if (!d->m_currentFile || !d->m_currentDev) { 01324 abort(); 01325 return false; 01326 } 01327 01328 // crc to be calculated over uncompressed stuff... 01329 // and they didn't mention it in their docs... 01330 d->m_crc = crc32(d->m_crc, (const Bytef *) c , i); 01331 01332 Q_LONG written = d->m_currentDev->writeBlock( c, i ); 01333 //kdDebug(7040) << "KZip::writeData wrote " << i << " bytes." << endl; 01334 bool ok = written == (Q_LONG)i; 01335 if ( !ok ) 01336 abort(); 01337 return ok; 01338 } 01339 01340 void KZip::setCompression( Compression c ) 01341 { 01342 d->m_compression = ( c == NoCompression ) ? 0 : 8; 01343 } 01344 01345 KZip::Compression KZip::compression() const 01346 { 01347 return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression; 01348 } 01349 01350 void KZip::setExtraField( ExtraField ef ) 01351 { 01352 d->m_extraField = ef; 01353 } 01354 01355 KZip::ExtraField KZip::extraField() const 01356 { 01357 return d->m_extraField; 01358 } 01359 01360 void KZip::abort() 01361 { 01362 if ( d->m_saveFile ) { 01363 d->m_saveFile->abort(); 01364 setDevice( 0 ); 01365 } 01366 } 01367 01368 01370 01371 QByteArray KZipFileEntry::data() const 01372 { 01373 QIODevice* dev = device(); 01374 QByteArray arr; 01375 if ( dev ) { 01376 arr = dev->readAll(); 01377 delete dev; 01378 } 01379 return arr; 01380 } 01381 01382 QIODevice* KZipFileEntry::device() const 01383 { 01384 //kdDebug(7040) << "KZipFileEntry::device creating iodevice limited to pos=" << position() << ", csize=" << compressedSize() << endl; 01385 // Limit the reading to the appropriate part of the underlying device (e.g. file) 01386 KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() ); 01387 if ( encoding() == 0 || compressedSize() == 0 ) // no compression (or even no data) 01388 return limitedDev; 01389 01390 if ( encoding() == 8 ) 01391 { 01392 // On top of that, create a device that uncompresses the zlib data 01393 QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" ); 01394 if ( !filterDev ) 01395 return 0L; // ouch 01396 static_cast<KFilterDev *>(filterDev)->setSkipHeaders(); // Just zlib, not gzip 01397 bool b = filterDev->open( IO_ReadOnly ); 01398 Q_ASSERT( b ); 01399 return filterDev; 01400 } 01401 01402 kdError() << "This zip file contains files compressed with method " 01403 << encoding() <<", this method is currently not supported by KZip," 01404 <<" please use a command-line tool to handle this file." << endl; 01405 return 0L; 01406 }
KDE Logo
This file is part of the documentation for kio Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Apr 12 23:09:30 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003