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 if ( handledextralen )
00447 kDebug(7040) << "handledextralen: " << handledextralen;
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 ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 |
00644 (uchar)buffer[25] << 8 | (uchar)buffer[24];
00645
00646 uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 |
00647 (uchar)buffer[21] << 8 | (uchar)buffer[20];
00648
00649
00650 uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 |
00651 (uchar)buffer[43] << 8 | (uchar)buffer[42];
00652
00653
00654
00655
00656
00657 int localextralen = pfi.extralen;
00658
00659
00660
00661
00662
00663 uint dataoffset = localheaderoffset + 30 + localextralen + namelen;
00664
00665
00666
00667
00668
00669 int os_madeby = (uchar)buffer[5];
00670 bool isdir = false;
00671 int access = 0100644;
00672
00673 if (os_madeby == 3) {
00674 access = (uchar)buffer[40] | (uchar)buffer[41] << 8;
00675 }
00676
00677 QString entryName;
00678
00679 if ( name.endsWith( '/' ) )
00680 {
00681 isdir = true;
00682 name = name.left( name.length() - 1 );
00683 if (os_madeby != 3) access = S_IFDIR | 0755;
00684 else Q_ASSERT(access & S_IFDIR);
00685 }
00686
00687 int pos = name.lastIndexOf( '/' );
00688 if ( pos == -1 )
00689 entryName = name;
00690 else
00691 entryName = name.mid( pos + 1 );
00692 Q_ASSERT( !entryName.isEmpty() );
00693
00694 KArchiveEntry* entry;
00695 if ( isdir )
00696 {
00697 QString path = QDir::cleanPath( name );
00698 const KArchiveEntry* ent = rootDir()->entry( path );
00699 if ( ent && ent->isDirectory() )
00700 {
00701
00702 entry = 0;
00703 }
00704 else
00705 {
00706 entry = new KArchiveDirectory( this, entryName, access, (int)pfi.mtime, rootDir()->user(), rootDir()->group(), QString() );
00707
00708 }
00709 }
00710 else
00711 {
00712 QString symlink;
00713 if (S_ISLNK(access)) {
00714 symlink = QFile::decodeName(pfi.guessed_symlink);
00715 }
00716 entry = new KZipFileEntry( this, entryName, access, pfi.mtime,
00717 rootDir()->user(), rootDir()->group(),
00718 symlink, name, dataoffset,
00719 ucsize, cmethod, csize );
00720 static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset );
00721
00722 d->m_fileList.append( static_cast<KZipFileEntry *>( entry ) );
00723 }
00724
00725 if ( entry )
00726 {
00727 if ( pos == -1 )
00728 {
00729 rootDir()->addEntry(entry);
00730 }
00731 else
00732 {
00733
00734 QString path = QDir::cleanPath( name.left( pos ) );
00735
00736 KArchiveDirectory * tdir = findOrCreate( path );
00737 tdir->addEntry(entry);
00738 }
00739 }
00740
00741
00742 offset += 46 + commlen + extralen + namelen;
00743 bool b = dev->seek(offset);
00744 Q_ASSERT( b );
00745 if ( !b )
00746 return false;
00747 }
00748 else if ( startOfFile )
00749 {
00750
00751
00752 kDebug(7040) << "Try to skip start of file";
00753 startOfFile = false;
00754 bool foundSignature = false;
00755
00756 while (!foundSignature)
00757 {
00758 n = dev->read( buffer, 1 );
00759 if (n < 1)
00760 {
00761 kWarning(7040) << "Invalid ZIP file. Unexpected end of file. " ;
00762 return false;
00763 }
00764
00765 if ( buffer[0] != 'P' )
00766 continue;
00767
00768 n = dev->read( buffer, 3 );
00769 if (n < 3)
00770 {
00771 kWarning(7040) << "Invalid ZIP file. Unexpected end of file. " ;
00772 return false;
00773 }
00774
00775
00776
00777
00778
00779
00780 if ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 )
00781 {
00782 foundSignature = true;
00783 dev->seek( dev->pos() - 4 );
00784 }
00785 else if ( buffer[0] == 'P' || buffer[1] == 'P' || buffer[2] == 'P' )
00786 {
00787
00788 dev->seek( dev->pos() - 3 );
00789 }
00790 }
00791 }
00792 else
00793 {
00794 kWarning(7040) << "Invalid ZIP file. Unrecognized header at offset " << offset;
00795
00796 return false;
00797 }
00798 }
00799
00800 return true;
00801 }
00802
00803 bool KZip::closeArchive()
00804 {
00805 if ( ! ( mode() & QIODevice::WriteOnly ) )
00806 {
00807
00808 return true;
00809 }
00810
00811
00812
00813
00814
00815 char buffer[ 22 ];
00816 uLong crc = crc32(0L, Z_NULL, 0);
00817
00818 qint64 centraldiroffset = device()->pos();
00819
00820 qint64 atbackup = centraldiroffset;
00821 QMutableListIterator<KZipFileEntry*> it( d->m_fileList );
00822
00823 while(it.hasNext())
00824 {
00825 it.next();
00826 if ( !device()->seek( it.value()->headerStart() + 14 ) )
00827 return false;
00828
00829
00830
00831
00832 uLong mycrc = it.value()->crc32();
00833 buffer[0] = char(mycrc);
00834 buffer[1] = char(mycrc >> 8);
00835 buffer[2] = char(mycrc >> 16);
00836 buffer[3] = char(mycrc >> 24);
00837
00838 int mysize1 = it.value()->compressedSize();
00839 buffer[4] = char(mysize1);
00840 buffer[5] = char(mysize1 >> 8);
00841 buffer[6] = char(mysize1 >> 16);
00842 buffer[7] = char(mysize1 >> 24);
00843
00844 int myusize = it.value()->size();
00845 buffer[8] = char(myusize);
00846 buffer[9] = char(myusize >> 8);
00847 buffer[10] = char(myusize >> 16);
00848 buffer[11] = char(myusize >> 24);
00849
00850 if ( device()->write( buffer, 12 ) != 12 )
00851 return false;
00852 }
00853 device()->seek( atbackup );
00854
00855 it.toFront();
00856 while (it.hasNext())
00857 {
00858 it.next();
00859
00860
00861
00862 QByteArray path = QFile::encodeName(it.value()->path());
00863
00864 const int extra_field_len = 9;
00865 int bufferSize = extra_field_len + path.length() + 46;
00866 char* buffer = new char[ bufferSize ];
00867
00868 memset(buffer, 0, 46);
00869
00870 const char head[] =
00871 {
00872 'P', 'K', 1, 2,
00873 0x14, 3,
00874 0x14, 0
00875 };
00876
00877
00878
00879 memmove(buffer, head, sizeof(head));
00880
00881 buffer[ 10 ] = char(it.value()->encoding());
00882 buffer[ 11 ] = char(it.value()->encoding() >> 8);
00883
00884 transformToMsDos( it.value()->datetime(), &buffer[ 12 ] );
00885
00886 uLong mycrc = it.value()->crc32();
00887 buffer[ 16 ] = char(mycrc);
00888 buffer[ 17 ] = char(mycrc >> 8);
00889 buffer[ 18 ] = char(mycrc >> 16);
00890 buffer[ 19 ] = char(mycrc >> 24);
00891
00892 int mysize1 = it.value()->compressedSize();
00893 buffer[ 20 ] = char(mysize1);
00894 buffer[ 21 ] = char(mysize1 >> 8);
00895 buffer[ 22 ] = char(mysize1 >> 16);
00896 buffer[ 23 ] = char(mysize1 >> 24);
00897
00898 int mysize = it.value()->size();
00899 buffer[ 24 ] = char(mysize);
00900 buffer[ 25 ] = char(mysize >> 8);
00901 buffer[ 26 ] = char(mysize >> 16);
00902 buffer[ 27 ] = char(mysize >> 24);
00903
00904 buffer[ 28 ] = char(it.value()->path().length());
00905 buffer[ 29 ] = char(it.value()->path().length() >> 8);
00906
00907 buffer[ 30 ] = char(extra_field_len);
00908 buffer[ 31 ] = char(extra_field_len >> 8);
00909
00910 buffer[ 40 ] = char(it.value()->permissions());
00911 buffer[ 41 ] = char(it.value()->permissions() >> 8);
00912
00913 int myhst = it.value()->headerStart();
00914 buffer[ 42 ] = char(myhst);
00915 buffer[ 43 ] = char(myhst >> 8);
00916 buffer[ 44 ] = char(myhst >> 16);
00917 buffer[ 45 ] = char(myhst >> 24);
00918
00919
00920 strncpy( buffer + 46, path, path.length() );
00921
00922
00923
00924 char *extfield = buffer + 46 + path.length();
00925 extfield[0] = 'U';
00926 extfield[1] = 'T';
00927 extfield[2] = 5;
00928 extfield[3] = 0;
00929 extfield[4] = 1 | 2 | 4;
00930
00931
00932 unsigned long time = (unsigned long)it.value()->date();
00933 extfield[5] = char(time);
00934 extfield[6] = char(time >> 8);
00935 extfield[7] = char(time >> 16);
00936 extfield[8] = char(time >> 24);
00937
00938 crc = crc32(crc, (Bytef *)buffer, bufferSize );
00939 bool ok = ( device()->write( buffer, bufferSize ) == bufferSize );
00940 delete[] buffer;
00941 if ( !ok )
00942 return false;
00943 }
00944 qint64 centraldirendoffset = device()->pos();
00945
00946
00947
00948
00949 buffer[ 0 ] = 'P';
00950 buffer[ 1 ] = 'K';
00951 buffer[ 2 ] = 5;
00952 buffer[ 3 ] = 6;
00953
00954 buffer[ 4 ] = 0;
00955 buffer[ 5 ] = 0;
00956
00957 buffer[ 6 ] = 0;
00958 buffer[ 7 ] = 0;
00959
00960 int count = d->m_fileList.count();
00961
00962
00963
00964 buffer[ 8 ] = char(count);
00965 buffer[ 9 ] = char(count >> 8);
00966
00967 buffer[ 10 ] = buffer[ 8 ];
00968 buffer[ 11 ] = buffer[ 9 ];
00969
00970 int cdsize = centraldirendoffset - centraldiroffset;
00971 buffer[ 12 ] = char(cdsize);
00972 buffer[ 13 ] = char(cdsize >> 8);
00973 buffer[ 14 ] = char(cdsize >> 16);
00974 buffer[ 15 ] = char(cdsize >> 24);
00975
00976
00977
00978
00979 buffer[ 16 ] = char(centraldiroffset);
00980 buffer[ 17 ] = char(centraldiroffset >> 8);
00981 buffer[ 18 ] = char(centraldiroffset >> 16);
00982 buffer[ 19 ] = char(centraldiroffset >> 24);
00983
00984 buffer[ 20 ] = 0;
00985 buffer[ 21 ] = 0;
00986
00987 if ( device()->write( buffer, 22 ) != 22 )
00988 return false;
00989
00990 return true;
00991 }
00992
00993 bool KZip::doWriteDir( const QString&, const QString&, const QString&,
00994 mode_t, time_t, time_t, time_t ) {
00995 return true;
00996 }
00997
00998 bool KZip::doPrepareWriting(const QString &name, const QString &user,
00999 const QString &group, qint64 , mode_t perm,
01000 time_t atime, time_t mtime, time_t ctime) {
01001
01002 if ( !isOpen() )
01003 {
01004 qWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
01005 return false;
01006 }
01007
01008 if ( ! ( mode() & QIODevice::WriteOnly ) )
01009 {
01010 qWarning( "KZip::writeFile: You must open the zip file for writing\n");
01011 return false;
01012 }
01013
01014 Q_ASSERT( device() );
01015
01016
01017 if ( !device()->seek( d->m_offset ) ) {
01018 kWarning(7040) << "doPrepareWriting: cannot seek in ZIP file. Disk full?";
01019 return false;
01020 }
01021
01022
01023
01024
01025
01026 QMutableListIterator<KZipFileEntry*> it( d->m_fileList );
01027
01028 while(it.hasNext())
01029 {
01030 it.next();
01031
01032 if (name == it.value()->path() )
01033 {
01034
01035 delete it.value();
01036 it.remove();
01037 }
01038
01039 }
01040
01041 KArchiveDirectory* parentDir = rootDir();
01042 QString fileName( name );
01043 int i = name.lastIndexOf( '/' );
01044 if ( i != -1 )
01045 {
01046 QString dir = name.left( i );
01047 fileName = name.mid( i + 1 );
01048
01049 parentDir = findOrCreate( dir );
01050 }
01051
01052
01053 KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, QString(),
01054 name, device()->pos() + 30 + name.length(),
01055 0 , d->m_compression, 0 );
01056 e->setHeaderStart( device()->pos() );
01057
01058 parentDir->addEntry( e );
01059
01060 d->m_currentFile = e;
01061 d->m_fileList.append( e );
01062
01063 int extra_field_len = 0;
01064 if ( d->m_extraField == ModificationTime )
01065 extra_field_len = 17;
01066
01067
01068 QByteArray encodedName = QFile::encodeName(name);
01069 int bufferSize = extra_field_len + encodedName.length() + 30;
01070
01071 char* buffer = new char[ bufferSize ];
01072
01073 buffer[ 0 ] = 'P';
01074 buffer[ 1 ] = 'K';
01075 buffer[ 2 ] = 3;
01076 buffer[ 3 ] = 4;
01077
01078 buffer[ 4 ] = 0x14;
01079 buffer[ 5 ] = 0;
01080
01081 buffer[ 6 ] = 0;
01082 buffer[ 7 ] = 0;
01083
01084 buffer[ 8 ] = char(e->encoding());
01085 buffer[ 9 ] = char(e->encoding() >> 8);
01086
01087 transformToMsDos( e->datetime(), &buffer[ 10 ] );
01088
01089 buffer[ 14 ] = 'C';
01090 buffer[ 15 ] = 'R';
01091 buffer[ 16 ] = 'C';
01092 buffer[ 17 ] = 'q';
01093
01094 buffer[ 18 ] = 'C';
01095 buffer[ 19 ] = 'S';
01096 buffer[ 20 ] = 'I';
01097 buffer[ 21 ] = 'Z';
01098
01099 buffer[ 22 ] = 'U';
01100 buffer[ 23 ] = 'S';
01101 buffer[ 24 ] = 'I';
01102 buffer[ 25 ] = 'Z';
01103
01104 buffer[ 26 ] = (uchar)(encodedName.length());
01105 buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
01106
01107 buffer[ 28 ] = (uchar)(extra_field_len);
01108 buffer[ 29 ] = (uchar)(extra_field_len >> 8);
01109
01110
01111 strncpy( buffer + 30, encodedName, encodedName.length() );
01112
01113
01114 if ( d->m_extraField == ModificationTime )
01115 {
01116 char *extfield = buffer + 30 + encodedName.length();
01117
01118 extfield[0] = 'U';
01119 extfield[1] = 'T';
01120 extfield[2] = 13;
01121 extfield[3] = 0;
01122 extfield[4] = 1 | 2 | 4;
01123
01124 extfield[5] = char(mtime);
01125 extfield[6] = char(mtime >> 8);
01126 extfield[7] = char(mtime >> 16);
01127 extfield[8] = char(mtime >> 24);
01128
01129 extfield[9] = char(atime);
01130 extfield[10] = char(atime >> 8);
01131 extfield[11] = char(atime >> 16);
01132 extfield[12] = char(atime >> 24);
01133
01134 extfield[13] = char(ctime);
01135 extfield[14] = char(ctime >> 8);
01136 extfield[15] = char(ctime >> 16);
01137 extfield[16] = char(ctime >> 24);
01138 }
01139
01140
01141 bool b = (device()->write( buffer, bufferSize ) == bufferSize );
01142 d->m_crc = 0L;
01143 delete[] buffer;
01144
01145 Q_ASSERT( b );
01146 if (!b) {
01147 return false;
01148 }
01149
01150
01151
01152 if ( d->m_compression == 0 ) {
01153 d->m_currentDev = device();
01154 return true;
01155 }
01156
01157 d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
01158 Q_ASSERT( d->m_currentDev );
01159 if ( !d->m_currentDev ) {
01160 return false;
01161 }
01162 static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders();
01163
01164 b = d->m_currentDev->open( QIODevice::WriteOnly );
01165 Q_ASSERT( b );
01166 return b;
01167 }
01168
01169 bool KZip::doFinishWriting( qint64 size )
01170 {
01171 if ( d->m_currentFile->encoding() == 8 ) {
01172
01173 (void)d->m_currentDev->write( 0, 0 );
01174 delete d->m_currentDev;
01175 }
01176
01177 d->m_currentDev = 0L;
01178
01179 Q_ASSERT( d->m_currentFile );
01180
01181
01182 d->m_currentFile->setSize(size);
01183 int extra_field_len = 0;
01184 if ( d->m_extraField == ModificationTime )
01185 extra_field_len = 17;
01186
01187 int csize = device()->pos() -
01188 d->m_currentFile->headerStart() - 30 -
01189 d->m_currentFile->path().length() - extra_field_len;
01190 d->m_currentFile->setCompressedSize(csize);
01191
01192
01193
01194
01195
01196 d->m_currentFile->setCRC32( d->m_crc );
01197
01198 d->m_currentFile = 0L;
01199
01200
01201 d->m_offset = device()->pos();
01202 return true;
01203 }
01204
01205 bool KZip::doWriteSymLink(const QString &name, const QString &target,
01206 const QString &user, const QString &group,
01207 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01208
01209
01210 perm |= S_IFLNK;
01211 Compression c = compression();
01212 setCompression(NoCompression);
01213
01214 if (!doPrepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) {
01215 kWarning() << "prepareWriting failed";
01216 setCompression(c);
01217 return false;
01218 }
01219
01220 QByteArray symlink_target = QFile::encodeName(target);
01221 if (!writeData(symlink_target, symlink_target.length())) {
01222 kWarning() << "writeData failed";
01223 setCompression(c);
01224 return false;
01225 }
01226
01227 if (!finishWriting(symlink_target.length())) {
01228 kWarning() << "finishWriting failed";
01229 setCompression(c);
01230 return false;
01231 }
01232
01233 setCompression(c);
01234 return true;
01235 }
01236
01237 void KZip::virtual_hook( int id, void* data )
01238 {
01239 KArchive::virtual_hook( id, data );
01240 }
01241
01242 bool KZip::writeData(const char * data, qint64 size)
01243 {
01244 Q_ASSERT( d->m_currentFile );
01245 Q_ASSERT( d->m_currentDev );
01246 if (!d->m_currentFile || !d->m_currentDev) {
01247 return false;
01248 }
01249
01250
01251
01252 d->m_crc = crc32(d->m_crc, (const Bytef *) data , size);
01253
01254 qint64 written = d->m_currentDev->write( data, size );
01255
01256 return written == size;
01257 }
01258
01259 void KZip::setCompression( Compression c )
01260 {
01261 d->m_compression = ( c == NoCompression ) ? 0 : 8;
01262 }
01263
01264 KZip::Compression KZip::compression() const
01265 {
01266 return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
01267 }
01268
01269 void KZip::setExtraField( ExtraField ef )
01270 {
01271 d->m_extraField = ef;
01272 }
01273
01274 KZip::ExtraField KZip::extraField() const
01275 {
01276 return d->m_extraField;
01277 }
01278
01282 class KZipFileEntry::KZipFileEntryPrivate
01283 {
01284 public:
01285 KZipFileEntryPrivate()
01286 : crc(0),
01287 compressedSize(0),
01288 headerStart(0),
01289 encoding(0)
01290 {}
01291 unsigned long crc;
01292 qint64 compressedSize;
01293 qint64 headerStart;
01294 int encoding;
01295 QString path;
01296 };
01297
01298 KZipFileEntry::KZipFileEntry(KZip* zip, const QString& name, int access, int date,
01299 const QString& user, const QString& group, const QString& symlink,
01300 const QString& path, qint64 start, qint64 uncompressedSize,
01301 int encoding, qint64 compressedSize)
01302 : KArchiveFile(zip, name, access, date, user, group, symlink, start, uncompressedSize ),
01303 d(new KZipFileEntryPrivate)
01304 {
01305 d->path = path;
01306 d->encoding = encoding;
01307 d->compressedSize = compressedSize;
01308 }
01309
01310 KZipFileEntry::~KZipFileEntry()
01311 {
01312 delete d;
01313 }
01314
01315 int KZipFileEntry::encoding() const
01316 {
01317 return d->encoding;
01318 }
01319
01320 qint64 KZipFileEntry::compressedSize() const
01321 {
01322 return d->compressedSize;
01323 }
01324
01325 void KZipFileEntry::setCompressedSize(qint64 compressedSize)
01326 {
01327 d->compressedSize = compressedSize;
01328 }
01329
01330 void KZipFileEntry::setHeaderStart(qint64 headerstart)
01331 {
01332 d->headerStart = headerstart;
01333 }
01334
01335 qint64 KZipFileEntry::headerStart() const
01336 {
01337 return d->headerStart;
01338 }
01339
01340 unsigned long KZipFileEntry::crc32() const
01341 {
01342 return d->crc;
01343 }
01344
01345 void KZipFileEntry::setCRC32(unsigned long crc32)
01346 {
01347 d->crc=crc32;
01348 }
01349
01350 const QString &KZipFileEntry::path() const
01351 {
01352 return d->path;
01353 }
01354
01355 QByteArray KZipFileEntry::data() const
01356 {
01357 QIODevice* dev = createDevice();
01358 QByteArray arr;
01359 if ( dev ) {
01360 arr = dev->readAll();
01361 delete dev;
01362 }
01363 return arr;
01364 }
01365
01366 QIODevice* KZipFileEntry::createDevice() const
01367 {
01368
01369
01370 KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
01371 if ( encoding() == 0 || compressedSize() == 0 )
01372 return limitedDev;
01373
01374 if ( encoding() == 8 )
01375 {
01376
01377 QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
01378 if ( !filterDev )
01379 return 0L;
01380 static_cast<KFilterDev *>(filterDev)->setSkipHeaders();
01381 bool b = filterDev->open( QIODevice::ReadOnly );
01382 Q_ASSERT( b );
01383 return filterDev;
01384 }
01385
01386 kError() << "This zip file contains files compressed with method"
01387 << encoding() << ", this method is currently not supported by KZip,"
01388 << "please use a command-line tool to handle this file.";
01389 return 0L;
01390 }