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 | 0111;
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.left( pos ) );
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