• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KIO

kzip.cpp

Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor,
00017    Boston, MA 02110-1301, USA.
00018 */
00019 
00020 /*
00021     This class implements a kioslave to access ZIP files from KDE.
00022     you can use it in QIODevice::ReadOnly or in QIODevice::WriteOnly mode, and it
00023     behaves just as expected (i hope ;-) ).
00024     It can also be used in QIODevice::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 QIODevice::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 "kzip.h"
00041 #include "kfilterdev.h"
00042 #include "klimitediodevice.h"
00043 #include <kdebug.h>
00044 
00045 #include <QtCore/QHash>
00046 #include <QtCore/QByteArray>
00047 #include <QtCore/QFile>
00048 #include <QtCore/QDir>
00049 #include <QtCore/QDate>
00050 #include <QtCore/QList>
00051 
00052 #include <zlib.h>
00053 #include <time.h>
00054 #include <string.h>
00055 
00056 const int max_path_len = 4095;  // maximum number of character a path may contain
00057 
00058 static void transformToMsDos(const QDateTime& dt, char* buffer)
00059 {
00060     if ( dt.isValid() )
00061     {
00062         const quint16 time =
00063              ( dt.time().hour() << 11 )    // 5 bit hour
00064            | ( dt.time().minute() << 5 )   // 6 bit minute
00065            | ( dt.time().second() >> 1 );  // 5 bit double seconds
00066 
00067         buffer[0] = char(time);
00068         buffer[1] = char(time >> 8);
00069 
00070         const quint16 date =
00071              ( ( dt.date().year() - 1980 ) << 9 ) // 7 bit year 1980-based
00072            | ( dt.date().month() << 5 )           // 4 bit month
00073            | ( dt.date().day() );                 // 5 bit day
00074 
00075         buffer[2] = char(date);
00076         buffer[3] = char(date >> 8);
00077     }
00078     else // !dt.isValid(), assume 1980-01-01 midnight
00079     {
00080         buffer[0] = 0;
00081         buffer[1] = 0;
00082         buffer[2] = 33;
00083         buffer[3] = 0;
00084     }
00085 }
00086 
00087 static time_t transformFromMsDos(const char* buffer)
00088 {
00089     quint16 time = (uchar)buffer[0] | ( (uchar)buffer[1] << 8 );
00090     int h = time >> 11;
00091     int m = ( time & 0x7ff ) >> 5;
00092     int s = ( time & 0x1f ) * 2 ;
00093     QTime qt(h, m, s);
00094 
00095     quint16 date = (uchar)buffer[2] | ( (uchar)buffer[3] << 8 );
00096     int y = ( date >> 9 ) + 1980;
00097     int o = ( date & 0x1ff ) >> 5;
00098     int d = ( date & 0x1f );
00099     QDate qd(y, o, d);
00100 
00101     QDateTime dt( qd, qt );
00102     return dt.toTime_t();
00103 }
00104 
00105 // == parsing routines for zip headers
00106 
00108 struct ParseFileInfo {
00109   // file related info
00110   mode_t perm;          // permissions of this file
00111   time_t atime;         // last access time (UNIX format)
00112   time_t mtime;         // modification time (UNIX format)
00113   time_t ctime;         // creation time (UNIX format)
00114   int uid;          // user id (-1 if not specified)
00115   int gid;          // group id (-1 if not specified)
00116   QByteArray guessed_symlink;   // guessed symlink target
00117   int extralen;         // length of extra field
00118 
00119   // parsing related info
00120   bool exttimestamp_seen;   // true if extended timestamp extra field
00121                 // has been parsed
00122   bool newinfounix_seen;    // true if Info-ZIP Unix New extra field has
00123                 // been parsed
00124 
00125   ParseFileInfo() : perm(0100644), uid(-1), gid(-1), extralen(0),
00126     exttimestamp_seen(false), newinfounix_seen(false) {
00127     ctime = mtime = atime = time(0);
00128   }
00129 };
00130 
00139 static bool parseExtTimestamp(const char *buffer, int size, bool islocal,
00140             ParseFileInfo &pfi) {
00141   if (size < 1) {
00142     kDebug(7040) << "premature end of extended timestamp (#1)";
00143     return false;
00144   }/*end if*/
00145   int flags = *buffer;      // read flags
00146   buffer += 1;
00147   size -= 1;
00148 
00149   if (flags & 1) {      // contains modification time
00150     if (size < 4) {
00151       kDebug(7040) << "premature end of extended timestamp (#2)";
00152       return false;
00153     }/*end if*/
00154     pfi.mtime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00155                 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00156     buffer += 4;
00157     size -= 4;
00158   }/*end if*/
00159   // central extended field cannot contain more than the modification time
00160   // even if other flags are set
00161   if (!islocal) {
00162     pfi.exttimestamp_seen = true;
00163     return true;
00164   }/*end if*/
00165 
00166   if (flags & 2) {      // contains last access time
00167     if (size < 4) {
00168       kDebug(7040) << "premature end of extended timestamp (#3)";
00169       return true;
00170     }/*end if*/
00171     pfi.atime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00172                 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00173     buffer += 4;
00174     size -= 4;
00175   }/*end if*/
00176 
00177   if (flags & 4) {      // contains creation time
00178     if (size < 4) {
00179       kDebug(7040) << "premature end of extended timestamp (#4)";
00180       return true;
00181     }/*end if*/
00182     pfi.ctime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00183                 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00184     buffer += 4;
00185   }/*end if*/
00186 
00187   pfi.exttimestamp_seen = true;
00188   return true;
00189 }
00190 
00199 static bool parseInfoZipUnixOld(const char *buffer, int size, bool islocal,
00200             ParseFileInfo &pfi) {
00201   // spec mandates to omit this field if one of the newer fields are available
00202   if (pfi.exttimestamp_seen || pfi.newinfounix_seen) return true;
00203 
00204   if (size < 8) {
00205     kDebug(7040) << "premature end of Info-ZIP unix extra field old";
00206     return false;
00207   }/*end if*/
00208 
00209   pfi.atime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00210                 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00211   buffer += 4;
00212   pfi.mtime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00213                 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00214   buffer += 4;
00215   if (islocal && size >= 12) {
00216     pfi.uid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00217     buffer += 2;
00218     pfi.gid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00219     buffer += 2;
00220   }/*end if*/
00221   return true;
00222 }
00223 
00224 #if 0 // not needed yet
00225 
00233 static bool parseInfoZipUnixNew(const char *buffer, int size, bool islocal,
00234             ParseFileInfo &pfi) {
00235   if (!islocal) {   // contains nothing in central field
00236     pfi.newinfounix = true;
00237     return true;
00238   }/*end if*/
00239 
00240   if (size < 4) {
00241     kDebug(7040) << "premature end of Info-ZIP unix extra field new";
00242     return false;
00243   }/*end if*/
00244 
00245   pfi.uid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00246   buffer += 2;
00247   pfi.gid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00248   buffer += 2;
00249 
00250   pfi.newinfounix = true;
00251   return true;
00252 }
00253 #endif
00254 
00263 static bool parseExtraField(const char *buffer, int size, bool islocal,
00264             ParseFileInfo &pfi) {
00265   // extra field in central directory doesn't contain useful data, so we
00266   // don't bother parsing it
00267   if (!islocal) return true;
00268 
00269   while (size >= 4) {   // as long as a potential extra field can be read
00270     int magic = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00271     buffer += 2;
00272     int fieldsize = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00273     buffer += 2;
00274     size -= 4;
00275 
00276     if (fieldsize > size) {
00277       //kDebug(7040) << "fieldsize: " << fieldsize << " size: " << size;
00278       kDebug(7040) << "premature end of extra fields reached";
00279       break;
00280     }/*end if*/
00281 
00282     switch (magic) {
00283       case 0x5455:      // extended timestamp
00284         if (!parseExtTimestamp(buffer, fieldsize, islocal, pfi)) return false;
00285     break;
00286       case 0x5855:      // old Info-ZIP unix extra field
00287         if (!parseInfoZipUnixOld(buffer, fieldsize, islocal, pfi)) return false;
00288     break;
00289 #if 0   // not needed yet
00290       case 0x7855:      // new Info-ZIP unix extra field
00291         if (!parseInfoZipUnixNew(buffer, fieldsize, islocal, pfi)) return false;
00292     break;
00293 #endif
00294       default:
00295         /* ignore everything else */;
00296     }/*end switch*/
00297 
00298     buffer += fieldsize;
00299     size -= fieldsize;
00300   }/*wend*/
00301   return true;
00302 }
00303 
00307 
00308 class KZip::KZipPrivate
00309 {
00310 public:
00311     KZipPrivate()
00312         : m_crc( 0 ),
00313           m_currentFile( 0 ),
00314           m_currentDev( 0 ),
00315           m_compression( 8 ),
00316           m_extraField( KZip::NoExtraField ),
00317       m_offset( 0 )
00318     {}
00319 
00320     unsigned long           m_crc;         // checksum
00321     KZipFileEntry*          m_currentFile; // file currently being written
00322     QIODevice*              m_currentDev;  // filterdev used to write to the above file
00323     QList<KZipFileEntry*> m_fileList;    // flat list of all files, for the index (saves a recursive method ;)
00324     int                     m_compression;
00325     KZip::ExtraField        m_extraField;
00326     // m_offset holds the offset of the place in the zip,
00327     // where new data can be appended. after openarchive it points to 0, when in
00328     // writeonly mode, or it points to the beginning of the central directory.
00329     // each call to writefile updates this value.
00330     unsigned int            m_offset;
00331 };
00332 
00333 KZip::KZip( const QString& fileName )
00334     : KArchive( fileName ),d(new KZipPrivate)
00335 {
00336 }
00337 
00338 KZip::KZip( QIODevice * dev )
00339     : KArchive( dev ),d(new KZipPrivate)
00340 {
00341 }
00342 
00343 KZip::~KZip()
00344 {
00345     //kDebug(7040) << this;
00346     if( isOpen() )
00347         close();
00348     delete d;
00349 }
00350 
00351 bool KZip::openArchive( QIODevice::OpenMode mode )
00352 {
00353     //kDebug(7040);
00354     d->m_fileList.clear();
00355 
00356     if ( mode == QIODevice::WriteOnly )
00357         return true;
00358 
00359     char buffer[47];
00360 
00361     // Check that it's a valid ZIP file
00362     // KArchive::open() opened the underlying device already.
00363 
00364     uint offset = 0; // holds offset, where we read
00365     int n;
00366 
00367     // contains information gathered from the local file headers
00368     QHash<QByteArray, ParseFileInfo> pfi_map;
00369 
00370     QIODevice* dev = device();
00371 
00372     // We set a bool for knowing if we are allowed to skip the start of the file
00373     bool startOfFile = true;
00374 
00375     for (;;) // repeat until 'end of entries' signature is reached
00376     {
00377         //kDebug(7040) << "loop starts";
00378         //kDebug(7040) << "dev->pos() now : " << dev->pos();
00379         n = dev->read( buffer, 4 );
00380 
00381         if (n < 4)
00382         {
00383             kWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#1)";
00384 
00385             return false;
00386         }
00387 
00388         if ( !memcmp( buffer, "PK\5\6", 4 ) ) // 'end of entries'
00389         {
00390         //kDebug(7040) << "PK56 found end of archive";
00391             startOfFile = false;
00392         break;
00393     }
00394 
00395     if ( !memcmp( buffer, "PK\3\4", 4 ) ) // local file header
00396         {
00397         //kDebug(7040) << "PK34 found local file header";
00398             startOfFile = false;
00399             // can this fail ???
00400         dev->seek( dev->pos() + 2 ); // skip 'version needed to extract'
00401 
00402         // read static header stuff
00403             n = dev->read( buffer, 24 );
00404         if (n < 24) {
00405                 kWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#4)";
00406                 return false;
00407         }
00408 
00409         int gpf = (uchar)buffer[0]; // "general purpose flag" not "general protection fault" ;-)
00410         int compression_mode = (uchar)buffer[2] | (uchar)buffer[3] << 8;
00411         time_t mtime = transformFromMsDos( buffer+4 );
00412 
00413         qint64 compr_size = (uchar)buffer[12] | (uchar)buffer[13] << 8
00414                     | (uchar)buffer[14] << 16 | (uchar)buffer[15] << 24;
00415         qint64 uncomp_size = (uchar)buffer[16] | (uchar)buffer[17] << 8
00416                     | (uchar)buffer[18] << 16 | (uchar)buffer[19] << 24;
00417         int namelen = (uchar)buffer[20] | (uchar)buffer[21] << 8;
00418         int extralen = (uchar)buffer[22] | (uchar)buffer[23] << 8;
00419 
00420             /*
00421         kDebug(7040) << "general purpose bit flag: " << gpf;
00422         kDebug(7040) << "compressed size: " << compr_size;
00423         kDebug(7040) << "uncompressed size: " << uncomp_size;
00424         kDebug(7040) << "namelen: " << namelen;
00425         kDebug(7040) << "extralen: " << extralen;
00426         kDebug(7040) << "archive size: " << dev->size();
00427         */
00428 
00429         // read fileName
00430             Q_ASSERT( namelen > 0 );
00431         QByteArray fileName = dev->read(namelen);
00432             if ( fileName.size() < namelen ) {
00433                 kWarning(7040) << "Invalid ZIP file. Name not completely read (#2)";
00434         return false;
00435         }
00436 
00437         ParseFileInfo pfi;
00438         pfi.mtime = mtime;
00439 
00440             // read and parse the beginning of the extra field,
00441             // skip rest of extra field in case it is too long
00442             unsigned int extraFieldEnd = dev->pos() + extralen;
00443         pfi.extralen = extralen;
00444         int handledextralen = qMin(extralen, (int)sizeof buffer);
00445 
00446             if ( handledextralen )
00447                 kDebug(7040) << "handledextralen: " << handledextralen;
00448 
00449         n = dev->read(buffer, handledextralen);
00450         // no error msg necessary as we deliberately truncate the extra field
00451         if (!parseExtraField(buffer, handledextralen, true, pfi))
00452         {
00453             kWarning(7040) << "Invalid ZIP File. Broken ExtraField.";
00454             return false;
00455         }
00456 
00457             // jump to end of extra field
00458             dev->seek( extraFieldEnd );
00459 
00460         // we have to take care of the 'general purpose bit flag'.
00461             // if bit 3 is set, the header doesn't contain the length of
00462             // the file and we look for the signature 'PK\7\8'.
00463             if ( gpf & 8 )
00464             {
00465             // here we have to read through the compressed data to find
00466         // the next PKxx
00467             kDebug(7040) << "trying to seek for next PK78";
00468                 bool foundSignature = false;
00469 
00470                 while (!foundSignature)
00471                 {
00472                     n = dev->read( buffer, 1 );
00473                     if (n < 1)
00474                     {
00475                         kWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)";
00476                         return false;
00477                     }
00478 
00479                     if ( buffer[0] != 'P' )
00480                         continue;
00481 
00482                     n = dev->read( buffer, 3 );
00483                     if (n < 3)
00484                     {
00485                         kWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)";
00486                         return false;
00487                     }
00488 
00489                     // we have to detect three magic tokens here:
00490             // PK34 for the next local header in case there is no data descriptor
00491             // PK12 for the central header in case there is no data descriptor
00492             // PK78 for the data descriptor in case it is following the compressed data
00493 
00494             if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
00495                     {
00496                         foundSignature = true;
00497                         dev->seek( dev->pos() + 12 ); // skip the 'data_descriptor'
00498                     }
00499             else if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 )
00500                  || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) )
00501                     {
00502                         foundSignature = true;
00503                         dev->seek( dev->pos() - 4 ); // go back 4 bytes, so that the magic bytes can be found...
00504                     }
00505                     else if ( buffer[0] == 'P' || buffer[1] == 'P' || buffer[2] == 'P' )
00506                     {
00507                         // We have another P character so we must go back a little to check if it is a magic
00508                         dev->seek( dev->pos() - 3 );
00509                     }
00510 
00511                 }
00512             }
00513             else
00514             {
00515             // here we skip the compressed data and jump to the next header
00516             //kDebug(7040) << "general purpose bit flag indicates, that local file header contains valid size";
00517         // check if this could be a symbolic link
00518         if (compression_mode == NoCompression
00519                 && uncomp_size <= max_path_len
00520             && uncomp_size > 0) {
00521             // read content and store it
00522                     // If it's not a symlink, then we'll just discard the data for now.
00523             pfi.guessed_symlink = dev->read(uncomp_size);
00524             if (pfi.guessed_symlink.size() < uncomp_size) {
00525             kWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#5)";
00526             return false;
00527             }
00528         } else {
00529 
00530                 if ( compr_size > dev->size() )
00531             {
00532                 // here we cannot trust the compressed size, so scan through the compressed
00533             // data to find the next header
00534             bool foundSignature = false;
00535 
00536             while (!foundSignature)
00537             {
00538                 n = dev->read( buffer, 1 );
00539                 if (n < 1)
00540                 {
00541                     kWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)";
00542                     return false;
00543                 }
00544 
00545                 if ( buffer[0] != 'P' )
00546                     continue;
00547 
00548                 n = dev->read( buffer, 3 );
00549                 if (n < 3)
00550                 {
00551                     kWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)";
00552                     return false;
00553                 }
00554 
00555                 // we have to detect three magic tokens here:
00556                 // PK34 for the next local header in case there is no data descriptor
00557                 // PK12 for the central header in case there is no data descriptor
00558                 // PK78 for the data descriptor in case it is following the compressed data
00559 
00560                 if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
00561                 {
00562                     foundSignature = true;
00563                     dev->seek( dev->pos() + 12 ); // skip the 'data_descriptor'
00564                 }
00565 
00566                 if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 )
00567                     || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) )
00568                 {
00569                     foundSignature = true;
00570                     dev->seek( dev->pos() - 4 );
00571                     // go back 4 bytes, so that the magic bytes can be found
00572                     // in the next cycle...
00573                 }
00574             }
00575             }
00576             else
00577             {
00578 //          kDebug(7040) << "before interesting dev->pos(): " << dev->pos();
00579             bool success = dev->seek( dev->pos() + compr_size ); // can this fail ???
00580                         Q_ASSERT( success ); // let's see...
00581 /*          kDebug(7040) << "after interesting dev->pos(): " << dev->pos();
00582             if ( success )
00583                 kDebug(7040) << "dev->at was successful... ";
00584             else
00585                 kDebug(7040) << "dev->at failed... ";*/
00586             }
00587 
00588         }
00589 
00590 // not needed any more
00591 /*                // here we calculate the length of the file in the zip
00592                 // with headers and jump to the next header.
00593                 uint skip = compr_size + namelen + extralen;
00594                 offset += 30 + skip;*/
00595             }
00596             pfi_map.insert(fileName, pfi);
00597         }
00598         else if ( !memcmp( buffer, "PK\1\2", 4 ) ) // central block
00599         {
00600         //kDebug(7040) << "PK12 found central block";
00601             startOfFile = false;
00602 
00603             // so we reached the central header at the end of the zip file
00604             // here we get all interesting data out of the central header
00605             // of a file
00606             offset = dev->pos() - 4;
00607 
00608             //set offset for appending new files
00609             if ( d->m_offset == 0L ) d->m_offset = offset;
00610 
00611             n = dev->read( buffer + 4, 42 );
00612             if (n < 42) {
00613                 kWarning(7040) << "Invalid ZIP file, central entry too short"; // not long enough for valid entry
00614                 return false;
00615             }
00616 
00617             //int gpf = (uchar)buffer[9] << 8 | (uchar)buffer[10];
00618             //kDebug() << "general purpose flag=" << gpf;
00619             // length of the fileName (well, pathname indeed)
00620             int namelen = (uchar)buffer[29] << 8 | (uchar)buffer[28];
00621             Q_ASSERT( namelen > 0 );
00622             QByteArray bufferName = dev->read( namelen );
00623             if ( bufferName.size() < namelen )
00624                 kWarning(7040) << "Invalid ZIP file. Name not completely read";
00625 
00626             ParseFileInfo pfi = pfi_map.value( bufferName, ParseFileInfo() );
00627 
00628             QString name( QFile::decodeName(bufferName) );
00629 
00630             //kDebug(7040) << "name: " << name;
00631             // only in central header ! see below.
00632             // length of extra attributes
00633             int extralen = (uchar)buffer[31] << 8 | (uchar)buffer[30];
00634             // length of comment for this file
00635             int commlen =  (uchar)buffer[33] << 8 | (uchar)buffer[32];
00636             // compression method of this file
00637             int cmethod =  (uchar)buffer[11] << 8 | (uchar)buffer[10];
00638 
00639             //kDebug(7040) << "cmethod: " << cmethod;
00640             //kDebug(7040) << "extralen: " << extralen;
00641 
00642             // uncompressed file size
00643             uint ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 |
00644                 (uchar)buffer[25] << 8 | (uchar)buffer[24];
00645             // compressed file size
00646             uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 |
00647                 (uchar)buffer[21] << 8 | (uchar)buffer[20];
00648 
00649             // offset of local header
00650             uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 |
00651                 (uchar)buffer[43] << 8 | (uchar)buffer[42];
00652 
00653             // some clever people use different extra field lengths
00654             // in the central header and in the local header... funny.
00655             // so we need to get the localextralen to calculate the offset
00656             // from localheaderstart to dataoffset
00657             int localextralen = pfi.extralen; // FIXME: this will not work if
00658                             // no local header exists
00659 
00660             //kDebug(7040) << "localextralen: " << localextralen;
00661 
00662             // offset, where the real data for uncompression starts
00663             uint dataoffset = localheaderoffset + 30 + localextralen + namelen; //comment only in central header
00664 
00665             //kDebug(7040) << "esize: " << esize;
00666             //kDebug(7040) << "eoffset: " << eoffset;
00667             //kDebug(7040) << "csize: " << csize;
00668 
00669         int os_madeby = (uchar)buffer[5];
00670             bool isdir = false;
00671             int access = 0100644;
00672 
00673         if (os_madeby == 3) {   // good ole unix
00674             access = (uchar)buffer[40] | (uchar)buffer[41] << 8;
00675         }
00676 
00677             QString entryName;
00678 
00679             if ( name.endsWith( '/' ) ) // Entries with a trailing slash are directories
00680             {
00681                 isdir = true;
00682                 name = name.left( name.length() - 1 );
00683                 if (os_madeby != 3) access = S_IFDIR | 0755;
00684         else Q_ASSERT(access & S_IFDIR);
00685             }
00686 
00687             int pos = name.lastIndexOf( '/' );
00688             if ( pos == -1 )
00689                 entryName = name;
00690             else
00691                 entryName = name.mid( pos + 1 );
00692             Q_ASSERT( !entryName.isEmpty() );
00693 
00694             KArchiveEntry* entry;
00695             if ( isdir )
00696             {
00697                 QString path = QDir::cleanPath( name );
00698                 const KArchiveEntry* ent = rootDir()->entry( path );
00699                 if ( ent && ent->isDirectory() )
00700                 {
00701                     //kDebug(7040) << "Directory already exists, NOT going to add it again";
00702                     entry = 0;
00703                 }
00704                 else
00705                 {
00706                     entry = new KArchiveDirectory( this, entryName, access, (int)pfi.mtime, rootDir()->user(), rootDir()->group(), QString() );
00707                     //kDebug(7040) << "KArchiveDirectory created, entryName= " << entryName << ", name=" << name;
00708                 }
00709         }
00710             else
00711             {
00712             QString symlink;
00713         if (S_ISLNK(access)) {
00714             symlink = QFile::decodeName(pfi.guessed_symlink);
00715         }
00716                 entry = new KZipFileEntry( this, entryName, access, pfi.mtime,
00717                     rootDir()->user(), rootDir()->group(),
00718                     symlink, name, dataoffset,
00719                     ucsize, cmethod, csize );
00720                 static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset );
00721                 //kDebug(7040) << "KZipFileEntry created, entryName= " << entryName << ", name=" << name;
00722                 d->m_fileList.append( static_cast<KZipFileEntry *>( entry ) );
00723             }
00724 
00725             if ( entry )
00726             {
00727                 if ( pos == -1 )
00728                 {
00729                     rootDir()->addEntry(entry);
00730                 }
00731                 else
00732                 {
00733                     // In some tar files we can find dir/./file => call cleanPath
00734                     QString path = QDir::cleanPath( name.left( pos ) );
00735                     // Ensure container directory exists, create otherwise
00736                     KArchiveDirectory * tdir = findOrCreate( path );
00737                     tdir->addEntry(entry);
00738                 }
00739             }
00740 
00741             //calculate offset to next entry
00742             offset += 46 + commlen + extralen + namelen;
00743             bool b = dev->seek(offset);
00744             Q_ASSERT( b );
00745             if ( !b )
00746               return false;
00747         }
00748         else if ( startOfFile )
00749         {
00750             // The file does not start with any ZIP header (e.g. self-extractable ZIP files)
00751             // Therefore we need to find the first PK\003\004 (local header)
00752             kDebug(7040) << "Try to skip start of file";
00753             startOfFile = false;
00754             bool foundSignature = false;
00755 
00756             while (!foundSignature)
00757             {
00758                 n = dev->read( buffer, 1 );
00759                 if (n < 1)
00760                 {
00761                     kWarning(7040) << "Invalid ZIP file. Unexpected end of file. " ;
00762                     return false;
00763                 }
00764 
00765                 if ( buffer[0] != 'P' )
00766                     continue;
00767 
00768                 n = dev->read( buffer, 3 );
00769                 if (n < 3)
00770                 {
00771                     kWarning(7040) << "Invalid ZIP file. Unexpected end of file. " ;
00772                     return false;
00773                 }
00774 
00775                 // We have to detect the magic token for a local header: PK\003\004
00776                 /*
00777                  * Note: we do not need to check the other magics, if the ZIP file has no
00778                  * local header, then it has not any files!
00779                  */
00780                 if ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 )
00781                 {
00782                     foundSignature = true;
00783                     dev->seek( dev->pos() - 4 ); // go back 4 bytes, so that the magic bytes can be found...
00784                 }
00785                 else if ( buffer[0] == 'P' || buffer[1] == 'P' || buffer[2] == 'P' )
00786                 {
00787                         // We have another P character so we must go back a little to check if it is a magic
00788                     dev->seek( dev->pos() - 3 );
00789                 }
00790             }
00791         }
00792         else
00793         {
00794             kWarning(7040) << "Invalid ZIP file. Unrecognized header at offset " << offset;
00795 
00796             return false;
00797         }
00798     }
00799     //kDebug(7040) << "*** done *** ";
00800     return true;
00801 }
00802 
00803 bool KZip::closeArchive()
00804 {
00805     if ( ! ( mode() & QIODevice::WriteOnly ) )
00806     {
00807         //kDebug(7040) << "readonly";
00808         return true;
00809     }
00810 
00811     //ReadWrite or WriteOnly
00812     //write all central dir file entries
00813 
00814     // to be written at the end of the file...
00815     char buffer[ 22 ]; // first used for 12, then for 22 at the end
00816     uLong crc = crc32(0L, Z_NULL, 0);
00817 
00818     qint64 centraldiroffset = device()->pos();
00819     //kDebug(7040) << "closearchive: centraldiroffset: " << centraldiroffset;
00820     qint64 atbackup = centraldiroffset;
00821     QMutableListIterator<KZipFileEntry*> it( d->m_fileList );
00822 
00823     while(it.hasNext())
00824     {   //set crc and compressed size in each local file header
00825         it.next();
00826         if ( !device()->seek( it.value()->headerStart() + 14 ) )
00827             return false;
00828     //kDebug(7040) << "closearchive setcrcandcsize: fileName:"
00829     //    << it.current()->path()
00830     //    << "encoding:" << it.current()->encoding();
00831 
00832         uLong mycrc = it.value()->crc32();
00833         buffer[0] = char(mycrc); // crc checksum, at headerStart+14
00834         buffer[1] = char(mycrc >> 8);
00835         buffer[2] = char(mycrc >> 16);
00836         buffer[3] = char(mycrc >> 24);
00837 
00838         int mysize1 = it.value()->compressedSize();
00839         buffer[4] = char(mysize1); // compressed file size, at headerStart+18
00840         buffer[5] = char(mysize1 >> 8);
00841         buffer[6] = char(mysize1 >> 16);
00842         buffer[7] = char(mysize1 >> 24);
00843 
00844         int myusize = it.value()->size();
00845         buffer[8] = char(myusize); // uncompressed file size, at headerStart+22
00846         buffer[9] = char(myusize >> 8);
00847         buffer[10] = char(myusize >> 16);
00848         buffer[11] = char(myusize >> 24);
00849 
00850         if ( device()->write( buffer, 12 ) != 12 )
00851             return false;
00852     }
00853     device()->seek( atbackup );
00854 
00855     it.toFront();
00856     while (it.hasNext())
00857     {
00858     it.next();
00859         //kDebug(7040) << "fileName:" << it.current()->path()
00860         //              << "encoding:" << it.current()->encoding();
00861 
00862         QByteArray path = QFile::encodeName(it.value()->path());
00863 
00864     const int extra_field_len = 9;
00865         int bufferSize = extra_field_len + path.length() + 46;
00866         char* buffer = new char[ bufferSize ];
00867 
00868         memset(buffer, 0, 46); // zero is a nice default for most header fields
00869 
00870         const char head[] =
00871         {
00872             'P', 'K', 1, 2, // central file header signature
00873             0x14, 3,        // version made by (3 == UNIX)
00874             0x14, 0         // version needed to extract
00875         };
00876 
00877     // I do not know why memcpy is not working here
00878         //memcpy(buffer, head, sizeof(head));
00879         memmove(buffer, head, sizeof(head));
00880 
00881         buffer[ 10 ] = char(it.value()->encoding()); // compression method
00882         buffer[ 11 ] = char(it.value()->encoding() >> 8);
00883 
00884         transformToMsDos( it.value()->datetime(), &buffer[ 12 ] );
00885 
00886         uLong mycrc = it.value()->crc32();
00887         buffer[ 16 ] = char(mycrc); // crc checksum
00888         buffer[ 17 ] = char(mycrc >> 8);
00889         buffer[ 18 ] = char(mycrc >> 16);
00890         buffer[ 19 ] = char(mycrc >> 24);
00891 
00892         int mysize1 = it.value()->compressedSize();
00893         buffer[ 20 ] = char(mysize1); // compressed file size
00894         buffer[ 21 ] = char(mysize1 >> 8);
00895         buffer[ 22 ] = char(mysize1 >> 16);
00896         buffer[ 23 ] = char(mysize1 >> 24);
00897 
00898         int mysize = it.value()->size();
00899         buffer[ 24 ] = char(mysize); // uncompressed file size
00900         buffer[ 25 ] = char(mysize >> 8);
00901         buffer[ 26 ] = char(mysize >> 16);
00902         buffer[ 27 ] = char(mysize >> 24);
00903 
00904         buffer[ 28 ] = char(it.value()->path().length()); // fileName length
00905         buffer[ 29 ] = char(it.value()->path().length() >> 8);
00906 
00907     buffer[ 30 ] = char(extra_field_len);
00908     buffer[ 31 ] = char(extra_field_len >> 8);
00909 
00910     buffer[ 40 ] = char(it.value()->permissions());
00911     buffer[ 41 ] = char(it.value()->permissions() >> 8);
00912 
00913         int myhst = it.value()->headerStart();
00914         buffer[ 42 ] = char(myhst); //relative offset of local header
00915         buffer[ 43 ] = char(myhst >> 8);
00916         buffer[ 44 ] = char(myhst >> 16);
00917         buffer[ 45 ] = char(myhst >> 24);
00918 
00919         // file name
00920         strncpy( buffer + 46, path, path.length() );
00921     //kDebug(7040) << "closearchive length to write: " << bufferSize;
00922 
00923     // extra field
00924     char *extfield = buffer + 46 + path.length();
00925     extfield[0] = 'U';
00926     extfield[1] = 'T';
00927     extfield[2] = 5;
00928     extfield[3] = 0;
00929     extfield[4] = 1 | 2 | 4;    // specify flags from local field
00930                     // (unless I misread the spec)
00931     // provide only modification time
00932     unsigned long time = (unsigned long)it.value()->date();
00933     extfield[5] = char(time);
00934     extfield[6] = char(time >> 8);
00935     extfield[7] = char(time >> 16);
00936     extfield[8] = char(time >> 24);
00937 
00938         crc = crc32(crc, (Bytef *)buffer, bufferSize );
00939         bool ok = ( device()->write( buffer, bufferSize ) == bufferSize );
00940         delete[] buffer;
00941         if ( !ok )
00942             return false;
00943     }
00944     qint64 centraldirendoffset = device()->pos();
00945     //kDebug(7040) << "closearchive: centraldirendoffset: " << centraldirendoffset;
00946     //kDebug(7040) << "closearchive: device()->pos(): " << device()->pos();
00947 
00948     //write end of central dir record.
00949     buffer[ 0 ] = 'P'; //end of central dir signature
00950     buffer[ 1 ] = 'K';
00951     buffer[ 2 ] = 5;
00952     buffer[ 3 ] = 6;
00953 
00954     buffer[ 4 ] = 0; // number of this disk
00955     buffer[ 5 ] = 0;
00956 
00957     buffer[ 6 ] = 0; // number of disk with start of central dir
00958     buffer[ 7 ] = 0;
00959 
00960     int count = d->m_fileList.count();
00961     //kDebug(7040) << "number of files (count): " << count;
00962 
00963 
00964     buffer[ 8 ] = char(count); // total number of entries in central dir of
00965     buffer[ 9 ] = char(count >> 8); // this disk
00966 
00967     buffer[ 10 ] = buffer[ 8 ]; // total number of entries in the central dir
00968     buffer[ 11 ] = buffer[ 9 ];
00969 
00970     int cdsize = centraldirendoffset - centraldiroffset;
00971     buffer[ 12 ] = char(cdsize); // size of the central dir
00972     buffer[ 13 ] = char(cdsize >> 8);
00973     buffer[ 14 ] = char(cdsize >> 16);
00974     buffer[ 15 ] = char(cdsize >> 24);
00975 
00976     //kDebug(7040) << "end : centraldiroffset: " << centraldiroffset;
00977     //kDebug(7040) << "end : centraldirsize: " << cdsize;
00978 
00979     buffer[ 16 ] = char(centraldiroffset); // central dir offset
00980     buffer[ 17 ] = char(centraldiroffset >> 8);
00981     buffer[ 18 ] = char(centraldiroffset >> 16);
00982     buffer[ 19 ] = char(centraldiroffset >> 24);
00983 
00984     buffer[ 20 ] = 0; //zipfile comment length
00985     buffer[ 21 ] = 0;
00986 
00987     if ( device()->write( buffer, 22 ) != 22 )
00988         return false;
00989 
00990     return true;
00991 }
00992 
00993 bool KZip::doWriteDir( const QString&, const QString&, const QString&,
00994                        mode_t, time_t, time_t, time_t ) {
00995         return true;
00996 }
00997 
00998 bool KZip::doPrepareWriting(const QString &name, const QString &user,
00999                                const QString &group, qint64 /*size*/, mode_t perm,
01000                                time_t atime, time_t mtime, time_t ctime) {
01001     //kDebug(7040);
01002     if ( !isOpen() )
01003     {
01004         qWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
01005         return false;
01006     }
01007 
01008     if ( ! ( mode() & QIODevice::WriteOnly ) ) // accept WriteOnly and ReadWrite
01009     {
01010         qWarning( "KZip::writeFile: You must open the zip file for writing\n");
01011         return false;
01012     }
01013 
01014     Q_ASSERT( device() );
01015 
01016     // set right offset in zip.
01017     if ( !device()->seek( d->m_offset ) ) {
01018         kWarning(7040) << "doPrepareWriting: cannot seek in ZIP file. Disk full?";
01019         return false;
01020     }
01021 
01022     // delete entries in the filelist with the same fileName as the one we want
01023     // to save, so that we don't have duplicate file entries when viewing the zip
01024     // with konqi...
01025     // CAUTION: the old file itself is still in the zip and won't be removed !!!
01026     QMutableListIterator<KZipFileEntry*> it( d->m_fileList );
01027     //kDebug(7040) << "fileName to write: " << name;
01028     while(it.hasNext())
01029     {
01030         it.next();
01031         //kDebug(7040) << "prepfileName: " << it.current()->path();
01032         if (name == it.value()->path() )
01033         {
01034             //kDebug(7040) << "removing following entry: " << it.current()->path();
01035         delete it.value();
01036             it.remove();
01037         }
01038 
01039     }
01040     // Find or create parent dir
01041     KArchiveDirectory* parentDir = rootDir();
01042     QString fileName( name );
01043     int i = name.lastIndexOf( '/' );
01044     if ( i != -1 )
01045     {
01046         QString dir = name.left( i );
01047         fileName = name.mid( i + 1 );
01048         //kDebug(7040) << "ensuring" << dir << "exists. fileName=" << fileName;
01049         parentDir = findOrCreate( dir );
01050     }
01051 
01052     // construct a KZipFileEntry and add it to list
01053     KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, QString(),
01054                                            name, device()->pos() + 30 + name.length(), // start
01055                                            0 /*size unknown yet*/, d->m_compression, 0 /*csize unknown yet*/ );
01056     e->setHeaderStart( device()->pos() );
01057     //kDebug(7040) << "wrote file start: " << e->position() << " name: " << name;
01058     parentDir->addEntry( e );
01059 
01060     d->m_currentFile = e;
01061     d->m_fileList.append( e );
01062 
01063     int extra_field_len = 0;
01064     if ( d->m_extraField == ModificationTime )
01065         extra_field_len = 17;   // value also used in finishWriting()
01066 
01067     // write out zip header
01068     QByteArray encodedName = QFile::encodeName(name);
01069     int bufferSize = extra_field_len + encodedName.length() + 30;
01070     //kDebug(7040) << "bufferSize=" << bufferSize;
01071     char* buffer = new char[ bufferSize ];
01072 
01073     buffer[ 0 ] = 'P'; //local file header signature
01074     buffer[ 1 ] = 'K';
01075     buffer[ 2 ] = 3;
01076     buffer[ 3 ] = 4;
01077 
01078     buffer[ 4 ] = 0x14; // version needed to extract
01079     buffer[ 5 ] = 0;
01080 
01081     buffer[ 6 ] = 0; // general purpose bit flag
01082     buffer[ 7 ] = 0;
01083 
01084     buffer[ 8 ] = char(e->encoding()); // compression method
01085     buffer[ 9 ] = char(e->encoding() >> 8);
01086 
01087     transformToMsDos( e->datetime(), &buffer[ 10 ] );
01088 
01089     buffer[ 14 ] = 'C'; //dummy crc
01090     buffer[ 15 ] = 'R';
01091     buffer[ 16 ] = 'C';
01092     buffer[ 17 ] = 'q';
01093 
01094     buffer[ 18 ] = 'C'; //compressed file size
01095     buffer[ 19 ] = 'S';
01096     buffer[ 20 ] = 'I';
01097     buffer[ 21 ] = 'Z';
01098 
01099     buffer[ 22 ] = 'U'; //uncompressed file size
01100     buffer[ 23 ] = 'S';
01101     buffer[ 24 ] = 'I';
01102     buffer[ 25 ] = 'Z';
01103 
01104     buffer[ 26 ] = (uchar)(encodedName.length()); //fileName length
01105     buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
01106 
01107     buffer[ 28 ] = (uchar)(extra_field_len); // extra field length
01108     buffer[ 29 ] = (uchar)(extra_field_len >> 8);
01109 
01110     // file name
01111     strncpy( buffer + 30, encodedName, encodedName.length() );
01112 
01113     // extra field
01114     if ( d->m_extraField == ModificationTime )
01115     {
01116         char *extfield = buffer + 30 + encodedName.length();
01117         // "Extended timestamp" header (0x5455)
01118         extfield[0] = 'U';
01119         extfield[1] = 'T';
01120         extfield[2] = 13; // data size
01121         extfield[3] = 0;
01122         extfield[4] = 1 | 2 | 4;    // contains mtime, atime, ctime
01123 
01124         extfield[5] = char(mtime);
01125         extfield[6] = char(mtime >> 8);
01126         extfield[7] = char(mtime >> 16);
01127         extfield[8] = char(mtime >> 24);
01128 
01129         extfield[9] = char(atime);
01130         extfield[10] = char(atime >> 8);
01131         extfield[11] = char(atime >> 16);
01132         extfield[12] = char(atime >> 24);
01133 
01134         extfield[13] = char(ctime);
01135         extfield[14] = char(ctime >> 8);
01136         extfield[15] = char(ctime >> 16);
01137         extfield[16] = char(ctime >> 24);
01138     }
01139 
01140     // Write header
01141     bool b = (device()->write( buffer, bufferSize ) == bufferSize );
01142     d->m_crc = 0L;
01143     delete[] buffer;
01144 
01145     Q_ASSERT( b );
01146     if (!b) {
01147         return false;
01148     }
01149 
01150     // Prepare device for writing the data
01151     // Either device() if no compression, or a KFilterDev to compress
01152     if ( d->m_compression == 0 ) {
01153         d->m_currentDev = device();
01154         return true;
01155     }
01156 
01157     d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
01158     Q_ASSERT( d->m_currentDev );
01159     if ( !d->m_currentDev ) {
01160         return false; // ouch
01161     }
01162     static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders(); // Just zlib, not gzip
01163 
01164     b = d->m_currentDev->open( QIODevice::WriteOnly );
01165     Q_ASSERT( b );
01166     return b;
01167 }
01168 
01169 bool KZip::doFinishWriting( qint64 size )
01170 {
01171     if ( d->m_currentFile->encoding() == 8 ) {
01172         // Finish
01173         (void)d->m_currentDev->write( 0, 0 );
01174         delete d->m_currentDev;
01175     }
01176     // If 0, d->m_currentDev was device() - don't delete ;)
01177     d->m_currentDev = 0L;
01178 
01179     Q_ASSERT( d->m_currentFile );
01180     //kDebug(7040) << "fileName: " << d->m_currentFile->path();
01181     //kDebug(7040) << "getpos (at): " << device()->pos();
01182     d->m_currentFile->setSize(size);
01183     int extra_field_len = 0;
01184     if ( d->m_extraField == ModificationTime )
01185         extra_field_len = 17;   // value also used in finishWriting()
01186 
01187     int csize = device()->pos() -
01188         d->m_currentFile->headerStart() - 30 -
01189         d->m_currentFile->path().length() - extra_field_len;
01190     d->m_currentFile->setCompressedSize(csize);
01191     //kDebug(7040) << "usize: " << d->m_currentFile->size();
01192     //kDebug(7040) << "csize: " << d->m_currentFile->compressedSize();
01193     //kDebug(7040) << "headerstart: " << d->m_currentFile->headerStart();
01194 
01195     //kDebug(7040) << "crc: " << d->m_crc;
01196     d->m_currentFile->setCRC32( d->m_crc );
01197 
01198     d->m_currentFile = 0L;
01199 
01200     // update saved offset for appending new files
01201     d->m_offset = device()->pos();
01202     return true;
01203 }
01204 
01205 bool KZip::doWriteSymLink(const QString &name, const QString &target,
01206                           const QString &user, const QString &group,
01207                           mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01208   // reassure that symlink flag is set, otherwise strange things happen on
01209   // extraction
01210   perm |= S_IFLNK;
01211   Compression c = compression();
01212   setCompression(NoCompression);    // link targets are never compressed
01213 
01214   if (!doPrepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) {
01215     kWarning() << "prepareWriting failed";
01216     setCompression(c);
01217     return false;
01218   }
01219 
01220   QByteArray symlink_target = QFile::encodeName(target);
01221   if (!writeData(symlink_target, symlink_target.length())) {
01222     kWarning() << "writeData failed";
01223     setCompression(c);
01224     return false;
01225   }
01226 
01227   if (!finishWriting(symlink_target.length())) {
01228     kWarning() << "finishWriting failed";
01229     setCompression(c);
01230     return false;
01231   }
01232 
01233   setCompression(c);
01234   return true;
01235 }
01236 
01237 void KZip::virtual_hook( int id, void* data )
01238 {
01239     KArchive::virtual_hook( id, data );
01240 }
01241 
01242 bool KZip::writeData(const char * data, qint64 size)
01243 {
01244     Q_ASSERT( d->m_currentFile );
01245     Q_ASSERT( d->m_currentDev );
01246     if (!d->m_currentFile || !d->m_currentDev) {
01247         return false;
01248     }
01249 
01250     // crc to be calculated over uncompressed stuff...
01251     // and they didn't mention it in their docs...
01252     d->m_crc = crc32(d->m_crc, (const Bytef *) data , size);
01253 
01254     qint64 written = d->m_currentDev->write( data, size );
01255     //kDebug(7040) << "wrote" << size << "bytes.";
01256     return written == size;
01257 }
01258 
01259 void KZip::setCompression( Compression c )
01260 {
01261     d->m_compression = ( c == NoCompression ) ? 0 : 8;
01262 }
01263 
01264 KZip::Compression KZip::compression() const
01265 {
01266    return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
01267 }
01268 
01269 void KZip::setExtraField( ExtraField ef )
01270 {
01271     d->m_extraField = ef;
01272 }
01273 
01274 KZip::ExtraField KZip::extraField() const
01275 {
01276     return d->m_extraField;
01277 }
01278 
01282 class KZipFileEntry::KZipFileEntryPrivate
01283 {
01284 public:
01285     KZipFileEntryPrivate()
01286     : crc(0),
01287       compressedSize(0),
01288       headerStart(0),
01289       encoding(0)
01290     {}
01291     unsigned long crc;
01292     qint64        compressedSize;
01293     qint64        headerStart;
01294     int           encoding;
01295     QString       path;
01296 };
01297 
01298 KZipFileEntry::KZipFileEntry(KZip* zip, const QString& name, int access, int date,
01299                              const QString& user, const QString& group, const QString& symlink,
01300                              const QString& path, qint64 start, qint64 uncompressedSize,
01301                              int encoding, qint64 compressedSize)
01302  : KArchiveFile(zip, name, access, date, user, group, symlink, start, uncompressedSize ),
01303    d(new KZipFileEntryPrivate)
01304 {
01305     d->path = path;
01306     d->encoding = encoding;
01307     d->compressedSize = compressedSize;
01308 }
01309 
01310 KZipFileEntry::~KZipFileEntry()
01311 {
01312     delete d;
01313 }
01314 
01315 int KZipFileEntry::encoding() const
01316 {
01317     return d->encoding;
01318 }
01319 
01320 qint64 KZipFileEntry::compressedSize() const
01321 {
01322     return d->compressedSize;
01323 }
01324 
01325 void KZipFileEntry::setCompressedSize(qint64 compressedSize)
01326 {
01327     d->compressedSize = compressedSize;
01328 }
01329 
01330 void KZipFileEntry::setHeaderStart(qint64 headerstart)
01331 {
01332     d->headerStart = headerstart;
01333 }
01334 
01335 qint64 KZipFileEntry::headerStart() const
01336 {
01337     return d->headerStart;
01338 }
01339 
01340 unsigned long KZipFileEntry::crc32() const
01341 {
01342     return d->crc;
01343 }
01344 
01345 void KZipFileEntry::setCRC32(unsigned long crc32)
01346 {
01347     d->crc=crc32;
01348 }
01349 
01350 const QString &KZipFileEntry::path() const
01351 {
01352     return d->path;
01353 }
01354 
01355 QByteArray KZipFileEntry::data() const
01356 {
01357     QIODevice* dev = createDevice();
01358     QByteArray arr;
01359     if ( dev ) {
01360         arr = dev->readAll();
01361         delete dev;
01362     }
01363     return arr;
01364 }
01365 
01366 QIODevice* KZipFileEntry::createDevice() const
01367 {
01368     //kDebug(7040) << "creating iodevice limited to pos=" << position() << ", csize=" << compressedSize();
01369     // Limit the reading to the appropriate part of the underlying device (e.g. file)
01370     KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
01371     if ( encoding() == 0 || compressedSize() == 0 ) // no compression (or even no data)
01372         return limitedDev;
01373 
01374     if ( encoding() == 8 )
01375     {
01376         // On top of that, create a device that uncompresses the zlib data
01377         QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
01378         if ( !filterDev )
01379             return 0L; // ouch
01380         static_cast<KFilterDev *>(filterDev)->setSkipHeaders(); // Just zlib, not gzip
01381         bool b = filterDev->open( QIODevice::ReadOnly );
01382         Q_ASSERT( b );
01383         return filterDev;
01384     }
01385 
01386     kError() << "This zip file contains files compressed with method"
01387               << encoding() << ", this method is currently not supported by KZip,"
01388               << "please use a command-line tool to handle this file.";
01389     return 0L;
01390 }

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal