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 &name, const QString &user, const QString &group,
00999 mode_t perm, time_t atime, time_t mtime, time_t ctime ) {
01000
01001
01002
01003 QString dirName = name;
01004 if (!name.endsWith("/"))
01005 dirName = dirName.append('/');
01006 return writeFile(dirName, user, group, 0, 0, perm, atime, mtime, ctime);
01007 }
01008
01009 bool KZip::doPrepareWriting(const QString &name, const QString &user,
01010 const QString &group, qint64 , mode_t perm,
01011 time_t atime, time_t mtime, time_t ctime) {
01012
01013 if ( !isOpen() )
01014 {
01015 qWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
01016 return false;
01017 }
01018
01019 if ( ! ( mode() & QIODevice::WriteOnly ) )
01020 {
01021 qWarning( "KZip::writeFile: You must open the zip file for writing\n");
01022 return false;
01023 }
01024
01025 Q_ASSERT( device() );
01026
01027
01028 if ( !device()->seek( d->m_offset ) ) {
01029 kWarning(7040) << "doPrepareWriting: cannot seek in ZIP file. Disk full?";
01030 return false;
01031 }
01032
01033
01034
01035
01036
01037 QMutableListIterator<KZipFileEntry*> it( d->m_fileList );
01038
01039 while(it.hasNext())
01040 {
01041 it.next();
01042
01043 if (name == it.value()->path() )
01044 {
01045
01046 delete it.value();
01047 it.remove();
01048 }
01049
01050 }
01051
01052 KArchiveDirectory* parentDir = rootDir();
01053 QString fileName( name );
01054 int i = name.lastIndexOf( '/' );
01055 if ( i != -1 )
01056 {
01057 QString dir = name.left( i );
01058 fileName = name.mid( i + 1 );
01059
01060 parentDir = findOrCreate( dir );
01061 }
01062
01063
01064 KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, QString(),
01065 name, device()->pos() + 30 + name.length(),
01066 0 , d->m_compression, 0 );
01067 e->setHeaderStart( device()->pos() );
01068
01069 parentDir->addEntry( e );
01070
01071 d->m_currentFile = e;
01072 d->m_fileList.append( e );
01073
01074 int extra_field_len = 0;
01075 if ( d->m_extraField == ModificationTime )
01076 extra_field_len = 17;
01077
01078
01079 QByteArray encodedName = QFile::encodeName(name);
01080 int bufferSize = extra_field_len + encodedName.length() + 30;
01081
01082 char* buffer = new char[ bufferSize ];
01083
01084 buffer[ 0 ] = 'P';
01085 buffer[ 1 ] = 'K';
01086 buffer[ 2 ] = 3;
01087 buffer[ 3 ] = 4;
01088
01089 buffer[ 4 ] = 0x14;
01090 buffer[ 5 ] = 0;
01091
01092 buffer[ 6 ] = 0;
01093 buffer[ 7 ] = 0;
01094
01095 buffer[ 8 ] = char(e->encoding());
01096 buffer[ 9 ] = char(e->encoding() >> 8);
01097
01098 transformToMsDos( e->datetime(), &buffer[ 10 ] );
01099
01100 buffer[ 14 ] = 'C';
01101 buffer[ 15 ] = 'R';
01102 buffer[ 16 ] = 'C';
01103 buffer[ 17 ] = 'q';
01104
01105 buffer[ 18 ] = 'C';
01106 buffer[ 19 ] = 'S';
01107 buffer[ 20 ] = 'I';
01108 buffer[ 21 ] = 'Z';
01109
01110 buffer[ 22 ] = 'U';
01111 buffer[ 23 ] = 'S';
01112 buffer[ 24 ] = 'I';
01113 buffer[ 25 ] = 'Z';
01114
01115 buffer[ 26 ] = (uchar)(encodedName.length());
01116 buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
01117
01118 buffer[ 28 ] = (uchar)(extra_field_len);
01119 buffer[ 29 ] = (uchar)(extra_field_len >> 8);
01120
01121
01122 strncpy( buffer + 30, encodedName, encodedName.length() );
01123
01124
01125 if ( d->m_extraField == ModificationTime )
01126 {
01127 char *extfield = buffer + 30 + encodedName.length();
01128
01129 extfield[0] = 'U';
01130 extfield[1] = 'T';
01131 extfield[2] = 13;
01132 extfield[3] = 0;
01133 extfield[4] = 1 | 2 | 4;
01134
01135 extfield[5] = char(mtime);
01136 extfield[6] = char(mtime >> 8);
01137 extfield[7] = char(mtime >> 16);
01138 extfield[8] = char(mtime >> 24);
01139
01140 extfield[9] = char(atime);
01141 extfield[10] = char(atime >> 8);
01142 extfield[11] = char(atime >> 16);
01143 extfield[12] = char(atime >> 24);
01144
01145 extfield[13] = char(ctime);
01146 extfield[14] = char(ctime >> 8);
01147 extfield[15] = char(ctime >> 16);
01148 extfield[16] = char(ctime >> 24);
01149 }
01150
01151
01152 bool b = (device()->write( buffer, bufferSize ) == bufferSize );
01153 d->m_crc = 0L;
01154 delete[] buffer;
01155
01156 Q_ASSERT( b );
01157 if (!b) {
01158 return false;
01159 }
01160
01161
01162
01163 if ( d->m_compression == 0 ) {
01164 d->m_currentDev = device();
01165 return true;
01166 }
01167
01168 d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
01169 Q_ASSERT( d->m_currentDev );
01170 if ( !d->m_currentDev ) {
01171 return false;
01172 }
01173 static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders();
01174
01175 b = d->m_currentDev->open( QIODevice::WriteOnly );
01176 Q_ASSERT( b );
01177 return b;
01178 }
01179
01180 bool KZip::doFinishWriting( qint64 size )
01181 {
01182 if ( d->m_currentFile->encoding() == 8 ) {
01183
01184 (void)d->m_currentDev->write( 0, 0 );
01185 delete d->m_currentDev;
01186 }
01187
01188 d->m_currentDev = 0L;
01189
01190 Q_ASSERT( d->m_currentFile );
01191
01192
01193 d->m_currentFile->setSize(size);
01194 int extra_field_len = 0;
01195 if ( d->m_extraField == ModificationTime )
01196 extra_field_len = 17;
01197
01198 const QByteArray encodedName = QFile::encodeName(d->m_currentFile->path());
01199 int csize = device()->pos() -
01200 d->m_currentFile->headerStart() - 30 -
01201 encodedName.length() - extra_field_len;
01202 d->m_currentFile->setCompressedSize(csize);
01203
01204
01205
01206
01207
01208 d->m_currentFile->setCRC32( d->m_crc );
01209
01210 d->m_currentFile = 0L;
01211
01212
01213 d->m_offset = device()->pos();
01214 return true;
01215 }
01216
01217 bool KZip::doWriteSymLink(const QString &name, const QString &target,
01218 const QString &user, const QString &group,
01219 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01220
01221
01222 perm |= S_IFLNK;
01223 Compression c = compression();
01224 setCompression(NoCompression);
01225
01226 if (!doPrepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) {
01227 kWarning() << "prepareWriting failed";
01228 setCompression(c);
01229 return false;
01230 }
01231
01232 QByteArray symlink_target = QFile::encodeName(target);
01233 if (!writeData(symlink_target, symlink_target.length())) {
01234 kWarning() << "writeData failed";
01235 setCompression(c);
01236 return false;
01237 }
01238
01239 if (!finishWriting(symlink_target.length())) {
01240 kWarning() << "finishWriting failed";
01241 setCompression(c);
01242 return false;
01243 }
01244
01245 setCompression(c);
01246 return true;
01247 }
01248
01249 void KZip::virtual_hook( int id, void* data )
01250 {
01251 KArchive::virtual_hook( id, data );
01252 }
01253
01254 bool KZip::writeData(const char * data, qint64 size)
01255 {
01256 Q_ASSERT( d->m_currentFile );
01257 Q_ASSERT( d->m_currentDev );
01258 if (!d->m_currentFile || !d->m_currentDev) {
01259 return false;
01260 }
01261
01262
01263
01264 d->m_crc = crc32(d->m_crc, (const Bytef *) data , size);
01265
01266 qint64 written = d->m_currentDev->write( data, size );
01267
01268 return written == size;
01269 }
01270
01271 void KZip::setCompression( Compression c )
01272 {
01273 d->m_compression = ( c == NoCompression ) ? 0 : 8;
01274 }
01275
01276 KZip::Compression KZip::compression() const
01277 {
01278 return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
01279 }
01280
01281 void KZip::setExtraField( ExtraField ef )
01282 {
01283 d->m_extraField = ef;
01284 }
01285
01286 KZip::ExtraField KZip::extraField() const
01287 {
01288 return d->m_extraField;
01289 }
01290
01294 class KZipFileEntry::KZipFileEntryPrivate
01295 {
01296 public:
01297 KZipFileEntryPrivate()
01298 : crc(0),
01299 compressedSize(0),
01300 headerStart(0),
01301 encoding(0)
01302 {}
01303 unsigned long crc;
01304 qint64 compressedSize;
01305 qint64 headerStart;
01306 int encoding;
01307 QString path;
01308 };
01309
01310 KZipFileEntry::KZipFileEntry(KZip* zip, const QString& name, int access, int date,
01311 const QString& user, const QString& group, const QString& symlink,
01312 const QString& path, qint64 start, qint64 uncompressedSize,
01313 int encoding, qint64 compressedSize)
01314 : KArchiveFile(zip, name, access, date, user, group, symlink, start, uncompressedSize ),
01315 d(new KZipFileEntryPrivate)
01316 {
01317 d->path = path;
01318 d->encoding = encoding;
01319 d->compressedSize = compressedSize;
01320 }
01321
01322 KZipFileEntry::~KZipFileEntry()
01323 {
01324 delete d;
01325 }
01326
01327 int KZipFileEntry::encoding() const
01328 {
01329 return d->encoding;
01330 }
01331
01332 qint64 KZipFileEntry::compressedSize() const
01333 {
01334 return d->compressedSize;
01335 }
01336
01337 void KZipFileEntry::setCompressedSize(qint64 compressedSize)
01338 {
01339 d->compressedSize = compressedSize;
01340 }
01341
01342 void KZipFileEntry::setHeaderStart(qint64 headerstart)
01343 {
01344 d->headerStart = headerstart;
01345 }
01346
01347 qint64 KZipFileEntry::headerStart() const
01348 {
01349 return d->headerStart;
01350 }
01351
01352 unsigned long KZipFileEntry::crc32() const
01353 {
01354 return d->crc;
01355 }
01356
01357 void KZipFileEntry::setCRC32(unsigned long crc32)
01358 {
01359 d->crc=crc32;
01360 }
01361
01362 const QString &KZipFileEntry::path() const
01363 {
01364 return d->path;
01365 }
01366
01367 QByteArray KZipFileEntry::data() const
01368 {
01369 QIODevice* dev = createDevice();
01370 QByteArray arr;
01371 if ( dev ) {
01372 arr = dev->readAll();
01373 delete dev;
01374 }
01375 return arr;
01376 }
01377
01378 QIODevice* KZipFileEntry::createDevice() 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( QIODevice::ReadOnly );
01394 Q_ASSERT( b );
01395 return filterDev;
01396 }
01397
01398 kError() << "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.";
01401 return 0L;
01402 }