diff --git a/plugins/libarchive/libarchiveplugin.cpp b/plugins/libarchive/libarchiveplugin.cpp index 46072b52..0e8b7aef 100644 --- a/plugins/libarchive/libarchiveplugin.cpp +++ b/plugins/libarchive/libarchiveplugin.cpp @@ -1,520 +1,517 @@ /* * Copyright (c) 2007 Henrique Pinto * Copyright (c) 2008-2009 Harald Hvaal * Copyright (c) 2010 Raphael Kubo da Costa * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "libarchiveplugin.h" #include "kerfuffle/queries.h" #include #include LibarchivePlugin::LibarchivePlugin(QObject *parent, const QVariantList & args) : ReadWriteArchiveInterface(parent, args) , m_archiveReadDisk(archive_read_disk_new()) , m_abortOperation(false) , m_cachedArchiveEntryCount(0) , m_emitNoEntries(false) , m_extractedFilesSize(0) { qCDebug(ARK) << "Initializing libarchive plugin"; archive_read_disk_set_standard_lookup(m_archiveReadDisk.data()); } LibarchivePlugin::~LibarchivePlugin() { } bool LibarchivePlugin::list() { qCDebug(ARK) << "Listing archive contents"; - ArchiveRead arch_reader(archive_read_new()); - - if (!initializeReader(arch_reader)) { + if (!initializeReader()) { return false; } - qDebug(ARK) << "Detected compression filter:" << archive_filter_name(arch_reader.data(), 0); + qDebug(ARK) << "Detected compression filter:" << archive_filter_name(m_archiveReader.data(), 0); m_cachedArchiveEntryCount = 0; m_extractedFilesSize = 0; struct archive_entry *aentry; int result = ARCHIVE_RETRY; bool firstEntry = true; - while (!m_abortOperation && (result = archive_read_next_header(arch_reader.data(), &aentry)) == ARCHIVE_OK) { + while (!m_abortOperation && (result = archive_read_next_header(m_archiveReader.data(), &aentry)) == ARCHIVE_OK) { if (firstEntry) { - qDebug(ARK) << "Detected format for first entry:" << archive_format_name(arch_reader.data()); + qDebug(ARK) << "Detected format for first entry:" << archive_format_name(m_archiveReader.data()); firstEntry = false; } if (!m_emitNoEntries) { emitEntryFromArchiveEntry(aentry); } m_extractedFilesSize += (qlonglong)archive_entry_size(aentry); m_cachedArchiveEntryCount++; - archive_read_data_skip(arch_reader.data()); + archive_read_data_skip(m_archiveReader.data()); } m_abortOperation = false; if (result != ARCHIVE_EOF) { - const QString errorString = QLatin1String(archive_error_string(arch_reader.data())); + const QString errorString = QLatin1String(archive_error_string(m_archiveReader.data())); // FIXME: what about the other archive_error_string() calls? Do they also happen to return empty strings? emit error(errorString.isEmpty() ? i18nc("@info", "Could not read until the end of the archive") : errorString); return false; } - return archive_read_close(arch_reader.data()) == ARCHIVE_OK; + return archive_read_close(m_archiveReader.data()) == ARCHIVE_OK; } bool LibarchivePlugin::addFiles(const QList &files, const Archive::Entry *destination, const CompressionOptions& options) { Q_UNUSED(files) Q_UNUSED(destination) Q_UNUSED(options) return false; } bool LibarchivePlugin::moveFiles(const QList &files, Archive::Entry *destination, const CompressionOptions &options) { Q_UNUSED(files) Q_UNUSED(destination) Q_UNUSED(options) return false; } bool LibarchivePlugin::deleteFiles(const QList &files) { Q_UNUSED(files) return false; } bool LibarchivePlugin::addComment(const QString& comment) { Q_UNUSED(comment) return false; } bool LibarchivePlugin::testArchive() { return false; } bool LibarchivePlugin::doKill() { m_abortOperation = true; return true; } bool LibarchivePlugin::copyFiles(const QList& files, const QString& destinationDirectory, const ExtractionOptions& options) { qCDebug(ARK) << "Changing current directory to " << destinationDirectory; QDir::setCurrent(destinationDirectory); const bool extractAll = files.isEmpty(); const bool preservePaths = options.value(QStringLiteral( "PreservePaths" )).toBool(); bool removeRootNode = options.value(QStringLiteral("RemoveRootNode"), QVariant()).toBool(); // To avoid traversing the entire archive when extracting a limited set of // entries, we maintain a list of remaining entries and stop when it's // empty. QStringList fullPaths = entryFullPaths(files); QStringList remainingFiles = entryFullPaths(files); - ArchiveRead arch(archive_read_new()); - - if (!initializeReader(arch)) { + if (!initializeReader()) { return false; } ArchiveWrite writer(archive_write_disk_new()); if (!writer.data()) { return false; } archive_write_disk_set_options(writer.data(), extractionFlags()); int entryNr = 0; int totalCount = 0; if (extractAll) { if (!m_cachedArchiveEntryCount) { emit progress(0); //TODO: once information progress has been implemented, send //feedback here that the archive is being read qCDebug(ARK) << "For getting progress information, the archive will be listed once"; m_emitNoEntries = true; list(); m_emitNoEntries = false; } totalCount = m_cachedArchiveEntryCount; } else { totalCount = files.size(); } qCDebug(ARK) << "Going to extract" << totalCount << "entries"; // Initialize variables. bool overwriteAll = false; // Whether to overwrite all files bool skipAll = false; // Whether to skip all files bool dontPromptErrors = false; // Whether to prompt for errors m_currentExtractedFilesSize = 0; int no_entries = 0; struct archive_entry *entry; QString fileBeingRenamed; // Iterate through all entries in archive. - while (!m_abortOperation && (archive_read_next_header(arch.data(), &entry) == ARCHIVE_OK)) { + while (!m_abortOperation && (archive_read_next_header(m_archiveReader.data(), &entry) == ARCHIVE_OK)) { if (!extractAll && remainingFiles.isEmpty()) { break; } fileBeingRenamed.clear(); int index = -1; // Retry with renamed entry, fire an overwrite query again // if the new entry also exists. retry: const bool entryIsDir = S_ISDIR(archive_entry_mode(entry)); // Skip directories if not preserving paths. if (!preservePaths && entryIsDir) { - archive_read_data_skip(arch.data()); + archive_read_data_skip(m_archiveReader.data()); continue; } // entryName is the name inside the archive, full path QString entryName = QDir::fromNativeSeparators(QFile::decodeName(archive_entry_pathname(entry))); // For now we just can't handle absolute filenames in a tar archive. // TODO: find out what to do here!! if (entryName.startsWith(QLatin1Char( '/' ))) { emit error(i18n("This archive contains archive entries with absolute paths, " "which are not supported by Ark.")); return false; } // Should the entry be extracted? if (extractAll || remainingFiles.contains(entryName) || entryName == fileBeingRenamed) { // Find the index of entry. if (entryName != fileBeingRenamed) { index = fullPaths.indexOf(entryName); } if (!extractAll && index == -1) { // If entry is not found in files, skip entry. continue; } // entryFI is the fileinfo pointing to where the file will be // written from the archive. QFileInfo entryFI(entryName); //qCDebug(ARK) << "setting path to " << archive_entry_pathname( entry ); const QString fileWithoutPath(entryFI.fileName()); // If we DON'T preserve paths, we cut the path and set the entryFI // fileinfo to the one without the path. if (!preservePaths) { // Empty filenames (ie dirs) should have been skipped already, // so asserting. Q_ASSERT(!fileWithoutPath.isEmpty()); archive_entry_copy_pathname(entry, QFile::encodeName(fileWithoutPath).constData()); entryFI = QFileInfo(fileWithoutPath); // OR, if the file has a rootNode attached, remove it from file path. } else if (!extractAll && removeRootNode && entryName != fileBeingRenamed) { const QString &rootNode = files.at(index)->rootNode; if (!rootNode.isEmpty()) { //qCDebug(ARK) << "Removing" << files.at(index).value().rootNode << "from" << entryName; const QString truncatedFilename(entryName.remove(0, rootNode.size())); archive_entry_copy_pathname(entry, QFile::encodeName(truncatedFilename).constData()); entryFI = QFileInfo(truncatedFilename); } } // Check if the file about to be written already exists. if (!entryIsDir && entryFI.exists()) { if (skipAll) { - archive_read_data_skip(arch.data()); + archive_read_data_skip(m_archiveReader.data()); archive_entry_clear(entry); continue; } else if (!overwriteAll && !skipAll) { Kerfuffle::OverwriteQuery query(entryName); emit userQuery(&query); query.waitForResponse(); if (query.responseCancelled()) { - archive_read_data_skip(arch.data()); + archive_read_data_skip(m_archiveReader.data()); archive_entry_clear(entry); break; } else if (query.responseSkip()) { - archive_read_data_skip(arch.data()); + archive_read_data_skip(m_archiveReader.data()); archive_entry_clear(entry); continue; } else if (query.responseAutoSkip()) { - archive_read_data_skip(arch.data()); + archive_read_data_skip(m_archiveReader.data()); archive_entry_clear(entry); skipAll = true; continue; } else if (query.responseRename()) { const QString newName(query.newFilename()); fileBeingRenamed = newName; archive_entry_copy_pathname(entry, QFile::encodeName(newName).constData()); goto retry; } else if (query.responseOverwriteAll()) { overwriteAll = true; } } } // If there is an already existing directory. if (entryIsDir && entryFI.exists()) { if (entryFI.isWritable()) { qCWarning(ARK) << "Warning, existing, but writable dir"; } else { qCWarning(ARK) << "Warning, existing, but non-writable dir. skipping"; archive_entry_clear(entry); - archive_read_data_skip(arch.data()); + archive_read_data_skip(m_archiveReader.data()); continue; } } // Write the entry header and check return value. const int returnCode = archive_write_header(writer.data(), entry); switch (returnCode) { case ARCHIVE_OK: // If the whole archive is extracted and the total filesize is // available, we use partial progress. - copyData(entryName, arch.data(), writer.data(), (extractAll && m_extractedFilesSize)); + copyData(entryName, m_archiveReader.data(), writer.data(), (extractAll && m_extractedFilesSize)); break; case ARCHIVE_FAILED: qCCritical(ARK) << "archive_write_header() has returned" << returnCode << "with errno" << archive_errno(writer.data()); // If they user previously decided to ignore future errors, // don't bother prompting again. if (!dontPromptErrors) { // Ask the user if he wants to continue extraction despite an error for this entry. Kerfuffle::ContinueExtractionQuery query(QLatin1String(archive_error_string(writer.data())), entryName); emit userQuery(&query); query.waitForResponse(); if (query.responseCancelled()) { emit cancelled(); return false; } dontPromptErrors = query.dontAskAgain(); } break; case ARCHIVE_FATAL: qCCritical(ARK) << "archive_write_header() has returned" << returnCode << "with errno" << archive_errno(writer.data()); emit error(xi18nc("@info", "Extraction failed at:%1", entryName)); return false; default: qCDebug(ARK) << "archive_write_header() returned" << returnCode << "which will be ignored."; break; } // If we only partially extract the archive and the number of // archive entries is available we use a simple progress based on // number of items extracted. if (!extractAll && m_cachedArchiveEntryCount) { ++entryNr; emit progress(float(entryNr) / totalCount); } - archive_entry_clear(entry); no_entries++; remainingFiles.removeOne(entryName); } else { // Archive entry not among selected files, skip it. - archive_read_data_skip(arch.data()); + archive_read_data_skip(m_archiveReader.data()); } } // While entries left to read in archive. m_abortOperation = false; qCDebug(ARK) << "Extracted" << no_entries << "entries"; - return archive_read_close(arch.data()) == ARCHIVE_OK; + return archive_read_close(m_archiveReader.data()) == ARCHIVE_OK; } -bool LibarchivePlugin::initializeReader(const LibarchivePlugin::ArchiveRead &archiveRead) +bool LibarchivePlugin::initializeReader() { - if (!(archiveRead.data())) { + m_archiveReader.reset(archive_read_new()); + + if (!(m_archiveReader.data())) { emit error(i18n("The archive reader could not be initialized.")); return false; } - if (archive_read_support_filter_all(archiveRead.data()) != ARCHIVE_OK) { + if (archive_read_support_filter_all(m_archiveReader.data()) != ARCHIVE_OK) { return false; } - if (archive_read_support_format_all(archiveRead.data()) != ARCHIVE_OK) { + if (archive_read_support_format_all(m_archiveReader.data()) != ARCHIVE_OK) { return false; } - if (archive_read_open_filename(archiveRead.data(), QFile::encodeName(filename()), 10240) != ARCHIVE_OK) { + if (archive_read_open_filename(m_archiveReader.data(), QFile::encodeName(filename()), 10240) != ARCHIVE_OK) { emit error(xi18nc("@info", "Could not open the archive %1." "Check whether you have sufficient permissions.", filename())); return false; } return true; } void LibarchivePlugin::emitEntryFromArchiveEntry(struct archive_entry *aentry) { Archive::Entry *e = new Archive::Entry(Q_NULLPTR); #ifdef _MSC_VER e->setProperty("fullPath", QDir::fromNativeSeparators(QString::fromUtf16((ushort*)archive_entry_pathname_w(aentry)))); #else e->setProperty("fullPath", QDir::fromNativeSeparators(QString::fromWCharArray(archive_entry_pathname_w(aentry)))); #endif const QString owner = QString::fromLatin1(archive_entry_uname(aentry)); if (!owner.isEmpty()) { e->setProperty("owner", owner); } const QString group = QString::fromLatin1(archive_entry_gname(aentry)); if (!group.isEmpty()) { e->setProperty("group", group); } e->compressedSizeIsSet = false; e->setProperty("size", (qlonglong)archive_entry_size(aentry)); e->setProperty("isDirectory", S_ISDIR(archive_entry_mode(aentry))); if (archive_entry_symlink(aentry)) { e->setProperty("link", QLatin1String( archive_entry_symlink(aentry) )); } e->setProperty("timestamp", QDateTime::fromTime_t(archive_entry_mtime(aentry))); emit entry(e); } int LibarchivePlugin::extractionFlags() const { int result = ARCHIVE_EXTRACT_TIME; result |= ARCHIVE_EXTRACT_SECURE_NODOTDOT; // TODO: Don't use arksettings here /*if ( ArkSettings::preservePerms() ) { result &= ARCHIVE_EXTRACT_PERM; } if ( !ArkSettings::extractOverwrite() ) { result &= ARCHIVE_EXTRACT_NO_OVERWRITE; }*/ return result; } void LibarchivePlugin::copyData(const QString& filename, struct archive *dest, bool partialprogress) { char buff[10240]; ssize_t readBytes; QFile file(filename); if (!file.open(QIODevice::ReadOnly)) { return; } readBytes = file.read(buff, sizeof(buff)); while (readBytes > 0) { archive_write_data(dest, buff, readBytes); if (archive_errno(dest) != ARCHIVE_OK) { qCCritical(ARK) << "Error while writing" << filename << ":" << archive_error_string(dest) << "(error no =" << archive_errno(dest) << ')'; return; } if (partialprogress) { m_currentExtractedFilesSize += readBytes; emit progress(float(m_currentExtractedFilesSize) / m_extractedFilesSize); } readBytes = file.read(buff, sizeof(buff)); } file.close(); } void LibarchivePlugin::copyData(const QString& filename, struct archive *source, struct archive *dest, bool partialprogress) { char buff[10240]; ssize_t readBytes; readBytes = archive_read_data(source, buff, sizeof(buff)); while (readBytes > 0) { archive_write_data(dest, buff, readBytes); if (archive_errno(dest) != ARCHIVE_OK) { qCCritical(ARK) << "Error while extracting" << filename << ":" << archive_error_string(dest) << "(error no =" << archive_errno(dest) << ')'; return; } if (partialprogress) { m_currentExtractedFilesSize += readBytes; emit progress(float(m_currentExtractedFilesSize) / m_extractedFilesSize); } readBytes = archive_read_data(source, buff, sizeof(buff)); } } #include "libarchiveplugin.moc" diff --git a/plugins/libarchive/libarchiveplugin.h b/plugins/libarchive/libarchiveplugin.h index 790dc9b4..86ab156c 100644 --- a/plugins/libarchive/libarchiveplugin.h +++ b/plugins/libarchive/libarchiveplugin.h @@ -1,98 +1,99 @@ /* * Copyright (c) 2007 Henrique Pinto * Copyright (c) 2008-2009 Harald Hvaal * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LIBARCHIVEPLUGIN_H #define LIBARCHIVEPLUGIN_H #include "kerfuffle/archiveinterface.h" #include "kerfuffle/archiveentry.h" #include #include using namespace Kerfuffle; class LibarchivePlugin : public ReadWriteArchiveInterface { Q_OBJECT public: explicit LibarchivePlugin(QObject *parent, const QVariantList& args); virtual ~LibarchivePlugin(); virtual bool list() Q_DECL_OVERRIDE; virtual bool doKill() Q_DECL_OVERRIDE; virtual bool copyFiles(const QList& files, const QString& destinationDirectory, const ExtractionOptions& options) Q_DECL_OVERRIDE; virtual bool addFiles(const QList &files, const Archive::Entry *destination, const CompressionOptions& options) Q_DECL_OVERRIDE; virtual bool moveFiles(const QList &files, Archive::Entry *destination, const CompressionOptions& options) Q_DECL_OVERRIDE; virtual bool deleteFiles(const QList& files) Q_DECL_OVERRIDE; virtual bool addComment(const QString& comment) Q_DECL_OVERRIDE; virtual bool testArchive() Q_DECL_OVERRIDE; protected: struct ArchiveReadCustomDeleter { static inline void cleanup(struct archive *a) { if (a) { archive_read_free(a); } } }; struct ArchiveWriteCustomDeleter { static inline void cleanup(struct archive *a) { if (a) { archive_write_free(a); } } }; typedef QScopedPointer ArchiveRead; typedef QScopedPointer ArchiveWrite; - bool initializeReader(const LibarchivePlugin::ArchiveRead &archiveRead); + bool initializeReader(); void emitEntryFromArchiveEntry(struct archive_entry *entry); void copyData(const QString& filename, struct archive *dest, bool partialprogress = true); void copyData(const QString& filename, struct archive *source, struct archive *dest, bool partialprogress = true); + ArchiveRead m_archiveReader; ArchiveRead m_archiveReadDisk; bool m_abortOperation; private: int extractionFlags() const; int m_cachedArchiveEntryCount; qlonglong m_currentExtractedFilesSize; bool m_emitNoEntries; qlonglong m_extractedFilesSize; }; #endif // LIBARCHIVEPLUGIN_H diff --git a/plugins/libarchive/readwritelibarchiveplugin.cpp b/plugins/libarchive/readwritelibarchiveplugin.cpp index f0ce3d5e..8c0bb1f8 100644 --- a/plugins/libarchive/readwritelibarchiveplugin.cpp +++ b/plugins/libarchive/readwritelibarchiveplugin.cpp @@ -1,539 +1,498 @@ /* * Copyright (c) 2007 Henrique Pinto * Copyright (c) 2008-2009 Harald Hvaal * Copyright (c) 2010 Raphael Kubo da Costa * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "readwritelibarchiveplugin.h" #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(ReadWriteLibarchivePluginFactory, "kerfuffle_libarchive.json", registerPlugin();) -ReadWriteLibarchivePlugin::ReadWriteLibarchivePlugin(QObject *parent, const QVariantList & args) +ReadWriteLibarchivePlugin::ReadWriteLibarchivePlugin(QObject *parent, const QVariantList &args) : LibarchivePlugin(parent, args) { qCDebug(ARK) << "Loaded libarchive read-write plugin"; } ReadWriteLibarchivePlugin::~ReadWriteLibarchivePlugin() { } -bool ReadWriteLibarchivePlugin::addFiles(const QList &files, const Archive::Entry *destination, const CompressionOptions& options) +bool ReadWriteLibarchivePlugin::addFiles(const QList &files, const Archive::Entry *destination, const CompressionOptions &options) { qCDebug(ARK) << "Adding" << files.size() << "entries with CompressionOptions" << options; const bool creatingNewFile = !QFileInfo::exists(filename()); m_writtenFiles.clear(); - ArchiveRead arch_reader; - if (!creatingNewFile) { - arch_reader.reset(archive_read_new()); - if (!initializeReader(arch_reader)) { - return false; - } - } - - // |tempFile| needs to be created before |arch_writer| so that when we go - // out of scope in a `return false' case ArchiveWriteCustomDeleter is - // called before destructor of QSaveFile (ie. we call archive_write_close() - // before close()'ing the file descriptor). - QSaveFile tempFile(filename()); - if (!tempFile.open(QIODevice::WriteOnly | QIODevice::Unbuffered)) { - emit error(xi18nc("@info", "Failed to create a temporary file to compress %1.", filename())); + if (!creatingNewFile && !initializeReader()) { return false; } - ArchiveWrite arch_writer(archive_write_new()); - if (!(arch_writer.data())) { - emit error(i18n("The archive writer could not be initialized.")); - return false; - } - - // pax_restricted is the libarchive default, let's go with that. - archive_write_set_format_pax_restricted(arch_writer.data()); - - if (creatingNewFile) { - if (!initializeNewFileWriter(arch_writer, options)) { - return false; - } - } else { - if (!initializeWriter(arch_writer, arch_reader)) { - return false; - } - } - - if (archive_write_open_fd(arch_writer.data(), tempFile.handle()) != ARCHIVE_OK) { - emit error(xi18nc("@info", "Opening the archive for writing failed with the following error:%1", - QLatin1String(archive_error_string(arch_writer.data())))); + if (!initializeWriter(creatingNewFile, options)) { return false; } // First write the new files. qCDebug(ARK) << "Writing new entries"; int no_entries = 0; // Recreate destination directory structure. const QString destinationPath = destination->fullPath(); foreach(Archive::Entry *selectedFile, files) { - if (m_abortOperation) { break; } - if (!writeFile(selectedFile->fullPath(), destinationPath, arch_writer.data())) { + if (!writeFile(selectedFile->fullPath(), destinationPath)) { + finish(false); return false; } no_entries++; // For directories, write all subfiles/folders. const QString &fullPath = selectedFile->fullPath(); if (QFileInfo(fullPath).isDir()) { QDirIterator it(fullPath, QDir::AllEntries | QDir::Readable | QDir::Hidden | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); while (!m_abortOperation && it.hasNext()) { QString path = it.next(); if ((it.fileName() == QLatin1String("..")) || (it.fileName() == QLatin1String("."))) { continue; } const bool isRealDir = it.fileInfo().isDir() && !it.fileInfo().isSymLink(); if (isRealDir) { path.append(QLatin1Char('/')); } - if (!writeFile(path, destinationPath, arch_writer.data())) { + if (!writeFile(path, destinationPath)) { + finish(false); return false; } no_entries++; } } } qCDebug(ARK) << "Added" << no_entries << "new entries to archive"; + bool isSuccessful = true; // If we have old archive entries. if (!creatingNewFile) { - qCDebug(ARK) << "Copying any old entries"; m_filePaths = m_writtenFiles; - if (!processOldEntries(arch_reader, arch_writer, no_entries, Add)) { - QFile::remove(tempFile.fileName()); - return false; + isSuccessful = processOldEntries(no_entries, Add); + if (isSuccessful) { + qCDebug(ARK) << "Added" << no_entries << "old entries to archive"; + } + else { + qCDebug(ARK) << "Adding entries failed"; } - - qCDebug(ARK) << "Added" << no_entries << "old entries to archive"; } m_abortOperation = false; - // In the success case, we need to manually close the archive_writer before - // calling QSaveFile::commit(), otherwise the latter will close() the - // file descriptor archive_writer is still working on. - // TODO: We need to abstract this code better so that we only deal with one - // object that manages both QSaveFile and ArchiveWriter. - archive_write_close(arch_writer.data()); - tempFile.commit(); - - return true; + finish(isSuccessful); + return isSuccessful; } bool ReadWriteLibarchivePlugin::moveFiles(const QList &files, Archive::Entry *destination, const CompressionOptions &options) { + Q_UNUSED(options); + qCDebug(ARK) << "Moving" << files.size() << "entries"; - ArchiveRead arch_reader(archive_read_new()); - if (!initializeReader(arch_reader)) { + if (!initializeReader()) { return false; } - // |tempFile| needs to be created before |arch_writer| so that when we go - // out of scope in a `return false' case ArchiveWriteCustomDeleter is - // called before destructor of QSaveFile (ie. we call archive_write_close() - // before close()'ing the file descriptor). - QSaveFile tempFile(filename()); - if (!tempFile.open(QIODevice::WriteOnly | QIODevice::Unbuffered)) { - emit error(i18nc("@info", "Failed to create a temporary file.")); + if (!initializeWriter()) { return false; } - ArchiveWrite arch_writer(archive_write_new()); - if (!(arch_writer.data())) { - emit error(i18n("The archive writer could not be initialized.")); - return false; + // Copy old elements from previous archive to new archive. + int no_entries = 0; + m_filePaths = entryFullPaths(files); + m_destination = destination; + const bool isSuccessful = processOldEntries(no_entries, Move); + if (isSuccessful) { + qCDebug(ARK) << "Moved" << no_entries << "entries within archive"; + } + else { + qCDebug(ARK) << "Moving entries failed"; } - // pax_restricted is the libarchive default, let's go with that. - archive_write_set_format_pax_restricted(arch_writer.data()); + finish(isSuccessful); + return isSuccessful; +} - if (!initializeWriter(arch_writer, arch_reader)) { +bool ReadWriteLibarchivePlugin::deleteFiles(const QList &files) +{ + qCDebug(ARK) << "Deleting" << files.size() << "entries"; + + if (!initializeReader()) { return false; } - if (archive_write_open_fd(arch_writer.data(), tempFile.handle()) != ARCHIVE_OK) { - emit error(xi18nc("@info", "Opening the archive for writing failed with the following error:" - "%1", QLatin1String(archive_error_string(arch_writer.data())))); + if (!initializeWriter()) { return false; } // Copy old elements from previous archive to new archive. int no_entries = 0; m_filePaths = entryFullPaths(files); - m_destination = destination; - processOldEntries(arch_reader, arch_writer, no_entries, Move); - qCDebug(ARK) << "Removed" << no_entries << "entries from archive"; - - // In the success case, we need to manually close the archive_writer before - // calling QSaveFile::commit(), otherwise the latter will close() the - // file descriptor archive_writer is still working on. - // TODO: We need to abstract this code better so that we only deal with one - // object that manages both QSaveFile and ArchiveWriter. - archive_write_close(arch_writer.data()); - tempFile.commit(); + const bool isSuccessful = processOldEntries(no_entries, Delete); + if (isSuccessful) { + qCDebug(ARK) << "Removed" << no_entries << "entries from archive"; + } + else { + qCDebug(ARK) << "Removing entries failed"; + } - return true; + finish(isSuccessful); + return isSuccessful; } -bool ReadWriteLibarchivePlugin::deleteFiles(const QList& files) +bool ReadWriteLibarchivePlugin::initializeWriter(const bool creatingNewFile, const CompressionOptions &options) { - qCDebug(ARK) << "Deleting" << files.size() << "entries"; - - ArchiveRead arch_reader(archive_read_new()); - if (!initializeReader(arch_reader)) { - return false; - } - // |tempFile| needs to be created before |arch_writer| so that when we go // out of scope in a `return false' case ArchiveWriteCustomDeleter is // called before destructor of QSaveFile (ie. we call archive_write_close() // before close()'ing the file descriptor). - QSaveFile tempFile(filename()); - if (!tempFile.open(QIODevice::WriteOnly | QIODevice::Unbuffered)) { - emit error(i18nc("@info", "Failed to create a temporary file.")); + m_tempFile.setFileName(filename()); + if (!m_tempFile.open(QIODevice::WriteOnly | QIODevice::Unbuffered)) { + emit error(xi18nc("@info", "Failed to create a temporary file to compress %1.", filename())); return false; } - ArchiveWrite arch_writer(archive_write_new()); - if (!(arch_writer.data())) { + m_archiveWriter.reset(archive_write_new()); + if (!(m_archiveWriter.data())) { emit error(i18n("The archive writer could not be initialized.")); return false; } // pax_restricted is the libarchive default, let's go with that. - archive_write_set_format_pax_restricted(arch_writer.data()); + archive_write_set_format_pax_restricted(m_archiveWriter.data()); - if (!initializeWriter(arch_writer, arch_reader)) { - return false; + if (creatingNewFile) { + if (!initializeNewFileWriterFilters(options)) { + return false; + } + } + else { + if (!initializeWriterFilters()) { + return false; + } } - if (archive_write_open_fd(arch_writer.data(), tempFile.handle()) != ARCHIVE_OK) { + if (archive_write_open_fd(m_archiveWriter.data(), m_tempFile.handle()) != ARCHIVE_OK) { emit error(xi18nc("@info", "Opening the archive for writing failed with the following error:" - "%1", QLatin1String(archive_error_string(arch_writer.data())))); + "%1", QLatin1String(archive_error_string(m_archiveWriter.data())))); return false; } - // Copy old elements from previous archive to new archive. - int no_entries = 0; - m_filePaths = entryFullPaths(files); - processOldEntries(arch_reader, arch_writer, no_entries, Delete); - qCDebug(ARK) << "Removed" << no_entries << "entries from archive"; + return true; +} - // In the success case, we need to manually close the archive_writer before - // calling QSaveFile::commit(), otherwise the latter will close() the - // file descriptor archive_writer is still working on. - // TODO: We need to abstract this code better so that we only deal with one - // object that manages both QSaveFile and ArchiveWriter. - archive_write_close(arch_writer.data()); - tempFile.commit(); +bool ReadWriteLibarchivePlugin::initializeWriterFilters() +{ + int ret; + bool requiresExecutable = false; + switch (archive_filter_code(m_archiveReader.data(), 0)) { + case ARCHIVE_FILTER_GZIP: + ret = archive_write_add_filter_gzip(m_archiveWriter.data()); + break; + case ARCHIVE_FILTER_BZIP2: + ret = archive_write_add_filter_bzip2(m_archiveWriter.data()); + break; + case ARCHIVE_FILTER_XZ: + ret = archive_write_add_filter_xz(m_archiveWriter.data()); + break; + case ARCHIVE_FILTER_LZMA: + ret = archive_write_add_filter_lzma(m_archiveWriter.data()); + break; + case ARCHIVE_FILTER_COMPRESS: + ret = archive_write_add_filter_compress(m_archiveWriter.data()); + break; + case ARCHIVE_FILTER_LZIP: + ret = archive_write_add_filter_lzip(m_archiveWriter.data()); + break; + case ARCHIVE_FILTER_LZOP: + ret = archive_write_add_filter_lzop(m_archiveWriter.data()); + break; + case ARCHIVE_FILTER_LRZIP: + ret = archive_write_add_filter_lrzip(m_archiveWriter.data()); + requiresExecutable = true; + break; +#ifdef HAVE_LIBARCHIVE_3_2_0 + case ARCHIVE_FILTER_LZ4: + ret = archive_write_add_filter_lz4(archiveWrite.data()); + break; +#endif + case ARCHIVE_FILTER_NONE: + ret = archive_write_add_filter_none(m_archiveWriter.data()); + break; + default: + emit error(i18n("The compression type '%1' is not supported by Ark.", + QLatin1String(archive_filter_name(m_archiveReader.data(), 0)))); + return false; + } + + // Libarchive emits a warning for lrzip due to using external executable. + if ((requiresExecutable && ret != ARCHIVE_WARN) || + (!requiresExecutable && ret != ARCHIVE_OK)) { + emit error(xi18nc("@info", "Setting the compression method failed with the following error:%1", + QLatin1String(archive_error_string(m_archiveWriter.data())))); + return false; + } return true; } -bool ReadWriteLibarchivePlugin::initializeNewFileWriter(const LibarchivePlugin::ArchiveWrite &archiveWrite, const CompressionOptions &options) +bool ReadWriteLibarchivePlugin::initializeNewFileWriterFilters(const CompressionOptions &options) { int ret; bool requiresExecutable = false; if (filename().right(2).toUpper() == QLatin1String("GZ")) { qCDebug(ARK) << "Detected gzip compression for new file"; - ret = archive_write_add_filter_gzip(archiveWrite.data()); + ret = archive_write_add_filter_gzip(m_archiveWriter.data()); } else if (filename().right(3).toUpper() == QLatin1String("BZ2")) { qCDebug(ARK) << "Detected bzip2 compression for new file"; - ret = archive_write_add_filter_bzip2(archiveWrite.data()); + ret = archive_write_add_filter_bzip2(m_archiveWriter.data()); } else if (filename().right(2).toUpper() == QLatin1String("XZ")) { qCDebug(ARK) << "Detected xz compression for new file"; - ret = archive_write_add_filter_xz(archiveWrite.data()); + ret = archive_write_add_filter_xz(m_archiveWriter.data()); } else if (filename().right(4).toUpper() == QLatin1String("LZMA")) { qCDebug(ARK) << "Detected lzma compression for new file"; - ret = archive_write_add_filter_lzma(archiveWrite.data()); + ret = archive_write_add_filter_lzma(m_archiveWriter.data()); } else if (filename().right(2).toUpper() == QLatin1String(".Z")) { qCDebug(ARK) << "Detected compress (.Z) compression for new file"; - ret = archive_write_add_filter_compress(archiveWrite.data()); + ret = archive_write_add_filter_compress(m_archiveWriter.data()); } else if (filename().right(2).toUpper() == QLatin1String("LZ")) { qCDebug(ARK) << "Detected lzip compression for new file"; - ret = archive_write_add_filter_lzip(archiveWrite.data()); + ret = archive_write_add_filter_lzip(m_archiveWriter.data()); } else if (filename().right(3).toUpper() == QLatin1String("LZO")) { qCDebug(ARK) << "Detected lzop compression for new file"; - ret = archive_write_add_filter_lzop(archiveWrite.data()); + ret = archive_write_add_filter_lzop(m_archiveWriter.data()); } else if (filename().right(3).toUpper() == QLatin1String("LRZ")) { qCDebug(ARK) << "Detected lrzip compression for new file"; - ret = archive_write_add_filter_lrzip(archiveWrite.data()); + ret = archive_write_add_filter_lrzip(m_archiveWriter.data()); requiresExecutable = true; #ifdef HAVE_LIBARCHIVE_3_2_0 } else if (filename().right(3).toUpper() == QLatin1String("LZ4")) { qCDebug(ARK) << "Detected lz4 compression for new file"; - ret = archive_write_add_filter_lz4(archiveWrite.data()); + ret = archive_write_add_filter_lz4(m_archiveWriter.data()); #endif } else if (filename().right(3).toUpper() == QLatin1String("TAR")) { qCDebug(ARK) << "Detected no compression for new file (pure tar)"; - ret = archive_write_add_filter_none(archiveWrite.data()); + ret = archive_write_add_filter_none(m_archiveWriter.data()); } else { qCDebug(ARK) << "Falling back to gzip"; - ret = archive_write_add_filter_gzip(archiveWrite.data()); + ret = archive_write_add_filter_gzip(m_archiveWriter.data()); } // Libarchive emits a warning for lrzip due to using external executable. if ((requiresExecutable && ret != ARCHIVE_WARN) || (!requiresExecutable && ret != ARCHIVE_OK)) { emit error(xi18nc("@info", "Setting the compression method failed with the following error:%1", - QLatin1String(archive_error_string(archiveWrite.data())))); + QLatin1String(archive_error_string(m_archiveWriter.data())))); return false; } // Set compression level if passed in CompressionOptions. if (options.contains(QStringLiteral("CompressionLevel"))) { qCDebug(ARK) << "Using compression level:" << options.value(QStringLiteral("CompressionLevel")).toString(); - ret = archive_write_set_filter_option(archiveWrite.data(), NULL, "compression-level", options.value(QStringLiteral("CompressionLevel")).toString().toUtf8()); + ret = archive_write_set_filter_option(m_archiveWriter.data(), NULL, "compression-level", options.value(QStringLiteral("CompressionLevel")).toString().toUtf8()); if (ret != ARCHIVE_OK) { qCWarning(ARK) << "Failed to set compression level"; emit error(xi18nc("@info", "Setting the compression level failed with the following error:%1", - QLatin1String(archive_error_string(archiveWrite.data())))); + QLatin1String(archive_error_string(m_archiveWriter.data())))); return false; } } return true; } -bool ReadWriteLibarchivePlugin::initializeWriter(const ArchiveWrite &archiveWrite, const ArchiveRead &archiveRead) +void ReadWriteLibarchivePlugin::finish(const bool isSuccessful) { - int ret; - bool requiresExecutable = false; - switch (archive_filter_code(archiveRead.data(), 0)) { - case ARCHIVE_FILTER_GZIP: - ret = archive_write_add_filter_gzip(archiveWrite.data()); - break; - case ARCHIVE_FILTER_BZIP2: - ret = archive_write_add_filter_bzip2(archiveWrite.data()); - break; - case ARCHIVE_FILTER_XZ: - ret = archive_write_add_filter_xz(archiveWrite.data()); - break; - case ARCHIVE_FILTER_LZMA: - ret = archive_write_add_filter_lzma(archiveWrite.data()); - break; - case ARCHIVE_FILTER_COMPRESS: - ret = archive_write_add_filter_compress(archiveWrite.data()); - break; - case ARCHIVE_FILTER_LZIP: - ret = archive_write_add_filter_lzip(archiveWrite.data()); - break; - case ARCHIVE_FILTER_LZOP: - ret = archive_write_add_filter_lzop(archiveWrite.data()); - break; - case ARCHIVE_FILTER_LRZIP: - ret = archive_write_add_filter_lrzip(archiveWrite.data()); - requiresExecutable = true; - break; -#ifdef HAVE_LIBARCHIVE_3_2_0 - case ARCHIVE_FILTER_LZ4: - ret = archive_write_add_filter_lz4(archiveWrite.data()); - break; -#endif - case ARCHIVE_FILTER_NONE: - ret = archive_write_add_filter_none(archiveWrite.data()); - break; - default: - emit error(i18n("The compression type '%1' is not supported by Ark.", - QLatin1String(archive_filter_name(archiveRead.data(), 0)))); - return false; - } - - // Libarchive emits a warning for lrzip due to using external executable. - if ((requiresExecutable && ret != ARCHIVE_WARN) || - (!requiresExecutable && ret != ARCHIVE_OK)) { - emit error(xi18nc("@info", "Setting the compression method failed with the following error:%1", - QLatin1String(archive_error_string(archiveWrite.data())))); - return false; + if (!isSuccessful) { + m_tempFile.cancelWriting(); } - - return true; + archive_write_close(m_archiveWriter.data()); + m_tempFile.commit(); } -bool ReadWriteLibarchivePlugin::processOldEntries(const LibarchivePlugin::ArchiveRead &archiveRead, const LibarchivePlugin::ArchiveWrite &archiveWrite, int &entriesCounter, OperationMode mode) +bool ReadWriteLibarchivePlugin::processOldEntries(int &entriesCounter, OperationMode mode) { struct archive_entry *entry; m_lastMovedFolder = QString(); entriesCounter = 0; - while ((mode != Add || !m_abortOperation) && archive_read_next_header(archiveRead.data(), &entry) == ARCHIVE_OK) { + while ((mode != Add || !m_abortOperation) && archive_read_next_header(m_archiveReader.data(), &entry) == ARCHIVE_OK) { const QString file = QFile::decodeName(archive_entry_pathname(entry)); if (mode == Move) { QString destination; if (m_lastMovedFolder.count() > 0 && file.startsWith(m_lastMovedFolder)) { destination = file; destination.replace(0, m_lastMovedFolder.count(), m_destination->fullPath()); } else if (m_filePaths.contains(file)) { destination = m_destination->fullPath(); if (m_filePaths.count() > 1) { destination = destination + file.split(QLatin1Char('/'), QString::SkipEmptyParts).last(); } else { destination = m_destination->fullPath(); if (destination[destination.count() - 1] == QLatin1Char('/')) { destination.remove(destination.count() - 1, 1); m_lastMovedFolder = destination; } } } archive_entry_set_pathname(entry, destination.toStdString().c_str()); emit entryRemoved(file); } else if (m_filePaths.contains(file)) { - archive_read_data_skip(archiveRead.data()); + archive_read_data_skip(m_archiveReader.data()); switch (mode) { case Delete: entriesCounter++; emit entryRemoved(file); break; case Add: qCDebug(ARK) << file << "is already present in the new archive, skipping."; break; + + default: + qCDebug(ARK) << "Mode" << mode << "is not considered for processing old libarchive entries"; + Q_ASSERT(false); } continue; } - if (writeEntry(archiveRead, archiveWrite, entry)) { + if (writeEntry(entry)) { if (mode == Add) { entriesCounter++; } - archive_entry_clear(entry); } else { return false; } } return true; } -bool ReadWriteLibarchivePlugin::writeEntry(const LibarchivePlugin::ArchiveRead &archiveRead, const LibarchivePlugin::ArchiveWrite &archiveWrite, struct archive_entry *entry) { - const int returnCode = archive_write_header(archiveWrite.data(), entry); +bool ReadWriteLibarchivePlugin::writeEntry(struct archive_entry *entry) { + const int returnCode = archive_write_header(m_archiveWriter.data(), entry); const QString file = QFile::decodeName(archive_entry_pathname(entry)); switch (returnCode) { case ARCHIVE_OK: // If the whole archive is extracted and the total filesize is // available, we use partial progress. - copyData(QLatin1String(archive_entry_pathname(entry)), archiveRead.data(), archiveWrite.data(), false); + copyData(QLatin1String(archive_entry_pathname(entry)), m_archiveReader.data(), m_archiveWriter.data(), false); break; case ARCHIVE_FAILED: case ARCHIVE_FATAL: qCCritical(ARK) << "archive_write_header() has returned" << returnCode - << "with errno" << archive_errno(archiveWrite.data()); + << "with errno" << archive_errno(m_archiveWriter.data()); emit error(xi18nc("@info", "Compression failed while processing:" "%1Operation aborted.", file)); return false; default: qCDebug(ARK) << "archive_writer_header() has returned" << returnCode << "which will be ignored."; break; } return true; } // TODO: if we merge this with copyData(), we can pass more data // such as an fd to archive_read_disk_entry_from_file() -bool ReadWriteLibarchivePlugin::writeFile(const QString& relativeName, const QString& destination, struct archive* arch_writer) +bool ReadWriteLibarchivePlugin::writeFile(const QString &relativeName, const QString &destination) { int header_response; const QString absoluteFilename = QFileInfo(relativeName).absoluteFilePath(); const QString destinationFilename = destination + relativeName; // #253059: Even if we use archive_read_disk_entry_from_file, // libarchive may have been compiled without HAVE_LSTAT, // or something may have caused it to follow symlinks, in // which case stat() will be called. To avoid this, we // call lstat() ourselves. struct stat st; lstat(QFile::encodeName(absoluteFilename).constData(), &st); struct archive_entry *entry = archive_entry_new(); archive_entry_set_pathname(entry, QFile::encodeName(destinationFilename).constData()); archive_entry_copy_sourcepath(entry, QFile::encodeName(absoluteFilename).constData()); archive_read_disk_entry_from_file(m_archiveReadDisk.data(), entry, -1, &st); - if ((header_response = archive_write_header(arch_writer, entry)) == ARCHIVE_OK) { + if ((header_response = archive_write_header(m_archiveWriter.data(), entry)) == ARCHIVE_OK) { // If the whole archive is extracted and the total filesize is // available, we use partial progress. - copyData(absoluteFilename, arch_writer, false); + copyData(absoluteFilename, m_archiveWriter.data(), false); } else { qCCritical(ARK) << "Writing header failed with error code " << header_response; - qCCritical(ARK) << "Error while writing..." << archive_error_string(arch_writer) << "(error no =" << archive_errno(arch_writer) << ')'; + qCCritical(ARK) << "Error while writing..." << archive_error_string(m_archiveWriter.data()) << "(error no =" << archive_errno(m_archiveWriter.data()) << ')'; emit error(xi18nc("@info Error in a message box", "Ark could not compress %1:%2", absoluteFilename, - QString::fromUtf8(archive_error_string(arch_writer)))); + QString::fromUtf8(archive_error_string(m_archiveWriter.data())))); archive_entry_free(entry); return false; } m_writtenFiles.push_back(destinationFilename); emitEntryFromArchiveEntry(entry); archive_entry_free(entry); return true; } #include "readwritelibarchiveplugin.moc" diff --git a/plugins/libarchive/readwritelibarchiveplugin.h b/plugins/libarchive/readwritelibarchiveplugin.h index f14e1a4e..d58b0384 100644 --- a/plugins/libarchive/readwritelibarchiveplugin.h +++ b/plugins/libarchive/readwritelibarchiveplugin.h @@ -1,64 +1,94 @@ /* * Copyright (c) 2007 Henrique Pinto * Copyright (c) 2008-2009 Harald Hvaal * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef READWRITELIBARCHIVEPLUGIN_H #define READWRITELIBARCHIVEPLUGIN_H #include "libarchiveplugin.h" #include #include +#include using namespace Kerfuffle; class ReadWriteLibarchivePlugin : public LibarchivePlugin { Q_OBJECT public: - explicit ReadWriteLibarchivePlugin(QObject *parent, const QVariantList& args); + explicit ReadWriteLibarchivePlugin(QObject *parent, const QVariantList &args); ~ReadWriteLibarchivePlugin(); - bool addFiles(const QList &files, const Archive::Entry *destination, const CompressionOptions& options) Q_DECL_OVERRIDE; - bool moveFiles(const QList &files, Archive::Entry *destination, const CompressionOptions& options) Q_DECL_OVERRIDE; - bool deleteFiles(const QList& files) Q_DECL_OVERRIDE; + bool addFiles(const QList &files, const Archive::Entry *destination, const CompressionOptions &options) Q_DECL_OVERRIDE; + bool moveFiles(const QList &files, Archive::Entry *destination, const CompressionOptions &options) Q_DECL_OVERRIDE; + bool deleteFiles(const QList &files) Q_DECL_OVERRIDE; + +protected: + bool initializeWriter(const bool creatingNewFile = false, const CompressionOptions &options = CompressionOptions()); + bool initializeWriterFilters(); + bool initializeNewFileWriterFilters(const CompressionOptions &options); + void finish(const bool isSuccessful); private: - bool initializeNewFileWriter(const ArchiveWrite &archiveWrite, const CompressionOptions &options); - bool initializeWriter(const ArchiveWrite &archiveWrite, const ArchiveRead &archiveRead); - bool processOldEntries(const ArchiveRead &archiveRead, const ArchiveWrite &archiveWrite, int &entriesCounter, OperationMode mode); - bool writeEntry(const LibarchivePlugin::ArchiveRead &archiveRead, const LibarchivePlugin::ArchiveWrite &archiveWrite, struct archive_entry *entry); - bool writeFile(const QString& relativeName, const QString& destination, struct archive* arch); + /** + * Processes all the existing entries and does manipulations to them + * based on the OperationMode (Add/Move/Copy/Delete). + * + * @param entriesCounter Counter of added/moved/copied/deleted entries. + * + * @return bool indicating whether the operation was successful. + */ + bool processOldEntries(int &entriesCounter, OperationMode mode); + + /** + * Writes entry being read into memory. + * + * @return bool indicating whether the operation was successful. + */ + bool writeEntry(struct archive_entry *entry); + + /** + * Writes entry from physical disk. + * + * @return bool indicating whether the operation was successful. + */ + bool writeFile(const QString &relativeName, const QString &destination); + + QSaveFile m_tempFile; + ArchiveWrite m_archiveWriter; + // New added files by addFiles methods. It's assigned to m_filePaths + // and then is used by processOldEntries method (in Add mode) for skipping already written entries. QStringList m_writtenFiles; // Passed argument from job which is used by processOldEntries method. QStringList m_filePaths; const Archive::Entry *m_destination; QString m_lastMovedFolder; }; #endif // READWRITELIBARCHIVEPLUGIN_H