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
00041
00042
00043 #include <sys/time.h>
00044
00045 #include <qfile.h>
00046 #include <qdir.h>
00047 #include <time.h>
00048 #include <string.h>
00049 #include <qdatetime.h>
00050 #include <kdebug.h>
00051 #include <qptrlist.h>
00052 #include <kmimetype.h>
00053 #include <zlib.h>
00054
00055 #include "kfilterdev.h"
00056 #include "kzip.h"
00057 #include "klimitediodevice.h"
00058
00059 static void transformToMsDos(const QDateTime& dt, char* buffer)
00060 {
00061 if ( dt.isValid() )
00062 {
00063 Q_UINT16 time =
00064 ( dt.time().hour() << 11 )
00065 | ( dt.time().minute() << 5 )
00066 | ( dt.time().second() >> 1 );
00067
00068 buffer[0] = char(time);
00069 buffer[1] = char(time >> 8);
00070
00071 Q_UINT16 date =
00072 ( ( dt.date().year() - 1980 ) << 9 )
00073 | ( dt.date().month() << 5 )
00074 | ( dt.date().day() );
00075
00076 buffer[2] = char(date);
00077 buffer[3] = char(date >> 8);
00078 }
00079 else
00080 {
00081 buffer[0] = 0;
00082 buffer[1] = 0;
00083 buffer[2] = 33;
00084 buffer[3] = 0;
00085 }
00086 }
00087
00088 static int getActualTime( void )
00089 {
00090 timeval value;
00091 gettimeofday( &value, NULL );
00092 return value.tv_sec;
00093 }
00094
00098
00099 class KZip::KZipPrivate
00100 {
00101 public:
00102 KZipPrivate()
00103 : m_crc( 0 ),
00104 m_currentFile( 0L ),
00105 m_currentDev( 0L ),
00106 m_compression( 8 ),
00107 m_offset( 0L ) { }
00108
00109 unsigned long m_crc;
00110 KZipFileEntry* m_currentFile;
00111 QIODevice* m_currentDev;
00112 QPtrList<KZipFileEntry> m_fileList;
00113 int m_compression;
00114 unsigned int m_offset;
00115
00116
00117
00118 };
00119
00120 KZip::KZip( const QString& filename )
00121 : KArchive( 0L )
00122 {
00123
00124 m_filename = filename;
00125 d = new KZipPrivate;
00126 setDevice( new QFile( filename ) );
00127 }
00128
00129 KZip::KZip( QIODevice * dev )
00130 : KArchive( dev )
00131 {
00132
00133 d = new KZipPrivate;
00134 }
00135
00136 KZip::~KZip()
00137 {
00138
00139
00140 if( isOpened() )
00141 close();
00142 if ( !m_filename.isEmpty() )
00143 delete device();
00144 delete d;
00145 }
00146
00147 bool KZip::openArchive( int mode )
00148 {
00149
00150 d->m_fileList.clear();
00151
00152 if ( mode == IO_WriteOnly )
00153 return true;
00154 if ( mode != IO_ReadOnly && mode != IO_ReadWrite )
00155 {
00156 kdWarning(7040) << "Unsupported mode " << mode << endl;
00157 return false;
00158 }
00159
00160 char buffer[47];
00161
00162
00163
00164 QIODevice* dev = device();
00165
00166 uint offset = 0;
00167 int n;
00168
00169 for (;;)
00170 {
00171 n = dev->readBlock( buffer, 4 );
00172
00173 if (n < 4)
00174 {
00175 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#1)" << endl;
00176
00177 return false;
00178 }
00179
00180 if ( !memcmp( buffer, "PK\5\6", 4 ) )
00181 break;
00182
00183 if ( !memcmp( buffer, "PK\3\4", 4 ) )
00184 {
00185 dev->at( dev->at() + 2 );
00186
00187
00188
00189
00190
00191 dev->readBlock( buffer, 2 );
00192 if ( buffer[0] & 8 )
00193 {
00194 bool foundSignature = false;
00195
00196 while (!foundSignature)
00197 {
00198 n = dev->readBlock( buffer, 1 );
00199 if (n < 1)
00200 {
00201 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)" << endl;
00202 return false;
00203 }
00204
00205 if ( buffer[0] != 'P' )
00206 continue;
00207
00208 n = dev->readBlock( buffer, 3 );
00209 if (n < 3)
00210 {
00211 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)" << endl;
00212 return false;
00213 }
00214
00215 if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
00216 {
00217 foundSignature = true;
00218 dev->at( dev->at() + 12 );
00219 }
00220 }
00221 }
00222 else
00223 {
00224
00225
00226 dev->at( dev->at() + 10 );
00227
00228 uint skip;
00229
00230 n = dev->readBlock( buffer, 4 );
00231 skip = (uchar)buffer[3] << 24 | (uchar)buffer[2] << 16 |
00232 (uchar)buffer[1] << 8 | (uchar)buffer[0];
00233
00234 dev->at( dev->at() + 4 );
00235 n = dev->readBlock( buffer, 2 );
00236 skip += (uchar)buffer[1] << 8 | (uchar)buffer[0];
00237
00238 n = dev->readBlock( buffer, 2 );
00239 skip += (uchar)buffer[1] << 8 | (uchar)buffer[0];
00240
00241 dev->at( dev->at() + skip );
00242 offset += 30 + skip;
00243 }
00244 }
00245 else if ( !memcmp( buffer, "PK\1\2", 4 ) )
00246 {
00247
00248
00249
00250
00251 offset = dev->at() - 4;
00252
00253
00254 if ( d->m_offset == 0L ) d->m_offset = offset;
00255
00256 n = dev->readBlock( buffer + 4, 42 );
00257 if (n < 42) {
00258 kdWarning(7040) << "Invalid ZIP file, central entry too short" << endl;
00259 return false;
00260 }
00261
00262 int namelen = (uchar)buffer[29] << 8 | (uchar)buffer[28];
00263 char* bufferName = new char[ namelen + 1 ];
00264 n = dev->readBlock( bufferName, namelen );
00265 if ( n < namelen )
00266 kdWarning(7040) << "Invalid ZIP file. Name not completely read" << endl;
00267 QString name( QString::fromLocal8Bit(bufferName, namelen) );
00268 delete[] bufferName;
00269
00270
00271
00272
00273 int extralen = (uchar)buffer[31] << 8 | (uchar)buffer[30];
00274
00275 int commlen = (uchar)buffer[33] << 8 | (uchar)buffer[32];
00276
00277 int cmethod = (uchar)buffer[11] << 8 | (uchar)buffer[10];
00278
00279
00280
00281
00282
00283 uint ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 |
00284 (uchar)buffer[25] << 8 | (uchar)buffer[24];
00285
00286 uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 |
00287 (uchar)buffer[21] << 8 | (uchar)buffer[20];
00288
00289
00290 uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 |
00291 (uchar)buffer[43] << 8 | (uchar)buffer[42];
00292
00293
00294
00295
00296
00297 char localbuf[5];
00298 int save_at = dev->at();
00299 dev->at( localheaderoffset + 28 );
00300 dev->readBlock( localbuf, 4);
00301 int localextralen = (uchar)localbuf[1] << 8 | (uchar)localbuf[0];
00302 dev->at(save_at);
00303
00304
00305
00306
00307 uint dataoffset = localheaderoffset + 30 + localextralen + namelen;
00308
00309
00310
00311
00312
00313 bool isdir = false;
00314 int access = 0777;
00315 int time = getActualTime();
00316
00317 QString entryName;
00318
00319 if ( name.endsWith( "/" ) )
00320 {
00321 isdir = true;
00322 name = name.left( name.length() - 1 );
00323 access |= S_IFDIR;
00324 }
00325
00326 int pos = name.findRev( '/' );
00327 if ( pos == -1 )
00328 entryName = name;
00329 else
00330 entryName = name.mid( pos + 1 );
00331 Q_ASSERT( !entryName.isEmpty() );
00332
00333 KArchiveEntry* entry;
00334 if ( isdir )
00335 {
00336 QString path = QDir::cleanDirPath( name.left( pos ) );
00337 KArchiveEntry* ent = rootDir()->entry( path );
00338 if ( ent && ent->isDirectory() )
00339 {
00340
00341 entry = 0L;
00342 }
00343 else
00344 {
00345 entry = new KArchiveDirectory( this, entryName, access, time, rootDir()->user(), rootDir()->group(), QString::null );
00346
00347 }
00348 }
00349 else
00350 {
00351 entry = new KZipFileEntry( this, entryName, access, time, rootDir()->user(), rootDir()->group(), QString::null,
00352 name, dataoffset, ucsize, cmethod, csize );
00353 static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset );
00354
00355 d->m_fileList.append( static_cast<KZipFileEntry *>( entry ) );
00356 }
00357
00358 if ( entry )
00359 {
00360 if ( pos == -1 )
00361 {
00362 rootDir()->addEntry(entry);
00363 }
00364 else
00365 {
00366
00367 QString path = QDir::cleanDirPath( name.left( pos ) );
00368
00369 KArchiveDirectory * tdir = findOrCreate( path );
00370 tdir->addEntry(entry);
00371 }
00372 }
00373
00374
00375 offset += 46 + commlen + extralen + namelen;
00376 bool b = dev->at(offset);
00377 Q_ASSERT( b );
00378 if ( !b )
00379 return false;
00380 }
00381 else
00382 {
00383 kdWarning(7040) << "Invalid ZIP file. Unrecognized header at offset " << offset << endl;
00384
00385 return false;
00386 }
00387 }
00388
00389 return true;
00390 }
00391
00392 bool KZip::closeArchive()
00393 {
00394 if ( ! ( mode() & IO_WriteOnly ) )
00395 {
00396
00397 return true;
00398 }
00399
00400
00401
00402
00403 char buffer[ 22 ];
00404 uLong crc = crc32(0L, Z_NULL, 0);
00405
00406 Q_LONG centraldiroffset = device()->at();
00407
00408 Q_LONG atbackup = device()->at();
00409 QPtrListIterator<KZipFileEntry> it( d->m_fileList );
00410
00411 for ( ; it.current() ; ++it )
00412 {
00413 device()->at( it.current()->headerStart() + 14 );
00414
00415
00416
00417
00418 uLong mycrc = it.current()->crc32();
00419 buffer[0] = char(mycrc);
00420 buffer[1] = char(mycrc >> 8);
00421 buffer[2] = char(mycrc >> 16);
00422 buffer[3] = char(mycrc >> 24);
00423
00424 int mysize1 = it.current()->compressedSize();
00425 buffer[4] = char(mysize1);
00426 buffer[5] = char(mysize1 >> 8);
00427 buffer[6] = char(mysize1 >> 16);
00428 buffer[7] = char(mysize1 >> 24);
00429
00430 int myusize = it.current()->size();
00431 buffer[8] = char(myusize);
00432 buffer[9] = char(myusize >> 8);
00433 buffer[10] = char(myusize >> 16);
00434 buffer[11] = char(myusize >> 24);
00435
00436 device()->writeBlock( buffer, 12 );
00437 }
00438 device()->at( atbackup );
00439
00440 for ( it.toFirst(); it.current() ; ++it )
00441 {
00442
00443
00444
00445 QCString path = QFile::encodeName(it.current()->path());
00446
00447 int bufferSize = path.length() + 46;
00448 char* buffer = new char[ bufferSize ];
00449
00450 memset(buffer, 0, 46);
00451
00452 const char head[] =
00453 {
00454 'P', 'K', 1, 2,
00455 0x14, 0,
00456 0x14, 0
00457 };
00458
00459
00460
00461 qmemmove(buffer, head, sizeof(head));
00462
00463 buffer[ 10 ] = char(it.current()->encoding());
00464 buffer[ 11 ] = char(it.current()->encoding() >> 8);
00465
00466
00467 transformToMsDos( it.current()->datetime(), &buffer[ 12 ] );
00468
00469 uLong mycrc = it.current()->crc32();
00470 buffer[ 16 ] = char(mycrc);
00471 buffer[ 17 ] = char(mycrc >> 8);
00472 buffer[ 18 ] = char(mycrc >> 16);
00473 buffer[ 19 ] = char(mycrc >> 24);
00474
00475 int mysize1 = it.current()->compressedSize();
00476 buffer[ 20 ] = char(mysize1);
00477 buffer[ 21 ] = char(mysize1 >> 8);
00478 buffer[ 22 ] = char(mysize1 >> 16);
00479 buffer[ 23 ] = char(mysize1 >> 24);
00480
00481 int mysize = it.current()->size();
00482 buffer[ 24 ] = char(mysize);
00483 buffer[ 25 ] = char(mysize >> 8);
00484 buffer[ 26 ] = char(mysize >> 16);
00485 buffer[ 27 ] = char(mysize >> 24);
00486
00487 buffer[ 28 ] = char(it.current()->path().length());
00488 buffer[ 29 ] = char(it.current()->path().length() >> 8);
00489
00490 int myhst = it.current()->headerStart();
00491 buffer[ 42 ] = char(myhst);
00492 buffer[ 43 ] = char(myhst >> 8);
00493 buffer[ 44 ] = char(myhst >> 16);
00494 buffer[ 45 ] = char(myhst >> 24);
00495
00496
00497 strncpy( buffer + 46, path, path.length() );
00498
00499 crc = crc32(crc, (Bytef *)buffer, bufferSize );
00500 device()->writeBlock( buffer, bufferSize );
00501 delete[] buffer;
00502 }
00503 Q_LONG centraldirendoffset = device()->at();
00504
00505
00506
00507
00508 buffer[ 0 ] = 'P';
00509 buffer[ 1 ] = 'K';
00510 buffer[ 2 ] = 5;
00511 buffer[ 3 ] = 6;
00512
00513 buffer[ 4 ] = 0;
00514 buffer[ 5 ] = 0;
00515
00516 buffer[ 6 ] = 0;
00517 buffer[ 7 ] = 0;
00518
00519 int count = d->m_fileList.count();
00520
00521
00522
00523 buffer[ 8 ] = char(count);
00524 buffer[ 9 ] = char(count >> 8);
00525
00526 buffer[ 10 ] = buffer[ 8 ];
00527 buffer[ 11 ] = buffer[ 9 ];
00528
00529 int cdsize = centraldirendoffset - centraldiroffset;
00530 buffer[ 12 ] = char(cdsize);
00531 buffer[ 13 ] = char(cdsize >> 8);
00532 buffer[ 14 ] = char(cdsize >> 16);
00533 buffer[ 15 ] = char(cdsize >> 24);
00534
00535
00536
00537
00538 buffer[ 16 ] = char(centraldiroffset);
00539 buffer[ 17 ] = char(centraldiroffset >> 8);
00540 buffer[ 18 ] = char(centraldiroffset >> 16);
00541 buffer[ 19 ] = char(centraldiroffset >> 24);
00542
00543 buffer[ 20 ] = 0;
00544 buffer[ 21 ] = 0;
00545
00546 device()->writeBlock( buffer, 22);
00547
00548
00549 return true;
00550 }
00551
00552
00553 bool KZip::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
00554 {
00555
00556 device()->at( d->m_offset );
00557 if ( !prepareWriting( name, user, group, size ) )
00558 {
00559 kdWarning() << "KZip::writeFile prepareWriting failed" << endl;
00560 return false;
00561 }
00562
00563
00564 if ( data && size && !writeData( data, size ) )
00565 {
00566 kdWarning() << "KZip::writeFile writeData failed" << endl;
00567 return false;
00568 }
00569
00570 if ( ! doneWriting( size ) )
00571 {
00572 kdWarning() << "KZip::writeFile doneWriting failed" << endl;
00573 return false;
00574 }
00575
00576 d->m_offset = device()->at();
00577 return true;
00578 }
00579
00580 bool KZip::prepareWriting( const QString& name, const QString& user, const QString& group, uint )
00581 {
00582
00583 if ( !isOpened() )
00584 {
00585 qWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
00586 return false;
00587 }
00588
00589 if ( ! ( mode() & IO_WriteOnly ) )
00590 {
00591 qWarning( "KZip::writeFile: You must open the zip file for writing\n");
00592 return false;
00593 }
00594
00595
00596
00597
00598
00599 QPtrListIterator<KZipFileEntry> it( d->m_fileList );
00600
00601
00602 for ( ; it.current() ; ++it )
00603 {
00604
00605 if (name == it.current()->path() )
00606 {
00607
00608 d->m_fileList.remove();
00609 }
00610
00611 }
00612
00613 KArchiveDirectory* parentDir = rootDir();
00614 QString fileName( name );
00615 int i = name.findRev( '/' );
00616 if ( i != -1 )
00617 {
00618 QString dir = name.left( i );
00619 fileName = name.mid( i + 1 );
00620
00621 parentDir = findOrCreate( dir );
00622 }
00623
00624 int time = getActualTime();
00625
00626
00627 KZipFileEntry * e = new KZipFileEntry( this, fileName, 0777, time, user, group, QString::null,
00628 name, device()->at() + 30 + name.length(),
00629 0 , d->m_compression, 0 );
00630 e->setHeaderStart( device()->at() );
00631
00632 parentDir->addEntry( e );
00633
00634 d->m_currentFile = e;
00635 d->m_fileList.append( e );
00636
00637
00638 QCString encodedName = QFile::encodeName(name);
00639 int bufferSize = encodedName.length() + 30;
00640
00641 char* buffer = new char[ bufferSize ];
00642
00643 buffer[ 0 ] = 'P';
00644 buffer[ 1 ] = 'K';
00645 buffer[ 2 ] = 3;
00646 buffer[ 3 ] = 4;
00647
00648 buffer[ 4 ] = 0x14;
00649 buffer[ 5 ] = 0;
00650
00651 buffer[ 6 ] = 0;
00652 buffer[ 7 ] = 0;
00653
00654 buffer[ 8 ] = char(e->encoding());
00655 buffer[ 9 ] = char(e->encoding() >> 8);
00656
00657 transformToMsDos( e->datetime(), &buffer[ 10 ] );
00658
00659 buffer[ 14 ] = 'C';
00660 buffer[ 15 ] = 'R';
00661 buffer[ 16 ] = 'C';
00662 buffer[ 17 ] = 'q';
00663
00664 buffer[ 18 ] = 'C';
00665 buffer[ 19 ] = 'S';
00666 buffer[ 20 ] = 'I';
00667 buffer[ 21 ] = 'Z';
00668
00669 buffer[ 22 ] = 'U';
00670 buffer[ 23 ] = 'S';
00671 buffer[ 24 ] = 'I';
00672 buffer[ 25 ] = 'Z';
00673
00674 buffer[ 26 ] = (uchar)(encodedName.length());
00675 buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
00676
00677 buffer[ 28 ] = 0;
00678 buffer[ 29 ] = 0;
00679
00680
00681 strncpy( buffer + 30, encodedName, encodedName.length() );
00682
00683
00684 bool b = (device()->writeBlock( buffer, bufferSize ) == bufferSize );
00685 d->m_crc = 0L;
00686 delete[] buffer;
00687
00688 Q_ASSERT( b );
00689 if (!b)
00690 return false;
00691
00692
00693
00694 if ( d->m_compression == 0 ) {
00695 d->m_currentDev = device();
00696 return true;
00697 }
00698
00699 d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
00700 Q_ASSERT( d->m_currentDev );
00701 if ( !d->m_currentDev )
00702 return false;
00703 static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders();
00704
00705 b = d->m_currentDev->open( IO_WriteOnly );
00706 Q_ASSERT( b );
00707 return b;
00708 }
00709
00710 bool KZip::doneWriting( uint size )
00711 {
00712 if ( d->m_currentFile->encoding() == 8 ) {
00713
00714 (void)d->m_currentDev->writeBlock( 0, 0 );
00715 delete d->m_currentDev;
00716 }
00717
00718 d->m_currentDev = 0L;
00719
00720 Q_ASSERT( d->m_currentFile );
00721
00722
00723
00724 d->m_currentFile->setSize(size);
00725 int csize = device()->at() -
00726 d->m_currentFile->headerStart() - 30 -
00727 d->m_currentFile->path().length();
00728 d->m_currentFile->setCompressedSize(csize);
00729
00730
00731
00732
00733
00734 d->m_currentFile->setCRC32( d->m_crc );
00735
00736 d->m_currentFile = 0L;
00737 return true;
00738 }
00739
00740 void KZip::virtual_hook( int id, void* data )
00741 {
00742 KArchive::virtual_hook( id, data );
00743 }
00744
00745 bool KZip::writeData(const char * c, uint i)
00746 {
00747 Q_ASSERT( d->m_currentFile );
00748 Q_ASSERT( d->m_currentDev );
00749 if (!d->m_currentFile || !d->m_currentDev)
00750 return false;
00751
00752
00753
00754 d->m_crc = crc32(d->m_crc, (const Bytef *) c , i);
00755
00756 Q_LONG written = d->m_currentDev->writeBlock( c, i );
00757
00758 Q_ASSERT( written == (Q_LONG)i );
00759 return written == (Q_LONG)i;
00760 }
00761
00762 void KZip::setCompression( Compression c )
00763 {
00764 d->m_compression = ( c == NoCompression ) ? 0 : 8;
00765 }
00766
00767 KZip::Compression KZip::compression() const
00768 {
00769 return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
00770 }
00771
00773
00774 QByteArray KZipFileEntry::data() const
00775 {
00776 QIODevice* dev = device();
00777 QByteArray arr = dev->readAll();
00778 delete dev;
00779 return arr;
00780 }
00781
00782 QIODevice* KZipFileEntry::device() const
00783 {
00784
00785
00786 KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
00787 if ( encoding() == 0 || compressedSize() == 0 )
00788 return limitedDev;
00789
00790 if ( encoding() == 8 )
00791 {
00792
00793 QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
00794 if ( !filterDev )
00795 return 0L;
00796 static_cast<KFilterDev *>(filterDev)->setSkipHeaders();
00797 bool b = filterDev->open( IO_ReadOnly );
00798 Q_ASSERT( b );
00799 return filterDev;
00800 }
00801
00802 kdError() << "This zip file contains files compressed with method "
00803 << encoding() <<", this method is currently not supported by KZip,"
00804 <<" please use a command-line tool to handle this file." << endl;
00805 return 0L;
00806 }
00807