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 for (;;)
00425 {
00426 kdDebug(7040) << "loop starts" << endl;
00427 kdDebug(7040) << "dev->at() now : " << dev->at() << endl;
00428 n = dev->readBlock( buffer, 4 );
00429
00430 if (n < 4)
00431 {
00432 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#1)" << endl;
00433
00434 return false;
00435 }
00436
00437 if ( !memcmp( buffer, "PK\5\6", 4 ) )
00438 {
00439 kdDebug(7040) << "PK56 found end of archive" << endl;
00440 break;
00441 }
00442
00443 if ( !memcmp( buffer, "PK\3\4", 4 ) )
00444 {
00445 kdDebug(7040) << "PK34 found local file header" << endl;
00446
00447 dev->at( dev->at() + 2 );
00448
00449
00450 n = dev->readBlock( buffer, 24 );
00451 if (n < 24) {
00452 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#4)" << endl;
00453 return false;
00454 }
00455
00456 int gpf = (uchar)buffer[0];
00457 int compression_mode = (uchar)buffer[2] | (uchar)buffer[3] << 8;
00458 time_t mtime = transformFromMsDos( buffer+4 );
00459
00460 Q_LONG compr_size = (uchar)buffer[12] | (uchar)buffer[13] << 8
00461 | (uchar)buffer[14] << 16 | (uchar)buffer[15] << 24;
00462 Q_LONG uncomp_size = (uchar)buffer[16] | (uchar)buffer[17] << 8
00463 | (uchar)buffer[18] << 16 | (uchar)buffer[19] << 24;
00464 int namelen = (uchar)buffer[20] | (uchar)buffer[21] << 8;
00465 int extralen = (uchar)buffer[22] | (uchar)buffer[23] << 8;
00466
00467 kdDebug(7040) << "general purpose bit flag: " << gpf << endl;
00468 kdDebug(7040) << "compressed size: " << compr_size << endl;
00469 kdDebug(7040) << "uncompressed size: " << uncomp_size << endl;
00470 kdDebug(7040) << "namelen: " << namelen << endl;
00471 kdDebug(7040) << "extralen: " << extralen << endl;
00472 kdDebug(7040) << "archive size: " << dev->size() << endl;
00473
00474
00475 QCString filename(namelen + 1);
00476 n = dev->readBlock(filename.data(), namelen);
00477 if ( n < namelen ) {
00478 kdWarning(7040) << "Invalid ZIP file. Name not completely read (#2)" << endl;
00479 return false;
00480 }
00481
00482 ParseFileInfo *pfi = new ParseFileInfo();
00483 pfi->mtime = mtime;
00484 pfi_map.insert(filename.data(), pfi);
00485
00486
00487
00488 unsigned int extraFieldEnd = dev->at() + extralen;
00489 pfi->extralen = extralen;
00490 int handledextralen = QMIN(extralen, (int)sizeof buffer);
00491
00492 kdDebug(7040) << "handledextralen: " << handledextralen << endl;
00493
00494 n = dev->readBlock(buffer, handledextralen);
00495
00496 if (!parseExtraField(buffer, handledextralen, true, *pfi))
00497 {
00498 kdWarning(7040) << "Invalid ZIP File. Broken ExtraField." << endl;
00499 return false;
00500 }
00501
00502
00503 dev->at( extraFieldEnd );
00504
00505
00506
00507
00508 if ( gpf & 8 )
00509 {
00510
00511
00512 kdDebug(7040) << "trying to seek for next PK78" << endl;
00513 bool foundSignature = false;
00514
00515 while (!foundSignature)
00516 {
00517 n = dev->readBlock( buffer, 1 );
00518 if (n < 1)
00519 {
00520 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)" << endl;
00521 return false;
00522 }
00523
00524 if ( buffer[0] != 'P' )
00525 continue;
00526
00527 n = dev->readBlock( buffer, 3 );
00528 if (n < 3)
00529 {
00530 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)" << endl;
00531 return false;
00532 }
00533
00534
00535
00536
00537
00538
00539 if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
00540 {
00541 foundSignature = true;
00542 dev->at( dev->at() + 12 );
00543 }
00544
00545 if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 )
00546 || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) )
00547 {
00548 foundSignature = true;
00549 dev->at( dev->at() - 4 );
00550 }
00551
00552 }
00553 }
00554 else
00555 {
00556
00557 kdDebug(7040) << "general purpose bit flag indicates, that local file header contains valid size" << endl;
00558
00559 if (compression_mode == NoCompression
00560 && uncomp_size <= max_path_len
00561 && uncomp_size > 0) {
00562
00563 pfi->guessed_symlink.resize(uncomp_size + 1);
00564 kdDebug(7040) << "guessed symlink size: " << uncomp_size << endl;
00565 n = dev->readBlock(pfi->guessed_symlink.data(), uncomp_size);
00566 if (n < uncomp_size) {
00567 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#5)" << endl;
00568 return false;
00569 }
00570 } else {
00571
00572 if ( compr_size > (Q_LONG)dev->size() )
00573 {
00574
00575
00576 bool foundSignature = false;
00577
00578 while (!foundSignature)
00579 {
00580 n = dev->readBlock( buffer, 1 );
00581 if (n < 1)
00582 {
00583 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)" << endl;
00584 return false;
00585 }
00586
00587 if ( buffer[0] != 'P' )
00588 continue;
00589
00590 n = dev->readBlock( buffer, 3 );
00591 if (n < 3)
00592 {
00593 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)" << endl;
00594 return false;
00595 }
00596
00597
00598
00599
00600
00601
00602 if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
00603 {
00604 foundSignature = true;
00605 dev->at( dev->at() + 12 );
00606 }
00607
00608 if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 )
00609 || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) )
00610 {
00611 foundSignature = true;
00612 dev->at( dev->at() - 4 );
00613
00614
00615 }
00616 }
00617 }
00618 else
00619 {
00620
00621 bool success;
00622 success = dev->at( dev->at() + compr_size );
00623
00624
00625
00626
00627
00628 }
00629
00630 }
00631
00632
00633
00634
00635
00636
00637 }
00638 }
00639 else if ( !memcmp( buffer, "PK\1\2", 4 ) )
00640 {
00641 kdDebug(7040) << "PK12 found central block" << endl;
00642
00643
00644
00645
00646 offset = dev->at() - 4;
00647
00648
00649 if ( d->m_offset == 0L ) d->m_offset = offset;
00650
00651 n = dev->readBlock( buffer + 4, 42 );
00652 if (n < 42) {
00653 kdWarning(7040) << "Invalid ZIP file, central entry too short" << endl;
00654 return false;
00655 }
00656
00657
00658
00659
00660 int namelen = (uchar)buffer[29] << 8 | (uchar)buffer[28];
00661 QCString bufferName( namelen + 1 );
00662 n = dev->readBlock( bufferName.data(), namelen );
00663 if ( n < namelen )
00664 kdWarning(7040) << "Invalid ZIP file. Name not completely read" << endl;
00665
00666 ParseFileInfo *pfi = pfi_map[bufferName];
00667 if (!pfi) {
00668 pfi_map.insert(bufferName.data(), pfi = new ParseFileInfo());
00669 }
00670 QString name( QFile::decodeName(bufferName) );
00671
00672
00673
00674
00675 int extralen = (uchar)buffer[31] << 8 | (uchar)buffer[30];
00676
00677 int commlen = (uchar)buffer[33] << 8 | (uchar)buffer[32];
00678
00679 int cmethod = (uchar)buffer[11] << 8 | (uchar)buffer[10];
00680
00681
00682
00683
00684
00685 uint ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 |
00686 (uchar)buffer[25] << 8 | (uchar)buffer[24];
00687
00688 uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 |
00689 (uchar)buffer[21] << 8 | (uchar)buffer[20];
00690
00691
00692 uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 |
00693 (uchar)buffer[43] << 8 | (uchar)buffer[42];
00694
00695
00696
00697
00698
00699 int localextralen = pfi->extralen;
00700
00701
00702
00703
00704
00705 uint dataoffset = localheaderoffset + 30 + localextralen + namelen;
00706
00707
00708
00709
00710
00711 int os_madeby = (uchar)buffer[5];
00712 bool isdir = false;
00713 int access = 0100644;
00714
00715 if (os_madeby == 3) {
00716 access = (uchar)buffer[40] | (uchar)buffer[41] << 8;
00717 }
00718
00719 QString entryName;
00720
00721 if ( name.endsWith( "/" ) )
00722 {
00723 isdir = true;
00724 name = name.left( name.length() - 1 );
00725 if (os_madeby != 3) access = S_IFDIR | 0755;
00726 else Q_ASSERT(access & S_IFDIR);
00727 }
00728
00729 int pos = name.findRev( '/' );
00730 if ( pos == -1 )
00731 entryName = name;
00732 else
00733 entryName = name.mid( pos + 1 );
00734 Q_ASSERT( !entryName.isEmpty() );
00735
00736 KArchiveEntry* entry;
00737 if ( isdir )
00738 {
00739 QString path = QDir::cleanDirPath( name );
00740 KArchiveEntry* ent = rootDir()->entry( path );
00741 if ( ent && ent->isDirectory() )
00742 {
00743
00744 entry = 0L;
00745 }
00746 else
00747 {
00748 entry = new KArchiveDirectory( this, entryName, access, (int)pfi->mtime, rootDir()->user(), rootDir()->group(), QString::null );
00749
00750 }
00751 }
00752 else
00753 {
00754 QString symlink;
00755 if (S_ISLNK(access)) {
00756 symlink = QFile::decodeName(pfi->guessed_symlink);
00757 }
00758 entry = new KZipFileEntry( this, entryName, access, pfi->mtime,
00759 rootDir()->user(), rootDir()->group(),
00760 symlink, name, dataoffset,
00761 ucsize, cmethod, csize );
00762 static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset );
00763
00764 d->m_fileList.append( static_cast<KZipFileEntry *>( entry ) );
00765 }
00766
00767 if ( entry )
00768 {
00769 if ( pos == -1 )
00770 {
00771 rootDir()->addEntry(entry);
00772 }
00773 else
00774 {
00775
00776 QString path = QDir::cleanDirPath( name.left( pos ) );
00777
00778 KArchiveDirectory * tdir = findOrCreate( path );
00779 tdir->addEntry(entry);
00780 }
00781 }
00782
00783
00784 offset += 46 + commlen + extralen + namelen;
00785 bool b = dev->at(offset);
00786 Q_ASSERT( b );
00787 if ( !b )
00788 return false;
00789 }
00790 else
00791 {
00792 kdWarning(7040) << "Invalid ZIP file. Unrecognized header at offset " << offset << endl;
00793
00794 return false;
00795 }
00796 }
00797
00798 return true;
00799 }
00800
00801 bool KZip::closeArchive()
00802 {
00803 if ( ! ( mode() & IO_WriteOnly ) )
00804 {
00805
00806 return true;
00807 }
00808
00809 kdDebug() << k_funcinfo << "device=" << device() << endl;
00810
00811
00812
00813 if ( !device() )
00814 return false;
00815
00816
00817 char buffer[ 22 ];
00818 uLong crc = crc32(0L, Z_NULL, 0);
00819
00820 Q_LONG centraldiroffset = device()->at();
00821
00822 Q_LONG atbackup = centraldiroffset;
00823 QPtrListIterator<KZipFileEntry> it( d->m_fileList );
00824
00825 for ( ; it.current() ; ++it )
00826 {
00827 if ( !device()->at( it.current()->headerStart() + 14 ) )
00828 return false;
00829
00830
00831
00832
00833 uLong mycrc = it.current()->crc32();
00834 buffer[0] = char(mycrc);
00835 buffer[1] = char(mycrc >> 8);
00836 buffer[2] = char(mycrc >> 16);
00837 buffer[3] = char(mycrc >> 24);
00838
00839 int mysize1 = it.current()->compressedSize();
00840 buffer[4] = char(mysize1);
00841 buffer[5] = char(mysize1 >> 8);
00842 buffer[6] = char(mysize1 >> 16);
00843 buffer[7] = char(mysize1 >> 24);
00844
00845 int myusize = it.current()->size();
00846 buffer[8] = char(myusize);
00847 buffer[9] = char(myusize >> 8);
00848 buffer[10] = char(myusize >> 16);
00849 buffer[11] = char(myusize >> 24);
00850
00851 if ( device()->writeBlock( buffer, 12 ) != 12 )
00852 return false;
00853 }
00854 device()->at( atbackup );
00855
00856 for ( it.toFirst(); it.current() ; ++it )
00857 {
00858
00859
00860
00861 QCString path = QFile::encodeName(it.current()->path());
00862
00863 const int extra_field_len = 9;
00864 int bufferSize = extra_field_len + path.length() + 46;
00865 char* buffer = new char[ bufferSize ];
00866
00867 memset(buffer, 0, 46);
00868
00869 const char head[] =
00870 {
00871 'P', 'K', 1, 2,
00872 0x14, 3,
00873 0x14, 0
00874 };
00875
00876
00877
00878 qmemmove(buffer, head, sizeof(head));
00879
00880 buffer[ 10 ] = char(it.current()->encoding());
00881 buffer[ 11 ] = char(it.current()->encoding() >> 8);
00882
00883 transformToMsDos( it.current()->datetime(), &buffer[ 12 ] );
00884
00885 uLong mycrc = it.current()->crc32();
00886 buffer[ 16 ] = char(mycrc);
00887 buffer[ 17 ] = char(mycrc >> 8);
00888 buffer[ 18 ] = char(mycrc >> 16);
00889 buffer[ 19 ] = char(mycrc >> 24);
00890
00891 int mysize1 = it.current()->compressedSize();
00892 buffer[ 20 ] = char(mysize1);
00893 buffer[ 21 ] = char(mysize1 >> 8);
00894 buffer[ 22 ] = char(mysize1 >> 16);
00895 buffer[ 23 ] = char(mysize1 >> 24);
00896
00897 int mysize = it.current()->size();
00898 buffer[ 24 ] = char(mysize);
00899 buffer[ 25 ] = char(mysize >> 8);
00900 buffer[ 26 ] = char(mysize >> 16);
00901 buffer[ 27 ] = char(mysize >> 24);
00902
00903 buffer[ 28 ] = char(it.current()->path().length());
00904 buffer[ 29 ] = char(it.current()->path().length() >> 8);
00905
00906 buffer[ 30 ] = char(extra_field_len);
00907 buffer[ 31 ] = char(extra_field_len >> 8);
00908
00909 buffer[ 40 ] = char(it.current()->permissions());
00910 buffer[ 41 ] = char(it.current()->permissions() >> 8);
00911
00912 int myhst = it.current()->headerStart();
00913 buffer[ 42 ] = char(myhst);
00914 buffer[ 43 ] = char(myhst >> 8);
00915 buffer[ 44 ] = char(myhst >> 16);
00916 buffer[ 45 ] = char(myhst >> 24);
00917
00918
00919 strncpy( buffer + 46, path, path.length() );
00920
00921
00922
00923 char *extfield = buffer + 46 + path.length();
00924 extfield[0] = 'U';
00925 extfield[1] = 'T';
00926 extfield[2] = 5;
00927 extfield[3] = 0;
00928 extfield[4] = 1 | 2 | 4;
00929
00930
00931 unsigned long time = (unsigned long)it.current()->date();
00932 extfield[5] = char(time);
00933 extfield[6] = char(time >> 8);
00934 extfield[7] = char(time >> 16);
00935 extfield[8] = char(time >> 24);
00936
00937 crc = crc32(crc, (Bytef *)buffer, bufferSize );
00938 bool ok = ( device()->writeBlock( buffer, bufferSize ) == bufferSize );
00939 delete[] buffer;
00940 if ( !ok )
00941 return false;
00942 }
00943 Q_LONG centraldirendoffset = device()->at();
00944
00945
00946
00947
00948 buffer[ 0 ] = 'P';
00949 buffer[ 1 ] = 'K';
00950 buffer[ 2 ] = 5;
00951 buffer[ 3 ] = 6;
00952
00953 buffer[ 4 ] = 0;
00954 buffer[ 5 ] = 0;
00955
00956 buffer[ 6 ] = 0;
00957 buffer[ 7 ] = 0;
00958
00959 int count = d->m_fileList.count();
00960
00961
00962
00963 buffer[ 8 ] = char(count);
00964 buffer[ 9 ] = char(count >> 8);
00965
00966 buffer[ 10 ] = buffer[ 8 ];
00967 buffer[ 11 ] = buffer[ 9 ];
00968
00969 int cdsize = centraldirendoffset - centraldiroffset;
00970 buffer[ 12 ] = char(cdsize);
00971 buffer[ 13 ] = char(cdsize >> 8);
00972 buffer[ 14 ] = char(cdsize >> 16);
00973 buffer[ 15 ] = char(cdsize >> 24);
00974
00975
00976
00977
00978 buffer[ 16 ] = char(centraldiroffset);
00979 buffer[ 17 ] = char(centraldiroffset >> 8);
00980 buffer[ 18 ] = char(centraldiroffset >> 16);
00981 buffer[ 19 ] = char(centraldiroffset >> 24);
00982
00983 buffer[ 20 ] = 0;
00984 buffer[ 21 ] = 0;
00985
00986 if ( device()->writeBlock( buffer, 22 ) != 22 )
00987 return false;
00988
00989 if ( d->m_saveFile ) {
00990 d->m_saveFile->close();
00991 setDevice( 0 );
00992 delete d->m_saveFile;
00993 d->m_saveFile = 0;
00994 }
00995
00996
00997 return true;
00998 }
00999
01000
01001 bool KZip::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
01002 {
01003 mode_t mode = 0100644;
01004 time_t the_time = time(0);
01005 return KArchive::writeFile( name, user, group, size, mode, the_time,
01006 the_time, the_time, data );
01007 }
01008
01009
01010 bool KZip::writeFile( const QString& name, const QString& user,
01011 const QString& group, uint size, mode_t perm,
01012 time_t atime, time_t mtime, time_t ctime,
01013 const char* data ) {
01014 return KArchive::writeFile(name, user, group, size, perm, atime, mtime,
01015 ctime, data);
01016 }
01017
01018
01019 bool KZip::prepareWriting( const QString& name, const QString& user, const QString& group, uint size )
01020 {
01021 mode_t dflt_perm = 0100644;
01022 time_t the_time = time(0);
01023 return prepareWriting(name,user,group,size,dflt_perm,
01024 the_time,the_time,the_time);
01025 }
01026
01027
01028 bool KZip::prepareWriting(const QString& name, const QString& user,
01029 const QString& group, uint size, mode_t perm,
01030 time_t atime, time_t mtime, time_t ctime) {
01031 return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime);
01032 }
01033
01034 bool KZip::prepareWriting_impl(const QString &name, const QString &user,
01035 const QString &group, uint , mode_t perm,
01036 time_t atime, time_t mtime, time_t ctime) {
01037
01038 if ( !isOpened() )
01039 {
01040 qWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
01041 return false;
01042 }
01043
01044 if ( ! ( mode() & IO_WriteOnly ) )
01045 {
01046 qWarning( "KZip::writeFile: You must open the zip file for writing\n");
01047 return false;
01048 }
01049
01050 if ( !device() ) {
01051
01052 return false;
01053 }
01054
01055
01056 if ( !device()->at( d->m_offset ) ) {
01057 kdWarning(7040) << "prepareWriting_impl: cannot seek in ZIP file. Disk full?" << endl;
01058 abort();
01059 return false;
01060 }
01061
01062
01063
01064
01065
01066 QPtrListIterator<KZipFileEntry> it( d->m_fileList );
01067
01068
01069 for ( ; it.current() ; ++it )
01070 {
01071
01072 if (name == it.current()->path() )
01073 {
01074
01075 d->m_fileList.remove();
01076 }
01077
01078 }
01079
01080 KArchiveDirectory* parentDir = rootDir();
01081 QString fileName( name );
01082 int i = name.findRev( '/' );
01083 if ( i != -1 )
01084 {
01085 QString dir = name.left( i );
01086 fileName = name.mid( i + 1 );
01087
01088 parentDir = findOrCreate( dir );
01089 }
01090
01091
01092 KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, QString::null,
01093 name, device()->at() + 30 + name.length(),
01094 0 , d->m_compression, 0 );
01095 e->setHeaderStart( device()->at() );
01096
01097 parentDir->addEntry( e );
01098
01099 d->m_currentFile = e;
01100 d->m_fileList.append( e );
01101
01102 int extra_field_len = 0;
01103 if ( d->m_extraField == ModificationTime )
01104 extra_field_len = 17;
01105
01106
01107 QCString encodedName = QFile::encodeName(name);
01108 int bufferSize = extra_field_len + encodedName.length() + 30;
01109
01110 char* buffer = new char[ bufferSize ];
01111
01112 buffer[ 0 ] = 'P';
01113 buffer[ 1 ] = 'K';
01114 buffer[ 2 ] = 3;
01115 buffer[ 3 ] = 4;
01116
01117 buffer[ 4 ] = 0x14;
01118 buffer[ 5 ] = 0;
01119
01120 buffer[ 6 ] = 0;
01121 buffer[ 7 ] = 0;
01122
01123 buffer[ 8 ] = char(e->encoding());
01124 buffer[ 9 ] = char(e->encoding() >> 8);
01125
01126 transformToMsDos( e->datetime(), &buffer[ 10 ] );
01127
01128 buffer[ 14 ] = 'C';
01129 buffer[ 15 ] = 'R';
01130 buffer[ 16 ] = 'C';
01131 buffer[ 17 ] = 'q';
01132
01133 buffer[ 18 ] = 'C';
01134 buffer[ 19 ] = 'S';
01135 buffer[ 20 ] = 'I';
01136 buffer[ 21 ] = 'Z';
01137
01138 buffer[ 22 ] = 'U';
01139 buffer[ 23 ] = 'S';
01140 buffer[ 24 ] = 'I';
01141 buffer[ 25 ] = 'Z';
01142
01143 buffer[ 26 ] = (uchar)(encodedName.length());
01144 buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
01145
01146 buffer[ 28 ] = (uchar)(extra_field_len);
01147 buffer[ 29 ] = (uchar)(extra_field_len >> 8);
01148
01149
01150 strncpy( buffer + 30, encodedName, encodedName.length() );
01151
01152
01153 if ( d->m_extraField == ModificationTime )
01154 {
01155 char *extfield = buffer + 30 + encodedName.length();
01156
01157 extfield[0] = 'U';
01158 extfield[1] = 'T';
01159 extfield[2] = 13;
01160 extfield[3] = 0;
01161 extfield[4] = 1 | 2 | 4;
01162
01163 extfield[5] = char(mtime);
01164 extfield[6] = char(mtime >> 8);
01165 extfield[7] = char(mtime >> 16);
01166 extfield[8] = char(mtime >> 24);
01167
01168 extfield[9] = char(atime);
01169 extfield[10] = char(atime >> 8);
01170 extfield[11] = char(atime >> 16);
01171 extfield[12] = char(atime >> 24);
01172
01173 extfield[13] = char(ctime);
01174 extfield[14] = char(ctime >> 8);
01175 extfield[15] = char(ctime >> 16);
01176 extfield[16] = char(ctime >> 24);
01177 }
01178
01179
01180 bool b = (device()->writeBlock( buffer, bufferSize ) == bufferSize );
01181 d->m_crc = 0L;
01182 delete[] buffer;
01183
01184 Q_ASSERT( b );
01185 if (!b) {
01186 abort();
01187 return false;
01188 }
01189
01190
01191
01192 if ( d->m_compression == 0 ) {
01193 d->m_currentDev = device();
01194 return true;
01195 }
01196
01197 d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
01198 Q_ASSERT( d->m_currentDev );
01199 if ( !d->m_currentDev ) {
01200 abort();
01201 return false;
01202 }
01203 static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders();
01204
01205 b = d->m_currentDev->open( IO_WriteOnly );
01206 Q_ASSERT( b );
01207 return b;
01208 }
01209
01210 bool KZip::doneWriting( uint size )
01211 {
01212 if ( d->m_currentFile->encoding() == 8 ) {
01213
01214 (void)d->m_currentDev->writeBlock( 0, 0 );
01215 delete d->m_currentDev;
01216 }
01217
01218 d->m_currentDev = 0L;
01219
01220 Q_ASSERT( d->m_currentFile );
01221
01222
01223
01224 d->m_currentFile->setSize(size);
01225 int extra_field_len = 0;
01226 if ( d->m_extraField == ModificationTime )
01227 extra_field_len = 17;
01228
01229 int csize = device()->at() -
01230 d->m_currentFile->headerStart() - 30 -
01231 d->m_currentFile->path().length() - extra_field_len;
01232 d->m_currentFile->setCompressedSize(csize);
01233
01234
01235
01236
01237
01238 d->m_currentFile->setCRC32( d->m_crc );
01239
01240 d->m_currentFile = 0L;
01241
01242
01243 d->m_offset = device()->at();
01244 return true;
01245 }
01246
01247 bool KZip::writeSymLink(const QString &name, const QString &target,
01248 const QString &user, const QString &group,
01249 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01250 return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime);
01251 }
01252
01253 bool KZip::writeSymLink_impl(const QString &name, const QString &target,
01254 const QString &user, const QString &group,
01255 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01256
01257
01258
01259 perm |= S_IFLNK;
01260 Compression c = compression();
01261 setCompression(NoCompression);
01262
01263 if (!prepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) {
01264 kdWarning() << "KZip::writeFile prepareWriting failed" << endl;
01265 setCompression(c);
01266 return false;
01267 }
01268
01269 QCString symlink_target = QFile::encodeName(target);
01270 if (!writeData(symlink_target, symlink_target.length())) {
01271 kdWarning() << "KZip::writeFile writeData failed" << endl;
01272 setCompression(c);
01273 return false;
01274 }
01275
01276 if (!doneWriting(symlink_target.length())) {
01277 kdWarning() << "KZip::writeFile doneWriting failed" << endl;
01278 setCompression(c);
01279 return false;
01280 }
01281
01282 setCompression(c);
01283 return true;
01284 }
01285
01286 void KZip::virtual_hook( int id, void* data )
01287 {
01288 switch (id) {
01289 case VIRTUAL_WRITE_DATA: {
01290 WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data);
01291 params->retval = writeData_impl( params->data, params->size );
01292 break;
01293 }
01294 case VIRTUAL_WRITE_SYMLINK: {
01295 WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
01296 params->retval = writeSymLink_impl(*params->name,*params->target,
01297 *params->user,*params->group,params->perm,
01298 params->atime,params->mtime,params->ctime);
01299 break;
01300 }
01301 case VIRTUAL_PREPARE_WRITING: {
01302 PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
01303 params->retval = prepareWriting_impl(*params->name,*params->user,
01304 *params->group,params->size,params->perm,
01305 params->atime,params->mtime,params->ctime);
01306 break;
01307 }
01308 default:
01309 KArchive::virtual_hook( id, data );
01310 }
01311 }
01312
01313
01314 bool KZip::writeData(const char * c, uint i)
01315 {
01316 return KArchive::writeData( c, i );
01317 }
01318
01319 bool KZip::writeData_impl(const char * c, uint i)
01320 {
01321 Q_ASSERT( d->m_currentFile );
01322 Q_ASSERT( d->m_currentDev );
01323 if (!d->m_currentFile || !d->m_currentDev) {
01324 abort();
01325 return false;
01326 }
01327
01328
01329
01330 d->m_crc = crc32(d->m_crc, (const Bytef *) c , i);
01331
01332 Q_LONG written = d->m_currentDev->writeBlock( c, i );
01333
01334 bool ok = written == (Q_LONG)i;
01335 if ( !ok )
01336 abort();
01337 return ok;
01338 }
01339
01340 void KZip::setCompression( Compression c )
01341 {
01342 d->m_compression = ( c == NoCompression ) ? 0 : 8;
01343 }
01344
01345 KZip::Compression KZip::compression() const
01346 {
01347 return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
01348 }
01349
01350 void KZip::setExtraField( ExtraField ef )
01351 {
01352 d->m_extraField = ef;
01353 }
01354
01355 KZip::ExtraField KZip::extraField() const
01356 {
01357 return d->m_extraField;
01358 }
01359
01360 void KZip::abort()
01361 {
01362 if ( d->m_saveFile ) {
01363 d->m_saveFile->abort();
01364 setDevice( 0 );
01365 }
01366 }
01367
01368
01370
01371 QByteArray KZipFileEntry::data() const
01372 {
01373 QIODevice* dev = device();
01374 QByteArray arr;
01375 if ( dev ) {
01376 arr = dev->readAll();
01377 delete dev;
01378 }
01379 return arr;
01380 }
01381
01382 QIODevice* KZipFileEntry::device() const
01383 {
01384
01385
01386 KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
01387 if ( encoding() == 0 || compressedSize() == 0 )
01388 return limitedDev;
01389
01390 if ( encoding() == 8 )
01391 {
01392
01393 QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
01394 if ( !filterDev )
01395 return 0L;
01396 static_cast<KFilterDev *>(filterDev)->setSkipHeaders();
01397 bool b = filterDev->open( IO_ReadOnly );
01398 Q_ASSERT( b );
01399 return filterDev;
01400 }
01401
01402 kdError() << "This zip file contains files compressed with method "
01403 << encoding() <<", this method is currently not supported by KZip,"
01404 <<" please use a command-line tool to handle this file." << endl;
01405 return 0L;
01406 }