• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • kio/kio
 

kio/kio

  • kio
  • kio
kzip.cpp
1 /* This file is part of the KDE libraries
2  Copyright (C) 2000 David Faure <faure@kde.org>
3  Copyright (C) 2002 Holger Schroeder <holger-kde@holgis.net>
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License version 2 as published by the Free Software Foundation.
8 
9  This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  Library General Public License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to
16  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  Boston, MA 02110-1301, USA.
18 */
19 
20 /*
21  This class implements a kioslave to access ZIP files from KDE.
22  you can use it in IO_ReadOnly or in IO_WriteOnly mode, and it
23  behaves just as expected (i hope ;-) ).
24  It can also be used in IO_ReadWrite mode, in this case one can
25  append files to an existing zip archive. when you append new files, which
26  are not yet in the zip, it works as expected, they are appended at the end.
27  when you append a file, which is already in the file, the reference to the
28  old file is dropped and the new one is added to the zip. but the
29  old data from the file itself is not deleted, it is still in the
30  zipfile. so when you want to have a small and garbagefree zipfile,
31  just read the contents of the appended zipfile and write it to a new one
32  in IO_WriteOnly mode. especially take care of this, when you don't want
33  to leak information of how intermediate versions of files in the zip
34  were looking.
35  For more information on the zip fileformat go to
36  http://www.pkware.com/support/appnote.html .
37 
38 */
39 
40 #include "kzip.h"
41 #include "kfilterdev.h"
42 #include "klimitediodevice.h"
43 #include <kmimetype.h>
44 #include <ksavefile.h>
45 #include <kdebug.h>
46 
47 #include <tqasciidict.h>
48 #include <tqfile.h>
49 #include <tqdir.h>
50 #include <tqdatetime.h>
51 #include <tqptrlist.h>
52 
53 #include <zlib.h>
54 #include <time.h>
55 #include <string.h>
56 
57 const int max_path_len = 4095; // maximum number of character a path may contain
58 
59 static void transformToMsDos(const TQDateTime& dt, char* buffer)
60 {
61  if ( dt.isValid() )
62  {
63  const TQ_UINT16 time =
64  ( dt.time().hour() << 11 ) // 5 bit hour
65  | ( dt.time().minute() << 5 ) // 6 bit minute
66  | ( dt.time().second() >> 1 ); // 5 bit double seconds
67 
68  buffer[0] = char(time);
69  buffer[1] = char(time >> 8);
70 
71  const TQ_UINT16 date =
72  ( ( dt.date().year() - 1980 ) << 9 ) // 7 bit year 1980-based
73  | ( dt.date().month() << 5 ) // 4 bit month
74  | ( dt.date().day() ); // 5 bit day
75 
76  buffer[2] = char(date);
77  buffer[3] = char(date >> 8);
78  }
79  else // !dt.isValid(), assume 1980-01-01 midnight
80  {
81  buffer[0] = 0;
82  buffer[1] = 0;
83  buffer[2] = 33;
84  buffer[3] = 0;
85  }
86 }
87 
88 static time_t transformFromMsDos(const char* buffer)
89 {
90  TQ_UINT16 time = (uchar)buffer[0] | ( (uchar)buffer[1] << 8 );
91  int h = time >> 11;
92  int m = ( time & 0x7ff ) >> 5;
93  int s = ( time & 0x1f ) * 2 ;
94  TQTime qt(h, m, s);
95 
96  TQ_UINT16 date = (uchar)buffer[2] | ( (uchar)buffer[3] << 8 );
97  int y = ( date >> 9 ) + 1980;
98  int o = ( date & 0x1ff ) >> 5;
99  int d = ( date & 0x1f );
100  TQDate qd(y, o, d);
101 
102  TQDateTime dt( qd, qt );
103  return dt.toTime_t();
104 }
105 
106 // == parsing routines for zip headers
107 
109 struct ParseFileInfo {
110  // file related info
111 // TQCString name; // filename
112  mode_t perm; // permissions of this file
113  time_t atime; // last access time (UNIX format)
114  time_t mtime; // modification time (UNIX format)
115  time_t ctime; // creation time (UNIX format)
116  int uid; // user id (-1 if not specified)
117  int gid; // group id (-1 if not specified)
118  TQCString guessed_symlink; // guessed symlink target
119  int extralen; // length of extra field
120 
121  // parsing related info
122  bool exttimestamp_seen; // true if extended timestamp extra field
123  // has been parsed
124  bool newinfounix_seen; // true if Info-ZIP Unix New extra field has
125  // been parsed
126 
127  ParseFileInfo() : perm(0100644), uid(-1), gid(-1), extralen(0),
128  exttimestamp_seen(false), newinfounix_seen(false) {
129  ctime = mtime = atime = time(0);
130  }
131 };
132 
141 static bool parseExtTimestamp(const char *buffer, int size, bool islocal,
142  ParseFileInfo &pfi) {
143  if (size < 1) {
144  kdDebug(7040) << "premature end of extended timestamp (#1)" << endl;
145  return false;
146  }/*end if*/
147  int flags = *buffer; // read flags
148  buffer += 1;
149  size -= 1;
150 
151  if (flags & 1) { // contains modification time
152  if (size < 4) {
153  kdDebug(7040) << "premature end of extended timestamp (#2)" << endl;
154  return false;
155  }/*end if*/
156  pfi.mtime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
157  | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
158  buffer += 4;
159  size -= 4;
160  }/*end if*/
161  // central extended field cannot contain more than the modification time
162  // even if other flags are set
163  if (!islocal) {
164  pfi.exttimestamp_seen = true;
165  return true;
166  }/*end if*/
167 
168  if (flags & 2) { // contains last access time
169  if (size < 4) {
170  kdDebug(7040) << "premature end of extended timestamp (#3)" << endl;
171  return true;
172  }/*end if*/
173  pfi.atime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
174  | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
175  buffer += 4;
176  size -= 4;
177  }/*end if*/
178 
179  if (flags & 4) { // contains creation time
180  if (size < 4) {
181  kdDebug(7040) << "premature end of extended timestamp (#4)" << endl;
182  return true;
183  }/*end if*/
184  pfi.ctime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
185  | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
186  buffer += 4;
187  }/*end if*/
188 
189  pfi.exttimestamp_seen = true;
190  return true;
191 }
192 
201 static bool parseInfoZipUnixOld(const char *buffer, int size, bool islocal,
202  ParseFileInfo &pfi) {
203  // spec mandates to omit this field if one of the newer fields are available
204  if (pfi.exttimestamp_seen || pfi.newinfounix_seen) return true;
205 
206  if (size < 8) {
207  kdDebug(7040) << "premature end of Info-ZIP unix extra field old" << endl;
208  return false;
209  }/*end if*/
210 
211  pfi.atime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
212  | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
213  buffer += 4;
214  pfi.mtime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
215  | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
216  buffer += 4;
217  if (islocal && size >= 12) {
218  pfi.uid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
219  buffer += 2;
220  pfi.gid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
221  buffer += 2;
222  }/*end if*/
223  return true;
224 }
225 
226 #if 0 // not needed yet
227 
235 static bool parseInfoZipUnixNew(const char *buffer, int size, bool islocal,
236  ParseFileInfo &pfi) {
237  if (!islocal) { // contains nothing in central field
238  pfi.newinfounix = true;
239  return true;
240  }/*end if*/
241 
242  if (size < 4) {
243  kdDebug(7040) << "premature end of Info-ZIP unix extra field new" << endl;
244  return false;
245  }/*end if*/
246 
247  pfi.uid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
248  buffer += 2;
249  pfi.gid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
250  buffer += 2;
251 
252  pfi.newinfounix = true;
253  return true;
254 }
255 #endif
256 
265 static bool parseExtraField(const char *buffer, int size, bool islocal,
266  ParseFileInfo &pfi) {
267  // extra field in central directory doesn't contain useful data, so we
268  // don't bother parsing it
269  if (!islocal) return true;
270 
271  while (size >= 4) { // as long as a potential extra field can be read
272  int magic = (uchar)buffer[0] | (uchar)buffer[1] << 8;
273  buffer += 2;
274  int fieldsize = (uchar)buffer[0] | (uchar)buffer[1] << 8;
275  buffer += 2;
276  size -= 4;
277 
278  if (fieldsize > size) {
279  //kdDebug(7040) << "fieldsize: " << fieldsize << " size: " << size << endl;
280  kdDebug(7040) << "premature end of extra fields reached" << endl;
281  break;
282  }/*end if*/
283 
284  switch (magic) {
285  case 0x5455: // extended timestamp
286  if (!parseExtTimestamp(buffer, fieldsize, islocal, pfi)) return false;
287  break;
288  case 0x5855: // old Info-ZIP unix extra field
289  if (!parseInfoZipUnixOld(buffer, fieldsize, islocal, pfi)) return false;
290  break;
291 #if 0 // not needed yet
292  case 0x7855: // new Info-ZIP unix extra field
293  if (!parseInfoZipUnixNew(buffer, fieldsize, islocal, pfi)) return false;
294  break;
295 #endif
296  default:
297  /* ignore everything else */;
298  }/*end switch*/
299 
300  buffer += fieldsize;
301  size -= fieldsize;
302  }/*wend*/
303  return true;
304 }
305 
309 
310 class KZip::KZipPrivate
311 {
312 public:
313  KZipPrivate()
314  : m_crc( 0 ),
315  m_currentFile( 0L ),
316  m_currentDev( 0L ),
317  m_compression( 8 ),
318  m_extraField( KZip::NoExtraField ),
319  m_offset( 0L ),
320  m_saveFile( 0 ) {}
321 
322  unsigned long m_crc; // checksum
323  KZipFileEntry* m_currentFile; // file currently being written
324  TQIODevice* m_currentDev; // filterdev used to write to the above file
325  TQPtrList<KZipFileEntry> m_fileList; // flat list of all files, for the index (saves a recursive method ;)
326  int m_compression;
327  KZip::ExtraField m_extraField;
328  unsigned int m_offset; // holds the offset of the place in the zip,
329  // where new data can be appended. after openarchive it points to 0, when in
330  // writeonly mode, or it points to the beginning of the central directory.
331  // each call to writefile updates this value.
332  KSaveFile* m_saveFile;
333 };
334 
335 KZip::KZip( const TQString& filename )
336  : KArchive( 0L )
337 {
338  //kdDebug(7040) << "KZip(filename) reached." << endl;
339  Q_ASSERT( !filename.isEmpty() );
340  m_filename = filename;
341  d = new KZipPrivate;
342  // unusual: this ctor leaves the device set to 0.
343  // This is for the use of KSaveFile, see openArchive.
344  // KDE4: move KSaveFile support to base class, KArchive.
345 }
346 
347 KZip::KZip( TQIODevice * dev )
348  : KArchive( dev )
349 {
350  //kdDebug(7040) << "KZip::KZip( TQIODevice * dev) reached." << endl;
351  d = new KZipPrivate;
352 }
353 
354 KZip::~KZip()
355 {
356  // mjarrett: Closes to prevent ~KArchive from aborting w/o device
357  //kdDebug(7040) << "~KZip reached." << endl;
358  if( isOpened() )
359  close();
360  if ( !m_filename.isEmpty() ) { // we created the device ourselves
361  if ( d->m_saveFile ) // writing mode
362  delete d->m_saveFile;
363  else // reading mode
364  delete device(); // (the TQFile)
365  }
366  delete d;
367 }
368 
369 bool KZip::openArchive( int mode )
370 {
371  //kdDebug(7040) << "openarchive reached." << endl;
372  d->m_fileList.clear();
373 
374  switch ( mode ) {
375  case IO_WriteOnly:
376  // The use of KSaveFile can't be done in the ctor (no mode known yet)
377  // Ideally we would reimplement open() and do it there (BIC)
378  if ( !m_filename.isEmpty() ) {
379  kdDebug(7040) << "Writing to a file using KSaveFile" << endl;
380  d->m_saveFile = new KSaveFile( m_filename );
381  if ( d->m_saveFile->status() != 0 ) {
382  kdWarning(7040) << "KSaveFile creation for " << m_filename << " failed, " << strerror( d->m_saveFile->status() ) << endl;
383  delete d->m_saveFile;
384  d->m_saveFile = 0;
385  return false;
386  }
387  Q_ASSERT( d->m_saveFile->file() );
388  setDevice( TQT_TQIODEVICE(d->m_saveFile->file()) );
389  }
390  return true;
391  case IO_ReadOnly:
392  case IO_ReadWrite:
393  {
394  // ReadWrite mode still uses TQFile for now; we'd need to copy to the tempfile, in fact.
395  if ( !m_filename.isEmpty() ) {
396  setDevice( TQT_TQIODEVICE(new TQFile( m_filename )) );
397  if ( !device()->open( mode ) )
398  return false;
399  }
400  break; // continued below
401  }
402  default:
403  kdWarning(7040) << "Unsupported mode " << mode << endl;
404  return false;
405  }
406 
407  char buffer[47];
408 
409  // Check that it's a valid ZIP file
410  // the above code opened the underlying device already.
411  TQIODevice* dev = device();
412 
413  if (!dev) {
414  return false;
415  }
416 
417  uint offset = 0; // holds offset, where we read
418  int n;
419 
420  // contains information gathered from the local file headers
421  TQAsciiDict<ParseFileInfo> pfi_map(1009, true /*case sensitive */, true /*copy keys*/);
422  pfi_map.setAutoDelete(true);
423 
424  // We set a bool for knowing if we are allowed to skip the start of the file
425  bool startOfFile = true;
426 
427  for (;;) // repeat until 'end of entries' signature is reached
428  {
429 kdDebug(7040) << "loop starts" << endl;
430 kdDebug(7040) << "dev->at() now : " << dev->at() << endl;
431  n = dev->readBlock( buffer, 4 );
432 
433  if (n < 4)
434  {
435  kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#1)" << endl;
436 
437  return false;
438  }
439 
440  if ( !memcmp( buffer, "PK\5\6", 4 ) ) // 'end of entries'
441  {
442  kdDebug(7040) << "PK56 found end of archive" << endl;
443  startOfFile = false;
444  break;
445  }
446 
447  if ( !memcmp( buffer, "PK\3\4", 4 ) ) // local file header
448  {
449  kdDebug(7040) << "PK34 found local file header" << endl;
450  startOfFile = false;
451  // can this fail ???
452  dev->at( dev->at() + 2 ); // skip 'version needed to extract'
453 
454  // read static header stuff
455  n = dev->readBlock( buffer, 24 );
456  if (n < 24) {
457  kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#4)" << endl;
458  return false;
459  }
460 
461  int gpf = (uchar)buffer[0]; // "general purpose flag" not "general protection fault" ;-)
462  int compression_mode = (uchar)buffer[2] | (uchar)buffer[3] << 8;
463  time_t mtime = transformFromMsDos( buffer+4 );
464 
465  TQ_LONG compr_size = (uchar)buffer[12] | (uchar)buffer[13] << 8
466  | (uchar)buffer[14] << 16 | (uchar)buffer[15] << 24;
467  TQ_LONG uncomp_size = (uchar)buffer[16] | (uchar)buffer[17] << 8
468  | (uchar)buffer[18] << 16 | (uchar)buffer[19] << 24;
469  int namelen = (uchar)buffer[20] | (uchar)buffer[21] << 8;
470  int extralen = (uchar)buffer[22] | (uchar)buffer[23] << 8;
471 
472  kdDebug(7040) << "general purpose bit flag: " << gpf << endl;
473  kdDebug(7040) << "compressed size: " << compr_size << endl;
474  kdDebug(7040) << "uncompressed size: " << uncomp_size << endl;
475  kdDebug(7040) << "namelen: " << namelen << endl;
476  kdDebug(7040) << "extralen: " << extralen << endl;
477  kdDebug(7040) << "archive size: " << dev->size() << endl;
478 
479  // read filename
480  TQCString filename(namelen + 1);
481  n = dev->readBlock(filename.data(), namelen);
482  if ( n < namelen ) {
483  kdWarning(7040) << "Invalid ZIP file. Name not completely read (#2)" << endl;
484  return false;
485  }
486 
487  ParseFileInfo *pfi = new ParseFileInfo();
488  pfi->mtime = mtime;
489  pfi_map.insert(filename.data(), pfi);
490 
491  // read and parse the beginning of the extra field,
492  // skip rest of extra field in case it is too long
493  unsigned int extraFieldEnd = dev->at() + extralen;
494  pfi->extralen = extralen;
495  int handledextralen = QMIN(extralen, (int)sizeof buffer);
496 
497  kdDebug(7040) << "handledextralen: " << handledextralen << endl;
498 
499  n = dev->readBlock(buffer, handledextralen);
500  // no error msg necessary as we deliberately truncate the extra field
501  if (!parseExtraField(buffer, handledextralen, true, *pfi))
502  {
503  kdWarning(7040) << "Invalid ZIP File. Broken ExtraField." << endl;
504  return false;
505  }
506 
507  // jump to end of extra field
508  dev->at( extraFieldEnd );
509 
510  // we have to take care of the 'general purpose bit flag'.
511  // if bit 3 is set, the header doesn't contain the length of
512  // the file and we look for the signature 'PK\7\8'.
513  if ( gpf & 8 )
514  {
515  // here we have to read through the compressed data to find
516  // the next PKxx
517  kdDebug(7040) << "trying to seek for next PK78" << endl;
518  bool foundSignature = false;
519 
520  while (!foundSignature)
521  {
522  n = dev->readBlock( buffer, 1 );
523  if (n < 1)
524  {
525  kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)" << endl;
526  return false;
527  }
528 
529  if ( buffer[0] != 'P' )
530  continue;
531 
532  n = dev->readBlock( buffer, 3 );
533  if (n < 3)
534  {
535  kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)" << endl;
536  return false;
537  }
538 
539  // we have to detect three magic tokens here:
540  // PK34 for the next local header in case there is no data descriptor
541  // PK12 for the central header in case there is no data descriptor
542  // PK78 for the data descriptor in case it is following the compressed data
543 
544  if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
545  {
546  foundSignature = true;
547  dev->at( dev->at() + 12 ); // skip the 'data_descriptor'
548  }
549  else if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 )
550  || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) )
551  {
552  foundSignature = true;
553  dev->at( dev->at() - 4 ); // go back 4 bytes, so that the magic bytes can be found...
554  }
555  else if ( buffer[0] == 'P' || buffer[1] == 'P' || buffer[2] == 'P' )
556  {
557  // We have another P character so we must go back a little to check if it is a magic
558  dev->at( dev->at() - 3 );
559  }
560 
561  }
562  }
563  else
564  {
565  // here we skip the compressed data and jump to the next header
566  kdDebug(7040) << "general purpose bit flag indicates, that local file header contains valid size" << endl;
567  // check if this could be a symbolic link
568  if (compression_mode == NoCompression
569  && uncomp_size <= max_path_len
570  && uncomp_size > 0) {
571  // read content and store it
572  pfi->guessed_symlink.resize(uncomp_size + 1);
573  kdDebug(7040) << "guessed symlink size: " << uncomp_size << endl;
574  n = dev->readBlock(pfi->guessed_symlink.data(), uncomp_size);
575  if (n < uncomp_size) {
576  kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#5)" << endl;
577  return false;
578  }
579  } else {
580 
581  if ( compr_size > (TQ_LONG)dev->size() )
582  {
583  // here we cannot trust the compressed size, so scan through the compressed
584  // data to find the next header
585  bool foundSignature = false;
586 
587  while (!foundSignature)
588  {
589  n = dev->readBlock( buffer, 1 );
590  if (n < 1)
591  {
592  kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)" << endl;
593  return false;
594  }
595 
596  if ( buffer[0] != 'P' )
597  continue;
598 
599  n = dev->readBlock( buffer, 3 );
600  if (n < 3)
601  {
602  kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)" << endl;
603  return false;
604  }
605 
606  // we have to detect three magic tokens here:
607  // PK34 for the next local header in case there is no data descriptor
608  // PK12 for the central header in case there is no data descriptor
609  // PK78 for the data descriptor in case it is following the compressed data
610 
611  if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
612  {
613  foundSignature = true;
614  dev->at( dev->at() + 12 ); // skip the 'data_descriptor'
615  }
616 
617  if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 )
618  || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) )
619  {
620  foundSignature = true;
621  dev->at( dev->at() - 4 );
622  // go back 4 bytes, so that the magic bytes can be found
623  // in the next cycle...
624  }
625  }
626  }
627  else
628  {
629 // kdDebug(7040) << "before interesting dev->at(): " << dev->at() << endl;
630  bool success;
631  success = dev->at( dev->at() + compr_size ); // can this fail ???
632 /* kdDebug(7040) << "after interesting dev->at(): " << dev->at() << endl;
633  if ( success )
634  kdDebug(7040) << "dev->at was successful... " << endl;
635  else
636  kdDebug(7040) << "dev->at failed... " << endl;*/
637  }
638 
639  }
640 
641 // not needed any more
642 /* // here we calculate the length of the file in the zip
643  // with headers and jump to the next header.
644  uint skip = compr_size + namelen + extralen;
645  offset += 30 + skip;*/
646  }
647  }
648  else if ( !memcmp( buffer, "PK\1\2", 4 ) ) // central block
649  {
650  kdDebug(7040) << "PK12 found central block" << endl;
651  startOfFile = false;
652 
653  // so we reached the central header at the end of the zip file
654  // here we get all interesting data out of the central header
655  // of a file
656  offset = dev->at() - 4;
657 
658  //set offset for appending new files
659  if ( d->m_offset == 0L ) d->m_offset = offset;
660 
661  n = dev->readBlock( buffer + 4, 42 );
662  if (n < 42) {
663  kdWarning(7040) << "Invalid ZIP file, central entry too short" << endl; // not long enough for valid entry
664  return false;
665  }
666 
667  //int gpf = (uchar)buffer[9] << 8 | (uchar)buffer[10];
668  //kdDebug() << "general purpose flag=" << gpf << endl;
669  // length of the filename (well, pathname indeed)
670  int namelen = (uchar)buffer[29] << 8 | (uchar)buffer[28];
671  TQCString bufferName( namelen + 1 );
672  n = dev->readBlock( bufferName.data(), namelen );
673  if ( n < namelen )
674  kdWarning(7040) << "Invalid ZIP file. Name not completely read" << endl;
675 
676  ParseFileInfo *pfi = pfi_map[bufferName];
677  if (!pfi) { // can that happen?
678  pfi_map.insert(bufferName.data(), pfi = new ParseFileInfo());
679  }
680  TQString name( TQFile::decodeName(bufferName) );
681 
682  //kdDebug(7040) << "name: " << name << endl;
683  // only in central header ! see below.
684  // length of extra attributes
685  int extralen = (uchar)buffer[31] << 8 | (uchar)buffer[30];
686  // length of comment for this file
687  int commlen = (uchar)buffer[33] << 8 | (uchar)buffer[32];
688  // compression method of this file
689  int cmethod = (uchar)buffer[11] << 8 | (uchar)buffer[10];
690 
691  //kdDebug(7040) << "cmethod: " << cmethod << endl;
692  //kdDebug(7040) << "extralen: " << extralen << endl;
693 
694  // uncompressed file size
695  uint ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 |
696  (uchar)buffer[25] << 8 | (uchar)buffer[24];
697  // compressed file size
698  uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 |
699  (uchar)buffer[21] << 8 | (uchar)buffer[20];
700 
701  // offset of local header
702  uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 |
703  (uchar)buffer[43] << 8 | (uchar)buffer[42];
704 
705  // some clever people use different extra field lengths
706  // in the central header and in the local header... funny.
707  // so we need to get the localextralen to calculate the offset
708  // from localheaderstart to dataoffset
709  int localextralen = pfi->extralen; // FIXME: this will not work if
710  // no local header exists
711 
712  //kdDebug(7040) << "localextralen: " << localextralen << endl;
713 
714  // offset, where the real data for uncompression starts
715  uint dataoffset = localheaderoffset + 30 + localextralen + namelen; //comment only in central header
716 
717  //kdDebug(7040) << "esize: " << esize << endl;
718  //kdDebug(7040) << "eoffset: " << eoffset << endl;
719  //kdDebug(7040) << "csize: " << csize << endl;
720 
721  int os_madeby = (uchar)buffer[5];
722  bool isdir = false;
723  int access = 0100644;
724 
725  if (os_madeby == 3) { // good ole unix
726  access = (uchar)buffer[40] | (uchar)buffer[41] << 8;
727  }
728 
729  TQString entryName;
730 
731  if ( name.endsWith( "/" ) ) // Entries with a trailing slash are directories
732  {
733  isdir = true;
734  name = name.left( name.length() - 1 );
735  if (os_madeby != 3) access = S_IFDIR | 0755;
736  else Q_ASSERT(access & S_IFDIR);
737  }
738 
739  int pos = name.findRev( '/' );
740  if ( pos == -1 )
741  entryName = name;
742  else
743  entryName = name.mid( pos + 1 );
744  Q_ASSERT( !entryName.isEmpty() );
745 
746  KArchiveEntry* entry;
747  if ( isdir )
748  {
749  TQString path = TQDir::cleanDirPath( name );
750  KArchiveEntry* ent = rootDir()->entry( path );
751  if ( ent && ent->isDirectory() )
752  {
753  //kdDebug(7040) << "Directory already exists, NOT going to add it again" << endl;
754  entry = 0L;
755  }
756  else
757  {
758  entry = new KArchiveDirectory( this, entryName, access, (int)pfi->mtime, rootDir()->user(), rootDir()->group(), TQString::null );
759  //kdDebug(7040) << "KArchiveDirectory created, entryName= " << entryName << ", name=" << name << endl;
760  }
761  }
762  else
763  {
764  TQString symlink;
765  if (S_ISLNK(access)) {
766  symlink = TQFile::decodeName(pfi->guessed_symlink);
767  }
768  entry = new KZipFileEntry( this, entryName, access, pfi->mtime,
769  rootDir()->user(), rootDir()->group(),
770  symlink, name, dataoffset,
771  ucsize, cmethod, csize );
772  static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset );
773  //kdDebug(7040) << "KZipFileEntry created, entryName= " << entryName << ", name=" << name << endl;
774  d->m_fileList.append( static_cast<KZipFileEntry *>( entry ) );
775  }
776 
777  if ( entry )
778  {
779  if ( pos == -1 )
780  {
781  rootDir()->addEntry(entry);
782  }
783  else
784  {
785  // In some tar files we can find dir/./file => call cleanDirPath
786  TQString path = TQDir::cleanDirPath( name.left( pos ) );
787  // Ensure container directory exists, create otherwise
788  KArchiveDirectory * tdir = findOrCreate( path );
789  tdir->addEntry(entry);
790  }
791  }
792 
793  //calculate offset to next entry
794  offset += 46 + commlen + extralen + namelen;
795  bool b = dev->at(offset);
796  Q_ASSERT( b );
797  if ( !b )
798  return false;
799  }
800  else if ( startOfFile )
801  {
802  // The file does not start with any ZIP header (e.g. self-extractable ZIP files)
803  // Therefore we need to find the first PK\003\004 (local header)
804  kdDebug(7040) << "Try to skip start of file" << endl;
805  startOfFile = false;
806  bool foundSignature = false;
807 
808  while (!foundSignature)
809  {
810  n = dev->readBlock( buffer, 1 );
811  if (n < 1)
812  {
813  kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. " << k_funcinfo << endl;
814  return false;
815  }
816 
817  if ( buffer[0] != 'P' )
818  continue;
819 
820  n = dev->readBlock( buffer, 3 );
821  if (n < 3)
822  {
823  kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. " << k_funcinfo << endl;
824  return false;
825  }
826 
827  // We have to detect the magic token for a local header: PK\003\004
828  /*
829  * Note: we do not need to check the other magics, if the ZIP file has no
830  * local header, then it has not any files!
831  */
832  if ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 )
833  {
834  foundSignature = true;
835  dev->at( dev->at() - 4 ); // go back 4 bytes, so that the magic bytes can be found...
836  }
837  else if ( buffer[0] == 'P' || buffer[1] == 'P' || buffer[2] == 'P' )
838  {
839  // We have another P character so we must go back a little to check if it is a magic
840  dev->at( dev->at() - 3 );
841  }
842  }
843  }
844  else
845  {
846  kdWarning(7040) << "Invalid ZIP file. Unrecognized header at offset " << offset << endl;
847 
848  return false;
849  }
850  }
851  //kdDebug(7040) << "*** done *** " << endl;
852  return true;
853 }
854 
855 bool KZip::closeArchive()
856 {
857  if ( ! ( mode() & IO_WriteOnly ) )
858  {
859  //kdDebug(7040) << "closearchive readonly reached." << endl;
860  return true;
861  }
862 
863  kdDebug() << k_funcinfo << "device=" << device() << endl;
864  //ReadWrite or WriteOnly
865  //write all central dir file entries
866 
867  if ( !device() ) // saving aborted
868  return false;
869 
870  // to be written at the end of the file...
871  char buffer[ 22 ]; // first used for 12, then for 22 at the end
872  uLong crc = crc32(0L, Z_NULL, 0);
873 
874  TQ_LONG centraldiroffset = device()->at();
875  //kdDebug(7040) << "closearchive: centraldiroffset: " << centraldiroffset << endl;
876  TQ_LONG atbackup = centraldiroffset;
877  TQPtrListIterator<KZipFileEntry> it( d->m_fileList );
878 
879  for ( ; it.current() ; ++it )
880  { //set crc and compressed size in each local file header
881  if ( !device()->at( it.current()->headerStart() + 14 ) )
882  return false;
883  //kdDebug(7040) << "closearchive setcrcandcsize: filename: "
884  // << it.current()->path()
885  // << " encoding: "<< it.current()->encoding() << endl;
886 
887  uLong mycrc = it.current()->crc32();
888  buffer[0] = char(mycrc); // crc checksum, at headerStart+14
889  buffer[1] = char(mycrc >> 8);
890  buffer[2] = char(mycrc >> 16);
891  buffer[3] = char(mycrc >> 24);
892 
893  int mysize1 = it.current()->compressedSize();
894  buffer[4] = char(mysize1); // compressed file size, at headerStart+18
895  buffer[5] = char(mysize1 >> 8);
896  buffer[6] = char(mysize1 >> 16);
897  buffer[7] = char(mysize1 >> 24);
898 
899  int myusize = it.current()->size();
900  buffer[8] = char(myusize); // uncompressed file size, at headerStart+22
901  buffer[9] = char(myusize >> 8);
902  buffer[10] = char(myusize >> 16);
903  buffer[11] = char(myusize >> 24);
904 
905  if ( device()->writeBlock( buffer, 12 ) != 12 )
906  return false;
907  }
908  device()->at( atbackup );
909 
910  for ( it.toFirst(); it.current() ; ++it )
911  {
912  //kdDebug(7040) << "closearchive: filename: " << it.current()->path()
913  // << " encoding: "<< it.current()->encoding() << endl;
914 
915  TQCString path = TQFile::encodeName(it.current()->path());
916 
917  const int extra_field_len = 9;
918  int bufferSize = extra_field_len + path.length() + 46;
919  char* buffer = new char[ bufferSize ];
920 
921  memset(buffer, 0, 46); // zero is a nice default for most header fields
922 
923  const char head[] =
924  {
925  'P', 'K', 1, 2, // central file header signature
926  0x14, 3, // version made by (3 == UNIX)
927  0x14, 0 // version needed to extract
928  };
929 
930  // I do not know why memcpy is not working here
931  //memcpy(buffer, head, sizeof(head));
932  tqmemmove(buffer, head, sizeof(head));
933 
934  buffer[ 10 ] = char(it.current()->encoding()); // compression method
935  buffer[ 11 ] = char(it.current()->encoding() >> 8);
936 
937  transformToMsDos( it.current()->datetime(), &buffer[ 12 ] );
938 
939  uLong mycrc = it.current()->crc32();
940  buffer[ 16 ] = char(mycrc); // crc checksum
941  buffer[ 17 ] = char(mycrc >> 8);
942  buffer[ 18 ] = char(mycrc >> 16);
943  buffer[ 19 ] = char(mycrc >> 24);
944 
945  int mysize1 = it.current()->compressedSize();
946  buffer[ 20 ] = char(mysize1); // compressed file size
947  buffer[ 21 ] = char(mysize1 >> 8);
948  buffer[ 22 ] = char(mysize1 >> 16);
949  buffer[ 23 ] = char(mysize1 >> 24);
950 
951  int mysize = it.current()->size();
952  buffer[ 24 ] = char(mysize); // uncompressed file size
953  buffer[ 25 ] = char(mysize >> 8);
954  buffer[ 26 ] = char(mysize >> 16);
955  buffer[ 27 ] = char(mysize >> 24);
956 
957  buffer[ 28 ] = char(it.current()->path().length()); // filename length
958  buffer[ 29 ] = char(it.current()->path().length() >> 8);
959 
960  buffer[ 30 ] = char(extra_field_len);
961  buffer[ 31 ] = char(extra_field_len >> 8);
962 
963  buffer[ 40 ] = char(it.current()->permissions());
964  buffer[ 41 ] = char(it.current()->permissions() >> 8);
965 
966  int myhst = it.current()->headerStart();
967  buffer[ 42 ] = char(myhst); //relative offset of local header
968  buffer[ 43 ] = char(myhst >> 8);
969  buffer[ 44 ] = char(myhst >> 16);
970  buffer[ 45 ] = char(myhst >> 24);
971 
972  // file name
973  strncpy( buffer + 46, path, path.length() );
974  //kdDebug(7040) << "closearchive length to write: " << bufferSize << endl;
975 
976  // extra field
977  char *extfield = buffer + 46 + path.length();
978  extfield[0] = 'U';
979  extfield[1] = 'T';
980  extfield[2] = 5;
981  extfield[3] = 0;
982  extfield[4] = 1 | 2 | 4; // specify flags from local field
983  // (unless I misread the spec)
984  // provide only modification time
985  unsigned long time = (unsigned long)it.current()->date();
986  extfield[5] = char(time);
987  extfield[6] = char(time >> 8);
988  extfield[7] = char(time >> 16);
989  extfield[8] = char(time >> 24);
990 
991  crc = crc32(crc, (Bytef *)buffer, bufferSize );
992  bool ok = ( device()->writeBlock( buffer, bufferSize ) == bufferSize );
993  delete[] buffer;
994  if ( !ok )
995  return false;
996  }
997  TQ_LONG centraldirendoffset = device()->at();
998  //kdDebug(7040) << "closearchive: centraldirendoffset: " << centraldirendoffset << endl;
999  //kdDebug(7040) << "closearchive: device()->at(): " << device()->at() << endl;
1000 
1001  //write end of central dir record.
1002  buffer[ 0 ] = 'P'; //end of central dir signature
1003  buffer[ 1 ] = 'K';
1004  buffer[ 2 ] = 5;
1005  buffer[ 3 ] = 6;
1006 
1007  buffer[ 4 ] = 0; // number of this disk
1008  buffer[ 5 ] = 0;
1009 
1010  buffer[ 6 ] = 0; // number of disk with start of central dir
1011  buffer[ 7 ] = 0;
1012 
1013  int count = d->m_fileList.count();
1014  //kdDebug(7040) << "number of files (count): " << count << endl;
1015 
1016 
1017  buffer[ 8 ] = char(count); // total number of entries in central dir of
1018  buffer[ 9 ] = char(count >> 8); // this disk
1019 
1020  buffer[ 10 ] = buffer[ 8 ]; // total number of entries in the central dir
1021  buffer[ 11 ] = buffer[ 9 ];
1022 
1023  int cdsize = centraldirendoffset - centraldiroffset;
1024  buffer[ 12 ] = char(cdsize); // size of the central dir
1025  buffer[ 13 ] = char(cdsize >> 8);
1026  buffer[ 14 ] = char(cdsize >> 16);
1027  buffer[ 15 ] = char(cdsize >> 24);
1028 
1029  //kdDebug(7040) << "end : centraldiroffset: " << centraldiroffset << endl;
1030  //kdDebug(7040) << "end : centraldirsize: " << cdsize << endl;
1031 
1032  buffer[ 16 ] = char(centraldiroffset); // central dir offset
1033  buffer[ 17 ] = char(centraldiroffset >> 8);
1034  buffer[ 18 ] = char(centraldiroffset >> 16);
1035  buffer[ 19 ] = char(centraldiroffset >> 24);
1036 
1037  buffer[ 20 ] = 0; //zipfile comment length
1038  buffer[ 21 ] = 0;
1039 
1040  if ( device()->writeBlock( buffer, 22 ) != 22 )
1041  return false;
1042 
1043  if ( d->m_saveFile ) {
1044  d->m_saveFile->close();
1045  setDevice( 0 );
1046  delete d->m_saveFile;
1047  d->m_saveFile = 0;
1048  }
1049 
1050  //kdDebug(7040) << __FILE__" reached." << endl;
1051  return true;
1052 }
1053 
1054 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0
1055 bool KZip::writeFile( const TQString& name, const TQString& user, const TQString& group, uint size, const char* data )
1056 {
1057  mode_t mode = 0100644;
1058  time_t the_time = time(0);
1059  return KArchive::writeFile( name, user, group, size, mode, the_time,
1060  the_time, the_time, data );
1061 }
1062 
1063 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0
1064 bool KZip::writeFile( const TQString& name, const TQString& user,
1065  const TQString& group, uint size, mode_t perm,
1066  time_t atime, time_t mtime, time_t ctime,
1067  const char* data ) {
1068  return KArchive::writeFile(name, user, group, size, perm, atime, mtime,
1069  ctime, data);
1070 }
1071 
1072 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0
1073 bool KZip::prepareWriting( const TQString& name, const TQString& user, const TQString& group, uint size )
1074 {
1075  mode_t dflt_perm = 0100644;
1076  time_t the_time = time(0);
1077  return prepareWriting(name,user,group,size,dflt_perm,
1078  the_time,the_time,the_time);
1079 }
1080 
1081 // Doesn't need to be reimplemented anymore. Remove for KDE-4.0
1082 bool KZip::prepareWriting(const TQString& name, const TQString& user,
1083  const TQString& group, uint size, mode_t perm,
1084  time_t atime, time_t mtime, time_t ctime) {
1085  return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime);
1086 }
1087 
1088 bool KZip::prepareWriting_impl(const TQString &name, const TQString &user,
1089  const TQString &group, uint /*size*/, mode_t perm,
1090  time_t atime, time_t mtime, time_t ctime) {
1091  //kdDebug(7040) << "prepareWriting reached." << endl;
1092  if ( !isOpened() )
1093  {
1094  qWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
1095  return false;
1096  }
1097 
1098  if ( ! ( mode() & IO_WriteOnly ) ) // accept WriteOnly and ReadWrite
1099  {
1100  qWarning( "KZip::writeFile: You must open the zip file for writing\n");
1101  return false;
1102  }
1103 
1104  if ( !device() ) { // aborted
1105  //kdWarning(7040) << "prepareWriting_impl: no device" << endl;
1106  return false;
1107  }
1108 
1109  // set right offset in zip.
1110  if ( !device()->at( d->m_offset ) ) {
1111  kdWarning(7040) << "prepareWriting_impl: cannot seek in ZIP file. Disk full?" << endl;
1112  abort();
1113  return false;
1114  }
1115 
1116  // delete entries in the filelist with the same filename as the one we want
1117  // to save, so that we don�t have duplicate file entries when viewing the zip
1118  // with konqi...
1119  // CAUTION: the old file itself is still in the zip and won't be removed !!!
1120  TQPtrListIterator<KZipFileEntry> it( d->m_fileList );
1121 
1122  //kdDebug(7040) << "filename to write: " << name <<endl;
1123  for ( ; it.current() ; ++it )
1124  {
1125  //kdDebug(7040) << "prepfilename: " << it.current()->path() <<endl;
1126  if (name == it.current()->path() )
1127  {
1128  //kdDebug(7040) << "removing following entry: " << it.current()->path() <<endl;
1129  d->m_fileList.remove();
1130  }
1131 
1132  }
1133  // Find or create parent dir
1134  KArchiveDirectory* parentDir = rootDir();
1135  TQString fileName( name );
1136  int i = name.findRev( '/' );
1137  if ( i != -1 )
1138  {
1139  TQString dir = name.left( i );
1140  fileName = name.mid( i + 1 );
1141  //kdDebug(7040) << "KZip::prepareWriting ensuring " << dir << " exists. fileName=" << fileName << endl;
1142  parentDir = findOrCreate( dir );
1143  }
1144 
1145  // construct a KZipFileEntry and add it to list
1146  KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, TQString::null,
1147  name, device()->at() + 30 + name.length(), // start
1148  0 /*size unknown yet*/, d->m_compression, 0 /*csize unknown yet*/ );
1149  e->setHeaderStart( device()->at() );
1150  //kdDebug(7040) << "wrote file start: " << e->position() << " name: " << name << endl;
1151  parentDir->addEntry( e );
1152 
1153  d->m_currentFile = e;
1154  d->m_fileList.append( e );
1155 
1156  int extra_field_len = 0;
1157  if ( d->m_extraField == ModificationTime )
1158  extra_field_len = 17; // value also used in doneWriting()
1159 
1160  // write out zip header
1161  TQCString encodedName = TQFile::encodeName(name);
1162  int bufferSize = extra_field_len + encodedName.length() + 30;
1163  //kdDebug(7040) << "KZip::prepareWriting bufferSize=" << bufferSize << endl;
1164  char* buffer = new char[ bufferSize ];
1165 
1166  buffer[ 0 ] = 'P'; //local file header signature
1167  buffer[ 1 ] = 'K';
1168  buffer[ 2 ] = 3;
1169  buffer[ 3 ] = 4;
1170 
1171  buffer[ 4 ] = 0x14; // version needed to extract
1172  buffer[ 5 ] = 0;
1173 
1174  buffer[ 6 ] = 0; // general purpose bit flag
1175  buffer[ 7 ] = 0;
1176 
1177  buffer[ 8 ] = char(e->encoding()); // compression method
1178  buffer[ 9 ] = char(e->encoding() >> 8);
1179 
1180  transformToMsDos( e->datetime(), &buffer[ 10 ] );
1181 
1182  buffer[ 14 ] = 'C'; //dummy crc
1183  buffer[ 15 ] = 'R';
1184  buffer[ 16 ] = 'C';
1185  buffer[ 17 ] = 'q';
1186 
1187  buffer[ 18 ] = 'C'; //compressed file size
1188  buffer[ 19 ] = 'S';
1189  buffer[ 20 ] = 'I';
1190  buffer[ 21 ] = 'Z';
1191 
1192  buffer[ 22 ] = 'U'; //uncompressed file size
1193  buffer[ 23 ] = 'S';
1194  buffer[ 24 ] = 'I';
1195  buffer[ 25 ] = 'Z';
1196 
1197  buffer[ 26 ] = (uchar)(encodedName.length()); //filename length
1198  buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
1199 
1200  buffer[ 28 ] = (uchar)(extra_field_len); // extra field length
1201  buffer[ 29 ] = (uchar)(extra_field_len >> 8);
1202 
1203  // file name
1204  strncpy( buffer + 30, encodedName, encodedName.length() );
1205 
1206  // extra field
1207  if ( d->m_extraField == ModificationTime )
1208  {
1209  char *extfield = buffer + 30 + encodedName.length();
1210  // "Extended timestamp" header (0x5455)
1211  extfield[0] = 'U';
1212  extfield[1] = 'T';
1213  extfield[2] = 13; // data size
1214  extfield[3] = 0;
1215  extfield[4] = 1 | 2 | 4; // contains mtime, atime, ctime
1216 
1217  extfield[5] = char(mtime);
1218  extfield[6] = char(mtime >> 8);
1219  extfield[7] = char(mtime >> 16);
1220  extfield[8] = char(mtime >> 24);
1221 
1222  extfield[9] = char(atime);
1223  extfield[10] = char(atime >> 8);
1224  extfield[11] = char(atime >> 16);
1225  extfield[12] = char(atime >> 24);
1226 
1227  extfield[13] = char(ctime);
1228  extfield[14] = char(ctime >> 8);
1229  extfield[15] = char(ctime >> 16);
1230  extfield[16] = char(ctime >> 24);
1231  }
1232 
1233  // Write header
1234  bool b = (device()->writeBlock( buffer, bufferSize ) == bufferSize );
1235  d->m_crc = 0L;
1236  delete[] buffer;
1237 
1238  Q_ASSERT( b );
1239  if (!b) {
1240  abort();
1241  return false;
1242  }
1243 
1244  // Prepare device for writing the data
1245  // Either device() if no compression, or a KFilterDev to compress
1246  if ( d->m_compression == 0 ) {
1247  d->m_currentDev = device();
1248  return true;
1249  }
1250 
1251  d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
1252  Q_ASSERT( d->m_currentDev );
1253  if ( !d->m_currentDev ) {
1254  abort();
1255  return false; // ouch
1256  }
1257  static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders(); // Just zlib, not gzip
1258 
1259  b = d->m_currentDev->open( IO_WriteOnly );
1260  Q_ASSERT( b );
1261  return b;
1262 }
1263 
1264 bool KZip::doneWriting( uint size )
1265 {
1266  if ( d->m_currentFile->encoding() == 8 ) {
1267  // Finish
1268  (void)d->m_currentDev->writeBlock( 0, 0 );
1269  delete d->m_currentDev;
1270  }
1271  // If 0, d->m_currentDev was device() - don't delete ;)
1272  d->m_currentDev = 0L;
1273 
1274  Q_ASSERT( d->m_currentFile );
1275  //kdDebug(7040) << "donewriting reached." << endl;
1276  //kdDebug(7040) << "filename: " << d->m_currentFile->path() << endl;
1277  //kdDebug(7040) << "getpos (at): " << device()->at() << endl;
1278  d->m_currentFile->setSize(size);
1279  int extra_field_len = 0;
1280  if ( d->m_extraField == ModificationTime )
1281  extra_field_len = 17; // value also used in doneWriting()
1282 
1283  int csize = device()->at() -
1284  d->m_currentFile->headerStart() - 30 -
1285  d->m_currentFile->path().length() - extra_field_len;
1286  d->m_currentFile->setCompressedSize(csize);
1287  //kdDebug(7040) << "usize: " << d->m_currentFile->size() << endl;
1288  //kdDebug(7040) << "csize: " << d->m_currentFile->compressedSize() << endl;
1289  //kdDebug(7040) << "headerstart: " << d->m_currentFile->headerStart() << endl;
1290 
1291  //kdDebug(7040) << "crc: " << d->m_crc << endl;
1292  d->m_currentFile->setCRC32( d->m_crc );
1293 
1294  d->m_currentFile = 0L;
1295 
1296  // update saved offset for appending new files
1297  d->m_offset = device()->at();
1298  return true;
1299 }
1300 
1301 bool KZip::writeSymLink(const TQString &name, const TQString &target,
1302  const TQString &user, const TQString &group,
1303  mode_t perm, time_t atime, time_t mtime, time_t ctime) {
1304  return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime);
1305 }
1306 
1307 bool KZip::writeSymLink_impl(const TQString &name, const TQString &target,
1308  const TQString &user, const TQString &group,
1309  mode_t perm, time_t atime, time_t mtime, time_t ctime) {
1310 
1311  // reassure that symlink flag is set, otherwise strange things happen on
1312  // extraction
1313  perm |= S_IFLNK;
1314  Compression c = compression();
1315  setCompression(NoCompression); // link targets are never compressed
1316 
1317  if (!prepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) {
1318  kdWarning() << "KZip::writeFile prepareWriting failed" << endl;
1319  setCompression(c);
1320  return false;
1321  }
1322 
1323  TQCString symlink_target = TQFile::encodeName(target);
1324  if (!writeData(symlink_target, symlink_target.length())) {
1325  kdWarning() << "KZip::writeFile writeData failed" << endl;
1326  setCompression(c);
1327  return false;
1328  }
1329 
1330  if (!doneWriting(symlink_target.length())) {
1331  kdWarning() << "KZip::writeFile doneWriting failed" << endl;
1332  setCompression(c);
1333  return false;
1334  }
1335 
1336  setCompression(c);
1337  return true;
1338 }
1339 
1340 void KZip::virtual_hook( int id, void* data )
1341 {
1342  switch (id) {
1343  case VIRTUAL_WRITE_DATA: {
1344  WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data);
1345  params->retval = writeData_impl( params->data, params->size );
1346  break;
1347  }
1348  case VIRTUAL_WRITE_SYMLINK: {
1349  WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
1350  params->retval = writeSymLink_impl(*params->name,*params->target,
1351  *params->user,*params->group,params->perm,
1352  params->atime,params->mtime,params->ctime);
1353  break;
1354  }
1355  case VIRTUAL_PREPARE_WRITING: {
1356  PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
1357  params->retval = prepareWriting_impl(*params->name,*params->user,
1358  *params->group,params->size,params->perm,
1359  params->atime,params->mtime,params->ctime);
1360  break;
1361  }
1362  default:
1363  KArchive::virtual_hook( id, data );
1364  }/*end switch*/
1365 }
1366 
1367 // made virtual using virtual_hook
1368 bool KZip::writeData(const char * c, uint i)
1369 {
1370  return KArchive::writeData( c, i );
1371 }
1372 
1373 bool KZip::writeData_impl(const char * c, uint i)
1374 {
1375  Q_ASSERT( d->m_currentFile );
1376  Q_ASSERT( d->m_currentDev );
1377  if (!d->m_currentFile || !d->m_currentDev) {
1378  abort();
1379  return false;
1380  }
1381 
1382  // crc to be calculated over uncompressed stuff...
1383  // and they didn't mention it in their docs...
1384  d->m_crc = crc32(d->m_crc, (const Bytef *) c , i);
1385 
1386  TQ_LONG written = d->m_currentDev->writeBlock( c, i );
1387  //kdDebug(7040) << "KZip::writeData wrote " << i << " bytes." << endl;
1388  bool ok = written == (TQ_LONG)i;
1389  if ( !ok )
1390  abort();
1391  return ok;
1392 }
1393 
1394 void KZip::setCompression( Compression c )
1395 {
1396  d->m_compression = ( c == NoCompression ) ? 0 : 8;
1397 }
1398 
1399 KZip::Compression KZip::compression() const
1400 {
1401  return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
1402 }
1403 
1404 void KZip::setExtraField( ExtraField ef )
1405 {
1406  d->m_extraField = ef;
1407 }
1408 
1409 KZip::ExtraField KZip::extraField() const
1410 {
1411  return d->m_extraField;
1412 }
1413 
1414 void KZip::abort()
1415 {
1416  if ( d->m_saveFile ) {
1417  d->m_saveFile->abort();
1418  setDevice( 0 );
1419  }
1420 }
1421 
1422 
1424 
1425 TQByteArray KZipFileEntry::data() const
1426 {
1427  TQIODevice* dev = device();
1428  TQByteArray arr;
1429  if ( dev ) {
1430  arr = dev->readAll();
1431  delete dev;
1432  }
1433  return arr;
1434 }
1435 
1436 TQIODevice* KZipFileEntry::device() const
1437 {
1438  //kdDebug(7040) << "KZipFileEntry::device creating iodevice limited to pos=" << position() << ", csize=" << compressedSize() << endl;
1439  // Limit the reading to the appropriate part of the underlying device (e.g. file)
1440  KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
1441  if ( encoding() == 0 || compressedSize() == 0 ) // no compression (or even no data)
1442  return limitedDev;
1443 
1444  if ( encoding() == 8 )
1445  {
1446  // On top of that, create a device that uncompresses the zlib data
1447  TQIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
1448  if ( !filterDev )
1449  return 0L; // ouch
1450  static_cast<KFilterDev *>(filterDev)->setSkipHeaders(); // Just zlib, not gzip
1451  bool b = filterDev->open( IO_ReadOnly );
1452  Q_ASSERT( b );
1453  return filterDev;
1454  }
1455 
1456  kdError() << "This zip file contains files compressed with method "
1457  << encoding() <<", this method is currently not supported by KZip,"
1458  <<" please use a command-line tool to handle this file." << endl;
1459  return 0L;
1460 }

kio/kio

Skip menu "kio/kio"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kio/kio

Skip menu "kio/kio"
  • arts
  • dcop
  • dnssd
  • interfaces
  •     interface
  •     library
  •   kspeech
  •   ktexteditor
  • kabc
  • kate
  • kcmshell
  • kdecore
  • kded
  • kdefx
  • kdeprint
  • kdesu
  • kdeui
  • kdoctools
  • khtml
  • kimgio
  • kinit
  • kio
  •   bookmarks
  •   httpfilter
  •   kfile
  •   kio
  •   kioexec
  •   kpasswdserver
  •   kssl
  • kioslave
  •   http
  • kjs
  • kmdi
  •   kmdi
  • knewstuff
  • kparts
  • krandr
  • kresources
  • kspell2
  • kunittest
  • kutils
  • kwallet
  • libkmid
  • libkscreensaver
Generated for kio/kio by doxygen 1.8.3.1
This website is maintained by Timothy Pearson.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. |