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(it.value()->path().length());
00910 buffer[ 29 ] = char(it.value()->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 int csize = device()->pos() -
01193 d->m_currentFile->headerStart() - 30 -
01194 d->m_currentFile->path().length() - extra_field_len;
01195 d->m_currentFile->setCompressedSize(csize);
01196
01197
01198
01199
01200
01201 d->m_currentFile->setCRC32( d->m_crc );
01202
01203 d->m_currentFile = 0L;
01204
01205
01206 d->m_offset = device()->pos();
01207 return true;
01208 }
01209
01210 bool KZip::doWriteSymLink(const QString &name, const QString &target,
01211 const QString &user, const QString &group,
01212 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01213
01214
01215 perm |= S_IFLNK;
01216 Compression c = compression();
01217 setCompression(NoCompression);
01218
01219 if (!doPrepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) {
01220 kWarning() << "prepareWriting failed";
01221 setCompression(c);
01222 return false;
01223 }
01224
01225 QByteArray symlink_target = QFile::encodeName(target);
01226 if (!writeData(symlink_target, symlink_target.length())) {
01227 kWarning() << "writeData failed";
01228 setCompression(c);
01229 return false;
01230 }
01231
01232 if (!finishWriting(symlink_target.length())) {
01233 kWarning() << "finishWriting failed";
01234 setCompression(c);
01235 return false;
01236 }
01237
01238 setCompression(c);
01239 return true;
01240 }
01241
01242 void KZip::virtual_hook( int id, void* data )
01243 {
01244 KArchive::virtual_hook( id, data );
01245 }
01246
01247 bool KZip::writeData(const char * data, qint64 size)
01248 {
01249 Q_ASSERT( d->m_currentFile );
01250 Q_ASSERT( d->m_currentDev );
01251 if (!d->m_currentFile || !d->m_currentDev) {
01252 return false;
01253 }
01254
01255
01256
01257 d->m_crc = crc32(d->m_crc, (const Bytef *) data , size);
01258
01259 qint64 written = d->m_currentDev->write( data, size );
01260
01261 return written == size;
01262 }
01263
01264 void KZip::setCompression( Compression c )
01265 {
01266 d->m_compression = ( c == NoCompression ) ? 0 : 8;
01267 }
01268
01269 KZip::Compression KZip::compression() const
01270 {
01271 return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
01272 }
01273
01274 void KZip::setExtraField( ExtraField ef )
01275 {
01276 d->m_extraField = ef;
01277 }
01278
01279 KZip::ExtraField KZip::extraField() const
01280 {
01281 return d->m_extraField;
01282 }
01283
01287 class KZipFileEntry::KZipFileEntryPrivate
01288 {
01289 public:
01290 KZipFileEntryPrivate()
01291 : crc(0),
01292 compressedSize(0),
01293 headerStart(0),
01294 encoding(0)
01295 {}
01296 unsigned long crc;
01297 qint64 compressedSize;
01298 qint64 headerStart;
01299 int encoding;
01300 QString path;
01301 };
01302
01303 KZipFileEntry::KZipFileEntry(KZip* zip, const QString& name, int access, int date,
01304 const QString& user, const QString& group, const QString& symlink,
01305 const QString& path, qint64 start, qint64 uncompressedSize,
01306 int encoding, qint64 compressedSize)
01307 : KArchiveFile(zip, name, access, date, user, group, symlink, start, uncompressedSize ),
01308 d(new KZipFileEntryPrivate)
01309 {
01310 d->path = path;
01311 d->encoding = encoding;
01312 d->compressedSize = compressedSize;
01313 }
01314
01315 KZipFileEntry::~KZipFileEntry()
01316 {
01317 delete d;
01318 }
01319
01320 int KZipFileEntry::encoding() const
01321 {
01322 return d->encoding;
01323 }
01324
01325 qint64 KZipFileEntry::compressedSize() const
01326 {
01327 return d->compressedSize;
01328 }
01329
01330 void KZipFileEntry::setCompressedSize(qint64 compressedSize)
01331 {
01332 d->compressedSize = compressedSize;
01333 }
01334
01335 void KZipFileEntry::setHeaderStart(qint64 headerstart)
01336 {
01337 d->headerStart = headerstart;
01338 }
01339
01340 qint64 KZipFileEntry::headerStart() const
01341 {
01342 return d->headerStart;
01343 }
01344
01345 unsigned long KZipFileEntry::crc32() const
01346 {
01347 return d->crc;
01348 }
01349
01350 void KZipFileEntry::setCRC32(unsigned long crc32)
01351 {
01352 d->crc=crc32;
01353 }
01354
01355 const QString &KZipFileEntry::path() const
01356 {
01357 return d->path;
01358 }
01359
01360 QByteArray KZipFileEntry::data() const
01361 {
01362 QIODevice* dev = createDevice();
01363 QByteArray arr;
01364 if ( dev ) {
01365 arr = dev->readAll();
01366 delete dev;
01367 }
01368 return arr;
01369 }
01370
01371 QIODevice* KZipFileEntry::createDevice() const
01372 {
01373
01374
01375 KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
01376 if ( encoding() == 0 || compressedSize() == 0 )
01377 return limitedDev;
01378
01379 if ( encoding() == 8 )
01380 {
01381
01382 QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
01383 if ( !filterDev )
01384 return 0L;
01385 static_cast<KFilterDev *>(filterDev)->setSkipHeaders();
01386 bool b = filterDev->open( QIODevice::ReadOnly );
01387 Q_ASSERT( b );
01388 return filterDev;
01389 }
01390
01391 kError() << "This zip file contains files compressed with method"
01392 << encoding() << ", this method is currently not supported by KZip,"
01393 << "please use a command-line tool to handle this file.";
01394 return 0L;
01395 }