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