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