kio Library API Documentation

kzip.cpp

00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2000 David Faure <faure@kde.org>
00003    Copyright (C) 2002 Holger Schroeder <holger-kde@holgis.net>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License version 2 as published by the Free Software Foundation.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017    Boston, MA 02111-1307, USA.
00018 */
00019 
00020 /*
00021     This class implements a kioslave to access ZIP files from KDE.
00022     you can use it in IO_ReadOnly or in IO_WriteOnly mode, and it
00023     behaves just as expected (i hope ;-) ).
00024     It can also be used in IO_ReadWrite mode, in this case one can
00025     append files to an existing zip archive. when you append new files, which
00026     are not yet in the zip, it works as expected, they are appended at the end.
00027     when you append a file, which is already in the file, the reference to the
00028     old file is dropped and the new one is added to the zip. but the
00029     old data from the file itself is not deleted, it is still in the
00030     zipfile. so when you want to have a small and garbagefree zipfile,
00031     just read the contents of the appended zipfile and write it to a new one
00032     in IO_WriteOnly mode. especially take care of this, when you don't want
00033     to leak information of how intermediate versions of files in the zip
00034     were looking.
00035     For more information on the zip fileformat go to
00036     http://www.pkware.com/support/appnote.html .
00037 
00038 */
00039 
00040 #include "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                 return false;
00385             }
00386             Q_ASSERT( d->m_saveFile->file() );
00387             setDevice( d->m_saveFile->file() );
00388         }
00389         return true;
00390     case IO_ReadOnly:
00391     case IO_ReadWrite:
00392     {
00393         // ReadWrite mode still uses QFile for now; we'd need to copy to the tempfile, in fact.
00394         if ( !m_filename.isEmpty() ) {
00395             setDevice( new QFile( m_filename ) );
00396             if ( !device()->open( mode ) )
00397                 return false;
00398         }
00399         break; // continued below
00400     }
00401     default:
00402         kdWarning(7040) << "Unsupported mode " << mode << endl;
00403         return false;
00404     }
00405 
00406     char buffer[47];
00407 
00408     // Check that it's a valid ZIP file
00409     // KArchive::open() opened the underlying device already.
00410     QIODevice* dev = device();
00411 
00412     if (!dev) {
00413         return false;
00414     }
00415 
00416     uint offset = 0; // holds offset, where we read
00417     int n;
00418 
00419     // contains information gathered from the local file headers
00420     QAsciiDict<ParseFileInfo> pfi_map(1009, true /*case sensitive */, true /*copy keys*/);
00421     pfi_map.setAutoDelete(true);
00422 
00423     for (;;) // repeat until 'end of entries' signature is reached
00424     {
00425 kdDebug(7040) << "loop starts" << endl;
00426 kdDebug(7040) << "dev->at() now : " << dev->at() << endl;
00427         n = dev->readBlock( buffer, 4 );
00428 
00429         if (n < 4)
00430         {
00431             kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#1)" << endl;
00432 
00433             return false;
00434         }
00435 
00436         if ( !memcmp( buffer, "PK\5\6", 4 ) ) // 'end of entries'
00437         {
00438         kdDebug(7040) << "PK56 found end of archive" << endl;
00439         break;
00440     }
00441         
00442     if ( !memcmp( buffer, "PK\3\4", 4 ) ) // local file header
00443         {
00444         kdDebug(7040) << "PK34 found local file header" << endl;
00445             // can this fail ???
00446         dev->at( dev->at() + 2 ); // skip 'version needed to extract'
00447 
00448         // read static header stuff
00449             n = dev->readBlock( buffer, 24 );
00450         if (n < 24) {
00451                 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#4)" << endl;
00452                 return false;
00453         }
00454 
00455         int gpf = (uchar)buffer[0]; // "general purpose flag" not "general protection fault" ;-)
00456         int compression_mode = (uchar)buffer[2] | (uchar)buffer[3] << 8;
00457         time_t mtime = transformFromMsDos( buffer+4 );
00458         
00459         Q_LONG compr_size = (uchar)buffer[12] | (uchar)buffer[13] << 8
00460                     | (uchar)buffer[14] << 16 | (uchar)buffer[15] << 24;
00461         Q_LONG uncomp_size = (uchar)buffer[16] | (uchar)buffer[17] << 8
00462                     | (uchar)buffer[18] << 16 | (uchar)buffer[19] << 24;
00463         int namelen = (uchar)buffer[20] | (uchar)buffer[21] << 8;
00464         int extralen = (uchar)buffer[22] | (uchar)buffer[23] << 8;
00465         
00466         kdDebug(7040) << "general purpose bit flag: " << gpf << endl;
00467         kdDebug(7040) << "compressed size: " << compr_size << endl;
00468         kdDebug(7040) << "uncompressed size: " << uncomp_size << endl;
00469         kdDebug(7040) << "namelen: " << namelen << endl;
00470         kdDebug(7040) << "extralen: " << extralen << endl;
00471         kdDebug(7040) << "archive size: " << dev->size() << endl;
00472 
00473         // read filename
00474         QCString filename(namelen + 1);
00475         n = dev->readBlock(filename.data(), namelen);
00476             if ( n < namelen ) {
00477                 kdWarning(7040) << "Invalid ZIP file. Name not completely read (#2)" << endl;
00478         return false;
00479         }
00480 
00481         ParseFileInfo *pfi = new ParseFileInfo();
00482         pfi->mtime = mtime;
00483         pfi_map.insert(filename.data(), pfi);
00484 
00485             // read and parse the beginning of the extra field,
00486             // skip rest of extra field in case it is too long
00487             unsigned int extraFieldEnd = dev->at() + extralen;
00488         pfi->extralen = extralen;
00489         int handledextralen = QMIN(extralen, (int)sizeof buffer);
00490         
00491         kdDebug(7040) << "handledextralen: " << handledextralen << endl;
00492         
00493         n = dev->readBlock(buffer, handledextralen);
00494         // no error msg necessary as we deliberately truncate the extra field
00495         if (!parseExtraField(buffer, handledextralen, true, *pfi))
00496         {
00497             kdWarning(7040) << "Invalid ZIP File. Broken ExtraField." << endl;
00498             return false;
00499         }
00500 
00501             // jump to end of extra field
00502             dev->at( extraFieldEnd );
00503 
00504         // we have to take care of the 'general purpose bit flag'.
00505             // if bit 3 is set, the header doesn't contain the length of
00506             // the file and we look for the signature 'PK\7\8'.
00507             if ( gpf & 8 )
00508             {
00509             // here we have to read through the compressed data to find
00510         // the next PKxx
00511             kdDebug(7040) << "trying to seek for next PK78" << endl;
00512                 bool foundSignature = false;
00513 
00514                 while (!foundSignature)
00515                 {
00516                     n = dev->readBlock( buffer, 1 );
00517                     if (n < 1)
00518                     {
00519                         kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)" << endl;
00520                         return false;
00521                     }
00522 
00523                     if ( buffer[0] != 'P' )
00524                         continue;
00525 
00526                     n = dev->readBlock( buffer, 3 );
00527                     if (n < 3)
00528                     {
00529                         kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)" << endl;
00530                         return false;
00531                     }
00532 
00533                     // we have to detect three magic tokens here:
00534             // PK34 for the next local header in case there is no data descriptor
00535             // PK12 for the central header in case there is no data descriptor
00536             // PK78 for the data descriptor in case it is following the compressed data
00537             
00538             if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
00539                     {
00540                         foundSignature = true;
00541                         dev->at( dev->at() + 12 ); // skip the 'data_descriptor'
00542                     }
00543             
00544             if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 )
00545                  || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) )
00546                     {
00547                         foundSignature = true;
00548                         dev->at( dev->at() - 4 ); // go back 4 bytes, so that the magic bytes can be found...
00549                     }
00550             
00551                 }
00552             }
00553             else
00554             {
00555             // here we skip the compressed data and jump to the next header
00556             kdDebug(7040) << "general purpose bit flag indicates, that local file header contains valid size" << endl;
00557         // check if this could be a symbolic link
00558         if (compression_mode == NoCompression
00559                 && uncomp_size <= max_path_len
00560             && uncomp_size > 0) {
00561             // read content and store it
00562             pfi->guessed_symlink.resize(uncomp_size + 1);
00563                     kdDebug(7040) << "guessed symlink size: " << uncomp_size << endl;
00564             n = dev->readBlock(pfi->guessed_symlink.data(), uncomp_size);
00565             if (n < uncomp_size) {
00566             kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#5)" << endl;
00567             return false;
00568             }
00569         } else {
00570 
00571                 if ( compr_size > (Q_LONG)dev->size() )
00572             {
00573                 // here we cannot trust the compressed size, so scan through the compressed
00574             // data to find the next header
00575             bool foundSignature = false;
00576     
00577             while (!foundSignature)
00578             {
00579                 n = dev->readBlock( buffer, 1 );
00580                 if (n < 1)
00581                 {
00582                     kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)" << endl;
00583                     return false;
00584                 }
00585         
00586                 if ( buffer[0] != 'P' )
00587                     continue;
00588         
00589                 n = dev->readBlock( buffer, 3 );
00590                 if (n < 3)
00591                 {
00592                     kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)" << endl;
00593                     return false;
00594                 }
00595         
00596                 // we have to detect three magic tokens here:
00597                 // PK34 for the next local header in case there is no data descriptor
00598                 // PK12 for the central header in case there is no data descriptor
00599                 // PK78 for the data descriptor in case it is following the compressed data
00600                 
00601                 if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
00602                 {
00603                     foundSignature = true;
00604                     dev->at( dev->at() + 12 ); // skip the 'data_descriptor'
00605                 }
00606                 
00607                 if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 )
00608                     || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) )
00609                 {
00610                     foundSignature = true;
00611                     dev->at( dev->at() - 4 ); 
00612                     // go back 4 bytes, so that the magic bytes can be found
00613                     // in the next cycle...
00614                 }
00615             }
00616             }
00617             else
00618             {
00619 //          kdDebug(7040) << "before interesting dev->at(): " << dev->at() << endl;
00620             bool success;
00621             success = dev->at( dev->at() + compr_size ); // can this fail ???
00622 /*          kdDebug(7040) << "after interesting dev->at(): " << dev->at() << endl;
00623             if ( success )
00624                 kdDebug(7040) << "dev->at was successful... " << endl;
00625             else
00626                 kdDebug(7040) << "dev->at failed... " << endl;*/
00627             }
00628             
00629         }
00630 
00631 // not needed any more
00632 /*                // here we calculate the length of the file in the zip
00633                 // with headers and jump to the next header.
00634                 uint skip = compr_size + namelen + extralen;
00635                 offset += 30 + skip;*/
00636             }
00637         }
00638         else if ( !memcmp( buffer, "PK\1\2", 4 ) ) // central block
00639         {
00640         kdDebug(7040) << "PK12 found central block" << endl;
00641 
00642             // so we reached the central header at the end of the zip file
00643             // here we get all interesting data out of the central header
00644             // of a file
00645             offset = dev->at() - 4;
00646 
00647             //set offset for appending new files
00648             if ( d->m_offset == 0L ) d->m_offset = offset;
00649 
00650             n = dev->readBlock( buffer + 4, 42 );
00651             if (n < 42) {
00652                 kdWarning(7040) << "Invalid ZIP file, central entry too short" << endl; // not long enough for valid entry
00653                 return false;
00654             }
00655             // length of the filename (well, pathname indeed)
00656             int namelen = (uchar)buffer[29] << 8 | (uchar)buffer[28];
00657             QCString bufferName( namelen + 1 );
00658             n = dev->readBlock( bufferName.data(), namelen );
00659             if ( n < namelen )
00660                 kdWarning(7040) << "Invalid ZIP file. Name not completely read" << endl;
00661 
00662             ParseFileInfo *pfi = pfi_map[bufferName];
00663             if (!pfi) {   // can that happen?
00664                 pfi_map.insert(bufferName.data(), pfi = new ParseFileInfo());
00665             }
00666             QString name( QFile::decodeName(bufferName) );
00667 
00668             //kdDebug(7040) << "name: " << name << endl;
00669             // only in central header ! see below.
00670             // length of extra attributes
00671             int extralen = (uchar)buffer[31] << 8 | (uchar)buffer[30];
00672             // length of comment for this file
00673             int commlen =  (uchar)buffer[33] << 8 | (uchar)buffer[32];
00674             // compression method of this file
00675             int cmethod =  (uchar)buffer[11] << 8 | (uchar)buffer[10];
00676 
00677             //kdDebug(7040) << "cmethod: " << cmethod << endl;
00678             //kdDebug(7040) << "extralen: " << extralen << endl;
00679 
00680             // uncompressed file size
00681             uint ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 |
00682                 (uchar)buffer[25] << 8 | (uchar)buffer[24];
00683             // compressed file size
00684             uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 |
00685                 (uchar)buffer[21] << 8 | (uchar)buffer[20];
00686 
00687             // offset of local header
00688             uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 |
00689                 (uchar)buffer[43] << 8 | (uchar)buffer[42];
00690 
00691             // some clever people use different extra field lengths
00692             // in the central header and in the local header... funny.
00693             // so we need to get the localextralen to calculate the offset
00694             // from localheaderstart to dataoffset
00695             int localextralen = pfi->extralen; // FIXME: this will not work if
00696                             // no local header exists
00697 
00698             //kdDebug(7040) << "localextralen: " << localextralen << endl;
00699 
00700             // offset, where the real data for uncompression starts
00701             uint dataoffset = localheaderoffset + 30 + localextralen + namelen; //comment only in central header
00702 
00703             //kdDebug(7040) << "esize: " << esize << endl;
00704             //kdDebug(7040) << "eoffset: " << eoffset << endl;
00705             //kdDebug(7040) << "csize: " << csize << endl;
00706 
00707         int os_madeby = (uchar)buffer[5];
00708             bool isdir = false;
00709             int access = 0100644;
00710 
00711         if (os_madeby == 3) {   // good ole unix
00712             access = (uchar)buffer[40] | (uchar)buffer[41] << 8;
00713         }
00714 
00715             QString entryName;
00716 
00717             if ( name.endsWith( "/" ) ) // Entries with a trailing slash are directories
00718             {
00719                 isdir = true;
00720                 name = name.left( name.length() - 1 );
00721                 if (os_madeby != 3) access = S_IFDIR | 0755;
00722         else Q_ASSERT(access & S_IFDIR);
00723             }
00724 
00725             int pos = name.findRev( '/' );
00726             if ( pos == -1 )
00727                 entryName = name;
00728             else
00729                 entryName = name.mid( pos + 1 );
00730             Q_ASSERT( !entryName.isEmpty() );
00731 
00732             KArchiveEntry* entry;
00733             if ( isdir )
00734             {
00735                 QString path = QDir::cleanDirPath( name );
00736                 KArchiveEntry* ent = rootDir()->entry( path );
00737                 if ( ent && ent->isDirectory() )
00738                 {
00739                     //kdDebug(7040) << "Directory already exists, NOT going to add it again" << endl;
00740                     entry = 0L;
00741                 }
00742                 else
00743                 {
00744                     entry = new KArchiveDirectory( this, entryName, access, (int)pfi->mtime, rootDir()->user(), rootDir()->group(), QString::null );
00745                     //kdDebug(7040) << "KArchiveDirectory created, entryName= " << entryName << ", name=" << name << endl;
00746                 }
00747         }
00748             else
00749             {
00750             QString symlink;
00751         if (S_ISLNK(access)) {
00752             symlink = QFile::decodeName(pfi->guessed_symlink);
00753         }
00754                 entry = new KZipFileEntry( this, entryName, access, pfi->mtime,
00755                     rootDir()->user(), rootDir()->group(),
00756                     symlink, name, dataoffset,
00757                     ucsize, cmethod, csize );
00758                 static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset );
00759                 //kdDebug(7040) << "KZipFileEntry created, entryName= " << entryName << ", name=" << name << endl;
00760                 d->m_fileList.append( static_cast<KZipFileEntry *>( entry ) );
00761             }
00762 
00763             if ( entry )
00764             {
00765                 if ( pos == -1 )
00766                 {
00767                     rootDir()->addEntry(entry);
00768                 }
00769                 else
00770                 {
00771                     // In some tar files we can find dir/./file => call cleanDirPath
00772                     QString path = QDir::cleanDirPath( name.left( pos ) );
00773                     // Ensure container directory exists, create otherwise
00774                     KArchiveDirectory * tdir = findOrCreate( path );
00775                     tdir->addEntry(entry);
00776                 }
00777             }
00778 
00779             //calculate offset to next entry
00780             offset += 46 + commlen + extralen + namelen;
00781             bool b = dev->at(offset);
00782             Q_ASSERT( b );
00783             if ( !b )
00784               return false;
00785         }
00786         else
00787         {
00788             kdWarning(7040) << "Invalid ZIP file. Unrecognized header at offset " << offset << endl;
00789 
00790             return false;
00791         }
00792     }
00793     //kdDebug(7040) << "*** done *** " << endl;
00794     return true;
00795 }
00796 
00797 bool KZip::closeArchive()
00798 {
00799     if ( ! ( mode() & IO_WriteOnly ) )
00800     {
00801         //kdDebug(7040) << "closearchive readonly reached." << endl;
00802         return true;
00803     }
00804 
00805     kdDebug() << k_funcinfo << "device=" << device() << endl;
00806     //ReadWrite or WriteOnly
00807     //write all central dir file entries
00808 
00809     if ( !device() ) // saving aborted
00810         return false;
00811 
00812     // to be written at the end of the file...
00813     char buffer[ 22 ]; // first used for 12, then for 22 at the end
00814     uLong crc = crc32(0L, Z_NULL, 0);
00815 
00816     Q_LONG centraldiroffset = device()->at();
00817     //kdDebug(7040) << "closearchive: centraldiroffset: " << centraldiroffset << endl;
00818     Q_LONG atbackup = centraldiroffset;
00819     QPtrListIterator<KZipFileEntry> it( d->m_fileList );
00820 
00821     for ( ; it.current() ; ++it )
00822     {   //set crc and compressed size in each local file header
00823         if ( !device()->at( it.current()->headerStart() + 14 ) )
00824             return false;
00825     //kdDebug(7040) << "closearchive setcrcandcsize: filename: "
00826     //    << it.current()->path()
00827     //    << " encoding: "<< it.current()->encoding() << endl;
00828 
00829         uLong mycrc = it.current()->crc32();
00830         buffer[0] = char(mycrc); // crc checksum, at headerStart+14
00831         buffer[1] = char(mycrc >> 8);
00832         buffer[2] = char(mycrc >> 16);
00833         buffer[3] = char(mycrc >> 24);
00834 
00835         int mysize1 = it.current()->compressedSize();
00836         buffer[4] = char(mysize1); // compressed file size, at headerStart+18
00837         buffer[5] = char(mysize1 >> 8);
00838         buffer[6] = char(mysize1 >> 16);
00839         buffer[7] = char(mysize1 >> 24);
00840 
00841         int myusize = it.current()->size();
00842         buffer[8] = char(myusize); // uncompressed file size, at headerStart+22
00843         buffer[9] = char(myusize >> 8);
00844         buffer[10] = char(myusize >> 16);
00845         buffer[11] = char(myusize >> 24);
00846 
00847         if ( device()->writeBlock( buffer, 12 ) != 12 )
00848             return false;
00849     }
00850     device()->at( atbackup );
00851 
00852     for ( it.toFirst(); it.current() ; ++it )
00853     {
00854         //kdDebug(7040) << "closearchive: filename: " << it.current()->path()
00855         //              << " encoding: "<< it.current()->encoding() << endl;
00856 
00857         QCString path = QFile::encodeName(it.current()->path());
00858 
00859     const int extra_field_len = 9;
00860         int bufferSize = extra_field_len + path.length() + 46;
00861         char* buffer = new char[ bufferSize ];
00862 
00863         memset(buffer, 0, 46); // zero is a nice default for most header fields
00864 
00865         const char head[] =
00866         {
00867             'P', 'K', 1, 2, // central file header signature
00868             0x14, 3,        // version made by (3 == UNIX)
00869             0x14, 0         // version needed to extract
00870         };
00871 
00872     // I do not know why memcpy is not working here
00873         //memcpy(buffer, head, sizeof(head));
00874         qmemmove(buffer, head, sizeof(head));
00875 
00876         buffer[ 10 ] = char(it.current()->encoding()); // compression method
00877         buffer[ 11 ] = char(it.current()->encoding() >> 8);
00878 
00879         transformToMsDos( it.current()->datetime(), &buffer[ 12 ] );
00880 
00881         uLong mycrc = it.current()->crc32();
00882         buffer[ 16 ] = char(mycrc); // crc checksum
00883         buffer[ 17 ] = char(mycrc >> 8);
00884         buffer[ 18 ] = char(mycrc >> 16);
00885         buffer[ 19 ] = char(mycrc >> 24);
00886 
00887         int mysize1 = it.current()->compressedSize();
00888         buffer[ 20 ] = char(mysize1); // compressed file size
00889         buffer[ 21 ] = char(mysize1 >> 8);
00890         buffer[ 22 ] = char(mysize1 >> 16);
00891         buffer[ 23 ] = char(mysize1 >> 24);
00892 
00893         int mysize = it.current()->size();
00894         buffer[ 24 ] = char(mysize); // uncompressed file size
00895         buffer[ 25 ] = char(mysize >> 8);
00896         buffer[ 26 ] = char(mysize >> 16);
00897         buffer[ 27 ] = char(mysize >> 24);
00898 
00899         buffer[ 28 ] = char(it.current()->path().length()); // filename length
00900         buffer[ 29 ] = char(it.current()->path().length() >> 8);
00901 
00902     buffer[ 30 ] = char(extra_field_len);
00903     buffer[ 31 ] = char(extra_field_len >> 8);
00904 
00905     buffer[ 40 ] = char(it.current()->permissions());
00906     buffer[ 41 ] = char(it.current()->permissions() >> 8);
00907 
00908         int myhst = it.current()->headerStart();
00909         buffer[ 42 ] = char(myhst); //relative offset of local header
00910         buffer[ 43 ] = char(myhst >> 8);
00911         buffer[ 44 ] = char(myhst >> 16);
00912         buffer[ 45 ] = char(myhst >> 24);
00913 
00914         // file name
00915         strncpy( buffer + 46, path, path.length() );
00916     //kdDebug(7040) << "closearchive length to write: " << bufferSize << endl;
00917 
00918     // extra field
00919     char *extfield = buffer + 46 + path.length();
00920     extfield[0] = 'U';
00921     extfield[1] = 'T';
00922     extfield[2] = 5;
00923     extfield[3] = 0;
00924     extfield[4] = 1 | 2 | 4;    // specify flags from local field
00925                     // (unless I misread the spec)
00926     // provide only modification time
00927     unsigned long time = (unsigned long)it.current()->date();
00928     extfield[5] = char(time);
00929     extfield[6] = char(time >> 8);
00930     extfield[7] = char(time >> 16);
00931     extfield[8] = char(time >> 24);
00932 
00933         crc = crc32(crc, (Bytef *)buffer, bufferSize );
00934         bool ok = ( device()->writeBlock( buffer, bufferSize ) == bufferSize );
00935         delete[] buffer;
00936         if ( !ok )
00937             return false;
00938     }
00939     Q_LONG centraldirendoffset = device()->at();
00940     //kdDebug(7040) << "closearchive: centraldirendoffset: " << centraldirendoffset << endl;
00941     //kdDebug(7040) << "closearchive: device()->at(): " << device()->at() << endl;
00942 
00943     //write end of central dir record.
00944     buffer[ 0 ] = 'P'; //end of central dir signature
00945     buffer[ 1 ] = 'K';
00946     buffer[ 2 ] = 5;
00947     buffer[ 3 ] = 6;
00948 
00949     buffer[ 4 ] = 0; // number of this disk
00950     buffer[ 5 ] = 0;
00951 
00952     buffer[ 6 ] = 0; // number of disk with start of central dir
00953     buffer[ 7 ] = 0;
00954 
00955     int count = d->m_fileList.count();
00956     //kdDebug(7040) << "number of files (count): " << count << endl;
00957 
00958 
00959     buffer[ 8 ] = char(count); // total number of entries in central dir of
00960     buffer[ 9 ] = char(count >> 8); // this disk
00961 
00962     buffer[ 10 ] = buffer[ 8 ]; // total number of entries in the central dir
00963     buffer[ 11 ] = buffer[ 9 ];
00964 
00965     int cdsize = centraldirendoffset - centraldiroffset;
00966     buffer[ 12 ] = char(cdsize); // size of the central dir
00967     buffer[ 13 ] = char(cdsize >> 8);
00968     buffer[ 14 ] = char(cdsize >> 16);
00969     buffer[ 15 ] = char(cdsize >> 24);
00970 
00971     //kdDebug(7040) << "end : centraldiroffset: " << centraldiroffset << endl;
00972     //kdDebug(7040) << "end : centraldirsize: " << cdsize << endl;
00973 
00974     buffer[ 16 ] = char(centraldiroffset); // central dir offset
00975     buffer[ 17 ] = char(centraldiroffset >> 8);
00976     buffer[ 18 ] = char(centraldiroffset >> 16);
00977     buffer[ 19 ] = char(centraldiroffset >> 24);
00978 
00979     buffer[ 20 ] = 0; //zipfile comment length
00980     buffer[ 21 ] = 0;
00981 
00982     if ( device()->writeBlock( buffer, 22 ) != 22 )
00983         return false;
00984 
00985     if ( d->m_saveFile ) {
00986         d->m_saveFile->close();
00987         setDevice( 0 );
00988         delete d->m_saveFile;
00989         d->m_saveFile = 0;
00990     }
00991 
00992     //kdDebug(7040) << __FILE__" reached." << endl;
00993     return true;
00994 }
00995 
00996 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0
00997 bool KZip::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
00998 {
00999     mode_t mode = 0100644;
01000     time_t the_time = time(0);
01001     return KArchive::writeFile( name, user, group, size, mode, the_time,
01002                 the_time, the_time, data );
01003 }
01004 
01005 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0
01006 bool KZip::writeFile( const QString& name, const QString& user,
01007                         const QString& group, uint size, mode_t perm,
01008                         time_t atime, time_t mtime, time_t ctime,
01009                         const char* data ) {
01010   return KArchive::writeFile(name, user, group, size, perm, atime, mtime,
01011             ctime, data);
01012 }
01013 
01014 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0
01015 bool KZip::prepareWriting( const QString& name, const QString& user, const QString& group, uint size )
01016 {
01017     mode_t dflt_perm = 0100644;
01018     time_t the_time = time(0);
01019     return prepareWriting(name,user,group,size,dflt_perm,
01020             the_time,the_time,the_time);
01021 }
01022 
01023 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0
01024 bool KZip::prepareWriting(const QString& name, const QString& user,
01025                 const QString& group, uint size, mode_t perm,
01026                 time_t atime, time_t mtime, time_t ctime) {
01027   return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime);
01028 }
01029 
01030 bool KZip::prepareWriting_impl(const QString &name, const QString &user,
01031                 const QString &group, uint /*size*/, mode_t perm,
01032                 time_t atime, time_t mtime, time_t ctime) {
01033     //kdDebug(7040) << "prepareWriting reached." << endl;
01034     if ( !isOpened() )
01035     {
01036         qWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
01037         return false;
01038     }
01039 
01040     if ( ! ( mode() & IO_WriteOnly ) ) // accept WriteOnly and ReadWrite
01041     {
01042         qWarning( "KZip::writeFile: You must open the zip file for writing\n");
01043         return false;
01044     }
01045 
01046     if ( !device() ) { // aborted
01047         //kdWarning(7040) << "prepareWriting_impl: no device" << endl;
01048         return false;
01049     }
01050 
01051     // set right offset in zip.
01052     if ( !device()->at( d->m_offset ) ) {
01053         kdWarning(7040) << "prepareWriting_impl: cannot seek in ZIP file. Disk full?" << endl;
01054         abort();
01055         return false;
01056     }
01057 
01058     // delete entries in the filelist with the same filename as the one we want
01059     // to save, so that we donīt have duplicate file entries when viewing the zip
01060     // with konqi...
01061     // CAUTION: the old file itself is still in the zip and won't be removed !!!
01062     QPtrListIterator<KZipFileEntry> it( d->m_fileList );
01063 
01064     //kdDebug(7040) << "filename to write: " << name <<endl;
01065     for ( ; it.current() ; ++it )
01066     {
01067         //kdDebug(7040) << "prepfilename: " << it.current()->path() <<endl;
01068         if (name == it.current()->path() )
01069         {
01070             //kdDebug(7040) << "removing following entry: " << it.current()->path() <<endl;
01071             d->m_fileList.remove();
01072         }
01073 
01074     }
01075     // Find or create parent dir
01076     KArchiveDirectory* parentDir = rootDir();
01077     QString fileName( name );
01078     int i = name.findRev( '/' );
01079     if ( i != -1 )
01080     {
01081         QString dir = name.left( i );
01082         fileName = name.mid( i + 1 );
01083         //kdDebug(7040) << "KZip::prepareWriting ensuring " << dir << " exists. fileName=" << fileName << endl;
01084         parentDir = findOrCreate( dir );
01085     }
01086 
01087     // construct a KZipFileEntry and add it to list
01088     KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, QString::null,
01089                                            name, device()->at() + 30 + name.length(), // start
01090                                            0 /*size unknown yet*/, d->m_compression, 0 /*csize unknown yet*/ );
01091     e->setHeaderStart( device()->at() );
01092     //kdDebug(7040) << "wrote file start: " << e->position() << " name: " << name << endl;
01093     parentDir->addEntry( e );
01094 
01095     d->m_currentFile = e;
01096     d->m_fileList.append( e );
01097 
01098     int extra_field_len = 0;
01099     if ( d->m_extraField == ModificationTime )
01100         extra_field_len = 17;   // value also used in doneWriting()
01101 
01102     // write out zip header
01103     QCString encodedName = QFile::encodeName(name);
01104     int bufferSize = extra_field_len + encodedName.length() + 30;
01105     //kdDebug(7040) << "KZip::prepareWriting bufferSize=" << bufferSize << endl;
01106     char* buffer = new char[ bufferSize ];
01107 
01108     buffer[ 0 ] = 'P'; //local file header signature
01109     buffer[ 1 ] = 'K';
01110     buffer[ 2 ] = 3;
01111     buffer[ 3 ] = 4;
01112 
01113     buffer[ 4 ] = 0x14; // version needed to extract
01114     buffer[ 5 ] = 0;
01115 
01116     buffer[ 6 ] = 0; // general purpose bit flag
01117     buffer[ 7 ] = 0;
01118 
01119     buffer[ 8 ] = char(e->encoding()); // compression method
01120     buffer[ 9 ] = char(e->encoding() >> 8);
01121 
01122     transformToMsDos( e->datetime(), &buffer[ 10 ] );
01123 
01124     buffer[ 14 ] = 'C'; //dummy crc
01125     buffer[ 15 ] = 'R';
01126     buffer[ 16 ] = 'C';
01127     buffer[ 17 ] = 'q';
01128 
01129     buffer[ 18 ] = 'C'; //compressed file size
01130     buffer[ 19 ] = 'S';
01131     buffer[ 20 ] = 'I';
01132     buffer[ 21 ] = 'Z';
01133 
01134     buffer[ 22 ] = 'U'; //uncompressed file size
01135     buffer[ 23 ] = 'S';
01136     buffer[ 24 ] = 'I';
01137     buffer[ 25 ] = 'Z';
01138 
01139     buffer[ 26 ] = (uchar)(encodedName.length()); //filename length
01140     buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
01141 
01142     buffer[ 28 ] = (uchar)(extra_field_len); // extra field length
01143     buffer[ 29 ] = (uchar)(extra_field_len >> 8);
01144 
01145     // file name
01146     strncpy( buffer + 30, encodedName, encodedName.length() );
01147 
01148     // extra field
01149     if ( d->m_extraField == ModificationTime )
01150     {
01151         char *extfield = buffer + 30 + encodedName.length();
01152         // "Extended timestamp" header (0x5455)
01153         extfield[0] = 'U';
01154         extfield[1] = 'T';
01155         extfield[2] = 13; // data size
01156         extfield[3] = 0;
01157         extfield[4] = 1 | 2 | 4;    // contains mtime, atime, ctime
01158 
01159         extfield[5] = char(mtime);
01160         extfield[6] = char(mtime >> 8);
01161         extfield[7] = char(mtime >> 16);
01162         extfield[8] = char(mtime >> 24);
01163 
01164         extfield[9] = char(atime);
01165         extfield[10] = char(atime >> 8);
01166         extfield[11] = char(atime >> 16);
01167         extfield[12] = char(atime >> 24);
01168 
01169         extfield[13] = char(ctime);
01170         extfield[14] = char(ctime >> 8);
01171         extfield[15] = char(ctime >> 16);
01172         extfield[16] = char(ctime >> 24);
01173     }
01174 
01175     // Write header
01176     bool b = (device()->writeBlock( buffer, bufferSize ) == bufferSize );
01177     d->m_crc = 0L;
01178     delete[] buffer;
01179 
01180     Q_ASSERT( b );
01181     if (!b) {
01182         abort();
01183         return false;
01184     }
01185 
01186     // Prepare device for writing the data
01187     // Either device() if no compression, or a KFilterDev to compress
01188     if ( d->m_compression == 0 ) {
01189         d->m_currentDev = device();
01190         return true;
01191     }
01192 
01193     d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
01194     Q_ASSERT( d->m_currentDev );
01195     if ( !d->m_currentDev ) {
01196         abort();
01197         return false; // ouch
01198     }
01199     static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders(); // Just zlib, not gzip
01200 
01201     b = d->m_currentDev->open( IO_WriteOnly );
01202     Q_ASSERT( b );
01203     return b;
01204 }
01205 
01206 bool KZip::doneWriting( uint size )
01207 {
01208     if ( d->m_currentFile->encoding() == 8 ) {
01209         // Finish
01210         (void)d->m_currentDev->writeBlock( 0, 0 );
01211         delete d->m_currentDev;
01212     }
01213     // If 0, d->m_currentDev was device() - don't delete ;)
01214     d->m_currentDev = 0L;
01215 
01216     Q_ASSERT( d->m_currentFile );
01217     //kdDebug(7040) << "donewriting reached." << endl;
01218     //kdDebug(7040) << "filename: " << d->m_currentFile->path() << endl;
01219     //kdDebug(7040) << "getpos (at): " << device()->at() << endl;
01220     d->m_currentFile->setSize(size);
01221     int extra_field_len = 0;
01222     if ( d->m_extraField == ModificationTime )
01223         extra_field_len = 17;   // value also used in doneWriting()
01224 
01225     int csize = device()->at() -
01226         d->m_currentFile->headerStart() - 30 -
01227         d->m_currentFile->path().length() - extra_field_len;
01228     d->m_currentFile->setCompressedSize(csize);
01229     //kdDebug(7040) << "usize: " << d->m_currentFile->size() << endl;
01230     //kdDebug(7040) << "csize: " << d->m_currentFile->compressedSize() << endl;
01231     //kdDebug(7040) << "headerstart: " << d->m_currentFile->headerStart() << endl;
01232 
01233     //kdDebug(7040) << "crc: " << d->m_crc << endl;
01234     d->m_currentFile->setCRC32( d->m_crc );
01235 
01236     d->m_currentFile = 0L;
01237 
01238     // update saved offset for appending new files
01239     d->m_offset = device()->at();
01240     return true;
01241 }
01242 
01243 bool KZip::writeSymLink(const QString &name, const QString &target,
01244                 const QString &user, const QString &group,
01245                 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01246   return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime);
01247 }
01248 
01249 bool KZip::writeSymLink_impl(const QString &name, const QString &target,
01250                 const QString &user, const QString &group,
01251                 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01252 
01253   // reassure that symlink flag is set, otherwise strange things happen on
01254   // extraction
01255   perm |= S_IFLNK;
01256   Compression c = compression();
01257   setCompression(NoCompression);    // link targets are never compressed
01258 
01259   if (!prepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) {
01260     kdWarning() << "KZip::writeFile prepareWriting failed" << endl;
01261     setCompression(c);
01262     return false;
01263   }
01264 
01265   QCString symlink_target = QFile::encodeName(target);
01266   if (!writeData(symlink_target, symlink_target.length())) {
01267     kdWarning() << "KZip::writeFile writeData failed" << endl;
01268     setCompression(c);
01269     return false;
01270   }
01271 
01272   if (!doneWriting(symlink_target.length())) {
01273     kdWarning() << "KZip::writeFile doneWriting failed" << endl;
01274     setCompression(c);
01275     return false;
01276   }
01277 
01278   setCompression(c);
01279   return true;
01280 }
01281 
01282 void KZip::virtual_hook( int id, void* data )
01283 {
01284     switch (id) {
01285       case VIRTUAL_WRITE_DATA: {
01286         WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data);
01287         params->retval = writeData_impl( params->data, params->size );
01288         break;
01289       }
01290       case VIRTUAL_WRITE_SYMLINK: {
01291         WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
01292         params->retval = writeSymLink_impl(*params->name,*params->target,
01293                 *params->user,*params->group,params->perm,
01294                 params->atime,params->mtime,params->ctime);
01295         break;
01296       }
01297       case VIRTUAL_PREPARE_WRITING: {
01298         PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
01299         params->retval = prepareWriting_impl(*params->name,*params->user,
01300                 *params->group,params->size,params->perm,
01301                 params->atime,params->mtime,params->ctime);
01302         break;
01303       }
01304       default:
01305         KArchive::virtual_hook( id, data );
01306     }/*end switch*/
01307 }
01308 
01309 // made virtual using virtual_hook
01310 bool KZip::writeData(const char * c, uint i)
01311 {
01312     return KArchive::writeData( c, i );
01313 }
01314 
01315 bool KZip::writeData_impl(const char * c, uint i)
01316 {
01317     Q_ASSERT( d->m_currentFile );
01318     Q_ASSERT( d->m_currentDev );
01319     if (!d->m_currentFile || !d->m_currentDev) {
01320         abort();
01321         return false;
01322     }
01323 
01324     // crc to be calculated over uncompressed stuff...
01325     // and they didn't mention it in their docs...
01326     d->m_crc = crc32(d->m_crc, (const Bytef *) c , i);
01327 
01328     Q_LONG written = d->m_currentDev->writeBlock( c, i );
01329     //kdDebug(7040) << "KZip::writeData wrote " << i << " bytes." << endl;
01330     bool ok = written == (Q_LONG)i;
01331     if ( !ok )
01332         abort();
01333     return ok;
01334 }
01335 
01336 void KZip::setCompression( Compression c )
01337 {
01338     d->m_compression = ( c == NoCompression ) ? 0 : 8;
01339 }
01340 
01341 KZip::Compression KZip::compression() const
01342 {
01343    return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
01344 }
01345 
01346 void KZip::setExtraField( ExtraField ef )
01347 {
01348     d->m_extraField = ef;
01349 }
01350 
01351 KZip::ExtraField KZip::extraField() const
01352 {
01353     return d->m_extraField;
01354 }
01355 
01356 void KZip::abort()
01357 {
01358     if ( d->m_saveFile ) {
01359         d->m_saveFile->abort();
01360         setDevice( 0 );
01361     }
01362 }
01363 
01364 
01366 
01367 QByteArray KZipFileEntry::data() const
01368 {
01369     QIODevice* dev = device();
01370     QByteArray arr;
01371     if ( dev ) {
01372         arr = dev->readAll();
01373         delete dev;
01374     }
01375     return arr;
01376 }
01377 
01378 QIODevice* KZipFileEntry::device() const
01379 {
01380     //kdDebug(7040) << "KZipFileEntry::device creating iodevice limited to pos=" << position() << ", csize=" << compressedSize() << endl;
01381     // Limit the reading to the appropriate part of the underlying device (e.g. file)
01382     KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
01383     if ( encoding() == 0 || compressedSize() == 0 ) // no compression (or even no data)
01384         return limitedDev;
01385 
01386     if ( encoding() == 8 )
01387     {
01388         // On top of that, create a device that uncompresses the zlib data
01389         QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
01390         if ( !filterDev )
01391             return 0L; // ouch
01392         static_cast<KFilterDev *>(filterDev)->setSkipHeaders(); // Just zlib, not gzip
01393         bool b = filterDev->open( IO_ReadOnly );
01394         Q_ASSERT( b );
01395         return filterDev;
01396     }
01397 
01398     kdError() << "This zip file contains files compressed with method "
01399               << encoding() <<", this method is currently not supported by KZip,"
01400               <<" please use a command-line tool to handle this file." << endl;
01401     return 0L;
01402 }
KDE Logo
This file is part of the documentation for kio Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 7 22:07:52 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003