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