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 #ifndef ORIGINAL_CODE
00958 buffer[ 28 ] = char(path.length());
00959 buffer[ 29 ] = char(path.length() >> 8);
00960 #else
00961 buffer[ 28 ] = char(it.current()->path().length());
00962 buffer[ 29 ] = char(it.current()->path().length() >> 8);
00963 #endif
00964
00965 buffer[ 30 ] = char(extra_field_len);
00966 buffer[ 31 ] = char(extra_field_len >> 8);
00967
00968 buffer[ 40 ] = char(it.current()->permissions());
00969 buffer[ 41 ] = char(it.current()->permissions() >> 8);
00970
00971 int myhst = it.current()->headerStart();
00972 buffer[ 42 ] = char(myhst);
00973 buffer[ 43 ] = char(myhst >> 8);
00974 buffer[ 44 ] = char(myhst >> 16);
00975 buffer[ 45 ] = char(myhst >> 24);
00976
00977
00978 strncpy( buffer + 46, path, path.length() );
00979
00980
00981
00982 char *extfield = buffer + 46 + path.length();
00983 extfield[0] = 'U';
00984 extfield[1] = 'T';
00985 extfield[2] = 5;
00986 extfield[3] = 0;
00987 extfield[4] = 1 | 2 | 4;
00988
00989
00990 unsigned long time = (unsigned long)it.current()->date();
00991 extfield[5] = char(time);
00992 extfield[6] = char(time >> 8);
00993 extfield[7] = char(time >> 16);
00994 extfield[8] = char(time >> 24);
00995
00996 crc = crc32(crc, (Bytef *)buffer, bufferSize );
00997 bool ok = ( device()->writeBlock( buffer, bufferSize ) == bufferSize );
00998 delete[] buffer;
00999 if ( !ok )
01000 return false;
01001 }
01002 Q_LONG centraldirendoffset = device()->at();
01003
01004
01005
01006
01007 buffer[ 0 ] = 'P';
01008 buffer[ 1 ] = 'K';
01009 buffer[ 2 ] = 5;
01010 buffer[ 3 ] = 6;
01011
01012 buffer[ 4 ] = 0;
01013 buffer[ 5 ] = 0;
01014
01015 buffer[ 6 ] = 0;
01016 buffer[ 7 ] = 0;
01017
01018 int count = d->m_fileList.count();
01019
01020
01021
01022 buffer[ 8 ] = char(count);
01023 buffer[ 9 ] = char(count >> 8);
01024
01025 buffer[ 10 ] = buffer[ 8 ];
01026 buffer[ 11 ] = buffer[ 9 ];
01027
01028 int cdsize = centraldirendoffset - centraldiroffset;
01029 buffer[ 12 ] = char(cdsize);
01030 buffer[ 13 ] = char(cdsize >> 8);
01031 buffer[ 14 ] = char(cdsize >> 16);
01032 buffer[ 15 ] = char(cdsize >> 24);
01033
01034
01035
01036
01037 buffer[ 16 ] = char(centraldiroffset);
01038 buffer[ 17 ] = char(centraldiroffset >> 8);
01039 buffer[ 18 ] = char(centraldiroffset >> 16);
01040 buffer[ 19 ] = char(centraldiroffset >> 24);
01041
01042 buffer[ 20 ] = 0;
01043 buffer[ 21 ] = 0;
01044
01045 if ( device()->writeBlock( buffer, 22 ) != 22 )
01046 return false;
01047
01048 if ( d->m_saveFile ) {
01049 d->m_saveFile->close();
01050 setDevice( 0 );
01051 delete d->m_saveFile;
01052 d->m_saveFile = 0;
01053 }
01054
01055
01056 return true;
01057 }
01058
01059 bool KZip::writeDir(const QString& name, const QString& user, const QString& group)
01060 {
01061
01062
01063
01064 QString dirName = name;
01065 if (!name.endsWith("/"))
01066 dirName = dirName.append('/');
01067
01068 mode_t perm = 040755;
01069 time_t the_time = time(0);
01070 return writeFile(dirName, user, group, 0, perm, the_time, the_time, the_time, 0);
01071 }
01072
01073
01074 bool KZip::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
01075 {
01076 mode_t mode = 0100644;
01077 time_t the_time = time(0);
01078 return KArchive::writeFile( name, user, group, size, mode, the_time,
01079 the_time, the_time, data );
01080 }
01081
01082
01083 bool KZip::writeFile( const QString& name, const QString& user,
01084 const QString& group, uint size, mode_t perm,
01085 time_t atime, time_t mtime, time_t ctime,
01086 const char* data ) {
01087 return KArchive::writeFile(name, user, group, size, perm, atime, mtime,
01088 ctime, data);
01089 }
01090
01091
01092 bool KZip::prepareWriting( const QString& name, const QString& user, const QString& group, uint size )
01093 {
01094 mode_t dflt_perm = 0100644;
01095 time_t the_time = time(0);
01096 return prepareWriting(name,user,group,size,dflt_perm,
01097 the_time,the_time,the_time);
01098 }
01099
01100
01101 bool KZip::prepareWriting(const QString& name, const QString& user,
01102 const QString& group, uint size, mode_t perm,
01103 time_t atime, time_t mtime, time_t ctime) {
01104 return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime);
01105 }
01106
01107 bool KZip::prepareWriting_impl(const QString &name, const QString &user,
01108 const QString &group, uint , mode_t perm,
01109 time_t atime, time_t mtime, time_t ctime) {
01110
01111 if ( !isOpened() )
01112 {
01113 qWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
01114 return false;
01115 }
01116
01117 if ( ! ( mode() & IO_WriteOnly ) )
01118 {
01119 qWarning( "KZip::writeFile: You must open the zip file for writing\n");
01120 return false;
01121 }
01122
01123 if ( !device() ) {
01124
01125 return false;
01126 }
01127
01128
01129 if ( !device()->at( d->m_offset ) ) {
01130 kdWarning(7040) << "prepareWriting_impl: cannot seek in ZIP file. Disk full?" << endl;
01131 abort();
01132 return false;
01133 }
01134
01135
01136
01137
01138
01139 QPtrListIterator<KZipFileEntry> it( d->m_fileList );
01140
01141
01142 for ( ; it.current() ; ++it )
01143 {
01144
01145 if (name == it.current()->path() )
01146 {
01147
01148 d->m_fileList.remove();
01149 }
01150
01151 }
01152
01153 KArchiveDirectory* parentDir = rootDir();
01154 QString fileName( name );
01155 int i = name.findRev( '/' );
01156 if ( i != -1 )
01157 {
01158 QString dir = name.left( i );
01159 fileName = name.mid( i + 1 );
01160
01161 parentDir = findOrCreate( dir );
01162 }
01163
01164
01165 #ifdef ORIGINAL_CODE
01166 KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, QString::null,
01167 name, device()->at() + 30 + name.length(),
01168 0 , d->m_compression, 0 );
01169 #else
01170 int extra_field_len = 0;
01171 if ( d->m_extraField == ModificationTime )
01172 extra_field_len = 17;
01173
01174 QCString encodedName = QFile::encodeName(name);
01175
01176 KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user,
01177 group, QString::null,
01178 name,
01179 device()->at() + 30 + encodedName.length() + extra_field_len,
01180 0 , d->m_compression, 0 );
01181 #endif
01182 e->setHeaderStart( device()->at() );
01183
01184 parentDir->addEntry( e );
01185
01186 d->m_currentFile = e;
01187 d->m_fileList.append( e );
01188
01189 #ifdef ORIGINAL_CODE
01190 int extra_field_len = 0;
01191 if ( d->m_extraField == ModificationTime )
01192 extra_field_len = 17;
01193
01194
01195 QCString encodedName = QFile::encodeName(name);
01196 #endif
01197 int bufferSize = extra_field_len + encodedName.length() + 30;
01198
01199 char* buffer = new char[ bufferSize ];
01200
01201 buffer[ 0 ] = 'P';
01202 buffer[ 1 ] = 'K';
01203 buffer[ 2 ] = 3;
01204 buffer[ 3 ] = 4;
01205
01206 buffer[ 4 ] = 0x14;
01207 buffer[ 5 ] = 0;
01208
01209 buffer[ 6 ] = 0;
01210 buffer[ 7 ] = 0;
01211
01212 buffer[ 8 ] = char(e->encoding());
01213 buffer[ 9 ] = char(e->encoding() >> 8);
01214
01215 transformToMsDos( e->datetime(), &buffer[ 10 ] );
01216
01217 buffer[ 14 ] = 'C';
01218 buffer[ 15 ] = 'R';
01219 buffer[ 16 ] = 'C';
01220 buffer[ 17 ] = 'q';
01221
01222 buffer[ 18 ] = 'C';
01223 buffer[ 19 ] = 'S';
01224 buffer[ 20 ] = 'I';
01225 buffer[ 21 ] = 'Z';
01226
01227 buffer[ 22 ] = 'U';
01228 buffer[ 23 ] = 'S';
01229 buffer[ 24 ] = 'I';
01230 buffer[ 25 ] = 'Z';
01231
01232 buffer[ 26 ] = (uchar)(encodedName.length());
01233 buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
01234
01235 buffer[ 28 ] = (uchar)(extra_field_len);
01236 buffer[ 29 ] = (uchar)(extra_field_len >> 8);
01237
01238
01239 strncpy( buffer + 30, encodedName, encodedName.length() );
01240
01241
01242 if ( d->m_extraField == ModificationTime )
01243 {
01244 char *extfield = buffer + 30 + encodedName.length();
01245
01246 extfield[0] = 'U';
01247 extfield[1] = 'T';
01248 extfield[2] = 13;
01249 extfield[3] = 0;
01250 extfield[4] = 1 | 2 | 4;
01251
01252 extfield[5] = char(mtime);
01253 extfield[6] = char(mtime >> 8);
01254 extfield[7] = char(mtime >> 16);
01255 extfield[8] = char(mtime >> 24);
01256
01257 extfield[9] = char(atime);
01258 extfield[10] = char(atime >> 8);
01259 extfield[11] = char(atime >> 16);
01260 extfield[12] = char(atime >> 24);
01261
01262 extfield[13] = char(ctime);
01263 extfield[14] = char(ctime >> 8);
01264 extfield[15] = char(ctime >> 16);
01265 extfield[16] = char(ctime >> 24);
01266 }
01267
01268
01269 bool b = (device()->writeBlock( buffer, bufferSize ) == bufferSize );
01270 d->m_crc = 0L;
01271 delete[] buffer;
01272
01273 Q_ASSERT( b );
01274 if (!b) {
01275 abort();
01276 return false;
01277 }
01278
01279
01280
01281 if ( d->m_compression == 0 ) {
01282 d->m_currentDev = device();
01283 return true;
01284 }
01285
01286 d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
01287 Q_ASSERT( d->m_currentDev );
01288 if ( !d->m_currentDev ) {
01289 abort();
01290 return false;
01291 }
01292 static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders();
01293
01294 b = d->m_currentDev->open( IO_WriteOnly );
01295 Q_ASSERT( b );
01296 return b;
01297 }
01298
01299 bool KZip::doneWriting( uint size )
01300 {
01301 if ( d->m_currentFile->encoding() == 8 ) {
01302
01303 (void)d->m_currentDev->writeBlock( 0, 0 );
01304 delete d->m_currentDev;
01305 }
01306
01307 d->m_currentDev = 0L;
01308
01309 Q_ASSERT( d->m_currentFile );
01310
01311
01312
01313 d->m_currentFile->setSize(size);
01314 int extra_field_len = 0;
01315 if ( d->m_extraField == ModificationTime )
01316 extra_field_len = 17;
01317
01318 #ifndef ORIGINAL_CODE
01319 QCString encodedName = QFile::encodeName(d->m_currentFile->path());
01320 int csize = device()->at() -
01321 d->m_currentFile->headerStart() - 30 -
01322 encodedName.length() - extra_field_len;
01323 #else
01324 int csize = device()->at() -
01325 d->m_currentFile->headerStart() - 30 -
01326 d->m_currentFile->path().length() - extra_field_len;
01327 #endif
01328 d->m_currentFile->setCompressedSize(csize);
01329
01330
01331
01332
01333
01334 d->m_currentFile->setCRC32( d->m_crc );
01335
01336 d->m_currentFile = 0L;
01337
01338
01339 d->m_offset = device()->at();
01340 return true;
01341 }
01342
01343 bool KZip::writeSymLink(const QString &name, const QString &target,
01344 const QString &user, const QString &group,
01345 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01346 return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime);
01347 }
01348
01349 bool KZip::writeSymLink_impl(const QString &name, const QString &target,
01350 const QString &user, const QString &group,
01351 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01352
01353
01354
01355 perm |= S_IFLNK;
01356 Compression c = compression();
01357 setCompression(NoCompression);
01358
01359 if (!prepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) {
01360 kdWarning() << "KZip::writeFile prepareWriting failed" << endl;
01361 setCompression(c);
01362 return false;
01363 }
01364
01365 QCString symlink_target = QFile::encodeName(target);
01366 if (!writeData(symlink_target, symlink_target.length())) {
01367 kdWarning() << "KZip::writeFile writeData failed" << endl;
01368 setCompression(c);
01369 return false;
01370 }
01371
01372 if (!doneWriting(symlink_target.length())) {
01373 kdWarning() << "KZip::writeFile doneWriting failed" << endl;
01374 setCompression(c);
01375 return false;
01376 }
01377
01378 setCompression(c);
01379 return true;
01380 }
01381
01382 void KZip::virtual_hook( int id, void* data )
01383 {
01384 switch (id) {
01385 case VIRTUAL_WRITE_DATA: {
01386 WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data);
01387 params->retval = writeData_impl( params->data, params->size );
01388 break;
01389 }
01390 case VIRTUAL_WRITE_SYMLINK: {
01391 WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
01392 params->retval = writeSymLink_impl(*params->name,*params->target,
01393 *params->user,*params->group,params->perm,
01394 params->atime,params->mtime,params->ctime);
01395 break;
01396 }
01397 case VIRTUAL_PREPARE_WRITING: {
01398 PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
01399 params->retval = prepareWriting_impl(*params->name,*params->user,
01400 *params->group,params->size,params->perm,
01401 params->atime,params->mtime,params->ctime);
01402 break;
01403 }
01404 default:
01405 KArchive::virtual_hook( id, data );
01406 }
01407 }
01408
01409
01410 bool KZip::writeData(const char * c, uint i)
01411 {
01412 return KArchive::writeData( c, i );
01413 }
01414
01415 bool KZip::writeData_impl(const char * c, uint i)
01416 {
01417 Q_ASSERT( d->m_currentFile );
01418 Q_ASSERT( d->m_currentDev );
01419 if (!d->m_currentFile || !d->m_currentDev) {
01420 abort();
01421 return false;
01422 }
01423
01424
01425
01426 d->m_crc = crc32(d->m_crc, (const Bytef *) c , i);
01427
01428 Q_LONG written = d->m_currentDev->writeBlock( c, i );
01429
01430 bool ok = written == (Q_LONG)i;
01431 if ( !ok )
01432 abort();
01433 return ok;
01434 }
01435
01436 void KZip::setCompression( Compression c )
01437 {
01438 d->m_compression = ( c == NoCompression ) ? 0 : 8;
01439 }
01440
01441 KZip::Compression KZip::compression() const
01442 {
01443 return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
01444 }
01445
01446 void KZip::setExtraField( ExtraField ef )
01447 {
01448 d->m_extraField = ef;
01449 }
01450
01451 KZip::ExtraField KZip::extraField() const
01452 {
01453 return d->m_extraField;
01454 }
01455
01456 void KZip::abort()
01457 {
01458 if ( d->m_saveFile ) {
01459 d->m_saveFile->abort();
01460 setDevice( 0 );
01461 }
01462 }
01463
01464
01466
01467 QByteArray KZipFileEntry::data() const
01468 {
01469 QIODevice* dev = device();
01470 QByteArray arr;
01471 if ( dev ) {
01472 arr = dev->readAll();
01473 delete dev;
01474 }
01475 return arr;
01476 }
01477
01478 QIODevice* KZipFileEntry::device() const
01479 {
01480
01481
01482 KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
01483 if ( encoding() == 0 || compressedSize() == 0 )
01484 return limitedDev;
01485
01486 if ( encoding() == 8 )
01487 {
01488
01489 QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
01490 if ( !filterDev )
01491 return 0L;
01492 static_cast<KFilterDev *>(filterDev)->setSkipHeaders();
01493 bool b = filterDev->open( IO_ReadOnly );
01494 Q_ASSERT( b );
01495 return filterDev;
01496 }
01497
01498 kdError() << "This zip file contains files compressed with method "
01499 << encoding() <<", this method is currently not supported by KZip,"
01500 <<" please use a command-line tool to handle this file." << endl;
01501 return 0L;
01502 }