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