diff --git a/src/crypto/autodecryptverifyfilescontroller.cpp b/src/crypto/autodecryptverifyfilescontroller.cpp
index 278a17bfd..35ae4cd31 100644
--- a/src/crypto/autodecryptverifyfilescontroller.cpp
+++ b/src/crypto/autodecryptverifyfilescontroller.cpp
@@ -1,667 +1,677 @@
 /* -*- mode: c++; c-basic-offset:4 -*-
     autodecryptverifyfilescontroller.cpp
 
     This file is part of Kleopatra, the KDE keymanager
     SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
 
     SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
     SPDX-FileContributor: Intevation GmbH
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 #include <config-kleopatra.h>
 
 #include "autodecryptverifyfilescontroller.h"
 
 #include "fileoperationspreferences.h"
 
 #include <crypto/decryptverifytask.h>
 #include <crypto/gui/decryptverifyfilesdialog.h>
 #include <crypto/gui/decryptverifyoperationwidget.h>
 #include <crypto/taskcollection.h>
 
 #include "commands/decryptverifyfilescommand.h"
 
 #include <utils/archivedefinition.h>
 #include <utils/input.h>
 #include <utils/kleo_assert.h>
 #include <utils/output.h>
 #include <utils/overwritedialog.h>
 #include <utils/path-helper.h>
 
 #include <Libkleo/Algorithm>
 #include <Libkleo/Classify>
 #include <Libkleo/GnuPG>
 
 #ifndef Q_OS_WIN
 #include <KIO/CopyJob>
 #include <KIO/Global>
 #endif
 #include "kleopatra_debug.h"
 #include <KLocalizedString>
 #include <KMessageBox>
 
 #include <QGpgME/DecryptVerifyArchiveJob>
 
 #include <QDir>
 #include <QFile>
 #include <QFileDialog>
 #include <QFileInfo>
 #include <QTemporaryDir>
 #include <QTimer>
 
 #include <gpgme++/decryptionresult.h>
 
 using namespace GpgME;
 using namespace Kleo;
 using namespace Kleo::Crypto;
 using namespace Kleo::Crypto::Gui;
 
 class AutoDecryptVerifyFilesController::Private
 {
     AutoDecryptVerifyFilesController *const q;
 
 public:
     explicit Private(AutoDecryptVerifyFilesController *qq);
 
     void schedule();
 
     QString getEmbeddedFileName(const QString &fileName) const;
     void exec();
     std::vector<std::shared_ptr<Task>> buildTasks(const QStringList &, QStringList &);
 
     struct CryptoFile {
         QString baseName;
         QString fileName;
         GpgME::Protocol protocol = GpgME::UnknownProtocol;
         int classification = 0;
         std::shared_ptr<Output> output;
     };
     QVector<CryptoFile> classifyAndSortFiles(const QStringList &files);
 
     void reportError(int err, const QString &details)
     {
         q->setLastError(err, details);
         q->emitDoneOrError();
     }
     void cancelAllTasks();
 
     QStringList m_passedFiles, m_filesAfterPreparation;
     std::vector<std::shared_ptr<const DecryptVerifyResult>> m_results;
     std::vector<std::shared_ptr<Task>> m_runnableTasks, m_completedTasks;
     std::shared_ptr<Task> m_runningTask;
     bool m_errorDetected = false;
     DecryptVerifyOperation m_operation = DecryptVerify;
     QPointer<DecryptVerifyFilesDialog> m_dialog;
     std::unique_ptr<QTemporaryDir> m_workDir;
 };
 
 AutoDecryptVerifyFilesController::Private::Private(AutoDecryptVerifyFilesController *qq)
     : q(qq)
 {
     qRegisterMetaType<VerificationResult>();
 }
 
 void AutoDecryptVerifyFilesController::Private::schedule()
 {
     if (!m_runningTask && !m_runnableTasks.empty()) {
         const std::shared_ptr<Task> t = m_runnableTasks.back();
         m_runnableTasks.pop_back();
         t->start();
         m_runningTask = t;
     }
     if (!m_runningTask) {
         kleo_assert(m_runnableTasks.empty());
         for (const std::shared_ptr<const DecryptVerifyResult> &i : std::as_const(m_results)) {
             Q_EMIT q->verificationResult(i->verificationResult());
         }
     }
 }
 
 QString AutoDecryptVerifyFilesController::Private::getEmbeddedFileName(const QString &fileName) const
 {
     auto it = std::find_if(m_results.cbegin(), m_results.cend(), [fileName](const auto &r) {
         return r->fileName() == fileName;
     });
     if (it != m_results.cend()) {
         const auto embeddedFilePath = QString::fromUtf8((*it)->decryptionResult().fileName());
         if (embeddedFilePath.contains(QLatin1Char{'\\'})) {
             // ignore embedded file names containing '\'
             return {};
         }
         // strip the path from the embedded file name
         return QFileInfo{embeddedFilePath}.fileName();
     } else {
         return {};
     }
 }
 
 void AutoDecryptVerifyFilesController::Private::exec()
 {
     Q_ASSERT(!m_dialog);
 
     QStringList undetected;
     std::vector<std::shared_ptr<Task>> tasks = buildTasks(m_passedFiles, undetected);
 
     if (!undetected.isEmpty()) {
         // Since GpgME 1.7.0 Classification is supposed to be reliable
         // so we really can't do anything with this data.
         reportError(makeGnuPGError(GPG_ERR_GENERAL),
                     xi18n("Failed to find encrypted or signed data in one or more files.<nl/>"
                           "You can manually select what to do with the files now.<nl/>"
                           "If they contain signed or encrypted data please report a bug (see Help->Report Bug)."));
         auto cmd = new Commands::DecryptVerifyFilesCommand(undetected, nullptr, true);
         cmd->start();
     }
     if (tasks.empty()) {
         q->emitDoneOrError();
         return;
     }
     Q_ASSERT(m_runnableTasks.empty());
     m_runnableTasks.swap(tasks);
 
     std::shared_ptr<TaskCollection> coll(new TaskCollection);
     for (const std::shared_ptr<Task> &i : std::as_const(m_runnableTasks)) {
         q->connectTask(i);
     }
     coll->setTasks(m_runnableTasks);
     DecryptVerifyFilesDialog dialog{coll};
     m_dialog = &dialog;
     m_dialog->setOutputLocation(heuristicBaseDirectory(m_passedFiles));
     q->applyWindowID(m_dialog);
 
     QTimer::singleShot(0, q, SLOT(schedule()));
     const auto result = m_dialog->exec();
     if (result == QDialog::Rejected) {
         q->cancel();
     } else if (result == QDialog::Accepted && m_workDir) {
         // Without workdir there is nothing to move.
         const QDir workdir(m_workDir->path());
         const QDir outDir(m_dialog->outputLocation());
         qCDebug(KLEOPATRA_LOG) << workdir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
         const auto filesAndFoldersToMove = workdir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
         const auto fileCount = Kleo::count_if(filesAndFoldersToMove, [](const auto &fi) {
             return !fi.isDir();
         });
         OverwritePolicy overwritePolicy{m_dialog, fileCount > 1 ? OverwritePolicy::MultipleFiles : OverwritePolicy::Options{}};
         for (const QFileInfo &fi : filesAndFoldersToMove) {
             const auto inpath = fi.absoluteFilePath();
 
             if (fi.isDir()) {
                 // A directory. Assume that the input was an archive
                 // and avoid directory merges by trying to find a non
                 // existing directory.
                 auto candidate = fi.fileName();
                 if (candidate.startsWith(QLatin1Char('-'))) {
                     // Bug in GpgTar Extracts stdout passed archives to a dir named -
                     candidate = QFileInfo(m_passedFiles.first()).baseName();
                 }
 
                 QString suffix;
                 QFileInfo ofi;
                 int i = 0;
                 do {
                     ofi = QFileInfo(outDir.absoluteFilePath(candidate + suffix));
                     if (!ofi.exists()) {
                         break;
                     }
                     suffix = QStringLiteral("_%1").arg(++i);
                 } while (i < 1000);
 
                 const auto destPath = ofi.absoluteFilePath();
 #ifndef Q_OS_WIN
                 auto job = KIO::moveAs(QUrl::fromLocalFile(inpath), QUrl::fromLocalFile(destPath));
                 qCDebug(KLEOPATRA_LOG) << "Moving" << job->srcUrls().front().toLocalFile() << "to" << job->destUrl().toLocalFile();
                 if (!job->exec()) {
                     if (job->error() == KIO::ERR_USER_CANCELED) {
                         break;
                     }
                     reportError(makeGnuPGError(GPG_ERR_GENERAL),
                                 xi18nc("@info",
                                        "<para>Failed to move <filename>%1</filename> to <filename>%2</filename>.</para>"
                                        "<para><message>%3</message></para>",
                                        inpath,
                                        destPath,
                                        job->errorString()));
                 }
 #else
                 // On Windows, KIO::move does not work for folders when crossing partition boundaries
                 if (!moveDir(inpath, destPath)) {
                     reportError(makeGnuPGError(GPG_ERR_GENERAL),
                                 xi18nc("@info", "<para>Failed to move <filename>%1</filename> to <filename>%2</filename>.</para>", inpath, destPath));
                 }
 #endif
                 continue;
             }
 
             const auto embeddedFileName = getEmbeddedFileName(inpath);
             QString outFileName = fi.fileName();
             if (!embeddedFileName.isEmpty() && embeddedFileName != fi.fileName()) {
                 // we switch "Yes" and "No" because Yes is default, but saving with embedded file name could be dangerous
                 const auto answer = KMessageBox::questionTwoActionsCancel(
                     m_dialog,
                     xi18n("Shall the file be saved with the original file name <filename>%1</filename>?", embeddedFileName),
                     i18n("Use Original File Name?"),
                     KGuiItem(xi18n("No, Save As <filename>%1</filename>", fi.fileName())),
                     KGuiItem(xi18n("Yes, Save As <filename>%1</filename>", embeddedFileName)));
                 if (answer == KMessageBox::Cancel) {
                     qCDebug(KLEOPATRA_LOG) << "Saving canceled for:" << inpath;
                     continue;
                 } else if (answer == KMessageBox::ButtonCode::SecondaryAction) {
                     outFileName = embeddedFileName;
                 }
             }
             auto outpath = outDir.absoluteFilePath(outFileName);
             qCDebug(KLEOPATRA_LOG) << "Moving " << inpath << " to " << outpath;
             const QFileInfo ofi(outpath);
             if (ofi.exists()) {
-                const auto newPath = overwritePolicy.obtainOverwritePermission(outpath);
-                if (newPath.isEmpty()) {
-                    if (overwritePolicy.policy() == OverwritePolicy::Cancel) {
-                        qCDebug(KLEOPATRA_LOG) << "Overwriting canceled for: " << outpath;
-                        break;
-                    }
-                    // else Skip
+                const auto policyAndPath = overwritePolicy.obtainOverwritePermission(outpath);
+                if (policyAndPath.policy == OverwritePolicy::Cancel) {
+                    qCDebug(KLEOPATRA_LOG) << "Overwriting canceled for: " << outpath;
+                    break;
+                }
+                switch (policyAndPath.policy) {
+                case OverwritePolicy::Skip:
                     continue;
-                } else if (newPath == outpath) {
+                case OverwritePolicy::Overwrite: {
                     // overwrite existing file
                     if (!QFile::remove(outpath)) {
                         reportError(makeGnuPGError(GPG_ERR_GENERAL), xi18n("Failed to delete <filename>%1</filename>.", outpath));
                         continue;
                     }
-                } else {
+                    break;
+                }
+                case OverwritePolicy::Rename: {
                     // use new name for file
-                    outpath = newPath;
+                    outpath = policyAndPath.fileName;
+                    break;
                 }
+                case OverwritePolicy::Cancel:
+                    // already handled outside of switch
+                    break;
+                case OverwritePolicy::None:
+                case OverwritePolicy::Ask:
+                    qCDebug(KLEOPATRA_LOG) << "Unexpected OverwritePolicy result" << policyAndPath.policy << "for" << outpath;
+                };
             }
             if (!QFile::rename(inpath, outpath)) {
                 reportError(makeGnuPGError(GPG_ERR_GENERAL), xi18n("Failed to move <filename>%1</filename> to <filename>%2</filename>.", inpath, outpath));
             }
         }
     }
     q->emitDoneOrError();
 }
 
 QVector<AutoDecryptVerifyFilesController::Private::CryptoFile> AutoDecryptVerifyFilesController::Private::classifyAndSortFiles(const QStringList &files)
 {
     const auto isSignature = [](int classification) -> bool {
         return mayBeDetachedSignature(classification) //
             || mayBeOpaqueSignature(classification) //
             || (classification & Class::TypeMask) == Class::ClearsignedMessage;
     };
 
     QVector<CryptoFile> out;
     for (const auto &file : files) {
         CryptoFile cFile;
         cFile.fileName = file;
         cFile.baseName = stripSuffix(file);
         cFile.classification = classify(file);
         cFile.protocol = findProtocol(cFile.classification);
 
         auto it = std::find_if(out.begin(), out.end(), [&cFile](const CryptoFile &other) {
             return other.protocol == cFile.protocol && other.baseName == cFile.baseName;
         });
         if (it != out.end()) {
             // If we found a file with the same basename, make sure that encrypted
             // file is before the signature file, so that we first decrypt and then
             // verify
             if (isSignature(cFile.classification) && isCipherText(it->classification)) {
                 out.insert(it + 1, cFile);
             } else if (isCipherText(cFile.classification) && isSignature(it->classification)) {
                 out.insert(it, cFile);
             } else {
                 // both are signatures or both are encrypted files, in which
                 // case order does not matter
                 out.insert(it, cFile);
             }
         } else {
             out.push_back(cFile);
         }
     }
 
     return out;
 }
 
 static bool archiveJobsCanBeUsed([[maybe_unused]] GpgME::Protocol protocol)
 {
     return (protocol == GpgME::OpenPGP) && QGpgME::DecryptVerifyArchiveJob::isSupported();
 }
 
 std::vector<std::shared_ptr<Task>> AutoDecryptVerifyFilesController::Private::buildTasks(const QStringList &fileNames, QStringList &undetected)
 {
     // sort files so that we make sure we first decrypt and then verify
     QVector<CryptoFile> cryptoFiles = classifyAndSortFiles(fileNames);
 
     std::vector<std::shared_ptr<Task>> tasks;
     for (auto it = cryptoFiles.begin(), end = cryptoFiles.end(); it != end; ++it) {
         auto &cFile = (*it);
         QFileInfo fi(cFile.fileName);
         qCDebug(KLEOPATRA_LOG) << "classified" << cFile.fileName << "as" << printableClassification(cFile.classification);
 
         if (!fi.isReadable()) {
             reportError(makeGnuPGError(GPG_ERR_ASS_NO_INPUT), xi18n("Cannot open <filename>%1</filename> for reading.", cFile.fileName));
             continue;
         }
 
         if (mayBeAnyCertStoreType(cFile.classification)) {
             // Trying to verify a certificate. Possible because extensions are often similar
             // for PGP Keys.
             reportError(makeGnuPGError(GPG_ERR_ASS_NO_INPUT),
                         xi18n("The file <filename>%1</filename> contains certificates and can't be decrypted or verified.", cFile.fileName));
             qCDebug(KLEOPATRA_LOG) << "reported error";
             continue;
         }
 
         // We can't reliably detect CMS detached signatures, so we will try to do
         // our best to use the current file as a detached signature and fallback to
         // opaque signature otherwise.
         if (cFile.protocol == GpgME::CMS && mayBeDetachedSignature(cFile.classification)) {
             // First, see if previous task was a decryption task for the same file
             // and "pipe" it's output into our input
             std::shared_ptr<Input> input;
             bool prepend = false;
             if (it != cryptoFiles.begin()) {
                 const auto prev = it - 1;
                 if (prev->protocol == cFile.protocol && prev->baseName == cFile.baseName) {
                     input = Input::createFromOutput(prev->output);
                     prepend = true;
                 }
             }
 
             if (!input) {
                 if (QFile::exists(cFile.baseName)) {
                     input = Input::createFromFile(cFile.baseName);
                 }
             }
 
             if (input) {
                 qCDebug(KLEOPATRA_LOG) << "Detached CMS verify: " << cFile.fileName;
                 std::shared_ptr<VerifyDetachedTask> t(new VerifyDetachedTask);
                 t->setInput(Input::createFromFile(cFile.fileName));
                 t->setSignedData(input);
                 t->setProtocol(cFile.protocol);
                 if (prepend) {
                     // Put the verify task BEFORE the decrypt task in the tasks queue,
                     // because the tasks are executed in reverse order!
                     tasks.insert(tasks.end() - 1, t);
                 } else {
                     tasks.push_back(t);
                 }
                 continue;
             } else {
                 // No signed data, maybe not a detached signature
             }
         }
 
         if (isDetachedSignature(cFile.classification)) {
             // Detached signature, try to find data or ask the user.
             QString signedDataFileName = cFile.baseName;
             if (!QFile::exists(signedDataFileName)) {
                 signedDataFileName = QFileDialog::getOpenFileName(nullptr,
                                                                   xi18n("Select the file to verify with the signature <filename>%1</filename>", fi.fileName()),
                                                                   fi.path());
             }
             if (signedDataFileName.isEmpty()) {
                 qCDebug(KLEOPATRA_LOG) << "No signed data selected. Verify aborted.";
             } else {
                 qCDebug(KLEOPATRA_LOG) << "Detached verify: " << cFile.fileName << " Data: " << signedDataFileName;
                 std::shared_ptr<VerifyDetachedTask> t(new VerifyDetachedTask);
 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
                 if (cFile.protocol == GpgME::OpenPGP) {
                     t->setSignatureFile(cFile.fileName);
                     t->setSignedFile(signedDataFileName);
                 } else {
                     t->setInput(Input::createFromFile(cFile.fileName));
                     t->setSignedData(Input::createFromFile(signedDataFileName));
                 }
 #else
                 t->setInput(Input::createFromFile(cFile.fileName));
                 t->setSignedData(Input::createFromFile(signedDataFileName));
 #endif
                 t->setProtocol(cFile.protocol);
                 tasks.push_back(t);
             }
             continue;
         }
 
         if (!mayBeAnyMessageType(cFile.classification)) {
             // Not a Message? Maybe there is a signature for this file?
             const auto signatures = findSignatures(cFile.fileName);
             bool foundSig = false;
             if (!signatures.empty()) {
                 for (const QString &sig : signatures) {
                     const auto classification = classify(sig);
                     qCDebug(KLEOPATRA_LOG) << "Guessing: " << sig << " is a signature for: " << cFile.fileName << "Classification: " << classification;
                     const auto proto = findProtocol(classification);
                     if (proto == GpgME::UnknownProtocol) {
                         qCDebug(KLEOPATRA_LOG) << "Could not determine protocol. Skipping guess.";
                         continue;
                     }
                     foundSig = true;
                     std::shared_ptr<VerifyDetachedTask> t(new VerifyDetachedTask);
 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
                     if (cFile.protocol == GpgME::OpenPGP) {
                         t->setSignatureFile(sig);
                         t->setSignedFile(cFile.fileName);
                     } else {
                         t->setInput(Input::createFromFile(sig));
                         t->setSignedData(Input::createFromFile(cFile.fileName));
                     }
 #else
                     t->setInput(Input::createFromFile(sig));
                     t->setSignedData(Input::createFromFile(cFile.fileName));
 #endif
                     t->setProtocol(proto);
                     tasks.push_back(t);
                 }
             }
             if (!foundSig) {
                 undetected << cFile.fileName;
                 qCDebug(KLEOPATRA_LOG) << "Failed detection for: " << cFile.fileName << " adding to undetected.";
             }
         } else {
             const FileOperationsPreferences fileOpSettings;
             // Any Message type so we have input and output.
             std::shared_ptr<Input> input;
 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
             if (cFile.protocol != GpgME::OpenPGP) {
                 input = Input::createFromFile(cFile.fileName);
             }
 #else
             input = Input::createFromFile(cFile.fileName);
 #endif
 
             std::shared_ptr<ArchiveDefinition> ad;
             if (fileOpSettings.autoExtractArchives()) {
                 const auto archiveDefinitions = ArchiveDefinition::getArchiveDefinitions();
                 ad = q->pick_archive_definition(cFile.protocol, archiveDefinitions, cFile.fileName);
             }
 
             if (fileOpSettings.dontUseTmpDir()) {
                 if (!m_workDir) {
                     m_workDir = std::make_unique<QTemporaryDir>(heuristicBaseDirectory(fileNames) + QStringLiteral("/kleopatra-XXXXXX"));
                 }
                 if (!m_workDir->isValid()) {
                     qCDebug(KLEOPATRA_LOG) << heuristicBaseDirectory(fileNames) << "not a valid temporary directory.";
                     m_workDir.reset();
                 }
             }
             if (!m_workDir) {
                 m_workDir = std::make_unique<QTemporaryDir>();
             }
             qCDebug(KLEOPATRA_LOG) << "Using:" << m_workDir->path() << "as temporary directory.";
 
             const auto wd = QDir(m_workDir->path());
 
             std::shared_ptr<Output> output;
             QString outputFilePath;
             if (ad) {
                 if ((ad->id() == QLatin1String{"tar"}) && archiveJobsCanBeUsed(cFile.protocol)) {
                     // we don't need an output
                 } else {
                     output = ad->createOutputFromUnpackCommand(cFile.protocol, ad->stripExtension(cFile.protocol, cFile.baseName), wd);
                 }
             } else {
                 outputFilePath = wd.absoluteFilePath(outputFileName(fi.fileName()));
 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
                 if (cFile.protocol != GpgME::OpenPGP) {
                     output = Output::createFromFile(outputFilePath, false);
                 }
 #else
                 output = Output::createFromFile(outputFilePath, false);
 #endif
             }
 
             // If this might be opaque CMS signature, then try that. We already handled
             // detached CMS signature above
             const auto isCMSOpaqueSignature = cFile.protocol == GpgME::CMS && mayBeOpaqueSignature(cFile.classification);
 
             if (isOpaqueSignature(cFile.classification) || isCMSOpaqueSignature) {
                 qCDebug(KLEOPATRA_LOG) << "creating a VerifyOpaqueTask";
                 std::shared_ptr<VerifyOpaqueTask> t(new VerifyOpaqueTask);
                 if (input) {
                     t->setInput(input);
                 }
                 if (output) {
                     t->setOutput(output);
                 }
                 t->setProtocol(cFile.protocol);
                 if (ad) {
                     t->setExtractArchive(true);
                     t->setInputFile(cFile.fileName);
                     if (output) {
                         t->setOutputDirectory(m_workDir->path());
                     } else {
                         // make gpgtar extract to a subfolder of the work directory based on the input file name
                         const auto baseFileName = QFileInfo{ad->stripExtension(cFile.protocol, cFile.baseName)}.fileName();
                         t->setOutputDirectory(QDir{m_workDir->path()}.filePath(baseFileName));
                     }
 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
                 } else if (cFile.protocol == GpgME::OpenPGP) {
                     t->setInputFile(cFile.fileName);
                     t->setOutputFile(outputFilePath);
 #endif
                 }
                 tasks.push_back(t);
             } else {
                 // Any message. That is not an opaque signature needs to be
                 // decrypted. Verify we always do because we can't know if
                 // an encrypted message is also signed.
                 qCDebug(KLEOPATRA_LOG) << "creating a DecryptVerifyTask";
                 std::shared_ptr<DecryptVerifyTask> t(new DecryptVerifyTask);
                 if (input) {
                     t->setInput(input);
                 }
                 if (output) {
                     t->setOutput(output);
                 }
                 t->setProtocol(cFile.protocol);
                 if (ad) {
                     t->setExtractArchive(true);
                     t->setInputFile(cFile.fileName);
                     if (output) {
                         t->setOutputDirectory(m_workDir->path());
                     } else {
                         // make gpgtar extract to a subfolder of the work directory based on the input file name
                         const auto baseFileName = QFileInfo{ad->stripExtension(cFile.protocol, cFile.baseName)}.fileName();
                         t->setOutputDirectory(QDir{m_workDir->path()}.filePath(baseFileName));
                     }
 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
                 } else if (cFile.protocol == GpgME::OpenPGP) {
                     t->setInputFile(cFile.fileName);
                     t->setOutputFile(outputFilePath);
 #endif
                 }
                 cFile.output = output;
                 tasks.push_back(t);
             }
         }
     }
 
     return tasks;
 }
 
 void AutoDecryptVerifyFilesController::setFiles(const QStringList &files)
 {
     d->m_passedFiles = files;
 }
 
 AutoDecryptVerifyFilesController::AutoDecryptVerifyFilesController(QObject *parent)
     : DecryptVerifyFilesController(parent)
     , d(new Private(this))
 {
 }
 
 AutoDecryptVerifyFilesController::AutoDecryptVerifyFilesController(const std::shared_ptr<const ExecutionContext> &ctx, QObject *parent)
     : DecryptVerifyFilesController(ctx, parent)
     , d(new Private(this))
 {
 }
 
 AutoDecryptVerifyFilesController::~AutoDecryptVerifyFilesController()
 {
     qCDebug(KLEOPATRA_LOG);
 }
 
 void AutoDecryptVerifyFilesController::start()
 {
     d->exec();
 }
 
 void AutoDecryptVerifyFilesController::setOperation(DecryptVerifyOperation op)
 {
     d->m_operation = op;
 }
 
 DecryptVerifyOperation AutoDecryptVerifyFilesController::operation() const
 {
     return d->m_operation;
 }
 
 void AutoDecryptVerifyFilesController::Private::cancelAllTasks()
 {
     // we just kill all runnable tasks - this will not result in
     // signal emissions.
     m_runnableTasks.clear();
 
     // a cancel() will result in a call to
     if (m_runningTask) {
         m_runningTask->cancel();
     }
 }
 
 void AutoDecryptVerifyFilesController::cancel()
 {
     qCDebug(KLEOPATRA_LOG) << this << __func__;
     try {
         d->m_errorDetected = true;
         if (d->m_dialog) {
             d->m_dialog->close();
         }
         d->cancelAllTasks();
     } catch (const std::exception &e) {
         qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what();
     }
 }
 
 void AutoDecryptVerifyFilesController::doTaskDone(const Task *task, const std::shared_ptr<const Task::Result> &result)
 {
     Q_ASSERT(task);
     Q_UNUSED(task)
 
     // We could just delete the tasks here, but we can't use
     // Qt::QueuedConnection here (we need sender()) and other slots
     // might not yet have executed. Therefore, we push completed tasks
     // into a burial container
 
     d->m_completedTasks.push_back(d->m_runningTask);
     d->m_runningTask.reset();
 
     if (const std::shared_ptr<const DecryptVerifyResult> &dvr = std::dynamic_pointer_cast<const DecryptVerifyResult>(result)) {
         d->m_results.push_back(dvr);
     }
 
     QTimer::singleShot(0, this, SLOT(schedule()));
 }
 #include "moc_autodecryptverifyfilescontroller.cpp"
diff --git a/src/crypto/signencryptfilescontroller.cpp b/src/crypto/signencryptfilescontroller.cpp
index 1515bddb4..4bd7311c8 100644
--- a/src/crypto/signencryptfilescontroller.cpp
+++ b/src/crypto/signencryptfilescontroller.cpp
@@ -1,789 +1,800 @@
 /* -*- mode: c++; c-basic-offset:4 -*-
     crypto/signencryptfilescontroller.cpp
 
     This file is part of Kleopatra, the KDE keymanager
     SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
 
     SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
     SPDX-FileContributor: Intevation GmbH
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 #include <config-kleopatra.h>
 
 #include "signencryptfilescontroller.h"
 
 #include "signencrypttask.h"
 
 #include "crypto/gui/signencryptfileswizard.h"
 #include "crypto/taskcollection.h"
 
 #include "fileoperationspreferences.h"
 
 #include "utils/archivedefinition.h"
 #include "utils/input.h"
 #include "utils/kleo_assert.h"
 #include "utils/output.h"
 #include "utils/path-helper.h"
 
 #include <Libkleo/Classify>
 #include <Libkleo/KleoException>
 
 #include "kleopatra_debug.h"
 #include <KLocalizedString>
 
 #include <QGpgME/SignEncryptArchiveJob>
 
 #include <QDir>
 #include <QFileInfo>
 #include <QPointer>
 #include <QTimer>
 
 using namespace Kleo;
 using namespace Kleo::Crypto;
 using namespace GpgME;
 
 class SignEncryptFilesController::Private
 {
     friend class ::Kleo::Crypto::SignEncryptFilesController;
     SignEncryptFilesController *const q;
 
 public:
     explicit Private(SignEncryptFilesController *qq);
     ~Private();
 
 private:
     void slotWizardOperationPrepared();
     void slotWizardCanceled();
 
 private:
     void ensureWizardCreated();
     void ensureWizardVisible();
     void updateWizardMode();
     void cancelAllTasks();
     void reportError(int err, const QString &details)
     {
         q->setLastError(err, details);
         q->emitDoneOrError();
     }
 
     void schedule();
     std::shared_ptr<SignEncryptTask> takeRunnable(GpgME::Protocol proto);
 
     static void assertValidOperation(unsigned int);
     static QString titleForOperation(unsigned int op);
 
 private:
     std::vector<std::shared_ptr<SignEncryptTask>> runnable, completed;
     std::shared_ptr<SignEncryptTask> cms, openpgp;
     QPointer<SignEncryptFilesWizard> wizard;
     QStringList files;
     unsigned int operation;
     Protocol protocol;
 };
 
 SignEncryptFilesController::Private::Private(SignEncryptFilesController *qq)
     : q(qq)
     , runnable()
     , cms()
     , openpgp()
     , wizard()
     , files()
     , operation(SignAllowed | EncryptAllowed | ArchiveAllowed)
     , protocol(UnknownProtocol)
 {
 }
 
 SignEncryptFilesController::Private::~Private()
 {
     qCDebug(KLEOPATRA_LOG) << q << __func__;
 }
 
 QString SignEncryptFilesController::Private::titleForOperation(unsigned int op)
 {
     const bool signDisallowed = (op & SignMask) == SignDisallowed;
     const bool encryptDisallowed = (op & EncryptMask) == EncryptDisallowed;
     const bool archiveSelected = (op & ArchiveMask) == ArchiveForced;
 
     kleo_assert(!signDisallowed || !encryptDisallowed);
 
     if (!signDisallowed && encryptDisallowed) {
         if (archiveSelected) {
             return i18n("Archive and Sign Files");
         } else {
             return i18n("Sign Files");
         }
     }
 
     if (signDisallowed && !encryptDisallowed) {
         if (archiveSelected) {
             return i18n("Archive and Encrypt Files");
         } else {
             return i18n("Encrypt Files");
         }
     }
 
     if (archiveSelected) {
         return i18n("Archive and Sign/Encrypt Files");
     } else {
         return i18n("Sign/Encrypt Files");
     }
 }
 
 SignEncryptFilesController::SignEncryptFilesController(QObject *p)
     : Controller(p)
     , d(new Private(this))
 {
 }
 
 SignEncryptFilesController::SignEncryptFilesController(const std::shared_ptr<const ExecutionContext> &ctx, QObject *p)
     : Controller(ctx, p)
     , d(new Private(this))
 {
 }
 
 SignEncryptFilesController::~SignEncryptFilesController()
 {
     qCDebug(KLEOPATRA_LOG) << this << __func__;
     if (d->wizard && !d->wizard->isVisible()) {
         delete d->wizard;
     }
     // d->wizard->close(); ### ?
 }
 
 void SignEncryptFilesController::setProtocol(Protocol proto)
 {
     kleo_assert(d->protocol == UnknownProtocol || d->protocol == proto);
     d->protocol = proto;
     d->ensureWizardCreated();
 }
 
 Protocol SignEncryptFilesController::protocol() const
 {
     return d->protocol;
 }
 
 // static
 void SignEncryptFilesController::Private::assertValidOperation(unsigned int op)
 {
     kleo_assert((op & SignMask) == SignDisallowed || //
                 (op & SignMask) == SignAllowed || //
                 (op & SignMask) == SignSelected);
     kleo_assert((op & EncryptMask) == EncryptDisallowed || //
                 (op & EncryptMask) == EncryptAllowed || //
                 (op & EncryptMask) == EncryptSelected);
     kleo_assert((op & ArchiveMask) == ArchiveDisallowed || //
                 (op & ArchiveMask) == ArchiveAllowed || //
                 (op & ArchiveMask) == ArchiveForced);
     kleo_assert((op & ~(SignMask | EncryptMask | ArchiveMask)) == 0);
 }
 
 void SignEncryptFilesController::setOperationMode(unsigned int mode)
 {
     Private::assertValidOperation(mode);
     d->operation = mode;
     d->updateWizardMode();
 }
 
 void SignEncryptFilesController::Private::updateWizardMode()
 {
     if (!wizard) {
         return;
     }
     wizard->setWindowTitle(titleForOperation(operation));
     const unsigned int signOp = (operation & SignMask);
     const unsigned int encrOp = (operation & EncryptMask);
     const unsigned int archOp = (operation & ArchiveMask);
 
     if (signOp == SignDisallowed) {
         wizard->setSigningUserMutable(false);
         wizard->setSigningPreset(false);
     } else {
         wizard->setSigningUserMutable(true);
         wizard->setSigningPreset(signOp == SignSelected);
     }
 
     if (encrOp == EncryptDisallowed) {
         wizard->setEncryptionPreset(false);
         wizard->setEncryptionUserMutable(false);
     } else {
         wizard->setEncryptionUserMutable(true);
         wizard->setEncryptionPreset(encrOp == EncryptSelected);
     }
 
     wizard->setArchiveForced(archOp == ArchiveForced);
     wizard->setArchiveMutable(archOp == ArchiveAllowed);
 }
 
 unsigned int SignEncryptFilesController::operationMode() const
 {
     return d->operation;
 }
 
 static QString extension(bool pgp, bool sign, bool encrypt, bool ascii, bool detached)
 {
     unsigned int cls = pgp ? Class::OpenPGP : Class::CMS;
     if (encrypt) {
         cls |= Class::CipherText;
     } else if (sign) {
         cls |= detached ? Class::DetachedSignature : Class::OpaqueSignature;
     }
     cls |= ascii ? Class::Ascii : Class::Binary;
     const bool usePGPFileExt = FileOperationsPreferences().usePGPFileExt();
     const auto ext = outputFileExtension(cls, usePGPFileExt);
     if (!ext.isEmpty()) {
         return ext;
     } else {
         return QStringLiteral("out");
     }
 }
 
 static std::shared_ptr<ArchiveDefinition> getDefaultAd()
 {
     const std::vector<std::shared_ptr<ArchiveDefinition>> ads = ArchiveDefinition::getArchiveDefinitions();
     Q_ASSERT(!ads.empty());
     std::shared_ptr<ArchiveDefinition> ad = ads.front();
     const FileOperationsPreferences prefs;
     const QString archiveCmd = prefs.archiveCommand();
     auto it = std::find_if(ads.cbegin(), ads.cend(), [&archiveCmd](const std::shared_ptr<ArchiveDefinition> &toCheck) {
         return toCheck->id() == archiveCmd;
     });
     if (it != ads.cend()) {
         ad = *it;
     }
     return ad;
 }
 
 static QMap<int, QString> buildOutputNames(const QStringList &files, const bool archive)
 {
     QMap<int, QString> nameMap;
 
     // Build the default names for the wizard.
     QString baseNameCms;
     QString baseNamePgp;
     const QFileInfo firstFile(files.first());
     if (archive) {
         QString baseName = files.size() > 1 ? i18nc("base name of an archive file, e.g. archive.zip or archive.tar.gz", "archive") : firstFile.baseName();
         baseName = QDir(heuristicBaseDirectory(files)).absoluteFilePath(baseName);
 
         const auto ad = getDefaultAd();
         baseNamePgp = baseName + QLatin1Char('.') + ad->extensions(GpgME::OpenPGP).first() + QLatin1Char('.');
         baseNameCms = baseName + QLatin1Char('.') + ad->extensions(GpgME::CMS).first() + QLatin1Char('.');
     } else {
         baseNameCms = baseNamePgp = files.first() + QLatin1Char('.');
     }
     const FileOperationsPreferences prefs;
     const bool ascii = prefs.addASCIIArmor();
 
     nameMap.insert(SignEncryptFilesWizard::SignatureCMS, baseNameCms + extension(false, true, false, ascii, true));
     nameMap.insert(SignEncryptFilesWizard::EncryptedCMS, baseNameCms + extension(false, false, true, ascii, false));
     nameMap.insert(SignEncryptFilesWizard::CombinedPGP, baseNamePgp + extension(true, true, true, ascii, false));
     nameMap.insert(SignEncryptFilesWizard::EncryptedPGP, baseNamePgp + extension(true, false, true, ascii, false));
     nameMap.insert(SignEncryptFilesWizard::SignaturePGP, baseNamePgp + extension(true, true, false, ascii, true));
     nameMap.insert(SignEncryptFilesWizard::Directory, heuristicBaseDirectory(files));
     return nameMap;
 }
 
 static QMap<int, QString> buildOutputNamesForDir(const QString &file, const QMap<int, QString> &orig)
 {
     QMap<int, QString> ret;
 
     const QString dir = orig.value(SignEncryptFilesWizard::Directory);
     if (dir.isEmpty()) {
         return orig;
     }
 
     // Build the default names for the wizard.
     const QFileInfo fi(file);
     const QString baseName = dir + QLatin1Char('/') + fi.fileName() + QLatin1Char('.');
 
     const FileOperationsPreferences prefs;
     const bool ascii = prefs.addASCIIArmor();
 
     ret.insert(SignEncryptFilesWizard::SignatureCMS, baseName + extension(false, true, false, ascii, true));
     ret.insert(SignEncryptFilesWizard::EncryptedCMS, baseName + extension(false, false, true, ascii, false));
     ret.insert(SignEncryptFilesWizard::CombinedPGP, baseName + extension(true, true, true, ascii, false));
     ret.insert(SignEncryptFilesWizard::EncryptedPGP, baseName + extension(true, false, true, ascii, false));
     ret.insert(SignEncryptFilesWizard::SignaturePGP, baseName + extension(true, true, false, ascii, true));
     return ret;
 }
 
 // strips all trailing slashes from the filename, but keeps filename "/"
 static QString stripTrailingSlashes(const QString &fileName)
 {
     if (fileName.size() < 2 || !fileName.endsWith(QLatin1Char('/'))) {
         return fileName;
     }
     auto tmp = QStringView{fileName}.chopped(1);
     while (tmp.size() > 1 && tmp.endsWith(QLatin1Char('/'))) {
         tmp.chop(1);
     }
     return tmp.toString();
 }
 
 static QStringList stripTrailingSlashesForAll(const QStringList &fileNames)
 {
     QStringList result;
     result.reserve(fileNames.size());
     std::transform(fileNames.begin(), fileNames.end(), std::back_inserter(result), &stripTrailingSlashes);
     return result;
 }
 
 void SignEncryptFilesController::setFiles(const QStringList &files)
 {
     kleo_assert(!files.empty());
     d->files = stripTrailingSlashesForAll(files);
     bool archive = false;
 
     if (d->files.size() > 1) {
         setOperationMode((operationMode() & ~ArchiveMask) | ArchiveAllowed);
         archive = true;
     }
     for (const auto &file : d->files) {
         if (QFileInfo(file).isDir()) {
             setOperationMode((operationMode() & ~ArchiveMask) | ArchiveForced);
             archive = true;
             break;
         }
     }
     d->ensureWizardCreated();
     d->wizard->setSingleFile(!archive);
     d->wizard->setOutputNames(buildOutputNames(d->files, archive));
 }
 
 void SignEncryptFilesController::Private::slotWizardCanceled()
 {
     qCDebug(KLEOPATRA_LOG) << q << __func__;
     q->cancel();
     reportError(gpg_error(GPG_ERR_CANCELED), i18n("User cancel"));
 }
 
 void SignEncryptFilesController::start()
 {
     d->ensureWizardVisible();
 }
 
 static std::shared_ptr<SignEncryptTask> createSignEncryptTaskForFileInfo(const QFileInfo &fi,
                                                                          bool ascii,
                                                                          const std::vector<Key> &recipients,
                                                                          const std::vector<Key> &signers,
                                                                          const QString &outputName,
                                                                          bool symmetric)
 {
     const std::shared_ptr<SignEncryptTask> task(new SignEncryptTask);
     Q_ASSERT(!signers.empty() || !recipients.empty() || symmetric);
     task->setAsciiArmor(ascii);
     if (!signers.empty()) {
         task->setSign(true);
         task->setSigners(signers);
         task->setDetachedSignature(true);
     } else {
         task->setSign(false);
     }
     if (!recipients.empty()) {
         task->setEncrypt(true);
         task->setRecipients(recipients);
         task->setDetachedSignature(false);
     } else {
         task->setEncrypt(false);
     }
     task->setEncryptSymmetric(symmetric);
     const QString input = fi.absoluteFilePath();
     task->setInputFileName(input);
 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
     if (task->protocol() != GpgME::OpenPGP) {
         task->setInput(Input::createFromFile(input));
     }
 #else
     task->setInput(Input::createFromFile(input));
 #endif
 
     task->setOutputFileName(outputName);
 
     return task;
 }
 
 static bool archiveJobsCanBeUsed([[maybe_unused]] GpgME::Protocol protocol)
 {
     return (protocol == GpgME::OpenPGP) && QGpgME::SignEncryptArchiveJob::isSupported();
 }
 
 static std::shared_ptr<SignEncryptTask> createArchiveSignEncryptTaskForFiles(const QStringList &files,
                                                                              const std::shared_ptr<ArchiveDefinition> &ad,
                                                                              bool pgp,
                                                                              bool ascii,
                                                                              const std::vector<Key> &recipients,
                                                                              const std::vector<Key> &signers,
                                                                              const QString &outputName,
                                                                              bool symmetric)
 {
     const std::shared_ptr<SignEncryptTask> task(new SignEncryptTask);
     task->setCreateArchive(true);
     task->setEncryptSymmetric(symmetric);
     Q_ASSERT(!signers.empty() || !recipients.empty() || symmetric);
     task->setAsciiArmor(ascii);
     if (!signers.empty()) {
         task->setSign(true);
         task->setSigners(signers);
         task->setDetachedSignature(false);
     } else {
         task->setSign(false);
     }
     if (!recipients.empty()) {
         task->setEncrypt(true);
         task->setRecipients(recipients);
     } else {
         task->setEncrypt(false);
     }
 
     const Protocol proto = pgp ? OpenPGP : CMS;
 
     task->setInputFileNames(files);
     if (!archiveJobsCanBeUsed(proto)) {
         // use legacy archive creation
         kleo_assert(ad);
         task->setInput(ad->createInputFromPackCommand(proto, files));
     }
 
     task->setOutputFileName(outputName);
 
     return task;
 }
 
 static std::vector<std::shared_ptr<SignEncryptTask>> createSignEncryptTasksForFileInfo(const QFileInfo &fi,
                                                                                        bool ascii,
                                                                                        const std::vector<Key> &pgpRecipients,
                                                                                        const std::vector<Key> &pgpSigners,
                                                                                        const std::vector<Key> &cmsRecipients,
                                                                                        const std::vector<Key> &cmsSigners,
                                                                                        const QMap<int, QString> &outputNames,
                                                                                        bool symmetric)
 {
     std::vector<std::shared_ptr<SignEncryptTask>> result;
 
     const bool pgp = !pgpSigners.empty() || !pgpRecipients.empty();
 
     const bool cms = !cmsSigners.empty() || !cmsRecipients.empty();
 
     result.reserve(pgp + cms);
 
     if (pgp || symmetric) {
         // Symmetric encryption is only supported for PGP
         int outKind = 0;
         if ((!pgpRecipients.empty() || symmetric) && !pgpSigners.empty()) {
             outKind = SignEncryptFilesWizard::CombinedPGP;
         } else if (!pgpRecipients.empty() || symmetric) {
             outKind = SignEncryptFilesWizard::EncryptedPGP;
         } else {
             outKind = SignEncryptFilesWizard::SignaturePGP;
         }
         result.push_back(createSignEncryptTaskForFileInfo(fi, ascii, pgpRecipients, pgpSigners, outputNames[outKind], symmetric));
     }
     if (cms) {
         // There is no combined sign / encrypt in gpgsm so we create one sign task
         // and one encrypt task. Which leaves us with the age old dilemma, encrypt
         // then sign, or sign then encrypt. Ugly.
         if (!cmsSigners.empty()) {
             result.push_back(
                 createSignEncryptTaskForFileInfo(fi, ascii, std::vector<Key>(), cmsSigners, outputNames[SignEncryptFilesWizard::SignatureCMS], false));
         }
         if (!cmsRecipients.empty()) {
             result.push_back(
                 createSignEncryptTaskForFileInfo(fi, ascii, cmsRecipients, std::vector<Key>(), outputNames[SignEncryptFilesWizard::EncryptedCMS], false));
         }
     }
 
     return result;
 }
 
 static std::vector<std::shared_ptr<SignEncryptTask>> createArchiveSignEncryptTasksForFiles(const QStringList &files,
                                                                                            const std::shared_ptr<ArchiveDefinition> &ad,
                                                                                            bool ascii,
                                                                                            const std::vector<Key> &pgpRecipients,
                                                                                            const std::vector<Key> &pgpSigners,
                                                                                            const std::vector<Key> &cmsRecipients,
                                                                                            const std::vector<Key> &cmsSigners,
                                                                                            const QMap<int, QString> &outputNames,
                                                                                            bool symmetric)
 {
     std::vector<std::shared_ptr<SignEncryptTask>> result;
 
     const bool pgp = !pgpSigners.empty() || !pgpRecipients.empty();
 
     const bool cms = !cmsSigners.empty() || !cmsRecipients.empty();
 
     result.reserve(pgp + cms);
 
     if (pgp || symmetric) {
         int outKind = 0;
         if ((!pgpRecipients.empty() || symmetric) && !pgpSigners.empty()) {
             outKind = SignEncryptFilesWizard::CombinedPGP;
         } else if (!pgpRecipients.empty() || symmetric) {
             outKind = SignEncryptFilesWizard::EncryptedPGP;
         } else {
             outKind = SignEncryptFilesWizard::SignaturePGP;
         }
         result.push_back(createArchiveSignEncryptTaskForFiles(files, ad, true, ascii, pgpRecipients, pgpSigners, outputNames[outKind], symmetric));
     }
     if (cms) {
         if (!cmsSigners.empty()) {
             result.push_back(createArchiveSignEncryptTaskForFiles(files,
                                                                   ad,
                                                                   false,
                                                                   ascii,
                                                                   std::vector<Key>(),
                                                                   cmsSigners,
                                                                   outputNames[SignEncryptFilesWizard::SignatureCMS],
                                                                   false));
         }
         if (!cmsRecipients.empty()) {
             result.push_back(createArchiveSignEncryptTaskForFiles(files,
                                                                   ad,
                                                                   false,
                                                                   ascii,
                                                                   cmsRecipients,
                                                                   std::vector<Key>(),
                                                                   outputNames[SignEncryptFilesWizard::EncryptedCMS],
                                                                   false));
         }
     }
 
     return result;
 }
 
 namespace
 {
 static auto resolveFileNameConflicts(const std::vector<std::shared_ptr<SignEncryptTask>> &tasks, QWidget *parent)
 {
     std::vector<std::shared_ptr<SignEncryptTask>> resolvedTasks;
 
     OverwritePolicy overwritePolicy{parent, tasks.size() > 1 ? OverwritePolicy::MultipleFiles : OverwritePolicy::Options{}};
     for (auto &task : tasks) {
         // by default, do not overwrite existing files
         task->setOverwritePolicy(std::make_shared<OverwritePolicy>(OverwritePolicy::Skip));
         const auto outputFileName = task->outputFileName();
         if (QFile::exists(outputFileName)) {
-            const auto newFileName = overwritePolicy.obtainOverwritePermission(outputFileName);
-            if (newFileName.isEmpty()) {
-                if (overwritePolicy.policy() == OverwritePolicy::Cancel) {
-                    resolvedTasks.clear();
-                    break;
-                }
-                // else Skip -> do not add task to the final task list
+            const auto policyAndFileName = overwritePolicy.obtainOverwritePermission(outputFileName);
+            if (policyAndFileName.policy == OverwritePolicy::Cancel) {
+                resolvedTasks.clear();
+                break;
+            }
+            switch (policyAndFileName.policy) {
+            case OverwritePolicy::Skip:
+                // do not add task to the final task list
                 continue;
-            } else if (newFileName != outputFileName) {
-                task->setOutputFileName(newFileName);
-            } else {
+            case OverwritePolicy::Rename: {
+                task->setOutputFileName(policyAndFileName.fileName);
+                break;
+            }
+            case OverwritePolicy::Overwrite: {
                 task->setOverwritePolicy(std::make_shared<OverwritePolicy>(OverwritePolicy::Overwrite));
+                break;
             }
+            case OverwritePolicy::Cancel:
+                // already handled outside of switch
+                break;
+            case OverwritePolicy::None:
+            case OverwritePolicy::Ask:
+                qCDebug(KLEOPATRA_LOG) << "Unexpected OverwritePolicy result" << policyAndFileName.policy << "for" << outputFileName;
+            };
         }
         resolvedTasks.push_back(task);
     }
 
     return resolvedTasks;
 }
 }
 
 void SignEncryptFilesController::Private::slotWizardOperationPrepared()
 {
     try {
         kleo_assert(wizard);
         kleo_assert(!files.empty());
 
         const bool archive = ((wizard->outputNames().value(SignEncryptFilesWizard::Directory).isNull() && files.size() > 1) //
                               || ((operation & ArchiveMask) == ArchiveForced));
 
         const std::vector<Key> recipients = wizard->resolvedRecipients();
         const std::vector<Key> signers = wizard->resolvedSigners();
 
         const FileOperationsPreferences prefs;
         const bool ascii = prefs.addASCIIArmor();
 
         std::vector<Key> pgpRecipients, cmsRecipients, pgpSigners, cmsSigners;
         for (const Key &k : recipients) {
             if (k.protocol() == GpgME::OpenPGP) {
                 pgpRecipients.push_back(k);
             } else {
                 cmsRecipients.push_back(k);
             }
         }
 
         for (const Key &k : signers) {
             if (k.protocol() == GpgME::OpenPGP) {
                 pgpSigners.push_back(k);
             } else {
                 cmsSigners.push_back(k);
             }
         }
 
         std::vector<std::shared_ptr<SignEncryptTask>> tasks;
         if (!archive) {
             tasks.reserve(files.size());
         }
 
         if (archive) {
             tasks = createArchiveSignEncryptTasksForFiles(files,
                                                           getDefaultAd(),
                                                           ascii,
                                                           pgpRecipients,
                                                           pgpSigners,
                                                           cmsRecipients,
                                                           cmsSigners,
                                                           wizard->outputNames(),
                                                           wizard->encryptSymmetric());
         } else {
             for (const QString &file : std::as_const(files)) {
                 const std::vector<std::shared_ptr<SignEncryptTask>> created =
                     createSignEncryptTasksForFileInfo(QFileInfo(file),
                                                       ascii,
                                                       pgpRecipients,
                                                       pgpSigners,
                                                       cmsRecipients,
                                                       cmsSigners,
                                                       buildOutputNamesForDir(file, wizard->outputNames()),
                                                       wizard->encryptSymmetric());
                 tasks.insert(tasks.end(), created.begin(), created.end());
             }
         }
 
         tasks = resolveFileNameConflicts(tasks, wizard);
         if (tasks.empty()) {
             q->cancel();
             return;
         }
 
         kleo_assert(runnable.empty());
 
         runnable.swap(tasks);
 
         for (const auto &task : std::as_const(runnable)) {
             q->connectTask(task);
         }
 
         std::shared_ptr<TaskCollection> coll(new TaskCollection);
 
         std::vector<std::shared_ptr<Task>> tmp;
         std::copy(runnable.begin(), runnable.end(), std::back_inserter(tmp));
         coll->setTasks(tmp);
         wizard->setTaskCollection(coll);
 
         QTimer::singleShot(0, q, SLOT(schedule()));
 
     } catch (const Kleo::Exception &e) {
         reportError(e.error().encodedError(), e.message());
     } catch (const std::exception &e) {
         reportError(
             gpg_error(GPG_ERR_UNEXPECTED),
             i18n("Caught unexpected exception in SignEncryptFilesController::Private::slotWizardOperationPrepared: %1", QString::fromLocal8Bit(e.what())));
     } catch (...) {
         reportError(gpg_error(GPG_ERR_UNEXPECTED), i18n("Caught unknown exception in SignEncryptFilesController::Private::slotWizardOperationPrepared"));
     }
 }
 
 void SignEncryptFilesController::Private::schedule()
 {
     if (!cms)
         if (const std::shared_ptr<SignEncryptTask> t = takeRunnable(CMS)) {
             t->start();
             cms = t;
         }
 
     if (!openpgp)
         if (const std::shared_ptr<SignEncryptTask> t = takeRunnable(OpenPGP)) {
             t->start();
             openpgp = t;
         }
 
     if (!cms && !openpgp) {
         kleo_assert(runnable.empty());
         q->emitDoneOrError();
     }
 }
 
 std::shared_ptr<SignEncryptTask> SignEncryptFilesController::Private::takeRunnable(GpgME::Protocol proto)
 {
     const auto it = std::find_if(runnable.begin(), runnable.end(), [proto](const std::shared_ptr<Task> &task) {
         return task->protocol() == proto;
     });
     if (it == runnable.end()) {
         return std::shared_ptr<SignEncryptTask>();
     }
 
     const std::shared_ptr<SignEncryptTask> result = *it;
     runnable.erase(it);
     return result;
 }
 
 void SignEncryptFilesController::doTaskDone(const Task *task, const std::shared_ptr<const Task::Result> &result)
 {
     Q_UNUSED(result)
     Q_ASSERT(task);
 
     // We could just delete the tasks here, but we can't use
     // Qt::QueuedConnection here (we need sender()) and other slots
     // might not yet have executed. Therefore, we push completed tasks
     // into a burial container
 
     if (task == d->cms.get()) {
         d->completed.push_back(d->cms);
         d->cms.reset();
     } else if (task == d->openpgp.get()) {
         d->completed.push_back(d->openpgp);
         d->openpgp.reset();
     }
 
     QTimer::singleShot(0, this, SLOT(schedule()));
 }
 
 void SignEncryptFilesController::cancel()
 {
     qCDebug(KLEOPATRA_LOG) << this << __func__;
     try {
         if (d->wizard) {
             d->wizard->close();
         }
         d->cancelAllTasks();
     } catch (const std::exception &e) {
         qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what();
     }
 }
 
 void SignEncryptFilesController::Private::cancelAllTasks()
 {
     // we just kill all runnable tasks - this will not result in
     // signal emissions.
     runnable.clear();
 
     // a cancel() will result in a call to
     if (cms) {
         cms->cancel();
     }
     if (openpgp) {
         openpgp->cancel();
     }
 }
 
 void SignEncryptFilesController::Private::ensureWizardCreated()
 {
     if (wizard) {
         return;
     }
 
     std::unique_ptr<SignEncryptFilesWizard> w(new SignEncryptFilesWizard);
     w->setAttribute(Qt::WA_DeleteOnClose);
 
     connect(w.get(), SIGNAL(operationPrepared()), q, SLOT(slotWizardOperationPrepared()), Qt::QueuedConnection);
     connect(w.get(), SIGNAL(rejected()), q, SLOT(slotWizardCanceled()), Qt::QueuedConnection);
     wizard = w.release();
 
     updateWizardMode();
 }
 
 void SignEncryptFilesController::Private::ensureWizardVisible()
 {
     ensureWizardCreated();
     q->bringToForeground(wizard);
 }
 
 #include "moc_signencryptfilescontroller.cpp"
diff --git a/src/utils/output.cpp b/src/utils/output.cpp
index a85c8fbab..5debdd1b6 100644
--- a/src/utils/output.cpp
+++ b/src/utils/output.cpp
@@ -1,827 +1,836 @@
 /* -*- mode: c++; c-basic-offset:4 -*-
     utils/output.cpp
 
     This file is part of Kleopatra, the KDE keymanager
     SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 #include <config-kleopatra.h>
 
 #include "output.h"
 
 #include "cached.h"
 #include "detail_p.h"
 #include "input_p.h"
 #include "kdpipeiodevice.h"
 #include "kleo_assert.h"
 #include "log.h"
 #include "overwritedialog.h"
 
 #include <Libkleo/KleoException>
 
 #include <KFileUtils>
 #include <KLocalizedString>
 #include <KMessageBox>
 
 #include "kleopatra_debug.h"
 
 #include <QApplication>
 #include <QBuffer>
 #include <QClipboard>
 #include <QDir>
 #include <QFileInfo>
 #include <QPointer>
 #include <QProcess>
 #include <QString>
 #include <QTemporaryFile>
 #include <QTimer>
 #include <QUrl>
 #include <QWidget>
 
 #ifdef Q_OS_WIN
 #include <windows.h>
 #endif
 
 #include <cerrno>
 
 using namespace Kleo;
 using namespace Kleo::_detail;
 
 static const int PROCESS_MAX_RUNTIME_TIMEOUT = -1; // no timeout
 static const int PROCESS_TERMINATE_TIMEOUT = 5 * 1000; // 5s
 
 class OverwritePolicy::Private
 {
 public:
     Private(QWidget *p, OverwritePolicy::Options options_, OverwritePolicy::Policy pol)
         : policy(pol)
         , parentWidget(p)
         , options{options_}
     {
     }
 
     OverwritePolicy::Policy policy;
     QWidget *parentWidget;
     OverwritePolicy::Options options;
 };
 
 OverwritePolicy::OverwritePolicy(Policy initialPolicy)
     : d{new Private{nullptr, {}, initialPolicy}}
 {
 }
 
 OverwritePolicy::OverwritePolicy(QWidget *parent, OverwritePolicy::Options options)
     : d{new Private{parent, options, Ask}}
 {
 }
 
 OverwritePolicy::~OverwritePolicy() = default;
 
 OverwritePolicy::Policy OverwritePolicy::policy() const
 {
     return d->policy;
 }
 
 void OverwritePolicy::setPolicy(Policy policy)
 {
     d->policy = policy;
 }
 
 namespace
 {
 
 class TemporaryFile : public QTemporaryFile
 {
 public:
     using QTemporaryFile::QTemporaryFile;
 
     void close() override
     {
         if (isOpen()) {
             m_oldFileName = fileName();
         }
         QTemporaryFile::close();
     }
 
     bool openNonInheritable()
     {
         if (!QTemporaryFile::open()) {
             return false;
         }
 #if defined(Q_OS_WIN)
         // QTemporaryFile (tested with 4.3.3) creates the file handle as inheritable.
         // The handle is then inherited by gpgsm, which prevents deletion of the temp file
         // in FileOutput::doFinalize()
         // There are no inheritable handles under wince
         return SetHandleInformation((HANDLE)_get_osfhandle(handle()), HANDLE_FLAG_INHERIT, 0);
 #endif
         return true;
     }
 
     QString oldFileName() const
     {
         return m_oldFileName;
     }
 
 private:
     QString m_oldFileName;
 };
 
 template<typename T_IODevice>
 struct inhibit_close : T_IODevice {
     explicit inhibit_close()
         : T_IODevice()
     {
     }
     template<typename T1>
     explicit inhibit_close(T1 &t1)
         : T_IODevice(t1)
     {
     }
 
     /* reimp */ void close() override
     {
     }
     void reallyClose()
     {
         T_IODevice::close();
     }
 };
 
 template<typename T_IODevice>
 struct redirect_close : T_IODevice {
     explicit redirect_close()
         : T_IODevice()
         , m_closed(false)
     {
     }
     template<typename T1>
     explicit redirect_close(T1 &t1)
         : T_IODevice(t1)
         , m_closed(false)
     {
     }
 
     /* reimp */ void close() override
     {
         this->closeWriteChannel();
         m_closed = true;
     }
 
     bool isClosed() const
     {
         return m_closed;
     }
 
 private:
     bool m_closed;
 };
 
 class FileOutput;
 class OutputInput : public InputImplBase
 {
 public:
     OutputInput(const std::shared_ptr<FileOutput> &output);
 
     unsigned int classification() const override
     {
         return 0;
     }
 
     void outputFinalized()
     {
         if (!m_ioDevice->open(QIODevice::ReadOnly)) {
             qCCritical(KLEOPATRA_LOG) << "Failed to open file for reading";
         }
     }
 
     std::shared_ptr<QIODevice> ioDevice() const override
     {
         return m_ioDevice;
     }
 
     unsigned long long size() const override
     {
         return 0;
     }
 
 private:
     std::shared_ptr<FileOutput> m_output;
     mutable std::shared_ptr<QIODevice> m_ioDevice = nullptr;
 };
 
 class OutputImplBase : public Output
 {
 public:
     OutputImplBase()
         : Output()
         , m_defaultLabel()
         , m_customLabel()
         , m_errorString()
         , m_isFinalized(false)
         , m_isFinalizing(false)
         , m_cancelPending(false)
         , m_canceled(false)
         , m_binaryOpt(false)
     {
     }
 
     QString label() const override
     {
         return m_customLabel.isEmpty() ? m_defaultLabel : m_customLabel;
     }
     void setLabel(const QString &label) override
     {
         m_customLabel = label;
     }
     void setDefaultLabel(const QString &l)
     {
         m_defaultLabel = l;
     }
     void setBinaryOpt(bool value) override
     {
         m_binaryOpt = value;
     }
     bool binaryOpt() const override
     {
         return m_binaryOpt;
     }
 
     QString errorString() const override
     {
         if (m_errorString.dirty()) {
             m_errorString = doErrorString();
         }
         return m_errorString;
     }
 
     bool isFinalized() const override
     {
         return m_isFinalized;
     }
     void finalize() override
     {
         qCDebug(KLEOPATRA_LOG) << this;
         if (m_isFinalized || m_isFinalizing) {
             return;
         }
         m_isFinalizing = true;
         try {
             doFinalize();
         } catch (...) {
             m_isFinalizing = false;
             throw;
         }
         m_isFinalizing = false;
         m_isFinalized = true;
         if (m_cancelPending) {
             cancel();
         }
     }
 
     void cancel() override
     {
         qCDebug(KLEOPATRA_LOG) << this;
         if (m_isFinalizing) {
             m_cancelPending = true;
         } else if (!m_canceled) {
             m_isFinalizing = true;
             try {
                 doCancel();
             } catch (...) {
             }
             m_isFinalizing = false;
             m_isFinalized = false;
             m_canceled = true;
         }
     }
 
 private:
     virtual QString doErrorString() const
     {
         if (std::shared_ptr<QIODevice> io = ioDevice()) {
             return io->errorString();
         } else {
             return i18n("No output device");
         }
     }
     virtual void doFinalize() = 0;
     virtual void doCancel() = 0;
 
 private:
     QString m_defaultLabel;
     QString m_customLabel;
     mutable cached<QString> m_errorString;
     bool m_isFinalized : 1;
     bool m_isFinalizing : 1;
     bool m_cancelPending : 1;
     bool m_canceled : 1;
     bool m_binaryOpt : 1;
 };
 
 class PipeOutput : public OutputImplBase
 {
 public:
     explicit PipeOutput(assuan_fd_t fd);
 
     std::shared_ptr<QIODevice> ioDevice() const override
     {
         return m_io;
     }
     void doFinalize() override
     {
         m_io->reallyClose();
     }
     void doCancel() override
     {
         doFinalize();
     }
 
 private:
     std::shared_ptr<inhibit_close<KDPipeIODevice>> m_io;
 };
 
 class ProcessStdInOutput : public OutputImplBase
 {
 public:
     explicit ProcessStdInOutput(const QString &cmd, const QStringList &args, const QDir &wd);
 
     std::shared_ptr<QIODevice> ioDevice() const override
     {
         return m_proc;
     }
     void doFinalize() override
     {
         /*
           Make sure the data is written in the output here. If this
           is not done the output will be written in small chunks
           trough the eventloop causing an unnecessary delay before
           the process has even a chance to work and finish.
           This delay is mainly noticeable on Windows where it can
           take ~30 seconds to write out a 10MB file in the 512 byte
           chunks gpgme serves. */
         qCDebug(KLEOPATRA_LOG) << "Waiting for " << m_proc->bytesToWrite() << " Bytes to be written";
         while (m_proc->waitForBytesWritten(PROCESS_MAX_RUNTIME_TIMEOUT))
             ;
 
         if (!m_proc->isClosed()) {
             m_proc->close();
         }
         m_proc->waitForFinished(PROCESS_MAX_RUNTIME_TIMEOUT);
     }
 
     bool failed() const override
     {
         if (!m_proc) {
             return false;
         }
         return !(m_proc->exitStatus() == QProcess::NormalExit && m_proc->exitCode() == 0);
     }
 
     void doCancel() override
     {
         m_proc->terminate();
         QTimer::singleShot(PROCESS_TERMINATE_TIMEOUT, m_proc.get(), &QProcess::kill);
     }
     QString label() const override;
 
 private:
     QString doErrorString() const override;
 
 private:
     const QString m_command;
     const QStringList m_arguments;
     const std::shared_ptr<redirect_close<QProcess>> m_proc;
 };
 
 class FileOutput : public OutputImplBase
 {
 public:
     explicit FileOutput(const QString &fileName, const std::shared_ptr<OverwritePolicy> &policy);
     ~FileOutput() override
     {
         qCDebug(KLEOPATRA_LOG) << this;
     }
 
     QString label() const override
     {
         return QFileInfo(m_fileName).fileName();
     }
     std::shared_ptr<QIODevice> ioDevice() const override
     {
         return m_tmpFile;
     }
     void doFinalize() override;
     void doCancel() override
     {
         qCDebug(KLEOPATRA_LOG) << this;
     }
     QString fileName() const override
     {
         return m_fileName;
     }
 
     void attachInput(const std::shared_ptr<OutputInput> &input)
     {
         m_attachedInput = std::weak_ptr<OutputInput>(input);
     }
 
 private:
     QString m_fileName;
     std::shared_ptr<TemporaryFile> m_tmpFile;
     const std::shared_ptr<OverwritePolicy> m_policy;
     std::weak_ptr<OutputInput> m_attachedInput;
 };
 
 #ifndef QT_NO_CLIPBOARD
 class ClipboardOutput : public OutputImplBase
 {
 public:
     explicit ClipboardOutput(QClipboard::Mode mode);
 
     QString label() const override;
     std::shared_ptr<QIODevice> ioDevice() const override
     {
         return m_buffer;
     }
     void doFinalize() override;
     void doCancel() override
     {
     }
 
 private:
     QString doErrorString() const override
     {
         return QString();
     }
 
 private:
     const QClipboard::Mode m_mode;
     std::shared_ptr<QBuffer> m_buffer;
 };
 #endif // QT_NO_CLIPBOARD
 
 class ByteArrayOutput : public OutputImplBase
 {
 public:
     explicit ByteArrayOutput(QByteArray *data)
         : m_buffer(std::shared_ptr<QBuffer>(new QBuffer(data)))
     {
         if (!m_buffer->open(QIODevice::WriteOnly))
             throw Exception(gpg_error(GPG_ERR_EIO), QStringLiteral("Could not open bytearray for writing?!"));
     }
 
     QString label() const override
     {
         return m_label;
     }
 
     void setLabel(const QString &label) override
     {
         m_label = label;
     }
 
     std::shared_ptr<QIODevice> ioDevice() const override
     {
         return m_buffer;
     }
 
     void doFinalize() override
     {
         m_buffer->close();
     }
 
     void doCancel() override
     {
         m_buffer->close();
     }
 
 private:
     QString doErrorString() const override
     {
         return QString();
     }
 
 private:
     QString m_label;
     std::shared_ptr<QBuffer> m_buffer;
 };
 
 }
 
 std::shared_ptr<Output> Output::createFromPipeDevice(assuan_fd_t fd, const QString &label)
 {
     std::shared_ptr<PipeOutput> po(new PipeOutput(fd));
     po->setDefaultLabel(label);
     return po;
 }
 
 PipeOutput::PipeOutput(assuan_fd_t fd)
     : OutputImplBase()
     , m_io(new inhibit_close<KDPipeIODevice>)
 {
     errno = 0;
     if (!m_io->open(fd, QIODevice::WriteOnly))
         throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO), i18n("Could not open FD %1 for writing", assuanFD2int(fd)));
 }
 
 std::shared_ptr<Output> Output::createFromFile(const QString &fileName, bool forceOverwrite)
 {
     return createFromFile(fileName, std::make_shared<OverwritePolicy>(forceOverwrite ? OverwritePolicy::Overwrite : OverwritePolicy::Skip));
 }
 
 std::shared_ptr<Output> Output::createFromFile(const QString &fileName, const std::shared_ptr<OverwritePolicy> &policy)
 {
     std::shared_ptr<FileOutput> fo(new FileOutput(fileName, policy));
     qCDebug(KLEOPATRA_LOG) << fo.get();
     return fo;
 }
 
 FileOutput::FileOutput(const QString &fileName, const std::shared_ptr<OverwritePolicy> &policy)
     : OutputImplBase()
     , m_fileName(fileName)
     , m_tmpFile(new TemporaryFile(fileName))
     , m_policy(policy)
 {
     Q_ASSERT(m_policy);
     errno = 0;
     if (!m_tmpFile->openNonInheritable())
         throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO), i18n("Could not create temporary file for output \"%1\"", fileName));
 }
 
 static QString suggestFileName(const QString &fileName)
 {
     const QFileInfo fileInfo{fileName};
     const QString path = fileInfo.absolutePath();
     const QString newFileName = KFileUtils::suggestName(QUrl::fromLocalFile(path), fileInfo.fileName());
     return path + QLatin1Char{'/'} + newFileName;
 }
 
-QString OverwritePolicy::obtainOverwritePermission(const QString &fileName)
+OverwritePolicy::PolicyAndFileName OverwritePolicy::obtainOverwritePermission(const QString &fileName)
 {
     switch (d->policy) {
+    case OverwritePolicy::None:
+        // shouldn't happen, but treat it as Ask
     case OverwritePolicy::Ask:
         break;
     case OverwritePolicy::Overwrite:
-        return fileName;
+        return {d->policy, fileName};
     case OverwritePolicy::Rename: {
-        return suggestFileName(fileName);
+        return {d->policy, suggestFileName(fileName)};
     }
     case OverwritePolicy::Skip:
-        return {};
+        return {d->policy, {}};
     case OverwritePolicy::Cancel:
         qCDebug(KLEOPATRA_LOG) << __func__ << "Error: Called although user canceled operation";
-        return {};
+        return {d->policy, {}};
     }
 
     OverwriteDialog::Options options = OverwriteDialog::AllowRename;
     if (d->options & MultipleFiles) {
         options |= OverwriteDialog::MultipleItems | OverwriteDialog::AllowSkip;
     }
     OverwriteDialog dialog{d->parentWidget, i18nc("@title:window", "File Already Exists"), fileName, options};
     const auto result = static_cast<OverwriteDialog::Result>(dialog.exec());
     qCDebug(KLEOPATRA_LOG) << __func__ << "result:" << static_cast<int>(result);
     switch (result) {
     case OverwriteDialog::Cancel:
         d->policy = OverwritePolicy::Cancel;
-        return {};
+        return {OverwritePolicy::Cancel, {}};
     case OverwriteDialog::AutoSkip:
         d->policy = OverwritePolicy::Skip;
         [[fallthrough]];
     case OverwriteDialog::Skip:
-        return {};
+        return {OverwritePolicy::Skip, {}};
     case OverwriteDialog::OverwriteAll:
         d->policy = OverwritePolicy::Overwrite;
         [[fallthrough]];
     case OverwriteDialog::Overwrite:
-        return fileName;
+        return {OverwritePolicy::Overwrite, fileName};
     case OverwriteDialog::Rename:
-        return dialog.newFileName();
+        return {OverwritePolicy::Rename, dialog.newFileName()};
     case OverwriteDialog::AutoRename: {
         d->policy = OverwritePolicy::Rename;
-        return suggestFileName(fileName);
+        return {OverwritePolicy::Rename, suggestFileName(fileName)};
     }
-    default:
-        qCDebug(KLEOPATRA_LOG) << __func__ << "unexpected result:" << result;
     };
-    return {};
+    qCDebug(KLEOPATRA_LOG) << __func__ << "unexpected result:" << result;
+    return {OverwritePolicy::None, {}};
 }
 
 void FileOutput::doFinalize()
 {
     qCDebug(KLEOPATRA_LOG) << this;
 
     struct Remover {
         QString file;
         ~Remover()
         {
             if (QFile::exists(file)) {
                 QFile::remove(file);
             }
         }
     } remover;
 
     kleo_assert(m_tmpFile);
 
     if (m_tmpFile->isOpen()) {
         m_tmpFile->close();
     }
 
     QString tmpFileName = m_tmpFile->oldFileName();
     remover.file = tmpFileName;
 
     m_tmpFile->setAutoRemove(false);
     QPointer<QObject> guard = m_tmpFile.get();
     m_tmpFile.reset(); // really close the file - needed on Windows for renaming :/
     kleo_assert(!guard); // if this triggers, we need to audit for holder of std::shared_ptr<QIODevice>s.
 
     const QFileInfo fi(tmpFileName);
     if (!fi.exists()) {
         /* QT Bug 83365 since qt 5.13 causes the filename of temporary files
          * in UNC path name directories (unmounted samba shares) to start with
          * UNC/ instead of the // that Qt can actually handle for things like
          * rename and remove. So if we can't find our temporary file we try
          * to workaround that bug. */
         qCDebug(KLEOPATRA_LOG) << "failure to find " << tmpFileName;
         if (tmpFileName.startsWith(QLatin1String("UNC"))) {
             tmpFileName.replace(0, strlen("UNC"), QLatin1Char('/'));
             remover.file = tmpFileName;
         }
         const QFileInfo fi2(tmpFileName);
         if (!fi2.exists()) {
             throw Exception(gpg_error(GPG_ERR_EIO), QStringLiteral("Could not find temporary file \"%1\".").arg(tmpFileName));
         }
     }
 
     qCDebug(KLEOPATRA_LOG) << this << "renaming" << tmpFileName << "->" << m_fileName;
     if (QFile::rename(tmpFileName, m_fileName)) {
         qCDebug(KLEOPATRA_LOG) << this << "renaming succeeded";
 
         if (!m_attachedInput.expired()) {
             m_attachedInput.lock()->outputFinalized();
         }
         return;
     }
 
     qCDebug(KLEOPATRA_LOG) << this << "renaming failed";
 
     if (QFile::exists(m_fileName)) {
-        const auto newFileName = m_policy->obtainOverwritePermission(m_fileName);
-        if (newFileName.isEmpty()) {
+        const auto policyAndFileName = m_policy->obtainOverwritePermission(m_fileName);
+        switch (policyAndFileName.policy) {
+        case OverwritePolicy::Cancel:
             throw Exception(gpg_error(GPG_ERR_CANCELED), i18n("Overwriting declined"));
-        }
-        if (newFileName == m_fileName) {
+        case OverwritePolicy::Overwrite: {
             qCDebug(KLEOPATRA_LOG) << this << "going to remove file for overwriting" << m_fileName;
             if (!QFile::remove(m_fileName)) {
                 throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO),
                                 xi18nc("@info", "Could not remove file <filename>%1</filename> for overwriting.", m_fileName));
             }
             qCDebug(KLEOPATRA_LOG) << this << "removing file to overwrite succeeded";
-        } else {
-            m_fileName = newFileName;
+            break;
+        }
+        case OverwritePolicy::Rename: {
+            m_fileName = policyAndFileName.fileName;
+            break;
         }
+        case OverwritePolicy::None:
+        case OverwritePolicy::Ask:
+        case OverwritePolicy::Skip:
+            qCDebug(KLEOPATRA_LOG) << "Unexpected OverwritePolicy result" << policyAndFileName.policy << "for" << m_fileName;
+        };
     }
 
     qCDebug(KLEOPATRA_LOG) << this << "renaming" << tmpFileName << "->" << m_fileName;
     if (QFile::rename(tmpFileName, m_fileName)) {
         qCDebug(KLEOPATRA_LOG) << this << "renaming succeeded";
 
         if (!m_attachedInput.expired()) {
             m_attachedInput.lock()->outputFinalized();
         }
         return;
     }
 
     qCDebug(KLEOPATRA_LOG) << this << "renaming failed";
 
     throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO), i18n(R"(Could not rename file "%1" to "%2")", tmpFileName, m_fileName));
 }
 
 std::shared_ptr<Output> Output::createFromProcessStdIn(const QString &command)
 {
     return std::shared_ptr<Output>(new ProcessStdInOutput(command, QStringList(), QDir::current()));
 }
 
 std::shared_ptr<Output> Output::createFromProcessStdIn(const QString &command, const QStringList &args)
 {
     return std::shared_ptr<Output>(new ProcessStdInOutput(command, args, QDir::current()));
 }
 
 std::shared_ptr<Output> Output::createFromProcessStdIn(const QString &command, const QStringList &args, const QDir &wd)
 {
     return std::shared_ptr<Output>(new ProcessStdInOutput(command, args, wd));
 }
 
 ProcessStdInOutput::ProcessStdInOutput(const QString &cmd, const QStringList &args, const QDir &wd)
     : OutputImplBase()
     , m_command(cmd)
     , m_arguments(args)
     , m_proc(new redirect_close<QProcess>)
 {
     qCDebug(KLEOPATRA_LOG) << "cd" << wd.absolutePath() << '\n' << cmd << args;
     if (cmd.isEmpty())
         throw Exception(gpg_error(GPG_ERR_INV_ARG), i18n("Command not specified"));
     m_proc->setWorkingDirectory(wd.absolutePath());
     m_proc->start(cmd, args);
     m_proc->setReadChannel(QProcess::StandardError);
     if (!m_proc->waitForStarted())
         throw Exception(gpg_error(GPG_ERR_EIO), i18n("Could not start %1 process: %2", cmd, m_proc->errorString()));
 }
 
 QString ProcessStdInOutput::label() const
 {
     if (!m_proc) {
         return OutputImplBase::label();
     }
     // output max. 3 arguments
     const QString cmdline = (QStringList(m_command) + m_arguments.mid(0, 3)).join(QLatin1Char(' '));
     if (m_arguments.size() > 3) {
         return i18nc("e.g. \"Input to tar xf - file1 ...\"", "Input to %1 ...", cmdline);
     } else {
         return i18nc("e.g. \"Input to tar xf - file\"", "Input to %1", cmdline);
     }
 }
 
 QString ProcessStdInOutput::doErrorString() const
 {
     kleo_assert(m_proc);
     if (m_proc->exitStatus() == QProcess::NormalExit && m_proc->exitCode() == 0) {
         return QString();
     }
     if (m_proc->error() == QProcess::UnknownError)
         return i18n("Error while running %1: %2", m_command, QString::fromLocal8Bit(m_proc->readAllStandardError().trimmed().constData()));
     else {
         return i18n("Failed to execute %1: %2", m_command, m_proc->errorString());
     }
 }
 
 #ifndef QT_NO_CLIPBOARD
 std::shared_ptr<Output> Output::createFromClipboard()
 {
     return std::shared_ptr<Output>(new ClipboardOutput(QClipboard::Clipboard));
 }
 
 ClipboardOutput::ClipboardOutput(QClipboard::Mode mode)
     : OutputImplBase()
     , m_mode(mode)
     , m_buffer(new QBuffer)
 {
     errno = 0;
     if (!m_buffer->open(QIODevice::WriteOnly))
         throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO), i18n("Could not write to clipboard"));
 }
 
 QString ClipboardOutput::label() const
 {
     switch (m_mode) {
     case QClipboard::Clipboard:
         return i18n("Clipboard");
     case QClipboard::FindBuffer:
         return i18n("Find buffer");
     case QClipboard::Selection:
         return i18n("Selection");
     }
     return QString();
 }
 
 void ClipboardOutput::doFinalize()
 {
     if (m_buffer->isOpen()) {
         m_buffer->close();
     }
     if (QClipboard *const cb = QApplication::clipboard()) {
         cb->setText(QString::fromUtf8(m_buffer->data()));
     } else
         throw Exception(gpg_error(GPG_ERR_EIO), i18n("Could not find clipboard"));
 }
 #endif // QT_NO_CLIPBOARD
 
 Output::~Output()
 {
 }
 
 OutputInput::OutputInput(const std::shared_ptr<FileOutput> &output)
     : m_output(output)
     , m_ioDevice(new QFile(output->fileName()))
 {
 }
 
 std::shared_ptr<Input> Input::createFromOutput(const std::shared_ptr<Output> &output)
 {
     if (auto fo = std::dynamic_pointer_cast<FileOutput>(output)) {
         auto input = std::shared_ptr<OutputInput>(new OutputInput(fo));
         fo->attachInput(input);
         return input;
     } else {
         return {};
     }
 }
 
 std::shared_ptr<Output> Output::createFromByteArray(QByteArray *data, const QString &label)
 {
     auto ret = std::shared_ptr<ByteArrayOutput>(new ByteArrayOutput(data));
     ret->setLabel(label);
     return ret;
 }
diff --git a/src/utils/output.h b/src/utils/output.h
index 4ff13dbcd..e5389249d 100644
--- a/src/utils/output.h
+++ b/src/utils/output.h
@@ -1,92 +1,104 @@
 /* -*- mode: c++; c-basic-offset:4 -*-
     utils/output.h
 
     This file is part of Kleopatra, the KDE keymanager
     SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 #pragma once
 
 #include <assuan.h> // for assuan_fd_t
 
 #include <QString>
 #include <QStringList>
 
 #include <memory>
 
 class QIODevice;
 class QDir;
 class QWidget;
 
 namespace Kleo
 {
 
 class OverwritePolicy
 {
 public:
     enum Policy {
+        None, ///< indicates an error
         Ask,
         Overwrite,
         Rename,
         Skip,
         Cancel,
     };
 
     enum Options {
         MultipleFiles = 1,
     };
 
+    struct PolicyAndFileName {
+        // define explicit constructor to implicitly delete default constructor
+        PolicyAndFileName(Policy p, const QString &f)
+            : policy{p}
+            , fileName{f}
+        {
+        }
+        Policy policy;
+        QString fileName;
+    };
+
     explicit OverwritePolicy(Policy initialPolicy);
     /// creates an interactive policy, i.e. with initial policy set to Ask
     OverwritePolicy(QWidget *parent, Options options);
     ~OverwritePolicy();
 
     Policy policy() const;
     void setPolicy(Policy);
 
-    /// returns the file name to write to or an empty string if overwriting was declined
-    QString obtainOverwritePermission(const QString &fileName);
+    /// returns the user's (or automatic) choice and the file name to write to or an empty string if overwriting was declined
+    PolicyAndFileName obtainOverwritePermission(const QString &fileName);
 
 private:
     class Private;
     const std::unique_ptr<Private> d;
 };
 
 class Output
 {
 public:
     virtual ~Output();
 
     virtual void setLabel(const QString &label) = 0;
     virtual QString label() const = 0;
     virtual std::shared_ptr<QIODevice> ioDevice() const = 0;
     virtual QString errorString() const = 0;
     virtual bool isFinalized() const = 0;
     virtual void finalize() = 0;
     virtual void cancel() = 0;
     virtual bool binaryOpt() const = 0;
     virtual void setBinaryOpt(bool value) = 0;
     /** Whether or not the output failed. */
     virtual bool failed() const
     {
         return false;
     }
     virtual QString fileName() const
     {
         return {};
     }
 
     static std::shared_ptr<Output> createFromFile(const QString &fileName, const std::shared_ptr<OverwritePolicy> &);
     static std::shared_ptr<Output> createFromFile(const QString &fileName, bool forceOverwrite);
     static std::shared_ptr<Output> createFromPipeDevice(assuan_fd_t fd, const QString &label);
     static std::shared_ptr<Output> createFromProcessStdIn(const QString &command);
     static std::shared_ptr<Output> createFromProcessStdIn(const QString &command, const QStringList &args);
     static std::shared_ptr<Output> createFromProcessStdIn(const QString &command, const QStringList &args, const QDir &workingDirectory);
 #ifndef QT_NO_CLIPBOARD
     static std::shared_ptr<Output> createFromClipboard();
 #endif
     static std::shared_ptr<Output> createFromByteArray(QByteArray *data, const QString &label);
 };
 }