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