00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
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;
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 )
00065 | ( dt.time().minute() << 5 )
00066 | ( dt.time().second() >> 1 );
00067
00068 buffer[0] = char(time);
00069 buffer[1] = char(time >> 8);
00070
00071 const Q_UINT16 date =
00072 ( ( dt.date().year() - 1980 ) << 9 )
00073 | ( dt.date().month() << 5 )
00074 | ( dt.date().day() );
00075
00076 buffer[2] = char(date);
00077 buffer[3] = char(date >> 8);
00078 }
00079 else
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
00107
00109 struct ParseFileInfo {
00110
00111
00112 mode_t perm;
00113 time_t atime;
00114 time_t mtime;
00115 time_t ctime;
00116 int uid;
00117 int gid;
00118 QCString guessed_symlink;
00119 int extralen;
00120
00121
00122 bool exttimestamp_seen;
00123
00124 bool newinfounix_seen;
00125
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 }
00147 int flags = *buffer;
00148 buffer += 1;
00149 size -= 1;
00150
00151 if (flags & 1) {
00152 if (size < 4) {
00153 kdDebug(7040) << "premature end of extended timestamp (#2)" << endl;
00154 return false;
00155 }
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 }
00161
00162
00163 if (!islocal) {
00164 pfi.exttimestamp_seen = true;
00165 return true;
00166 }
00167
00168 if (flags & 2) {
00169 if (size < 4) {
00170 kdDebug(7040) << "premature end of extended timestamp (#3)" << endl;
00171 return true;
00172 }
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 }
00178
00179 if (flags & 4) {
00180 if (size < 4) {
00181 kdDebug(7040) << "premature end of extended timestamp (#4)" << endl;
00182 return true;
00183 }
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 }
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
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 }
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 }
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) {
00238 pfi.newinfounix = true;
00239 return true;
00240 }
00241
00242 if (size < 4) {
00243 kdDebug(7040) << "premature end of Info-ZIP unix extra field new" << endl;
00244 return false;
00245 }
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
00268
00269 if (!islocal) return true;
00270
00271 while (size >= 4) {
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
00280 kdDebug(7040) << "premature end of extra fields reached" << endl;
00281 break;
00282 }
00283
00284 switch (magic) {
00285 case 0x5455:
00286 if (!parseExtTimestamp(buffer, fieldsize, islocal, pfi)) return false;
00287 break;
00288 case 0x5855:
00289 if (!parseInfoZipUnixOld(buffer, fieldsize, islocal, pfi)) return false;
00290 break;
00291 #if 0 // not needed yet
00292 case 0x7855:
00293 if (!parseInfoZipUnixNew(buffer, fieldsize, islocal, pfi)) return false;
00294 break;
00295 #endif
00296 default:
00297 ;
00298 }
00299
00300 buffer += fieldsize;
00301 size -= fieldsize;
00302 }
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;
00323 KZipFileEntry* m_currentFile;
00324 QIODevice* m_currentDev;
00325 QPtrList<KZipFileEntry> m_fileList;
00326 int m_compression;
00327 KZip::ExtraField m_extraField;
00328 unsigned int m_offset;
00329
00330
00331
00332 KSaveFile* m_saveFile;
00333 };
00334
00335 KZip::KZip( const QString& filename )
00336 : KArchive( 0L )
00337 {
00338
00339 Q_ASSERT( !filename.isEmpty() );
00340 m_filename = filename;
00341 d = new KZipPrivate;
00342
00343
00344
00345 }
00346
00347 KZip::KZip( QIODevice * dev )
00348 : KArchive( dev )
00349 {
00350
00351 d = new KZipPrivate;
00352 }
00353
00354 KZip::~KZip()
00355 {
00356
00357
00358 if( isOpened() )
00359 close();
00360 if ( !m_filename.isEmpty() ) {
00361 if ( d->m_saveFile )
00362 delete d->m_saveFile;
00363 else
00364 delete device();
00365 }
00366 delete d;
00367 }
00368
00369 bool KZip::openArchive( int mode )
00370 {
00371
00372 d->m_fileList.clear();
00373
00374 switch ( mode ) {
00375 case IO_WriteOnly:
00376
00377
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
00395 if ( !m_filename.isEmpty() ) {
00396 setDevice( new QFile( m_filename ) );
00397 if ( !device()->open( mode ) )
00398 return false;
00399 }
00400 break;
00401 }
00402 default:
00403 kdWarning(7040) << "Unsupported mode " << mode << endl;
00404 return false;
00405 }
00406
00407 char buffer[47];
00408
00409
00410
00411 QIODevice* dev = device();
00412
00413 if (!dev) {
00414 return false;
00415 }
00416
00417 uint offset = 0;
00418 int n;
00419
00420
00421 QAsciiDict<ParseFileInfo> pfi_map(1009, true , true );
00422 pfi_map.setAutoDelete(true);
00423
00424
00425 bool startOfFile = true;
00426
00427 for (;;)
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 ) )
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 ) )
00448 {
00449 kdDebug(7040) << "PK34 found local file header" << endl;
00450 startOfFile = false;
00451
00452 dev->at( dev->at() + 2 );
00453
00454
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];
00462 int compression_mode = (uchar)buffer[2] | (uchar)buffer[3] << 8;
00463 time_t mtime = transformFromMsDos( buffer+4 );
00464
00465 Q_LONG compr_size = (uchar)buffer[12] | (uchar)buffer[13] << 8
00466 | (uchar)buffer[14] << 16 | (uchar)buffer[15] << 24;
00467 Q_LONG uncomp_size = (uchar)buffer[16] | (uchar)buffer[17] << 8
00468 | (uchar)buffer[18] << 16 | (uchar)buffer[19] << 24;
00469 int namelen = (uchar)buffer[20] | (uchar)buffer[21] << 8;
00470 int extralen = (uchar)buffer[22] | (uchar)buffer[23] << 8;
00471
00472 kdDebug(7040) << "general purpose bit flag: " << gpf << endl;
00473 kdDebug(7040) << "compressed size: " << compr_size << endl;
00474 kdDebug(7040) << "uncompressed size: " << uncomp_size << endl;
00475 kdDebug(7040) << "namelen: " << namelen << endl;
00476 kdDebug(7040) << "extralen: " << extralen << endl;
00477 kdDebug(7040) << "archive size: " << dev->size() << endl;
00478
00479
00480 QCString filename(namelen + 1);
00481 n = dev->readBlock(filename.data(), namelen);
00482 if ( n < namelen ) {
00483 kdWarning(7040) << "Invalid ZIP file. Name not completely read (#2)" << endl;
00484 return false;
00485 }
00486
00487 ParseFileInfo *pfi = new ParseFileInfo();
00488 pfi->mtime = mtime;
00489 pfi_map.insert(filename.data(), pfi);
00490
00491
00492
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
00501 if (!parseExtraField(buffer, handledextralen, true, *pfi))
00502 {
00503 kdWarning(7040) << "Invalid ZIP File. Broken ExtraField." << endl;
00504 return false;
00505 }
00506
00507
00508 dev->at( extraFieldEnd );
00509
00510
00511
00512
00513 if ( gpf & 8 )
00514 {
00515
00516
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
00540
00541
00542
00543
00544 if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
00545 {
00546 foundSignature = true;
00547 dev->at( dev->at() + 12 );
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 );
00554 }
00555 else if ( buffer[0] == 'P' || buffer[1] == 'P' || buffer[2] == 'P' )
00556 {
00557
00558 dev->at( dev->at() - 3 );
00559 }
00560
00561 }
00562 }
00563 else
00564 {
00565
00566 kdDebug(7040) << "general purpose bit flag indicates, that local file header contains valid size" << endl;
00567
00568 if (compression_mode == NoCompression
00569 && uncomp_size <= max_path_len
00570 && uncomp_size > 0) {
00571
00572 pfi->guessed_symlink.resize(uncomp_size + 1);
00573 kdDebug(7040) << "guessed symlink size: " << uncomp_size << endl;
00574 n = dev->readBlock(pfi->guessed_symlink.data(), uncomp_size);
00575 if (n < uncomp_size) {
00576 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#5)" << endl;
00577 return false;
00578 }
00579 } else {
00580
00581 if ( compr_size > (Q_LONG)dev->size() )
00582 {
00583
00584
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
00607
00608
00609
00610
00611 if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
00612 {
00613 foundSignature = true;
00614 dev->at( dev->at() + 12 );
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
00623
00624 }
00625 }
00626 }
00627 else
00628 {
00629
00630 bool success;
00631 success = dev->at( dev->at() + compr_size );
00632
00633
00634
00635
00636
00637 }
00638
00639 }
00640
00641
00642
00643
00644
00645
00646 }
00647 }
00648 else if ( !memcmp( buffer, "PK\1\2", 4 ) )
00649 {
00650 kdDebug(7040) << "PK12 found central block" << endl;
00651 startOfFile = false;
00652
00653
00654
00655
00656 offset = dev->at() - 4;
00657
00658
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;
00664 return false;
00665 }
00666
00667
00668
00669
00670 int namelen = (uchar)buffer[29] << 8 | (uchar)buffer[28];
00671 QCString bufferName( namelen + 1 );
00672 n = dev->readBlock( bufferName.data(), namelen );
00673 if ( n < namelen )
00674 kdWarning(7040) << "Invalid ZIP file. Name not completely read" << endl;
00675
00676 ParseFileInfo *pfi = pfi_map[bufferName];
00677 if (!pfi) {
00678 pfi_map.insert(bufferName.data(), pfi = new ParseFileInfo());
00679 }
00680 QString name( QFile::decodeName(bufferName) );
00681
00682
00683
00684
00685 int extralen = (uchar)buffer[31] << 8 | (uchar)buffer[30];
00686
00687 int commlen = (uchar)buffer[33] << 8 | (uchar)buffer[32];
00688
00689 int cmethod = (uchar)buffer[11] << 8 | (uchar)buffer[10];
00690
00691
00692
00693
00694
00695 uint ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 |
00696 (uchar)buffer[25] << 8 | (uchar)buffer[24];
00697
00698 uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 |
00699 (uchar)buffer[21] << 8 | (uchar)buffer[20];
00700
00701
00702 uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 |
00703 (uchar)buffer[43] << 8 | (uchar)buffer[42];
00704
00705
00706
00707
00708
00709 int localextralen = pfi->extralen;
00710
00711
00712
00713
00714
00715 uint dataoffset = localheaderoffset + 30 + localextralen + namelen;
00716
00717
00718
00719
00720
00721 int os_madeby = (uchar)buffer[5];
00722 bool isdir = false;
00723 int access = 0100644;
00724
00725 if (os_madeby == 3) {
00726 access = (uchar)buffer[40] | (uchar)buffer[41] << 8;
00727 }
00728
00729 QString entryName;
00730
00731 if ( name.endsWith( "/" ) )
00732 {
00733 isdir = true;
00734 name = name.left( name.length() - 1 );
00735 if (os_madeby != 3) access = S_IFDIR | 0755;
00736 else Q_ASSERT(access & S_IFDIR);
00737 }
00738
00739 int pos = name.findRev( '/' );
00740 if ( pos == -1 )
00741 entryName = name;
00742 else
00743 entryName = name.mid( pos + 1 );
00744 Q_ASSERT( !entryName.isEmpty() );
00745
00746 KArchiveEntry* entry;
00747 if ( isdir )
00748 {
00749 QString path = QDir::cleanDirPath( name );
00750 KArchiveEntry* ent = rootDir()->entry( path );
00751 if ( ent && ent->isDirectory() )
00752 {
00753
00754 entry = 0L;
00755 }
00756 else
00757 {
00758 entry = new KArchiveDirectory( this, entryName, access, (int)pfi->mtime, rootDir()->user(), rootDir()->group(), QString::null );
00759
00760 }
00761 }
00762 else
00763 {
00764 QString symlink;
00765 if (S_ISLNK(access)) {
00766 symlink = QFile::decodeName(pfi->guessed_symlink);
00767 }
00768 entry = new KZipFileEntry( this, entryName, access, pfi->mtime,
00769 rootDir()->user(), rootDir()->group(),
00770 symlink, name, dataoffset,
00771 ucsize, cmethod, csize );
00772 static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset );
00773
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
00786 QString path = QDir::cleanDirPath( name.left( pos ) );
00787
00788 KArchiveDirectory * tdir = findOrCreate( path );
00789 tdir->addEntry(entry);
00790 }
00791 }
00792
00793
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
00803
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
00828
00829
00830
00831
00832 if ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 )
00833 {
00834 foundSignature = true;
00835 dev->at( dev->at() - 4 );
00836 }
00837 else if ( buffer[0] == 'P' || buffer[1] == 'P' || buffer[2] == 'P' )
00838 {
00839
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
00852 return true;
00853 }
00854
00855 bool KZip::closeArchive()
00856 {
00857 if ( ! ( mode() & IO_WriteOnly ) )
00858 {
00859
00860 return true;
00861 }
00862
00863 kdDebug() << k_funcinfo << "device=" << device() << endl;
00864
00865
00866
00867 if ( !device() )
00868 return false;
00869
00870
00871 char buffer[ 22 ];
00872 uLong crc = crc32(0L, Z_NULL, 0);
00873
00874 Q_LONG centraldiroffset = device()->at();
00875
00876 Q_LONG atbackup = centraldiroffset;
00877 QPtrListIterator<KZipFileEntry> it( d->m_fileList );
00878
00879 for ( ; it.current() ; ++it )
00880 {
00881 if ( !device()->at( it.current()->headerStart() + 14 ) )
00882 return false;
00883
00884
00885
00886
00887 uLong mycrc = it.current()->crc32();
00888 buffer[0] = char(mycrc);
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);
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);
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
00913
00914
00915 QCString path = QFile::encodeName(it.current()->path());
00916
00917 const int extra_field_len = 9;
00918 int bufferSize = extra_field_len + path.length() + 46;
00919 char* buffer = new char[ bufferSize ];
00920
00921 memset(buffer, 0, 46);
00922
00923 const char head[] =
00924 {
00925 'P', 'K', 1, 2,
00926 0x14, 3,
00927 0x14, 0
00928 };
00929
00930
00931
00932 qmemmove(buffer, head, sizeof(head));
00933
00934 buffer[ 10 ] = char(it.current()->encoding());
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);
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);
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);
00953 buffer[ 25 ] = char(mysize >> 8);
00954 buffer[ 26 ] = char(mysize >> 16);
00955 buffer[ 27 ] = char(mysize >> 24);
00956
00957 buffer[ 28 ] = char(path.length());
00958 buffer[ 29 ] = char(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);
00968 buffer[ 43 ] = char(myhst >> 8);
00969 buffer[ 44 ] = char(myhst >> 16);
00970 buffer[ 45 ] = char(myhst >> 24);
00971
00972
00973 strncpy( buffer + 46, path, path.length() );
00974
00975
00976
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;
00983
00984
00985 unsigned long time = (unsigned long)it.current()->date();
00986 extfield[5] = char(time);
00987 extfield[6] = char(time >> 8);
00988 extfield[7] = char(time >> 16);
00989 extfield[8] = char(time >> 24);
00990
00991 crc = crc32(crc, (Bytef *)buffer, bufferSize );
00992 bool ok = ( device()->writeBlock( buffer, bufferSize ) == bufferSize );
00993 delete[] buffer;
00994 if ( !ok )
00995 return false;
00996 }
00997 Q_LONG centraldirendoffset = device()->at();
00998
00999
01000
01001
01002 buffer[ 0 ] = 'P';
01003 buffer[ 1 ] = 'K';
01004 buffer[ 2 ] = 5;
01005 buffer[ 3 ] = 6;
01006
01007 buffer[ 4 ] = 0;
01008 buffer[ 5 ] = 0;
01009
01010 buffer[ 6 ] = 0;
01011 buffer[ 7 ] = 0;
01012
01013 int count = d->m_fileList.count();
01014
01015
01016
01017 buffer[ 8 ] = char(count);
01018 buffer[ 9 ] = char(count >> 8);
01019
01020 buffer[ 10 ] = buffer[ 8 ];
01021 buffer[ 11 ] = buffer[ 9 ];
01022
01023 int cdsize = centraldirendoffset - centraldiroffset;
01024 buffer[ 12 ] = char(cdsize);
01025 buffer[ 13 ] = char(cdsize >> 8);
01026 buffer[ 14 ] = char(cdsize >> 16);
01027 buffer[ 15 ] = char(cdsize >> 24);
01028
01029
01030
01031
01032 buffer[ 16 ] = char(centraldiroffset);
01033 buffer[ 17 ] = char(centraldiroffset >> 8);
01034 buffer[ 18 ] = char(centraldiroffset >> 16);
01035 buffer[ 19 ] = char(centraldiroffset >> 24);
01036
01037 buffer[ 20 ] = 0;
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
01051 return true;
01052 }
01053
01054 bool KZip::writeDir(const QString& name, const QString& user, const QString& group)
01055 {
01056
01057
01058
01059 QString dirName = name;
01060 if (!name.endsWith("/"))
01061 dirName = dirName.append('/');
01062
01063 mode_t perm = 040755;
01064 time_t the_time = time(0);
01065 return writeFile(dirName, user, group, 0, perm, the_time, the_time, the_time, 0);
01066 }
01067
01068
01069 bool KZip::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
01070 {
01071 mode_t mode = 0100644;
01072 time_t the_time = time(0);
01073 return KArchive::writeFile( name, user, group, size, mode, the_time,
01074 the_time, the_time, data );
01075 }
01076
01077
01078 bool KZip::writeFile( const QString& name, const QString& user,
01079 const QString& group, uint size, mode_t perm,
01080 time_t atime, time_t mtime, time_t ctime,
01081 const char* data ) {
01082 return KArchive::writeFile(name, user, group, size, perm, atime, mtime,
01083 ctime, data);
01084 }
01085
01086
01087 bool KZip::prepareWriting( const QString& name, const QString& user, const QString& group, uint size )
01088 {
01089 mode_t dflt_perm = 0100644;
01090 time_t the_time = time(0);
01091 return prepareWriting(name,user,group,size,dflt_perm,
01092 the_time,the_time,the_time);
01093 }
01094
01095
01096 bool KZip::prepareWriting(const QString& name, const QString& user,
01097 const QString& group, uint size, mode_t perm,
01098 time_t atime, time_t mtime, time_t ctime) {
01099 return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime);
01100 }
01101
01102 bool KZip::prepareWriting_impl(const QString &name, const QString &user,
01103 const QString &group, uint , mode_t perm,
01104 time_t atime, time_t mtime, time_t ctime) {
01105
01106 if ( !isOpened() )
01107 {
01108 qWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
01109 return false;
01110 }
01111
01112 if ( ! ( mode() & IO_WriteOnly ) )
01113 {
01114 qWarning( "KZip::writeFile: You must open the zip file for writing\n");
01115 return false;
01116 }
01117
01118 if ( !device() ) {
01119
01120 return false;
01121 }
01122
01123
01124 if ( !device()->at( d->m_offset ) ) {
01125 kdWarning(7040) << "prepareWriting_impl: cannot seek in ZIP file. Disk full?" << endl;
01126 abort();
01127 return false;
01128 }
01129
01130
01131
01132
01133
01134 QPtrListIterator<KZipFileEntry> it( d->m_fileList );
01135
01136
01137 for ( ; it.current() ; ++it )
01138 {
01139
01140 if (name == it.current()->path() )
01141 {
01142
01143 d->m_fileList.remove();
01144 }
01145
01146 }
01147
01148 KArchiveDirectory* parentDir = rootDir();
01149 QString fileName( name );
01150 int i = name.findRev( '/' );
01151 if ( i != -1 )
01152 {
01153 QString dir = name.left( i );
01154 fileName = name.mid( i + 1 );
01155
01156 parentDir = findOrCreate( dir );
01157 }
01158
01159
01160 KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, QString::null,
01161 name, device()->at() + 30 + name.length(),
01162 0 , d->m_compression, 0 );
01163 e->setHeaderStart( device()->at() );
01164
01165 parentDir->addEntry( e );
01166
01167 d->m_currentFile = e;
01168 d->m_fileList.append( e );
01169
01170 int extra_field_len = 0;
01171 if ( d->m_extraField == ModificationTime )
01172 extra_field_len = 17;
01173
01174
01175 QCString encodedName = QFile::encodeName(name);
01176 int bufferSize = extra_field_len + encodedName.length() + 30;
01177
01178 char* buffer = new char[ bufferSize ];
01179
01180 buffer[ 0 ] = 'P';
01181 buffer[ 1 ] = 'K';
01182 buffer[ 2 ] = 3;
01183 buffer[ 3 ] = 4;
01184
01185 buffer[ 4 ] = 0x14;
01186 buffer[ 5 ] = 0;
01187
01188 buffer[ 6 ] = 0;
01189 buffer[ 7 ] = 0;
01190
01191 buffer[ 8 ] = char(e->encoding());
01192 buffer[ 9 ] = char(e->encoding() >> 8);
01193
01194 transformToMsDos( e->datetime(), &buffer[ 10 ] );
01195
01196 buffer[ 14 ] = 'C';
01197 buffer[ 15 ] = 'R';
01198 buffer[ 16 ] = 'C';
01199 buffer[ 17 ] = 'q';
01200
01201 buffer[ 18 ] = 'C';
01202 buffer[ 19 ] = 'S';
01203 buffer[ 20 ] = 'I';
01204 buffer[ 21 ] = 'Z';
01205
01206 buffer[ 22 ] = 'U';
01207 buffer[ 23 ] = 'S';
01208 buffer[ 24 ] = 'I';
01209 buffer[ 25 ] = 'Z';
01210
01211 buffer[ 26 ] = (uchar)(encodedName.length());
01212 buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
01213
01214 buffer[ 28 ] = (uchar)(extra_field_len);
01215 buffer[ 29 ] = (uchar)(extra_field_len >> 8);
01216
01217
01218 strncpy( buffer + 30, encodedName, encodedName.length() );
01219
01220
01221 if ( d->m_extraField == ModificationTime )
01222 {
01223 char *extfield = buffer + 30 + encodedName.length();
01224
01225 extfield[0] = 'U';
01226 extfield[1] = 'T';
01227 extfield[2] = 13;
01228 extfield[3] = 0;
01229 extfield[4] = 1 | 2 | 4;
01230
01231 extfield[5] = char(mtime);
01232 extfield[6] = char(mtime >> 8);
01233 extfield[7] = char(mtime >> 16);
01234 extfield[8] = char(mtime >> 24);
01235
01236 extfield[9] = char(atime);
01237 extfield[10] = char(atime >> 8);
01238 extfield[11] = char(atime >> 16);
01239 extfield[12] = char(atime >> 24);
01240
01241 extfield[13] = char(ctime);
01242 extfield[14] = char(ctime >> 8);
01243 extfield[15] = char(ctime >> 16);
01244 extfield[16] = char(ctime >> 24);
01245 }
01246
01247
01248 bool b = (device()->writeBlock( buffer, bufferSize ) == bufferSize );
01249 d->m_crc = 0L;
01250 delete[] buffer;
01251
01252 Q_ASSERT( b );
01253 if (!b) {
01254 abort();
01255 return false;
01256 }
01257
01258
01259
01260 if ( d->m_compression == 0 ) {
01261 d->m_currentDev = device();
01262 return true;
01263 }
01264
01265 d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
01266 Q_ASSERT( d->m_currentDev );
01267 if ( !d->m_currentDev ) {
01268 abort();
01269 return false;
01270 }
01271 static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders();
01272
01273 b = d->m_currentDev->open( IO_WriteOnly );
01274 Q_ASSERT( b );
01275 return b;
01276 }
01277
01278 bool KZip::doneWriting( uint size )
01279 {
01280 if ( d->m_currentFile->encoding() == 8 ) {
01281
01282 (void)d->m_currentDev->writeBlock( 0, 0 );
01283 delete d->m_currentDev;
01284 }
01285
01286 d->m_currentDev = 0L;
01287
01288 Q_ASSERT( d->m_currentFile );
01289
01290
01291
01292 d->m_currentFile->setSize(size);
01293 int extra_field_len = 0;
01294 if ( d->m_extraField == ModificationTime )
01295 extra_field_len = 17;
01296
01297 const QCString encodedName = QFile::encodeName(d->m_currentFile->path());
01298 int csize = device()->at() -
01299 d->m_currentFile->headerStart() - 30 -
01300 encodedName.length() - extra_field_len;
01301 d->m_currentFile->setCompressedSize(csize);
01302
01303
01304
01305
01306
01307 d->m_currentFile->setCRC32( d->m_crc );
01308
01309 d->m_currentFile = 0L;
01310
01311
01312 d->m_offset = device()->at();
01313 return true;
01314 }
01315
01316 bool KZip::writeSymLink(const QString &name, const QString &target,
01317 const QString &user, const QString &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 QString &name, const QString &target,
01323 const QString &user, const QString &group,
01324 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01325
01326
01327
01328 perm |= S_IFLNK;
01329 Compression c = compression();
01330 setCompression(NoCompression);
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 QCString symlink_target = QFile::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 }
01380 }
01381
01382
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
01398
01399 d->m_crc = crc32(d->m_crc, (const Bytef *) c , i);
01400
01401 Q_LONG written = d->m_currentDev->writeBlock( c, i );
01402
01403 bool ok = written == (Q_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 QByteArray KZipFileEntry::data() const
01441 {
01442 QIODevice* dev = device();
01443 QByteArray arr;
01444 if ( dev ) {
01445 arr = dev->readAll();
01446 delete dev;
01447 }
01448 return arr;
01449 }
01450
01451 QIODevice* KZipFileEntry::device() const
01452 {
01453
01454
01455 KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
01456 if ( encoding() == 0 || compressedSize() == 0 )
01457 return limitedDev;
01458
01459 if ( encoding() == 8 )
01460 {
01461
01462 QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
01463 if ( !filterDev )
01464 return 0L;
01465 static_cast<KFilterDev *>(filterDev)->setSkipHeaders();
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 }