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(it.current()->path().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);
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
01055 bool KZip::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
01056 {
01057 mode_t mode = 0100644;
01058 time_t the_time = time(0);
01059 return KArchive::writeFile( name, user, group, size, mode, the_time,
01060 the_time, the_time, data );
01061 }
01062
01063
01064 bool KZip::writeFile( const QString& name, const QString& user,
01065 const QString& group, uint size, mode_t perm,
01066 time_t atime, time_t mtime, time_t ctime,
01067 const char* data ) {
01068 return KArchive::writeFile(name, user, group, size, perm, atime, mtime,
01069 ctime, data);
01070 }
01071
01072
01073 bool KZip::prepareWriting( const QString& name, const QString& user, const QString& group, uint size )
01074 {
01075 mode_t dflt_perm = 0100644;
01076 time_t the_time = time(0);
01077 return prepareWriting(name,user,group,size,dflt_perm,
01078 the_time,the_time,the_time);
01079 }
01080
01081
01082 bool KZip::prepareWriting(const QString& name, const QString& user,
01083 const QString& group, uint size, mode_t perm,
01084 time_t atime, time_t mtime, time_t ctime) {
01085 return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime);
01086 }
01087
01088 bool KZip::prepareWriting_impl(const QString &name, const QString &user,
01089 const QString &group, uint , mode_t perm,
01090 time_t atime, time_t mtime, time_t ctime) {
01091
01092 if ( !isOpened() )
01093 {
01094 qWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
01095 return false;
01096 }
01097
01098 if ( ! ( mode() & IO_WriteOnly ) )
01099 {
01100 qWarning( "KZip::writeFile: You must open the zip file for writing\n");
01101 return false;
01102 }
01103
01104 if ( !device() ) {
01105
01106 return false;
01107 }
01108
01109
01110 if ( !device()->at( d->m_offset ) ) {
01111 kdWarning(7040) << "prepareWriting_impl: cannot seek in ZIP file. Disk full?" << endl;
01112 abort();
01113 return false;
01114 }
01115
01116
01117
01118
01119
01120 QPtrListIterator<KZipFileEntry> it( d->m_fileList );
01121
01122
01123 for ( ; it.current() ; ++it )
01124 {
01125
01126 if (name == it.current()->path() )
01127 {
01128
01129 d->m_fileList.remove();
01130 }
01131
01132 }
01133
01134 KArchiveDirectory* parentDir = rootDir();
01135 QString fileName( name );
01136 int i = name.findRev( '/' );
01137 if ( i != -1 )
01138 {
01139 QString dir = name.left( i );
01140 fileName = name.mid( i + 1 );
01141
01142 parentDir = findOrCreate( dir );
01143 }
01144
01145
01146 KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, QString::null,
01147 name, device()->at() + 30 + name.length(),
01148 0 , d->m_compression, 0 );
01149 e->setHeaderStart( device()->at() );
01150
01151 parentDir->addEntry( e );
01152
01153 d->m_currentFile = e;
01154 d->m_fileList.append( e );
01155
01156 int extra_field_len = 0;
01157 if ( d->m_extraField == ModificationTime )
01158 extra_field_len = 17;
01159
01160
01161 QCString encodedName = QFile::encodeName(name);
01162 int bufferSize = extra_field_len + encodedName.length() + 30;
01163
01164 char* buffer = new char[ bufferSize ];
01165
01166 buffer[ 0 ] = 'P';
01167 buffer[ 1 ] = 'K';
01168 buffer[ 2 ] = 3;
01169 buffer[ 3 ] = 4;
01170
01171 buffer[ 4 ] = 0x14;
01172 buffer[ 5 ] = 0;
01173
01174 buffer[ 6 ] = 0;
01175 buffer[ 7 ] = 0;
01176
01177 buffer[ 8 ] = char(e->encoding());
01178 buffer[ 9 ] = char(e->encoding() >> 8);
01179
01180 transformToMsDos( e->datetime(), &buffer[ 10 ] );
01181
01182 buffer[ 14 ] = 'C';
01183 buffer[ 15 ] = 'R';
01184 buffer[ 16 ] = 'C';
01185 buffer[ 17 ] = 'q';
01186
01187 buffer[ 18 ] = 'C';
01188 buffer[ 19 ] = 'S';
01189 buffer[ 20 ] = 'I';
01190 buffer[ 21 ] = 'Z';
01191
01192 buffer[ 22 ] = 'U';
01193 buffer[ 23 ] = 'S';
01194 buffer[ 24 ] = 'I';
01195 buffer[ 25 ] = 'Z';
01196
01197 buffer[ 26 ] = (uchar)(encodedName.length());
01198 buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
01199
01200 buffer[ 28 ] = (uchar)(extra_field_len);
01201 buffer[ 29 ] = (uchar)(extra_field_len >> 8);
01202
01203
01204 strncpy( buffer + 30, encodedName, encodedName.length() );
01205
01206
01207 if ( d->m_extraField == ModificationTime )
01208 {
01209 char *extfield = buffer + 30 + encodedName.length();
01210
01211 extfield[0] = 'U';
01212 extfield[1] = 'T';
01213 extfield[2] = 13;
01214 extfield[3] = 0;
01215 extfield[4] = 1 | 2 | 4;
01216
01217 extfield[5] = char(mtime);
01218 extfield[6] = char(mtime >> 8);
01219 extfield[7] = char(mtime >> 16);
01220 extfield[8] = char(mtime >> 24);
01221
01222 extfield[9] = char(atime);
01223 extfield[10] = char(atime >> 8);
01224 extfield[11] = char(atime >> 16);
01225 extfield[12] = char(atime >> 24);
01226
01227 extfield[13] = char(ctime);
01228 extfield[14] = char(ctime >> 8);
01229 extfield[15] = char(ctime >> 16);
01230 extfield[16] = char(ctime >> 24);
01231 }
01232
01233
01234 bool b = (device()->writeBlock( buffer, bufferSize ) == bufferSize );
01235 d->m_crc = 0L;
01236 delete[] buffer;
01237
01238 Q_ASSERT( b );
01239 if (!b) {
01240 abort();
01241 return false;
01242 }
01243
01244
01245
01246 if ( d->m_compression == 0 ) {
01247 d->m_currentDev = device();
01248 return true;
01249 }
01250
01251 d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
01252 Q_ASSERT( d->m_currentDev );
01253 if ( !d->m_currentDev ) {
01254 abort();
01255 return false;
01256 }
01257 static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders();
01258
01259 b = d->m_currentDev->open( IO_WriteOnly );
01260 Q_ASSERT( b );
01261 return b;
01262 }
01263
01264 bool KZip::doneWriting( uint size )
01265 {
01266 if ( d->m_currentFile->encoding() == 8 ) {
01267
01268 (void)d->m_currentDev->writeBlock( 0, 0 );
01269 delete d->m_currentDev;
01270 }
01271
01272 d->m_currentDev = 0L;
01273
01274 Q_ASSERT( d->m_currentFile );
01275
01276
01277
01278 d->m_currentFile->setSize(size);
01279 int extra_field_len = 0;
01280 if ( d->m_extraField == ModificationTime )
01281 extra_field_len = 17;
01282
01283 int csize = device()->at() -
01284 d->m_currentFile->headerStart() - 30 -
01285 d->m_currentFile->path().length() - extra_field_len;
01286 d->m_currentFile->setCompressedSize(csize);
01287
01288
01289
01290
01291
01292 d->m_currentFile->setCRC32( d->m_crc );
01293
01294 d->m_currentFile = 0L;
01295
01296
01297 d->m_offset = device()->at();
01298 return true;
01299 }
01300
01301 bool KZip::writeSymLink(const QString &name, const QString &target,
01302 const QString &user, const QString &group,
01303 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01304 return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime);
01305 }
01306
01307 bool KZip::writeSymLink_impl(const QString &name, const QString &target,
01308 const QString &user, const QString &group,
01309 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01310
01311
01312
01313 perm |= S_IFLNK;
01314 Compression c = compression();
01315 setCompression(NoCompression);
01316
01317 if (!prepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) {
01318 kdWarning() << "KZip::writeFile prepareWriting failed" << endl;
01319 setCompression(c);
01320 return false;
01321 }
01322
01323 QCString symlink_target = QFile::encodeName(target);
01324 if (!writeData(symlink_target, symlink_target.length())) {
01325 kdWarning() << "KZip::writeFile writeData failed" << endl;
01326 setCompression(c);
01327 return false;
01328 }
01329
01330 if (!doneWriting(symlink_target.length())) {
01331 kdWarning() << "KZip::writeFile doneWriting failed" << endl;
01332 setCompression(c);
01333 return false;
01334 }
01335
01336 setCompression(c);
01337 return true;
01338 }
01339
01340 void KZip::virtual_hook( int id, void* data )
01341 {
01342 switch (id) {
01343 case VIRTUAL_WRITE_DATA: {
01344 WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data);
01345 params->retval = writeData_impl( params->data, params->size );
01346 break;
01347 }
01348 case VIRTUAL_WRITE_SYMLINK: {
01349 WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
01350 params->retval = writeSymLink_impl(*params->name,*params->target,
01351 *params->user,*params->group,params->perm,
01352 params->atime,params->mtime,params->ctime);
01353 break;
01354 }
01355 case VIRTUAL_PREPARE_WRITING: {
01356 PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
01357 params->retval = prepareWriting_impl(*params->name,*params->user,
01358 *params->group,params->size,params->perm,
01359 params->atime,params->mtime,params->ctime);
01360 break;
01361 }
01362 default:
01363 KArchive::virtual_hook( id, data );
01364 }
01365 }
01366
01367
01368 bool KZip::writeData(const char * c, uint i)
01369 {
01370 return KArchive::writeData( c, i );
01371 }
01372
01373 bool KZip::writeData_impl(const char * c, uint i)
01374 {
01375 Q_ASSERT( d->m_currentFile );
01376 Q_ASSERT( d->m_currentDev );
01377 if (!d->m_currentFile || !d->m_currentDev) {
01378 abort();
01379 return false;
01380 }
01381
01382
01383
01384 d->m_crc = crc32(d->m_crc, (const Bytef *) c , i);
01385
01386 Q_LONG written = d->m_currentDev->writeBlock( c, i );
01387
01388 bool ok = written == (Q_LONG)i;
01389 if ( !ok )
01390 abort();
01391 return ok;
01392 }
01393
01394 void KZip::setCompression( Compression c )
01395 {
01396 d->m_compression = ( c == NoCompression ) ? 0 : 8;
01397 }
01398
01399 KZip::Compression KZip::compression() const
01400 {
01401 return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
01402 }
01403
01404 void KZip::setExtraField( ExtraField ef )
01405 {
01406 d->m_extraField = ef;
01407 }
01408
01409 KZip::ExtraField KZip::extraField() const
01410 {
01411 return d->m_extraField;
01412 }
01413
01414 void KZip::abort()
01415 {
01416 if ( d->m_saveFile ) {
01417 d->m_saveFile->abort();
01418 setDevice( 0 );
01419 }
01420 }
01421
01422
01424
01425 QByteArray KZipFileEntry::data() const
01426 {
01427 QIODevice* dev = device();
01428 QByteArray arr;
01429 if ( dev ) {
01430 arr = dev->readAll();
01431 delete dev;
01432 }
01433 return arr;
01434 }
01435
01436 QIODevice* KZipFileEntry::device() const
01437 {
01438
01439
01440 KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
01441 if ( encoding() == 0 || compressedSize() == 0 )
01442 return limitedDev;
01443
01444 if ( encoding() == 8 )
01445 {
01446
01447 QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
01448 if ( !filterDev )
01449 return 0L;
01450 static_cast<KFilterDev *>(filterDev)->setSkipHeaders();
01451 bool b = filterDev->open( IO_ReadOnly );
01452 Q_ASSERT( b );
01453 return filterDev;
01454 }
01455
01456 kdError() << "This zip file contains files compressed with method "
01457 << encoding() <<", this method is currently not supported by KZip,"
01458 <<" please use a command-line tool to handle this file." << endl;
01459 return 0L;
01460 }