kio Library API Documentation

kzip.cpp

00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2000 David Faure <faure@kde.org>
00003    Copyright (C) 2002 Holger Schroeder <holger-kde@holgis.net>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License version 2 as published by the Free Software Foundation.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017    Boston, MA 02111-1307, USA.
00018 */
00019 
00020 /*
00021     This class implements a kioslave to access ZIP files from KDE.
00022     you can use it in IO_ReadOnly or in IO_WriteOnly mode, and it
00023     behaves just as expected (i hope ;-) ).
00024     It can also be used in IO_ReadWrite mode, in this case one can
00025     append files to an existing zip archive. when you append new files, which
00026     are not yet in the zip, it works as expected, they are appended at the end.
00027     when you append a file, which is already in the file, the reference to the
00028     old file is dropped and the new one is added to the zip. but the
00029     old data from the file itself is not deleted, it is still in the
00030     zipfile. so when you want to have a small and garbagefree zipfile,
00031     just read the contents of the appended zipfile and write it to a new one
00032     in IO_WriteOnly mode. especially take care of this, when you don't want
00033     to leak information of how intermediate versions of files in the zip
00034     were looking.
00035     For more information on the zip fileformat go to
00036     http://www.pkware.com/support/appnote.html .
00037 
00038 */
00039 
00040 #include <qasciidict.h>
00041 #include <qfile.h>
00042 #include <qdir.h>
00043 #include <time.h>
00044 #include <string.h>
00045 #include <qdatetime.h>
00046 #include <kdebug.h>
00047 #include <qptrlist.h>
00048 #include <kmimetype.h>
00049 #include <zlib.h>
00050 
00051 #include "kfilterdev.h"
00052 #include "kzip.h"
00053 #include "klimitediodevice.h"
00054 
00055 const int max_path_len = 4095;  // maximum number of character a path may contain
00056 
00057 static void transformToMsDos(const QDateTime& dt, char* buffer)
00058 {
00059     if ( dt.isValid() )
00060     {
00061         const Q_UINT16 time =
00062              ( dt.time().hour() << 11 )    // 5 bit hour
00063            | ( dt.time().minute() << 5 )   // 6 bit minute
00064            | ( dt.time().second() >> 1 );  // 5 bit double seconds
00065 
00066         buffer[0] = char(time);
00067         buffer[1] = char(time >> 8);
00068 
00069         const Q_UINT16 date =
00070              ( ( dt.date().year() - 1980 ) << 9 ) // 7 bit year 1980-based
00071            | ( dt.date().month() << 5 )           // 4 bit month
00072            | ( dt.date().day() );                 // 5 bit day
00073 
00074         buffer[2] = char(date);
00075         buffer[3] = char(date >> 8);
00076     }
00077     else // !dt.isValid(), assume 1980-01-01 midnight
00078     {
00079         buffer[0] = 0;
00080         buffer[1] = 0;
00081         buffer[2] = 33;
00082         buffer[3] = 0;
00083     }
00084 }
00085 
00086 // == parsing routines for zip headers
00087 
00089 struct ParseFileInfo {
00090   // file related info
00091 //  QCString name;      // filename
00092   mode_t perm;          // permissions of this file
00093   time_t atime;         // last access time (UNIX format)
00094   time_t mtime;         // modification time (UNIX format)
00095   time_t ctime;         // creation time (UNIX format)
00096   int uid;          // user id (-1 if not specified)
00097   int gid;          // group id (-1 if not specified)
00098   QCString guessed_symlink; // guessed symlink target
00099   int extralen;         // length of extra field
00100 
00101   // parsing related info
00102   bool exttimestamp_seen;   // true if extended timestamp extra field
00103                 // has been parsed
00104   bool newinfounix_seen;    // true if Info-ZIP Unix New extra field has
00105                 // been parsed
00106 
00107   ParseFileInfo() : perm(0100644), uid(-1), gid(-1), extralen(0),
00108     exttimestamp_seen(false), newinfounix_seen(false) {
00109     ctime = mtime = atime = time(0);
00110   }
00111 };
00112 
00121 static bool parseExtTimestamp(const char *buffer, int size, bool islocal,
00122             ParseFileInfo &pfi) {
00123   if (size < 1) {
00124     kdDebug(7040) << "premature end of extended timestamp (#1)" << endl;
00125     return false;
00126   }/*end if*/
00127   int flags = *buffer;      // read flags
00128   buffer += 1;
00129 
00130   if (flags & 1) {      // contains modification time
00131     if (size < 5) {
00132       kdDebug(7040) << "premature end of extended timestamp (#2)" << endl;
00133       return false;
00134     }/*end if*/
00135     pfi.mtime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00136                 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00137   }/*end if*/
00138   buffer += 4;
00139   // central extended field cannot contain more than the modification time
00140   // even if other flags are set
00141   if (!islocal) {
00142     pfi.exttimestamp_seen = true;
00143     return true;
00144   }/*end if*/
00145 
00146   if (flags & 2) {      // contains last access time
00147     if (size < 9) {
00148       kdDebug(7040) << "premature end of extended timestamp (#3)" << endl;
00149       return false;
00150     }/*end if*/
00151     pfi.atime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00152                 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00153   }/*end if*/
00154   buffer += 4;
00155 
00156   if (flags & 4) {      // contains creation time
00157     if (size < 13) {
00158       kdDebug(7040) << "premature end of extended timestamp (#4)" << endl;
00159       return false;
00160     }/*end if*/
00161     pfi.ctime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00162                 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00163   }/*end if*/
00164   buffer += 4;
00165 
00166   pfi.exttimestamp_seen = true;
00167   return true;
00168 }
00169 
00178 static bool parseInfoZipUnixOld(const char *buffer, int size, bool islocal,
00179             ParseFileInfo &pfi) {
00180   // spec mandates to omit this field if one of the newer fields are available
00181   if (pfi.exttimestamp_seen || pfi.newinfounix_seen) return true;
00182 
00183   if (size < 8) {
00184     kdDebug(7040) << "premature end of Info-ZIP unix extra field old" << endl;
00185     return false;
00186   }/*end if*/
00187 
00188   pfi.atime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00189                 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00190   buffer += 4;
00191   pfi.mtime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00192                 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00193   buffer += 4;
00194   if (islocal && size >= 12) {
00195     pfi.uid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00196     buffer += 2;
00197     pfi.gid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00198     buffer += 2;
00199   }/*end if*/
00200   return true;
00201 }
00202 
00203 #if 0 // not needed yet
00204 
00212 static bool parseInfoZipUnixNew(const char *buffer, int size, bool islocal,
00213             ParseFileInfo &pfi) {
00214   if (!islocal) {   // contains nothing in central field
00215     pfi.newinfounix = true;
00216     return true;
00217   }/*end if*/
00218 
00219   if (size < 4) {
00220     kdDebug(7040) << "premature end of Info-ZIP unix extra field new" << endl;
00221     return false;
00222   }/*end if*/
00223 
00224   pfi.uid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00225   buffer += 2;
00226   pfi.gid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00227   buffer += 2;
00228 
00229   pfi.newinfounix = true;
00230   return true;
00231 }
00232 #endif
00233 
00242 static bool parseExtraField(const char *buffer, int size, bool islocal,
00243             ParseFileInfo &pfi) {
00244   // extra field in central directory doesn't contain useful data, so we
00245   // don't bother parsing it
00246   if (!islocal) return true;
00247 
00248   while (size >= 4) {   // as long as a potential extra field can be read
00249     int magic = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00250     buffer += 2;
00251     int fieldsize = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00252     buffer += 2;
00253     size -= 4;
00254 
00255     if (fieldsize > size) {
00256       //kdDebug(7040) << "fieldsize: " << fieldsize << " size: " << size << endl;
00257       kdDebug(7040) << "premature end of extra fields reached" << endl;
00258       break;
00259     }/*end if*/
00260 
00261     switch (magic) {
00262       case 0x5455:      // extended timestamp
00263         if (!parseExtTimestamp(buffer, fieldsize, islocal, pfi)) return false;
00264     break;
00265       case 0x5855:      // old Info-ZIP unix extra field
00266         if (!parseInfoZipUnixOld(buffer, fieldsize, islocal, pfi)) return false;
00267     break;
00268 #if 0   // not needed yet
00269       case 0x7855:      // new Info-ZIP unix extra field
00270         if (!parseInfoZipUnixNew(buffer, fieldsize, islocal, pfi)) return false;
00271     break;
00272 #endif
00273       default:
00274         /* ignore everything else */;
00275     }/*end switch*/
00276 
00277     buffer += fieldsize;
00278     size -= fieldsize;
00279   }/*wend*/
00280   return true;
00281 }
00282 
00286 
00287 class KZip::KZipPrivate
00288 {
00289 public:
00290     KZipPrivate()
00291         : m_crc( 0 ),
00292           m_currentFile( 0L ),
00293           m_currentDev( 0L ),
00294           m_compression( 8 ),
00295           m_extraField( KZip::NoExtraField ),
00296       m_offset( 0L ) { }
00297 
00298     unsigned long           m_crc;         // checksum
00299     KZipFileEntry*          m_currentFile; // file currently being written
00300     QIODevice*              m_currentDev;  // filterdev used to write to the above file
00301     QPtrList<KZipFileEntry> m_fileList;    // flat list of all files, for the index (saves a recursive method ;)
00302     int                     m_compression;
00303     KZip::ExtraField        m_extraField;
00304     unsigned int            m_offset; // holds the offset of the place in the zip,
00305     // where new data can be appended. after openarchive it points to 0, when in
00306     // writeonly mode, or it points to the beginning of the central directory.
00307     // each call to writefile updates this value.
00308 };
00309 
00310 KZip::KZip( const QString& filename )
00311     : KArchive( 0L )
00312 {
00313     //kdDebug(7040) << "KZip(filename) reached." << endl;
00314     m_filename = filename;
00315     d = new KZipPrivate;
00316     setDevice( new QFile( filename ) );
00317 }
00318 
00319 KZip::KZip( QIODevice * dev )
00320     : KArchive( dev )
00321 {
00322     //kdDebug(7040) << "KZip::KZip( QIODevice * dev) reached." << endl;
00323     d = new KZipPrivate;
00324 }
00325 
00326 KZip::~KZip()
00327 {
00328     // mjarrett: Closes to prevent ~KArchive from aborting w/o device
00329     //kdDebug(7040) << "~KZip reached." << endl;
00330     if( isOpened() )
00331         close();
00332     if ( !m_filename.isEmpty() )
00333         delete device(); // we created it ourselves
00334     delete d;
00335 }
00336 
00337 bool KZip::openArchive( int mode )
00338 {
00339     //kdDebug(7040) << "openarchive reached." << endl;
00340     d->m_fileList.clear();
00341 
00342     if ( mode == IO_WriteOnly )
00343         return true;
00344     if ( mode != IO_ReadOnly && mode != IO_ReadWrite )
00345     {
00346         kdWarning(7040) << "Unsupported mode " << mode << endl;
00347         return false;
00348     }
00349 
00350     char buffer[47];
00351 
00352     // Check that it's a valid ZIP file
00353     // KArchive::open() opened the underlying device already.
00354     QIODevice* dev = device();
00355 
00356     uint offset = 0; // holds offset, where we read
00357     int n;
00358 
00359     // contains information gathered from the local file headers
00360     QAsciiDict<ParseFileInfo> pfi_map(1009, true /*case sensitive */, true /*copy keys*/);
00361     pfi_map.setAutoDelete(true);
00362 
00363     for (;;) // repeat until 'end of entries' signature is reached
00364     {
00365         n = dev->readBlock( buffer, 4 );
00366 
00367         if (n < 4)
00368         {
00369             kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#1)" << endl;
00370 
00371             return false;
00372         }
00373 
00374         if ( !memcmp( buffer, "PK\5\6", 4 ) ) // 'end of entries'
00375             break;
00376 
00377         if ( !memcmp( buffer, "PK\3\4", 4 ) ) // local file header
00378         {
00379             dev->at( dev->at() + 2 ); // skip 'version needed to extract'
00380 
00381         // read static header stuff
00382             n = dev->readBlock( buffer, 24 );
00383         if (n < 24) {
00384                 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#4)" << endl;
00385                 return false;
00386         }
00387 
00388         int gpf = (uchar)buffer[0]; // "general purpose flag" not "general protection fault" ;-)
00389         int compression_mode = (uchar)buffer[2] | (uchar)buffer[3] << 8;
00390         Q_LONG compr_size = (uchar)buffer[12] | (uchar)buffer[13] << 8
00391                     | (uchar)buffer[14] << 16 | (uchar)buffer[15] << 24;
00392         Q_LONG uncomp_size = (uchar)buffer[16] | (uchar)buffer[17] << 8
00393                     | (uchar)buffer[18] << 16 | (uchar)buffer[19] << 24;
00394         int namelen = (uchar)buffer[20] | (uchar)buffer[21] << 8;
00395         int extralen = (uchar)buffer[22] | (uchar)buffer[23] << 8;
00396 
00397         // read filename
00398         QCString filename(namelen + 1);
00399         n = dev->readBlock(filename.data(), namelen);
00400             if ( n < namelen ) {
00401                 kdWarning(7040) << "Invalid ZIP file. Name not completely read (#2)" << endl;
00402         return false;
00403         }
00404 
00405         ParseFileInfo *pfi = new ParseFileInfo();
00406         pfi_map.insert(filename.data(), pfi);
00407 
00408         // read and parse the beginning of the extra field,
00409         // skip rest of extra field in case it is too long
00410         unsigned int extraFieldEnd = dev->at() + extralen;
00411         pfi->extralen = extralen;
00412         int handledextralen = QMIN(extralen, (int)sizeof buffer);
00413         n = dev->readBlock(buffer, handledextralen);
00414         // no error msg necessary as we deliberately truncate the extra field
00415         if (!parseExtraField(buffer, handledextralen, true, *pfi))
00416             return false;
00417         
00418         // jump to end of extra field
00419         dev->at( extraFieldEnd );
00420 
00421         // we have to take care of the 'general purpose bit flag'.
00422             // if bit 3 is set, the header doesn't contain the length of
00423             // the file and we look for the signature 'PK\7\8' or another
00424         // PKxx signature.
00425             if ( gpf & 8 )
00426             {
00427                 bool foundSignature = false;
00428 
00429                 while (!foundSignature)
00430                 {
00431                     n = dev->readBlock( buffer, 1 );
00432                     if (n < 1)
00433                     {
00434                         kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)" << endl;
00435                         return false;
00436                     }
00437 
00438                     if ( buffer[0] != 'P' )
00439                         continue;
00440 
00441                     n = dev->readBlock( buffer, 3 );
00442                     if (n < 3)
00443                     {
00444                         kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)" << endl;
00445                         return false;
00446                     }
00447 
00448                     // we have to detect three magic tokens here:
00449                     // PK34 for the next local header in case there is no data descriptor
00450                     // PK12 for the central header in case there is no data descriptor
00451                     // PK78 for the data descriptor in case it is following the compressed data
00452 
00453                     if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
00454                     {
00455                         foundSignature = true;
00456                         dev->at( dev->at() + 12 ); // skip the 'data_descriptor'
00457                     }
00458                     else if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 )
00459                          || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) )
00460                     {
00461                         foundSignature = true;
00462                         dev->at( dev->at() - 4 ); // go back 4 bytes, so that the magic bytes can be found
00463                           // in the next cycle
00464                     }
00465                 }
00466             }
00467             else // local header contains the compressed and uncompressed size
00468             {
00469         // check if this could be a symbolic link
00470         if (compression_mode == NoCompression
00471                 && uncomp_size <= max_path_len
00472             && uncomp_size > 0) {
00473             // read content and store it
00474             pfi->guessed_symlink.resize(uncomp_size + 1);
00475             n = dev->readBlock(pfi->guessed_symlink.data(), uncomp_size);
00476             if (n < uncomp_size) {
00477             kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#5)" << endl;
00478             return false;
00479             }
00480         }
00481         else // normal file
00482         {
00483                    if ( dev->size() > 0 && compr_size > (Q_LONG)dev->size() )
00484                    {
00485                         // here we cannot trust the compressed size, so scan through the compressed
00486                         // data to find the next header
00487                         bool foundSignature = false;
00488 
00489                         while (!foundSignature)
00490                         {
00491                                 n = dev->readBlock( buffer, 1 );
00492                                 if (n < 1)
00493                                 {
00494                                         kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#6)" << endl;
00495                                         return false;
00496                                 }
00497 
00498                                 if ( buffer[0] != 'P' )
00499                                         continue;
00500 
00501                                 n = dev->readBlock( buffer, 3 );
00502                                 if (n < 3)
00503                                 {
00504                                         kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#7)" << endl;
00505                                         return false;
00506                                 }
00507 
00508                                 // we have to detect three magic tokens here:
00509                                 // PK34 for the next local header in case there is no data descriptor
00510                                 // PK12 for the central header in case there is no data descriptor
00511                                 // PK78 for the data descriptor in case it is following the compressed data
00512 
00513                                 if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
00514                                 {
00515                                         foundSignature = true;
00516                                         dev->at( dev->at() + 12 ); // skip the 'data_descriptor'
00517                                 }
00518                                 else if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 )
00519                                         || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) )
00520                                 {
00521                                         foundSignature = true;
00522                                         dev->at( dev->at() - 4 );
00523                                         // go back 4 bytes, so that the magic bytes can be found
00524                                         // in the next cycle...
00525                                 }
00526                         }
00527                     }
00528             else
00529             {
00530                         dev->at( dev->at() + compr_size );
00531             }
00532         }
00533                 // here we calculate the length of the file in the zip
00534                 // with headers and jump to the next header.
00535                 uint skip = compr_size + namelen + extralen;
00536                 offset += 30 + skip;
00537             }
00538         }
00539         else if ( !memcmp( buffer, "PK\1\2", 4 ) ) // central block
00540         {
00541 
00542             // so we reached the central header at the end of the zip file
00543             // here we get all interesting data out of the central header
00544             // of a file
00545             offset = dev->at() - 4;
00546 
00547             //set offset for appending new files
00548             if ( d->m_offset == 0L ) d->m_offset = offset;
00549 
00550             n = dev->readBlock( buffer + 4, 42 );
00551             if (n < 42) {
00552                 kdWarning(7040) << "Invalid ZIP file, central entry too short" << endl; // not long enough for valid entry
00553                 return false;
00554             }
00555             // length of the filename (well, pathname indeed)
00556             int namelen = (uchar)buffer[29] << 8 | (uchar)buffer[28];
00557             QCString bufferName( namelen + 1 );
00558             n = dev->readBlock( bufferName.data(), namelen );
00559             if ( n < namelen )
00560                 kdWarning(7040) << "Invalid ZIP file. Name not completely read" << endl;
00561 
00562             ParseFileInfo *pfi = pfi_map[bufferName];
00563             if (!pfi) {   // can that happen?
00564                 pfi_map.insert(bufferName.data(), pfi = new ParseFileInfo());
00565             }
00566             QString name( QFile::decodeName(bufferName) );
00567 
00568             //kdDebug(7040) << "name: " << name << endl;
00569             // only in central header ! see below.
00570             // length of extra attributes
00571             int extralen = (uchar)buffer[31] << 8 | (uchar)buffer[30];
00572             // length of comment for this file
00573             int commlen =  (uchar)buffer[33] << 8 | (uchar)buffer[32];
00574             // compression method of this file
00575             int cmethod =  (uchar)buffer[11] << 8 | (uchar)buffer[10];
00576 
00577             //kdDebug(7040) << "cmethod: " << cmethod << endl;
00578             //kdDebug(7040) << "extralen: " << extralen << endl;
00579 
00580             // uncompressed file size
00581             uint ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 |
00582                 (uchar)buffer[25] << 8 | (uchar)buffer[24];
00583             // compressed file size
00584             uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 |
00585                 (uchar)buffer[21] << 8 | (uchar)buffer[20];
00586 
00587             // offset of local header
00588             uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 |
00589                 (uchar)buffer[43] << 8 | (uchar)buffer[42];
00590 
00591             // some clever people use different extra field lengths
00592             // in the central header and in the local header... funny.
00593             // so we need to get the localextralen to calculate the offset
00594             // from localheaderstart to dataoffset
00595             int localextralen = pfi->extralen; // FIXME: this will not work if
00596                             // no local header exists
00597 
00598             //kdDebug(7040) << "localextralen: " << localextralen << endl;
00599 
00600             // offset, where the real data for uncompression starts
00601             uint dataoffset = localheaderoffset + 30 + localextralen + namelen; //comment only in central header
00602 
00603             //kdDebug(7040) << "esize: " << esize << endl;
00604             //kdDebug(7040) << "eoffset: " << eoffset << endl;
00605             //kdDebug(7040) << "csize: " << csize << endl;
00606 
00607         int os_madeby = (uchar)buffer[5];
00608             bool isdir = false;
00609             int access = 0100644;
00610 
00611         if (os_madeby == 3) {   // good ole unix
00612             access = (uchar)buffer[40] | (uchar)buffer[41] << 8;
00613         }
00614 
00615             QString entryName;
00616 
00617             if ( name.endsWith( "/" ) ) // Entries with a trailing slash are directories
00618             {
00619                 isdir = true;
00620                 name = name.left( name.length() - 1 );
00621                 if (os_madeby != 3) access = S_IFDIR | 0755;
00622         else Q_ASSERT(access & S_IFDIR);
00623             }
00624 
00625             int pos = name.findRev( '/' );
00626             if ( pos == -1 )
00627                 entryName = name;
00628             else
00629                 entryName = name.mid( pos + 1 );
00630             Q_ASSERT( !entryName.isEmpty() );
00631 
00632             KArchiveEntry* entry;
00633             if ( isdir )
00634             {
00635                 QString path = QDir::cleanDirPath( name );
00636                 KArchiveEntry* ent = rootDir()->entry( path );
00637                 if ( ent && ent->isDirectory() )
00638                 {
00639                     //kdDebug(7040) << "Directory already exists, NOT going to add it again" << endl;
00640                     entry = 0L;
00641                 }
00642                 else
00643                 {
00644                     entry = new KArchiveDirectory( this, entryName, access, (int)pfi->mtime, rootDir()->user(), rootDir()->group(), QString::null );
00645                     //kdDebug(7040) << "KArchiveDirectory created, entryName= " << entryName << ", name=" << name << endl;
00646                 }
00647         }
00648             else
00649             {
00650             QString symlink;
00651         if (S_ISLNK(access)) {
00652             symlink = QFile::decodeName(pfi->guessed_symlink);
00653         }
00654                 entry = new KZipFileEntry( this, entryName, access, pfi->mtime,
00655                     rootDir()->user(), rootDir()->group(),
00656                     symlink, name, dataoffset,
00657                     ucsize, cmethod, csize );
00658                 static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset );
00659                 //kdDebug(7040) << "KZipFileEntry created, entryName= " << entryName << ", name=" << name << endl;
00660                 d->m_fileList.append( static_cast<KZipFileEntry *>( entry ) );
00661             }
00662 
00663             if ( entry )
00664             {
00665                 if ( pos == -1 )
00666                 {
00667                     rootDir()->addEntry(entry);
00668                 }
00669                 else
00670                 {
00671                     // In some tar files we can find dir/./file => call cleanDirPath
00672                     QString path = QDir::cleanDirPath( name.left( pos ) );
00673                     // Ensure container directory exists, create otherwise
00674                     KArchiveDirectory * tdir = findOrCreate( path );
00675                     tdir->addEntry(entry);
00676                 }
00677             }
00678 
00679             //calculate offset to next entry
00680             offset += 46 + commlen + extralen + namelen;
00681             bool b = dev->at(offset);
00682             Q_ASSERT( b );
00683             if ( !b )
00684               return false;
00685         }
00686         else
00687         {
00688             kdWarning(7040) << "Invalid ZIP file. Unrecognized header at offset " << offset << endl;
00689 
00690             return false;
00691         }
00692     }
00693     //kdDebug(7040) << "*** done *** " << endl;
00694     return true;
00695 }
00696 
00697 bool KZip::closeArchive()
00698 {
00699     if ( ! ( mode() & IO_WriteOnly ) )
00700     {
00701         //kdDebug(7040) << "closearchive readonly reached." << endl;
00702         return true;
00703     }
00704     //ReadWrite or WriteOnly
00705     //write all central dir file entries
00706 
00707     // to be written at the end of the file...
00708     char buffer[ 22 ]; // first used for 12, then for 22 at the end
00709     uLong crc = crc32(0L, Z_NULL, 0);
00710 
00711     Q_LONG centraldiroffset = device()->at();
00712     //kdDebug(7040) << "closearchive: centraldiroffset: " << centraldiroffset << endl;
00713     Q_LONG atbackup = centraldiroffset;
00714     QPtrListIterator<KZipFileEntry> it( d->m_fileList );
00715 
00716     for ( ; it.current() ; ++it )
00717     {   //set crc and compressed size in each local file header
00718         if ( !device()->at( it.current()->headerStart() + 14 ) )
00719             return false;
00720     //kdDebug(7040) << "closearchive setcrcandcsize: filename: "
00721     //    << it.current()->path()
00722     //    << " encoding: "<< it.current()->encoding() << endl;
00723 
00724         uLong mycrc = it.current()->crc32();
00725         buffer[0] = char(mycrc); // crc checksum, at headerStart+14
00726         buffer[1] = char(mycrc >> 8);
00727         buffer[2] = char(mycrc >> 16);
00728         buffer[3] = char(mycrc >> 24);
00729 
00730         int mysize1 = it.current()->compressedSize();
00731         buffer[4] = char(mysize1); // compressed file size, at headerStart+18
00732         buffer[5] = char(mysize1 >> 8);
00733         buffer[6] = char(mysize1 >> 16);
00734         buffer[7] = char(mysize1 >> 24);
00735 
00736         int myusize = it.current()->size();
00737         buffer[8] = char(myusize); // uncompressed file size, at headerStart+22
00738         buffer[9] = char(myusize >> 8);
00739         buffer[10] = char(myusize >> 16);
00740         buffer[11] = char(myusize >> 24);
00741 
00742         if ( device()->writeBlock( buffer, 12 ) != 12 )
00743             return false;
00744     }
00745     device()->at( atbackup );
00746 
00747     for ( it.toFirst(); it.current() ; ++it )
00748     {
00749         //kdDebug(7040) << "closearchive: filename: " << it.current()->path()
00750         //              << " encoding: "<< it.current()->encoding() << endl;
00751 
00752         QCString path = QFile::encodeName(it.current()->path());
00753 
00754     const int extra_field_len = 9;
00755         int bufferSize = extra_field_len + path.length() + 46;
00756         char* buffer = new char[ bufferSize ];
00757 
00758         memset(buffer, 0, 46); // zero is a nice default for most header fields
00759 
00760         const char head[] =
00761         {
00762             'P', 'K', 1, 2, // central file header signature
00763             0x14, 3,        // version made by (3 == UNIX)
00764             0x14, 0         // version needed to extract
00765         };
00766 
00767     // I do not know why memcpy is not working here
00768         //memcpy(buffer, head, sizeof(head));
00769         qmemmove(buffer, head, sizeof(head));
00770 
00771         buffer[ 10 ] = char(it.current()->encoding()); // compression method
00772         buffer[ 11 ] = char(it.current()->encoding() >> 8);
00773 
00774         transformToMsDos( it.current()->datetime(), &buffer[ 12 ] );
00775 
00776         uLong mycrc = it.current()->crc32();
00777         buffer[ 16 ] = char(mycrc); // crc checksum
00778         buffer[ 17 ] = char(mycrc >> 8);
00779         buffer[ 18 ] = char(mycrc >> 16);
00780         buffer[ 19 ] = char(mycrc >> 24);
00781 
00782         int mysize1 = it.current()->compressedSize();
00783         buffer[ 20 ] = char(mysize1); // compressed file size
00784         buffer[ 21 ] = char(mysize1 >> 8);
00785         buffer[ 22 ] = char(mysize1 >> 16);
00786         buffer[ 23 ] = char(mysize1 >> 24);
00787 
00788         int mysize = it.current()->size();
00789         buffer[ 24 ] = char(mysize); // uncompressed file size
00790         buffer[ 25 ] = char(mysize >> 8);
00791         buffer[ 26 ] = char(mysize >> 16);
00792         buffer[ 27 ] = char(mysize >> 24);
00793 
00794         buffer[ 28 ] = char(it.current()->path().length()); // filename length
00795         buffer[ 29 ] = char(it.current()->path().length() >> 8);
00796 
00797     buffer[ 30 ] = char(extra_field_len);
00798     buffer[ 31 ] = char(extra_field_len >> 8);
00799 
00800     buffer[ 40 ] = char(it.current()->permissions());
00801     buffer[ 41 ] = char(it.current()->permissions() >> 8);
00802 
00803         int myhst = it.current()->headerStart();
00804         buffer[ 42 ] = char(myhst); //relative offset of local header
00805         buffer[ 43 ] = char(myhst >> 8);
00806         buffer[ 44 ] = char(myhst >> 16);
00807         buffer[ 45 ] = char(myhst >> 24);
00808 
00809         // file name
00810         strncpy( buffer + 46, path, path.length() );
00811     //kdDebug(7040) << "closearchive length to write: " << bufferSize << endl;
00812 
00813     // extra field
00814     char *extfield = buffer + 46 + path.length();
00815     extfield[0] = 'U';
00816     extfield[1] = 'T';
00817     extfield[2] = 5;
00818     extfield[3] = 0;
00819     extfield[4] = 1 | 2 | 4;    // specify flags from local field
00820                     // (unless I misread the spec)
00821     // provide only modification time
00822     unsigned long time = (unsigned long)it.current()->date();
00823     extfield[5] = char(time);
00824     extfield[6] = char(time >> 8);
00825     extfield[7] = char(time >> 16);
00826     extfield[8] = char(time >> 24);
00827 
00828         crc = crc32(crc, (Bytef *)buffer, bufferSize );
00829         bool ok = ( device()->writeBlock( buffer, bufferSize ) == bufferSize );
00830         delete[] buffer;
00831         if ( !ok )
00832             return false;
00833     }
00834     Q_LONG centraldirendoffset = device()->at();
00835     //kdDebug(7040) << "closearchive: centraldirendoffset: " << centraldirendoffset << endl;
00836     //kdDebug(7040) << "closearchive: device()->at(): " << device()->at() << endl;
00837 
00838     //write end of central dir record.
00839     buffer[ 0 ] = 'P'; //end of central dir signature
00840     buffer[ 1 ] = 'K';
00841     buffer[ 2 ] = 5;
00842     buffer[ 3 ] = 6;
00843 
00844     buffer[ 4 ] = 0; // number of this disk
00845     buffer[ 5 ] = 0;
00846 
00847     buffer[ 6 ] = 0; // number of disk with start of central dir
00848     buffer[ 7 ] = 0;
00849 
00850     int count = d->m_fileList.count();
00851     //kdDebug(7040) << "number of files (count): " << count << endl;
00852 
00853 
00854     buffer[ 8 ] = char(count); // total number of entries in central dir of
00855     buffer[ 9 ] = char(count >> 8); // this disk
00856 
00857     buffer[ 10 ] = buffer[ 8 ]; // total number of entries in the central dir
00858     buffer[ 11 ] = buffer[ 9 ];
00859 
00860     int cdsize = centraldirendoffset - centraldiroffset;
00861     buffer[ 12 ] = char(cdsize); // size of the central dir
00862     buffer[ 13 ] = char(cdsize >> 8);
00863     buffer[ 14 ] = char(cdsize >> 16);
00864     buffer[ 15 ] = char(cdsize >> 24);
00865 
00866     //kdDebug(7040) << "end : centraldiroffset: " << centraldiroffset << endl;
00867     //kdDebug(7040) << "end : centraldirsize: " << cdsize << endl;
00868 
00869     buffer[ 16 ] = char(centraldiroffset); // central dir offset
00870     buffer[ 17 ] = char(centraldiroffset >> 8);
00871     buffer[ 18 ] = char(centraldiroffset >> 16);
00872     buffer[ 19 ] = char(centraldiroffset >> 24);
00873 
00874     buffer[ 20 ] = 0; //zipfile comment length
00875     buffer[ 21 ] = 0;
00876 
00877     if ( device()->writeBlock( buffer, 22 ) != 22 )
00878         return false;
00879 
00880     //kdDebug(7040) << "kzip.cpp reached." << endl;
00881     return true;
00882 }
00883 
00884 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0
00885 bool KZip::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
00886 {
00887     mode_t mode = 0100644;
00888     time_t the_time = time(0);
00889     return KArchive::writeFile( name, user, group, size, mode, the_time,
00890                 the_time, the_time, data );
00891 }
00892 
00893 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0
00894 bool KZip::writeFile( const QString& name, const QString& user,
00895                         const QString& group, uint size, mode_t perm,
00896                         time_t atime, time_t mtime, time_t ctime,
00897                         const char* data ) {
00898   return KArchive::writeFile(name, user, group, size, perm, atime, mtime,
00899             ctime, data);
00900 }
00901 
00902 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0
00903 bool KZip::prepareWriting( const QString& name, const QString& user, const QString& group, uint size )
00904 {
00905     mode_t dflt_perm = 0100644;
00906     time_t the_time = time(0);
00907     return prepareWriting(name,user,group,size,dflt_perm,
00908             the_time,the_time,the_time);
00909 }
00910 
00911 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0
00912 bool KZip::prepareWriting(const QString& name, const QString& user,
00913                 const QString& group, uint size, mode_t perm,
00914                 time_t atime, time_t mtime, time_t ctime) {
00915   return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime);
00916 }
00917 
00918 bool KZip::prepareWriting_impl(const QString &name, const QString &user,
00919                 const QString &group, uint /*size*/, mode_t perm,
00920                 time_t atime, time_t mtime, time_t ctime) {
00921     //kdDebug(7040) << "prepareWriting reached." << endl;
00922     if ( !isOpened() )
00923     {
00924         qWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
00925         return false;
00926     }
00927 
00928     if ( ! ( mode() & IO_WriteOnly ) ) // accept WriteOnly and ReadWrite
00929     {
00930         qWarning( "KZip::writeFile: You must open the zip file for writing\n");
00931         return false;
00932     }
00933 
00934     // set right offset in zip.
00935     if ( !device()->at( d->m_offset ) ) {
00936         kdWarning(7040) << "prepareWriting_impl: cannot seek in ZIP file. Disk full?" << endl;
00937         return false;
00938     }
00939 
00940     // delete entries in the filelist with the same filename as the one we want
00941     // to save, so that we donīt have duplicate file entries when viewing the zip
00942     // with konqi...
00943     // CAUTION: the old file itself is still in the zip and won't be removed !!!
00944     QPtrListIterator<KZipFileEntry> it( d->m_fileList );
00945 
00946     //kdDebug(7040) << "filename to write: " << name <<endl;
00947     for ( ; it.current() ; ++it )
00948     {
00949         //kdDebug(7040) << "prepfilename: " << it.current()->path() <<endl;
00950         if (name == it.current()->path() )
00951         {
00952             //kdDebug(7040) << "removing following entry: " << it.current()->path() <<endl;
00953             d->m_fileList.remove();
00954         }
00955 
00956     }
00957     // Find or create parent dir
00958     KArchiveDirectory* parentDir = rootDir();
00959     QString fileName( name );
00960     int i = name.findRev( '/' );
00961     if ( i != -1 )
00962     {
00963         QString dir = name.left( i );
00964         fileName = name.mid( i + 1 );
00965         //kdDebug(7040) << "KZip::prepareWriting ensuring " << dir << " exists. fileName=" << fileName << endl;
00966         parentDir = findOrCreate( dir );
00967     }
00968 
00969     // construct a KZipFileEntry and add it to list
00970     KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, QString::null,
00971                                            name, device()->at() + 30 + name.length(), // start
00972                                            0 /*size unknown yet*/, d->m_compression, 0 /*csize unknown yet*/ );
00973     e->setHeaderStart( device()->at() );
00974     //kdDebug(7040) << "wrote file start: " << e->position() << " name: " << name << endl;
00975     parentDir->addEntry( e );
00976 
00977     d->m_currentFile = e;
00978     d->m_fileList.append( e );
00979 
00980     int extra_field_len = 0;
00981     if ( d->m_extraField == ModificationTime )
00982         extra_field_len = 17;   // value also used in doneWriting()
00983 
00984     // write out zip header
00985     QCString encodedName = QFile::encodeName(name);
00986     int bufferSize = extra_field_len + encodedName.length() + 30;
00987     //kdDebug(7040) << "KZip::prepareWriting bufferSize=" << bufferSize << endl;
00988     char* buffer = new char[ bufferSize ];
00989 
00990     buffer[ 0 ] = 'P'; //local file header signature
00991     buffer[ 1 ] = 'K';
00992     buffer[ 2 ] = 3;
00993     buffer[ 3 ] = 4;
00994 
00995     buffer[ 4 ] = 0x14; // version needed to extract
00996     buffer[ 5 ] = 0;
00997 
00998     buffer[ 6 ] = 0; // general purpose bit flag
00999     buffer[ 7 ] = 0;
01000 
01001     buffer[ 8 ] = char(e->encoding()); // compression method
01002     buffer[ 9 ] = char(e->encoding() >> 8);
01003 
01004     transformToMsDos( e->datetime(), &buffer[ 10 ] );
01005 
01006     buffer[ 14 ] = 'C'; //dummy crc
01007     buffer[ 15 ] = 'R';
01008     buffer[ 16 ] = 'C';
01009     buffer[ 17 ] = 'q';
01010 
01011     buffer[ 18 ] = 'C'; //compressed file size
01012     buffer[ 19 ] = 'S';
01013     buffer[ 20 ] = 'I';
01014     buffer[ 21 ] = 'Z';
01015 
01016     buffer[ 22 ] = 'U'; //uncompressed file size
01017     buffer[ 23 ] = 'S';
01018     buffer[ 24 ] = 'I';
01019     buffer[ 25 ] = 'Z';
01020 
01021     buffer[ 26 ] = (uchar)(encodedName.length()); //filename length
01022     buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
01023 
01024     buffer[ 28 ] = (uchar)(extra_field_len); // extra field length
01025     buffer[ 29 ] = (uchar)(extra_field_len >> 8);
01026 
01027     // file name
01028     strncpy( buffer + 30, encodedName, encodedName.length() );
01029 
01030     // extra field
01031     if ( d->m_extraField == ModificationTime )
01032     {
01033         char *extfield = buffer + 30 + encodedName.length();
01034         // "Extended timestamp" header (0x5455)
01035         extfield[0] = 'U';
01036         extfield[1] = 'T';
01037         extfield[2] = 13; // data size
01038         extfield[3] = 0;
01039         extfield[4] = 1 | 2 | 4;    // contains mtime, atime, ctime
01040 
01041         extfield[5] = char(mtime);
01042         extfield[6] = char(mtime >> 8);
01043         extfield[7] = char(mtime >> 16);
01044         extfield[8] = char(mtime >> 24);
01045 
01046         extfield[9] = char(atime);
01047         extfield[10] = char(atime >> 8);
01048         extfield[11] = char(atime >> 16);
01049         extfield[12] = char(atime >> 24);
01050 
01051         extfield[13] = char(ctime);
01052         extfield[14] = char(ctime >> 8);
01053         extfield[15] = char(ctime >> 16);
01054         extfield[16] = char(ctime >> 24);
01055     }
01056 
01057     // Write header
01058     bool b = (device()->writeBlock( buffer, bufferSize ) == bufferSize );
01059     d->m_crc = 0L;
01060     delete[] buffer;
01061 
01062     Q_ASSERT( b );
01063     if (!b)
01064         return false;
01065 
01066     // Prepare device for writing the data
01067     // Either device() if no compression, or a KFilterDev to compress
01068     if ( d->m_compression == 0 ) {
01069         d->m_currentDev = device();
01070         return true;
01071     }
01072 
01073     d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
01074     Q_ASSERT( d->m_currentDev );
01075     if ( !d->m_currentDev )
01076         return false; // ouch
01077     static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders(); // Just zlib, not gzip
01078 
01079     b = d->m_currentDev->open( IO_WriteOnly );
01080     Q_ASSERT( b );
01081     return b;
01082 }
01083 
01084 bool KZip::doneWriting( uint size )
01085 {
01086     if ( d->m_currentFile->encoding() == 8 ) {
01087         // Finish
01088         (void)d->m_currentDev->writeBlock( 0, 0 );
01089         delete d->m_currentDev;
01090     }
01091     // If 0, d->m_currentDev was device() - don't delete ;)
01092     d->m_currentDev = 0L;
01093 
01094     Q_ASSERT( d->m_currentFile );
01095     //kdDebug(7040) << "donewriting reached." << endl;
01096     //kdDebug(7040) << "filename: " << d->m_currentFile->path() << endl;
01097     //kdDebug(7040) << "getpos (at): " << device()->at() << endl;
01098     d->m_currentFile->setSize(size);
01099     int extra_field_len = 0;
01100     if ( d->m_extraField == ModificationTime )
01101         extra_field_len = 17;   // value also used in doneWriting()
01102 
01103     int csize = device()->at() -
01104         d->m_currentFile->headerStart() - 30 -
01105         d->m_currentFile->path().length() - extra_field_len;
01106     d->m_currentFile->setCompressedSize(csize);
01107     //kdDebug(7040) << "usize: " << d->m_currentFile->size() << endl;
01108     //kdDebug(7040) << "csize: " << d->m_currentFile->compressedSize() << endl;
01109     //kdDebug(7040) << "headerstart: " << d->m_currentFile->headerStart() << endl;
01110 
01111     //kdDebug(7040) << "crc: " << d->m_crc << endl;
01112     d->m_currentFile->setCRC32( d->m_crc );
01113 
01114     d->m_currentFile = 0L;
01115 
01116     // update saved offset for appending new files
01117     d->m_offset = device()->at();
01118     return true;
01119 }
01120 
01121 bool KZip::writeSymLink(const QString &name, const QString &target,
01122                 const QString &user, const QString &group,
01123                 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01124   return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime);
01125 }
01126 
01127 bool KZip::writeSymLink_impl(const QString &name, const QString &target,
01128                 const QString &user, const QString &group,
01129                 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01130 
01131   // reassure that symlink flag is set, otherwise strange things happen on
01132   // extraction
01133   perm |= S_IFLNK;
01134   Compression c = compression();
01135   setCompression(NoCompression);    // link targets are never compressed
01136 
01137   if (!prepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) {
01138     kdWarning() << "KZip::writeFile prepareWriting failed" << endl;
01139     setCompression(c);
01140     return false;
01141   }
01142 
01143   QCString symlink_target = QFile::encodeName(target);
01144   if (!writeData(symlink_target, symlink_target.length())) {
01145     kdWarning() << "KZip::writeFile writeData failed" << endl;
01146     setCompression(c);
01147     return false;
01148   }
01149 
01150   if (!doneWriting(symlink_target.length())) {
01151     kdWarning() << "KZip::writeFile doneWriting failed" << endl;
01152     setCompression(c);
01153     return false;
01154   }
01155 
01156   setCompression(c);
01157   return true;
01158 }
01159 
01160 void KZip::virtual_hook( int id, void* data )
01161 {
01162     switch (id) {
01163       case VIRTUAL_WRITE_DATA: {
01164         WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data);
01165         params->retval = writeData_impl( params->data, params->size );
01166         break;
01167       }
01168       case VIRTUAL_WRITE_SYMLINK: {
01169         WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
01170         params->retval = writeSymLink_impl(*params->name,*params->target,
01171                 *params->user,*params->group,params->perm,
01172                 params->atime,params->mtime,params->ctime);
01173         break;
01174       }
01175       case VIRTUAL_PREPARE_WRITING: {
01176         PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
01177         params->retval = prepareWriting_impl(*params->name,*params->user,
01178                 *params->group,params->size,params->perm,
01179                 params->atime,params->mtime,params->ctime);
01180         break;
01181       }
01182       default:
01183         KArchive::virtual_hook( id, data );
01184     }/*end switch*/
01185 }
01186 
01187 // made virtual using virtual_hook
01188 bool KZip::writeData(const char * c, uint i)
01189 {
01190     return KArchive::writeData( c, i );
01191 }
01192 
01193 bool KZip::writeData_impl(const char * c, uint i)
01194 {
01195     Q_ASSERT( d->m_currentFile );
01196     Q_ASSERT( d->m_currentDev );
01197     if (!d->m_currentFile || !d->m_currentDev)
01198         return false;
01199 
01200     // crc to be calculated over uncompressed stuff...
01201     // and they didn't mention it in their docs...
01202     d->m_crc = crc32(d->m_crc, (const Bytef *) c , i);
01203 
01204     Q_LONG written = d->m_currentDev->writeBlock( c, i );
01205     //kdDebug(7040) << "KZip::writeData wrote " << i << " bytes." << endl;
01206     return written == (Q_LONG)i;
01207 }
01208 
01209 void KZip::setCompression( Compression c )
01210 {
01211     d->m_compression = ( c == NoCompression ) ? 0 : 8;
01212 }
01213 
01214 KZip::Compression KZip::compression() const
01215 {
01216    return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
01217 }
01218 
01219 void KZip::setExtraField( ExtraField ef )
01220 {
01221     d->m_extraField = ef;
01222 }
01223 
01224 KZip::ExtraField KZip::extraField() const
01225 {
01226     return d->m_extraField;
01227 }
01228 
01230 
01231 QByteArray KZipFileEntry::data() const
01232 {
01233     QIODevice* dev = device();
01234     QByteArray arr;
01235     if ( dev ) {
01236         arr = dev->readAll();
01237         delete dev;
01238     }
01239     return arr;
01240 }
01241 
01242 QIODevice* KZipFileEntry::device() const
01243 {
01244     //kdDebug(7040) << "KZipFileEntry::device creating iodevice limited to pos=" << position() << ", csize=" << compressedSize() << endl;
01245     // Limit the reading to the appropriate part of the underlying device (e.g. file)
01246     KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
01247     if ( encoding() == 0 || compressedSize() == 0 ) // no compression (or even no data)
01248         return limitedDev;
01249 
01250     if ( encoding() == 8 )
01251     {
01252         // On top of that, create a device that uncompresses the zlib data
01253         QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
01254         if ( !filterDev )
01255             return 0L; // ouch
01256         static_cast<KFilterDev *>(filterDev)->setSkipHeaders(); // Just zlib, not gzip
01257         bool b = filterDev->open( IO_ReadOnly );
01258         Q_ASSERT( b );
01259         return filterDev;
01260     }
01261 
01262     kdError() << "This zip file contains files compressed with method "
01263               << encoding() <<", this method is currently not supported by KZip,"
01264               <<" please use a command-line tool to handle this file." << endl;
01265     return 0L;
01266 }
01267 
KDE Logo
This file is part of the documentation for kio Library Version 3.3.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Feb 18 15:10:44 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003