Commit 7c025dd5 authored by Ragnar Thomsen's avatar Ragnar Thomsen

Fix drag'n'drop extraction with CLI-plugins

This fixes drag'n'drop extraction of selected entries from Ark to e.g.
Dolphin for archives handled by CLI-plugins. Previously, the entries
would be extracted with full path, which is not what the user expects
and not how the libarchive-plugin handles tar-based archives. This was
due to CLI-plugins not supporting individual root nodes (i.e. removing
a part of the path from entry to be extracted). This is now circumvented
by extracting to a QTemporaryDir, removing the root node from the path,
and finally moving the files to their final destination. The moving to
final destination is done in a new member function in CliInterface
(moveToFinalDest). This function checks if the destination file exists
and prompts the user for action if that is the case.

The finished signal() is now emitted from copyFiles()/addFiles()/
deleteFiles()/list(), instead of from processFinished().

BUG: 208384
FIXED-IN: 15.08.2
REVIEW: 125293
parent 06024b03
......@@ -46,11 +46,12 @@
#include <QEventLoop>
#include <QFile>
#include <QProcess>
#include <QRegularExpression>
#include <QStandardPaths>
#include <QTemporaryDir>
#include <QThread>
#include <QTimer>
#include <QStandardPaths>
#include <QUrl>
#include <QRegularExpression>
namespace Kerfuffle
{
......@@ -101,10 +102,11 @@ bool CliInterface::list()
return false;
}
emit finished(true);
return true;
}
bool CliInterface::copyFiles(const QList<QVariant> & files, const QString & destinationDirectory, ExtractionOptions options)
bool CliInterface::copyFiles(const QVariantList &files, const QString &destinationDirectory, ExtractionOptions options)
{
qCDebug(KERFUFFLE) << Q_FUNC_INFO << "to" << destinationDirectory;
......@@ -236,15 +238,33 @@ bool CliInterface::copyFiles(const QList<QVariant> & files, const QString & dest
}
}
QUrl destDir(destinationDirectory);
bool useTmpExtractDir = !files.isEmpty();
QUrl destDir;
QTemporaryDir tmpExtractDir;
if (useTmpExtractDir) {
qCDebug(KERFUFFLE) << "Using temporary extraction dir:" << tmpExtractDir.path();
if (!tmpExtractDir.isValid()) {
qCDebug(KERFUFFLE) << "Creation of temporary directory failed.";
failOperation();
return false;
}
destDir = QUrl(tmpExtractDir.path());
} else {
destDir = QUrl(destinationDirectory);
}
QDir::setCurrent(destDir.adjusted(QUrl::RemoveScheme).url());
qCDebug(KERFUFFLE) << "Setting current dir to " << destinationDirectory;
if (!runProcess(m_param.value(ExtractProgram).toStringList(), args)) {
failOperation();
return false;
}
if (useTmpExtractDir) {
moveToFinalDest(files, destinationDirectory, tmpExtractDir);
}
emit finished(true);
return true;
}
......@@ -353,7 +373,7 @@ bool CliInterface::addFiles(const QStringList & files, const CompressionOptions&
failOperation();
return false;
}
emit finished(true);
return true;
}
......@@ -388,7 +408,7 @@ bool CliInterface::deleteFiles(const QList<QVariant> & files)
failOperation();
return false;
}
emit finished(true);
return true;
}
......@@ -474,9 +494,88 @@ void CliInterface::processFinished(int exitCode, QProcess::ExitStatus exitStatus
list();
return;
}
}
//and we're finished
emit finished(true);
bool CliInterface::moveToFinalDest(const QVariantList &files, const QString &finalDest, const QTemporaryDir &tmpDir)
{
// Move extracted files from a QTemporaryDir to the final destination.
QDir finalDestDir(finalDest);
qCDebug(KERFUFFLE) << "Setting final dir to" << finalDest;
bool overwriteAll = false;
bool skipAll = false;
foreach (const QVariant& file, files) {
QFileInfo relEntry(file.value<fileRootNodePair>().file.remove(file.value<fileRootNodePair>().rootNode));
QFileInfo absSourceEntry(tmpDir.path() + QLatin1Char('/') + file.value<fileRootNodePair>().file);
QFileInfo absDestEntry(finalDestDir.path() + QLatin1Char('/') + relEntry.filePath());
if (absSourceEntry.isDir()) {
// For directories, just create the path.
if (!finalDestDir.mkpath(relEntry.filePath())) {
qCWarning(KERFUFFLE) << "Failed to create directory" << relEntry.filePath() << "in final destination.";
}
} else {
// If destination file exists, prompt the user.
if (absDestEntry.exists()) {
qCWarning(KERFUFFLE) << "File" << absDestEntry.absoluteFilePath() << "exists.";
if (!skipAll && !overwriteAll) {
Kerfuffle::OverwriteQuery query(absDestEntry.absoluteFilePath());
query.setNoRenameMode(true);
emit userQuery(&query);
query.waitForResponse();
if (query.responseOverwrite() || query.responseOverwriteAll()) {
if (query.responseOverwriteAll()) {
overwriteAll = true;
}
if (!QFile::remove(absDestEntry.absoluteFilePath())) {
qCWarning(KERFUFFLE) << "Failed to remove" << absDestEntry.absoluteFilePath();
}
} else if (query.responseSkip() || query.responseAutoSkip()) {
if (query.responseAutoSkip()) {
skipAll = true;
}
continue;
} else if (query.responseCancelled()) {
qCDebug(KERFUFFLE) << "Copy action cancelled.";
return false;
}
} else if (skipAll) {
continue;
} else if (overwriteAll) {
if (!QFile::remove(absDestEntry.absoluteFilePath())) {
qCWarning(KERFUFFLE) << "Failed to remove" << absDestEntry.absoluteFilePath();
}
}
}
// Create any parent directories.
if (!finalDestDir.mkpath(relEntry.path())) {
qCWarning(KERFUFFLE) << "Failed to create parent dirctory for file:" << absDestEntry.filePath();
}
// Move files to the final destination.
if (!QFile(absSourceEntry.absoluteFilePath()).rename(absDestEntry.absoluteFilePath())) {
qCWarning(KERFUFFLE) << "Failed to move file" << absSourceEntry.filePath() << "to final destination.";
return false;
}
}
}
return true;
}
void CliInterface::failOperation()
......
......@@ -35,6 +35,7 @@
class KProcess;
class KPtyProcess;
class QTemporaryDir;
namespace Kerfuffle
{
......@@ -351,6 +352,8 @@ private:
*/
void writeToProcess(const QByteArray& data);
bool moveToFinalDest(const QVariantList &files, const QString &finalDest, const QTemporaryDir &tmpDir);
QByteArray m_stdOutData;
QRegExp m_existsPattern;
QRegExp m_passwordPromptPattern;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment