• Skip to content
  • Skip to link menu
KDE 4.2 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             // crc32 of the file
00643             uint crc32 = (uchar)buffer[19] << 24 | (uchar)buffer[18] << 16 |
00644                        (uchar)buffer[17] << 8 | (uchar)buffer[16];
00645 
00646             // uncompressed file size
00647             uint ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 |
00648                 (uchar)buffer[25] << 8 | (uchar)buffer[24];
00649             // compressed file size
00650             uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 |
00651                 (uchar)buffer[21] << 8 | (uchar)buffer[20];
00652 
00653             // offset of local header
00654             uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 |
00655                 (uchar)buffer[43] << 8 | (uchar)buffer[42];
00656 
00657             // some clever people use different extra field lengths
00658             // in the central header and in the local header... funny.
00659             // so we need to get the localextralen to calculate the offset
00660             // from localheaderstart to dataoffset
00661             int localextralen = pfi.extralen; // FIXME: this will not work if
00662                             // no local header exists
00663 
00664             //kDebug(7040) << "localextralen: " << localextralen;
00665 
00666             // offset, where the real data for uncompression starts
00667             uint dataoffset = localheaderoffset + 30 + localextralen + namelen; //comment only in central header
00668 
00669             //kDebug(7040) << "esize: " << esize;
00670             //kDebug(7040) << "eoffset: " << eoffset;
00671             //kDebug(7040) << "csize: " << csize;
00672 
00673         int os_madeby = (uchar)buffer[5];
00674             bool isdir = false;
00675             int access = 0100644;
00676 
00677         if (os_madeby == 3) {   // good ole unix
00678             access = (uchar)buffer[40] | (uchar)buffer[41] << 8;
00679         }
00680 
00681             QString entryName;
00682 
00683             if ( name.endsWith( '/' ) ) // Entries with a trailing slash are directories
00684             {
00685                 isdir = true;
00686                 name = name.left( name.length() - 1 );
00687                 if (os_madeby != 3) access = S_IFDIR | 0755;
00688         else Q_ASSERT(access & S_IFDIR);
00689             }
00690 
00691             int pos = name.lastIndexOf( '/' );
00692             if ( pos == -1 )
00693                 entryName = name;
00694             else
00695                 entryName = name.mid( pos + 1 );
00696             Q_ASSERT( !entryName.isEmpty() );
00697 
00698             KArchiveEntry* entry;
00699             if ( isdir )
00700             {
00701                 QString path = QDir::cleanPath( name );
00702                 const KArchiveEntry* ent = rootDir()->entry( path );
00703                 if ( ent && ent->isDirectory() )
00704                 {
00705                     //kDebug(7040) << "Directory already exists, NOT going to add it again";
00706                     entry = 0;
00707                 }
00708                 else
00709                 {
00710                     entry = new KArchiveDirectory( this, entryName, access, (int)pfi.mtime, rootDir()->user(), rootDir()->group(), QString() );
00711                     //kDebug(7040) << "KArchiveDirectory created, entryName= " << entryName << ", name=" << name;
00712                 }
00713         }
00714             else
00715             {
00716             QString symlink;
00717         if (S_ISLNK(access)) {
00718             symlink = QFile::decodeName(pfi.guessed_symlink);
00719         }
00720                 entry = new KZipFileEntry( this, entryName, access, pfi.mtime,
00721                     rootDir()->user(), rootDir()->group(),
00722                     symlink, name, dataoffset,
00723                     ucsize, cmethod, csize );
00724                 static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset );
00725                 static_cast<KZipFileEntry*>(entry)->setCRC32(crc32);
00726                 //kDebug(7040) << "KZipFileEntry created, entryName= " << entryName << ", name=" << name;
00727                 d->m_fileList.append( static_cast<KZipFileEntry *>( entry ) );
00728             }
00729 
00730             if ( entry )
00731             {
00732                 if ( pos == -1 )
00733                 {
00734                     rootDir()->addEntry(entry);
00735                 }
00736                 else
00737                 {
00738                     // In some tar files we can find dir/./file => call cleanPath
00739                     QString path = QDir::cleanPath( name.left( pos ) );
00740                     // Ensure container directory exists, create otherwise
00741                     KArchiveDirectory * tdir = findOrCreate( path );
00742                     tdir->addEntry(entry);
00743                 }
00744             }
00745 
00746             //calculate offset to next entry
00747             offset += 46 + commlen + extralen + namelen;
00748             bool b = dev->seek(offset);
00749             Q_ASSERT( b );
00750             if ( !b )
00751               return false;
00752         }
00753         else if ( startOfFile )
00754         {
00755             // The file does not start with any ZIP header (e.g. self-extractable ZIP files)
00756             // Therefore we need to find the first PK\003\004 (local header)
00757             kDebug(7040) << "Try to skip start of file";
00758             startOfFile = false;
00759             bool foundSignature = false;
00760 
00761             while (!foundSignature)
00762             {
00763                 n = dev->read( buffer, 1 );
00764                 if (n < 1)
00765                 {
00766                     kWarning(7040) << "Invalid ZIP file. Unexpected end of file. " ;
00767                     return false;
00768                 }
00769 
00770                 if ( buffer[0] != 'P' )
00771                     continue;
00772 
00773                 n = dev->read( buffer, 3 );
00774                 if (n < 3)
00775                 {
00776                     kWarning(7040) << "Invalid ZIP file. Unexpected end of file. " ;
00777                     return false;
00778                 }
00779 
00780                 // We have to detect the magic token for a local header: PK\003\004
00781                 /*
00782                  * Note: we do not need to check the other magics, if the ZIP file has no
00783                  * local header, then it has not any files!
00784                  */
00785                 if ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 )
00786                 {
00787                     foundSignature = true;
00788                     dev->seek( dev->pos() - 4 ); // go back 4 bytes, so that the magic bytes can be found...
00789                 }
00790                 else if ( buffer[0] == 'P' || buffer[1] == 'P' || buffer[2] == 'P' )
00791                 {
00792                         // We have another P character so we must go back a little to check if it is a magic
00793                     dev->seek( dev->pos() - 3 );
00794                 }
00795             }
00796         }
00797         else
00798         {
00799             kWarning(7040) << "Invalid ZIP file. Unrecognized header at offset " << offset;
00800 
00801             return false;
00802         }
00803     }
00804     //kDebug(7040) << "*** done *** ";
00805     return true;
00806 }
00807 
00808 bool KZip::closeArchive()
00809 {
00810     if ( ! ( mode() & QIODevice::WriteOnly ) )
00811     {
00812         //kDebug(7040) << "readonly";
00813         return true;
00814     }
00815 
00816     //ReadWrite or WriteOnly
00817     //write all central dir file entries
00818 
00819     // to be written at the end of the file...
00820     char buffer[ 22 ]; // first used for 12, then for 22 at the end
00821     uLong crc = crc32(0L, Z_NULL, 0);
00822 
00823     qint64 centraldiroffset = device()->pos();
00824     //kDebug(7040) << "closearchive: centraldiroffset: " << centraldiroffset;
00825     qint64 atbackup = centraldiroffset;
00826     QMutableListIterator<KZipFileEntry*> it( d->m_fileList );
00827 
00828     while(it.hasNext())
00829     {   //set crc and compressed size in each local file header
00830         it.next();
00831         if ( !device()->seek( it.value()->headerStart() + 14 ) )
00832             return false;
00833     //kDebug(7040) << "closearchive setcrcandcsize: fileName:"
00834     //    << it.current()->path()
00835     //    << "encoding:" << it.current()->encoding();
00836 
00837         uLong mycrc = it.value()->crc32();
00838         buffer[0] = char(mycrc); // crc checksum, at headerStart+14
00839         buffer[1] = char(mycrc >> 8);
00840         buffer[2] = char(mycrc >> 16);
00841         buffer[3] = char(mycrc >> 24);
00842 
00843         int mysize1 = it.value()->compressedSize();
00844         buffer[4] = char(mysize1); // compressed file size, at headerStart+18
00845         buffer[5] = char(mysize1 >> 8);
00846         buffer[6] = char(mysize1 >> 16);
00847         buffer[7] = char(mysize1 >> 24);
00848 
00849         int myusize = it.value()->size();
00850         buffer[8] = char(myusize); // uncompressed file size, at headerStart+22
00851         buffer[9] = char(myusize >> 8);
00852         buffer[10] = char(myusize >> 16);
00853         buffer[11] = char(myusize >> 24);
00854 
00855         if ( device()->write( buffer, 12 ) != 12 )
00856             return false;
00857     }
00858     device()->seek( atbackup );
00859 
00860     it.toFront();
00861     while (it.hasNext())
00862     {
00863     it.next();
00864         //kDebug(7040) << "fileName:" << it.current()->path()
00865         //              << "encoding:" << it.current()->encoding();
00866 
00867         QByteArray path = QFile::encodeName(it.value()->path());
00868 
00869     const int extra_field_len = 9;
00870         int bufferSize = extra_field_len + path.length() + 46;
00871         char* buffer = new char[ bufferSize ];
00872 
00873         memset(buffer, 0, 46); // zero is a nice default for most header fields
00874 
00875         const char head[] =
00876         {
00877             'P', 'K', 1, 2, // central file header signature
00878             0x14, 3,        // version made by (3 == UNIX)
00879             0x14, 0         // version needed to extract
00880         };
00881 
00882     // I do not know why memcpy is not working here
00883         //memcpy(buffer, head, sizeof(head));
00884         memmove(buffer, head, sizeof(head));
00885 
00886         buffer[ 10 ] = char(it.value()->encoding()); // compression method
00887         buffer[ 11 ] = char(it.value()->encoding() >> 8);
00888 
00889         transformToMsDos( it.value()->datetime(), &buffer[ 12 ] );
00890 
00891         uLong mycrc = it.value()->crc32();
00892         buffer[ 16 ] = char(mycrc); // crc checksum
00893         buffer[ 17 ] = char(mycrc >> 8);
00894         buffer[ 18 ] = char(mycrc >> 16);
00895         buffer[ 19 ] = char(mycrc >> 24);
00896 
00897         int mysize1 = it.value()->compressedSize();
00898         buffer[ 20 ] = char(mysize1); // compressed file size
00899         buffer[ 21 ] = char(mysize1 >> 8);
00900         buffer[ 22 ] = char(mysize1 >> 16);
00901         buffer[ 23 ] = char(mysize1 >> 24);
00902 
00903         int mysize = it.value()->size();
00904         buffer[ 24 ] = char(mysize); // uncompressed file size
00905         buffer[ 25 ] = char(mysize >> 8);
00906         buffer[ 26 ] = char(mysize >> 16);
00907         buffer[ 27 ] = char(mysize >> 24);
00908 
00909         buffer[ 28 ] = char(it.value()->path().length()); // fileName length
00910         buffer[ 29 ] = char(it.value()->path().length() >> 8);
00911 
00912     buffer[ 30 ] = char(extra_field_len);
00913     buffer[ 31 ] = char(extra_field_len >> 8);
00914 
00915     buffer[ 40 ] = char(it.value()->permissions());
00916     buffer[ 41 ] = char(it.value()->permissions() >> 8);
00917 
00918         int myhst = it.value()->headerStart();
00919         buffer[ 42 ] = char(myhst); //relative offset of local header
00920         buffer[ 43 ] = char(myhst >> 8);
00921         buffer[ 44 ] = char(myhst >> 16);
00922         buffer[ 45 ] = char(myhst >> 24);
00923 
00924         // file name
00925         strncpy( buffer + 46, path, path.length() );
00926     //kDebug(7040) << "closearchive length to write: " << bufferSize;
00927 
00928     // extra field
00929     char *extfield = buffer + 46 + path.length();
00930     extfield[0] = 'U';
00931     extfield[1] = 'T';
00932     extfield[2] = 5;
00933     extfield[3] = 0;
00934     extfield[4] = 1 | 2 | 4;    // specify flags from local field
00935                     // (unless I misread the spec)
00936     // provide only modification time
00937     unsigned long time = (unsigned long)it.value()->date();
00938     extfield[5] = char(time);
00939     extfield[6] = char(time >> 8);
00940     extfield[7] = char(time >> 16);
00941     extfield[8] = char(time >> 24);
00942 
00943         crc = crc32(crc, (Bytef *)buffer, bufferSize );
00944         bool ok = ( device()->write( buffer, bufferSize ) == bufferSize );
00945         delete[] buffer;
00946         if ( !ok )
00947             return false;
00948     }
00949     qint64 centraldirendoffset = device()->pos();
00950     //kDebug(7040) << "closearchive: centraldirendoffset: " << centraldirendoffset;
00951     //kDebug(7040) << "closearchive: device()->pos(): " << device()->pos();
00952 
00953     //write end of central dir record.
00954     buffer[ 0 ] = 'P'; //end of central dir signature
00955     buffer[ 1 ] = 'K';
00956     buffer[ 2 ] = 5;
00957     buffer[ 3 ] = 6;
00958 
00959     buffer[ 4 ] = 0; // number of this disk
00960     buffer[ 5 ] = 0;
00961 
00962     buffer[ 6 ] = 0; // number of disk with start of central dir
00963     buffer[ 7 ] = 0;
00964 
00965     int count = d->m_fileList.count();
00966     //kDebug(7040) << "number of files (count): " << count;
00967 
00968 
00969     buffer[ 8 ] = char(count); // total number of entries in central dir of
00970     buffer[ 9 ] = char(count >> 8); // this disk
00971 
00972     buffer[ 10 ] = buffer[ 8 ]; // total number of entries in the central dir
00973     buffer[ 11 ] = buffer[ 9 ];
00974 
00975     int cdsize = centraldirendoffset - centraldiroffset;
00976     buffer[ 12 ] = char(cdsize); // size of the central dir
00977     buffer[ 13 ] = char(cdsize >> 8);
00978     buffer[ 14 ] = char(cdsize >> 16);
00979     buffer[ 15 ] = char(cdsize >> 24);
00980 
00981     //kDebug(7040) << "end : centraldiroffset: " << centraldiroffset;
00982     //kDebug(7040) << "end : centraldirsize: " << cdsize;
00983 
00984     buffer[ 16 ] = char(centraldiroffset); // central dir offset
00985     buffer[ 17 ] = char(centraldiroffset >> 8);
00986     buffer[ 18 ] = char(centraldiroffset >> 16);
00987     buffer[ 19 ] = char(centraldiroffset >> 24);
00988 
00989     buffer[ 20 ] = 0; //zipfile comment length
00990     buffer[ 21 ] = 0;
00991 
00992     if ( device()->write( buffer, 22 ) != 22 )
00993         return false;
00994 
00995     return true;
00996 }
00997 
00998 bool KZip::doWriteDir( const QString&, const QString&, const QString&,
00999                        mode_t, time_t, time_t, time_t ) {
01000         return true;
01001 }
01002 
01003 bool KZip::doPrepareWriting(const QString &name, const QString &user,
01004                                const QString &group, qint64 /*size*/, mode_t perm,
01005                                time_t atime, time_t mtime, time_t ctime) {
01006     //kDebug(7040);
01007     if ( !isOpen() )
01008     {
01009         qWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
01010         return false;
01011     }
01012 
01013     if ( ! ( mode() & QIODevice::WriteOnly ) ) // accept WriteOnly and ReadWrite
01014     {
01015         qWarning( "KZip::writeFile: You must open the zip file for writing\n");
01016         return false;
01017     }
01018 
01019     Q_ASSERT( device() );
01020 
01021     // set right offset in zip.
01022     if ( !device()->seek( d->m_offset ) ) {
01023         kWarning(7040) << "doPrepareWriting: cannot seek in ZIP file. Disk full?";
01024         return false;
01025     }
01026 
01027     // delete entries in the filelist with the same fileName as the one we want
01028     // to save, so that we don't have duplicate file entries when viewing the zip
01029     // with konqi...
01030     // CAUTION: the old file itself is still in the zip and won't be removed !!!
01031     QMutableListIterator<KZipFileEntry*> it( d->m_fileList );
01032     //kDebug(7040) << "fileName to write: " << name;
01033     while(it.hasNext())
01034     {
01035         it.next();
01036         //kDebug(7040) << "prepfileName: " << it.current()->path();
01037         if (name == it.value()->path() )
01038         {
01039             //kDebug(7040) << "removing following entry: " << it.current()->path();
01040         delete it.value();
01041             it.remove();
01042         }
01043 
01044     }
01045     // Find or create parent dir
01046     KArchiveDirectory* parentDir = rootDir();
01047     QString fileName( name );
01048     int i = name.lastIndexOf( '/' );
01049     if ( i != -1 )
01050     {
01051         QString dir = name.left( i );
01052         fileName = name.mid( i + 1 );
01053         //kDebug(7040) << "ensuring" << dir << "exists. fileName=" << fileName;
01054         parentDir = findOrCreate( dir );
01055     }
01056 
01057     // construct a KZipFileEntry and add it to list
01058     KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, QString(),
01059                                            name, device()->pos() + 30 + name.length(), // start
01060                                            0 /*size unknown yet*/, d->m_compression, 0 /*csize unknown yet*/ );
01061     e->setHeaderStart( device()->pos() );
01062     //kDebug(7040) << "wrote file start: " << e->position() << " name: " << name;
01063     parentDir->addEntry( e );
01064 
01065     d->m_currentFile = e;
01066     d->m_fileList.append( e );
01067 
01068     int extra_field_len = 0;
01069     if ( d->m_extraField == ModificationTime )
01070         extra_field_len = 17;   // value also used in finishWriting()
01071 
01072     // write out zip header
01073     QByteArray encodedName = QFile::encodeName(name);
01074     int bufferSize = extra_field_len + encodedName.length() + 30;
01075     //kDebug(7040) << "bufferSize=" << bufferSize;
01076     char* buffer = new char[ bufferSize ];
01077 
01078     buffer[ 0 ] = 'P'; //local file header signature
01079     buffer[ 1 ] = 'K';
01080     buffer[ 2 ] = 3;
01081     buffer[ 3 ] = 4;
01082 
01083     buffer[ 4 ] = 0x14; // version needed to extract
01084     buffer[ 5 ] = 0;
01085 
01086     buffer[ 6 ] = 0; // general purpose bit flag
01087     buffer[ 7 ] = 0;
01088 
01089     buffer[ 8 ] = char(e->encoding()); // compression method
01090     buffer[ 9 ] = char(e->encoding() >> 8);
01091 
01092     transformToMsDos( e->datetime(), &buffer[ 10 ] );
01093 
01094     buffer[ 14 ] = 'C'; //dummy crc
01095     buffer[ 15 ] = 'R';
01096     buffer[ 16 ] = 'C';
01097     buffer[ 17 ] = 'q';
01098 
01099     buffer[ 18 ] = 'C'; //compressed file size
01100     buffer[ 19 ] = 'S';
01101     buffer[ 20 ] = 'I';
01102     buffer[ 21 ] = 'Z';
01103 
01104     buffer[ 22 ] = 'U'; //uncompressed file size
01105     buffer[ 23 ] = 'S';
01106     buffer[ 24 ] = 'I';
01107     buffer[ 25 ] = 'Z';
01108 
01109     buffer[ 26 ] = (uchar)(encodedName.length()); //fileName length
01110     buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
01111 
01112     buffer[ 28 ] = (uchar)(extra_field_len); // extra field length
01113     buffer[ 29 ] = (uchar)(extra_field_len >> 8);
01114 
01115     // file name
01116     strncpy( buffer + 30, encodedName, encodedName.length() );
01117 
01118     // extra field
01119     if ( d->m_extraField == ModificationTime )
01120     {
01121         char *extfield = buffer + 30 + encodedName.length();
01122         // "Extended timestamp" header (0x5455)
01123         extfield[0] = 'U';
01124         extfield[1] = 'T';
01125         extfield[2] = 13; // data size
01126         extfield[3] = 0;
01127         extfield[4] = 1 | 2 | 4;    // contains mtime, atime, ctime
01128 
01129         extfield[5] = char(mtime);
01130         extfield[6] = char(mtime >> 8);
01131         extfield[7] = char(mtime >> 16);
01132         extfield[8] = char(mtime >> 24);
01133 
01134         extfield[9] = char(atime);
01135         extfield[10] = char(atime >> 8);
01136         extfield[11] = char(atime >> 16);
01137         extfield[12] = char(atime >> 24);
01138 
01139         extfield[13] = char(ctime);
01140         extfield[14] = char(ctime >> 8);
01141         extfield[15] = char(ctime >> 16);
01142         extfield[16] = char(ctime >> 24);
01143     }
01144 
01145     // Write header
01146     bool b = (device()->write( buffer, bufferSize ) == bufferSize );
01147     d->m_crc = 0L;
01148     delete[] buffer;
01149 
01150     Q_ASSERT( b );
01151     if (!b) {
01152         return false;
01153     }
01154 
01155     // Prepare device for writing the data
01156     // Either device() if no compression, or a KFilterDev to compress
01157     if ( d->m_compression == 0 ) {
01158         d->m_currentDev = device();
01159         return true;
01160     }
01161 
01162     d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
01163     Q_ASSERT( d->m_currentDev );
01164     if ( !d->m_currentDev ) {
01165         return false; // ouch
01166     }
01167     static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders(); // Just zlib, not gzip
01168 
01169     b = d->m_currentDev->open( QIODevice::WriteOnly );
01170     Q_ASSERT( b );
01171     return b;
01172 }
01173 
01174 bool KZip::doFinishWriting( qint64 size )
01175 {
01176     if ( d->m_currentFile->encoding() == 8 ) {
01177         // Finish
01178         (void)d->m_currentDev->write( 0, 0 );
01179         delete d->m_currentDev;
01180     }
01181     // If 0, d->m_currentDev was device() - don't delete ;)
01182     d->m_currentDev = 0L;
01183 
01184     Q_ASSERT( d->m_currentFile );
01185     //kDebug(7040) << "fileName: " << d->m_currentFile->path();
01186     //kDebug(7040) << "getpos (at): " << device()->pos();
01187     d->m_currentFile->setSize(size);
01188     int extra_field_len = 0;
01189     if ( d->m_extraField == ModificationTime )
01190         extra_field_len = 17;   // value also used in finishWriting()
01191 
01192     int csize = device()->pos() -
01193         d->m_currentFile->headerStart() - 30 -
01194         d->m_currentFile->path().length() - extra_field_len;
01195     d->m_currentFile->setCompressedSize(csize);
01196     //kDebug(7040) << "usize: " << d->m_currentFile->size();
01197     //kDebug(7040) << "csize: " << d->m_currentFile->compressedSize();
01198     //kDebug(7040) << "headerstart: " << d->m_currentFile->headerStart();
01199 
01200     //kDebug(7040) << "crc: " << d->m_crc;
01201     d->m_currentFile->setCRC32( d->m_crc );
01202 
01203     d->m_currentFile = 0L;
01204 
01205     // update saved offset for appending new files
01206     d->m_offset = device()->pos();
01207     return true;
01208 }
01209 
01210 bool KZip::doWriteSymLink(const QString &name, const QString &target,
01211                           const QString &user, const QString &group,
01212                           mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01213   // reassure that symlink flag is set, otherwise strange things happen on
01214   // extraction
01215   perm |= S_IFLNK;
01216   Compression c = compression();
01217   setCompression(NoCompression);    // link targets are never compressed
01218 
01219   if (!doPrepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) {
01220     kWarning() << "prepareWriting failed";
01221     setCompression(c);
01222     return false;
01223   }
01224 
01225   QByteArray symlink_target = QFile::encodeName(target);
01226   if (!writeData(symlink_target, symlink_target.length())) {
01227     kWarning() << "writeData failed";
01228     setCompression(c);
01229     return false;
01230   }
01231 
01232   if (!finishWriting(symlink_target.length())) {
01233     kWarning() << "finishWriting failed";
01234     setCompression(c);
01235     return false;
01236   }
01237 
01238   setCompression(c);
01239   return true;
01240 }
01241 
01242 void KZip::virtual_hook( int id, void* data )
01243 {
01244     KArchive::virtual_hook( id, data );
01245 }
01246 
01247 bool KZip::writeData(const char * data, qint64 size)
01248 {
01249     Q_ASSERT( d->m_currentFile );
01250     Q_ASSERT( d->m_currentDev );
01251     if (!d->m_currentFile || !d->m_currentDev) {
01252         return false;
01253     }
01254 
01255     // crc to be calculated over uncompressed stuff...
01256     // and they didn't mention it in their docs...
01257     d->m_crc = crc32(d->m_crc, (const Bytef *) data , size);
01258 
01259     qint64 written = d->m_currentDev->write( data, size );
01260     //kDebug(7040) << "wrote" << size << "bytes.";
01261     return written == size;
01262 }
01263 
01264 void KZip::setCompression( Compression c )
01265 {
01266     d->m_compression = ( c == NoCompression ) ? 0 : 8;
01267 }
01268 
01269 KZip::Compression KZip::compression() const
01270 {
01271    return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
01272 }
01273 
01274 void KZip::setExtraField( ExtraField ef )
01275 {
01276     d->m_extraField = ef;
01277 }
01278 
01279 KZip::ExtraField KZip::extraField() const
01280 {
01281     return d->m_extraField;
01282 }
01283 
01287 class KZipFileEntry::KZipFileEntryPrivate
01288 {
01289 public:
01290     KZipFileEntryPrivate()
01291     : crc(0),
01292       compressedSize(0),
01293       headerStart(0),
01294       encoding(0)
01295     {}
01296     unsigned long crc;
01297     qint64        compressedSize;
01298     qint64        headerStart;
01299     int           encoding;
01300     QString       path;
01301 };
01302 
01303 KZipFileEntry::KZipFileEntry(KZip* zip, const QString& name, int access, int date,
01304                              const QString& user, const QString& group, const QString& symlink,
01305                              const QString& path, qint64 start, qint64 uncompressedSize,
01306                              int encoding, qint64 compressedSize)
01307  : KArchiveFile(zip, name, access, date, user, group, symlink, start, uncompressedSize ),
01308    d(new KZipFileEntryPrivate)
01309 {
01310     d->path = path;
01311     d->encoding = encoding;
01312     d->compressedSize = compressedSize;
01313 }
01314 
01315 KZipFileEntry::~KZipFileEntry()
01316 {
01317     delete d;
01318 }
01319 
01320 int KZipFileEntry::encoding() const
01321 {
01322     return d->encoding;
01323 }
01324 
01325 qint64 KZipFileEntry::compressedSize() const
01326 {
01327     return d->compressedSize;
01328 }
01329 
01330 void KZipFileEntry::setCompressedSize(qint64 compressedSize)
01331 {
01332     d->compressedSize = compressedSize;
01333 }
01334 
01335 void KZipFileEntry::setHeaderStart(qint64 headerstart)
01336 {
01337     d->headerStart = headerstart;
01338 }
01339 
01340 qint64 KZipFileEntry::headerStart() const
01341 {
01342     return d->headerStart;
01343 }
01344 
01345 unsigned long KZipFileEntry::crc32() const
01346 {
01347     return d->crc;
01348 }
01349 
01350 void KZipFileEntry::setCRC32(unsigned long crc32)
01351 {
01352     d->crc=crc32;
01353 }
01354 
01355 const QString &KZipFileEntry::path() const
01356 {
01357     return d->path;
01358 }
01359 
01360 QByteArray KZipFileEntry::data() const
01361 {
01362     QIODevice* dev = createDevice();
01363     QByteArray arr;
01364     if ( dev ) {
01365         arr = dev->readAll();
01366         delete dev;
01367     }
01368     return arr;
01369 }
01370 
01371 QIODevice* KZipFileEntry::createDevice() const
01372 {
01373     //kDebug(7040) << "creating iodevice limited to pos=" << position() << ", csize=" << compressedSize();
01374     // Limit the reading to the appropriate part of the underlying device (e.g. file)
01375     KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
01376     if ( encoding() == 0 || compressedSize() == 0 ) // no compression (or even no data)
01377         return limitedDev;
01378 
01379     if ( encoding() == 8 )
01380     {
01381         // On top of that, create a device that uncompresses the zlib data
01382         QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
01383         if ( !filterDev )
01384             return 0L; // ouch
01385         static_cast<KFilterDev *>(filterDev)->setSkipHeaders(); // Just zlib, not gzip
01386         bool b = filterDev->open( QIODevice::ReadOnly );
01387         Q_ASSERT( b );
01388         return filterDev;
01389     }
01390 
01391     kError() << "This zip file contains files compressed with method"
01392               << encoding() << ", this method is currently not supported by KZip,"
01393               << "please use a command-line tool to handle this file.";
01394     return 0L;
01395 }

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
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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