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 device()->at( it.current()->headerStart() + 14 );
00652
00653
00654
00655
00656 uLong mycrc = it.current()->crc32();
00657 buffer[0] = char(mycrc);
00658 buffer[1] = char(mycrc >> 8);
00659 buffer[2] = char(mycrc >> 16);
00660 buffer[3] = char(mycrc >> 24);
00661
00662 int mysize1 = it.current()->compressedSize();
00663 buffer[4] = char(mysize1);
00664 buffer[5] = char(mysize1 >> 8);
00665 buffer[6] = char(mysize1 >> 16);
00666 buffer[7] = char(mysize1 >> 24);
00667
00668 int myusize = it.current()->size();
00669 buffer[8] = char(myusize);
00670 buffer[9] = char(myusize >> 8);
00671 buffer[10] = char(myusize >> 16);
00672 buffer[11] = char(myusize >> 24);
00673
00674 device()->writeBlock( buffer, 12 );
00675 }
00676 device()->at( atbackup );
00677
00678 for ( it.toFirst(); it.current() ; ++it )
00679 {
00680
00681
00682
00683 QCString path = QFile::encodeName(it.current()->path());
00684
00685 const int extra_field_len = 9;
00686 int bufferSize = extra_field_len + path.length() + 46;
00687 char* buffer = new char[ bufferSize ];
00688
00689 memset(buffer, 0, 46);
00690
00691 const char head[] =
00692 {
00693 'P', 'K', 1, 2,
00694 0x14, 3,
00695 0x14, 0
00696 };
00697
00698
00699
00700 qmemmove(buffer, head, sizeof(head));
00701
00702 buffer[ 10 ] = char(it.current()->encoding());
00703 buffer[ 11 ] = char(it.current()->encoding() >> 8);
00704
00705 transformToMsDos( it.current()->datetime(), &buffer[ 12 ] );
00706
00707 uLong mycrc = it.current()->crc32();
00708 buffer[ 16 ] = char(mycrc);
00709 buffer[ 17 ] = char(mycrc >> 8);
00710 buffer[ 18 ] = char(mycrc >> 16);
00711 buffer[ 19 ] = char(mycrc >> 24);
00712
00713 int mysize1 = it.current()->compressedSize();
00714 buffer[ 20 ] = char(mysize1);
00715 buffer[ 21 ] = char(mysize1 >> 8);
00716 buffer[ 22 ] = char(mysize1 >> 16);
00717 buffer[ 23 ] = char(mysize1 >> 24);
00718
00719 int mysize = it.current()->size();
00720 buffer[ 24 ] = char(mysize);
00721 buffer[ 25 ] = char(mysize >> 8);
00722 buffer[ 26 ] = char(mysize >> 16);
00723 buffer[ 27 ] = char(mysize >> 24);
00724
00725 buffer[ 28 ] = char(it.current()->path().length());
00726 buffer[ 29 ] = char(it.current()->path().length() >> 8);
00727
00728 buffer[ 30 ] = char(extra_field_len);
00729 buffer[ 31 ] = char(extra_field_len >> 8);
00730
00731 buffer[ 40 ] = char(it.current()->permissions());
00732 buffer[ 41 ] = char(it.current()->permissions() >> 8);
00733
00734 int myhst = it.current()->headerStart();
00735 buffer[ 42 ] = char(myhst);
00736 buffer[ 43 ] = char(myhst >> 8);
00737 buffer[ 44 ] = char(myhst >> 16);
00738 buffer[ 45 ] = char(myhst >> 24);
00739
00740
00741 strncpy( buffer + 46, path, path.length() );
00742
00743
00744
00745 char *extfield = buffer + 46 + path.length();
00746 extfield[0] = 'U';
00747 extfield[1] = 'T';
00748 extfield[2] = 5;
00749 extfield[3] = 0;
00750 extfield[4] = 1 | 2 | 4;
00751
00752
00753 unsigned long time = (unsigned long)it.current()->date();
00754 extfield[5] = char(time);
00755 extfield[6] = char(time >> 8);
00756 extfield[7] = char(time >> 16);
00757 extfield[8] = char(time >> 24);
00758
00759 crc = crc32(crc, (Bytef *)buffer, bufferSize );
00760 device()->writeBlock( buffer, bufferSize );
00761 delete[] buffer;
00762 }
00763 Q_LONG centraldirendoffset = device()->at();
00764
00765
00766
00767
00768 buffer[ 0 ] = 'P';
00769 buffer[ 1 ] = 'K';
00770 buffer[ 2 ] = 5;
00771 buffer[ 3 ] = 6;
00772
00773 buffer[ 4 ] = 0;
00774 buffer[ 5 ] = 0;
00775
00776 buffer[ 6 ] = 0;
00777 buffer[ 7 ] = 0;
00778
00779 int count = d->m_fileList.count();
00780
00781
00782
00783 buffer[ 8 ] = char(count);
00784 buffer[ 9 ] = char(count >> 8);
00785
00786 buffer[ 10 ] = buffer[ 8 ];
00787 buffer[ 11 ] = buffer[ 9 ];
00788
00789 int cdsize = centraldirendoffset - centraldiroffset;
00790 buffer[ 12 ] = char(cdsize);
00791 buffer[ 13 ] = char(cdsize >> 8);
00792 buffer[ 14 ] = char(cdsize >> 16);
00793 buffer[ 15 ] = char(cdsize >> 24);
00794
00795
00796
00797
00798 buffer[ 16 ] = char(centraldiroffset);
00799 buffer[ 17 ] = char(centraldiroffset >> 8);
00800 buffer[ 18 ] = char(centraldiroffset >> 16);
00801 buffer[ 19 ] = char(centraldiroffset >> 24);
00802
00803 buffer[ 20 ] = 0;
00804 buffer[ 21 ] = 0;
00805
00806 device()->writeBlock( buffer, 22);
00807
00808
00809 return true;
00810 }
00811
00812
00813 bool KZip::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
00814 {
00815 mode_t mode = 0100644;
00816 time_t the_time = time(0);
00817 return KArchive::writeFile( name, user, group, size, mode, the_time,
00818 the_time, the_time, data );
00819 }
00820
00821
00822 bool KZip::writeFile( const QString& name, const QString& user,
00823 const QString& group, uint size, mode_t perm,
00824 time_t atime, time_t mtime, time_t ctime,
00825 const char* data ) {
00826 return KArchive::writeFile(name, user, group, size, perm, atime, mtime,
00827 ctime, data);
00828 }
00829
00830
00831 bool KZip::prepareWriting( const QString& name, const QString& user, const QString& group, uint size )
00832 {
00833 mode_t dflt_perm = 0100644;
00834 time_t the_time = time(0);
00835 return prepareWriting(name,user,group,size,dflt_perm,
00836 the_time,the_time,the_time);
00837 }
00838
00839
00840 bool KZip::prepareWriting(const QString& name, const QString& user,
00841 const QString& group, uint size, mode_t perm,
00842 time_t atime, time_t mtime, time_t ctime) {
00843 return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime);
00844 }
00845
00846 bool KZip::prepareWriting_impl(const QString &name, const QString &user,
00847 const QString &group, uint , mode_t perm,
00848 time_t atime, time_t mtime, time_t ctime) {
00849
00850 if ( !isOpened() )
00851 {
00852 qWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
00853 return false;
00854 }
00855
00856 if ( ! ( mode() & IO_WriteOnly ) )
00857 {
00858 qWarning( "KZip::writeFile: You must open the zip file for writing\n");
00859 return false;
00860 }
00861
00862
00863 device()->at( d->m_offset );
00864
00865
00866
00867
00868
00869 QPtrListIterator<KZipFileEntry> it( d->m_fileList );
00870
00871
00872 for ( ; it.current() ; ++it )
00873 {
00874
00875 if (name == it.current()->path() )
00876 {
00877
00878 d->m_fileList.remove();
00879 }
00880
00881 }
00882
00883 KArchiveDirectory* parentDir = rootDir();
00884 QString fileName( name );
00885 int i = name.findRev( '/' );
00886 if ( i != -1 )
00887 {
00888 QString dir = name.left( i );
00889 fileName = name.mid( i + 1 );
00890
00891 parentDir = findOrCreate( dir );
00892 }
00893
00894
00895 KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, QString::null,
00896 name, device()->at() + 30 + name.length(),
00897 0 , d->m_compression, 0 );
00898 e->setHeaderStart( device()->at() );
00899
00900 parentDir->addEntry( e );
00901
00902 d->m_currentFile = e;
00903 d->m_fileList.append( e );
00904
00905 int extra_field_len = 0;
00906 if ( d->m_extraField == ModificationTime )
00907 extra_field_len = 17;
00908
00909
00910 QCString encodedName = QFile::encodeName(name);
00911 int bufferSize = extra_field_len + encodedName.length() + 30;
00912
00913 char* buffer = new char[ bufferSize ];
00914
00915 buffer[ 0 ] = 'P';
00916 buffer[ 1 ] = 'K';
00917 buffer[ 2 ] = 3;
00918 buffer[ 3 ] = 4;
00919
00920 buffer[ 4 ] = 0x14;
00921 buffer[ 5 ] = 0;
00922
00923 buffer[ 6 ] = 0;
00924 buffer[ 7 ] = 0;
00925
00926 buffer[ 8 ] = char(e->encoding());
00927 buffer[ 9 ] = char(e->encoding() >> 8);
00928
00929 transformToMsDos( e->datetime(), &buffer[ 10 ] );
00930
00931 buffer[ 14 ] = 'C';
00932 buffer[ 15 ] = 'R';
00933 buffer[ 16 ] = 'C';
00934 buffer[ 17 ] = 'q';
00935
00936 buffer[ 18 ] = 'C';
00937 buffer[ 19 ] = 'S';
00938 buffer[ 20 ] = 'I';
00939 buffer[ 21 ] = 'Z';
00940
00941 buffer[ 22 ] = 'U';
00942 buffer[ 23 ] = 'S';
00943 buffer[ 24 ] = 'I';
00944 buffer[ 25 ] = 'Z';
00945
00946 buffer[ 26 ] = (uchar)(encodedName.length());
00947 buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
00948
00949 buffer[ 28 ] = (uchar)(extra_field_len);
00950 buffer[ 29 ] = (uchar)(extra_field_len >> 8);
00951
00952
00953 strncpy( buffer + 30, encodedName, encodedName.length() );
00954
00955
00956 if ( d->m_extraField == ModificationTime )
00957 {
00958 char *extfield = buffer + 30 + encodedName.length();
00959
00960 extfield[0] = 'U';
00961 extfield[1] = 'T';
00962 extfield[2] = 13;
00963 extfield[3] = 0;
00964 extfield[4] = 1 | 2 | 4;
00965
00966 extfield[5] = char(mtime);
00967 extfield[6] = char(mtime >> 8);
00968 extfield[7] = char(mtime >> 16);
00969 extfield[8] = char(mtime >> 24);
00970
00971 extfield[9] = char(atime);
00972 extfield[10] = char(atime >> 8);
00973 extfield[11] = char(atime >> 16);
00974 extfield[12] = char(atime >> 24);
00975
00976 extfield[13] = char(ctime);
00977 extfield[14] = char(ctime >> 8);
00978 extfield[15] = char(ctime >> 16);
00979 extfield[16] = char(ctime >> 24);
00980 }
00981
00982
00983 bool b = (device()->writeBlock( buffer, bufferSize ) == bufferSize );
00984 d->m_crc = 0L;
00985 delete[] buffer;
00986
00987 Q_ASSERT( b );
00988 if (!b)
00989 return false;
00990
00991
00992
00993 if ( d->m_compression == 0 ) {
00994 d->m_currentDev = device();
00995 return true;
00996 }
00997
00998 d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
00999 Q_ASSERT( d->m_currentDev );
01000 if ( !d->m_currentDev )
01001 return false;
01002 static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders();
01003
01004 b = d->m_currentDev->open( IO_WriteOnly );
01005 Q_ASSERT( b );
01006 return b;
01007 }
01008
01009 bool KZip::doneWriting( uint size )
01010 {
01011 if ( d->m_currentFile->encoding() == 8 ) {
01012
01013 (void)d->m_currentDev->writeBlock( 0, 0 );
01014 delete d->m_currentDev;
01015 }
01016
01017 d->m_currentDev = 0L;
01018
01019 Q_ASSERT( d->m_currentFile );
01020
01021
01022
01023 d->m_currentFile->setSize(size);
01024 int extra_field_len = 0;
01025 if ( d->m_extraField == ModificationTime )
01026 extra_field_len = 17;
01027
01028 int csize = device()->at() -
01029 d->m_currentFile->headerStart() - 30 -
01030 d->m_currentFile->path().length() - extra_field_len;
01031 d->m_currentFile->setCompressedSize(csize);
01032
01033
01034
01035
01036
01037 d->m_currentFile->setCRC32( d->m_crc );
01038
01039 d->m_currentFile = 0L;
01040
01041
01042 d->m_offset = device()->at();
01043 return true;
01044 }
01045
01046 bool KZip::writeSymLink(const QString &name, const QString &target,
01047 const QString &user, const QString &group,
01048 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01049 return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime);
01050 }
01051
01052 bool KZip::writeSymLink_impl(const QString &name, const QString &target,
01053 const QString &user, const QString &group,
01054 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01055
01056
01057
01058 perm |= S_IFLNK;
01059 Compression c = compression();
01060 setCompression(NoCompression);
01061
01062 if (!prepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) {
01063 kdWarning() << "KZip::writeFile prepareWriting failed" << endl;
01064 setCompression(c);
01065 return false;
01066 }
01067
01068 QCString symlink_target = QFile::encodeName(target);
01069 if (!writeData(symlink_target, symlink_target.length())) {
01070 kdWarning() << "KZip::writeFile writeData failed" << endl;
01071 setCompression(c);
01072 return false;
01073 }
01074
01075 if (!doneWriting(symlink_target.length())) {
01076 kdWarning() << "KZip::writeFile doneWriting failed" << endl;
01077 setCompression(c);
01078 return false;
01079 }
01080
01081 setCompression(c);
01082 return true;
01083 }
01084
01085 void KZip::virtual_hook( int id, void* data )
01086 {
01087 switch (id) {
01088 case VIRTUAL_WRITE_DATA: {
01089 WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data);
01090 params->retval = writeData_impl( params->data, params->size );
01091 break;
01092 }
01093 case VIRTUAL_WRITE_SYMLINK: {
01094 WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
01095 params->retval = writeSymLink_impl(*params->name,*params->target,
01096 *params->user,*params->group,params->perm,
01097 params->atime,params->mtime,params->ctime);
01098 break;
01099 }
01100 case VIRTUAL_PREPARE_WRITING: {
01101 PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
01102 params->retval = prepareWriting_impl(*params->name,*params->user,
01103 *params->group,params->size,params->perm,
01104 params->atime,params->mtime,params->ctime);
01105 break;
01106 }
01107 default:
01108 KArchive::virtual_hook( id, data );
01109 }
01110 }
01111
01112
01113 bool KZip::writeData(const char * c, uint i)
01114 {
01115 return KArchive::writeData( c, i );
01116 }
01117
01118 bool KZip::writeData_impl(const char * c, uint i)
01119 {
01120 Q_ASSERT( d->m_currentFile );
01121 Q_ASSERT( d->m_currentDev );
01122 if (!d->m_currentFile || !d->m_currentDev)
01123 return false;
01124
01125
01126
01127 d->m_crc = crc32(d->m_crc, (const Bytef *) c , i);
01128
01129 Q_LONG written = d->m_currentDev->writeBlock( c, i );
01130
01131 Q_ASSERT( written == (Q_LONG)i );
01132 return written == (Q_LONG)i;
01133 }
01134
01135 void KZip::setCompression( Compression c )
01136 {
01137 d->m_compression = ( c == NoCompression ) ? 0 : 8;
01138 }
01139
01140 KZip::Compression KZip::compression() const
01141 {
01142 return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
01143 }
01144
01145 void KZip::setExtraField( ExtraField ef )
01146 {
01147 d->m_extraField = ef;
01148 }
01149
01150 KZip::ExtraField KZip::extraField() const
01151 {
01152 return d->m_extraField;
01153 }
01154
01156
01157 QByteArray KZipFileEntry::data() const
01158 {
01159 QIODevice* dev = device();
01160 QByteArray arr;
01161 if ( dev ) {
01162 arr = dev->readAll();
01163 delete dev;
01164 }
01165 return arr;
01166 }
01167
01168 QIODevice* KZipFileEntry::device() const
01169 {
01170
01171
01172 KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
01173 if ( encoding() == 0 || compressedSize() == 0 )
01174 return limitedDev;
01175
01176 if ( encoding() == 8 )
01177 {
01178
01179 QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
01180 if ( !filterDev )
01181 return 0L;
01182 static_cast<KFilterDev *>(filterDev)->setSkipHeaders();
01183 bool b = filterDev->open( IO_ReadOnly );
01184 Q_ASSERT( b );
01185 return filterDev;
01186 }
01187
01188 kdError() << "This zip file contains files compressed with method "
01189 << encoding() <<", this method is currently not supported by KZip,"
01190 <<" please use a command-line tool to handle this file." << endl;
01191 return 0L;
01192 }
01193