• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdeutils API Reference
  • KDE Home
  • Contact Us
 

ark

  • sources
  • kde-4.12
  • kdeutils
  • ark
  • plugins
  • libarchive
libarchivehandler.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
3  * Copyright (c) 2008-2009 Harald Hvaal <haraldhv@stud.ntnu.no>
4  * Copyright (c) 2010 Raphael Kubo da Costa <rakuco@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <config.h>
29 
30 #include "libarchivehandler.h"
31 #include "kerfuffle/kerfuffle_export.h"
32 #include "kerfuffle/queries.h"
33 
34 #include <archive.h>
35 #include <archive_entry.h>
36 
37 #include <KDebug>
38 #include <KLocale>
39 #include <kde_file.h>
40 
41 #include <QDateTime>
42 #include <QDir>
43 #include <QDirIterator>
44 #include <QFile>
45 #include <QList>
46 #include <QStringList>
47 
48 struct LibArchiveInterface::ArchiveReadCustomDeleter
49 {
50  static inline void cleanup(struct archive *a)
51  {
52  if (a) {
53  archive_read_finish(a);
54  }
55  }
56 };
57 
58 struct LibArchiveInterface::ArchiveWriteCustomDeleter
59 {
60  static inline void cleanup(struct archive *a)
61  {
62  if (a) {
63  archive_write_finish(a);
64  }
65  }
66 };
67 
68 LibArchiveInterface::LibArchiveInterface(QObject *parent, const QVariantList & args)
69  : ReadWriteArchiveInterface(parent, args)
70  , m_cachedArchiveEntryCount(0)
71  , m_emitNoEntries(false)
72  , m_extractedFilesSize(0)
73  , m_workDir(QDir::current())
74  , m_archiveReadDisk(archive_read_disk_new())
75 {
76  archive_read_disk_set_standard_lookup(m_archiveReadDisk.data());
77 }
78 
79 LibArchiveInterface::~LibArchiveInterface()
80 {
81 }
82 
83 bool LibArchiveInterface::list()
84 {
85  kDebug();
86 
87  ArchiveRead arch_reader(archive_read_new());
88 
89  if (!(arch_reader.data())) {
90  return false;
91  }
92 
93  if (archive_read_support_compression_all(arch_reader.data()) != ARCHIVE_OK) {
94  return false;
95  }
96 
97  if (archive_read_support_format_all(arch_reader.data()) != ARCHIVE_OK) {
98  return false;
99  }
100 
101  if (archive_read_open_filename(arch_reader.data(), QFile::encodeName(filename()), 10240) != ARCHIVE_OK) {
102  emit error(i18nc("@info", "Could not open the archive <filename>%1</filename>, libarchive cannot handle it.",
103  filename()));
104  return false;
105  }
106 
107  m_cachedArchiveEntryCount = 0;
108  m_extractedFilesSize = 0;
109 
110  struct archive_entry *aentry;
111  int result;
112 
113  while ((result = archive_read_next_header(arch_reader.data(), &aentry)) == ARCHIVE_OK) {
114  if (!m_emitNoEntries) {
115  emitEntryFromArchiveEntry(aentry);
116  }
117 
118  m_extractedFilesSize += (qlonglong)archive_entry_size(aentry);
119 
120  m_cachedArchiveEntryCount++;
121  archive_read_data_skip(arch_reader.data());
122  }
123 
124  if (result != ARCHIVE_EOF) {
125  emit error(i18nc("@info", "The archive reading failed with the following error: <message>%1</message>",
126  QLatin1String( archive_error_string(arch_reader.data()))));
127  return false;
128  }
129 
130  return archive_read_close(arch_reader.data()) == ARCHIVE_OK;
131 }
132 
133 bool LibArchiveInterface::copyFiles(const QVariantList& files, const QString& destinationDirectory, ExtractionOptions options)
134 {
135  kDebug() << "Changing current directory to " << destinationDirectory;
136  QDir::setCurrent(destinationDirectory);
137 
138  const bool extractAll = files.isEmpty();
139  const bool preservePaths = options.value(QLatin1String( "PreservePaths" )).toBool();
140 
141  QString rootNode = options.value(QLatin1String("RootNode"), QVariant()).toString();
142  if ((!rootNode.isEmpty()) && (!rootNode.endsWith(QLatin1Char('/')))) {
143  rootNode.append(QLatin1Char('/'));
144  }
145 
146  ArchiveRead arch(archive_read_new());
147 
148  if (!(arch.data())) {
149  return false;
150  }
151 
152  if (archive_read_support_compression_all(arch.data()) != ARCHIVE_OK) {
153  return false;
154  }
155 
156  if (archive_read_support_format_all(arch.data()) != ARCHIVE_OK) {
157  return false;
158  }
159 
160  if (archive_read_open_filename(arch.data(), QFile::encodeName(filename()), 10240) != ARCHIVE_OK) {
161  emit error(i18nc("@info", "Could not open the archive <filename>%1</filename>, libarchive cannot handle it.",
162  filename()));
163  return false;
164  }
165 
166  ArchiveWrite writer(archive_write_disk_new());
167  if (!(writer.data())) {
168  return false;
169  }
170 
171  archive_write_disk_set_options(writer.data(), extractionFlags());
172 
173  int entryNr = 0;
174  int totalCount = 0;
175 
176  if (extractAll) {
177  if (!m_cachedArchiveEntryCount) {
178  emit progress(0);
179  //TODO: once information progress has been implemented, send
180  //feedback here that the archive is being read
181  kDebug() << "For getting progress information, the archive will be listed once";
182  m_emitNoEntries = true;
183  list();
184  m_emitNoEntries = false;
185  }
186  totalCount = m_cachedArchiveEntryCount;
187  } else {
188  totalCount = files.size();
189  }
190 
191  m_currentExtractedFilesSize = 0;
192 
193  bool overwriteAll = false; // Whether to overwrite all files
194  bool skipAll = false; // Whether to skip all files
195  struct archive_entry *entry;
196 
197  QString fileBeingRenamed;
198 
199  while (archive_read_next_header(arch.data(), &entry) == ARCHIVE_OK) {
200  fileBeingRenamed.clear();
201 
202  // retry with renamed entry, fire an overwrite query again
203  // if the new entry also exists
204  retry:
205  const bool entryIsDir = S_ISDIR(archive_entry_mode(entry));
206 
207  //we skip directories if not preserving paths
208  if (!preservePaths && entryIsDir) {
209  archive_read_data_skip(arch.data());
210  continue;
211  }
212 
213  //entryName is the name inside the archive, full path
214  QString entryName = QDir::fromNativeSeparators(QFile::decodeName(archive_entry_pathname(entry)));
215 
216  if (entryName.startsWith(QLatin1Char( '/' ))) {
217  //for now we just can't handle absolute filenames in a tar archive.
218  //TODO: find out what to do here!!
219  emit error(i18n("This archive contains archive entries with absolute paths, which are not yet supported by ark."));
220 
221  return false;
222  }
223 
224  if (files.contains(entryName) || entryName == fileBeingRenamed || extractAll) {
225  // entryFI is the fileinfo pointing to where the file will be
226  // written from the archive
227  QFileInfo entryFI(entryName);
228  //kDebug() << "setting path to " << archive_entry_pathname( entry );
229 
230  const QString fileWithoutPath(entryFI.fileName());
231 
232  //if we DON'T preserve paths, we cut the path and set the entryFI
233  //fileinfo to the one without the path
234  if (!preservePaths) {
235  //empty filenames (ie dirs) should have been skipped already,
236  //so asserting
237  Q_ASSERT(!fileWithoutPath.isEmpty());
238 
239  archive_entry_copy_pathname(entry, QFile::encodeName(fileWithoutPath).constData());
240  entryFI = QFileInfo(fileWithoutPath);
241 
242  //OR, if the commonBase has been set, then we remove this
243  //common base from the filename
244  } else if (!rootNode.isEmpty()) {
245  kDebug() << "Removing" << rootNode << "from" << entryName;
246 
247  const QString truncatedFilename(entryName.remove(0, rootNode.size()));
248  archive_entry_copy_pathname(entry, QFile::encodeName(truncatedFilename).constData());
249 
250  entryFI = QFileInfo(truncatedFilename);
251  }
252 
253  //now check if the file about to be written already exists
254  if (!entryIsDir && entryFI.exists()) {
255  if (skipAll) {
256  archive_read_data_skip(arch.data());
257  archive_entry_clear(entry);
258  continue;
259  } else if (!overwriteAll && !skipAll) {
260  Kerfuffle::OverwriteQuery query(entryName);
261  emit userQuery(&query);
262  query.waitForResponse();
263 
264  if (query.responseCancelled()) {
265  archive_read_data_skip(arch.data());
266  archive_entry_clear(entry);
267  break;
268  } else if (query.responseSkip()) {
269  archive_read_data_skip(arch.data());
270  archive_entry_clear(entry);
271  continue;
272  } else if (query.responseAutoSkip()) {
273  archive_read_data_skip(arch.data());
274  archive_entry_clear(entry);
275  skipAll = true;
276  continue;
277  } else if (query.responseRename()) {
278  const QString newName(query.newFilename());
279  fileBeingRenamed = newName;
280  archive_entry_copy_pathname(entry, QFile::encodeName(newName).constData());
281  goto retry;
282  } else if (query.responseOverwriteAll()) {
283  overwriteAll = true;
284  }
285  }
286  }
287 
288  //if there is an already existing directory:
289  if (entryIsDir && entryFI.exists()) {
290  if (entryFI.isWritable()) {
291  kDebug(1601) << "Warning, existing, but writable dir";
292  } else {
293  kDebug(1601) << "Warning, existing, but non-writable dir. skipping";
294  archive_entry_clear(entry);
295  archive_read_data_skip(arch.data());
296  continue;
297  }
298  }
299 
300  int header_response;
301  kDebug() << "Writing " << fileWithoutPath << " to " << archive_entry_pathname(entry);
302  if ((header_response = archive_write_header(writer.data(), entry)) == ARCHIVE_OK) {
303  //if the whole archive is extracted and the total filesize is
304  //available, we use partial progress
305  copyData(arch.data(), writer.data(), (extractAll && m_extractedFilesSize));
306  } else if (header_response == ARCHIVE_WARN) {
307  kDebug() << "Warning while writing " << entryName;
308  } else {
309  kDebug() << "Writing header failed with error code " << header_response
310  << "While attempting to write " << entryName;
311  }
312 
313  //if we only partially extract the archive and the number of
314  //archive entries is available we use a simple progress based on
315  //number of items extracted
316  if (!extractAll && m_cachedArchiveEntryCount) {
317  ++entryNr;
318  emit progress(float(entryNr) / totalCount);
319  }
320  archive_entry_clear(entry);
321  } else {
322  archive_read_data_skip(arch.data());
323  }
324  }
325 
326  return archive_read_close(arch.data()) == ARCHIVE_OK;
327 }
328 
329 bool LibArchiveInterface::addFiles(const QStringList& files, const CompressionOptions& options)
330 {
331  const bool creatingNewFile = !QFileInfo(filename()).exists();
332  const QString tempFilename = filename() + QLatin1String( ".arkWriting" );
333  const QString globalWorkDir = options.value(QLatin1String( "GlobalWorkDir" )).toString();
334 
335  if (!globalWorkDir.isEmpty()) {
336  kDebug() << "GlobalWorkDir is set, changing dir to " << globalWorkDir;
337  m_workDir.setPath(globalWorkDir);
338  QDir::setCurrent(globalWorkDir);
339  }
340 
341  m_writtenFiles.clear();
342 
343  ArchiveRead arch_reader;
344  if (!creatingNewFile) {
345  arch_reader.reset(archive_read_new());
346  if (!(arch_reader.data())) {
347  emit error(i18n("The archive reader could not be initialized."));
348  return false;
349  }
350 
351  if (archive_read_support_compression_all(arch_reader.data()) != ARCHIVE_OK) {
352  return false;
353  }
354 
355  if (archive_read_support_format_all(arch_reader.data()) != ARCHIVE_OK) {
356  return false;
357  }
358 
359  if (archive_read_open_filename(arch_reader.data(), QFile::encodeName(filename()), 10240) != ARCHIVE_OK) {
360  emit error(i18n("The source file could not be read."));
361  return false;
362  }
363  }
364 
365  ArchiveWrite arch_writer(archive_write_new());
366  if (!(arch_writer.data())) {
367  emit error(i18n("The archive writer could not be initialized."));
368  return false;
369  }
370 
371  //pax_restricted is the libarchive default, let's go with that.
372  archive_write_set_format_pax_restricted(arch_writer.data());
373 
374  int ret;
375  if (creatingNewFile) {
376  if (filename().right(2).toUpper() == QLatin1String( "GZ" )) {
377  kDebug() << "Detected gzip compression for new file";
378  ret = archive_write_set_compression_gzip(arch_writer.data());
379  } else if (filename().right(3).toUpper() == QLatin1String( "BZ2" )) {
380  kDebug() << "Detected bzip2 compression for new file";
381  ret = archive_write_set_compression_bzip2(arch_writer.data());
382 #ifdef HAVE_LIBARCHIVE_XZ_SUPPORT
383  } else if (filename().right(2).toUpper() == QLatin1String( "XZ" )) {
384  kDebug() << "Detected xz compression for new file";
385  ret = archive_write_set_compression_xz(arch_writer.data());
386 #endif
387 #ifdef HAVE_LIBARCHIVE_LZMA_SUPPORT
388  } else if (filename().right(4).toUpper() == QLatin1String( "LZMA" )) {
389  kDebug() << "Detected lzma compression for new file";
390  ret = archive_write_set_compression_lzma(arch_writer.data());
391 #endif
392  } else if (filename().right(3).toUpper() == QLatin1String( "TAR" )) {
393  kDebug() << "Detected no compression for new file (pure tar)";
394  ret = archive_write_set_compression_none(arch_writer.data());
395  } else {
396  kDebug() << "Falling back to gzip";
397  ret = archive_write_set_compression_gzip(arch_writer.data());
398  }
399 
400  if (ret != ARCHIVE_OK) {
401  emit error(i18nc("@info", "Setting the compression method failed with the following error: <message>%1</message>",
402  QLatin1String(archive_error_string(arch_writer.data()))));
403 
404  return false;
405  }
406  } else {
407  switch (archive_compression(arch_reader.data())) {
408  case ARCHIVE_COMPRESSION_GZIP:
409  ret = archive_write_set_compression_gzip(arch_writer.data());
410  break;
411  case ARCHIVE_COMPRESSION_BZIP2:
412  ret = archive_write_set_compression_bzip2(arch_writer.data());
413  break;
414 #ifdef HAVE_LIBARCHIVE_XZ_SUPPORT
415  case ARCHIVE_COMPRESSION_XZ:
416  ret = archive_write_set_compression_xz(arch_writer.data());
417  break;
418 #endif
419 #ifdef HAVE_LIBARCHIVE_LZMA_SUPPORT
420  case ARCHIVE_COMPRESSION_LZMA:
421  ret = archive_write_set_compression_lzma(arch_writer.data());
422  break;
423 #endif
424  case ARCHIVE_COMPRESSION_NONE:
425  ret = archive_write_set_compression_none(arch_writer.data());
426  break;
427  default:
428  emit error(i18n("The compression type '%1' is not supported by Ark.", QLatin1String(archive_compression_name(arch_reader.data()))));
429  return false;
430  }
431 
432  if (ret != ARCHIVE_OK) {
433  emit error(i18nc("@info", "Setting the compression method failed with the following error: <message>%1</message>", QLatin1String(archive_error_string(arch_writer.data()))));
434  return false;
435  }
436  }
437 
438  ret = archive_write_open_filename(arch_writer.data(), QFile::encodeName(tempFilename));
439  if (ret != ARCHIVE_OK) {
440  emit error(i18nc("@info", "Opening the archive for writing failed with the following error: <message>%1</message>", QLatin1String(archive_error_string(arch_writer.data()))));
441  return false;
442  }
443 
444  //**************** first write the new files
445  foreach(const QString& selectedFile, files) {
446  bool success;
447 
448  success = writeFile(selectedFile, arch_writer.data());
449 
450  if (!success) {
451  QFile::remove(tempFilename);
452  return false;
453  }
454 
455  if (QFileInfo(selectedFile).isDir()) {
456  QDirIterator it(selectedFile,
457  QDir::AllEntries | QDir::Readable |
458  QDir::Hidden | QDir::NoDotAndDotDot,
459  QDirIterator::Subdirectories);
460 
461  while (it.hasNext()) {
462  const QString path = it.next();
463 
464  if ((it.fileName() == QLatin1String("..")) ||
465  (it.fileName() == QLatin1String("."))) {
466  continue;
467  }
468 
469  success = writeFile(path +
470  (it.fileInfo().isDir() ? QLatin1String( "/" ) : QLatin1String( "" )),
471  arch_writer.data());
472 
473  if (!success) {
474  QFile::remove(tempFilename);
475  return false;
476  }
477  }
478  }
479  }
480 
481  struct archive_entry *entry;
482 
483  //and if we have old elements...
484  if (!creatingNewFile) {
485  //********** copy old elements from previous archive to new archive
486  while (archive_read_next_header(arch_reader.data(), &entry) == ARCHIVE_OK) {
487  if (m_writtenFiles.contains(QFile::decodeName(archive_entry_pathname(entry)))) {
488  archive_read_data_skip(arch_reader.data());
489  kDebug() << "Entry already existing, will be refresh: ===> " << archive_entry_pathname(entry);
490  continue;
491  }
492 
493  int header_response;
494  //kDebug() << "Writing entry " << fn;
495  if ((header_response = archive_write_header(arch_writer.data(), entry)) == ARCHIVE_OK) {
496  //if the whole archive is extracted and the total filesize is
497  //available, we use partial progress
498  copyData(arch_reader.data(), arch_writer.data(), false);
499  } else {
500  kDebug() << "Writing header failed with error code " << header_response;
501  QFile::remove(tempFilename);
502  return false;
503  }
504 
505  archive_entry_clear(entry);
506  }
507 
508  //everything seems OK, so we remove the source file and replace it with
509  //the new one.
510  //TODO: do some extra checks to see if this is really OK
511  QFile::remove(filename());
512  }
513 
514  QFile::rename(tempFilename, filename());
515 
516  return true;
517 }
518 
519 bool LibArchiveInterface::deleteFiles(const QVariantList& files)
520 {
521  const QString tempFilename = filename() + QLatin1String( ".arkWriting" );
522 
523  ArchiveRead arch_reader(archive_read_new());
524  if (!(arch_reader.data())) {
525  emit error(i18n("The archive reader could not be initialized."));
526  return false;
527  }
528 
529  if (archive_read_support_compression_all(arch_reader.data()) != ARCHIVE_OK) {
530  return false;
531  }
532 
533  if (archive_read_support_format_all(arch_reader.data()) != ARCHIVE_OK) {
534  return false;
535  }
536 
537  if (archive_read_open_filename(arch_reader.data(), QFile::encodeName(filename()), 10240) != ARCHIVE_OK) {
538  emit error(i18n("The source file could not be read."));
539  return false;
540  }
541 
542  ArchiveWrite arch_writer(archive_write_new());
543  if (!(arch_writer.data())) {
544  emit error(i18n("The archive writer could not be initialized."));
545  return false;
546  }
547 
548  //pax_restricted is the libarchive default, let's go with that.
549  archive_write_set_format_pax_restricted(arch_writer.data());
550 
551  int ret;
552  switch (archive_compression(arch_reader.data())) {
553  case ARCHIVE_COMPRESSION_GZIP:
554  ret = archive_write_set_compression_gzip(arch_writer.data());
555  break;
556  case ARCHIVE_COMPRESSION_BZIP2:
557  ret = archive_write_set_compression_bzip2(arch_writer.data());
558  break;
559 #ifdef HAVE_LIBARCHIVE_XZ_SUPPORT
560  case ARCHIVE_COMPRESSION_XZ:
561  ret = archive_write_set_compression_xz(arch_writer.data());
562  break;
563 #endif
564 #ifdef HAVE_LIBARCHIVE_LZMA_SUPPORT
565  case ARCHIVE_COMPRESSION_LZMA:
566  ret = archive_write_set_compression_lzma(arch_writer.data());
567  break;
568 #endif
569  case ARCHIVE_COMPRESSION_NONE:
570  ret = archive_write_set_compression_none(arch_writer.data());
571  break;
572  default:
573  emit error(i18n("The compression type '%1' is not supported by Ark.", QLatin1String(archive_compression_name(arch_reader.data()))));
574  return false;
575  }
576 
577  if (ret != ARCHIVE_OK) {
578  emit error(i18nc("@info", "Setting the compression method failed with the following error: <message>%1</message>", QLatin1String(archive_error_string(arch_writer.data()))));
579  return false;
580  }
581 
582  ret = archive_write_open_filename(arch_writer.data(), QFile::encodeName(tempFilename));
583  if (ret != ARCHIVE_OK) {
584  emit error(i18nc("@info", "Opening the archive for writing failed with the following error: <message>%1</message>", QLatin1String(archive_error_string(arch_writer.data()))));
585  return false;
586  }
587 
588  struct archive_entry *entry;
589 
590  //********** copy old elements from previous archive to new archive
591  while (archive_read_next_header(arch_reader.data(), &entry) == ARCHIVE_OK) {
592  if (files.contains(QFile::decodeName(archive_entry_pathname(entry)))) {
593  archive_read_data_skip(arch_reader.data());
594  kDebug() << "Entry to be deleted, skipping"
595  << archive_entry_pathname(entry);
596  emit entryRemoved(QFile::decodeName(archive_entry_pathname(entry)));
597  continue;
598  }
599 
600  int header_response;
601  //kDebug() << "Writing entry " << fn;
602  if ((header_response = archive_write_header(arch_writer.data(), entry)) == ARCHIVE_OK) {
603  //if the whole archive is extracted and the total filesize is
604  //available, we use partial progress
605  copyData(arch_reader.data(), arch_writer.data(), false);
606  } else {
607  kDebug() << "Writing header failed with error code " << header_response;
608  return false;
609  }
610  }
611 
612  //everything seems OK, so we remove the source file and replace it with
613  //the new one.
614  //TODO: do some extra checks to see if this is really OK
615  QFile::remove(filename());
616  QFile::rename(tempFilename, filename());
617 
618  return true;
619 }
620 
621 void LibArchiveInterface::emitEntryFromArchiveEntry(struct archive_entry *aentry)
622 {
623  ArchiveEntry e;
624 
625 #ifdef _MSC_VER
626  e[FileName] = QDir::fromNativeSeparators(QString::fromUtf16((ushort*)archive_entry_pathname_w(aentry)));
627 #else
628  e[FileName] = QDir::fromNativeSeparators(QString::fromWCharArray(archive_entry_pathname_w(aentry)));
629 #endif
630  e[InternalID] = e[FileName];
631 
632  const QString owner = QString::fromAscii(archive_entry_uname(aentry));
633  if (!owner.isEmpty()) {
634  e[Owner] = owner;
635  }
636 
637  const QString group = QString::fromAscii(archive_entry_gname(aentry));
638  if (!group.isEmpty()) {
639  e[Group] = group;
640  }
641 
642  e[Size] = (qlonglong)archive_entry_size(aentry);
643  e[IsDirectory] = S_ISDIR(archive_entry_mode(aentry));
644 
645  if (archive_entry_symlink(aentry)) {
646  e[Link] = QLatin1String( archive_entry_symlink(aentry) );
647  }
648 
649  e[Timestamp] = QDateTime::fromTime_t(archive_entry_mtime(aentry));
650 
651  emit entry(e);
652 }
653 
654 int LibArchiveInterface::extractionFlags() const
655 {
656  int result = ARCHIVE_EXTRACT_TIME;
657  result |= ARCHIVE_EXTRACT_SECURE_NODOTDOT;
658 
659  // TODO: Don't use arksettings here
660  /*if ( ArkSettings::preservePerms() )
661  {
662  result &= ARCHIVE_EXTRACT_PERM;
663  }
664 
665  if ( !ArkSettings::extractOverwrite() )
666  {
667  result &= ARCHIVE_EXTRACT_NO_OVERWRITE;
668  }*/
669 
670  return result;
671 }
672 
673 void LibArchiveInterface::copyData(const QString& filename, struct archive *dest, bool partialprogress)
674 {
675  char buff[10240];
676  ssize_t readBytes;
677  QFile file(filename);
678 
679  if (!file.open(QIODevice::ReadOnly)) {
680  return;
681  }
682 
683  readBytes = file.read(buff, sizeof(buff));
684  while (readBytes > 0) {
685  /* int writeBytes = */
686  archive_write_data(dest, buff, readBytes);
687  if (archive_errno(dest) != ARCHIVE_OK) {
688  kDebug() << "Error while writing..." << archive_error_string(dest) << "(error nb =" << archive_errno(dest) << ')';
689  return;
690  }
691 
692  if (partialprogress) {
693  m_currentExtractedFilesSize += readBytes;
694  emit progress(float(m_currentExtractedFilesSize) / m_extractedFilesSize);
695  }
696 
697  readBytes = file.read(buff, sizeof(buff));
698  }
699 
700  file.close();
701 }
702 
703 void LibArchiveInterface::copyData(struct archive *source, struct archive *dest, bool partialprogress)
704 {
705  char buff[10240];
706  ssize_t readBytes;
707 
708  readBytes = archive_read_data(source, buff, sizeof(buff));
709  while (readBytes > 0) {
710  /* int writeBytes = */
711  archive_write_data(dest, buff, readBytes);
712  if (archive_errno(dest) != ARCHIVE_OK) {
713  kDebug() << "Error while extracting..." << archive_error_string(dest) << "(error nb =" << archive_errno(dest) << ')';
714  return;
715  }
716 
717  if (partialprogress) {
718  m_currentExtractedFilesSize += readBytes;
719  emit progress(float(m_currentExtractedFilesSize) / m_extractedFilesSize);
720  }
721 
722  readBytes = archive_read_data(source, buff, sizeof(buff));
723  }
724 }
725 
726 // TODO: if we merge this with copyData(), we can pass more data
727 // such as an fd to archive_read_disk_entry_from_file()
728 bool LibArchiveInterface::writeFile(const QString& fileName, struct archive* arch_writer)
729 {
730  int header_response;
731 
732  const bool trailingSlash = fileName.endsWith(QLatin1Char( '/' ));
733 
734  // #191821: workDir must be used instead of QDir::current()
735  // so that symlinks aren't resolved automatically
736  // TODO: this kind of call should be moved upwards in the
737  // class hierarchy to avoid code duplication
738  const QString relativeName = m_workDir.relativeFilePath(fileName) + (trailingSlash ? QLatin1String( "/" ) : QLatin1String( "" ));
739 
740  // #253059: Even if we use archive_read_disk_entry_from_file,
741  // libarchive may have been compiled without HAVE_LSTAT,
742  // or something may have caused it to follow symlinks, in
743  // which case stat() will be called. To avoid this, we
744  // call lstat() ourselves.
745  KDE_struct_stat st;
746  KDE_lstat(QFile::encodeName(fileName).constData(), &st);
747 
748  struct archive_entry *entry = archive_entry_new();
749  archive_entry_set_pathname(entry, QFile::encodeName(relativeName).constData());
750  archive_entry_copy_sourcepath(entry, QFile::encodeName(fileName).constData());
751  archive_read_disk_entry_from_file(m_archiveReadDisk.data(), entry, -1, &st);
752 
753  kDebug() << "Writing new entry " << archive_entry_pathname(entry);
754  if ((header_response = archive_write_header(arch_writer, entry)) == ARCHIVE_OK) {
755  //if the whole archive is extracted and the total filesize is
756  //available, we use partial progress
757  copyData(fileName, arch_writer, false);
758  } else {
759  kDebug() << "Writing header failed with error code " << header_response;
760  kDebug() << "Error while writing..." << archive_error_string(arch_writer) << "(error nb =" << archive_errno(arch_writer) << ')';
761 
762  emit error(i18nc("@info Error in a message box",
763  "Ark could not compress <filename>%1</filename>:<nl/>%2",
764  fileName,
765  QLatin1String(archive_error_string(arch_writer))));
766 
767  archive_entry_free(entry);
768 
769  return false;
770  }
771 
772  m_writtenFiles.push_back(relativeName);
773 
774  emitEntryFromArchiveEntry(entry);
775 
776  archive_entry_free(entry);
777 
778  return true;
779 }
780 
781 KERFUFFLE_EXPORT_PLUGIN(LibArchiveInterface)
782 
783 #include "libarchivehandler.moc"
Kerfuffle::Owner
The user the entry belongs to.
Definition: archive.h:62
Kerfuffle::ReadOnlyArchiveInterface::entryRemoved
void entryRemoved(const QString &path)
Kerfuffle::IsDirectory
The entry is a directory.
Definition: archive.h:72
kerfuffle_export.h
LibArchiveInterface::list
bool list()
List archive contents.
Definition: libarchivehandler.cpp:83
libarchivehandler.h
Kerfuffle::OverwriteQuery::responseOverwriteAll
bool responseOverwriteAll()
Definition: queries.cpp:116
Kerfuffle::ReadOnlyArchiveInterface::userQuery
void userQuery(Query *query)
Kerfuffle::ReadOnlyArchiveInterface::filename
QString filename() const
Returns the filename of the archive currently being handled.
Definition: archiveinterface.cpp:48
archive.h
queries.h
LibArchiveInterface::deleteFiles
bool deleteFiles(const QVariantList &files)
Definition: libarchivehandler.cpp:519
QObject
Kerfuffle::Link
The entry is a symbolic link.
Definition: archive.h:66
Kerfuffle::Query::waitForResponse
void waitForResponse()
Will block until the response have been set.
Definition: queries.cpp:50
Kerfuffle::ReadOnlyArchiveInterface::entry
void entry(const ArchiveEntry &archiveEntry)
Kerfuffle::ArchiveEntry
QHash< int, QVariant > ArchiveEntry
Definition: archive.h:78
Kerfuffle::ReadWriteArchiveInterface
Definition: archiveinterface.h:121
Kerfuffle::OverwriteQuery::responseCancelled
bool responseCancelled()
Definition: queries.cpp:112
LibArchiveInterface::addFiles
bool addFiles(const QStringList &files, const CompressionOptions &options)
Definition: libarchivehandler.cpp:329
Kerfuffle::OverwriteQuery::responseSkip
bool responseSkip()
Definition: queries.cpp:130
Kerfuffle::CompressionOptions
QHash< QString, QVariant > CompressionOptions
These are the extra options for doing the compression.
Definition: archive.h:85
Kerfuffle::Timestamp
The timestamp for the current entry.
Definition: archive.h:71
Kerfuffle::FileName
The entry's file name.
Definition: archive.h:59
Kerfuffle::OverwriteQuery::newFilename
QString newFilename()
Definition: queries.cpp:140
LibArchiveInterface::LibArchiveInterface
LibArchiveInterface(QObject *parent, const QVariantList &args)
Definition: libarchivehandler.cpp:68
LibArchiveInterface::~LibArchiveInterface
~LibArchiveInterface()
Definition: libarchivehandler.cpp:79
LibArchiveInterface
Definition: libarchivehandler.h:39
LibArchiveInterface::copyFiles
bool copyFiles(const QVariantList &files, const QString &destinationDirectory, ExtractionOptions options)
Definition: libarchivehandler.cpp:133
Kerfuffle::ExtractionOptions
QHash< QString, QVariant > ExtractionOptions
Definition: archive.h:86
Kerfuffle::ReadOnlyArchiveInterface::error
void error(const QString &message, const QString &details=QString())
Kerfuffle::Size
The entry's original size.
Definition: archive.h:64
Kerfuffle::OverwriteQuery
Definition: queries.h:77
Kerfuffle::ReadOnlyArchiveInterface::progress
void progress(double progress)
KERFUFFLE_EXPORT_PLUGIN
#define KERFUFFLE_EXPORT_PLUGIN(p)
Definition: kerfuffle_export.h:49
Kerfuffle::OverwriteQuery::responseRename
bool responseRename()
Definition: queries.cpp:125
Kerfuffle::Group
The user group the entry belongs to.
Definition: archive.h:63
Kerfuffle::InternalID
The entry's ID for Ark's internal manipulation.
Definition: archive.h:60
Kerfuffle::OverwriteQuery::responseAutoSkip
bool responseAutoSkip()
Definition: queries.cpp:135
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 23:08:10 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

ark

Skip menu "ark"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdeutils API Reference

Skip menu "kdeutils API Reference"
  • ark
  • filelight
  • kcalc
  • kcharselect
  • kdf
  • kfloppy
  • kgpg
  • kremotecontrol
  • ktimer
  • kwallet
  • superkaramba
  • sweeper

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal