# kio Library API Documentation

Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# 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             {
    00336                 QString path = QDir::cleanDirPath( name.left( pos ) );
    00337                 KArchiveEntry* ent = rootDir()->entry( path );
    00338                 if ( ent && ent->isDirectory() )
    00339                 {
    00340                     //kdDebug(7040) << "Directory already exists, NOT going to add it again" << endl;
    00341                     entry = 0L;
    00342                 }
    00343                 else
    00344                 {
    00345                     entry = new KArchiveDirectory( this, entryName, access, time, rootDir()->user(), rootDir()->group(), QString::null );
    00346                     //kdDebug(7040) << "KArchiveDirectory created, entryName= " << entryName << ", name=" << name << endl;
    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                 //kdDebug(7040) << "KZipFileEntry created, entryName= " << entryName << ", name=" << name << endl;
    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                     // In some tar files we can find dir/./file => call cleanDirPath
    00367                     QString path = QDir::cleanDirPath( name.left( pos ) );
    00368                     // Ensure container directory exists, create otherwise
    00369                     KArchiveDirectory * tdir = findOrCreate( path );
    00370                     tdir->addEntry(entry);
    00371                 }
    00372             }
    00373
    00374             //calculate offset to next entry
    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     //kdDebug(7040) << "*** done *** " << endl;
    00389     return true;
    00390 }
    00391
    00392 bool KZip::closeArchive()
    00393 {
    00394     if ( ! ( mode() & IO_WriteOnly ) )
    00395     {
    00396         //kdDebug(7040) << "closearchive readonly reached." << endl;
    00397         return true;
    00398     }
    00399     //ReadWrite or WriteOnly
    00400     //write all central dir file entries
    00401
    00402     // to be written at the end of the file...
    00403     char buffer[ 22 ]; // first used for 12, then for 22 at the end
    00404     uLong crc = crc32(0L, Z_NULL, 0);
    00405
    00406     Q_LONG centraldiroffset = device()->at();
    00407     //kdDebug(7040) << "closearchive: centraldiroffset: " << centraldiroffset << endl;
    00408     Q_LONG atbackup = device()->at();
    00409     QPtrListIterator<KZipFileEntry> it( d->m_fileList );
    00410
    00411     for ( ; it.current() ; ++it )
    00412     {   //set crc and compressed size in each local file header
    00413         device()->at( it.current()->headerStart() + 14 );
    00414         //kdDebug(7040) << "closearchive setcrcandcsize: filename: "
    00415         //    << it.current()->path()
    00416         //    << " encoding: "<< it.current()->encoding() << endl;
    00417
    00418         uLong mycrc = it.current()->crc32();
    00419         buffer[0] = char(mycrc); // crc checksum, at headerStart+14
    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); // compressed file size, at headerStart+18
    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); // uncompressed file size, at headerStart+22
    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         //kdDebug(7040) << "closearchive: filename: " << it.current()->path()
    00443         //              << " encoding: "<< it.current()->encoding() << endl;
    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); // zero is a nice default for most header fields
    00451
    00452         const char head[] =
    00453         {
    00454             'P', 'K', 1, 2, // central file header signature
    00455             0x14, 0,        // version made by
    00456             0x14, 0         // version needed to extract
    00457         };
    00458
    00459         // I do not know why memcpy is not working here
    00460         //memcpy(buffer, head, sizeof(head));
    00461         qmemmove(buffer, head, sizeof(head));
    00462
    00463         buffer[ 10 ] = char(it.current()->encoding()); // compression method
    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); // crc checksum
    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); // compressed file size
    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); // uncompressed file size
    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()); // filename length
    00488         buffer[ 29 ] = char(it.current()->path().length() >> 8);
    00489
    00490         int myhst = it.current()->headerStart();
    00491         buffer[ 42 ] = char(myhst); //relative offset of local header
    00492         buffer[ 43 ] = char(myhst >> 8);
    00493         buffer[ 44 ] = char(myhst >> 16);
    00494         buffer[ 45 ] = char(myhst >> 24);
    00495
    00496         // file name
    00497         strncpy( buffer + 46, path, path.length() );
    00498         //kdDebug(7040) << "closearchive length to write: " << bufferSize << endl;
    00499         crc = crc32(crc, (Bytef *)buffer, bufferSize );
    00500         device()->writeBlock( buffer, bufferSize );
    00501         delete[] buffer;
    00502     }
    00503     Q_LONG centraldirendoffset = device()->at();
    00504     //kdDebug(7040) << "closearchive: centraldirendoffset: " << centraldirendoffset << endl;
    00505     //kdDebug(7040) << "closearchive: device()->at(): " << device()->at() << endl;
    00506
    00507     //write end of central dir record.
    00508     buffer[ 0 ] = 'P'; //end of central dir signature
    00509     buffer[ 1 ] = 'K';
    00510     buffer[ 2 ] = 5;
    00511     buffer[ 3 ] = 6;
    00512
    00513     buffer[ 4 ] = 0; // number of this disk
    00514     buffer[ 5 ] = 0;
    00515
    00516     buffer[ 6 ] = 0; // number of disk with start of central dir
    00517     buffer[ 7 ] = 0;
    00518
    00519     int count = d->m_fileList.count();
    00520     //kdDebug(7040) << "number of files (count): " << count << endl;
    00521
    00522
    00523     buffer[ 8 ] = char(count); // total number of entries in central dir of
    00524     buffer[ 9 ] = char(count >> 8); // this disk
    00525
    00526     buffer[ 10 ] = buffer[ 8 ]; // total number of entries in the central dir
    00527     buffer[ 11 ] = buffer[ 9 ];
    00528
    00529     int cdsize = centraldirendoffset - centraldiroffset;
    00530     buffer[ 12 ] = char(cdsize); // size of the central dir
    00531     buffer[ 13 ] = char(cdsize >> 8);
    00532     buffer[ 14 ] = char(cdsize >> 16);
    00533     buffer[ 15 ] = char(cdsize >> 24);
    00534
    00535     //kdDebug(7040) << "end : centraldiroffset: " << centraldiroffset << endl;
    00536     //kdDebug(7040) << "end : centraldirsize: " << cdsize << endl;
    00537
    00538     buffer[ 16 ] = char(centraldiroffset); // central dir offset
    00539     buffer[ 17 ] = char(centraldiroffset >> 8);
    00540     buffer[ 18 ] = char(centraldiroffset >> 16);
    00541     buffer[ 19 ] = char(centraldiroffset >> 24);
    00542
    00543     buffer[ 20 ] = 0; //zipfile comment length
    00544     buffer[ 21 ] = 0;
    00545
    00546     device()->writeBlock( buffer, 22);
    00547
    00548     //kdDebug(7040) << "kzip.cpp reached." << endl;
    00549     return true;
    00550 }
    00551
    00552 // Reimplemented to replace device()->writeBlock with writeData
    00553 bool KZip::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
    00554 {
    00555     // set right offset in zip.
    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     // Write data
    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     // update saved offset for appending new files
    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 /*size*/ )
    00581 {
    00582     //kdDebug(7040) << "prepareWriting reached." << endl;
    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 ) ) // accept WriteOnly and ReadWrite
    00590     {
    00591         qWarning( "KZip::writeFile: You must open the zip file for writing\n");
    00592         return false;
    00593     }
    00594
    00595     // delete entries in the filelist with the same filename as the one we want
    00596     // to save, so that we donīt have duplicate file entries when viewing the zip
    00597     // with konqi...
    00598     // CAUTION: the old file itself is still in the zip and wonīt be removed !!!
    00599     QPtrListIterator<KZipFileEntry> it( d->m_fileList );
    00600
    00601         //kdDebug(7040) << "filename to write: " << name <<endl;
    00602     for ( ; it.current() ; ++it )
    00603     {
    00604         //kdDebug(7040) << "prepfilename: " << it.current()->path() <<endl;
    00605                 if (name == it.current()->path() )
    00606         {
    00607                 //kdDebug(7040) << "removing following entry: " << it.current()->path() <<endl;
    00608                 d->m_fileList.remove();
    00609         }
    00610
    00611     }
    00612     // Find or create parent dir
    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         //kdDebug(7040) << "KZip::prepareWriting ensuring " << dir << " exists. fileName=" << fileName << endl;
    00621         parentDir = findOrCreate( dir );
    00622     }
    00623
    00624     int time = getActualTime();
    00625
    00626     // construct a KZipFileEntry and add it to list
    00627     KZipFileEntry * e = new KZipFileEntry( this, fileName, 0777, time, user, group, QString::null,
    00628                                            name, device()->at() + 30 + name.length(), // start
    00629                                            0 /*size unknown yet*/, d->m_compression, 0 /*csize unknown yet*/ );
    00630     e->setHeaderStart( device()->at() );
    00631     //kdDebug(7040) << "wrote file start: " << e->position() << " name: " << name << endl;
    00632     parentDir->addEntry( e );
    00633
    00634     d->m_currentFile = e;
    00635     d->m_fileList.append( e );
    00636
    00637     // write out zip header
    00638     QCString encodedName = QFile::encodeName(name);
    00639     int bufferSize = encodedName.length() + 30;
    00640     //kdDebug(7040) << "KZip::prepareWriting bufferSize=" << bufferSize << endl;
    00641     char* buffer = new char[ bufferSize ];
    00642
    00643     buffer[ 0 ] = 'P'; //local file header signature
    00644     buffer[ 1 ] = 'K';
    00645     buffer[ 2 ] = 3;
    00646     buffer[ 3 ] = 4;
    00647
    00648     buffer[ 4 ] = 0x14; // version needed to extract
    00649     buffer[ 5 ] = 0;
    00650
    00651     buffer[ 6 ] = 0; // general purpose bit flag
    00652     buffer[ 7 ] = 0;
    00653
    00654     buffer[ 8 ] = char(e->encoding()); // compression method
    00655     buffer[ 9 ] = char(e->encoding() >> 8);
    00656
    00657     transformToMsDos( e->datetime(), &buffer[ 10 ] );
    00658
    00659     buffer[ 14 ] = 'C'; //dummy crc
    00660     buffer[ 15 ] = 'R';
    00661     buffer[ 16 ] = 'C';
    00662     buffer[ 17 ] = 'q';
    00663
    00664     buffer[ 18 ] = 'C'; //compressed file size
    00665     buffer[ 19 ] = 'S';
    00666     buffer[ 20 ] = 'I';
    00667     buffer[ 21 ] = 'Z';
    00668
    00669     buffer[ 22 ] = 'U'; //uncompressed file size
    00670     buffer[ 23 ] = 'S';
    00671     buffer[ 24 ] = 'I';
    00672     buffer[ 25 ] = 'Z';
    00673
    00674     buffer[ 26 ] = (uchar)(encodedName.length()); //filename length
    00675     buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
    00676
    00677     buffer[ 28 ] = 0; // extra field length
    00678     buffer[ 29 ] = 0;
    00679
    00680     // file name
    00681     strncpy( buffer + 30, encodedName, encodedName.length() );
    00682
    00683     // Write header
    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     // Prepare device for writing the data
    00693     // Either device() if no compression, or a KFilterDev to compress
    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; // ouch
    00703     static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders(); // Just zlib, not gzip
    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         // Finish
    00714         (void)d->m_currentDev->writeBlock( 0, 0 );
    00715         delete d->m_currentDev;
    00716     }
    00717     // If 0, d->m_currentDev was device() - don't delete ;)
    00718     d->m_currentDev = 0L;
    00719
    00720     Q_ASSERT( d->m_currentFile );
    00721     //kdDebug(7040) << "donewriting reached." << endl;
    00722     //kdDebug(7040) << "filename: " << d->m_currentFile->path() << endl;
    00723     //kdDebug(7040) << "getpos (at): " << device()->at() << endl;
    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     //kdDebug(7040) << "usize: " << d->m_currentFile->size() << endl;
    00730     //kdDebug(7040) << "csize: " << d->m_currentFile->compressedSize() << endl;
    00731     //kdDebug(7040) << "headerstart: " << d->m_currentFile->headerStart() << endl;
    00732
    00733     //kdDebug(7040) << "crc: " << d->m_crc << endl;
    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     // crc to be calculated over uncompressed stuff...
    00753     // and they didn't mention it in their docs...
    00754     d->m_crc = crc32(d->m_crc, (const Bytef *) c , i);
    00755
    00756     Q_LONG written = d->m_currentDev->writeBlock( c, i );
    00757     //kdDebug(7040) << "KZip::writeData wrote " << i << " bytes." << endl;
    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     //kdDebug(7040) << "KZipFileEntry::device creating iodevice limited to pos=" << position() << ", csize=" << compressedSize() << endl;
    00785     // Limit the reading to the appropriate part of the underlying device (e.g. file)
    00786     KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
    00787     if ( encoding() == 0 || compressedSize() == 0 ) // no compression (or even no data)
    00788         return limitedDev;
    00789
    00790     if ( encoding() == 8 )
    00791     {
    00792         // On top of that, create a device that uncompresses the zlib data
    00793         QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
    00794         if ( !filterDev )
    00795             return 0L; // ouch
    00796         static_cast<KFilterDev *>(filterDev)->setSkipHeaders(); // Just zlib, not gzip
    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

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:14:34 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001