kio Library API Documentation

kzip.cpp

00001 
00002 /* This file is part of the KDE libraries
00003    Copyright (C) 2000 David Faure <faure@kde.org>
00004    Copyright (C) 2002 Holger Schroeder <holger-kde@holgis.net>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License version 2 as published by the Free Software Foundation.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018    Boston, MA 02111-1307, USA.
00019 */
00020 
00021 /*
00022     This class implements a kioslave to acces ZIP files from KDE.
00023     you can use it in IO_ReadOnly or in IO_WriteOnly mode, and it
00024     behaves just as expected (i hope ;-) ).
00025     It can also be used in IO_ReadWrite mode, in this case one can
00026     append files to an existing zip archive. when you append new files, which
00027     are not yet in the zip, it works as expected, they are appended at the end.
00028     when you append a file, which is already in the file, the reference to the
00029     old file is dropped and the new one is added to the zip. but the
00030     old data from the file itself is not deleted, it is still in the
00031     zipfile. so when you want to have a small and garbagefree zipfile,
00032     just read the contents of the appended zipfile and write it to a new one
00033     in IO_WriteOnly mode. exspecially take care of this, when you donīt want
00034     to leak information of how intermediate versions of files in the zip
00035     were looking.
00036     for more information on the zip fileformat go to
00037     http://www.pkware.com/support/appnote.html .
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 )    // 5 bit hour
00065            | ( dt.time().minute() << 5 )   // 6 bit minute
00066            | ( dt.time().second() >> 1 );  // 5 bit double seconds
00067 
00068         buffer[0] = char(time);
00069         buffer[1] = char(time >> 8);
00070 
00071         Q_UINT16 date =
00072              ( ( dt.date().year() - 1980 ) << 9 ) // 7 bit year 1980-based
00073            | ( dt.date().month() << 5 )           // 4 bit month
00074            | ( dt.date().day() );                 // 5 bit day
00075 
00076         buffer[2] = char(date);
00077         buffer[3] = char(date >> 8);
00078     }
00079     else // !dt.isValid(), assume 1980-01-01 midnight
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;         // checksum
00110     KZipFileEntry*          m_currentFile; // file currently being written
00111     QIODevice*              m_currentDev;  // filterdev used to write to the above file
00112     QPtrList<KZipFileEntry> m_fileList;    // flat list of all files, for the index (saves a recursive method ;)
00113     int                     m_compression;
00114     unsigned int            m_offset; // holds the offset of the place in the zip,
00115     // where new data can be appended. after openarchive it points to 0, when in
00116     // writeonly mode, or it points to the beginning of the central directory.
00117     // each call to writefile updates this value.
00118 };
00119 
00120 KZip::KZip( const QString& filename )
00121     : KArchive( 0L )
00122 {
00123     //kdDebug(7040) << "KZip(filename) reached." << endl;
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     //kdDebug(7040) << "KZip::KZip( QIODevice * dev) reached." << endl;
00133     d = new KZipPrivate;
00134 }
00135 
00136 KZip::~KZip()
00137 {
00138     // mjarrett: Closes to prevent ~KArchive from aborting w/o device
00139     //kdDebug(7040) << "~KZip reached." << endl;
00140     if( isOpened() )
00141         close();
00142     if ( !m_filename.isEmpty() )
00143         delete device(); // we created it ourselves
00144     delete d;
00145 }
00146 
00147 bool KZip::openArchive( int mode )
00148 {
00149     //kdDebug(7040) << "openarchive reached." << endl;
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     // Check that it's a valid ZIP file
00163     // KArchive::open() opened the underlying device already.
00164     QIODevice* dev = device();
00165 
00166     uint offset = 0; // holds offset, where we read
00167     int n;
00168 
00169     for (;;) // repeat until 'end of entries' signature is reached
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 ) ) // 'end of entries'
00181             break;
00182 
00183         if ( !memcmp( buffer, "PK\3\4", 4 ) ) // local file header
00184         {
00185             dev->at( dev->at() + 2 ); // skip 'version needed to extract'
00186 
00187             // we have to take care of the 'general purpose bit flag'.
00188             // if bit 3 is set, the header doesn't contain the length of
00189             // the file and we look for the signature 'PK\7\8'.
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 ); // skip the 'data_descriptor'
00219                     }
00220                 }
00221             }
00222             else
00223             {
00224                 // here we calculate the length of the file in the zip
00225                 // with headers and jump to the next header.
00226                 dev->at( dev->at() + 10 );
00227 
00228                 uint skip;
00229 
00230                 n = dev->readBlock( buffer, 4 ); // compressed file size
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 ); // file name length
00236                 skip += (uchar)buffer[1] << 8 | (uchar)buffer[0];
00237 
00238                 n = dev->readBlock( buffer, 2 ); // extra field length
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 ) ) // central block
00246         {
00247 
00248             // so we reached the central header at the end of the zip file
00249             // here we get all interesting data out of the central header
00250             // of a file
00251             offset = dev->at() - 4;
00252 
00253             //set offset for appending new files
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; // not long enough for valid entry
00259                 return false;
00260             }
00261             // length of the filename (well, pathname indeed)
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             //kdDebug(7040) << "name: " << name << endl;
00271             // only in central header ! see below.
00272             // length of extra attributes
00273             int extralen = (uchar)buffer[31] << 8 | (uchar)buffer[30];
00274             // length of comment for this file
00275             int commlen =  (uchar)buffer[33] << 8 | (uchar)buffer[32];
00276             // compression method of this file
00277             int cmethod =  (uchar)buffer[11] << 8 | (uchar)buffer[10];
00278 
00279             //kdDebug(7040) << "cmethod: " << cmethod << endl;
00280             //kdDebug(7040) << "extralen: " << extralen << endl;
00281 
00282             // uncompressed file size
00283             uint ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 |
00284                 (uchar)buffer[25] << 8 | (uchar)buffer[24];
00285             // compressed file size
00286             uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 |
00287                 (uchar)buffer[21] << 8 | (uchar)buffer[20];
00288 
00289             // offset of local header
00290             uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 |
00291                 (uchar)buffer[43] << 8 | (uchar)buffer[42];
00292 
00293             // some clever people use different extra field lengths
00294             // in the central header and in the local header... funny.
00295             // so we need to get the localextralen to calculate the offset
00296             // from localheaderstart to dataoffset
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             //kdDebug(7040) << "localextralen: " << localextralen << endl;
00305 
00306             // offset, where the real data for uncompression starts
00307             uint dataoffset = localheaderoffset + 30 + localextralen + namelen; //comment only in central header
00308 
00309             //kdDebug(7040) << "esize: " << esize << endl;
00310             //kdDebug(7040) << "eoffset: " << eoffset << endl;
00311             //kdDebug(7040) << "csize: " << csize << endl;
00312 
00313             bool isdir = false;
00314             int access = 0777; // TODO available in zip file?
00315             int time = getActualTime();
00316 
00317             QString entryName;
00318 
00319             if ( name.endsWith( "/" ) ) // Entries with a trailing slash are directories
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                 //kdDebug(7040) << "KZipFileEntry created" << endl;
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                 // In some tar files we can find dir/./file => call cleanDirPath
00352                 QString path = QDir::cleanDirPath( name.left( pos ) );
00353                 // Ensure container directory exists, create otherwise
00354                 KArchiveDirectory * tdir = findOrCreate( path );
00355                 tdir->addEntry(entry);
00356             }
00357 
00358             //calculate offset to next entry
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     //kdDebug(7040) << "*** done *** " << endl;
00373     return true;
00374 }
00375 
00376 bool KZip::closeArchive()
00377 {
00378     if ( ! ( mode() & IO_WriteOnly ) )
00379     {
00380         //kdDebug(7040) << "closearchive readonly reached." << endl;
00381         return true;
00382     }
00383     //ReadWrite or WriteOnly
00384     //write all central dir file entries
00385 
00386     // to be written at the end of the file...
00387     char buffer[ 22 ]; // first used for 12, then for 22 at the end
00388     uLong crc = crc32(0L, Z_NULL, 0);
00389 
00390     Q_LONG centraldiroffset = device()->at();
00391     //kdDebug(7040) << "closearchive: centraldiroffset: " << centraldiroffset << endl;
00392     Q_LONG atbackup = device()->at();
00393     QPtrListIterator<KZipFileEntry> it( d->m_fileList );
00394 
00395     for ( ; it.current() ; ++it )
00396     {   //set crc and compressed size in each local file header
00397         device()->at( it.current()->headerStart() + 14 );
00398     //kdDebug(7040) << "closearchive setcrcandcsize: filename: "
00399     //    << it.current()->path()
00400     //    << " encoding: "<< it.current()->encoding() << endl;
00401 
00402         uLong mycrc = it.current()->crc32();
00403         buffer[0] = char(mycrc); // crc checksum, at headerStart+14
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); // compressed file size, at headerStart+18
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); // uncompressed file size, at headerStart+22
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         //kdDebug(7040) << "closearchive: filename: " << it.current()->path()
00427         //              << " encoding: "<< it.current()->encoding() << endl;
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); // zero is a nice default for most header fields
00435 
00436         const char head[] =
00437         {
00438             'P', 'K', 1, 2, // central file header signature
00439             0x14, 0,        // version made by
00440             0x14, 0         // version needed to extract
00441         };
00442 
00443     // I do not know why memcpy is not working here
00444         //memcpy(buffer, head, sizeof(head));
00445         qmemmove(buffer, head, sizeof(head));
00446 
00447         if ( it.current()->encoding() == 8 )
00448         {
00449             buffer[ 8 ] = 8, // general purpose bit flag, deflated
00450             buffer[ 10 ] = 8; // compression method, deflated
00451         }
00452 
00453         transformToMsDos( it.current()->datetime(), &buffer[ 12 ] );
00454 
00455         uLong mycrc = it.current()->crc32();
00456         buffer[ 16 ] = char(mycrc); // crc checksum
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); // compressed file size
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); // uncompressed file size
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()); // filename length
00474         buffer[ 29 ] = char(it.current()->path().length() >> 8);
00475 
00476         int myhst = it.current()->headerStart();
00477         buffer[ 42 ] = char(myhst); //relative offset of local header
00478         buffer[ 43 ] = char(myhst >> 8);
00479         buffer[ 44 ] = char(myhst >> 16);
00480         buffer[ 45 ] = char(myhst >> 24);
00481 
00482         // file name
00483         strncpy( buffer + 46, path, path.length() );
00484     //kdDebug(7040) << "closearchive length to write: " << bufferSize << endl;
00485         crc = crc32(crc, (Bytef *)buffer, bufferSize );
00486         device()->writeBlock( buffer, bufferSize );
00487         delete[] buffer;
00488     }
00489     Q_LONG centraldirendoffset = device()->at();
00490     //kdDebug(7040) << "closearchive: centraldirendoffset: " << centraldirendoffset << endl;
00491     //kdDebug(7040) << "closearchive: device()->at(): " << device()->at() << endl;
00492 
00493     //write end of central dir record.
00494     buffer[ 0 ] = 'P'; //end of central dir signature
00495     buffer[ 1 ] = 'K';
00496     buffer[ 2 ] = 5;
00497     buffer[ 3 ] = 6;
00498 
00499     buffer[ 4 ] = 0; // number of this disk
00500     buffer[ 5 ] = 0;
00501 
00502     buffer[ 6 ] = 0; // number of disk with start of central dir
00503     buffer[ 7 ] = 0;
00504 
00505     int count = d->m_fileList.count();
00506     //kdDebug(7040) << "number of files (count): " << count << endl;
00507 
00508 
00509     buffer[ 8 ] = char(count); // total number of entries in central dir of
00510     buffer[ 9 ] = char(count >> 8); // this disk
00511 
00512     buffer[ 10 ] = buffer[ 8 ]; // total number of entries in the central dir
00513     buffer[ 11 ] = buffer[ 9 ];
00514 
00515     int cdsize = centraldirendoffset - centraldiroffset;
00516     buffer[ 12 ] = char(cdsize); // size of the central dir
00517     buffer[ 13 ] = char(cdsize >> 8);
00518     buffer[ 14 ] = char(cdsize >> 16);
00519     buffer[ 15 ] = char(cdsize >> 24);
00520 
00521     //kdDebug(7040) << "end : centraldiroffset: " << centraldiroffset << endl;
00522     //kdDebug(7040) << "end : centraldirsize: " << cdsize << endl;
00523 
00524     buffer[ 16 ] = char(centraldiroffset); // central dir offset
00525     buffer[ 17 ] = char(centraldiroffset >> 8);
00526     buffer[ 18 ] = char(centraldiroffset >> 16);
00527     buffer[ 19 ] = char(centraldiroffset >> 24);
00528 
00529     buffer[ 20 ] = 0; //zipfile comment length
00530     buffer[ 21 ] = 0;
00531 
00532     device()->writeBlock( buffer, 22);
00533 
00534     //kdDebug(7040) << "kzip.cpp reached." << endl;
00535     return true;
00536 }
00537 
00538 // Reimplemented to replace device()->writeBlock with writeData
00539 bool KZip::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
00540 {
00541     // set right offset in zip.
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     // Write data
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     // update saved offset for appending new files
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 /*size*/ )
00567 {
00568     //kdDebug(7040) << "prepareWriting reached." << endl;
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 ) ) // accept WriteOnly and ReadWrite
00576     {
00577         qWarning( "KZip::writeFile: You must open the zip file for writing\n");
00578         return false;
00579     }
00580 
00581     // delete entries in the filelist with the same filename as the one we want
00582     // to save, so that we donīt have duplicate file entries when viewing the zip
00583     // with konqi...
00584     // CAUTION: the old file itself is still in the zip and wonīt be removed !!!
00585     QPtrListIterator<KZipFileEntry> it( d->m_fileList );
00586 
00587     //kdDebug(7040) << "filename to write: " << name <<endl;
00588     for ( ; it.current() ; ++it )
00589     {
00590         //kdDebug(7040) << "prepfilename: " << it.current()->path() <<endl;
00591         if (name == it.current()->path() )
00592         {
00593             //kdDebug(7040) << "removing following entry: " << it.current()->path() <<endl;
00594             d->m_fileList.remove();
00595         }
00596 
00597     }
00598     // Find or create parent dir
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         //kdDebug(7040) << "KZip::prepareWriting ensuring " << dir << " exists. fileName=" << fileName << endl;
00607         parentDir = findOrCreate( dir );
00608     }
00609 
00610     int time = getActualTime();
00611 
00612     // construct a KZipFileEntry and add it to list
00613     KZipFileEntry * e = new KZipFileEntry( this, fileName, 0777, time, user, group, QString::null,
00614                                            name, device()->at() + 30 + name.length(), // start
00615                                            0 /*size unknown yet*/, d->m_compression, 0 /*csize unknown yet*/ );
00616     e->setHeaderStart( device()->at() );
00617     //kdDebug(7040) << "wrote file start: " << e->position() << " name: " << name << endl;
00618     parentDir->addEntry( e );
00619 
00620     d->m_currentFile = e;
00621     d->m_fileList.append( e );
00622 
00623     // write out zip header
00624     QCString encodedName = QFile::encodeName(name);
00625     int bufferSize = encodedName.length() + 30;
00626     //kdDebug(7040) << "KZip::prepareWriting bufferSize=" << bufferSize << endl;
00627     char* buffer = new char[ bufferSize ];
00628 
00629     buffer[ 0 ] = 'P'; //local file header signature
00630     buffer[ 1 ] = 'K';
00631     buffer[ 2 ] = 3;
00632     buffer[ 3 ] = 4;
00633 
00634     buffer[ 4 ] = 0x14; // version needed to extract
00635     buffer[ 5 ] = 0;
00636 
00637     buffer[ 6 ] = 0; // general purpose bit flag
00638     buffer[ 7 ] = 0;
00639 
00640     buffer[ 8 ] = char(e->encoding()); // compression method
00641     buffer[ 9 ] = char(e->encoding() >> 8);
00642 
00643     transformToMsDos( e->datetime(), &buffer[ 10 ] );
00644 
00645     buffer[ 14 ] = 'C'; //dummy crc
00646     buffer[ 15 ] = 'R';
00647     buffer[ 16 ] = 'C';
00648     buffer[ 17 ] = 'q';
00649 
00650     buffer[ 18 ] = 'C'; //compressed file size
00651     buffer[ 19 ] = 'S';
00652     buffer[ 20 ] = 'I';
00653     buffer[ 21 ] = 'Z';
00654 
00655     buffer[ 22 ] = 'U'; //uncompressed file size
00656     buffer[ 23 ] = 'S';
00657     buffer[ 24 ] = 'I';
00658     buffer[ 25 ] = 'Z';
00659 
00660     buffer[ 26 ] = (uchar)(encodedName.length()); //filename length
00661     buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
00662 
00663     buffer[ 28 ] = 0; // extra field length
00664     buffer[ 29 ] = 0;
00665 
00666     // file name
00667     strncpy( buffer + 30, encodedName, encodedName.length() );
00668 
00669     // Write header
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     // Prepare device for writing the data
00679     // Either device() if no compression, or a KFilterDev to compress
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; // ouch
00689     static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders(); // Just zlib, not gzip
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         // Finish
00700         (void)d->m_currentDev->writeBlock( 0, 0 );
00701         delete d->m_currentDev;
00702     }
00703     // If 0, d->m_currentDev was device() - don't delete ;)
00704     d->m_currentDev = 0L;
00705 
00706     Q_ASSERT( d->m_currentFile );
00707     //kdDebug(7040) << "donewriting reached." << endl;
00708     //kdDebug(7040) << "filename: " << d->m_currentFile->path() << endl;
00709     //kdDebug(7040) << "getpos (at): " << device()->at() << endl;
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     //kdDebug(7040) << "usize: " << d->m_currentFile->size() << endl;
00716     //kdDebug(7040) << "csize: " << d->m_currentFile->compressedSize() << endl;
00717     //kdDebug(7040) << "headerstart: " << d->m_currentFile->headerStart() << endl;
00718 
00719     //kdDebug(7040) << "crc: " << d->m_crc << endl;
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     // crc to be calculated over uncompressed stuff...
00739     // and they didn't mention it in their docs...
00740     d->m_crc = crc32(d->m_crc, (const Bytef *) c , i);
00741 
00742     Q_LONG written = d->m_currentDev->writeBlock( c, i );
00743     //kdDebug(7040) << "KZip::writeData wrote " << i << " bytes." << endl;
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     //kdDebug(7040) << "KZipFileEntry::device creating iodevice limited to pos=" << position() << ", csize=" << compressedSize() << endl;
00771     // Limit the reading to the appropriate part of the underlying device (e.g. file)
00772     KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
00773     if ( encoding() == 0 || compressedSize() == 0 ) // no compression (or even no data)
00774         return limitedDev;
00775 
00776     if ( encoding() == 8 )
00777     {
00778         // On top of that, create a device that uncompresses the zlib data
00779         QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
00780         if ( !filterDev )
00781             return 0L; // ouch
00782         static_cast<KFilterDev *>(filterDev)->setSkipHeaders(); // Just zlib, not gzip
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 
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.0.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Oct 8 12:21:32 2003 by doxygen 1.2.18 written by Dimitri van Heesch, © 1997-2001