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
00410
unsigned int extraFieldEnd = dev->at() + extralen;
00411 pfi->extralen = extralen;
00412
int handledextralen = QMIN(extralen, (
int)
sizeof buffer);
00413 n = dev->readBlock(buffer, handledextralen);
00414
00415
if (!parseExtraField(buffer, handledextralen,
true, *pfi))
00416
return false;
00417
00418
00419 dev->at( extraFieldEnd );
00420
00421
00422
00423
00424
00425
if ( gpf & 8 )
00426 {
00427
bool foundSignature =
false;
00428
00429
while (!foundSignature)
00430 {
00431 n = dev->readBlock( buffer, 1 );
00432
if (n < 1)
00433 {
00434
kdWarning(7040) <<
"Invalid ZIP file. Unexpected end of file. (#2)" <<
endl;
00435
return false;
00436 }
00437
00438
if ( buffer[0] !=
'P' )
00439
continue;
00440
00441 n = dev->readBlock( buffer, 3 );
00442
if (n < 3)
00443 {
00444
kdWarning(7040) <<
"Invalid ZIP file. Unexpected end of file. (#3)" <<
endl;
00445
return false;
00446 }
00447
00448
00449
00450
00451
00452
00453
if ( buffer[0] ==
'K' && buffer[1] == 7 && buffer[2] == 8 )
00454 {
00455 foundSignature =
true;
00456 dev->at( dev->at() + 12 );
00457 }
00458
else if ( ( buffer[0] ==
'K' && buffer[1] == 1 && buffer[2] == 2 )
00459 || ( buffer[0] ==
'K' && buffer[1] == 3 && buffer[2] == 4 ) )
00460 {
00461 foundSignature =
true;
00462 dev->at( dev->at() - 4 );
00463
00464 }
00465 }
00466 }
00467
else
00468 {
00469
00470
if (compression_mode ==
NoCompression
00471 && uncomp_size <= max_path_len
00472 && uncomp_size > 0) {
00473
00474 pfi->guessed_symlink.resize(uncomp_size + 1);
00475 n = dev->readBlock(pfi->guessed_symlink.data(), uncomp_size);
00476
if (n < uncomp_size) {
00477
kdWarning(7040) <<
"Invalid ZIP file. Unexpected end of file. (#5)" <<
endl;
00478
return false;
00479 }
00480 }
00481
else
00482 {
00483
if ( dev->size() > 0 && compr_size > (Q_LONG)dev->size() )
00484 {
00485
00486
00487
bool foundSignature =
false;
00488
00489
while (!foundSignature)
00490 {
00491 n = dev->readBlock( buffer, 1 );
00492
if (n < 1)
00493 {
00494
kdWarning(7040) <<
"Invalid ZIP file. Unexpected end of file. (#6)" <<
endl;
00495
return false;
00496 }
00497
00498
if ( buffer[0] !=
'P' )
00499
continue;
00500
00501 n = dev->readBlock( buffer, 3 );
00502
if (n < 3)
00503 {
00504
kdWarning(7040) <<
"Invalid ZIP file. Unexpected end of file. (#7)" <<
endl;
00505
return false;
00506 }
00507
00508
00509
00510
00511
00512
00513
if ( buffer[0] ==
'K' && buffer[1] == 7 && buffer[2] == 8 )
00514 {
00515 foundSignature =
true;
00516 dev->at( dev->at() + 12 );
00517 }
00518
else if ( ( buffer[0] ==
'K' && buffer[1] == 1 && buffer[2] == 2 )
00519 || ( buffer[0] ==
'K' && buffer[1] == 3 && buffer[2] == 4 ) )
00520 {
00521 foundSignature =
true;
00522 dev->at( dev->at() - 4 );
00523
00524
00525 }
00526 }
00527 }
00528
else
00529 {
00530 dev->at( dev->at() + compr_size );
00531 }
00532 }
00533
00534
00535 uint skip = compr_size + namelen + extralen;
00536 offset += 30 + skip;
00537 }
00538 }
00539
else if ( !memcmp( buffer,
"PK\1\2", 4 ) )
00540 {
00541
00542
00543
00544
00545 offset = dev->at() - 4;
00546
00547
00548
if ( d->m_offset == 0L ) d->m_offset = offset;
00549
00550 n = dev->readBlock( buffer + 4, 42 );
00551
if (n < 42) {
00552
kdWarning(7040) <<
"Invalid ZIP file, central entry too short" <<
endl;
00553
return false;
00554 }
00555
00556
int namelen = (uchar)buffer[29] << 8 | (uchar)buffer[28];
00557
QCString bufferName( namelen + 1 );
00558 n = dev->readBlock( bufferName.data(), namelen );
00559
if ( n < namelen )
00560
kdWarning(7040) <<
"Invalid ZIP file. Name not completely read" <<
endl;
00561
00562 ParseFileInfo *pfi = pfi_map[bufferName];
00563
if (!pfi) {
00564 pfi_map.insert(bufferName.data(), pfi =
new ParseFileInfo());
00565 }
00566
QString name( QFile::decodeName(bufferName) );
00567
00568
00569
00570
00571
int extralen = (uchar)buffer[31] << 8 | (uchar)buffer[30];
00572
00573
int commlen = (uchar)buffer[33] << 8 | (uchar)buffer[32];
00574
00575
int cmethod = (uchar)buffer[11] << 8 | (uchar)buffer[10];
00576
00577
00578
00579
00580
00581 uint ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 |
00582 (uchar)buffer[25] << 8 | (uchar)buffer[24];
00583
00584 uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 |
00585 (uchar)buffer[21] << 8 | (uchar)buffer[20];
00586
00587
00588 uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 |
00589 (uchar)buffer[43] << 8 | (uchar)buffer[42];
00590
00591
00592
00593
00594
00595
int localextralen = pfi->extralen;
00596
00597
00598
00599
00600
00601 uint dataoffset = localheaderoffset + 30 + localextralen + namelen;
00602
00603
00604
00605
00606
00607
int os_madeby = (uchar)buffer[5];
00608
bool isdir =
false;
00609
int access = 0100644;
00610
00611
if (os_madeby == 3) {
00612 access = (uchar)buffer[40] | (uchar)buffer[41] << 8;
00613 }
00614
00615
QString entryName;
00616
00617
if ( name.endsWith(
"/" ) )
00618 {
00619 isdir =
true;
00620 name = name.left( name.length() - 1 );
00621
if (os_madeby != 3) access = S_IFDIR | 0755;
00622
else Q_ASSERT(access & S_IFDIR);
00623 }
00624
00625
int pos = name.findRev(
'/' );
00626
if ( pos == -1 )
00627 entryName = name;
00628
else
00629 entryName = name.mid( pos + 1 );
00630 Q_ASSERT( !entryName.isEmpty() );
00631
00632
KArchiveEntry* entry;
00633
if ( isdir )
00634 {
00635
QString path = QDir::cleanDirPath( name );
00636
KArchiveEntry* ent =
rootDir()->
entry( path );
00637
if ( ent && ent->
isDirectory() )
00638 {
00639
00640 entry = 0L;
00641 }
00642
else
00643 {
00644 entry =
new KArchiveDirectory(
this, entryName, access, (
int)pfi->mtime, rootDir()->user(),
rootDir()->group(), QString::null );
00645
00646 }
00647 }
00648
else
00649 {
00650
QString symlink;
00651
if (S_ISLNK(access)) {
00652 symlink = QFile::decodeName(pfi->guessed_symlink);
00653 }
00654 entry =
new KZipFileEntry(
this, entryName, access, pfi->mtime,
00655 rootDir()->user(),
rootDir()->group(),
00656 symlink, name, dataoffset,
00657 ucsize, cmethod, csize );
00658 static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset );
00659
00660 d->m_fileList.append( static_cast<KZipFileEntry *>( entry ) );
00661 }
00662
00663
if ( entry )
00664 {
00665
if ( pos == -1 )
00666 {
00667
rootDir()->
addEntry(entry);
00668 }
00669
else
00670 {
00671
00672
QString path = QDir::cleanDirPath( name.left( pos ) );
00673
00674
KArchiveDirectory * tdir =
findOrCreate( path );
00675 tdir->
addEntry(entry);
00676 }
00677 }
00678
00679
00680 offset += 46 + commlen + extralen + namelen;
00681
bool b = dev->at(offset);
00682 Q_ASSERT( b );
00683
if ( !b )
00684
return false;
00685 }
00686
else
00687 {
00688
kdWarning(7040) <<
"Invalid ZIP file. Unrecognized header at offset " << offset <<
endl;
00689
00690
return false;
00691 }
00692 }
00693
00694
return true;
00695 }
00696
00697 bool KZip::closeArchive()
00698 {
00699
if ( ! (
mode() & IO_WriteOnly ) )
00700 {
00701
00702
return true;
00703 }
00704
00705
00706
00707
00708
char buffer[ 22 ];
00709 uLong crc = crc32(0L, Z_NULL, 0);
00710
00711 Q_LONG centraldiroffset =
device()->at();
00712
00713 Q_LONG atbackup = centraldiroffset;
00714
QPtrListIterator<KZipFileEntry> it( d->m_fileList );
00715
00716
for ( ; it.current() ; ++it )
00717 {
00718
if ( !
device()->at( it.current()->headerStart() + 14 ) )
00719
return false;
00720
00721
00722
00723
00724 uLong mycrc = it.current()->crc32();
00725 buffer[0] = char(mycrc);
00726 buffer[1] = char(mycrc >> 8);
00727 buffer[2] = char(mycrc >> 16);
00728 buffer[3] = char(mycrc >> 24);
00729
00730
int mysize1 = it.current()->compressedSize();
00731 buffer[4] = char(mysize1);
00732 buffer[5] = char(mysize1 >> 8);
00733 buffer[6] = char(mysize1 >> 16);
00734 buffer[7] = char(mysize1 >> 24);
00735
00736
int myusize = it.current()->size();
00737 buffer[8] = char(myusize);
00738 buffer[9] = char(myusize >> 8);
00739 buffer[10] = char(myusize >> 16);
00740 buffer[11] = char(myusize >> 24);
00741
00742
if (
device()->writeBlock( buffer, 12 ) != 12 )
00743
return false;
00744 }
00745
device()->at( atbackup );
00746
00747
for ( it.toFirst(); it.current() ; ++it )
00748 {
00749
00750
00751
00752
QCString path = QFile::encodeName(it.current()->path());
00753
00754
const int extra_field_len = 9;
00755
int bufferSize = extra_field_len + path.length() + 46;
00756
char* buffer =
new char[ bufferSize ];
00757
00758 memset(buffer, 0, 46);
00759
00760
const char head[] =
00761 {
00762
'P',
'K', 1, 2,
00763 0x14, 3,
00764 0x14, 0
00765 };
00766
00767
00768
00769 qmemmove(buffer, head,
sizeof(head));
00770
00771 buffer[ 10 ] = char(it.current()->encoding());
00772 buffer[ 11 ] = char(it.current()->encoding() >> 8);
00773
00774 transformToMsDos( it.current()->datetime(), &buffer[ 12 ] );
00775
00776 uLong mycrc = it.current()->crc32();
00777 buffer[ 16 ] = char(mycrc);
00778 buffer[ 17 ] = char(mycrc >> 8);
00779 buffer[ 18 ] = char(mycrc >> 16);
00780 buffer[ 19 ] = char(mycrc >> 24);
00781
00782
int mysize1 = it.current()->compressedSize();
00783 buffer[ 20 ] = char(mysize1);
00784 buffer[ 21 ] = char(mysize1 >> 8);
00785 buffer[ 22 ] = char(mysize1 >> 16);
00786 buffer[ 23 ] = char(mysize1 >> 24);
00787
00788
int mysize = it.current()->size();
00789 buffer[ 24 ] = char(mysize);
00790 buffer[ 25 ] = char(mysize >> 8);
00791 buffer[ 26 ] = char(mysize >> 16);
00792 buffer[ 27 ] = char(mysize >> 24);
00793
00794 buffer[ 28 ] = char(it.current()->path().length());
00795 buffer[ 29 ] = char(it.current()->path().length() >> 8);
00796
00797 buffer[ 30 ] = char(extra_field_len);
00798 buffer[ 31 ] = char(extra_field_len >> 8);
00799
00800 buffer[ 40 ] = char(it.current()->permissions());
00801 buffer[ 41 ] = char(it.current()->permissions() >> 8);
00802
00803
int myhst = it.current()->headerStart();
00804 buffer[ 42 ] = char(myhst);
00805 buffer[ 43 ] = char(myhst >> 8);
00806 buffer[ 44 ] = char(myhst >> 16);
00807 buffer[ 45 ] = char(myhst >> 24);
00808
00809
00810 strncpy( buffer + 46, path, path.length() );
00811
00812
00813
00814
char *extfield = buffer + 46 + path.length();
00815 extfield[0] =
'U';
00816 extfield[1] =
'T';
00817 extfield[2] = 5;
00818 extfield[3] = 0;
00819 extfield[4] = 1 | 2 | 4;
00820
00821
00822
unsigned long time = (
unsigned long)it.current()->date();
00823 extfield[5] = char(time);
00824 extfield[6] = char(time >> 8);
00825 extfield[7] = char(time >> 16);
00826 extfield[8] = char(time >> 24);
00827
00828 crc = crc32(crc, (Bytef *)buffer, bufferSize );
00829
bool ok = (
device()->writeBlock( buffer, bufferSize ) == bufferSize );
00830
delete[] buffer;
00831
if ( !ok )
00832
return false;
00833 }
00834 Q_LONG centraldirendoffset =
device()->at();
00835
00836
00837
00838
00839 buffer[ 0 ] =
'P';
00840 buffer[ 1 ] =
'K';
00841 buffer[ 2 ] = 5;
00842 buffer[ 3 ] = 6;
00843
00844 buffer[ 4 ] = 0;
00845 buffer[ 5 ] = 0;
00846
00847 buffer[ 6 ] = 0;
00848 buffer[ 7 ] = 0;
00849
00850
int count = d->m_fileList.count();
00851
00852
00853
00854 buffer[ 8 ] = char(count);
00855 buffer[ 9 ] = char(count >> 8);
00856
00857 buffer[ 10 ] = buffer[ 8 ];
00858 buffer[ 11 ] = buffer[ 9 ];
00859
00860
int cdsize = centraldirendoffset - centraldiroffset;
00861 buffer[ 12 ] = char(cdsize);
00862 buffer[ 13 ] = char(cdsize >> 8);
00863 buffer[ 14 ] = char(cdsize >> 16);
00864 buffer[ 15 ] = char(cdsize >> 24);
00865
00866
00867
00868
00869 buffer[ 16 ] = char(centraldiroffset);
00870 buffer[ 17 ] = char(centraldiroffset >> 8);
00871 buffer[ 18 ] = char(centraldiroffset >> 16);
00872 buffer[ 19 ] = char(centraldiroffset >> 24);
00873
00874 buffer[ 20 ] = 0;
00875 buffer[ 21 ] = 0;
00876
00877
if (
device()->writeBlock( buffer, 22 ) != 22 )
00878
return false;
00879
00880
00881
return true;
00882 }
00883
00884
00885 bool KZip::writeFile(
const QString& name,
const QString& user,
const QString& group, uint size,
const char* data )
00886 {
00887 mode_t mode = 0100644;
00888 time_t the_time = time(0);
00889
return KArchive::writeFile( name, user, group, size, mode, the_time,
00890 the_time, the_time, data );
00891 }
00892
00893
00894 bool KZip::writeFile(
const QString& name,
const QString& user,
00895
const QString& group, uint size, mode_t perm,
00896 time_t atime, time_t mtime, time_t ctime,
00897
const char* data ) {
00898
return KArchive::writeFile(name, user, group, size, perm, atime, mtime,
00899 ctime, data);
00900 }
00901
00902
00903 bool KZip::prepareWriting(
const QString& name,
const QString& user,
const QString& group, uint size )
00904 {
00905 mode_t dflt_perm = 0100644;
00906 time_t the_time = time(0);
00907
return prepareWriting(name,user,group,size,dflt_perm,
00908 the_time,the_time,the_time);
00909 }
00910
00911
00912 bool KZip::prepareWriting(
const QString& name,
const QString& user,
00913
const QString& group, uint size, mode_t perm,
00914 time_t atime, time_t mtime, time_t ctime) {
00915
return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime);
00916 }
00917
00918
bool KZip::prepareWriting_impl(
const QString &name,
const QString &user,
00919
const QString &group, uint , mode_t perm,
00920 time_t atime, time_t mtime, time_t ctime) {
00921
00922
if ( !
isOpened() )
00923 {
00924 qWarning(
"KZip::writeFile: You must open the zip file before writing to it\n");
00925
return false;
00926 }
00927
00928
if ( ! (
mode() & IO_WriteOnly ) )
00929 {
00930 qWarning(
"KZip::writeFile: You must open the zip file for writing\n");
00931
return false;
00932 }
00933
00934
00935
if ( !
device()->at( d->m_offset ) ) {
00936
kdWarning(7040) <<
"prepareWriting_impl: cannot seek in ZIP file. Disk full?" <<
endl;
00937
return false;
00938 }
00939
00940
00941
00942
00943
00944
QPtrListIterator<KZipFileEntry> it( d->m_fileList );
00945
00946
00947
for ( ; it.current() ; ++it )
00948 {
00949
00950
if (
name == it.current()->path() )
00951 {
00952
00953 d->m_fileList.remove();
00954 }
00955
00956 }
00957
00958
KArchiveDirectory* parentDir =
rootDir();
00959
QString fileName( name );
00960
int i =
name.findRev(
'/' );
00961
if ( i != -1 )
00962 {
00963
QString dir =
name.left( i );
00964
fileName =
name.mid( i + 1 );
00965
00966 parentDir =
findOrCreate( dir );
00967 }
00968
00969
00970 KZipFileEntry * e =
new KZipFileEntry(
this, fileName, perm, mtime, user, group, QString::null,
00971 name,
device()->at() + 30 +
name.length(),
00972 0 , d->m_compression, 0 );
00973 e->setHeaderStart(
device()->at() );
00974
00975 parentDir->
addEntry( e );
00976
00977 d->m_currentFile = e;
00978 d->m_fileList.append( e );
00979
00980
int extra_field_len = 0;
00981
if ( d->m_extraField ==
ModificationTime )
00982 extra_field_len = 17;
00983
00984
00985
QCString encodedName = QFile::encodeName(name);
00986
int bufferSize = extra_field_len + encodedName.length() + 30;
00987
00988
char* buffer =
new char[ bufferSize ];
00989
00990 buffer[ 0 ] =
'P';
00991 buffer[ 1 ] =
'K';
00992 buffer[ 2 ] = 3;
00993 buffer[ 3 ] = 4;
00994
00995 buffer[ 4 ] = 0x14;
00996 buffer[ 5 ] = 0;
00997
00998 buffer[ 6 ] = 0;
00999 buffer[ 7 ] = 0;
01000
01001 buffer[ 8 ] = char(e->encoding());
01002 buffer[ 9 ] = char(e->encoding() >> 8);
01003
01004 transformToMsDos( e->datetime(), &buffer[ 10 ] );
01005
01006 buffer[ 14 ] =
'C';
01007 buffer[ 15 ] =
'R';
01008 buffer[ 16 ] =
'C';
01009 buffer[ 17 ] =
'q';
01010
01011 buffer[ 18 ] =
'C';
01012 buffer[ 19 ] =
'S';
01013 buffer[ 20 ] =
'I';
01014 buffer[ 21 ] =
'Z';
01015
01016 buffer[ 22 ] =
'U';
01017 buffer[ 23 ] =
'S';
01018 buffer[ 24 ] =
'I';
01019 buffer[ 25 ] =
'Z';
01020
01021 buffer[ 26 ] = (uchar)(encodedName.length());
01022 buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
01023
01024 buffer[ 28 ] = (uchar)(extra_field_len);
01025 buffer[ 29 ] = (uchar)(extra_field_len >> 8);
01026
01027
01028 strncpy( buffer + 30, encodedName, encodedName.length() );
01029
01030
01031
if ( d->m_extraField ==
ModificationTime )
01032 {
01033
char *extfield = buffer + 30 + encodedName.length();
01034
01035 extfield[0] =
'U';
01036 extfield[1] =
'T';
01037 extfield[2] = 13;
01038 extfield[3] = 0;
01039 extfield[4] = 1 | 2 | 4;
01040
01041 extfield[5] = char(mtime);
01042 extfield[6] = char(mtime >> 8);
01043 extfield[7] = char(mtime >> 16);
01044 extfield[8] = char(mtime >> 24);
01045
01046 extfield[9] = char(atime);
01047 extfield[10] = char(atime >> 8);
01048 extfield[11] = char(atime >> 16);
01049 extfield[12] = char(atime >> 24);
01050
01051 extfield[13] = char(ctime);
01052 extfield[14] = char(ctime >> 8);
01053 extfield[15] = char(ctime >> 16);
01054 extfield[16] = char(ctime >> 24);
01055 }
01056
01057
01058
bool b = (
device()->writeBlock( buffer, bufferSize ) == bufferSize );
01059 d->m_crc = 0L;
01060
delete[] buffer;
01061
01062 Q_ASSERT( b );
01063
if (!b)
01064
return false;
01065
01066
01067
01068
if ( d->m_compression == 0 ) {
01069 d->m_currentDev =
device();
01070
return true;
01071 }
01072
01073 d->m_currentDev =
KFilterDev::device(
device(),
"application/x-gzip",
false );
01074 Q_ASSERT( d->m_currentDev );
01075
if ( !d->m_currentDev )
01076
return false;
01077 static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders();
01078
01079 b = d->m_currentDev->open( IO_WriteOnly );
01080 Q_ASSERT( b );
01081
return b;
01082 }
01083
01084 bool KZip::doneWriting( uint size )
01085 {
01086
if ( d->m_currentFile->encoding() == 8 ) {
01087
01088 (
void)d->m_currentDev->writeBlock( 0, 0 );
01089
delete d->m_currentDev;
01090 }
01091
01092 d->m_currentDev = 0L;
01093
01094 Q_ASSERT( d->m_currentFile );
01095
01096
01097
01098 d->m_currentFile->setSize(size);
01099
int extra_field_len = 0;
01100
if ( d->m_extraField ==
ModificationTime )
01101 extra_field_len = 17;
01102
01103
int csize =
device()->at() -
01104 d->m_currentFile->headerStart() - 30 -
01105 d->m_currentFile->path().length() - extra_field_len;
01106 d->m_currentFile->setCompressedSize(csize);
01107
01108
01109
01110
01111
01112 d->m_currentFile->setCRC32( d->m_crc );
01113
01114 d->m_currentFile = 0L;
01115
01116
01117 d->m_offset =
device()->at();
01118
return true;
01119 }
01120
01121 bool KZip::writeSymLink(
const QString &name,
const QString &target,
01122
const QString &user,
const QString &group,
01123 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01124
return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime);
01125 }
01126
01127
bool KZip::writeSymLink_impl(
const QString &name,
const QString &target,
01128
const QString &user,
const QString &group,
01129 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01130
01131
01132
01133 perm |= S_IFLNK;
01134 Compression c =
compression();
01135
setCompression(NoCompression);
01136
01137
if (!
prepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) {
01138
kdWarning() <<
"KZip::writeFile prepareWriting failed" <<
endl;
01139
setCompression(c);
01140
return false;
01141 }
01142
01143
QCString symlink_target = QFile::encodeName(target);
01144
if (!
writeData(symlink_target, symlink_target.length())) {
01145
kdWarning() <<
"KZip::writeFile writeData failed" <<
endl;
01146
setCompression(c);
01147
return false;
01148 }
01149
01150
if (!
doneWriting(symlink_target.length())) {
01151
kdWarning() <<
"KZip::writeFile doneWriting failed" <<
endl;
01152
setCompression(c);
01153
return false;
01154 }
01155
01156
setCompression(c);
01157
return true;
01158 }
01159
01160
void KZip::virtual_hook(
int id,
void* data )
01161 {
01162
switch (
id) {
01163
case VIRTUAL_WRITE_DATA: {
01164 WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data);
01165 params->retval = writeData_impl( params->data, params->size );
01166
break;
01167 }
01168
case VIRTUAL_WRITE_SYMLINK: {
01169 WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
01170 params->retval = writeSymLink_impl(*params->name,*params->target,
01171 *params->user,*params->group,params->perm,
01172 params->atime,params->mtime,params->ctime);
01173
break;
01174 }
01175
case VIRTUAL_PREPARE_WRITING: {
01176 PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
01177 params->retval = prepareWriting_impl(*params->name,*params->user,
01178 *params->group,params->size,params->perm,
01179 params->atime,params->mtime,params->ctime);
01180
break;
01181 }
01182
default:
01183 KArchive::virtual_hook(
id, data );
01184 }
01185 }
01186
01187
01188 bool KZip::writeData(
const char * c, uint i)
01189 {
01190
return KArchive::writeData( c, i );
01191 }
01192
01193
bool KZip::writeData_impl(
const char * c, uint i)
01194 {
01195 Q_ASSERT( d->m_currentFile );
01196 Q_ASSERT( d->m_currentDev );
01197
if (!d->m_currentFile || !d->m_currentDev)
01198
return false;
01199
01200
01201
01202 d->m_crc = crc32(d->m_crc, (
const Bytef *) c , i);
01203
01204 Q_LONG written = d->m_currentDev->writeBlock( c, i );
01205
01206
return written == (Q_LONG)i;
01207 }
01208
01209 void KZip::setCompression( Compression c )
01210 {
01211 d->m_compression = ( c ==
NoCompression ) ? 0 : 8;
01212 }
01213
01214 KZip::Compression KZip::compression()
const
01215
{
01216
return ( d->m_compression == 8 ) ?
DeflateCompression :
NoCompression;
01217 }
01218
01219 void KZip::setExtraField( ExtraField ef )
01220 {
01221 d->m_extraField = ef;
01222 }
01223
01224 KZip::ExtraField KZip::extraField()
const
01225
{
01226
return d->m_extraField;
01227 }
01228
01230
01231
QByteArray KZipFileEntry::data()
const
01232
{
01233
QIODevice* dev =
device();
01234
QByteArray arr;
01235
if ( dev ) {
01236 arr = dev->readAll();
01237
delete dev;
01238 }
01239
return arr;
01240 }
01241
01242
QIODevice* KZipFileEntry::device()
const
01243
{
01244
01245
01246
KLimitedIODevice* limitedDev =
new KLimitedIODevice( archive()->
device(), position(), compressedSize() );
01247
if ( encoding() == 0 || compressedSize() == 0 )
01248
return limitedDev;
01249
01250
if ( encoding() == 8 )
01251 {
01252
01253
QIODevice* filterDev =
KFilterDev::device( limitedDev,
"application/x-gzip" );
01254
if ( !filterDev )
01255
return 0L;
01256 static_cast<KFilterDev *>(filterDev)->setSkipHeaders();
01257
bool b = filterDev->open( IO_ReadOnly );
01258 Q_ASSERT( b );
01259
return filterDev;
01260 }
01261
01262
kdError() <<
"This zip file contains files compressed with method "
01263 << encoding() <<
", this method is currently not supported by KZip,"
01264 <<
" please use a command-line tool to handle this file." <<
endl;
01265
return 0L;
01266 }
01267