Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F26725158
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
68 KB
Subscribers
None
View Options
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b8236f7f5..86165529f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,268 +1,269 @@
# SPDX-FileCopyrightText: none
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
set(RELEASE_SERVICE_VERSION_MAJOR "23")
set(RELEASE_SERVICE_VERSION_MINOR "03")
set(RELEASE_SERVICE_VERSION_MICRO "70")
# The RELEASE_SERVICE_VERSION is used by Gpg4win to add the Gpg4win version
if (NOT RELEASE_SERVICE_VERSION)
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
endif()
if(RELEASE_SERVICE_VERSION_MICRO LESS 10)
set(KDE_APPLICATIONS_COMPACT_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}${RELEASE_SERVICE_VERSION_MINOR}0${RELEASE_SERVICE_VERSION_MICRO}")
else()
set(KDE_APPLICATIONS_COMPACT_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}${RELEASE_SERVICE_VERSION_MINOR}${RELEASE_SERVICE_VERSION_MICRO}")
endif()
set(KLEOPATRA_VERSION_MAJOR "3")
set(KLEOPATRA_VERSION_MINOR "1")
set(KLEOPATRA_VERSION_MICRO "26")
set(kleopatra_version "${KLEOPATRA_VERSION_MAJOR}.${KLEOPATRA_VERSION_MINOR}.${KLEOPATRA_VERSION_MICRO}.${KDE_APPLICATIONS_COMPACT_VERSION}")
# The following is for Windows
set(kleopatra_version_win "${KLEOPATRA_VERSION_MAJOR}.${KLEOPATRA_VERSION_MINOR}.${KLEOPATRA_VERSION_MICRO}")
set(kleopatra_fileversion_win "${KLEOPATRA_VERSION_MAJOR},${KLEOPATRA_VERSION_MINOR},${KLEOPATRA_VERSION_MICRO},0")
if (NOT KLEOPATRA_DISTRIBUTION_TEXT)
# This is only used on Windows for the file attributes of Kleopatra
set(KLEOPATRA_DISTRIBUTION_TEXT "KDE")
endif()
project(kleopatra VERSION ${kleopatra_version})
option(DISABLE_KWATCHGNUPG "Don't build the kwatchgnupg tool [default=OFF]" OFF)
# Standalone build. Find / include everything necessary.
set(KF_MIN_VERSION "5.102.0")
set(KIDENTITYMANAGEMENT_VERSION "5.22.40")
set(KMAILTRANSPORT_VERSION "5.22.40")
set(KMIME_VERSION "5.22.40")
set(LIBKLEO_VERSION "5.22.45")
set(QT_REQUIRED_VERSION "5.15.2")
if (QT_MAJOR_VERSION STREQUAL "6")
set(QT_REQUIRED_VERSION "6.4.0")
endif()
set(GPGME_REQUIRED_VERSION "1.16.0")
set(LIBASSUAN_REQUIRED_VERSION "2.4.2")
set(GPG_ERROR_REQUIRED_VERSION "1.36")
if (WIN32)
set(KF5_WANT_VERSION "5.70.0")
set(KMIME_WANT_VERSION "5.12.0")
else ()
set(KF5_WANT_VERSION ${KF_MIN_VERSION})
set(KMIME_WANT_VERSION ${KMIME_VERSION})
endif ()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(ECM ${KF5_WANT_VERSION} CONFIG REQUIRED)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
include(ECMInstallIcons)
include(ECMSetupVersion)
include(ECMAddTests)
include(GenerateExportHeader)
include(ECMGenerateHeaders)
include(FeatureSummary)
include(CheckFunctionExists)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(ECMAddAppIcon)
include(ECMQtDeclareLoggingCategory)
include(ECMDeprecationSettings)
include(KDEClangFormat)
if (QT_MAJOR_VERSION STREQUAL "6")
set(QT_REQUIRED_VERSION "6.4.0")
set(KF_MIN_VERSION "5.240.0")
set(KF_MAJOR_VERSION "6")
else()
set(KF_MIN_VERSION "5.100.0")
set(KF_MAJOR_VERSION "5")
endif()
# Find KF5 packages
find_package(KF${KF_MAJOR_VERSION}WidgetsAddons ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF${KF_MAJOR_VERSION}ConfigWidgets ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF${KF_MAJOR_VERSION}CoreAddons ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF${KF_MAJOR_VERSION}Codecs ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF${KF_MAJOR_VERSION}Config ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF${KF_MAJOR_VERSION}I18n ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF${KF_MAJOR_VERSION}IconThemes ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF${KF_MAJOR_VERSION}ItemModels ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF${KF_MAJOR_VERSION}XmlGui ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF${KF_MAJOR_VERSION}WindowSystem ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF${KF_MAJOR_VERSION}DocTools ${KF5_WANT_VERSION} CONFIG)
find_package(KF${KF_MAJOR_VERSION}Crash ${KF5_WANT_VERSION} REQUIRED)
set_package_properties(KF${KF_MAJOR_VERSION}DocTools PROPERTIES
DESCRIPTION "Documentation tools"
PURPOSE "Required to generate Kleopatra documentation."
TYPE OPTIONAL)
# Optional packages
if (WIN32)
# Only a replacement available for Windows so this
# is required on other platforms.
find_package(KF${KF_MAJOR_VERSION}DBusAddons ${KF5_WANT_VERSION} CONFIG)
set_package_properties(KF5DBusAddons PROPERTIES DESCRIPTION "Support library to work with DBus"
PURPOSE "DBus session integration"
URL "https://inqlude.org/libraries/kdbusaddons.html"
TYPE OPTIONAL)
else()
find_package(KF${KF_MAJOR_VERSION}DBusAddons ${KF5_WANT_VERSION} CONFIG REQUIRED)
set(_kleopatra_dbusaddons_libs KF${KF_MAJOR_VERSION}::DBusAddons)
endif()
set(HAVE_QDBUS ${Qt${QT_MAJOR_VERSION}DBus_FOUND})
find_package(Gpgmepp ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
if (Gpgmepp_VERSION VERSION_GREATER_EQUAL "1.19.0")
set(GPGMEPP_SUPPORTS_SET_CURVE 1)
endif()
if (QT_MAJOR_VERSION STREQUAL "6")
find_package(QGpgmeQt6 ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
else()
find_package(QGpgme ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
endif()
if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.17.0")
set(QGPGME_SUPPORTS_CHANGING_EXPIRATION_OF_COMPLETE_KEY 1)
set(QGPGME_CRYPTOCONFIGENTRY_HAS_DEFAULT_VALUE 1)
set(QGPGME_SUPPORTS_WKDLOOKUP 1)
set(QGPGME_SUPPORTS_IMPORT_WITH_FILTER 1)
set(QGPGME_SUPPORTS_IMPORT_WITH_KEY_ORIGIN 1)
set(QGPGME_SUPPORTS_SECRET_KEY_EXPORT 1)
set(QGPGME_SUPPORTS_SECRET_SUBKEY_EXPORT 1)
set(QGPGME_SUPPORTS_RECEIVING_KEYS_BY_KEY_ID 1)
endif()
if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.18.0")
set(QGPGME_SUPPORTS_KEY_REVOCATION 1)
set(QGPGME_SUPPORTS_KEY_REFRESH 1)
set(QGPGME_SUPPORTS_SET_FILENAME 1)
set(QGPGME_SUPPORTS_SET_PRIMARY_UID 1)
endif()
if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.19.0")
set(QGPGME_SUPPORTS_DEFERRED_IMPORT_JOB 1)
+ set(QGPGME_SUPPORTS_ARCHIVE_JOBS 1)
endif()
# Kdepimlibs packages
find_package(KF5Libkleo ${LIBKLEO_VERSION} CONFIG REQUIRED)
find_package(KF5Mime ${KMIME_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5IdentityManagement ${KIDENTITYMANAGEMENT_VERSION} CONFIG)
find_package(KF5MailTransport ${KMAILTRANSPORT_VERSION} CONFIG)
find_package(KF5MailTransportAkonadi ${KMAILTRANSPORT_VERSION} CONFIG)
find_package(Qt${QT_MAJOR_VERSION} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets Test Network PrintSupport)
find_package(LibAssuan ${LIBASSUAN_REQUIRED_VERSION} REQUIRED)
set_package_properties(LibAssuan PROPERTIES
TYPE REQUIRED
PURPOSE "Needed for Kleopatra to act as the GnuPG UI Server"
)
find_package(LibGpgError ${GPG_ERROR_REQUIRED_VERSION} REQUIRED)
set_package_properties(LibGpgError PROPERTIES
TYPE REQUIRED
)
set(kleopatra_release FALSE)
if(NOT kleopatra_release)
find_package(Git)
if(GIT_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE rc
ERROR_QUIET)
if(rc EQUAL 0)
execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%h ${CMAKE_CURRENT_SOURCE_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE Kleopatra_WC_REVISION)
string(REGEX REPLACE "\n" "" Kleopatra_WC_REVISION "${Kleopatra_WC_REVISION}")
execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%cI ${CMAKE_CURRENT_SOURCE_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE Kleopatra_WC_LAST_CHANGED_DATE)
string(REGEX REPLACE "^([0-9]+)-([0-9]+)-([0-9]+)T([0-9]+):([0-9]+):([0-9]+).*$" "\\1\\2\\3T\\4\\5\\6"
Kleopatra_WC_LAST_CHANGED_DATE "${Kleopatra_WC_LAST_CHANGED_DATE}")
set(kleopatra_version "${kleopatra_version}+git${Kleopatra_WC_LAST_CHANGED_DATE}~${Kleopatra_WC_REVISION}")
endif()
endif()
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version-kleopatra.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/version-kleopatra.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-kleopatra.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kleopatra.h)
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
)
if (WIN32)
# On Windows, we need to use stuff deprecated since Qt 5.11, e.g. from QDesktopWidget
ecm_set_disabled_deprecation_versions(QT 5.10.0 KF 5.102.0)
else ()
ecm_set_disabled_deprecation_versions(QT 6.4 KF 5.102.0)
endif ()
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-braces -Wno-parentheses -Wno-ignored-qualifiers")
endif()
if(MINGW)
# we do not care about different signedness of passed pointer arguments
add_compile_options($<$<COMPILE_LANGUAGE:C>:-Wno-pointer-sign>)
endif()
add_definitions(-DQT_NO_EMIT)
remove_definitions(-DQT_NO_FOREACH)
# Disable the use of QStringBuilder for operator+ to prevent crashes when
# returning the result of concatenating string temporaries in lambdas. We do
# this for example in some std::transform expressions.
# This is a known issue: https://bugreports.qt.io/browse/QTBUG-47066
# Alternatively, one would always have to remember to force the lambdas to
# return a QString instead of QStringBuilder, but that's just too easy to
# forget and, unfortunately, the compiler doesn't issue a warning if one forgets
# this. So, it's just too dangerous.
# One can still use QStringBuilder explicitly with the operator% if necessary.
remove_definitions(-DQT_USE_FAST_OPERATOR_PLUS)
remove_definitions(-DQT_USE_QSTRINGBUILDER)
kde_enable_exceptions()
option(USE_UNITY_CMAKE_SUPPORT "Use UNITY cmake support (speedup compile time)" OFF)
set(COMPILE_WITH_UNITY_CMAKE_SUPPORT OFF)
if (USE_UNITY_CMAKE_SUPPORT)
set(COMPILE_WITH_UNITY_CMAKE_SUPPORT ON)
endif()
add_subdirectory(pics)
add_subdirectory(src)
if(BUILD_TESTING)
add_subdirectory(tests)
add_subdirectory(autotests)
endif()
ecm_qt_install_logging_categories(
EXPORT KLEOPATRA
FILE kleopatra.categories
DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
)
ki18n_install(po)
if(KF5DocTools_FOUND)
kdoctools_install(po)
add_subdirectory(doc)
endif()
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
# add clang-format target for all our real source files
file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h *.c)
kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
diff --git a/config-kleopatra.h.cmake b/config-kleopatra.h.cmake
index 6d41c51c0..b51b0e8f9 100644
--- a/config-kleopatra.h.cmake
+++ b/config-kleopatra.h.cmake
@@ -1,44 +1,47 @@
/* DBus available */
#cmakedefine01 HAVE_QDBUS
/* Whether QGpgME supports changing the expiration date of the primary key and the subkeys simultaneously */
#cmakedefine01 QGPGME_SUPPORTS_CHANGING_EXPIRATION_OF_COMPLETE_KEY
/* Whether QGpgME supports retrieving the default value of a config entry */
#cmakedefine01 QGPGME_CRYPTOCONFIGENTRY_HAS_DEFAULT_VALUE
/* Whether QGpgME supports WKD lookup */
#cmakedefine01 QGPGME_SUPPORTS_WKDLOOKUP
/* Whether QGpgME supports specifying an import filter when importing keys */
#cmakedefine01 QGPGME_SUPPORTS_IMPORT_WITH_FILTER
/* Whether QGpgME supports setting key origin when importing keys */
#cmakedefine01 QGPGME_SUPPORTS_IMPORT_WITH_KEY_ORIGIN
/* Whether QGpgME supports the export of secret keys */
#cmakedefine01 QGPGME_SUPPORTS_SECRET_KEY_EXPORT
/* Whether QGpgME supports the export of secret subkeys */
#cmakedefine01 QGPGME_SUPPORTS_SECRET_SUBKEY_EXPORT
/* Whether QGpgME supports receiving keys by their key ids */
#cmakedefine01 QGPGME_SUPPORTS_RECEIVING_KEYS_BY_KEY_ID
/* Whether QGpgME supports revoking own OpenPGP keys */
#cmakedefine01 QGPGME_SUPPORTS_KEY_REVOCATION
/* Whether QGpgME supports refreshing keys */
#cmakedefine01 QGPGME_SUPPORTS_KEY_REFRESH
/* Whether QGpgME supports setting the file name of encrypted data */
#cmakedefine01 QGPGME_SUPPORTS_SET_FILENAME
/* Whether QGpgME supports setting the primary user id of a key */
#cmakedefine01 QGPGME_SUPPORTS_SET_PRIMARY_UID
/* Whether GpgME++ supports setting the curve when generating ECC card keys */
#cmakedefine01 GPGMEPP_SUPPORTS_SET_CURVE
/* Whether QGpgME supports deferred start of ImportJob */
#cmakedefine01 QGPGME_SUPPORTS_DEFERRED_IMPORT_JOB
+
+/* Whether QGpgME supports the sign/encrypt/decrypt/verify archive jobs */
+#cmakedefine01 QGPGME_SUPPORTS_ARCHIVE_JOBS
diff --git a/src/crypto/signencryptfilescontroller.cpp b/src/crypto/signencryptfilescontroller.cpp
index 023dd6fc8..e923b77ea 100644
--- a/src/crypto/signencryptfilescontroller.cpp
+++ b/src/crypto/signencryptfilescontroller.cpp
@@ -1,704 +1,720 @@
/* -*- 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 "certificateresolver.h"
#include "crypto/gui/signencryptfileswizard.h"
#include "crypto/taskcollection.h"
#include "fileoperationspreferences.h"
#include "utils/input.h"
#include "utils/output.h"
#include "utils/kleo_assert.h"
#include "utils/archivedefinition.h"
#include "utils/path-helper.h"
#include <Libkleo/KleoException>
#include <Libkleo/Classify>
#include <KLocalizedString>
#include "kleopatra_debug.h"
+#if QGPGME_SUPPORTS_ARCHIVE_JOBS
+#include <QGpgME/SignEncryptArchiveJob>
+#endif
+
#include <QPointer>
#include <QTimer>
#include <QFileInfo>
#include <QDir>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace GpgME;
using namespace KMime::Types;
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);
}
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);
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 const char *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();
if (const char *const ext = outputFileExtension(cls, usePGPFileExt)) {
return ext;
} else {
return "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;
baseName = QDir(heuristicBaseDirectory(files)).absoluteFilePath(files.size() > 1 ?
i18nc("base name of an archive file, e.g. archive.zip or archive.tar.gz", "archive") :
firstFile.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 + QString::fromLatin1(extension(false, true, false, ascii, true)));
nameMap.insert(SignEncryptFilesWizard::EncryptedCMS, baseNameCms + QString::fromLatin1(extension(false, false, true, ascii, false)));
nameMap.insert(SignEncryptFilesWizard::CombinedPGP, baseNamePgp + QString::fromLatin1(extension(true, true, true, ascii, false)));
nameMap.insert(SignEncryptFilesWizard::EncryptedPGP, baseNamePgp + QString::fromLatin1(extension(true, false, true, ascii, false)));
nameMap.insert(SignEncryptFilesWizard::SignaturePGP, baseNamePgp + QString::fromLatin1(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 + QString::fromLatin1(extension(false, true, false, ascii, true)));
ret.insert(SignEncryptFilesWizard::EncryptedCMS, baseName + QString::fromLatin1(extension(false, false, true, ascii, false)));
ret.insert(SignEncryptFilesWizard::CombinedPGP, baseName + QString::fromLatin1(extension(true, true, true, ascii, false)));
ret.insert(SignEncryptFilesWizard::EncryptedPGP, baseName + QString::fromLatin1(extension(true, false, true, ascii, false)));
ret.insert(SignEncryptFilesWizard::SignaturePGP, baseName + QString::fromLatin1(extension(true, true, false, ascii, true)));
return ret;
}
void SignEncryptFilesController::setFiles(const QStringList &files)
{
kleo_assert(!files.empty());
d->files = files;
bool archive = false;
if (files.size() > 1) {
setOperationMode((operationMode() & ~ArchiveMask) | ArchiveAllowed);
archive = true;
}
for (const auto &file: files) {
if (QFileInfo(file).isDir()) {
setOperationMode((operationMode() & ~ArchiveMask) | ArchiveForced);
archive = true;
break;
}
}
d->ensureWizardCreated();
d->wizard->setSingleFile(!archive);
d->wizard->setOutputNames(buildOutputNames(files, archive));
}
void SignEncryptFilesController::Private::slotWizardCanceled()
{
qCDebug(KLEOPATRA_LOG);
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);
task->setInput(Input::createFromFile(input));
task->setOutputFileName(outputName);
return task;
}
+static bool archiveJobsCanBeUsed(GpgME::Protocol protocol)
+{
+#if QGPGME_SUPPORTS_ARCHIVE_JOBS
+ return (protocol == GpgME::OpenPGP) && QGpgME::SignEncryptArchiveJob::isSupported();
+#else
+ return false;
+#endif
+}
+
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);
}
- kleo_assert(ad);
-
const Protocol proto = pgp ? OpenPGP : CMS;
task->setInputFileNames(files);
- task->setInput(ad->createInputFromPackCommand(proto, 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;
}
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());
}
}
const std::shared_ptr<OverwritePolicy> overwritePolicy(new OverwritePolicy(wizard));
for (const std::shared_ptr<SignEncryptTask> &i : tasks) {
i->setOverwritePolicy(overwritePolicy);
}
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);
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/crypto/signencrypttask.cpp b/src/crypto/signencrypttask.cpp
index 710248915..550b73ba4 100644
--- a/src/crypto/signencrypttask.cpp
+++ b/src/crypto/signencrypttask.cpp
@@ -1,691 +1,822 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/signencrypttask.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 "signencrypttask.h"
#include <utils/input.h>
#include <utils/output.h>
#include <utils/path-helper.h>
#include <utils/kleo_assert.h>
#include <Libkleo/AuditLogEntry>
#include <Libkleo/Formatting>
#include <Libkleo/Stl_Util>
#include <Libkleo/KleoException>
#include <QGpgME/Protocol>
#include <QGpgME/SignJob>
#include <QGpgME/SignEncryptJob>
#include <QGpgME/EncryptJob>
+#if QGPGME_SUPPORTS_ARCHIVE_JOBS
+#include <QGpgME/EncryptArchiveJob>
+#include <QGpgME/SignArchiveJob>
+#include <QGpgME/SignEncryptArchiveJob>
+#endif
#include <gpgme++/signingresult.h>
#include <gpgme++/encryptionresult.h>
#include <gpgme++/key.h>
#include <KLocalizedString>
#include "kleopatra_debug.h"
#include <QPointer>
#include <QTextDocument> // for Qt::escape
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace GpgME;
namespace
{
QString formatInputOutputLabel(const QString &input, const QString &output, bool outputDeleted)
{
return i18nc("Input file --> Output file (rarr is arrow", "%1 → %2",
input.toHtmlEscaped(),
outputDeleted ? QStringLiteral("<s>%1</s>").arg(output.toHtmlEscaped()) : output.toHtmlEscaped());
}
class ErrorResult : public Task::Result
{
public:
ErrorResult(bool sign, bool encrypt, const Error &err, const QString &errStr, const QString &input, const QString &output, const AuditLogEntry &auditLog)
: Task::Result(), m_sign(sign), m_encrypt(encrypt), m_error(err), m_errString(errStr), m_inputLabel(input), m_outputLabel(output), m_auditLog(auditLog) {}
QString overview() const override;
QString details() const override;
GpgME::Error error() const override
{
return m_error;
}
QString errorString() const override
{
return m_errString;
}
VisualCode code() const override
{
return NeutralError;
}
AuditLogEntry auditLog() const override
{
return m_auditLog;
}
private:
const bool m_sign;
const bool m_encrypt;
const Error m_error;
const QString m_errString;
const QString m_inputLabel;
const QString m_outputLabel;
const AuditLogEntry m_auditLog;
};
class SignEncryptFilesResult : public Task::Result
{
public:
SignEncryptFilesResult(const SigningResult &sr, const std::shared_ptr<Input> &input, const std::shared_ptr<Output> &output, bool outputCreated, const AuditLogEntry &auditLog)
: Task::Result(),
m_sresult(sr),
m_inputLabel(input ? input->label() : QString()),
m_inputErrorString(input ? input->errorString() : QString()),
m_outputLabel(output ? output->label() : QString()),
m_outputErrorString(output ? output->errorString() : QString()),
m_outputCreated(outputCreated),
m_auditLog(auditLog)
{
qCDebug(KLEOPATRA_LOG) << "\ninputError :" << m_inputErrorString
<< "\noutputError:" << m_outputErrorString;
Q_ASSERT(!m_sresult.isNull());
}
SignEncryptFilesResult(const EncryptionResult &er, const std::shared_ptr<Input> &input, const std::shared_ptr<Output> &output, bool outputCreated, const AuditLogEntry &auditLog)
: Task::Result(),
m_eresult(er),
m_inputLabel(input ? input->label() : QString()),
m_inputErrorString(input ? input->errorString() : QString()),
m_outputLabel(output ? output->label() : QString()),
m_outputErrorString(output ? output->errorString() : QString()),
m_outputCreated(outputCreated),
m_auditLog(auditLog)
{
qCDebug(KLEOPATRA_LOG) << "\ninputError :" << m_inputErrorString
<< "\noutputError:" << m_outputErrorString;
Q_ASSERT(!m_eresult.isNull());
}
SignEncryptFilesResult(const SigningResult &sr, const EncryptionResult &er, const std::shared_ptr<Input> &input, const std::shared_ptr<Output> &output, bool outputCreated, const AuditLogEntry &auditLog)
: Task::Result(),
m_sresult(sr),
m_eresult(er),
m_inputLabel(input ? input->label() : QString()),
m_inputErrorString(input ? input->errorString() : QString()),
m_outputLabel(output ? output->label() : QString()),
m_outputErrorString(output ? output->errorString() : QString()),
m_outputCreated(outputCreated),
m_auditLog(auditLog)
{
qCDebug(KLEOPATRA_LOG) << "\ninputError :" << m_inputErrorString
<< "\noutputError:" << m_outputErrorString;
Q_ASSERT(!m_sresult.isNull() || !m_eresult.isNull());
}
QString overview() const override;
QString details() const override;
GpgME::Error error() const override;
QString errorString() const override;
VisualCode code() const override;
AuditLogEntry auditLog() const override;
private:
const SigningResult m_sresult;
const EncryptionResult m_eresult;
const QString m_inputLabel;
const QString m_inputErrorString;
const QString m_outputLabel;
const QString m_outputErrorString;
const bool m_outputCreated;
const AuditLogEntry m_auditLog;
};
static QString makeSigningOverview(const Error &err)
{
if (err.isCanceled()) {
return i18n("Signing canceled.");
}
if (err) {
return i18n("Signing failed.");
}
return i18n("Signing succeeded.");
}
static QString makeResultOverview(const SigningResult &result)
{
return makeSigningOverview(result.error());
}
static QString makeEncryptionOverview(const Error &err)
{
if (err.isCanceled()) {
return i18n("Encryption canceled.");
}
if (err) {
return i18n("Encryption failed.");
}
return i18n("Encryption succeeded.");
}
static QString makeResultOverview(const EncryptionResult &result)
{
return makeEncryptionOverview(result.error());
}
static QString makeResultOverview(const SigningResult &sr, const EncryptionResult &er)
{
if (er.isNull() && sr.isNull()) {
return QString();
}
if (er.isNull()) {
return makeResultOverview(sr);
}
if (sr.isNull()) {
return makeResultOverview(er);
}
if (sr.error().isCanceled() || sr.error()) {
return makeResultOverview(sr);
}
if (er.error().isCanceled() || er.error()) {
return makeResultOverview(er);
}
return i18n("Signing and encryption succeeded.");
}
static QString escape(QString s)
{
s = s.toHtmlEscaped();
s.replace(QLatin1Char('\n'), QStringLiteral("<br>"));
return s;
}
static QString makeResultDetails(const SigningResult &result, const QString &inputError, const QString &outputError)
{
const Error err = result.error();
if (err.code() == GPG_ERR_EIO) {
if (!inputError.isEmpty()) {
return i18n("Input error: %1", escape(inputError));
} else if (!outputError.isEmpty()) {
return i18n("Output error: %1", escape(outputError));
}
}
if (err || err.isCanceled()) {
return QString::fromLocal8Bit(err.asString()).toHtmlEscaped();
}
return QString();
}
static QString makeResultDetails(const EncryptionResult &result, const QString &inputError, const QString &outputError)
{
const Error err = result.error();
if (err.code() == GPG_ERR_EIO) {
if (!inputError.isEmpty()) {
return i18n("Input error: %1", escape(inputError));
} else if (!outputError.isEmpty()) {
return i18n("Output error: %1", escape(outputError));
}
}
if (err || err.isCanceled()) {
return QString::fromLocal8Bit(err.asString()).toHtmlEscaped();
}
return i18n(" Encryption succeeded.");
}
}
QString ErrorResult::overview() const
{
Q_ASSERT(m_error || m_error.isCanceled());
Q_ASSERT(m_sign || m_encrypt);
const QString label = formatInputOutputLabel(m_inputLabel, m_outputLabel, true);
const bool canceled = m_error.isCanceled();
if (m_sign && m_encrypt) {
return canceled ? i18n("%1: <b>Sign/encrypt canceled.</b>", label) : i18n(" %1: Sign/encrypt failed.", label);
}
return i18nc("label: result. Example: foo -> foo.gpg: Encryption failed.", "%1: <b>%2</b>", label,
m_sign ? makeSigningOverview(m_error) : makeEncryptionOverview(m_error));
}
QString ErrorResult::details() const
{
return m_errString;
}
class SignEncryptTask::Private
{
friend class ::Kleo::Crypto::SignEncryptTask;
SignEncryptTask *const q;
public:
explicit Private(SignEncryptTask *qq);
private:
+ void startSignEncryptJob(GpgME::Protocol proto);
std::unique_ptr<QGpgME::SignJob> createSignJob(GpgME::Protocol proto);
std::unique_ptr<QGpgME::SignEncryptJob> createSignEncryptJob(GpgME::Protocol proto);
std::unique_ptr<QGpgME::EncryptJob> createEncryptJob(GpgME::Protocol proto);
+
+#if QGPGME_SUPPORTS_ARCHIVE_JOBS
+ void startSignEncryptArchiveJob(GpgME::Protocol proto);
+ std::unique_ptr<QGpgME::SignArchiveJob> createSignArchiveJob(GpgME::Protocol proto);
+ std::unique_ptr<QGpgME::SignEncryptArchiveJob> createSignEncryptArchiveJob(GpgME::Protocol proto);
+ std::unique_ptr<QGpgME::EncryptArchiveJob> createEncryptArchiveJob(GpgME::Protocol proto);
+#endif
+
std::shared_ptr<const Task::Result> makeErrorResult(const Error &err, const QString &errStr, const AuditLogEntry &auditLog);
private:
void slotResult(const SigningResult &);
void slotResult(const SigningResult &, const EncryptionResult &);
void slotResult(const EncryptionResult &);
private:
std::shared_ptr<Input> input;
std::shared_ptr<Output> output;
QStringList inputFileNames;
QString outputFileName;
std::vector<Key> signers;
std::vector<Key> recipients;
bool sign : 1;
bool encrypt : 1;
bool detached : 1;
bool symmetric: 1;
bool clearsign: 1;
+ bool archive : 1;
QPointer<QGpgME::Job> job;
std::shared_ptr<OverwritePolicy> m_overwritePolicy;
};
SignEncryptTask::Private::Private(SignEncryptTask *qq)
: q{qq}
, sign{true}
, encrypt{true}
, detached{false}
, clearsign{false}
+ , archive{false}
, m_overwritePolicy{new OverwritePolicy{nullptr}}
{
q->setAsciiArmor(true);
}
std::shared_ptr<const Task::Result> SignEncryptTask::Private::makeErrorResult(const Error &err, const QString &errStr, const AuditLogEntry &auditLog)
{
return std::shared_ptr<const ErrorResult>(new ErrorResult(sign, encrypt, err, errStr, input ? input->label() : QString{}, output ? output->label() : QString{}, auditLog));
}
SignEncryptTask::SignEncryptTask(QObject *p)
: Task(p), d(new Private(this))
{
}
SignEncryptTask::~SignEncryptTask() {}
void SignEncryptTask::setInputFileName(const QString &fileName)
{
kleo_assert(!d->job);
kleo_assert(!fileName.isEmpty());
d->inputFileNames = QStringList(fileName);
}
void SignEncryptTask::setInputFileNames(const QStringList &fileNames)
{
kleo_assert(!d->job);
kleo_assert(!fileNames.empty());
d->inputFileNames = fileNames;
}
void SignEncryptTask::setInput(const std::shared_ptr<Input> &input)
{
kleo_assert(!d->job);
kleo_assert(input);
d->input = input;
}
void SignEncryptTask::setOutput(const std::shared_ptr<Output> &output)
{
kleo_assert(!d->job);
kleo_assert(output);
d->output = output;
}
void SignEncryptTask::setOutputFileName(const QString &fileName)
{
kleo_assert(!d->job);
kleo_assert(!fileName.isEmpty());
d->outputFileName = fileName;
}
void SignEncryptTask::setSigners(const std::vector<Key> &signers)
{
kleo_assert(!d->job);
d->signers = signers;
}
void SignEncryptTask::setRecipients(const std::vector<Key> &recipients)
{
kleo_assert(!d->job);
d->recipients = recipients;
}
void SignEncryptTask::setOverwritePolicy(const std::shared_ptr<OverwritePolicy> &policy)
{
kleo_assert(!d->job);
d->m_overwritePolicy = policy;
}
void SignEncryptTask::setSign(bool sign)
{
kleo_assert(!d->job);
d->sign = sign;
}
void SignEncryptTask::setEncrypt(bool encrypt)
{
kleo_assert(!d->job);
d->encrypt = encrypt;
}
void SignEncryptTask::setDetachedSignature(bool detached)
{
kleo_assert(!d->job);
d->detached = detached;
}
void SignEncryptTask::setEncryptSymmetric(bool symmetric)
{
kleo_assert(!d->job);
d->symmetric = symmetric;
}
void SignEncryptTask::setClearsign(bool clearsign)
{
kleo_assert(!d->job);
d->clearsign = clearsign;
}
+void SignEncryptTask::setCreateArchive(bool archive)
+{
+ kleo_assert(!d->job);
+ d->archive = archive;
+}
+
Protocol SignEncryptTask::protocol() const
{
if (d->sign && !d->signers.empty()) {
return d->signers.front().protocol();
}
if (d->encrypt || d->symmetric) {
if (!d->recipients.empty()) {
return d->recipients.front().protocol();
} else {
return GpgME::OpenPGP; // symmetric OpenPGP encryption
}
}
throw Kleo::Exception(gpg_error(GPG_ERR_INTERNAL),
i18n("Cannot determine protocol for task"));
}
QString SignEncryptTask::label() const
{
return d->input ? d->input->label() : QString();
}
QString SignEncryptTask::tag() const
{
return Formatting::displayName(protocol());
}
unsigned long long SignEncryptTask::inputSize() const
{
return d->input ? d->input->size() : 0U;
}
+static bool archiveJobsCanBeUsed(GpgME::Protocol protocol)
+{
+#if QGPGME_SUPPORTS_ARCHIVE_JOBS
+ return (protocol == GpgME::OpenPGP) && QGpgME::SignEncryptArchiveJob::isSupported();
+#else
+ return false;
+#endif
+}
+
void SignEncryptTask::doStart()
{
kleo_assert(!d->job);
if (d->sign) {
kleo_assert(!d->signers.empty());
+ if (d->archive) {
+ kleo_assert(!d->detached && !d->clearsign);
+ }
}
- kleo_assert(d->input);
-
if (!d->output) {
d->output = Output::createFromFile(d->outputFileName, d->m_overwritePolicy);
}
- if (d->encrypt || d->symmetric) {
+ const auto proto = protocol();
+#if QGPGME_SUPPORTS_ARCHIVE_JOBS
+ if (d->archive && archiveJobsCanBeUsed(proto)) {
+ d->startSignEncryptArchiveJob(proto);
+ } else
+#endif
+ {
+ d->startSignEncryptJob(proto);
+ }
+}
+
+void SignEncryptTask::Private::startSignEncryptJob(GpgME::Protocol proto)
+{
+ kleo_assert(input);
+
+ if (encrypt || symmetric) {
Context::EncryptionFlags flags = Context::AlwaysTrust;
- if (d->symmetric) {
+ if (symmetric) {
flags = static_cast<Context::EncryptionFlags>(flags | Context::Symmetric);
qCDebug(KLEOPATRA_LOG) << "Adding symmetric flag";
}
- if (d->sign) {
- std::unique_ptr<QGpgME::SignEncryptJob> job = d->createSignEncryptJob(protocol());
+ if (sign) {
+ std::unique_ptr<QGpgME::SignEncryptJob> job = createSignEncryptJob(proto);
kleo_assert(job.get());
#if QGPGME_SUPPORTS_SET_FILENAME
- if (d->inputFileNames.size() == 1) {
- job->setFileName(d->inputFileNames.front());
+ if (inputFileNames.size() == 1) {
+ job->setFileName(inputFileNames.front());
}
#endif
- job->start(d->signers, d->recipients,
- d->input->ioDevice(), d->output->ioDevice(), flags);
+ job->start(signers, recipients,
+ input->ioDevice(), output->ioDevice(), flags);
- d->job = job.release();
+ this->job = job.release();
} else {
- std::unique_ptr<QGpgME::EncryptJob> job = d->createEncryptJob(protocol());
+ std::unique_ptr<QGpgME::EncryptJob> job = createEncryptJob(proto);
kleo_assert(job.get());
#if QGPGME_SUPPORTS_SET_FILENAME
- if (d->inputFileNames.size() == 1) {
- job->setFileName(d->inputFileNames.front());
+ if (inputFileNames.size() == 1) {
+ job->setFileName(inputFileNames.front());
}
#endif
- job->start(d->recipients, d->input->ioDevice(), d->output->ioDevice(), flags);
+ job->start(recipients, input->ioDevice(), output->ioDevice(), flags);
- d->job = job.release();
+ this->job = job.release();
}
- } else if (d->sign) {
- std::unique_ptr<QGpgME::SignJob> job = d->createSignJob(protocol());
+ } else if (sign) {
+ std::unique_ptr<QGpgME::SignJob> job = createSignJob(proto);
kleo_assert(job.get());
- kleo_assert(! (d->detached && d->clearsign));
+ kleo_assert(! (detached && clearsign));
- job->start(d->signers,
- d->input->ioDevice(), d->output->ioDevice(),
- d->detached ? GpgME::Detached : d->clearsign ?
+ job->start(signers,
+ input->ioDevice(), output->ioDevice(),
+ detached ? GpgME::Detached : clearsign ?
GpgME::Clearsigned : GpgME::NormalSignatureMode);
- d->job = job.release();
+ this->job = job.release();
} else {
kleo_assert(!"Either 'sign' or 'encrypt' or 'symmetric' must be set!");
}
}
void SignEncryptTask::cancel()
{
if (d->job) {
d->job->slotCancel();
}
}
std::unique_ptr<QGpgME::SignJob> SignEncryptTask::Private::createSignJob(GpgME::Protocol proto)
{
const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(backend);
std::unique_ptr<QGpgME::SignJob> signJob(backend->signJob(q->asciiArmor(), /*textmode=*/false));
kleo_assert(signJob.get());
connect(signJob.get(), SIGNAL(progress(QString,int,int)),
q, SLOT(setProgress(QString,int,int)));
connect(signJob.get(), SIGNAL(result(GpgME::SigningResult,QByteArray)),
q, SLOT(slotResult(GpgME::SigningResult)));
return signJob;
}
std::unique_ptr<QGpgME::SignEncryptJob> SignEncryptTask::Private::createSignEncryptJob(GpgME::Protocol proto)
{
const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(backend);
std::unique_ptr<QGpgME::SignEncryptJob> signEncryptJob(backend->signEncryptJob(q->asciiArmor(), /*textmode=*/false));
kleo_assert(signEncryptJob.get());
connect(signEncryptJob.get(), SIGNAL(progress(QString,int,int)),
q, SLOT(setProgress(QString,int,int)));
connect(signEncryptJob.get(), SIGNAL(result(GpgME::SigningResult,GpgME::EncryptionResult,QByteArray)),
q, SLOT(slotResult(GpgME::SigningResult,GpgME::EncryptionResult)));
return signEncryptJob;
}
std::unique_ptr<QGpgME::EncryptJob> SignEncryptTask::Private::createEncryptJob(GpgME::Protocol proto)
{
const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(backend);
std::unique_ptr<QGpgME::EncryptJob> encryptJob(backend->encryptJob(q->asciiArmor(), /*textmode=*/false));
kleo_assert(encryptJob.get());
connect(encryptJob.get(), SIGNAL(progress(QString,int,int)),
q, SLOT(setProgress(QString,int,int)));
connect(encryptJob.get(), SIGNAL(result(GpgME::EncryptionResult,QByteArray)),
q, SLOT(slotResult(GpgME::EncryptionResult)));
return encryptJob;
}
+#if QGPGME_SUPPORTS_ARCHIVE_JOBS
+void SignEncryptTask::Private::startSignEncryptArchiveJob(GpgME::Protocol proto)
+{
+ kleo_assert(!input);
+
+ const auto baseDirectory = heuristicBaseDirectory(inputFileNames);
+ if (baseDirectory.isEmpty()) {
+ throw Kleo::Exception(GPG_ERR_CONFLICT, i18n("Cannot find common base directory for these files:\n%1", inputFileNames.join(QLatin1Char('\n'))));
+ }
+ qCDebug(KLEOPATRA_LOG) << "heuristicBaseDirectory(" << inputFileNames << ") ->" << baseDirectory;
+ const auto tempPaths = makeRelativeTo(baseDirectory, inputFileNames);
+ const auto relativePaths = std::vector<QString>{tempPaths.begin(), tempPaths.end()};
+ qCDebug(KLEOPATRA_LOG) << "relative paths:" << relativePaths;
+
+ if (encrypt || symmetric) {
+ Context::EncryptionFlags flags = Context::AlwaysTrust;
+ if (symmetric) {
+ flags = static_cast<Context::EncryptionFlags>(flags | Context::Symmetric);
+ qCDebug(KLEOPATRA_LOG) << "Adding symmetric flag";
+ }
+ if (sign) {
+ std::unique_ptr<QGpgME::SignEncryptArchiveJob> job = createSignEncryptArchiveJob(proto);
+ kleo_assert(job.get());
+ job->setBaseDirectory(baseDirectory);
+
+ job->start(signers, recipients, relativePaths, output->ioDevice(), flags);
+
+ this->job = job.release();
+ } else {
+ std::unique_ptr<QGpgME::EncryptArchiveJob> job = createEncryptArchiveJob(proto);
+ kleo_assert(job.get());
+ job->setBaseDirectory(baseDirectory);
+
+ job->start(recipients, relativePaths, output->ioDevice(), flags);
+
+ this->job = job.release();
+ }
+ } else if (sign) {
+ std::unique_ptr<QGpgME::SignArchiveJob> job = createSignArchiveJob(proto);
+ kleo_assert(job.get());
+ job->setBaseDirectory(baseDirectory);
+
+ job->start(signers, relativePaths, output->ioDevice());
+
+ this->job = job.release();
+ } else {
+ kleo_assert(!"Either 'sign' or 'encrypt' or 'symmetric' must be set!");
+ }
+}
+
+std::unique_ptr<QGpgME::SignArchiveJob> SignEncryptTask::Private::createSignArchiveJob(GpgME::Protocol proto)
+{
+ const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
+ kleo_assert(backend);
+ std::unique_ptr<QGpgME::SignArchiveJob> signJob(backend->signArchiveJob(q->asciiArmor()));
+ kleo_assert(signJob.get());
+ connect(signJob.get(), &QGpgME::SignArchiveJob::progress, q, &SignEncryptTask::setProgress);
+ connect(signJob.get(), &QGpgME::SignArchiveJob::result, q, [this](const GpgME::SigningResult &signResult) { slotResult(signResult); });
+ return signJob;
+}
+
+std::unique_ptr<QGpgME::SignEncryptArchiveJob> SignEncryptTask::Private::createSignEncryptArchiveJob(GpgME::Protocol proto)
+{
+ const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
+ kleo_assert(backend);
+ std::unique_ptr<QGpgME::SignEncryptArchiveJob> signEncryptJob(backend->signEncryptArchiveJob(q->asciiArmor()));
+ kleo_assert(signEncryptJob.get());
+ connect(signEncryptJob.get(), &QGpgME::SignEncryptArchiveJob::progress, q, &SignEncryptTask::setProgress);
+ connect(signEncryptJob.get(), &QGpgME::SignEncryptArchiveJob::result, q, [this](const GpgME::SigningResult &signResult, const GpgME::EncryptionResult &encryptResult) { slotResult(signResult, encryptResult); });
+ return signEncryptJob;
+}
+
+std::unique_ptr<QGpgME::EncryptArchiveJob> SignEncryptTask::Private::createEncryptArchiveJob(GpgME::Protocol proto)
+{
+ const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
+ kleo_assert(backend);
+ std::unique_ptr<QGpgME::EncryptArchiveJob> encryptJob(backend->encryptArchiveJob(q->asciiArmor()));
+ kleo_assert(encryptJob.get());
+ connect(encryptJob.get(), &QGpgME::EncryptArchiveJob::progress, q, &SignEncryptTask::setProgress);
+ connect(encryptJob.get(), &QGpgME::EncryptArchiveJob::result, q, [this](const GpgME::EncryptionResult &encryptResult) { slotResult(encryptResult); });
+ return encryptJob;
+}
+#endif
+
void SignEncryptTask::Private::slotResult(const SigningResult &result)
{
const auto *const job = qobject_cast<const QGpgME::Job *>(q->sender());
const AuditLogEntry auditLog = AuditLogEntry::fromJob(job);
bool outputCreated = false;
if (input && input->failed()) {
q->emitResult(makeErrorResult(Error::fromCode(GPG_ERR_EIO),
i18n("Input error: %1", escape( input->errorString())),
auditLog));
return;
} else if (result.error().code()) {
output->cancel();
} else {
try {
kleo_assert(!result.isNull());
output->finalize();
outputCreated = true;
if (input) {
input->finalize();
}
} catch (const GpgME::Exception &e) {
q->emitResult(makeErrorResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
return;
}
}
q->emitResult(std::shared_ptr<Result>(new SignEncryptFilesResult(result, input, output, outputCreated, auditLog)));
}
void SignEncryptTask::Private::slotResult(const SigningResult &sresult, const EncryptionResult &eresult)
{
const auto *const job = qobject_cast<const QGpgME::Job *>(q->sender());
const AuditLogEntry auditLog = AuditLogEntry::fromJob(job);
bool outputCreated = false;
if (input && input->failed()) {
output->cancel();
q->emitResult(makeErrorResult(Error::fromCode(GPG_ERR_EIO),
i18n("Input error: %1", escape( input->errorString())),
auditLog));
return;
} else if (sresult.error().code() || eresult.error().code()) {
output->cancel();
} else {
try {
kleo_assert(!sresult.isNull() || !eresult.isNull());
output->finalize();
outputCreated = true;
if (input) {
input->finalize();
}
} catch (const GpgME::Exception &e) {
q->emitResult(makeErrorResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
return;
}
}
q->emitResult(std::shared_ptr<Result>(new SignEncryptFilesResult(sresult, eresult, input, output, outputCreated, auditLog)));
}
void SignEncryptTask::Private::slotResult(const EncryptionResult &result)
{
const auto *const job = qobject_cast<const QGpgME::Job *>(q->sender());
const AuditLogEntry auditLog = AuditLogEntry::fromJob(job);
bool outputCreated = false;
if (input && input->failed()) {
output->cancel();
q->emitResult(makeErrorResult(Error::fromCode(GPG_ERR_EIO),
i18n("Input error: %1", escape(input->errorString())),
auditLog));
return;
} else if (result.error().code()) {
output->cancel();
} else {
try {
kleo_assert(!result.isNull());
output->finalize();
outputCreated = true;
if (input) {
input->finalize();
}
} catch (const GpgME::Exception &e) {
q->emitResult(makeErrorResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
return;
}
}
q->emitResult(std::shared_ptr<Result>(new SignEncryptFilesResult(result, input, output, outputCreated, auditLog)));
}
QString SignEncryptFilesResult::overview() const
{
const QString files = formatInputOutputLabel(m_inputLabel, m_outputLabel, !m_outputCreated);
return files + QLatin1String(": ") + makeOverview(makeResultOverview(m_sresult, m_eresult));
}
QString SignEncryptFilesResult::details() const
{
return errorString();
}
GpgME::Error SignEncryptFilesResult::error() const
{
if (m_sresult.error().code()) {
return m_sresult.error();
}
if (m_eresult.error().code()) {
return m_eresult.error();
}
return {};
}
QString SignEncryptFilesResult::errorString() const
{
const bool sign = !m_sresult.isNull();
const bool encrypt = !m_eresult.isNull();
kleo_assert(sign || encrypt);
if (sign && encrypt) {
return
m_sresult.error().code() ? makeResultDetails(m_sresult, m_inputErrorString, m_outputErrorString) :
m_eresult.error().code() ? makeResultDetails(m_eresult, m_inputErrorString, m_outputErrorString) :
QString();
}
return
sign ? makeResultDetails(m_sresult, m_inputErrorString, m_outputErrorString) :
/*else*/ makeResultDetails(m_eresult, m_inputErrorString, m_outputErrorString);
}
Task::Result::VisualCode SignEncryptFilesResult::code() const
{
if (m_sresult.error().isCanceled() || m_eresult.error().isCanceled()) {
return Warning;
}
return (m_sresult.error().code() || m_eresult.error().code()) ? NeutralError : NeutralSuccess;
}
AuditLogEntry SignEncryptFilesResult::auditLog() const
{
return m_auditLog;
}
#include "moc_signencrypttask.cpp"
diff --git a/src/crypto/signencrypttask.h b/src/crypto/signencrypttask.h
index 7b9bd8eec..e30548c40 100644
--- a/src/crypto/signencrypttask.h
+++ b/src/crypto/signencrypttask.h
@@ -1,83 +1,84 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/signencrypttask.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 <crypto/task.h>
#include <utils/pimpl_ptr.h>
#include <gpgme++/global.h>
#include <memory>
#include <vector>
class QString;
namespace GpgME
{
class Key;
}
namespace Kleo
{
class OverwritePolicy;
class Input;
class Output;
}
namespace Kleo
{
namespace Crypto
{
class SignEncryptTask : public Task
{
Q_OBJECT
public:
explicit SignEncryptTask(QObject *parent = nullptr);
~SignEncryptTask() override;
void setInputFileName(const QString &fileName);
void setInputFileNames(const QStringList &fileNames);
void setInput(const std::shared_ptr<Input> &input);
void setOutput(const std::shared_ptr<Output> &output);
void setOutputFileName(const QString &fileName);
void setSigners(const std::vector<GpgME::Key> &signers);
void setRecipients(const std::vector<GpgME::Key> &recipients);
void setSign(bool sign);
void setEncrypt(bool encrypt);
void setDetachedSignature(bool detached);
void setEncryptSymmetric(bool symmetric);
void setClearsign(bool clearsign);
+ void setCreateArchive(bool archive);
void setOverwritePolicy(const std::shared_ptr<OverwritePolicy> &policy);
GpgME::Protocol protocol() const override;
void cancel() override;
QString label() const override;
QString tag() const override;
private:
void doStart() override;
unsigned long long inputSize() const override;
private:
class Private;
kdtools::pimpl_ptr<Private> d;
Q_PRIVATE_SLOT(d, void slotResult(const GpgME::SigningResult &))
Q_PRIVATE_SLOT(d, void slotResult(const GpgME::SigningResult &, const GpgME::EncryptionResult &))
Q_PRIVATE_SLOT(d, void slotResult(const GpgME::EncryptionResult &))
};
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Aug 4, 4:52 PM (12 h, 41 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
a7/b1/889dd8da53c009b95e454c7c670f
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment