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 entry = new KArchiveDirectory( this, entryName, access, time, rootDir()->user(), rootDir()->group(), QString::null );
00336 else
00337 {
00338 entry = new KZipFileEntry( this, entryName, access, time, rootDir()->user(), rootDir()->group(), QString::null,
00339 name, dataoffset, ucsize, cmethod, csize );
00340 static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset );
00341
00342 d->m_fileList.append( static_cast<KZipFileEntry *>( entry ) );
00343 }
00344
00345 if ( pos == -1 )
00346 {
00347 rootDir()->addEntry(entry);
00348 }
00349 else
00350 {
00351
00352 QString path = QDir::cleanDirPath( name.left( pos ) );
00353
00354 KArchiveDirectory * tdir = findOrCreate( path );
00355 tdir->addEntry(entry);
00356 }
00357
00358
00359 offset += 46 + commlen + extralen + namelen;
00360 bool b = dev->at(offset);
00361 Q_ASSERT( b );
00362 if ( !b )
00363 return false;
00364 }
00365 else
00366 {
00367 kdWarning(7040) << "Invalid ZIP file. Unrecognized header at offset " << offset << endl;
00368
00369 return false;
00370 }
00371 }
00372
00373 return true;
00374 }
00375
00376 bool KZip::closeArchive()
00377 {
00378 if ( ! ( mode() & IO_WriteOnly ) )
00379 {
00380
00381 return true;
00382 }
00383
00384
00385
00386
00387 char buffer[ 22 ];
00388 uLong crc = crc32(0L, Z_NULL, 0);
00389
00390 Q_LONG centraldiroffset = device()->at();
00391
00392 Q_LONG atbackup = device()->at();
00393 QPtrListIterator<KZipFileEntry> it( d->m_fileList );
00394
00395 for ( ; it.current() ; ++it )
00396 {
00397 device()->at( it.current()->headerStart() + 14 );
00398
00399
00400
00401
00402 uLong mycrc = it.current()->crc32();
00403 buffer[0] = char(mycrc);
00404 buffer[1] = char(mycrc >> 8);
00405 buffer[2] = char(mycrc >> 16);
00406 buffer[3] = char(mycrc >> 24);
00407
00408 int mysize1 = it.current()->compressedSize();
00409 buffer[4] = char(mysize1);
00410 buffer[5] = char(mysize1 >> 8);
00411 buffer[6] = char(mysize1 >> 16);
00412 buffer[7] = char(mysize1 >> 24);
00413
00414 int myusize = it.current()->size();
00415 buffer[8] = char(myusize);
00416 buffer[9] = char(myusize >> 8);
00417 buffer[10] = char(myusize >> 16);
00418 buffer[11] = char(myusize >> 24);
00419
00420 device()->writeBlock( buffer, 12 );
00421 }
00422 device()->at( atbackup );
00423
00424 for ( it.toFirst(); it.current() ; ++it )
00425 {
00426
00427
00428
00429 QCString path = QFile::encodeName(it.current()->path());
00430
00431 int bufferSize = path.length() + 46;
00432 char* buffer = new char[ bufferSize ];
00433
00434 memset(buffer, 0, 46);
00435
00436 const char head[] =
00437 {
00438 'P', 'K', 1, 2,
00439 0x14, 0,
00440 0x14, 0
00441 };
00442
00443
00444
00445 qmemmove(buffer, head, sizeof(head));
00446
00447 if ( it.current()->encoding() == 8 )
00448 {
00449 buffer[ 8 ] = 8,
00450 buffer[ 10 ] = 8;
00451 }
00452
00453 transformToMsDos( it.current()->datetime(), &buffer[ 12 ] );
00454
00455 uLong mycrc = it.current()->crc32();
00456 buffer[ 16 ] = char(mycrc);
00457 buffer[ 17 ] = char(mycrc >> 8);
00458 buffer[ 18 ] = char(mycrc >> 16);
00459 buffer[ 19 ] = char(mycrc >> 24);
00460
00461 int mysize1 = it.current()->compressedSize();
00462 buffer[ 20 ] = char(mysize1);
00463 buffer[ 21 ] = char(mysize1 >> 8);
00464 buffer[ 22 ] = char(mysize1 >> 16);
00465 buffer[ 23 ] = char(mysize1 >> 24);
00466
00467 int mysize = it.current()->size();
00468 buffer[ 24 ] = char(mysize);
00469 buffer[ 25 ] = char(mysize >> 8);
00470 buffer[ 26 ] = char(mysize >> 16);
00471 buffer[ 27 ] = char(mysize >> 24);
00472
00473 buffer[ 28 ] = char(it.current()->path().length());
00474 buffer[ 29 ] = char(it.current()->path().length() >> 8);
00475
00476 int myhst = it.current()->headerStart();
00477 buffer[ 42 ] = char(myhst);
00478 buffer[ 43 ] = char(myhst >> 8);
00479 buffer[ 44 ] = char(myhst >> 16);
00480 buffer[ 45 ] = char(myhst >> 24);
00481
00482
00483 strncpy( buffer + 46, path, path.length() );
00484
00485 crc = crc32(crc, (Bytef *)buffer, bufferSize );
00486 device()->writeBlock( buffer, bufferSize );
00487 delete[] buffer;
00488 }
00489 Q_LONG centraldirendoffset = device()->at();
00490
00491
00492
00493
00494 buffer[ 0 ] = 'P';
00495 buffer[ 1 ] = 'K';
00496 buffer[ 2 ] = 5;
00497 buffer[ 3 ] = 6;
00498
00499 buffer[ 4 ] = 0;
00500 buffer[ 5 ] = 0;
00501
00502 buffer[ 6 ] = 0;
00503 buffer[ 7 ] = 0;
00504
00505 int count = d->m_fileList.count();
00506
00507
00508
00509 buffer[ 8 ] = char(count);
00510 buffer[ 9 ] = char(count >> 8);
00511
00512 buffer[ 10 ] = buffer[ 8 ];
00513 buffer[ 11 ] = buffer[ 9 ];
00514
00515 int cdsize = centraldirendoffset - centraldiroffset;
00516 buffer[ 12 ] = char(cdsize);
00517 buffer[ 13 ] = char(cdsize >> 8);
00518 buffer[ 14 ] = char(cdsize >> 16);
00519 buffer[ 15 ] = char(cdsize >> 24);
00520
00521
00522
00523
00524 buffer[ 16 ] = char(centraldiroffset);
00525 buffer[ 17 ] = char(centraldiroffset >> 8);
00526 buffer[ 18 ] = char(centraldiroffset >> 16);
00527 buffer[ 19 ] = char(centraldiroffset >> 24);
00528
00529 buffer[ 20 ] = 0;
00530 buffer[ 21 ] = 0;
00531
00532 device()->writeBlock( buffer, 22);
00533
00534
00535 return true;
00536 }
00537
00538
00539 bool KZip::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
00540 {
00541
00542 device()->at( d->m_offset );
00543 if ( !prepareWriting( name, user, group, size ) )
00544 {
00545 kdWarning() << "KZip::writeFile prepareWriting failed" << endl;
00546 return false;
00547 }
00548
00549
00550 if ( data && size && !writeData( data, size ) )
00551 {
00552 kdWarning() << "KZip::writeFile writeData failed" << endl;
00553 return false;
00554 }
00555
00556 if ( ! doneWriting( size ) )
00557 {
00558 kdWarning() << "KZip::writeFile doneWriting failed" << endl;
00559 return false;
00560 }
00561
00562 d->m_offset = device()->at();
00563 return true;
00564 }
00565
00566 bool KZip::prepareWriting( const QString& name, const QString& user, const QString& group, uint )
00567 {
00568
00569 if ( !isOpened() )
00570 {
00571 qWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
00572 return false;
00573 }
00574
00575 if ( ! ( mode() & IO_WriteOnly ) )
00576 {
00577 qWarning( "KZip::writeFile: You must open the zip file for writing\n");
00578 return false;
00579 }
00580
00581
00582
00583
00584
00585 QPtrListIterator<KZipFileEntry> it( d->m_fileList );
00586
00587
00588 for ( ; it.current() ; ++it )
00589 {
00590
00591 if (name == it.current()->path() )
00592 {
00593
00594 d->m_fileList.remove();
00595 }
00596
00597 }
00598
00599 KArchiveDirectory* parentDir = rootDir();
00600 QString fileName( name );
00601 int i = name.findRev( '/' );
00602 if ( i != -1 )
00603 {
00604 QString dir = name.left( i );
00605 fileName = name.mid( i + 1 );
00606
00607 parentDir = findOrCreate( dir );
00608 }
00609
00610 int time = getActualTime();
00611
00612
00613 KZipFileEntry * e = new KZipFileEntry( this, fileName, 0777, time, user, group, QString::null,
00614 name, device()->at() + 30 + name.length(),
00615 0 , d->m_compression, 0 );
00616 e->setHeaderStart( device()->at() );
00617
00618 parentDir->addEntry( e );
00619
00620 d->m_currentFile = e;
00621 d->m_fileList.append( e );
00622
00623
00624 QCString encodedName = QFile::encodeName(name);
00625 int bufferSize = encodedName.length() + 30;
00626
00627 char* buffer = new char[ bufferSize ];
00628
00629 buffer[ 0 ] = 'P';
00630 buffer[ 1 ] = 'K';
00631 buffer[ 2 ] = 3;
00632 buffer[ 3 ] = 4;
00633
00634 buffer[ 4 ] = 0x14;
00635 buffer[ 5 ] = 0;
00636
00637 buffer[ 6 ] = 0;
00638 buffer[ 7 ] = 0;
00639
00640 buffer[ 8 ] = char(e->encoding());
00641 buffer[ 9 ] = char(e->encoding() >> 8);
00642
00643 transformToMsDos( e->datetime(), &buffer[ 10 ] );
00644
00645 buffer[ 14 ] = 'C';
00646 buffer[ 15 ] = 'R';
00647 buffer[ 16 ] = 'C';
00648 buffer[ 17 ] = 'q';
00649
00650 buffer[ 18 ] = 'C';
00651 buffer[ 19 ] = 'S';
00652 buffer[ 20 ] = 'I';
00653 buffer[ 21 ] = 'Z';
00654
00655 buffer[ 22 ] = 'U';
00656 buffer[ 23 ] = 'S';
00657 buffer[ 24 ] = 'I';
00658 buffer[ 25 ] = 'Z';
00659
00660 buffer[ 26 ] = (uchar)(encodedName.length());
00661 buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
00662
00663 buffer[ 28 ] = 0;
00664 buffer[ 29 ] = 0;
00665
00666
00667 strncpy( buffer + 30, encodedName, encodedName.length() );
00668
00669
00670 bool b = (device()->writeBlock( buffer, bufferSize ) == bufferSize );
00671 d->m_crc = 0L;
00672 delete[] buffer;
00673
00674 Q_ASSERT( b );
00675 if (!b)
00676 return false;
00677
00678
00679
00680 if ( d->m_compression == 0 ) {
00681 d->m_currentDev = device();
00682 return true;
00683 }
00684
00685 d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
00686 Q_ASSERT( d->m_currentDev );
00687 if ( !d->m_currentDev )
00688 return false;
00689 static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders();
00690
00691 b = d->m_currentDev->open( IO_WriteOnly );
00692 Q_ASSERT( b );
00693 return b;
00694 }
00695
00696 bool KZip::doneWriting( uint size )
00697 {
00698 if ( d->m_currentFile->encoding() == 8 ) {
00699
00700 (void)d->m_currentDev->writeBlock( 0, 0 );
00701 delete d->m_currentDev;
00702 }
00703
00704 d->m_currentDev = 0L;
00705
00706 Q_ASSERT( d->m_currentFile );
00707
00708
00709
00710 d->m_currentFile->setSize(size);
00711 int csize = device()->at() -
00712 d->m_currentFile->headerStart() - 30 -
00713 d->m_currentFile->path().length();
00714 d->m_currentFile->setCompressedSize(csize);
00715
00716
00717
00718
00719
00720 d->m_currentFile->setCRC32( d->m_crc );
00721
00722 d->m_currentFile = 0L;
00723 return true;
00724 }
00725
00726 void KZip::virtual_hook( int id, void* data )
00727 {
00728 KArchive::virtual_hook( id, data );
00729 }
00730
00731 bool KZip::writeData(const char * c, uint i)
00732 {
00733 Q_ASSERT( d->m_currentFile );
00734 Q_ASSERT( d->m_currentDev );
00735 if (!d->m_currentFile || !d->m_currentDev)
00736 return false;
00737
00738
00739
00740 d->m_crc = crc32(d->m_crc, (const Bytef *) c , i);
00741
00742 Q_LONG written = d->m_currentDev->writeBlock( c, i );
00743
00744 Q_ASSERT( written == (Q_LONG)i );
00745 return written == (Q_LONG)i;
00746 }
00747
00748 void KZip::setCompression( Compression c )
00749 {
00750 d->m_compression = ( c == NoCompression ) ? 0 : 8;
00751 }
00752
00753 KZip::Compression KZip::compression() const
00754 {
00755 return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
00756 }
00757
00759
00760 QByteArray KZipFileEntry::data() const
00761 {
00762 QIODevice* dev = device();
00763 QByteArray arr = dev->readAll();
00764 delete dev;
00765 return arr;
00766 }
00767
00768 QIODevice* KZipFileEntry::device() const
00769 {
00770
00771
00772 KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
00773 if ( encoding() == 0 || compressedSize() == 0 )
00774 return limitedDev;
00775
00776 if ( encoding() == 8 )
00777 {
00778
00779 QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
00780 if ( !filterDev )
00781 return 0L;
00782 static_cast<KFilterDev *>(filterDev)->setSkipHeaders();
00783 bool b = filterDev->open( IO_ReadOnly );
00784 Q_ASSERT( b );
00785 return filterDev;
00786 }
00787
00788 kdError() << "This zip file contains files compressed with method "
00789 << encoding() <<", this method is currently not supported by KZip,"
00790 <<" please use a command-line tool to handle this file." << endl;
00791 return 0L;
00792 }
00793