00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 
00036 
00037 
00038 
00039 
00040 #include <qasciidict.h>
00041 #include <qfile.h>
00042 #include <qdir.h>
00043 #include <time.h>
00044 #include <string.h>
00045 #include <qdatetime.h>
00046 #include <kdebug.h>
00047 #include <qptrlist.h>
00048 #include <kmimetype.h>
00049 #include <zlib.h>
00050 
00051 #include "kfilterdev.h"
00052 #include "kzip.h"
00053 #include "klimitediodevice.h"
00054 
00055 const int max_path_len = 4095;  
00056 
00057 static void transformToMsDos(const QDateTime& dt, char* buffer)
00058 {
00059     if ( dt.isValid() )
00060     {
00061         const Q_UINT16 time =
00062              ( dt.time().hour() << 11 )    
00063            | ( dt.time().minute() << 5 )   
00064            | ( dt.time().second() >> 1 );  
00065 
00066         buffer[0] = char(time);
00067         buffer[1] = char(time >> 8);
00068 
00069         const Q_UINT16 date =
00070              ( ( dt.date().year() - 1980 ) << 9 ) 
00071            | ( dt.date().month() << 5 )           
00072            | ( dt.date().day() );                 
00073 
00074         buffer[2] = char(date);
00075         buffer[3] = char(date >> 8);
00076     }
00077     else 
00078     {
00079         buffer[0] = 0;
00080         buffer[1] = 0;
00081         buffer[2] = 33;
00082         buffer[3] = 0;
00083     }
00084 }
00085 
00086 
00087 
00089 struct ParseFileInfo {
00090   
00091 
00092   mode_t perm;          
00093   time_t atime;         
00094   time_t mtime;         
00095   time_t ctime;         
00096   int uid;          
00097   int gid;          
00098   QCString guessed_symlink; 
00099   int extralen;         
00100 
00101   
00102   bool exttimestamp_seen;   
00103                 
00104   bool newinfounix_seen;    
00105                 
00106 
00107   ParseFileInfo() : perm(0100644), uid(-1), gid(-1), extralen(0),
00108     exttimestamp_seen(false), newinfounix_seen(false) {
00109     ctime = mtime = atime = time(0);
00110   }
00111 };
00112 
00121 static bool parseExtTimestamp(const char *buffer, int size, bool islocal,
00122             ParseFileInfo &pfi) {
00123   if (size < 1) {
00124     kdDebug(7040) << "premature end of extended timestamp (#1)" << endl;
00125     return false;
00126   }
00127   int flags = *buffer;      
00128   buffer += 1;
00129 
00130   if (flags & 1) {      
00131     if (size < 5) {
00132       kdDebug(7040) << "premature end of extended timestamp (#2)" << endl;
00133       return false;
00134     }
00135     pfi.mtime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00136                 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00137   }
00138   buffer += 4;
00139   
00140   
00141   if (!islocal) {
00142     pfi.exttimestamp_seen = true;
00143     return true;
00144   }
00145 
00146   if (flags & 2) {      
00147     if (size < 9) {
00148       kdDebug(7040) << "premature end of extended timestamp (#3)" << endl;
00149       return false;
00150     }
00151     pfi.atime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00152                 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00153   }
00154   buffer += 4;
00155 
00156   if (flags & 4) {      
00157     if (size < 13) {
00158       kdDebug(7040) << "premature end of extended timestamp (#4)" << endl;
00159       return false;
00160     }
00161     pfi.ctime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00162                 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00163   }
00164   buffer += 4;
00165 
00166   pfi.exttimestamp_seen = true;
00167   return true;
00168 }
00169 
00178 static bool parseInfoZipUnixOld(const char *buffer, int size, bool islocal,
00179             ParseFileInfo &pfi) {
00180   
00181   if (pfi.exttimestamp_seen || pfi.newinfounix_seen) return true;
00182 
00183   if (size < 8) {
00184     kdDebug(7040) << "premature end of Info-ZIP unix extra field old" << endl;
00185     return false;
00186   }
00187 
00188   pfi.atime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00189                 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00190   buffer += 4;
00191   pfi.mtime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
00192                 | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
00193   buffer += 4;
00194   if (islocal && size >= 12) {
00195     pfi.uid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00196     buffer += 2;
00197     pfi.gid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00198     buffer += 2;
00199   }
00200   return true;
00201 }
00202 
00203 #if 0 // not needed yet
00204 
00212 static bool parseInfoZipUnixNew(const char *buffer, int size, bool islocal,
00213             ParseFileInfo &pfi) {
00214   if (!islocal) {   
00215     pfi.newinfounix = true;
00216     return true;
00217   }
00218 
00219   if (size < 4) {
00220     kdDebug(7040) << "premature end of Info-ZIP unix extra field new" << endl;
00221     return false;
00222   }
00223 
00224   pfi.uid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00225   buffer += 2;
00226   pfi.gid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00227   buffer += 2;
00228 
00229   pfi.newinfounix = true;
00230   return true;
00231 }
00232 #endif
00233 
00242 static bool parseExtraField(const char *buffer, int size, bool islocal,
00243             ParseFileInfo &pfi) {
00244   
00245   
00246   if (!islocal) return true;
00247 
00248   while (size >= 4) {   
00249     int magic = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00250     buffer += 2;
00251     int fieldsize = (uchar)buffer[0] | (uchar)buffer[1] << 8;
00252     buffer += 2;
00253     size -= 4;
00254 
00255     if (fieldsize > size) {
00256       
00257       kdDebug(7040) << "premature end of extra fields reached" << endl;
00258       break;
00259     }
00260 
00261     switch (magic) {
00262       case 0x5455:      
00263         if (!parseExtTimestamp(buffer, fieldsize, islocal, pfi)) return false;
00264     break;
00265       case 0x5855:      
00266         if (!parseInfoZipUnixOld(buffer, fieldsize, islocal, pfi)) return false;
00267     break;
00268 #if 0   // not needed yet
00269       case 0x7855:      
00270         if (!parseInfoZipUnixNew(buffer, fieldsize, islocal, pfi)) return false;
00271     break;
00272 #endif
00273       default:
00274         ;
00275     }
00276 
00277     buffer += fieldsize;
00278     size -= fieldsize;
00279   }
00280   return true;
00281 }
00282 
00286 
00287 class KZip::KZipPrivate
00288 {
00289 public:
00290     KZipPrivate()
00291         : m_crc( 0 ),
00292           m_currentFile( 0L ),
00293           m_currentDev( 0L ),
00294           m_compression( 8 ),
00295           m_extraField( KZip::NoExtraField ),
00296       m_offset( 0L ) { }
00297 
00298     unsigned long           m_crc;         
00299     KZipFileEntry*          m_currentFile; 
00300     QIODevice*              m_currentDev;  
00301     QPtrList<KZipFileEntry> m_fileList;    
00302     int                     m_compression;
00303     KZip::ExtraField        m_extraField;
00304     unsigned int            m_offset; 
00305     
00306     
00307     
00308 };
00309 
00310 KZip::KZip( const QString& filename )
00311     : KArchive( 0L )
00312 {
00313     
00314     m_filename = filename;
00315     d = new KZipPrivate;
00316     setDevice( new QFile( filename ) );
00317 }
00318 
00319 KZip::KZip( QIODevice * dev )
00320     : KArchive( dev )
00321 {
00322     
00323     d = new KZipPrivate;
00324 }
00325 
00326 KZip::~KZip()
00327 {
00328     
00329     
00330     if( isOpened() )
00331         close();
00332     if ( !m_filename.isEmpty() )
00333         delete device(); 
00334     delete d;
00335 }
00336 
00337 bool KZip::openArchive( int mode )
00338 {
00339     
00340     d->m_fileList.clear();
00341 
00342     if ( mode == IO_WriteOnly )
00343         return true;
00344     if ( mode != IO_ReadOnly && mode != IO_ReadWrite )
00345     {
00346         kdWarning(7040) << "Unsupported mode " << mode << endl;
00347         return false;
00348     }
00349 
00350     char buffer[47];
00351 
00352     
00353     
00354     QIODevice* dev = device();
00355 
00356     uint offset = 0; 
00357     int n;
00358 
00359     
00360     QAsciiDict<ParseFileInfo> pfi_map(1009, true , true );
00361     pfi_map.setAutoDelete(true);
00362 
00363     for (;;) 
00364     {
00365         n = dev->readBlock( buffer, 4 );
00366 
00367         if (n < 4)
00368         {
00369             kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#1)" << endl;
00370 
00371             return false;
00372         }
00373 
00374         if ( !memcmp( buffer, "PK\5\6", 4 ) ) 
00375             break;
00376 
00377         if ( !memcmp( buffer, "PK\3\4", 4 ) ) 
00378         {
00379             dev->at( dev->at() + 2 ); 
00380 
00381         
00382             n = dev->readBlock( buffer, 24 );
00383         if (n < 24) {
00384                 kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#4)" << endl;
00385                 return false;
00386         }
00387 
00388         int gpf = (uchar)buffer[0]; 
00389         int compression_mode = (uchar)buffer[2] | (uchar)buffer[3] << 8;
00390         Q_LONG compr_size = (uchar)buffer[12] | (uchar)buffer[13] << 8
00391                     | (uchar)buffer[14] << 16 | (uchar)buffer[15] << 24;
00392         Q_LONG uncomp_size = (uchar)buffer[16] | (uchar)buffer[17] << 8
00393                     | (uchar)buffer[18] << 16 | (uchar)buffer[19] << 24;
00394         int namelen = (uchar)buffer[20] | (uchar)buffer[21] << 8;
00395         int extralen = (uchar)buffer[22] | (uchar)buffer[23] << 8;
00396 
00397         
00398         QCString filename(namelen + 1);
00399         n = dev->readBlock(filename.data(), namelen);
00400             if ( n < namelen ) {
00401                 kdWarning(7040) << "Invalid ZIP file. Name not completely read (#2)" << endl;
00402         return false;
00403         }
00404 
00405         ParseFileInfo *pfi = new ParseFileInfo();
00406         pfi_map.insert(filename.data(), pfi);
00407 
00408         
00409         pfi->extralen = extralen;
00410         int handledextralen = QMIN(extralen, (int)sizeof buffer);
00411         n = dev->readBlock(buffer, handledextralen);
00412         
00413         if (!parseExtraField(buffer, handledextralen, true, *pfi))
00414             return false;
00415 
00416         
00417             
00418             
00419             if ( gpf & 8 )
00420             {
00421                 bool foundSignature = false;
00422 
00423                 while (!foundSignature)
00424                 {
00425                     n = dev->readBlock( buffer, 1 );
00426                     if (n < 1)
00427                     {
00428                         kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)" << endl;
00429                         return false;
00430                     }
00431 
00432                     if ( buffer[0] != 'P' )
00433                         continue;
00434 
00435                     n = dev->readBlock( buffer, 3 );
00436                     if (n < 3)
00437                     {
00438                         kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)" << endl;
00439                         return false;
00440                     }
00441 
00442                     if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
00443                     {
00444                         foundSignature = true;
00445                         dev->at( dev->at() + 12 ); 
00446                     }
00447                 }
00448             }
00449             else
00450             {
00451         
00452         if (compression_mode == NoCompression
00453                 && uncomp_size <= max_path_len
00454             && uncomp_size > 0) {
00455             
00456             pfi->guessed_symlink.resize(uncomp_size + 1);
00457             n = dev->readBlock(pfi->guessed_symlink.data(), uncomp_size);
00458             if (n < uncomp_size) {
00459             kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#5)" << endl;
00460             return false;
00461             }
00462         } else {
00463 
00464                     dev->at( dev->at() + compr_size );
00465         }
00466                 
00467                 
00468                 uint skip = compr_size + namelen + extralen;
00469                 offset += 30 + skip;
00470             }
00471         }
00472         else if ( !memcmp( buffer, "PK\1\2", 4 ) ) 
00473         {
00474 
00475             
00476             
00477             
00478             offset = dev->at() - 4;
00479 
00480             
00481             if ( d->m_offset == 0L ) d->m_offset = offset;
00482 
00483             n = dev->readBlock( buffer + 4, 42 );
00484             if (n < 42) {
00485                 kdWarning(7040) << "Invalid ZIP file, central entry too short" << endl; 
00486                 return false;
00487             }
00488             
00489             int namelen = (uchar)buffer[29] << 8 | (uchar)buffer[28];
00490             QCString bufferName( namelen + 1 );
00491             n = dev->readBlock( bufferName.data(), namelen );
00492             if ( n < namelen )
00493                 kdWarning(7040) << "Invalid ZIP file. Name not completely read" << endl;
00494 
00495             ParseFileInfo *pfi = pfi_map[bufferName];
00496             if (!pfi) {   
00497                 pfi_map.insert(bufferName.data(), pfi = new ParseFileInfo());
00498             }
00499             QString name( QFile::decodeName(bufferName) );
00500 
00501             
00502             
00503             
00504             int extralen = (uchar)buffer[31] << 8 | (uchar)buffer[30];
00505             
00506             int commlen =  (uchar)buffer[33] << 8 | (uchar)buffer[32];
00507             
00508             int cmethod =  (uchar)buffer[11] << 8 | (uchar)buffer[10];
00509 
00510             
00511             
00512 
00513             
00514             uint ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 |
00515                 (uchar)buffer[25] << 8 | (uchar)buffer[24];
00516             
00517             uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 |
00518                 (uchar)buffer[21] << 8 | (uchar)buffer[20];
00519 
00520             
00521             uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 |
00522                 (uchar)buffer[43] << 8 | (uchar)buffer[42];
00523 
00524             
00525             
00526             
00527             
00528             int localextralen = pfi->extralen; 
00529                             
00530 
00531             
00532 
00533             
00534             uint dataoffset = localheaderoffset + 30 + localextralen + namelen; 
00535 
00536             
00537             
00538             
00539 
00540         int os_madeby = (uchar)buffer[5];
00541             bool isdir = false;
00542             int access = 0100644;
00543 
00544         if (os_madeby == 3) {   
00545             access = (uchar)buffer[40] | (uchar)buffer[41] << 8;
00546         }
00547 
00548             QString entryName;
00549 
00550             if ( name.endsWith( "/" ) ) 
00551             {
00552                 isdir = true;
00553                 name = name.left( name.length() - 1 );
00554                 if (os_madeby != 3) access |= S_IFDIR | 0111;
00555         else Q_ASSERT(access & S_IFDIR);
00556             }
00557 
00558             int pos = name.findRev( '/' );
00559             if ( pos == -1 )
00560                 entryName = name;
00561             else
00562                 entryName = name.mid( pos + 1 );
00563             Q_ASSERT( !entryName.isEmpty() );
00564 
00565             KArchiveEntry* entry;
00566             if ( isdir )
00567             {
00568                 QString path = QDir::cleanDirPath( name.left( pos ) );
00569                 KArchiveEntry* ent = rootDir()->entry( path );
00570                 if ( ent && ent->isDirectory() )
00571                 {
00572                     
00573                     entry = 0L;
00574                 }
00575                 else
00576                 {
00577                     entry = new KArchiveDirectory( this, entryName, access, (int)pfi->mtime, rootDir()->user(), rootDir()->group(), QString::null );
00578                     
00579                 }
00580         }
00581             else
00582             {
00583             QString symlink;
00584         if (S_ISLNK(access)) {
00585             symlink = QFile::decodeName(pfi->guessed_symlink);
00586         }
00587                 entry = new KZipFileEntry( this, entryName, access, pfi->mtime,
00588                     rootDir()->user(), rootDir()->group(),
00589                     symlink, name, dataoffset,
00590                     ucsize, cmethod, csize );
00591                 static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset );
00592                 
00593                 d->m_fileList.append( static_cast<KZipFileEntry *>( entry ) );
00594             }
00595 
00596             if ( entry )
00597             {
00598                 if ( pos == -1 )
00599                 {
00600                     rootDir()->addEntry(entry);
00601                 }
00602                 else
00603                 {
00604                     
00605                     QString path = QDir::cleanDirPath( name.left( pos ) );
00606                     
00607                     KArchiveDirectory * tdir = findOrCreate( path );
00608                     tdir->addEntry(entry);
00609                 }
00610             }
00611 
00612             
00613             offset += 46 + commlen + extralen + namelen;
00614             bool b = dev->at(offset);
00615             Q_ASSERT( b );
00616             if ( !b )
00617               return false;
00618         }
00619         else
00620         {
00621             kdWarning(7040) << "Invalid ZIP file. Unrecognized header at offset " << offset << endl;
00622 
00623             return false;
00624         }
00625     }
00626     
00627     return true;
00628 }
00629 
00630 bool KZip::closeArchive()
00631 {
00632     if ( ! ( mode() & IO_WriteOnly ) )
00633     {
00634         
00635         return true;
00636     }
00637     
00638     
00639 
00640     
00641     char buffer[ 22 ]; 
00642     uLong crc = crc32(0L, Z_NULL, 0);
00643 
00644     Q_LONG centraldiroffset = device()->at();
00645     
00646     Q_LONG atbackup = centraldiroffset;
00647     QPtrListIterator<KZipFileEntry> it( d->m_fileList );
00648 
00649     for ( ; it.current() ; ++it )
00650     {   
00651         device()->at( it.current()->headerStart() + 14 );
00652     
00653     
00654     
00655 
00656         uLong mycrc = it.current()->crc32();
00657         buffer[0] = char(mycrc); 
00658         buffer[1] = char(mycrc >> 8);
00659         buffer[2] = char(mycrc >> 16);
00660         buffer[3] = char(mycrc >> 24);
00661 
00662         int mysize1 = it.current()->compressedSize();
00663         buffer[4] = char(mysize1); 
00664         buffer[5] = char(mysize1 >> 8);
00665         buffer[6] = char(mysize1 >> 16);
00666         buffer[7] = char(mysize1 >> 24);
00667 
00668         int myusize = it.current()->size();
00669         buffer[8] = char(myusize); 
00670         buffer[9] = char(myusize >> 8);
00671         buffer[10] = char(myusize >> 16);
00672         buffer[11] = char(myusize >> 24);
00673 
00674         device()->writeBlock( buffer, 12 );
00675     }
00676     device()->at( atbackup );
00677 
00678     for ( it.toFirst(); it.current() ; ++it )
00679     {
00680         
00681         
00682 
00683         QCString path = QFile::encodeName(it.current()->path());
00684 
00685     const int extra_field_len = 9;
00686         int bufferSize = extra_field_len + path.length() + 46;
00687         char* buffer = new char[ bufferSize ];
00688 
00689         memset(buffer, 0, 46); 
00690 
00691         const char head[] =
00692         {
00693             'P', 'K', 1, 2, 
00694             0x14, 3,        
00695             0x14, 0         
00696         };
00697 
00698     
00699         
00700         qmemmove(buffer, head, sizeof(head));
00701 
00702         buffer[ 10 ] = char(it.current()->encoding()); 
00703         buffer[ 11 ] = char(it.current()->encoding() >> 8);
00704 
00705         transformToMsDos( it.current()->datetime(), &buffer[ 12 ] );
00706 
00707         uLong mycrc = it.current()->crc32();
00708         buffer[ 16 ] = char(mycrc); 
00709         buffer[ 17 ] = char(mycrc >> 8);
00710         buffer[ 18 ] = char(mycrc >> 16);
00711         buffer[ 19 ] = char(mycrc >> 24);
00712 
00713         int mysize1 = it.current()->compressedSize();
00714         buffer[ 20 ] = char(mysize1); 
00715         buffer[ 21 ] = char(mysize1 >> 8);
00716         buffer[ 22 ] = char(mysize1 >> 16);
00717         buffer[ 23 ] = char(mysize1 >> 24);
00718 
00719         int mysize = it.current()->size();
00720         buffer[ 24 ] = char(mysize); 
00721         buffer[ 25 ] = char(mysize >> 8);
00722         buffer[ 26 ] = char(mysize >> 16);
00723         buffer[ 27 ] = char(mysize >> 24);
00724 
00725         buffer[ 28 ] = char(it.current()->path().length()); 
00726         buffer[ 29 ] = char(it.current()->path().length() >> 8);
00727 
00728     buffer[ 30 ] = char(extra_field_len);
00729     buffer[ 31 ] = char(extra_field_len >> 8);
00730 
00731     buffer[ 40 ] = char(it.current()->permissions());
00732     buffer[ 41 ] = char(it.current()->permissions() >> 8);
00733 
00734         int myhst = it.current()->headerStart();
00735         buffer[ 42 ] = char(myhst); 
00736         buffer[ 43 ] = char(myhst >> 8);
00737         buffer[ 44 ] = char(myhst >> 16);
00738         buffer[ 45 ] = char(myhst >> 24);
00739 
00740         
00741         strncpy( buffer + 46, path, path.length() );
00742     
00743 
00744     
00745     char *extfield = buffer + 46 + path.length();
00746     extfield[0] = 'U';
00747     extfield[1] = 'T';
00748     extfield[2] = 5;
00749     extfield[3] = 0;
00750     extfield[4] = 1 | 2 | 4;    
00751                     
00752     
00753     unsigned long time = (unsigned long)it.current()->date();
00754     extfield[5] = char(time);
00755     extfield[6] = char(time >> 8);
00756     extfield[7] = char(time >> 16);
00757     extfield[8] = char(time >> 24);
00758 
00759         crc = crc32(crc, (Bytef *)buffer, bufferSize );
00760         device()->writeBlock( buffer, bufferSize );
00761         delete[] buffer;
00762     }
00763     Q_LONG centraldirendoffset = device()->at();
00764     
00765     
00766 
00767     
00768     buffer[ 0 ] = 'P'; 
00769     buffer[ 1 ] = 'K';
00770     buffer[ 2 ] = 5;
00771     buffer[ 3 ] = 6;
00772 
00773     buffer[ 4 ] = 0; 
00774     buffer[ 5 ] = 0;
00775 
00776     buffer[ 6 ] = 0; 
00777     buffer[ 7 ] = 0;
00778 
00779     int count = d->m_fileList.count();
00780     
00781 
00782 
00783     buffer[ 8 ] = char(count); 
00784     buffer[ 9 ] = char(count >> 8); 
00785 
00786     buffer[ 10 ] = buffer[ 8 ]; 
00787     buffer[ 11 ] = buffer[ 9 ];
00788 
00789     int cdsize = centraldirendoffset - centraldiroffset;
00790     buffer[ 12 ] = char(cdsize); 
00791     buffer[ 13 ] = char(cdsize >> 8);
00792     buffer[ 14 ] = char(cdsize >> 16);
00793     buffer[ 15 ] = char(cdsize >> 24);
00794 
00795     
00796     
00797 
00798     buffer[ 16 ] = char(centraldiroffset); 
00799     buffer[ 17 ] = char(centraldiroffset >> 8);
00800     buffer[ 18 ] = char(centraldiroffset >> 16);
00801     buffer[ 19 ] = char(centraldiroffset >> 24);
00802 
00803     buffer[ 20 ] = 0; 
00804     buffer[ 21 ] = 0;
00805 
00806     device()->writeBlock( buffer, 22);
00807 
00808     
00809     return true;
00810 }
00811 
00812 
00813 bool KZip::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
00814 {
00815     mode_t mode = 0100644;
00816     time_t the_time = time(0);
00817     return KArchive::writeFile( name, user, group, size, mode, the_time,
00818                 the_time, the_time, data );
00819 }
00820 
00821 
00822 bool KZip::writeFile( const QString& name, const QString& user,
00823                         const QString& group, uint size, mode_t perm,
00824                         time_t atime, time_t mtime, time_t ctime,
00825                         const char* data ) {
00826   return KArchive::writeFile(name, user, group, size, perm, atime, mtime,
00827             ctime, data);
00828 }
00829 
00830 
00831 bool KZip::prepareWriting( const QString& name, const QString& user, const QString& group, uint size )
00832 {
00833     mode_t dflt_perm = 0100644;
00834     time_t the_time = time(0);
00835     return prepareWriting(name,user,group,size,dflt_perm,
00836             the_time,the_time,the_time);
00837 }
00838 
00839 
00840 bool KZip::prepareWriting(const QString& name, const QString& user,
00841                 const QString& group, uint size, mode_t perm,
00842                 time_t atime, time_t mtime, time_t ctime) {
00843   return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime);
00844 }
00845 
00846 bool KZip::prepareWriting_impl(const QString &name, const QString &user,
00847                 const QString &group, uint , mode_t perm,
00848                 time_t atime, time_t mtime, time_t ctime) {
00849     
00850     if ( !isOpened() )
00851     {
00852         qWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
00853         return false;
00854     }
00855 
00856     if ( ! ( mode() & IO_WriteOnly ) ) 
00857     {
00858         qWarning( "KZip::writeFile: You must open the zip file for writing\n");
00859         return false;
00860     }
00861 
00862     
00863     device()->at( d->m_offset );
00864 
00865     
00866     
00867     
00868     
00869     QPtrListIterator<KZipFileEntry> it( d->m_fileList );
00870 
00871     
00872     for ( ; it.current() ; ++it )
00873     {
00874         
00875         if (name == it.current()->path() )
00876         {
00877             
00878             d->m_fileList.remove();
00879         }
00880 
00881     }
00882     
00883     KArchiveDirectory* parentDir = rootDir();
00884     QString fileName( name );
00885     int i = name.findRev( '/' );
00886     if ( i != -1 )
00887     {
00888         QString dir = name.left( i );
00889         fileName = name.mid( i + 1 );
00890         
00891         parentDir = findOrCreate( dir );
00892     }
00893 
00894     
00895     KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, QString::null,
00896                                            name, device()->at() + 30 + name.length(), 
00897                                            0 , d->m_compression, 0  );
00898     e->setHeaderStart( device()->at() );
00899     
00900     parentDir->addEntry( e );
00901 
00902     d->m_currentFile = e;
00903     d->m_fileList.append( e );
00904 
00905     int extra_field_len = 0;
00906     if ( d->m_extraField == ModificationTime )
00907         extra_field_len = 17;   
00908 
00909     
00910     QCString encodedName = QFile::encodeName(name);
00911     int bufferSize = extra_field_len + encodedName.length() + 30;
00912     
00913     char* buffer = new char[ bufferSize ];
00914 
00915     buffer[ 0 ] = 'P'; 
00916     buffer[ 1 ] = 'K';
00917     buffer[ 2 ] = 3;
00918     buffer[ 3 ] = 4;
00919 
00920     buffer[ 4 ] = 0x14; 
00921     buffer[ 5 ] = 0;
00922 
00923     buffer[ 6 ] = 0; 
00924     buffer[ 7 ] = 0;
00925 
00926     buffer[ 8 ] = char(e->encoding()); 
00927     buffer[ 9 ] = char(e->encoding() >> 8);
00928 
00929     transformToMsDos( e->datetime(), &buffer[ 10 ] );
00930 
00931     buffer[ 14 ] = 'C'; 
00932     buffer[ 15 ] = 'R';
00933     buffer[ 16 ] = 'C';
00934     buffer[ 17 ] = 'q';
00935 
00936     buffer[ 18 ] = 'C'; 
00937     buffer[ 19 ] = 'S';
00938     buffer[ 20 ] = 'I';
00939     buffer[ 21 ] = 'Z';
00940 
00941     buffer[ 22 ] = 'U'; 
00942     buffer[ 23 ] = 'S';
00943     buffer[ 24 ] = 'I';
00944     buffer[ 25 ] = 'Z';
00945 
00946     buffer[ 26 ] = (uchar)(encodedName.length()); 
00947     buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
00948 
00949     buffer[ 28 ] = (uchar)(extra_field_len); 
00950     buffer[ 29 ] = (uchar)(extra_field_len >> 8);
00951 
00952     
00953     strncpy( buffer + 30, encodedName, encodedName.length() );
00954 
00955     
00956     if ( d->m_extraField == ModificationTime )
00957     {
00958         char *extfield = buffer + 30 + encodedName.length();
00959         
00960         extfield[0] = 'U';
00961         extfield[1] = 'T';
00962         extfield[2] = 13; 
00963         extfield[3] = 0;
00964         extfield[4] = 1 | 2 | 4;    
00965 
00966         extfield[5] = char(mtime);
00967         extfield[6] = char(mtime >> 8);
00968         extfield[7] = char(mtime >> 16);
00969         extfield[8] = char(mtime >> 24);
00970 
00971         extfield[9] = char(atime);
00972         extfield[10] = char(atime >> 8);
00973         extfield[11] = char(atime >> 16);
00974         extfield[12] = char(atime >> 24);
00975 
00976         extfield[13] = char(ctime);
00977         extfield[14] = char(ctime >> 8);
00978         extfield[15] = char(ctime >> 16);
00979         extfield[16] = char(ctime >> 24);
00980     }
00981 
00982     
00983     bool b = (device()->writeBlock( buffer, bufferSize ) == bufferSize );
00984     d->m_crc = 0L;
00985     delete[] buffer;
00986 
00987     Q_ASSERT( b );
00988     if (!b)
00989         return false;
00990 
00991     
00992     
00993     if ( d->m_compression == 0 ) {
00994         d->m_currentDev = device();
00995         return true;
00996     }
00997 
00998     d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
00999     Q_ASSERT( d->m_currentDev );
01000     if ( !d->m_currentDev )
01001         return false; 
01002     static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders(); 
01003 
01004     b = d->m_currentDev->open( IO_WriteOnly );
01005     Q_ASSERT( b );
01006     return b;
01007 }
01008 
01009 bool KZip::doneWriting( uint size )
01010 {
01011     if ( d->m_currentFile->encoding() == 8 ) {
01012         
01013         (void)d->m_currentDev->writeBlock( 0, 0 );
01014         delete d->m_currentDev;
01015     }
01016     
01017     d->m_currentDev = 0L;
01018 
01019     Q_ASSERT( d->m_currentFile );
01020     
01021     
01022     
01023     d->m_currentFile->setSize(size);
01024     int extra_field_len = 0;
01025     if ( d->m_extraField == ModificationTime )
01026         extra_field_len = 17;   
01027 
01028     int csize = device()->at() -
01029         d->m_currentFile->headerStart() - 30 -
01030         d->m_currentFile->path().length() - extra_field_len;
01031     d->m_currentFile->setCompressedSize(csize);
01032     
01033     
01034     
01035 
01036     
01037     d->m_currentFile->setCRC32( d->m_crc );
01038 
01039     d->m_currentFile = 0L;
01040 
01041     
01042     d->m_offset = device()->at();
01043     return true;
01044 }
01045 
01046 bool KZip::writeSymLink(const QString &name, const QString &target,
01047                 const QString &user, const QString &group,
01048                 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01049   return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime);
01050 }
01051 
01052 bool KZip::writeSymLink_impl(const QString &name, const QString &target,
01053                 const QString &user, const QString &group,
01054                 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
01055 
01056   
01057   
01058   perm |= S_IFLNK;
01059   Compression c = compression();
01060   setCompression(NoCompression);    
01061 
01062   if (!prepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) {
01063     kdWarning() << "KZip::writeFile prepareWriting failed" << endl;
01064     setCompression(c);
01065     return false;
01066   }
01067 
01068   QCString symlink_target = QFile::encodeName(target);
01069   if (!writeData(symlink_target, symlink_target.length())) {
01070     kdWarning() << "KZip::writeFile writeData failed" << endl;
01071     setCompression(c);
01072     return false;
01073   }
01074 
01075   if (!doneWriting(symlink_target.length())) {
01076     kdWarning() << "KZip::writeFile doneWriting failed" << endl;
01077     setCompression(c);
01078     return false;
01079   }
01080 
01081   setCompression(c);
01082   return true;
01083 }
01084 
01085 void KZip::virtual_hook( int id, void* data )
01086 {
01087     switch (id) {
01088       case VIRTUAL_WRITE_DATA: {
01089         WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data);
01090         params->retval = writeData_impl( params->data, params->size );
01091         break;
01092       }
01093       case VIRTUAL_WRITE_SYMLINK: {
01094         WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
01095         params->retval = writeSymLink_impl(*params->name,*params->target,
01096                 *params->user,*params->group,params->perm,
01097                 params->atime,params->mtime,params->ctime);
01098         break;
01099       }
01100       case VIRTUAL_PREPARE_WRITING: {
01101         PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
01102         params->retval = prepareWriting_impl(*params->name,*params->user,
01103                 *params->group,params->size,params->perm,
01104                 params->atime,params->mtime,params->ctime);
01105         break;
01106       }
01107       default:
01108         KArchive::virtual_hook( id, data );
01109     }
01110 }
01111 
01112 
01113 bool KZip::writeData(const char * c, uint i)
01114 {
01115     return KArchive::writeData( c, i );
01116 }
01117 
01118 bool KZip::writeData_impl(const char * c, uint i)
01119 {
01120     Q_ASSERT( d->m_currentFile );
01121     Q_ASSERT( d->m_currentDev );
01122     if (!d->m_currentFile || !d->m_currentDev)
01123         return false;
01124 
01125     
01126     
01127     d->m_crc = crc32(d->m_crc, (const Bytef *) c , i);
01128 
01129     Q_LONG written = d->m_currentDev->writeBlock( c, i );
01130     
01131     Q_ASSERT( written == (Q_LONG)i );
01132     return written == (Q_LONG)i;
01133 }
01134 
01135 void KZip::setCompression( Compression c )
01136 {
01137     d->m_compression = ( c == NoCompression ) ? 0 : 8;
01138 }
01139 
01140 KZip::Compression KZip::compression() const
01141 {
01142    return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
01143 }
01144 
01145 void KZip::setExtraField( ExtraField ef )
01146 {
01147     d->m_extraField = ef;
01148 }
01149 
01150 KZip::ExtraField KZip::extraField() const
01151 {
01152     return d->m_extraField;
01153 }
01154 
01156 
01157 QByteArray KZipFileEntry::data() const
01158 {
01159     QIODevice* dev = device();
01160     QByteArray arr;
01161     if ( dev ) {
01162         arr = dev->readAll();
01163         delete dev;
01164     }
01165     return arr;
01166 }
01167 
01168 QIODevice* KZipFileEntry::device() const
01169 {
01170     
01171     
01172     KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
01173     if ( encoding() == 0 || compressedSize() == 0 ) 
01174         return limitedDev;
01175 
01176     if ( encoding() == 8 )
01177     {
01178         
01179         QIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
01180         if ( !filterDev )
01181             return 0L; 
01182         static_cast<KFilterDev *>(filterDev)->setSkipHeaders(); 
01183         bool b = filterDev->open( IO_ReadOnly );
01184         Q_ASSERT( b );
01185         return filterDev;
01186     }
01187 
01188     kdError() << "This zip file contains files compressed with method "
01189               << encoding() <<", this method is currently not supported by KZip,"
01190               <<" please use a command-line tool to handle this file." << endl;
01191     return 0L;
01192 }
01193