kio Library: kzip.cpp Source File (kio Library)

| # kio Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# kzip.cpp

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

This file is part of the documentation for kio Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:24:06 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003