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