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 <kdebug.h>
00044
00045 #include <QtCore/QHash>
00046 #include <QtCore/QByteArray>
00047 #include <QtCore/QFile>
00048 #include <QtCore/QDir>
00049 #include <QtCore/QDate>
00050 #include <QtCore/QList>
00051
00052 #include <zlib.h>
00053 #include <time.h>
00054 #include <string.h>
00055
00056 const int max_path_len = 4095;
00057
00058 static void transformToMsDos(const QDateTime& dt, char* buffer)
00059 {
00060 if ( dt.isValid() )
00061 {
00062 const quint16 time =
00063 ( dt.time().hour() << 11 )
00064 | ( dt.time().minute() << 5 )
00065 | ( dt.time().second() >> 1 );
00066
00067 buffer[0] = char(time);
00068 buffer[1] = char(time >> 8);
00069
00070 const quint16 date =
00071 ( ( dt.date().year() - 1980 ) << 9 )
00072 | ( dt.date().month() << 5 )
00073 | ( dt.date().day() );
00074
00075 buffer[2] = char(date);
00076 buffer[3] = char(date >> 8);
00077 }
00078 else
00079 {
00080 buffer[0] = 0;
00081 buffer[1] = 0;
00082 buffer[2] = 33;
00083 buffer[3] = 0;
00084 }
00085 }
00086
00087 static time_t transformFromMsDos(const char* buffer)
00088 {
00089 quint16 time = (uchar)buffer[0] | ( (uchar)buffer[1] << 8 );
00090 int h = time >> 11;
00091 int m = ( time & 0x7ff ) >> 5;
00092 int s = ( time & 0x1f ) * 2 ;
00093 QTime qt(h, m, s);
00094
00095 quint16 date = (uchar)buffer[2] | ( (uchar)buffer[3] << 8 );
00096 int y = ( date >> 9 ) + 1980;
00097 int o = ( date & 0x1ff ) >> 5;
00098 int d = ( date & 0x1f );
00099 QDate qd(y, o, d);
00100
00101 QDateTime dt( qd, qt );
00102 return dt.toTime_t();
00103 }
00104
00105
00106
00108 struct ParseFileInfo {
00109
00110 mode_t perm;
00111 time_t atime;
00112 time_t mtime;
00113 time_t ctime;
00114 int uid;
00115 int gid;
00116 QByteArray guessed_symlink;
00117 int extralen;
00118
00119
00120 bool exttimestamp_seen;
00121
00122 bool newinfounix_seen;
00123
00124
00125 ParseFileInfo() : perm(0100644), uid(-1), gid(-1), extralen(0),
00126 exttimestamp_seen(false), newinfounix_seen(false) {
00127 ctime = mtime = atime = time(0);
00128 }
00129 };
00130
00139 static bool parseExtTimestamp(const char *buffer, int size, bool islocal,
00140 ParseFileInfo &pfi) {
00141 if (size < 1) {
00142 kDebug(7040) << "premature end of extended timestamp (#1)";
00143 return false;
00144 }
00145 int flags = *buffer;
00146 buffer += 1;
00147 size -= 1;
00148
00149 if (flags & 1) {
00150 if (size < 4) {
00151 kDebug(7040) << "premature end of extended timestamp (#2)";
00152 return false;
00153 }
00154 pfi.mtime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00155 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00156 buffer += 4;
00157 size -= 4;
00158 }
00159
00160
00161 if (!islocal) {
00162 pfi.exttimestamp_seen = true;
00163 return true;
00164 }
00165
00166 if (flags & 2) {
00167 if (size < 4) {
00168 kDebug(7040) << "premature end of extended timestamp (#3)";
00169 return true;
00170 }
00171 pfi.atime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00172 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00173 buffer += 4;
00174 size -= 4;
00175 }
00176
00177 if (flags & 4) {
00178 if (size < 4) {
00179 kDebug(7040) << "premature end of extended timestamp (#4)";
00180 return true;
00181 }
00182 pfi.ctime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00183 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00184 buffer += 4;
00185 }
00186
00187 pfi.exttimestamp_seen = true;
00188 return true;
00189 }
00190
00199 static bool parseInfoZipUnixOld(const char *buffer, int size, bool islocal,
00200 ParseFileInfo &pfi) {
00201
00202 if (pfi.exttimestamp_seen || pfi.newinfounix_seen) return true;
00203
00204 if (size < 8) {
00205 kDebug(7040) << "premature end of Info-ZIP unix extra field old";
00206 return false;
00207 }
00208
00209 pfi.atime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00210 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00211 buffer += 4;
00212 pfi.mtime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00213 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00214 buffer += 4;
00215 if (islocal && size >= 12) {
00216 pfi.uid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00217 buffer += 2;
00218 pfi.gid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00219 buffer += 2;
00220 }
00221 return true;
00222 }
00223
00224 #if 0 // not needed yet
00225
00233 static bool parseInfoZipUnixNew(const char *buffer, int size, bool islocal,
00234 ParseFileInfo &pfi) {
00235 if (!islocal) {
00236 pfi.newinfounix = true;
00237 return true;
00238 }
00239
00240 if (size < 4) {
00241 kDebug(7040) << "premature end of Info-ZIP unix extra field new";
00242 return false;
00243 }
00244
00245 pfi.uid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00246 buffer += 2;
00247 pfi.gid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00248 buffer += 2;
00249
00250 pfi.newinfounix = true;
00251 return true;
00252 }
00253 #endif
00254
00263 static bool parseExtraField(const char *buffer, int size, bool islocal,
00264 ParseFileInfo &pfi) {
00265
00266
00267 if (!islocal) return true;
00268
00269 while (size >= 4) {
00270 int magic = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00271 buffer += 2;
00272 int fieldsize = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00273 buffer += 2;
00274 size -= 4;
00275
00276 if (fieldsize > size) {
00277
00278 kDebug(7040) << "premature end of extra fields reached";
00279 break;
00280 }
00281
00282 switch (magic) {
00283 case 0x5455:
00284 if (!parseExtTimestamp(buffer, fieldsize, islocal, pfi)) return false;
00285 break;
00286 case 0x5855:
00287 if (!parseInfoZipUnixOld(buffer, fieldsize, islocal, pfi)) return false;
00288 break;
00289 #if 0 // not needed yet
00290 case 0x7855:
00291 if (!parseInfoZipUnixNew(buffer, fieldsize, islocal, pfi)) return false;
00292 break;
00293 #endif
00294 default:
00295 ;
00296 }
00297
00298 buffer += fieldsize;
00299 size -= fieldsize;
00300 }
00301 return true;
00302 }
00303
00307
00308 class KZip::KZipPrivate
00309 {
00310 public:
00311 KZipPrivate()
00312 : m_crc( 0 ),
00313 m_currentFile( 0 ),
00314 m_currentDev( 0 ),
00315 m_compression( 8 ),
00316 m_extraField( KZip::NoExtraField ),
00317 m_offset( 0 )
00318 {}
00319
00320 unsigned long m_crc;
00321 KZipFileEntry* m_currentFile;
00322 QIODevice* m_currentDev;
00323 QList<KZipFileEntry*> m_fileList;
00324 int m_compression;
00325 KZip::ExtraField m_extraField;
00326
00327
00328
00329
00330 unsigned int m_offset;
00331 };
00332
00333 KZip::KZip( const QString& fileName )
00334 : KArchive( fileName ),d(new KZipPrivate)
00335 {
00336 }
00337
00338 KZip::KZip( QIODevice * dev )
00339 : KArchive( dev ),d(new KZipPrivate)
00340 {
00341 }
00342
00343 KZip::~KZip()
00344 {
00345
00346 if( isOpen() )
00347 close();
00348 delete d;
00349 }
00350
00351 bool KZip::openArchive( QIODevice::OpenMode mode )
00352 {
00353
00354 d->m_fileList.clear();
00355
00356 if ( mode == QIODevice::WriteOnly )
00357 return true;
00358
00359 char buffer[47];
00360
00361
00362
00363
00364 uint offset = 0;
00365 int n;
00366
00367
00368 QHash<QByteArray, ParseFileInfo> pfi_map;
00369
00370 QIODevice* dev = device();
00371
00372
00373 bool startOfFile = true;
00374
00375 for (;;)
00376 {
00377
00378
00379 n = dev->read( buffer, 4 );
00380
00381 if (n < 4)
00382 {
00383 kWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#1)";
00384
00385 return false;
00386 }
00387
00388 if ( !memcmp( buffer, "PK\5\6", 4 ) )
00389 {
00390
00391 startOfFile = false;
00392 break;
00393 }
00394
00395 if ( !memcmp( buffer, "PK\3\4", 4 ) )
00396 {
00397
00398 startOfFile = false;
00399
00400 dev->seek( dev->pos() + 2 );
00401
00402
00403 n = dev->read( buffer, 24 );
00404 if (n < 24) {
00405 kWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#4)";
00406 return false;
00407 }
00408
00409 int gpf = (uchar)buffer[0];
00410 int compression_mode = (uchar)buffer[2] | (uchar)buffer[3] << 8;
00411 time_t mtime = transformFromMsDos( buffer+4 );
00412
00413 qint64 compr_size = (uchar)buffer[12] | (uchar)buffer[13] << 8
00414 | (uchar)buffer[14] << 16 | (uchar)buffer[15] << 24;
00415 qint64 uncomp_size = (uchar)buffer[16] | (uchar)buffer[17] << 8
00416 | (uchar)buffer[18] << 16 | (uchar)buffer[19] << 24;
00417 int namelen = (uchar)buffer[20] | (uchar)buffer[21] << 8;
00418 int extralen = (uchar)buffer[22] | (uchar)buffer[23] << 8;
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430 Q_ASSERT( namelen > 0 );
00431 QByteArray fileName = dev->read(namelen);
00432 if ( fileName.size() < namelen ) {
00433 kWarning(7040) << "Invalid ZIP file. Name not completely read (#2)";
00434 return false;
00435 }
00436
00437 ParseFileInfo pfi;
00438 pfi.mtime = mtime;
00439
00440
00441
00442 unsigned int extraFieldEnd = dev->pos() + extralen;
00443 pfi.extralen = extralen;
00444 int handledextralen = qMin(extralen, (int)sizeof buffer);
00445
00446
00447
00448
00449 n = dev->read(buffer, handledextralen);
00450
00451 if (!parseExtraField(buffer, handledextralen, true, pfi))
00452 {
00453 kWarning(7040) << "Invalid ZIP File. Broken ExtraField.";
00454 return false;
00455 }
00456
00457
00458 dev->seek( extraFieldEnd );
00459
00460
00461
00462
00463 if ( gpf & 8 )
00464 {
00465
00466
00467 kDebug(7040) << "trying to seek for next PK78";
00468 bool foundSignature = false;
00469
00470 while (!foundSignature)
00471 {
00472 n = dev->read( buffer, 1 );
00473 if (n < 1)
00474 {
00475 kWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)";
00476 return false;
00477 }
00478
00479 if ( buffer[0] != 'P' )
00480 continue;
00481
00482 n = dev->read( buffer, 3 );
00483 if (n < 3)
00484 {
00485 kWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)";
00486 return false;
00487 }
00488
00489
00490
00491
00492
00493
00494 if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
00495 {
00496 foundSignature = true;
00497 dev->seek( dev->pos() + 12 );
00498 }
00499 else if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 )
00500 || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) )
00501 {
00502 foundSignature = true;
00503 dev->seek( dev->pos() - 4 );
00504 }
00505 else if ( buffer[0] == 'P' || buffer[1] == 'P' || buffer[2] == 'P' )
00506 {
00507
00508 dev->seek( dev->pos() - 3 );
00509 }
00510
00511 }
00512 }
00513 else
00514 {
00515
00516
00517
00518 if (compression_mode == NoCompression
00519 && uncomp_size <= max_path_len
00520 && uncomp_size > 0) {
00521
00522
00523 pfi.guessed_symlink = dev->read(uncomp_size);
00524 if (pfi.guessed_symlink.size() < uncomp_size) {
00525 kWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#5)";
00526 return false;
00527 }
00528 } else {
00529
00530 if ( compr_size > dev->size() )
00531 {
00532
00533
00534 bool foundSignature = false;
00535
00536 while (!foundSignature)
00537 {
00538 n = dev->read( buffer, 1 );
00539 if (n < 1)
00540 {
00541 kWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)";
00542 return false;
00543 }
00544
00545 if ( buffer[0] != 'P' )
00546 continue;
00547
00548 n = dev->read( buffer, 3 );
00549 if (n < 3)
00550 {
00551 kWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)";
00552 return false;
00553 }
00554
00555
00556
00557
00558
00559
00560 if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
00561 {
00562 foundSignature = true;
00563 dev->seek( dev->pos() + 12 );
00564 }
00565
00566 if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 )
00567 || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) )
00568 {
00569 foundSignature = true;
00570 dev->seek( dev->pos() - 4 );
00571
00572
00573 }
00574 }
00575 }
00576 else
00577 {
00578
00579 bool success = dev->seek( dev->pos() + compr_size );
00580 Q_ASSERT( success );
00581
00582
00583
00584
00585
00586 }
00587
00588 }
00589
00590
00591
00592
00593
00594
00595 }
00596 pfi_map.insert(fileName, pfi);
00597 }
00598 else if ( !memcmp( buffer, "PK\1\2", 4 ) )
00599 {
00600
00601 startOfFile = false;
00602
00603
00604
00605
00606 offset = dev->pos() - 4;
00607
00608
00609 if ( d->m_offset == 0L ) d->m_offset = offset;
00610
00611 n = dev->read( buffer + 4, 42 );
00612 if (n < 42) {
00613 kWarning(7040) << "Invalid ZIP file, central entry too short";
00614 return false;
00615 }
00616
00617
00618
00619
00620 int namelen = (uchar)buffer[29] << 8 | (uchar)buffer[28];
00621 Q_ASSERT( namelen > 0 );
00622 QByteArray bufferName = dev->read( namelen );
00623 if ( bufferName.size() < namelen )
00624 kWarning(7040) << "Invalid ZIP file. Name not completely read";
00625
00626 ParseFileInfo pfi = pfi_map.value( bufferName, ParseFileInfo() );
00627
00628 QString name( QFile::decodeName(bufferName) );
00629
00630
00631
00632
00633 int extralen = (uchar)buffer[31] << 8 | (uchar)buffer[30];
00634
00635 int commlen = (uchar)buffer[33] << 8 | (uchar)buffer[32];
00636
00637 int cmethod = (uchar)buffer[11] << 8 | (uchar)buffer[10];
00638
00639
00640
00641
00642
00643 uint crc32 = (uchar)buffer[19] << 24 | (uchar)buffer[18] << 16 |
00644 (uchar)buffer[17] << 8 | (uchar)buffer[16];
00645
00646
00647 uint ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 |
00648 (uchar)buffer[25] << 8 | (uchar)buffer[24];
00649
00650 uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 |
00651 (uchar)buffer[21] << 8 | (uchar)buffer[20];
00652
00653
00654 uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 |
00655 (uchar)buffer[43] << 8 | (uchar)buffer[42];
00656
00657
00658
00659
00660
00661 int localextralen = pfi.extralen;
00662
00663
00664
00665
00666
00667 uint dataoffset = localheaderoffset + 30 + localextralen + namelen;
00668
00669
00670
00671
00672
00673 int os_madeby = (uchar)buffer[5];
00674 bool isdir = false;
00675 int access = 0100644;
00676
00677 if (os_madeby == 3) {
00678 access = (uchar)buffer[40] | (uchar)buffer[41] << 8;
00679 }
00680
00681 QString entryName;
00682
00683 if ( name.endsWith( '/' ) )
00684 {
00685 isdir = true;
00686 name = name.left( name.length() - 1 );
00687 if (os_madeby != 3) access = S_IFDIR | 0755;
00688 else Q_ASSERT(access & S_IFDIR);
00689 }
00690
00691 int pos = name.lastIndexOf( '/' );
00692 if ( pos == -1 )
00693 entryName = name;
00694 else
00695 entryName = name.mid( pos + 1 );
00696 Q_ASSERT( !entryName.isEmpty() );
00697
00698 KArchiveEntry* entry;
00699 if ( isdir )
00700 {
00701 QString path = QDir::cleanPath( name );
00702 const KArchiveEntry* ent = rootDir()->entry( path );
00703 if ( ent && ent->isDirectory() )
00704 {
00705
00706 entry = 0;
00707 }
00708 else
00709 {
00710 entry = new KArchiveDirectory( this, entryName, access, (int)pfi.mtime, rootDir()->user(), rootDir()->group(), QString() );
00711
00712 }
00713 }
00714 else
00715 {
00716 QString symlink;
00717 if (S_ISLNK(access)) {
00718 symlink = QFile::decodeName(pfi.guessed_symlink);
00719 }
00720 entry = new KZipFileEntry( this, entryName, access, pfi.mtime,
00721 rootDir()->user(), rootDir()->group(),
00722 symlink, name, dataoffset,
00723 ucsize, cmethod, csize );
00724 static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset );
00725 static_cast<KZipFileEntry*>(entry)->setCRC32(crc32);
00726
00727 d->m_fileList.append( static_cast<KZipFileEntry *>( entry ) );
00728 }
00729
00730 if ( entry )
00731 {
00732 if ( pos == -1 )
00733 {
00734 rootDir()->addEntry(entry);
00735 }
00736 else
00737 {
00738
00739 QString path = QDir::cleanPath( name.left( pos ) );
00740
00741 KArchiveDirectory * tdir = findOrCreate( path );
00742 tdir->addEntry(entry);
00743 }
00744 }
00745
00746
00747 offset += 46 + commlen + extralen + namelen;
00748 bool b = dev->seek(offset);
00749 Q_ASSERT( b );
00750 if ( !b )
00751 return false;
00752 }
00753 else if ( startOfFile )
00754 {
00755
00756
00757 kDebug(7040) << "Try to skip start of file";
00758 startOfFile = false;
00759 bool foundSignature = false;
00760
00761 while (!foundSignature)
00762 {
00763 n = dev->read( buffer, 1 );
00764 if (n < 1)
00765 {
00766 kWarning(7040) << "Invalid ZIP file. Unexpected end of file. " ;
00767 return false;
00768 }
00769
00770 if ( buffer[0] != 'P' )
00771 continue;
00772
00773 n = dev->read( buffer, 3 );
00774 if (n < 3)
00775 {
00776 kWarning(7040) << "Invalid ZIP file. Unexpected end of file. " ;
00777 return false;
00778 }
00779
00780
00781
00782
00783
00784
00785 if ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 )
00786 {
00787 foundSignature = true;
00788 dev->seek( dev->pos() - 4 );
00789 }
00790 else if ( buffer[0] == 'P' || buffer[1] == 'P' || buffer[2] == 'P' )
00791 {
00792
00793 dev->seek( dev->pos() - 3 );
00794 }
00795 }
00796 }
00797 else
00798 {
00799 kWarning(7040) << "Invalid ZIP file. Unrecognized header at offset " << offset;
00800
00801 return false;
00802 }
00803 }
00804
00805 return true;
00806 }
00807
00808 bool KZip::closeArchive()
00809 {
00810 if ( ! ( mode() & QIODevice::WriteOnly ) )
00811 {
00812
00813 return true;
00814 }
00815
00816
00817
00818
00819
00820 char buffer[ 22 ];
00821 uLong crc = crc32(0L, Z_NULL, 0);
00822
00823 qint64 centraldiroffset = device()->pos();
00824
00825 qint64 atbackup = centraldiroffset;
00826 QMutableListIterator<KZipFileEntry*> it( d->m_fileList );
00827
00828 while(it.hasNext())
00829 {
00830 it.next();
00831 if ( !device()->seek( it.value()->headerStart() + 14 ) )
00832 return false;
00833
00834
00835
00836
00837 uLong mycrc = it.value()->crc32();
00838 buffer[0] = char(mycrc);
00839 buffer[1] = char(mycrc >> 8);
00840 buffer[2] = char(mycrc >> 16);
00841 buffer[3] = char(mycrc >> 24);
00842
00843 int mysize1 = it.value()->compressedSize();
00844 buffer[4] = char(mysize1);
00845 buffer[5] = char(mysize1 >> 8);
00846 buffer[6] = char(mysize1 >> 16);
00847 buffer[7] = char(mysize1 >> 24);
00848
00849 int myusize = it.value()->size();
00850 buffer[8] = char(myusize);
00851 buffer[9] = char(myusize >> 8);
00852 buffer[10] = char(myusize >> 16);
00853 buffer[11] = char(myusize >> 24);
00854
00855 if ( device()->write( buffer, 12 ) != 12 )
00856 return false;
00857 }
00858 device()->seek( atbackup );
00859
00860 it.toFront();
00861 while (it.hasNext())
00862 {
00863 it.next();
00864
00865
00866
00867 QByteArray path = QFile::encodeName(it.value()->path());
00868
00869 const int extra_field_len = 9;
00870 int bufferSize = extra_field_len + path.length() + 46;
00871 char* buffer = new char[ bufferSize ];
00872
00873 memset(buffer, 0, 46);
00874
00875 const char head[] =
00876 {
00877 'P', 'K', 1, 2,
00878 0x14, 3,
00879 0x14, 0
00880 };
00881
00882
00883
00884 memmove(buffer, head, sizeof(head));
00885
00886 buffer[ 10 ] = char(it.value()->encoding());
00887 buffer[ 11 ] = char(it.value()->encoding() >> 8);
00888
00889 transformToMsDos( it.value()->datetime(), &buffer[ 12 ] );
00890
00891 uLong mycrc = it.value()->crc32();
00892 buffer[ 16 ] = char(mycrc);
00893 buffer[ 17 ] = char(mycrc >> 8);
00894 buffer[ 18 ] = char(mycrc >> 16);
00895 buffer[ 19 ] = char(mycrc >> 24);
00896
00897 int mysize1 = it.value()->compressedSize();
00898 buffer[ 20 ] = char(mysize1);
00899 buffer[ 21 ] = char(mysize1 >> 8);
00900 buffer[ 22 ] = char(mysize1 >> 16);
00901 buffer[ 23 ] = char(mysize1 >> 24);
00902
00903 int mysize = it.value()->size();
00904 buffer[ 24 ] = char(mysize);
00905 buffer[ 25 ] = char(mysize >> 8);
00906 buffer[ 26 ] = char(mysize >> 16);
00907 buffer[ 27 ] = char(mysize >> 24);
00908
00909 buffer[ 28 ] = char(path.length());
00910 buffer[ 29 ] = char(path.length() >> 8);
00911
00912 buffer[ 30 ] = char(extra_field_len);
00913 buffer[ 31 ] = char(extra_field_len >> 8);
00914
00915 buffer[ 40 ] = char(it.value()->permissions());
00916 buffer[ 41 ] = char(it.value()->permissions() >> 8);
00917
00918 int myhst = it.value()->headerStart();
00919 buffer[ 42 ] = char(myhst);
00920 buffer[ 43 ] = char(myhst >> 8);
00921 buffer[ 44 ] = char(myhst >> 16);
00922 buffer[ 45 ] = char(myhst >> 24);
00923
00924
00925 strncpy( buffer + 46, path, path.length() );
00926
00927
00928
00929 char *extfield = buffer + 46 + path.length();
00930 extfield[0] = 'U';
00931 extfield[1] = 'T';
00932 extfield[2] = 5;
00933 extfield[3] = 0;
00934 extfield[4] = 1 | 2 | 4;
00935
00936
00937 unsigned long time = (unsigned long)it.value()->date();
00938 extfield[5] = char(time);
00939 extfield[6] = char(time >> 8);
00940 extfield[7] = char(time >> 16);
00941 extfield[8] = char(time >> 24);
00942
00943 crc = crc32(crc, (Bytef *)buffer, bufferSize );
00944 bool ok = ( device()->write( buffer, bufferSize ) == bufferSize );
00945 delete[] buffer;
00946 if ( !ok )
00947 return false;
00948 }
00949 qint64 centraldirendoffset = device()->pos();
00950
00951
00952
00953
00954 buffer[ 0 ] = 'P';
00955 buffer[ 1 ] = 'K';
00956 buffer[ 2 ] = 5;
00957 buffer[ 3 ] = 6;
00958
00959 buffer[ 4 ] = 0;
00960 buffer[ 5 ] = 0;
00961
00962 buffer[ 6 ] = 0;
00963 buffer[ 7 ] = 0;
00964
00965 int count = d->m_fileList.count();
00966
00967
00968
00969 buffer[ 8 ] = char(count);
00970 buffer[ 9 ] = char(count >> 8);
00971
00972 buffer[ 10 ] = buffer[ 8 ];
00973 buffer[ 11 ] = buffer[ 9 ];
00974
00975 int cdsize = centraldirendoffset - centraldiroffset;
00976 buffer[ 12 ] = char(cdsize);
00977 buffer[ 13 ] = char(cdsize >> 8);
00978 buffer[ 14 ] = char(cdsize >> 16);
00979 buffer[ 15 ] = char(cdsize >> 24);
00980
00981
00982
00983
00984 buffer[ 16 ] = char(centraldiroffset);
00985 buffer[ 17 ] = char(centraldiroffset >> 8);
00986 buffer[ 18 ] = char(centraldiroffset >> 16);
00987 buffer[ 19 ] = char(centraldiroffset >> 24);
00988
00989 buffer[ 20 ] = 0;
00990 buffer[ 21 ] = 0;
00991
00992 if ( device()->write( buffer, 22 ) != 22 )
00993 return false;
00994
00995 return true;
00996 }
00997
00998 bool KZip::doWriteDir( const QString&, const QString&, const QString&,
00999 mode_t, time_t, time_t, time_t ) {
01000 return true;
01001 }
01002
01003 bool KZip::doPrepareWriting(const QString &name, const QString &user,
01004 const QString &group, qint64 , mode_t perm,
01005 time_t atime, time_t mtime, time_t ctime) {
01006
01007 if ( !isOpen() )
01008 {
01009 qWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
01010 return false;
01011 }
01012
01013 if ( ! ( mode() & QIODevice::WriteOnly ) )
01014 {
01015 qWarning( "KZip::writeFile: You must open the zip file for writing\n");
01016 return false;
01017 }
01018
01019 Q_ASSERT( device() );
01020
01021
01022 if ( !device()->seek( d->m_offset ) ) {
01023 kWarning(7040) << "doPrepareWriting: cannot seek in ZIP file. Disk full?";
01024 return false;
01025 }
01026
01027
01028
01029
01030
01031 QMutableListIterator<KZipFileEntry*> it( d->m_fileList );
01032
01033 while(it.hasNext())
01034 {
01035 it.next();
01036
01037 if (name == it.value()->path() )
01038 {
01039
01040 delete it.value();
01041 it.remove();
01042 }
01043
01044 }
01045
01046 KArchiveDirectory* parentDir = rootDir();
01047 QString fileName( name );
01048 int i = name.lastIndexOf( '/' );
01049 if ( i != -1 )
01050 {
01051 QString dir = name.left( i );
01052 fileName = name.mid( i + 1 );
01053
01054 parentDir = findOrCreate( dir );
01055 }
01056
01057
01058 KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, QString(),
01059 name, device()->pos() + 30 + name.length(),
01060 0 , d->m_compression, 0 );
01061 e->setHeaderStart( device()->pos() );
01062
01063 parentDir->addEntry( e );
01064
01065 d->m_currentFile = e;
01066 d->m_fileList.append( e );
01067
01068 int extra_field_len = 0;
01069 if ( d->m_extraField == ModificationTime )
01070 extra_field_len = 17;
01071
01072
01073 QByteArray encodedName = QFile::encodeName(name);
01074 int bufferSize = extra_field_len + encodedName.length() + 30;
01075
01076 char* buffer = new char[ bufferSize ];
01077
01078 buffer[ 0 ] = 'P';
01079 buffer[ 1 ] = 'K';
01080 buffer[ 2 ] = 3;
01081 buffer[ 3 ] = 4;
01082
01083 buffer[ 4 ] = 0x14;
01084 buffer[ 5 ] = 0;
01085
01086 buffer[ 6 ] = 0;
01087 buffer[ 7 ] = 0;
01088
01089 buffer[ 8 ] = char(e->encoding());
01090 buffer[ 9 ] = char(e->encoding() >> 8);
01091
01092 transformToMsDos( e->datetime(), &buffer[ 10 ] );
01093
01094 buffer[ 14 ] = 'C';
01095 buffer[ 15 ] = 'R';
01096 buffer[ 16 ] = 'C';
01097 buffer[ 17 ] = 'q';
01098
01099 buffer[ 18 ] = 'C';
01100 buffer[ 19 ] = 'S';
01101 buffer[ 20 ] = 'I';
01102 buffer[ 21 ] = 'Z';
01103
01104 buffer[ 22 ] = 'U';
01105 buffer[ 23 ] = 'S';
01106 buffer[ 24 ] = 'I';
01107 buffer[ 25 ] = 'Z';
01108
01109 buffer[ 26 ] = (uchar)(encodedName.length());
01110 buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
01111
01112 buffer[ 28 ] = (uchar)(extra_field_len);
01113 buffer[ 29 ] = (uchar)(extra_field_len >> 8);
01114
01115
01116 strncpy( buffer + 30, encodedName, encodedName.length() );
01117
01118
01119 if ( d->m_extraField == ModificationTime )
01120 {
01121 char *extfield = buffer + 30 + encodedName.length();
01122
01123 extfield[0] = 'U';
01124 extfield[1] = 'T';
01125 extfield[2] = 13;
01126 extfield[3] = 0;
01127 extfield[4] = 1 | 2 | 4;
01128
01129 extfield[5] = char(mtime);
01130 extfield[6] = char(mtime >> 8);
01131 extfield[7] = char(mtime >> 16);
01132 extfield[8] = char(mtime >> 24);
01133
01134 extfield[9] = char(atime);
01135 extfield[10] = char(atime >> 8);
01136 extfield[11] = char(atime >> 16);
01137 extfield[12] = char(atime >> 24);
01138
01139 extfield[13] = char(ctime);
01140 extfield[14] = char(ctime >> 8);
01141 extfield[15] = char(ctime >> 16);
01142 extfield[16] = char(ctime >> 24);
01143 }
01144
01145
01146 bool b = (device()->write( buffer, bufferSize ) == bufferSize );
01147 d->m_crc = 0L;
01148 delete[] buffer;
01149
01150 Q_ASSERT( b );
01151 if (!b) {
01152 return false;
01153 }
01154
01155
01156
01157 if ( d->m_compression == 0 ) {
01158 d->m_currentDev = device();
01159 return true;
01160 }
01161
01162 d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
01163 Q_ASSERT( d->m_currentDev );
01164 if ( !d->m_currentDev ) {
01165 return false;
01166 }
01167 static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders();
01168
01169 b = d->m_currentDev->open( QIODevice::WriteOnly );
01170 Q_ASSERT( b );
01171 return b;
01172 }
01173
01174 bool KZip::doFinishWriting( qint64 size )
01175 {
01176 if ( d->m_currentFile->encoding() == 8 ) {
01177
01178 (void)d->m_currentDev->write( 0, 0 );
01179 delete d->m_currentDev;
01180 }
01181
01182 d->m_currentDev = 0L;
01183
01184 Q_ASSERT( d->m_currentFile );
01185
01186
01187 d->m_currentFile->setSize(size);
01188 int extra_field_len = 0;
01189 if ( d->m_extraField == ModificationTime )
01190 extra_field_len = 17;
01191
01192 const QByteArray encodedName = QFile::encodeName(d->m_currentFile->path());
01193 int csize = device()->pos() -
01194 d->m_currentFile->headerStart() - 30 -
01195 encodedName.length() - extra_field_len;
01196 d->m_currentFile->setCompressedSize(csize);
01197
01198
01199
01200
01201
01202 d->m_currentFile->setCRC32( d->m_crc );
01203
01204 d->m_currentFile = 0L;
01205
01206
01207 d->m_offset = device()->pos();
01208 return true;
01209 }
01210
01211 bool KZip::doWriteSymLink(const QString &name, const QString &target,
01212 const QString &user, const QString &group,
01213 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01214
01215
01216 perm |= S_IFLNK;
01217 Compression c = compression();
01218 setCompression(NoCompression);
01219
01220 if (!doPrepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) {
01221 kWarning() << "prepareWriting failed";
01222 setCompression(c);
01223 return false;
01224 }
01225
01226 QByteArray symlink_target = QFile::encodeName(target);
01227 if (!writeData(symlink_target, symlink_target.length())) {
01228 kWarning() << "writeData failed";
01229 setCompression(c);
01230 return false;
01231 }
01232
01233 if (!finishWriting(symlink_target.length())) {
01234 kWarning() << "finishWriting failed";
01235 setCompression(c);
01236 return false;
01237 }
01238
01239 setCompression(c);
01240 return true;
01241 }
01242
01243 void KZip::virtual_hook( int id, void* data )
01244 {
01245 KArchive::virtual_hook( id, data );
01246 }
01247
01248 bool KZip::writeData(const char * data, qint64 size)
01249 {
01250 Q_ASSERT( d->m_currentFile );
01251 Q_ASSERT( d->m_currentDev );
01252 if (!d->m_currentFile || !d->m_currentDev) {
01253 return false;
01254 }
01255
01256
01257
01258 d->m_crc = crc32(d->m_crc, (const Bytef *) data , size);
01259
01260 qint64 written = d->m_currentDev->write( data, size );
01261
01262 return written == size;
01263 }
01264
01265 void KZip::setCompression( Compression c )
01266 {
01267 d->m_compression = ( c == NoCompression ) ? 0 : 8;
01268 }
01269
01270 KZip::Compression KZip::compression() const
01271 {
01272 return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
01273 }
01274
01275 void KZip::setExtraField( ExtraField ef )
01276 {
01277 d->m_extraField = ef;
01278 }
01279
01280 KZip::ExtraField KZip::extraField() const
01281 {
01282 return d->m_extraField;
01283 }
01284
01288 class KZipFileEntry::KZipFileEntryPrivate
01289 {
01290 public:
01291 KZipFileEntryPrivate()
01292 : crc(0),
01293 compressedSize(0),
01294 headerStart(0),
01295 encoding(0)
01296 {}
01297 unsigned long crc;
01298 qint64 compressedSize;
01299 qint64 headerStart;
01300 int encoding;
01301 QString path;
01302 };
01303
01304 KZipFileEntry::KZipFileEntry(KZip* zip, const QString& name, int access, int date,
01305 const QString& user, const QString& group, const QString& symlink,
01306 const QString& path, qint64 start, qint64 uncompressedSize,
01307 int encoding, qint64 compressedSize)
01308 : KArchiveFile(zip, name, access, date, user, group, symlink, start, uncompressedSize ),
01309 d(new KZipFileEntryPrivate)
01310 {
01311 d->path = path;
01312 d->encoding = encoding;
01313 d->compressedSize = compressedSize;
01314 }
01315
01316 KZipFileEntry::~KZipFileEntry()
01317 {
01318 delete d;
01319 }
01320
01321 int KZipFileEntry::encoding() const
01322 {
01323 return d->encoding;
01324 }
01325
01326 qint64 KZipFileEntry::compressedSize() const
01327 {
01328 return d->compressedSize;
01329 }
01330
01331 void KZipFileEntry::setCompressedSize(qint64 compressedSize)
01332 {
01333 d->compressedSize = compressedSize;
01334 }
01335
01336 void KZipFileEntry::setHeaderStart(qint64 headerstart)
01337 {
01338 d->headerStart = headerstart;
01339 }
01340
01341 qint64 KZipFileEntry::headerStart() const
01342 {
01343 return d->headerStart;
01344 }
01345
01346 unsigned long KZipFileEntry::crc32() const
01347 {
01348 return d->crc;
01349 }
01350
01351 void KZipFileEntry::setCRC32(unsigned long crc32)
01352 {
01353 d->crc=crc32;
01354 }
01355
01356 const QString &KZipFileEntry::path() const
01357 {
01358 return d->path;
01359 }
01360
01361 QByteArray KZipFileEntry::data() const
01362 {
01363 QIODevice* dev = createDevice();
01364 QByteArray arr;
01365 if ( dev ) {
01366 arr = dev->readAll();
01367 delete dev;
01368 }
01369 return arr;
01370 }
01371
01372 QIODevice* KZipFileEntry::createDevice() const
01373 {
01374
01375
01376 KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
01377 if ( encoding() == 0 || compressedSize() == 0 )
01378 return limitedDev;
01379
01380 if ( encoding() == 8 )
01381 {
01382
01383 QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
01384 if ( !filterDev )
01385 return 0L;
01386 static_cast<KFilterDev *>(filterDev)->setSkipHeaders();
01387 bool b = filterDev->open( QIODevice::ReadOnly );
01388 Q_ASSERT( b );
01389 return filterDev;
01390 }
01391
01392 kError() << "This zip file contains files compressed with method"
01393 << encoding() << ", this method is currently not supported by KZip,"
01394 << "please use a command-line tool to handle this file.";
01395 return 0L;
01396 }