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