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

ark

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