Page MenuHome GnuPG

No OneTemporary

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 905259677..41155d937 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,251 +1,251 @@
# SPDX-FileCopyrightText: none
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
set(RELEASE_SERVICE_VERSION_MAJOR "24")
set(RELEASE_SERVICE_VERSION_MINOR "07")
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 "2")
set(KLEOPATRA_VERSION_MICRO "0")
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 "6.3.0")
set(KIDENTITYMANAGEMENT_VERSION "6.1.80")
set(KMAILTRANSPORT_VERSION "6.1.80")
set(AKONADI_MIME_VERSION "6.1.80")
set(KMIME_VERSION "6.1.80")
-set(LIBKLEO_VERSION "6.1.80")
+set(LIBKLEO_VERSION "6.1.81")
set(QT_REQUIRED_VERSION "6.6.0")
set(MIMETREEPARSER_VERSION "6.1.80")
set(GPGME_REQUIRED_VERSION "1.21.0")
set(LIBASSUAN_REQUIRED_VERSION "2.4.2")
set(GPG_ERROR_REQUIRED_VERSION "1.36")
if (WIN32)
set(KF6_WANT_VERSION ${KF_MIN_VERSION})
set(KMIME_WANT_VERSION ${KMIME_VERSION})
else ()
set(KF6_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 ${KF6_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(ECMFeatureSummary)
include(KDEClangFormat)
include(KDEGitCommitHooks)
# Find KF6 packages
find_package(KF6 ${KF6_WANT_VERSION}
REQUIRED COMPONENTS
Codecs
ColorScheme
Config
CoreAddons
Crash
I18n
IconThemes
ItemModels
KIO
WidgetsAddons
WindowSystem
XmlGui
StatusNotifierItem
OPTIONAL_COMPONENTS
DocTools
)
set_package_properties(KF6DocTools 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(KF6DBusAddons ${KF6_WANT_VERSION} CONFIG)
set_package_properties(KF6DBusAddons PROPERTIES DESCRIPTION "Support library to work with DBus"
PURPOSE "DBus session integration"
URL "https://inqlude.org/libraries/kdbusaddons.html"
TYPE OPTIONAL)
else()
find_package(KF6DBusAddons ${KF6_WANT_VERSION} CONFIG REQUIRED)
set(_kleopatra_dbusaddons_libs KF6::DBusAddons)
endif()
set(HAVE_QDBUS ${Qt6DBus_FOUND})
find_package(Gpgmepp ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
set(QGPGME_NAME "QGpgmeQt6")
find_package(${QGPGME_NAME} ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
if (${QGPGME_NAME}_VERSION VERSION_GREATER_EQUAL "1.22.0")
set(QGPGME_HAS_TOLOGSTRING 1)
set(QGPGME_SUPPORTS_IS_MIME 1)
endif()
if (${QGPGME_NAME}_VERSION VERSION_GREATER_EQUAL "1.23.0")
set(QGPGME_SUPPORTS_WKD_REFRESH_JOB 1)
endif()
if (${QGPGME_NAME}_VERSION VERSION_GREATER_EQUAL "1.23.3")
set(QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO 1)
set(QGPGME_IMPORT_JOB_SUPPORTS_IMPORT_OPTIONS 1)
endif()
find_package(KPim6Libkleo ${LIBKLEO_VERSION} CONFIG REQUIRED)
find_package(KPim6Mime ${KMIME_WANT_VERSION} CONFIG REQUIRED)
find_package(KPim6IdentityManagementCore ${KIDENTITYMANAGEMENT_VERSION} CONFIG)
find_package(KPim6MailTransport ${KMAILTRANSPORT_VERSION} CONFIG)
find_package(KPim6AkonadiMime ${AKONADI_MIME_VERSION} CONFIG)
find_package(KPim6MimeTreeParserWidgets ${MIMETREEPARSER_VERSION} CONFIG REQUIRED)
find_package(Qt6 ${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.in ${CMAKE_CURRENT_BINARY_DIR}/version-kleopatra.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-kleopatra.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-kleopatra.h)
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
)
add_definitions(-DQT_NO_CONTEXTLESS_CONNECT)
ecm_set_disabled_deprecation_versions(QT 6.7.2 KF 6.4.0)
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(KF6DocTools_FOUND)
kdoctools_install(po)
add_subdirectory(doc)
endif()
ecm_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})
kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT)
diff --git a/src/crypto/gui/signencryptfileswizard.cpp b/src/crypto/gui/signencryptfileswizard.cpp
index d8c5755c8..71b6e6151 100644
--- a/src/crypto/gui/signencryptfileswizard.cpp
+++ b/src/crypto/gui/signencryptfileswizard.cpp
@@ -1,688 +1,688 @@
/* crypto/gui/signencryptfileswizard.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2009 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 "signencryptfileswizard.h"
#include "signencryptwidget.h"
#include "newresultpage.h"
#include <fileoperationspreferences.h>
#include <settings.h>
#include <KColorScheme>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KMessageBox>
#include <KMessageWidget>
#include <KSharedConfig>
#include "kleopatra_debug.h"
#include <Libkleo/Compliance>
#include <Libkleo/Formatting>
#include <Libkleo/GnuPG>
#include <Libkleo/SystemInfo>
#include <Libkleo/FileNameRequester>
#include <QCheckBox>
#include <QGroupBox>
#include <QIcon>
#include <QLabel>
#include <QPushButton>
#include <QStyle>
#include <QVBoxLayout>
#include <QWindow>
#include <QWizardPage>
#include <gpgme++/key.h>
#include <array>
using namespace GpgME;
using namespace Kleo;
using namespace Kleo::Crypto::Gui;
enum Page {
SigEncPageId,
ResultPageId,
NumPages
};
class FileNameRequesterWithIcon : public QWidget
{
Q_OBJECT
public:
explicit FileNameRequesterWithIcon(QDir::Filters filter, QWidget *parent = nullptr)
: QWidget(parent)
{
auto layout = new QHBoxLayout{this};
layout->setContentsMargins(0, 0, 0, 0);
mIconLabel = new QLabel{this};
mRequester = new FileNameRequester{filter, this};
mRequester->setExistingOnly(false);
layout->addWidget(mIconLabel);
layout->addWidget(mRequester);
setFocusPolicy(mRequester->focusPolicy());
setFocusProxy(mRequester);
connect(mRequester, &FileNameRequester::fileNameChanged, this, &FileNameRequesterWithIcon::fileNameChanged);
}
void setIcon(const QIcon &icon)
{
mIconLabel->setPixmap(icon.pixmap(32, 32));
}
void setFileName(const QString &name)
{
mRequester->setFileName(name);
}
QString fileName() const
{
return mRequester->fileName();
}
void setNameFilter(const QString &nameFilter)
{
mRequester->setNameFilter(nameFilter);
}
QString nameFilter() const
{
return mRequester->nameFilter();
}
FileNameRequester *requester()
{
return mRequester;
}
Q_SIGNALS:
void fileNameChanged(const QString &filename);
protected:
bool event(QEvent *e) override
{
if (e->type() == QEvent::ToolTipChange) {
mRequester->setToolTip(toolTip());
}
return QWidget::event(e);
}
private:
QLabel *mIconLabel;
FileNameRequester *mRequester;
};
class SigEncPage : public QWizardPage
{
Q_OBJECT
public:
explicit SigEncPage(QWidget *parent = nullptr)
: QWizardPage(parent)
, mParent((SignEncryptFilesWizard *)parent)
, mWidget(new SignEncryptWidget)
, mOutLayout(new QVBoxLayout)
, mOutputLabel{nullptr}
, mArchive(false)
, mUseOutputDir(false)
, mSingleFile{true}
{
setTitle(i18nc("@title", "Sign / Encrypt Files"));
auto vLay = new QVBoxLayout(this);
vLay->setContentsMargins(0, 0, 0, 0);
if (!Settings{}.cmsEnabled()) {
mWidget->setProtocol(GpgME::OpenPGP);
}
mWidget->setSignAsText(i18nc("@option:check on SignEncryptPage", "&Sign as:"));
mWidget->setEncryptForMeText(i18nc("@option:check on SignEncryptPage", "Encrypt for &me:"));
mWidget->setEncryptForOthersText(i18nc("@option:check on SignEncryptPage", "Encrypt for &others:"));
mWidget->setEncryptWithPasswordText(
i18nc("@option:check on SignEncryptPage", "Encrypt with &password. Anyone you share the password with can read the data."));
vLay->addWidget(mWidget);
connect(mWidget, &SignEncryptWidget::operationChanged, this, &SigEncPage::updateCommitButton);
connect(mWidget, &SignEncryptWidget::keysChanged, this, &SigEncPage::updateFileWidgets);
auto outputGrp = new QGroupBox(i18nc("@title:group", "Output"));
outputGrp->setLayout(mOutLayout);
mPlaceholderWidget = new QLabel(i18nc("@label:textbox", "Please select an action."));
mOutLayout->addWidget(mPlaceholderWidget);
mOutputLabel = new QLabel(i18nc("@label on SignEncryptPage", "Output &files/folder:"));
mOutLayout->addWidget(mOutputLabel);
createRequesters(mOutLayout);
mUseOutputDirChk = new QCheckBox(i18nc("@option:check on SignEncryptPage", "Encrypt / Sign &each file separately."));
mUseOutputDirChk->setToolTip(i18nc("@info:tooltip", "Keep each file separate instead of creating an archive for all."));
mOutLayout->addWidget(mUseOutputDirChk);
connect(mUseOutputDirChk, &QCheckBox::toggled, this, [this](bool state) {
mUseOutputDir = state;
mArchive = !mUseOutputDir && !mSingleFile;
updateFileWidgets();
});
vLay->addWidget(outputGrp);
auto messageWidget = new KMessageWidget;
messageWidget->setMessageType(KMessageWidget::Error);
messageWidget->setIcon(style()->standardIcon(QStyle::SP_MessageBoxCritical, nullptr, this));
messageWidget->setText(i18n("Invalid compliance settings for signing and encrypting files."));
messageWidget->setToolTip(xi18nc("@info %1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
"<para>You cannot use <application>Kleopatra</application> for signing or encrypting files "
"because the <application>GnuPG</application> system used by <application>Kleopatra</application> is not %1.</para>",
DeVSCompliance::name(true)));
messageWidget->setCloseButtonVisible(false);
messageWidget->setVisible(DeVSCompliance::isActive() && !DeVSCompliance::isCompliant());
vLay->addWidget(messageWidget);
setMinimumHeight(300);
}
void setEncryptionPreset(bool value)
{
mWidget->setEncryptionChecked(value);
}
void setSigningPreset(bool value)
{
mWidget->setSigningChecked(value);
}
bool isComplete() const override
{
if (DeVSCompliance::isActive() && !DeVSCompliance::isCompliant()) {
return false;
}
return mWidget->isComplete();
}
int nextId() const override
{
return ResultPageId;
}
void initializePage() override
{
setCommitPage(true);
updateCommitButton(mWidget->currentOp());
}
void setArchiveForced(bool archive)
{
mArchive = archive;
setArchiveMutable(!archive);
}
void setArchiveMutable(bool archive)
{
mUseOutputDirChk->setVisible(archive);
if (archive) {
const KConfigGroup archCfg(KSharedConfig::openConfig(), QStringLiteral("SignEncryptFilesWizard"));
mUseOutputDirChk->setChecked(archCfg.readEntry("LastUseOutputDir", false));
} else {
mUseOutputDirChk->setChecked(false);
}
}
void setSingleFile(bool singleFile)
{
mSingleFile = singleFile;
mArchive = !mUseOutputDir && !mSingleFile;
}
bool validatePage() override
{
if (DeVSCompliance::isActive() && !DeVSCompliance::isCompliant()) {
KMessageBox::error(topLevelWidget(),
xi18nc("@info %1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
"<para>Sorry! You cannot use <application>Kleopatra</application> for signing or encrypting files "
"because the <application>GnuPG</application> system used by <application>Kleopatra</application> is not %1.</para>",
DeVSCompliance::name(true)));
return false;
}
- bool sign = !mWidget->signKey().isNull();
- bool encrypt = !mWidget->selfKey().isNull() || !mWidget->recipients().empty();
+ bool sign = !mWidget->signUserId().isNull();
+ bool encrypt = !mWidget->selfUserId().isNull() || !mWidget->recipients().empty();
if (!mWidget->validate()) {
return false;
}
mWidget->saveOwnKeys();
if (mUseOutputDirChk->isVisible()) {
KConfigGroup archCfg(KSharedConfig::openConfig(), QStringLiteral("SignEncryptFilesWizard"));
archCfg.writeEntry("LastUseOutputDir", mUseOutputDir);
}
if (sign && !encrypt && mArchive) {
return KMessageBox::warningContinueCancel(
this,
xi18nc("@info",
"<para>Archiving in combination with sign-only currently requires what are known as opaque signatures - "
"unlike detached ones, these embed the content in the signature.</para>"
"<para>This format is rather unusual. You might want to archive the files separately, "
"and then sign the archive as one file with Kleopatra.</para>"
"<para>Future versions of Kleopatra are expected to also support detached signatures in this case.</para>"),
i18nc("@title:window", "Unusual Signature Warning"),
KStandardGuiItem::cont(),
KStandardGuiItem::cancel(),
QStringLiteral("signencryptfileswizard-archive+sign-only-warning"))
== KMessageBox::Continue;
} else if (sign && !encrypt) {
return true;
}
- if (!mWidget->selfKey().isNull() || mWidget->encryptSymmetric()) {
+ if (!mWidget->selfUserId().isNull() || mWidget->encryptSymmetric()) {
return true;
}
const auto recipientKeys = recipients();
const bool hasSecret = std::any_of(std::begin(recipientKeys), std::end(recipientKeys), [](const auto &k) {
return k.hasSecret();
});
if (!hasSecret) {
if (KMessageBox::warningContinueCancel(this,
xi18nc("@info",
"<para>None of the recipients you are encrypting to seems to be your own.</para>"
"<para>This means that you will not be able to decrypt the data anymore, once encrypted.</para>"
"<para>Do you want to continue, or cancel to change the recipient selection?</para>"),
i18nc("@title:window", "Encrypt-To-Self Warning"),
KStandardGuiItem::cont(),
KStandardGuiItem::cancel(),
QStringLiteral("warn-encrypt-to-non-self"),
KMessageBox::Notify | KMessageBox::Dangerous)
== KMessageBox::Cancel) {
return false;
}
}
return true;
}
std::vector<Key> recipients() const
{
return mWidget->recipients();
}
/* In the future we might find a usecase for multiple
* signers */
std::vector<Key> signers() const
{
- const Key k = mWidget->signKey();
+ const Key k = mWidget->signUserId().parent();
if (!k.isNull()) {
return {k};
}
return {};
}
private:
struct RequesterInfo {
SignEncryptFilesWizard::KindNames id;
QString icon;
QString toolTip;
QString accessibleName;
QString nameFilterBinary;
QString nameFilterAscii;
};
void createRequesters(QBoxLayout *lay)
{
static const std::array<RequesterInfo, 6> requestersInfo = {{
{
SignEncryptFilesWizard::SignatureCMS,
QStringLiteral("document-sign"),
i18nc("@info:tooltip", "This is the filename of the S/MIME signature."),
i18nc("Lineedit accessible name", "S/MIME signature file"),
i18nc("Name filter binary", "S/MIME Signatures (*.p7s)"),
i18nc("Name filter ASCII", "S/MIME Signatures (*.p7s *.pem)"),
},
{
SignEncryptFilesWizard::SignaturePGP,
QStringLiteral("document-sign"),
i18nc("@info:tooltip", "This is the filename of the detached OpenPGP signature."),
i18nc("Lineedit accessible name", "OpenPGP signature file"),
i18nc("Name filter binary", "OpenPGP Signatures (*.sig *.pgp)"),
i18nc("Name filter ASCII", "OpenPGP Signatures (*.asc *.sig)"),
},
{
SignEncryptFilesWizard::CombinedPGP,
QStringLiteral("document-edit-sign-encrypt"),
i18nc("@info:tooltip", "This is the filename of the OpenPGP-signed and encrypted file."),
i18nc("Lineedit accessible name", "OpenPGP signed and encrypted file"),
i18nc("Name filter binary", "OpenPGP Files (*.gpg *.pgp)"),
i18nc("Name filter ASCII", "OpenPGP Files (*.asc)"),
},
{
SignEncryptFilesWizard::EncryptedPGP,
QStringLiteral("document-encrypt"),
i18nc("@info:tooltip", "This is the filename of the OpenPGP encrypted file."),
i18nc("Lineedit accessible name", "OpenPGP encrypted file"),
i18nc("Name filter binary", "OpenPGP Files (*.gpg *.pgp)"),
i18nc("Name filter ASCII", "OpenPGP Files (*.asc)"),
},
{
SignEncryptFilesWizard::EncryptedCMS,
QStringLiteral("document-encrypt"),
i18nc("@info:tooltip", "This is the filename of the S/MIME encrypted file."),
i18nc("Lineedit accessible name", "S/MIME encrypted file"),
i18nc("Name filter binary", "S/MIME Files (*.p7m)"),
i18nc("Name filter ASCII", "S/MIME Files (*.p7m *.pem)"),
},
{
SignEncryptFilesWizard::Directory,
QStringLiteral("folder"),
i18nc("@info:tooltip", "The resulting files are written to this directory."),
i18nc("Lineedit accessible name", "Output directory"),
{},
{},
},
}};
if (!mRequesters.empty()) {
return;
}
const bool isAscii = FileOperationsPreferences().addASCIIArmor();
for (const auto &requester : requestersInfo) {
const auto id = requester.id;
auto requesterWithIcon = new FileNameRequesterWithIcon{id == SignEncryptFilesWizard::Directory ? QDir::Dirs : QDir::Files, this};
requesterWithIcon->setIcon(QIcon::fromTheme(requester.icon));
requesterWithIcon->setToolTip(requester.toolTip);
requesterWithIcon->requester()->setAccessibleNameOfLineEdit(requester.accessibleName);
requesterWithIcon->setNameFilter(isAscii ? requester.nameFilterAscii : requester.nameFilterBinary);
lay->addWidget(requesterWithIcon);
connect(requesterWithIcon, &FileNameRequesterWithIcon::fileNameChanged, this, [this, id](const QString &newName) {
mOutNames[id] = newName;
});
mRequesters.insert(id, requesterWithIcon);
}
}
public:
void setOutputNames(const QMap<int, QString> &names)
{
Q_ASSERT(mOutNames.isEmpty());
for (auto it = std::begin(names); it != std::end(names); ++it) {
mRequesters.value(it.key())->setFileName(it.value());
}
mOutNames = names;
updateFileWidgets();
}
QMap<int, QString> outputNames() const
{
if (!mUseOutputDir) {
auto ret = mOutNames;
ret.remove(SignEncryptFilesWizard::Directory);
return ret;
}
return mOutNames;
}
bool encryptSymmetric() const
{
return mWidget->encryptSymmetric();
}
private Q_SLOTS:
void updateCommitButton(const SignEncryptWidget::Operations op)
{
if (mParent->currentPage() != this) {
return;
}
QString label;
switch (op) {
case SignEncryptWidget::Sign:
label = i18nc("@action:button", "Sign");
break;
case SignEncryptWidget::Encrypt:
label = i18nc("@action:button", "Encrypt");
break;
case SignEncryptWidget::SignAndEncrypt:
label = i18nc("@action:button", "Sign / Encrypt");
break;
default:;
};
auto btn = qobject_cast<QPushButton *>(mParent->button(QWizard::CommitButton));
if (!label.isEmpty()) {
mParent->setButtonText(QWizard::CommitButton, label);
if (DeVSCompliance::isActive()) {
const bool de_vs = DeVSCompliance::isCompliant() && mWidget->isDeVsAndValid();
DeVSCompliance::decorate(btn, de_vs);
mParent->setLabelText(DeVSCompliance::name(de_vs));
}
} else {
mParent->setButtonText(QWizard::CommitButton, i18n("Next"));
btn->setIcon(QIcon());
btn->setStyleSheet(QString());
}
Q_EMIT completeChanged();
}
void updateFileWidgets()
{
if (mRequesters.isEmpty()) {
return;
}
const std::vector<Key> recipients = mWidget->recipients();
- const Key sigKey = mWidget->signKey();
+ const Key sigKey = mWidget->signUserId().parent();
const bool pgp = mWidget->encryptSymmetric() || std::any_of(std::cbegin(recipients), std::cend(recipients), [](const auto &k) {
return k.protocol() == Protocol::OpenPGP;
});
const bool cms = std::any_of(std::cbegin(recipients), std::cend(recipients), [](const auto &k) {
return k.protocol() == Protocol::CMS;
});
mOutLayout->setEnabled(false);
if (cms || pgp || !sigKey.isNull()) {
mPlaceholderWidget->setVisible(false);
mOutputLabel->setVisible(true);
mRequesters[SignEncryptFilesWizard::SignatureCMS]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::CMS);
mRequesters[SignEncryptFilesWizard::EncryptedCMS]->setVisible(!mUseOutputDir && cms);
mRequesters[SignEncryptFilesWizard::CombinedPGP]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::OpenPGP && pgp);
mRequesters[SignEncryptFilesWizard::EncryptedPGP]->setVisible(!mUseOutputDir && sigKey.protocol() != Protocol::OpenPGP && pgp);
mRequesters[SignEncryptFilesWizard::SignaturePGP]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::OpenPGP && !pgp);
mRequesters[SignEncryptFilesWizard::Directory]->setVisible(mUseOutputDir);
auto firstNotHidden = std::find_if(std::cbegin(mRequesters), std::cend(mRequesters), [](auto w) {
return !w->isHidden();
});
mOutputLabel->setBuddy(*firstNotHidden);
} else {
mPlaceholderWidget->setVisible(true);
mOutputLabel->setVisible(false);
std::for_each(std::cbegin(mRequesters), std::cend(mRequesters), [](auto w) {
w->setVisible(false);
});
mOutputLabel->setBuddy(nullptr);
}
mOutLayout->setEnabled(true);
}
private:
SignEncryptFilesWizard *mParent;
SignEncryptWidget *mWidget;
QMap<int, QString> mOutNames;
QMap<int, FileNameRequesterWithIcon *> mRequesters;
QVBoxLayout *mOutLayout;
QWidget *mPlaceholderWidget;
QCheckBox *mUseOutputDirChk;
QLabel *mOutputLabel;
bool mArchive;
bool mUseOutputDir;
bool mSingleFile;
};
class ResultPage : public NewResultPage
{
Q_OBJECT
public:
explicit ResultPage(QWidget *parent = nullptr)
: NewResultPage(parent)
, mParent((SignEncryptFilesWizard *)parent)
{
setTitle(i18nc("@title", "Results"));
setSubTitle(i18nc("@title", "Status and progress of the crypto operations is shown here."));
}
void initializePage() override
{
mParent->setLabelText(QString());
}
private:
SignEncryptFilesWizard *mParent;
};
SignEncryptFilesWizard::SignEncryptFilesWizard(QWidget *parent, Qt::WindowFlags f)
: QWizard(parent, f)
{
readConfig();
const bool de_vs = DeVSCompliance::isActive();
#ifdef Q_OS_WIN
// Enforce modern style to avoid vista style ugliness.
setWizardStyle(QWizard::ModernStyle);
#endif
mSigEncPage = new SigEncPage(this);
mResultPage = new ResultPage(this);
connect(this, &QWizard::currentIdChanged, this, &SignEncryptFilesWizard::slotCurrentIdChanged);
setPage(SigEncPageId, mSigEncPage);
setPage(ResultPageId, mResultPage);
setOptions(QWizard::IndependentPages | //
(de_vs ? QWizard::HaveCustomButton1 : QWizard::WizardOption(0)) | //
QWizard::NoBackButtonOnLastPage | //
QWizard::NoBackButtonOnStartPage);
if (de_vs) {
/* We use a custom button to display a label next to the
buttons. */
auto btn = button(QWizard::CustomButton1);
/* We style the button so that it looks and acts like a
label. */
btn->setStyleSheet(QStringLiteral("border: none"));
btn->setFocusPolicy(Qt::NoFocus);
}
}
void SignEncryptFilesWizard::setLabelText(const QString &label)
{
button(QWizard::CommitButton)->setToolTip(label);
setButtonText(QWizard::CustomButton1, label);
}
void SignEncryptFilesWizard::slotCurrentIdChanged(int id)
{
if (id == ResultPageId) {
Q_EMIT operationPrepared();
}
}
SignEncryptFilesWizard::~SignEncryptFilesWizard()
{
qCDebug(KLEOPATRA_LOG) << this << __func__;
writeConfig();
}
void SignEncryptFilesWizard::setSigningPreset(bool preset)
{
mSigEncPage->setSigningPreset(preset);
}
void SignEncryptFilesWizard::setSigningUserMutable(bool mut)
{
if (mut == mSigningUserMutable) {
return;
}
mSigningUserMutable = mut;
}
void SignEncryptFilesWizard::setEncryptionPreset(bool preset)
{
mSigEncPage->setEncryptionPreset(preset);
}
void SignEncryptFilesWizard::setEncryptionUserMutable(bool mut)
{
if (mut == mEncryptionUserMutable) {
return;
}
mEncryptionUserMutable = mut;
}
void SignEncryptFilesWizard::setArchiveForced(bool archive)
{
mSigEncPage->setArchiveForced(archive);
}
void SignEncryptFilesWizard::setArchiveMutable(bool archive)
{
mSigEncPage->setArchiveMutable(archive);
}
void SignEncryptFilesWizard::setSingleFile(bool singleFile)
{
mSigEncPage->setSingleFile(singleFile);
}
std::vector<Key> SignEncryptFilesWizard::resolvedRecipients() const
{
return mSigEncPage->recipients();
}
std::vector<Key> SignEncryptFilesWizard::resolvedSigners() const
{
return mSigEncPage->signers();
}
void SignEncryptFilesWizard::setTaskCollection(const std::shared_ptr<Kleo::Crypto::TaskCollection> &coll)
{
mResultPage->setTaskCollection(coll);
}
void SignEncryptFilesWizard::setOutputNames(const QMap<int, QString> &map) const
{
mSigEncPage->setOutputNames(map);
}
QMap<int, QString> SignEncryptFilesWizard::outputNames() const
{
return mSigEncPage->outputNames();
}
bool SignEncryptFilesWizard::encryptSymmetric() const
{
return mSigEncPage->encryptSymmetric();
}
void SignEncryptFilesWizard::readConfig()
{
KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("SignEncryptFilesWizard"));
const QSize size = dialog.readEntry("Size", QSize(640, 480));
if (size.isValid()) {
resize(size);
}
}
void SignEncryptFilesWizard::writeConfig()
{
KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("SignEncryptFilesWizard"));
dialog.writeEntry("Size", size());
dialog.sync();
}
#include "signencryptfileswizard.moc"
#include "moc_signencryptfileswizard.cpp"
diff --git a/src/crypto/gui/signencryptwidget.cpp b/src/crypto/gui/signencryptwidget.cpp
index f0b828058..d30868d10 100644
--- a/src/crypto/gui/signencryptwidget.cpp
+++ b/src/crypto/gui/signencryptwidget.cpp
@@ -1,898 +1,947 @@
/* crypto/gui/signencryptwidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "signencryptwidget.h"
#include "kleopatra_debug.h"
#include "certificatelineedit.h"
#include "fileoperationspreferences.h"
#include "kleopatraapplication.h"
#include "settings.h"
#include "unknownrecipientwidget.h"
#include "dialogs/certificateselectiondialog.h"
#include "utils/gui-helper.h"
#include <QCheckBox>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QScrollArea>
#include <QScrollBar>
#include <QVBoxLayout>
#include <Libkleo/Algorithm>
#include <Libkleo/Compliance>
#include <Libkleo/DefaultKeyFilter>
#include <Libkleo/ExpiryChecker>
#include <Libkleo/ExpiryCheckerConfig>
#include <Libkleo/ExpiryCheckerSettings>
+#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyHelpers>
#include <Libkleo/KeyListModel>
#include <Libkleo/KeyListSortFilterProxyModel>
#include <Libkleo/UserIDSelectionCombo>
#include <Libkleo/GnuPG>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KMessageBox>
#include <KMessageWidget>
#include <KSharedConfig>
using namespace Kleo;
using namespace Kleo::Dialogs;
using namespace GpgME;
namespace
{
class SignCertificateFilter : public DefaultKeyFilter
{
public:
SignCertificateFilter(GpgME::Protocol proto)
: DefaultKeyFilter()
{
setIsBad(DefaultKeyFilter::NotSet);
setHasSecret(DefaultKeyFilter::Set);
setCanSign(DefaultKeyFilter::Set);
setValidIfSMIME(DefaultKeyFilter::Set);
if (proto == GpgME::OpenPGP) {
setIsOpenPGP(DefaultKeyFilter::Set);
} else if (proto == GpgME::CMS) {
setIsOpenPGP(DefaultKeyFilter::NotSet);
}
}
};
class EncryptCertificateFilter : public DefaultKeyFilter
{
public:
EncryptCertificateFilter(GpgME::Protocol proto)
: DefaultKeyFilter()
{
setIsBad(DefaultKeyFilter::NotSet);
setCanEncrypt(DefaultKeyFilter::Set);
setValidIfSMIME(DefaultKeyFilter::Set);
if (proto == GpgME::OpenPGP) {
setIsOpenPGP(DefaultKeyFilter::Set);
} else if (proto == GpgME::CMS) {
setIsOpenPGP(DefaultKeyFilter::NotSet);
}
}
};
class EncryptSelfCertificateFilter : public EncryptCertificateFilter
{
public:
EncryptSelfCertificateFilter(GpgME::Protocol proto)
: EncryptCertificateFilter(proto)
{
setRevoked(DefaultKeyFilter::NotSet);
setExpired(DefaultKeyFilter::NotSet);
setCanEncrypt(DefaultKeyFilter::Set);
setHasSecret(DefaultKeyFilter::Set);
setValidIfSMIME(DefaultKeyFilter::Set);
}
};
}
class SignEncryptWidget::Private
{
SignEncryptWidget *const q;
public:
struct RecipientWidgets {
CertificateLineEdit *edit;
KMessageWidget *expiryMessage;
};
explicit Private(SignEncryptWidget *qq, bool sigEncExclusive)
: q{qq}
, mModel{AbstractKeyListModel::createFlatKeyListModel(qq)}
, mIsExclusive{sigEncExclusive}
{
}
CertificateLineEdit *addRecipientWidget();
/* Inserts a new recipient widget after widget @p after or at the end
* if @p after is null. */
CertificateLineEdit *insertRecipientWidget(CertificateLineEdit *after);
void recpRemovalRequested(const RecipientWidgets &recipient);
void onProtocolChanged();
void updateCheckBoxes();
ExpiryChecker *expiryChecker();
- void updateExpiryMessages(KMessageWidget *w, const GpgME::Key &key, ExpiryChecker::CheckFlags flags);
+ void updateExpiryMessages(KMessageWidget *w, const GpgME::UserID &userID, ExpiryChecker::CheckFlags flags);
void updateAllExpiryMessages();
public:
UserIDSelectionCombo *mSigSelect = nullptr;
KMessageWidget *mSignKeyExpiryMessage = nullptr;
UserIDSelectionCombo *mSelfSelect = nullptr;
KMessageWidget *mEncryptToSelfKeyExpiryMessage = nullptr;
std::vector<RecipientWidgets> mRecpWidgets;
QList<UnknownRecipientWidget *> mUnknownWidgets;
QList<GpgME::Key> mAddedKeys;
QList<KeyGroup> mAddedGroups;
QVBoxLayout *mRecpLayout = nullptr;
Operations mOp;
AbstractKeyListModel *mModel = nullptr;
QCheckBox *mSymmetric = nullptr;
QCheckBox *mSigChk = nullptr;
QCheckBox *mEncOtherChk = nullptr;
QCheckBox *mEncSelfChk = nullptr;
GpgME::Protocol mCurrentProto = GpgME::UnknownProtocol;
const bool mIsExclusive;
std::unique_ptr<ExpiryChecker> mExpiryChecker;
};
SignEncryptWidget::SignEncryptWidget(QWidget *parent, bool sigEncExclusive)
: QWidget{parent}
, d{new Private{this, sigEncExclusive}}
{
auto lay = new QVBoxLayout(this);
lay->setContentsMargins(0, 0, 0, 0);
d->mModel->useKeyCache(true, KeyList::IncludeGroups);
const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty();
const bool havePublicKeys = !KeyCache::instance()->keys().empty();
const bool symmetricOnly = FileOperationsPreferences().symmetricEncryptionOnly();
const bool publicKeyOnly = FileOperationsPreferences().publicKeyEncryptionOnly();
/* The signature selection */
{
auto sigGrp = new QGroupBox{i18nc("@title:group", "Prove authenticity (sign)"), this};
d->mSigChk = new QCheckBox{i18n("Sign as:"), this};
d->mSigChk->setEnabled(haveSecretKeys);
d->mSigChk->setChecked(haveSecretKeys);
d->mSigSelect = new UserIDSelectionCombo{KeyUsage::Sign, this};
d->mSigSelect->setEnabled(d->mSigChk->isChecked());
d->mSignKeyExpiryMessage = new KMessageWidget{this};
d->mSignKeyExpiryMessage->setVisible(false);
+ connect(d->mSigSelect, &UserIDSelectionCombo::certificateSelectionRequested, this, [this]() {
+ ownCertificateSelectionRequested(CertificateSelectionDialog::SignOnly, d->mSigSelect);
+ });
+ connect(d->mSigSelect, &UserIDSelectionCombo::customItemSelected, this, [this]() {
+ updateOp();
+ d->updateExpiryMessages(d->mSignKeyExpiryMessage, signUserId(), ExpiryChecker::OwnSigningKey);
+ });
+
auto groupLayout = new QGridLayout{sigGrp};
groupLayout->setColumnStretch(1, 1);
groupLayout->addWidget(d->mSigChk, 0, 0);
groupLayout->addWidget(d->mSigSelect, 0, 1);
groupLayout->addWidget(d->mSignKeyExpiryMessage, 1, 1);
lay->addWidget(sigGrp);
connect(d->mSigChk, &QCheckBox::toggled, this, [this](bool checked) {
d->mSigSelect->setEnabled(checked);
updateOp();
- d->updateExpiryMessages(d->mSignKeyExpiryMessage, signKey(), ExpiryChecker::OwnSigningKey);
+ d->updateExpiryMessages(d->mSignKeyExpiryMessage, signUserId(), ExpiryChecker::OwnSigningKey);
});
connect(d->mSigSelect, &UserIDSelectionCombo::currentKeyChanged, this, [this]() {
updateOp();
- d->updateExpiryMessages(d->mSignKeyExpiryMessage, signKey(), ExpiryChecker::OwnSigningKey);
+ d->updateExpiryMessages(d->mSignKeyExpiryMessage, signUserId(), ExpiryChecker::OwnSigningKey);
});
}
// Recipient selection
{
auto encBox = new QGroupBox{i18nc("@title:group", "Encrypt"), this};
auto encBoxLay = new QVBoxLayout{encBox};
auto recipientGrid = new QGridLayout;
int row = 0;
// Own key
d->mEncSelfChk = new QCheckBox{i18n("Encrypt for me:"), this};
d->mEncSelfChk->setEnabled(haveSecretKeys && !symmetricOnly);
d->mEncSelfChk->setChecked(haveSecretKeys && !symmetricOnly);
d->mSelfSelect = new UserIDSelectionCombo{KeyUsage::Encrypt, this};
d->mSelfSelect->setEnabled(d->mEncSelfChk->isChecked());
+
+ connect(d->mSelfSelect, &UserIDSelectionCombo::certificateSelectionRequested, this, [this]() {
+ ownCertificateSelectionRequested(CertificateSelectionDialog::EncryptOnly, d->mSelfSelect);
+ });
+ connect(d->mSelfSelect, &UserIDSelectionCombo::customItemSelected, this, [this]() {
+ updateOp();
+ d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfUserId(), ExpiryChecker::OwnEncryptionKey);
+ });
+
d->mEncryptToSelfKeyExpiryMessage = new KMessageWidget{this};
d->mEncryptToSelfKeyExpiryMessage->setVisible(false);
recipientGrid->addWidget(d->mEncSelfChk, row, 0);
recipientGrid->addWidget(d->mSelfSelect, row, 1);
row++;
recipientGrid->addWidget(d->mEncryptToSelfKeyExpiryMessage, row, 1);
// Checkbox for other keys
row++;
d->mEncOtherChk = new QCheckBox{i18n("Encrypt for others:"), this};
d->mEncOtherChk->setEnabled(havePublicKeys && !symmetricOnly);
d->mEncOtherChk->setChecked(havePublicKeys && !symmetricOnly);
recipientGrid->addWidget(d->mEncOtherChk, row, 0, Qt::AlignTop);
connect(d->mEncOtherChk, &QCheckBox::toggled, this, [this](bool checked) {
for (const auto &recipient : std::as_const(d->mRecpWidgets)) {
recipient.edit->setEnabled(checked);
- d->updateExpiryMessages(recipient.expiryMessage, checked ? recipient.edit->key() : Key{}, ExpiryChecker::EncryptionKey);
+ d->updateExpiryMessages(recipient.expiryMessage, checked ? recipient.edit->userID() : UserID{}, ExpiryChecker::EncryptionKey);
}
updateOp();
});
d->mRecpLayout = new QVBoxLayout;
recipientGrid->addLayout(d->mRecpLayout, row, 1);
recipientGrid->setRowStretch(row + 1, 1);
// Scroll area for other keys
auto recipientWidget = new QWidget;
auto recipientScroll = new QScrollArea;
recipientWidget->setLayout(recipientGrid);
recipientScroll->setWidget(recipientWidget);
recipientScroll->setWidgetResizable(true);
recipientScroll->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);
recipientScroll->setFrameStyle(QFrame::NoFrame);
recipientScroll->setFocusPolicy(Qt::NoFocus);
recipientGrid->setContentsMargins(0, 0, 0, 0);
encBoxLay->addWidget(recipientScroll, 1);
auto bar = recipientScroll->verticalScrollBar();
connect(bar, &QScrollBar::rangeChanged, this, [bar](int, int max) {
bar->setValue(max);
});
d->addRecipientWidget();
// Checkbox for password
d->mSymmetric = new QCheckBox(i18n("Encrypt with password. Anyone you share the password with can read the data."));
d->mSymmetric->setToolTip(i18nc("Tooltip information for symmetric encryption",
"Additionally to the keys of the recipients you can encrypt your data with a password. "
"Anyone who has the password can read the data without any secret key. "
"Using a password is <b>less secure</b> then public key cryptography. Even if you pick a very strong password."));
d->mSymmetric->setChecked((symmetricOnly || !havePublicKeys) && !publicKeyOnly);
d->mSymmetric->setEnabled(!publicKeyOnly);
encBoxLay->addWidget(d->mSymmetric);
// Connect it
connect(d->mEncSelfChk, &QCheckBox::toggled, this, [this](bool checked) {
d->mSelfSelect->setEnabled(checked);
updateOp();
- d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfKey(), ExpiryChecker::OwnEncryptionKey);
+ d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfUserId(), ExpiryChecker::OwnEncryptionKey);
});
connect(d->mSelfSelect, &UserIDSelectionCombo::currentKeyChanged, this, [this]() {
updateOp();
- d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfKey(), ExpiryChecker::OwnEncryptionKey);
+ d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfUserId(), ExpiryChecker::OwnEncryptionKey);
});
connect(d->mSymmetric, &QCheckBox::toggled, this, &SignEncryptWidget::updateOp);
if (d->mIsExclusive) {
connect(d->mEncOtherChk, &QCheckBox::toggled, this, [this](bool value) {
if (d->mCurrentProto != GpgME::CMS) {
return;
}
if (value) {
d->mSigChk->setChecked(false);
}
});
connect(d->mEncSelfChk, &QCheckBox::toggled, this, [this](bool value) {
if (d->mCurrentProto != GpgME::CMS) {
return;
}
if (value) {
d->mSigChk->setChecked(false);
}
});
connect(d->mSigChk, &QCheckBox::toggled, this, [this](bool value) {
if (d->mCurrentProto != GpgME::CMS) {
return;
}
if (value) {
d->mEncSelfChk->setChecked(false);
d->mEncOtherChk->setChecked(false);
}
});
}
// Ensure that the d->mSigChk is aligned together with the encryption check boxes.
d->mSigChk->setMinimumWidth(qMax(d->mEncOtherChk->width(), d->mEncSelfChk->width()));
lay->addWidget(encBox);
}
connect(KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, this, [this]() {
d->updateCheckBoxes();
d->updateAllExpiryMessages();
});
connect(KleopatraApplication::instance(), &KleopatraApplication::configurationChanged, this, [this]() {
d->updateCheckBoxes();
d->mExpiryChecker.reset();
d->updateAllExpiryMessages();
});
loadKeys();
d->onProtocolChanged();
updateOp();
}
SignEncryptWidget::~SignEncryptWidget() = default;
void SignEncryptWidget::setSignAsText(const QString &text)
{
d->mSigChk->setText(text);
}
void SignEncryptWidget::setEncryptForMeText(const QString &text)
{
d->mEncSelfChk->setText(text);
}
void SignEncryptWidget::setEncryptForOthersText(const QString &text)
{
d->mEncOtherChk->setText(text);
}
void SignEncryptWidget::setEncryptWithPasswordText(const QString &text)
{
d->mSymmetric->setText(text);
}
CertificateLineEdit *SignEncryptWidget::Private::addRecipientWidget()
{
return insertRecipientWidget(nullptr);
}
CertificateLineEdit *SignEncryptWidget::Private::insertRecipientWidget(CertificateLineEdit *after)
{
Q_ASSERT(!after || mRecpLayout->indexOf(after) != -1);
const auto index = after ? mRecpLayout->indexOf(after) + 2 : mRecpLayout->count();
const RecipientWidgets recipient{new CertificateLineEdit{mModel, KeyUsage::Encrypt, new EncryptCertificateFilter{mCurrentProto}, q}, new KMessageWidget{q}};
recipient.edit->setAccessibleNameOfLineEdit(i18nc("text for screen readers", "recipient key"));
recipient.edit->setEnabled(mEncOtherChk->isChecked());
recipient.expiryMessage->setVisible(false);
if (static_cast<unsigned>(index / 2) < mRecpWidgets.size()) {
mRecpWidgets.insert(mRecpWidgets.begin() + index / 2, recipient);
} else {
mRecpWidgets.push_back(recipient);
}
if (mRecpLayout->count() > 0) {
auto prevWidget = after ? after : mRecpLayout->itemAt(mRecpLayout->count() - 1)->widget();
Kleo::forceSetTabOrder(prevWidget, recipient.edit);
Kleo::forceSetTabOrder(recipient.edit, recipient.expiryMessage);
}
mRecpLayout->insertWidget(index, recipient.edit);
mRecpLayout->insertWidget(index + 1, recipient.expiryMessage);
connect(recipient.edit, &CertificateLineEdit::keyChanged, q, &SignEncryptWidget::recipientsChanged);
connect(recipient.edit, &CertificateLineEdit::editingStarted, q, &SignEncryptWidget::recipientsChanged);
connect(recipient.edit, &CertificateLineEdit::cleared, q, &SignEncryptWidget::recipientsChanged);
connect(recipient.edit, &CertificateLineEdit::certificateSelectionRequested, q, [this, recipient]() {
q->certificateSelectionRequested(recipient.edit);
});
return recipient.edit;
}
void SignEncryptWidget::addRecipient(const Key &key)
{
CertificateLineEdit *certSel = d->addRecipientWidget();
if (!key.isNull()) {
certSel->setKey(key);
d->mAddedKeys << key;
}
}
void SignEncryptWidget::addRecipient(const KeyGroup &group)
{
CertificateLineEdit *certSel = d->addRecipientWidget();
if (!group.isNull()) {
certSel->setGroup(group);
d->mAddedGroups << group;
}
}
+void SignEncryptWidget::ownCertificateSelectionRequested(CertificateSelectionDialog::Options options, UserIDSelectionCombo *combo)
+{
+ CertificateSelectionDialog dialog{this};
+ dialog.setOptions(CertificateSelectionDialog::Options( //
+ CertificateSelectionDialog::SingleSelection | //
+ CertificateSelectionDialog::SecretKeys | //
+ CertificateSelectionDialog::optionsFromProtocol(d->mCurrentProto))
+ | //
+ options);
+
+ if (dialog.exec()) {
+ auto userId = dialog.selectedUserIDs()[0];
+ auto index = combo->findUserId(userId);
+ if (index == -1) {
+ combo->appendCustomItem(QIcon::fromTheme(QStringLiteral("emblem-error")), Formatting::summaryLine(userId), QVariant::fromValue(userId));
+ index = combo->combo()->count() - 1;
+ }
+ combo->combo()->setCurrentIndex(index);
+ }
+
+ recipientsChanged();
+}
+
void SignEncryptWidget::certificateSelectionRequested(CertificateLineEdit *certificateLineEdit)
{
CertificateSelectionDialog dlg{this};
dlg.setOptions(CertificateSelectionDialog::Options( //
CertificateSelectionDialog::MultiSelection | //
CertificateSelectionDialog::EncryptOnly | //
CertificateSelectionDialog::optionsFromProtocol(d->mCurrentProto) | //
CertificateSelectionDialog::IncludeGroups));
if (!certificateLineEdit->key().isNull()) {
const auto key = certificateLineEdit->key();
const auto name = QString::fromUtf8(key.userID(0).name());
const auto email = QString::fromUtf8(key.userID(0).email());
dlg.setStringFilter(!name.isEmpty() ? name : email);
} else if (!certificateLineEdit->group().isNull()) {
dlg.setStringFilter(certificateLineEdit->group().name());
} else if (!certificateLineEdit->userID().isNull()) {
const auto userID = certificateLineEdit->userID();
const auto name = QString::fromUtf8(userID.name());
const auto email = QString::fromUtf8(userID.email());
dlg.setStringFilter(!name.isEmpty() ? name : email);
} else {
dlg.setStringFilter(certificateLineEdit->text());
}
if (dlg.exec()) {
const std::vector<UserID> userIds = dlg.selectedUserIDs();
const std::vector<KeyGroup> groups = dlg.selectedGroups();
if (userIds.size() == 0 && groups.size() == 0) {
return;
}
CertificateLineEdit *certWidget = nullptr;
for (const auto &userId : userIds) {
if (!certWidget) {
certWidget = certificateLineEdit;
} else {
certWidget = d->insertRecipientWidget(certWidget);
}
certWidget->setUserID(userId);
}
for (const KeyGroup &group : groups) {
if (!certWidget) {
certWidget = certificateLineEdit;
} else {
certWidget = d->insertRecipientWidget(certWidget);
}
certWidget->setGroup(group);
}
}
recipientsChanged();
}
void SignEncryptWidget::clearAddedRecipients()
{
for (auto w : std::as_const(d->mUnknownWidgets)) {
d->mRecpLayout->removeWidget(w);
delete w;
}
for (auto &key : std::as_const(d->mAddedKeys)) {
removeRecipient(key);
}
for (auto &group : std::as_const(d->mAddedGroups)) {
removeRecipient(group);
}
}
void SignEncryptWidget::addUnknownRecipient(const char *keyID)
{
auto unknownWidget = new UnknownRecipientWidget(keyID);
d->mUnknownWidgets << unknownWidget;
if (d->mRecpLayout->count() > 0) {
auto lastWidget = d->mRecpLayout->itemAt(d->mRecpLayout->count() - 1)->widget();
setTabOrder(lastWidget, unknownWidget);
}
d->mRecpLayout->addWidget(unknownWidget);
connect(KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, this, [this]() {
// Check if any unknown recipient can now be found.
for (auto w : d->mUnknownWidgets) {
auto key = KeyCache::instance()->findByKeyIDOrFingerprint(w->keyID().toLatin1().constData());
if (key.isNull()) {
std::vector<std::string> subids;
subids.push_back(std::string(w->keyID().toLatin1().constData()));
for (const auto &subkey : KeyCache::instance()->findSubkeysByKeyID(subids)) {
key = subkey.parent();
}
}
if (key.isNull()) {
continue;
}
// Key is now available replace by line edit.
qCDebug(KLEOPATRA_LOG) << "Removing widget for keyid: " << w->keyID();
d->mRecpLayout->removeWidget(w);
d->mUnknownWidgets.removeAll(w);
delete w;
addRecipient(key);
}
});
}
void SignEncryptWidget::recipientsChanged()
{
const bool hasEmptyRecpWidget = std::any_of(std::cbegin(d->mRecpWidgets), std::cend(d->mRecpWidgets), [](auto w) {
return w.edit->isEmpty();
});
if (!hasEmptyRecpWidget) {
d->addRecipientWidget();
}
updateOp();
for (const auto &recipient : std::as_const(d->mRecpWidgets)) {
if (!recipient.edit->isEditingInProgress() || recipient.edit->isEmpty()) {
- d->updateExpiryMessages(recipient.expiryMessage, d->mEncOtherChk->isChecked() ? recipient.edit->key() : Key{}, ExpiryChecker::EncryptionKey);
+ d->updateExpiryMessages(recipient.expiryMessage, d->mEncOtherChk->isChecked() ? recipient.edit->userID() : UserID{}, ExpiryChecker::EncryptionKey);
}
}
}
-Key SignEncryptWidget::signKey() const
+UserID SignEncryptWidget::signUserId() const
{
if (d->mSigSelect->isEnabled()) {
- return d->mSigSelect->currentKey();
+ return d->mSigSelect->currentUserID();
}
- return Key();
+ return UserID();
}
-Key SignEncryptWidget::selfKey() const
+UserID SignEncryptWidget::selfUserId() const
{
if (d->mSelfSelect->isEnabled()) {
- return d->mSelfSelect->currentKey();
+ return d->mSelfSelect->currentUserID();
}
- return Key();
+ return UserID();
}
std::vector<Key> SignEncryptWidget::recipients() const
{
std::vector<Key> ret;
for (const auto &recipient : std::as_const(d->mRecpWidgets)) {
const auto *const w = recipient.edit;
if (!w->isEnabled()) {
// If one is disabled, all are disabled.
break;
}
const Key k = w->key();
const KeyGroup g = w->group();
const UserID u = w->userID();
if (!k.isNull()) {
ret.push_back(k);
} else if (!g.isNull()) {
const auto keys = g.keys();
std::copy(keys.begin(), keys.end(), std::back_inserter(ret));
} else if (!u.isNull()) {
ret.push_back(u.parent());
}
}
- const Key k = selfKey();
+ const Key k = selfUserId().parent();
if (!k.isNull()) {
ret.push_back(k);
}
return ret;
}
bool SignEncryptWidget::isDeVsAndValid() const
{
- if (!signKey().isNull() && !DeVSCompliance::keyIsCompliant(signKey())) {
+ if (!signUserId().isNull() && !DeVSCompliance::userIDIsCompliant(signUserId())) {
return false;
}
- if (!selfKey().isNull() && !DeVSCompliance::keyIsCompliant(selfKey())) {
+ if (!selfUserId().isNull() && !DeVSCompliance::userIDIsCompliant(selfUserId())) {
return false;
}
for (const auto &key : recipients()) {
if (!DeVSCompliance::keyIsCompliant(key)) {
return false;
}
}
return true;
}
static QString expiryMessage(const ExpiryChecker::Result &result)
{
if (result.expiration.certificate.isNull()) {
return {};
}
switch (result.expiration.status) {
case ExpiryChecker::Expired:
return i18nc("@info", "This certificate is expired.");
case ExpiryChecker::ExpiresSoon: {
if (result.expiration.duration.count() == 0) {
return i18nc("@info", "This certificate expires today.");
} else {
return i18ncp("@info", "This certificate expires tomorrow.", "This certificate expires in %1 days.", result.expiration.duration.count());
}
}
case ExpiryChecker::NoSuitableSubkey:
if (result.checkFlags & ExpiryChecker::EncryptionKey) {
return i18nc("@info", "This certificate cannot be used for encryption.");
} else {
return i18nc("@info", "This certificate cannot be used for signing.");
}
case ExpiryChecker::InvalidKey:
case ExpiryChecker::InvalidCheckFlags:
break; // wrong usage of ExpiryChecker; can be ignored
case ExpiryChecker::NotNearExpiry:;
}
return {};
}
void SignEncryptWidget::updateOp()
{
- const Key sigKey = signKey();
const std::vector<Key> recp = recipients();
Operations op = NoOperation;
- if (!sigKey.isNull()) {
+ if (!signUserId().isNull()) {
op |= Sign;
}
if (!recp.empty() || encryptSymmetric()) {
op |= Encrypt;
}
d->mOp = op;
Q_EMIT operationChanged(d->mOp);
Q_EMIT keysChanged();
}
SignEncryptWidget::Operations SignEncryptWidget::currentOp() const
{
return d->mOp;
}
namespace
{
bool recipientWidgetHasFocus(QWidget *w)
{
// check if w (or its focus proxy) or a child widget of w has focus
return w->hasFocus() || w->isAncestorOf(qApp->focusWidget());
}
}
void SignEncryptWidget::Private::recpRemovalRequested(const RecipientWidgets &recipient)
{
if (!recipient.edit) {
return;
}
const int emptyEdits = std::count_if(std::cbegin(mRecpWidgets), std::cend(mRecpWidgets), [](const auto &r) {
return r.edit->isEmpty();
});
if (emptyEdits > 1) {
if (recipientWidgetHasFocus(recipient.edit) || recipientWidgetHasFocus(recipient.expiryMessage)) {
const int index = mRecpLayout->indexOf(recipient.edit);
const auto focusWidget = (index < mRecpLayout->count() - 2) ? //
mRecpLayout->itemAt(index + 2)->widget()
: mRecpLayout->itemAt(mRecpLayout->count() - 3)->widget();
focusWidget->setFocus();
}
mRecpLayout->removeWidget(recipient.expiryMessage);
mRecpLayout->removeWidget(recipient.edit);
const auto it = std::find_if(std::begin(mRecpWidgets), std::end(mRecpWidgets), [recipient](const auto &r) {
return r.edit == recipient.edit;
});
mRecpWidgets.erase(it);
recipient.expiryMessage->deleteLater();
recipient.edit->deleteLater();
}
}
void SignEncryptWidget::removeRecipient(const GpgME::Key &key)
{
for (const auto &recipient : std::as_const(d->mRecpWidgets)) {
const auto editKey = recipient.edit->key();
if (key.isNull() && editKey.isNull()) {
d->recpRemovalRequested(recipient);
return;
}
if (editKey.primaryFingerprint() && key.primaryFingerprint() && !strcmp(editKey.primaryFingerprint(), key.primaryFingerprint())) {
d->recpRemovalRequested(recipient);
return;
}
}
}
void SignEncryptWidget::removeRecipient(const KeyGroup &group)
{
for (const auto &recipient : std::as_const(d->mRecpWidgets)) {
const auto editGroup = recipient.edit->group();
if (group.isNull() && editGroup.isNull()) {
d->recpRemovalRequested(recipient);
return;
}
if (editGroup.name() == group.name()) {
d->recpRemovalRequested(recipient);
return;
}
}
}
bool SignEncryptWidget::encryptSymmetric() const
{
return d->mSymmetric->isChecked();
}
void SignEncryptWidget::loadKeys()
{
KConfigGroup keys(KSharedConfig::openConfig(), QStringLiteral("SignEncryptKeys"));
auto cache = KeyCache::instance();
d->mSigSelect->setDefaultKey(keys.readEntry("SigningKey", QString()));
d->mSelfSelect->setDefaultKey(keys.readEntry("EncryptKey", QString()));
}
void SignEncryptWidget::saveOwnKeys() const
{
KConfigGroup keys(KSharedConfig::openConfig(), QStringLiteral("SignEncryptKeys"));
auto sigKey = d->mSigSelect->currentKey();
auto encKey = d->mSelfSelect->currentKey();
if (!sigKey.isNull()) {
keys.writeEntry("SigningKey", sigKey.primaryFingerprint());
}
if (!encKey.isNull()) {
keys.writeEntry("EncryptKey", encKey.primaryFingerprint());
}
}
void SignEncryptWidget::setSigningChecked(bool value)
{
d->mSigChk->setChecked(value && !KeyCache::instance()->secretKeys().empty());
}
void SignEncryptWidget::setEncryptionChecked(bool checked)
{
if (checked) {
const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty();
const bool havePublicKeys = !KeyCache::instance()->keys().empty();
const bool symmetricOnly = FileOperationsPreferences().symmetricEncryptionOnly();
const bool publicKeyOnly = FileOperationsPreferences().publicKeyEncryptionOnly();
d->mEncSelfChk->setChecked(haveSecretKeys && !symmetricOnly);
d->mEncOtherChk->setChecked(havePublicKeys && !symmetricOnly);
d->mSymmetric->setChecked((symmetricOnly || !havePublicKeys) && !publicKeyOnly);
} else {
d->mEncSelfChk->setChecked(false);
d->mEncOtherChk->setChecked(false);
d->mSymmetric->setChecked(false);
}
}
void SignEncryptWidget::setProtocol(GpgME::Protocol proto)
{
if (d->mCurrentProto == proto) {
return;
}
d->mCurrentProto = proto;
d->onProtocolChanged();
}
void Kleo::SignEncryptWidget::Private::onProtocolChanged()
{
mSigSelect->setKeyFilter(std::shared_ptr<KeyFilter>(new SignCertificateFilter(mCurrentProto)));
mSelfSelect->setKeyFilter(std::shared_ptr<KeyFilter>(new EncryptSelfCertificateFilter(mCurrentProto)));
const auto encFilter = std::shared_ptr<KeyFilter>(new EncryptCertificateFilter(mCurrentProto));
for (const auto &recipient : std::as_const(mRecpWidgets)) {
recipient.edit->setKeyFilter(encFilter);
}
if (mIsExclusive) {
mSymmetric->setDisabled(mCurrentProto == GpgME::CMS);
if (mSymmetric->isChecked() && mCurrentProto == GpgME::CMS) {
mSymmetric->setChecked(false);
}
if (mSigChk->isChecked() && mCurrentProto == GpgME::CMS && (mEncSelfChk->isChecked() || mEncOtherChk->isChecked())) {
mSigChk->setChecked(false);
}
}
}
static bool recipientIsOkay(const CertificateLineEdit *edit)
{
if (!edit->isEnabled() || edit->isEmpty()) {
return true;
}
if (!edit->hasAcceptableInput()) {
return false;
}
if (const auto userID = edit->userID(); !userID.isNull()) {
return Kleo::canBeUsedForEncryption(userID.parent()) && !userID.isBad();
}
if (const auto key = edit->key(); !key.isNull()) {
return Kleo::canBeUsedForEncryption(key);
}
if (const auto group = edit->group(); !group.isNull()) {
return std::ranges::all_of(group.keys(), Kleo::canBeUsedForEncryption);
}
// we should never reach this point
return false;
}
bool SignEncryptWidget::isComplete() const
{
if (currentOp() == NoOperation) {
return false;
}
- if ((currentOp() & SignEncryptWidget::Sign) && !Kleo::canBeUsedForSigning(signKey())) {
+ if ((currentOp() & SignEncryptWidget::Sign) && !Kleo::canBeUsedForSigning(signUserId().parent())) {
return false;
}
if (currentOp() & SignEncryptWidget::Encrypt) {
- if (!selfKey().isNull() && !Kleo::canBeUsedForEncryption(selfKey())) {
+ if (!selfUserId().isNull() && !Kleo::canBeUsedForEncryption(selfUserId().parent())) {
return false;
}
const bool allOtherRecipientsAreOkay = std::ranges::all_of(d->mRecpWidgets, [](const auto &r) {
return recipientIsOkay(r.edit);
});
if (!allOtherRecipientsAreOkay) {
return false;
}
}
return true;
}
bool SignEncryptWidget::validate()
{
CertificateLineEdit *firstUnresolvedRecipient = nullptr;
QStringList unresolvedRecipients;
for (const auto &recipient : std::as_const(d->mRecpWidgets)) {
if (recipient.edit->isEnabled() && !recipient.edit->hasAcceptableInput()) {
if (!firstUnresolvedRecipient) {
firstUnresolvedRecipient = recipient.edit;
}
unresolvedRecipients.push_back(recipient.edit->text().toHtmlEscaped());
}
}
if (!unresolvedRecipients.isEmpty()) {
KMessageBox::errorList(this,
i18n("Could not find a key for the following recipients:"),
unresolvedRecipients,
i18nc("@title:window", "Failed to find some keys"));
}
if (firstUnresolvedRecipient) {
firstUnresolvedRecipient->setFocus();
}
return unresolvedRecipients.isEmpty();
}
void SignEncryptWidget::Private::updateCheckBoxes()
{
const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty();
const bool havePublicKeys = !KeyCache::instance()->keys().empty();
const bool symmetricOnly = FileOperationsPreferences().symmetricEncryptionOnly();
mSigChk->setEnabled(haveSecretKeys);
mEncSelfChk->setEnabled(haveSecretKeys && !symmetricOnly);
mEncOtherChk->setEnabled(havePublicKeys && !symmetricOnly);
if (symmetricOnly) {
mEncSelfChk->setChecked(false);
mEncOtherChk->setChecked(false);
mSymmetric->setChecked(true);
}
}
ExpiryChecker *Kleo::SignEncryptWidget::Private::expiryChecker()
{
if (!mExpiryChecker) {
mExpiryChecker.reset(new ExpiryChecker{ExpiryCheckerConfig{}.settings()});
}
return mExpiryChecker.get();
}
-void SignEncryptWidget::Private::updateExpiryMessages(KMessageWidget *messageWidget, const GpgME::Key &key, ExpiryChecker::CheckFlags flags)
+void SignEncryptWidget::Private::updateExpiryMessages(KMessageWidget *messageWidget, const GpgME::UserID &userID, ExpiryChecker::CheckFlags flags)
{
messageWidget->setCloseButtonVisible(false);
- if (!Settings{}.showExpiryNotifications() || key.isNull()) {
+
+ if (userID.isNull()) {
messageWidget->setVisible(false);
- } else {
- const auto result = expiryChecker()->checkKey(key, flags);
+ } else if (userID.parent().isExpired()) {
+ messageWidget->setText(i18nc("@info", "This certificate is expired."));
+ messageWidget->setVisible(true);
+ } else if (userID.isRevoked() || userID.parent().isRevoked()) {
+ messageWidget->setText(i18nc("@info", "This certificate is revoked."));
+ messageWidget->setVisible(true);
+ } else if (Settings{}.showExpiryNotifications()) {
+ const auto result = expiryChecker()->checkKey(userID.parent(), flags);
const auto message = expiryMessage(result);
messageWidget->setText(message);
messageWidget->setVisible(!message.isEmpty());
+ } else {
+ messageWidget->setVisible(false);
}
}
void SignEncryptWidget::Private::updateAllExpiryMessages()
{
- updateExpiryMessages(mSignKeyExpiryMessage, q->signKey(), ExpiryChecker::OwnSigningKey);
- updateExpiryMessages(mEncryptToSelfKeyExpiryMessage, q->selfKey(), ExpiryChecker::OwnEncryptionKey);
+ updateExpiryMessages(mSignKeyExpiryMessage, q->signUserId(), ExpiryChecker::OwnSigningKey);
+ updateExpiryMessages(mEncryptToSelfKeyExpiryMessage, q->selfUserId(), ExpiryChecker::OwnEncryptionKey);
for (const auto &recipient : std::as_const(mRecpWidgets)) {
if (recipient.edit->isEnabled()) {
- updateExpiryMessages(recipient.expiryMessage, recipient.edit->key(), ExpiryChecker::EncryptionKey);
+ updateExpiryMessages(recipient.expiryMessage, recipient.edit->userID(), ExpiryChecker::EncryptionKey);
}
}
}
#include "moc_signencryptwidget.cpp"
diff --git a/src/crypto/gui/signencryptwidget.h b/src/crypto/gui/signencryptwidget.h
index 696eaf4ae..b88b706b4 100644
--- a/src/crypto/gui/signencryptwidget.h
+++ b/src/crypto/gui/signencryptwidget.h
@@ -1,132 +1,137 @@
/* crypto/gui/signencryptwidget.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
+#include "dialogs/certificateselectiondialog.h"
+
#include <QList>
#include <QWidget>
#include <gpgme++/global.h>
+#include <Libkleo/UserIDSelectionCombo>
+
#include <memory>
namespace GpgME
{
class Key;
}
namespace Kleo
{
class CertificateLineEdit;
class KeyGroup;
class SignEncryptWidget : public QWidget
{
Q_OBJECT
public:
enum Operation { NoOperation = 0x00, Sign = 0x01, Encrypt = 0x02, SignAndEncrypt = Sign | Encrypt };
Q_DECLARE_FLAGS(Operations, Operation)
/** If cmsSigEncExclusive is true CMS operations can be
* done only either as sign or as encrypt */
explicit SignEncryptWidget(QWidget *parent = nullptr, bool cmsSigEncExclusive = false);
~SignEncryptWidget() override;
/** Overwrite default text with custom text, e.g. with a character marked
* as shortcut key. */
void setSignAsText(const QString &text);
void setEncryptForMeText(const QString &text);
void setEncryptForOthersText(const QString &text);
void setEncryptWithPasswordText(const QString &text);
/** Returns the list of recipients selected in the dialog
* or an empty list if encryption is disabled */
std::vector<GpgME::Key> recipients() const;
/** Returns the selected signing key or a null key if signing
* is disabled. */
- GpgME::Key signKey() const;
+ GpgME::UserID signUserId() const;
/** Returns the selected encrypt to self key or a null key if
* encrypt to self is disabled. */
- GpgME::Key selfKey() const;
+ GpgME::UserID selfUserId() const;
/** Returns the operation based on the current selection. */
Operations currentOp() const;
/** Whether or not symmetric encryption should also be used. */
bool encryptSymmetric() const;
/** Save the currently selected signing and encrypt to self keys. */
void saveOwnKeys() const;
/** Return whether or not all keys involved in the operation are
compliant with CO_DE_VS, and all keys are valid (i.e. all
userIDs have Validity >= Full). */
bool isDeVsAndValid() const;
/** Set whether or not signing group should be checked */
void setSigningChecked(bool value);
/** Set whether or not encryption group should be checked */
void setEncryptionChecked(bool value);
/** Filter for a specific protocol. Use UnknownProtocol for both
* S/MIME and OpenPGP */
void setProtocol(GpgME::Protocol protocol);
/** Add a recipient with the key key */
void addRecipient(const GpgME::Key &key);
/** Add a group of recipients */
void addRecipient(const Kleo::KeyGroup &group);
/** Add a placehoder for an unknown key */
void addUnknownRecipient(const char *keyId);
/** Remove all Recipients added by keyId or by key. */
void clearAddedRecipients();
/** Remove a Recipient key */
void removeRecipient(const GpgME::Key &key);
/** Remove a recipient group */
void removeRecipient(const Kleo::KeyGroup &group);
/** Returns true if all required information has been entered. */
bool isComplete() const;
/** Returns true if all recipients have been resolved. Otherwise, shows
an error message and returns false. */
bool validate();
protected Q_SLOTS:
void updateOp();
void recipientsChanged();
void certificateSelectionRequested(CertificateLineEdit *w);
+ void ownCertificateSelectionRequested(Kleo::Dialogs::CertificateSelectionDialog::Options options, UserIDSelectionCombo *combo);
protected:
void loadKeys();
Q_SIGNALS:
/* Emitted when the certificate selection changed the operation
* with that selection. e.g. "Sign" or "Sign/Encrypt".
* If no crypto operation is selected this returns a null string. */
void operationChanged(Operations op);
/* Emitted when the certificate selection might be changed. */
void keysChanged();
private:
class Private;
const std::unique_ptr<Private> d;
};
} // namespace Kleo
Q_DECLARE_OPERATORS_FOR_FLAGS(Kleo::SignEncryptWidget::Operations)
diff --git a/src/view/padwidget.cpp b/src/view/padwidget.cpp
index 6f23369b2..482f96dee 100644
--- a/src/view/padwidget.cpp
+++ b/src/view/padwidget.cpp
@@ -1,546 +1,546 @@
/* -*- mode: c++; c-basic-offset:4 -*-
padwidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2018 Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "padwidget.h"
#include "kleopatra_debug.h"
#include <settings.h>
#include <Libkleo/Classify>
#include <Libkleo/Compliance>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KleoException>
#include <Libkleo/SystemInfo>
#include "crypto/gui/resultitemwidget.h"
#include "crypto/gui/signencryptwidget.h"
#include "crypto/decryptverifytask.h"
#include "crypto/signencrypttask.h"
#include "utils/input.h"
#include "utils/output.h"
#include <Libkleo/GnuPG>
#include "commands/importcertificatefromdatacommand.h"
#include <gpgme++/data.h>
#include <gpgme++/decryptionresult.h>
#include <QGpgME/DataProvider>
#include <QButtonGroup>
#include <QFontDatabase>
#include <QFontMetrics>
#include <QLabel>
#include <QProgressBar>
#include <QPushButton>
#include <QRadioButton>
#include <QStyle>
#include <QTabWidget>
#include <QTextEdit>
#include <QVBoxLayout>
#include <KColorScheme>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KMessageBox>
#include <KMessageWidget>
#include <KSharedConfig>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace Kleo::Crypto::Gui;
static GpgME::Protocol getProtocol(const std::shared_ptr<const Kleo::Crypto::Task::Result> &result)
{
const auto dvResult = dynamic_cast<const Kleo::Crypto::DecryptVerifyResult *>(result.get());
if (dvResult) {
for (const auto &key : KeyCache::instance()->findRecipients(dvResult->decryptionResult())) {
return key.protocol();
}
for (const auto &key : KeyCache::instance()->findSigners(dvResult->verificationResult())) {
return key.protocol();
}
}
return GpgME::UnknownProtocol;
}
class PadWidget::Private
{
friend class ::Kleo::PadWidget;
public:
Private(PadWidget *qq)
: q(qq)
, mEdit(new QTextEdit)
, mCryptBtn(new QPushButton(QIcon::fromTheme(QStringLiteral("document-edit-sign-encrypt")), i18n("Sign / Encrypt Notepad")))
, mDecryptBtn(new QPushButton(QIcon::fromTheme(QStringLiteral("document-edit-decrypt-verify")), i18n("Decrypt / Verify Notepad")))
, mImportBtn(new QPushButton(QIcon::fromTheme(QStringLiteral("view-certificate-import")), i18n("Import Notepad")))
, mRevertBtn(new QPushButton(QIcon::fromTheme(QStringLiteral("edit-undo")), i18n("Revert")))
, mMessageWidget{new KMessageWidget}
, mAdditionalInfoLabel(new QLabel)
, mSigEncWidget(new SignEncryptWidget(nullptr, true))
, mProgressBar(new QProgressBar)
, mProgressLabel(new QLabel)
, mLastResultWidget(nullptr)
, mPGPRB(nullptr)
, mCMSRB(nullptr)
, mImportProto(GpgME::UnknownProtocol)
{
auto vLay = new QVBoxLayout(q);
auto btnLay = new QHBoxLayout;
vLay->addLayout(btnLay);
btnLay->addWidget(mCryptBtn);
btnLay->addWidget(mDecryptBtn);
btnLay->addWidget(mImportBtn);
btnLay->addWidget(mRevertBtn);
mRevertBtn->setVisible(false);
btnLay->addWidget(mAdditionalInfoLabel);
btnLay->addStretch(-1);
mMessageWidget->setMessageType(KMessageWidget::Warning);
mMessageWidget->setIcon(q->style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, q));
mMessageWidget->setText(i18n("Signing and encryption is not possible."));
mMessageWidget->setToolTip(xi18nc("@info %1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
"<para>You cannot use <application>Kleopatra</application> for signing or encryption "
"because the <application>GnuPG</application> system used by <application>Kleopatra</application> is not %1.</para>",
DeVSCompliance::name(true)));
mMessageWidget->setCloseButtonVisible(false);
mMessageWidget->setVisible(false);
vLay->addWidget(mMessageWidget);
mProgressBar->setRange(0, 0);
mProgressBar->setVisible(false);
mProgressLabel->setVisible(false);
auto progLay = new QHBoxLayout;
progLay->addWidget(mProgressLabel);
progLay->addWidget(mProgressBar);
mStatusLay = new QVBoxLayout;
mStatusLay->addLayout(progLay);
vLay->addLayout(mStatusLay, 0);
auto tabWidget = new QTabWidget;
vLay->addWidget(tabWidget, 1);
tabWidget->addTab(mEdit, QIcon::fromTheme(QStringLiteral("edittext")), i18n("Notepad"));
// The recipients area
auto recipientsWidget = new QWidget;
auto recipientsVLay = new QVBoxLayout(recipientsWidget);
auto protocolSelectionLay = new QHBoxLayout;
bool pgpOnly = KeyCache::instance()->pgpOnly();
if (!pgpOnly) {
recipientsVLay->addLayout(protocolSelectionLay);
}
protocolSelectionLay->addWidget(new QLabel(i18nc("@label:textbox", "<h3>Protocol:</h3>")));
protocolSelectionLay->addStretch(-1);
// Once S/MIME is supported add radio for S/MIME here.
recipientsVLay->addWidget(mSigEncWidget);
tabWidget->addTab(recipientsWidget, QIcon::fromTheme(QStringLiteral("contact-new-symbolic")), i18n("Recipients"));
mEdit->setPlaceholderText(i18nc("@info:placeholder", "Enter a message to encrypt or decrypt..."));
auto fixedFont = QFont(QStringLiteral("Monospace"));
fixedFont.setStyleHint(QFont::TypeWriter);
// This does not work well
// QFontDatabase::systemFont(QFontDatabase::FixedFont);
mEdit->setFont(fixedFont);
mEdit->setAcceptRichText(false);
mEdit->setMinimumWidth(QFontMetrics(fixedFont).averageCharWidth() * 70);
if (KeyCache::instance()->pgpOnly() || !Settings{}.cmsEnabled()) {
mSigEncWidget->setProtocol(GpgME::OpenPGP);
} else {
auto grp = new QButtonGroup(q);
auto mPGPRB = new QRadioButton(i18n("OpenPGP"));
auto mCMSRB = new QRadioButton(i18n("S/MIME"));
grp->addButton(mPGPRB);
grp->addButton(mCMSRB);
KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("Notepad"));
if (config.readEntry("wasCMS", false)) {
mCMSRB->setChecked(true);
mSigEncWidget->setProtocol(GpgME::CMS);
} else {
mPGPRB->setChecked(true);
mSigEncWidget->setProtocol(GpgME::OpenPGP);
}
protocolSelectionLay->addWidget(mPGPRB);
protocolSelectionLay->addWidget(mCMSRB);
connect(mPGPRB, &QRadioButton::toggled, q, [this](bool value) {
if (value) {
mSigEncWidget->setProtocol(GpgME::OpenPGP);
}
});
connect(mCMSRB, &QRadioButton::toggled, q, [this](bool value) {
if (value) {
mSigEncWidget->setProtocol(GpgME::CMS);
}
});
}
updateButtons();
connect(mEdit, &QTextEdit::textChanged, q, [this]() {
updateButtons();
});
connect(mCryptBtn, &QPushButton::clicked, q, [this]() {
doEncryptSign();
});
connect(mSigEncWidget, &SignEncryptWidget::operationChanged, q, [this]() {
updateButtons();
});
connect(mDecryptBtn, &QPushButton::clicked, q, [this]() {
doDecryptVerify();
});
connect(mImportBtn, &QPushButton::clicked, q, [this]() {
doImport();
});
connect(mRevertBtn, &QPushButton::clicked, q, [this]() {
revert();
});
}
void revert()
{
mEdit->setPlainText(QString::fromUtf8(mInputData));
mRevertBtn->setVisible(false);
}
void updateRecipientsFromResult(const Kleo::Crypto::DecryptVerifyResult &result)
{
const auto decResult = result.decryptionResult();
for (const auto &recipient : decResult.recipients()) {
if (!recipient.keyID()) {
continue;
}
GpgME::Key key;
if (strlen(recipient.keyID()) < 16) {
key = KeyCache::instance()->findByShortKeyID(recipient.keyID());
} else {
key = KeyCache::instance()->findByKeyIDOrFingerprint(recipient.keyID());
}
if (key.isNull()) {
std::vector<std::string> subids;
subids.push_back(std::string(recipient.keyID()));
for (const auto &subkey : KeyCache::instance()->findSubkeysByKeyID(subids)) {
key = subkey.parent();
break;
}
}
if (key.isNull()) {
qCDebug(KLEOPATRA_LOG) << "Unknown key" << recipient.keyID();
mSigEncWidget->addUnknownRecipient(recipient.keyID());
continue;
}
bool keyFound = false;
for (const auto &existingKey : mSigEncWidget->recipients()) {
if (existingKey.primaryFingerprint() && key.primaryFingerprint() && !strcmp(existingKey.primaryFingerprint(), key.primaryFingerprint())) {
keyFound = true;
break;
}
}
if (!keyFound) {
mSigEncWidget->addRecipient(key);
}
}
}
void cryptDone(const std::shared_ptr<const Kleo::Crypto::Task::Result> &result)
{
updateButtons();
mProgressBar->setVisible(false);
mProgressLabel->setVisible(false);
if (!result->error().isCanceled()) {
mLastResultWidget = new ResultItemWidget(result);
mLastResultWidget->showCloseButton(true);
mStatusLay->addWidget(mLastResultWidget);
connect(mLastResultWidget, &ResultItemWidget::closeButtonClicked, q, [this]() {
removeLastResultItem();
});
}
// Check result protocol
if (mPGPRB) {
auto proto = getProtocol(result);
if (proto == GpgME::UnknownProtocol) {
proto = mPGPRB->isChecked() ? GpgME::OpenPGP : GpgME::CMS;
} else if (proto == GpgME::OpenPGP) {
mPGPRB->setChecked(true);
} else if (proto == GpgME::CMS) {
mCMSRB->setChecked(true);
}
KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("Notepad"));
config.writeEntry("wasCMS", proto == GpgME::CMS);
}
if (result->error()) {
if (!result->errorString().isEmpty()) {
KMessageBox::error(q, result->errorString(), i18nc("@title", "Error in crypto action"));
}
} else if (!result->error().isCanceled()) {
mEdit->setPlainText(QString::fromUtf8(mOutputData));
mOutputData.clear();
mRevertBtn->setVisible(true);
const auto decryptVerifyResult = dynamic_cast<const Kleo::Crypto::DecryptVerifyResult *>(result.get());
if (decryptVerifyResult) {
updateRecipientsFromResult(*decryptVerifyResult);
}
}
}
void doDecryptVerify()
{
doCryptoCommon();
mSigEncWidget->clearAddedRecipients();
mProgressLabel->setText(i18n("Decrypt / Verify") + QStringLiteral("..."));
auto input = Input::createFromByteArray(&mInputData, i18n("Notepad"));
auto output = Output::createFromByteArray(&mOutputData, i18n("Notepad"));
AbstractDecryptVerifyTask *task;
auto classification = input->classification();
if (classification & Class::OpaqueSignature || classification & Class::ClearsignedMessage) {
auto verifyTask = new VerifyOpaqueTask();
verifyTask->setInput(input);
verifyTask->setOutput(output);
task = verifyTask;
} else {
auto decTask = new DecryptVerifyTask();
decTask->setInput(input);
decTask->setOutput(output);
task = decTask;
}
try {
task->autodetectProtocolFromInput();
} catch (const Kleo::Exception &e) {
KMessageBox::error(q, e.message(), i18nc("@title", "Error in crypto action"));
updateButtons();
mProgressBar->setVisible(false);
mProgressLabel->setVisible(false);
return;
}
connect(task, &Task::result, q, [this, task](const std::shared_ptr<const Kleo::Crypto::Task::Result> &result) {
qCDebug(KLEOPATRA_LOG) << "Decrypt / Verify done. Err:" << result->error().code();
task->deleteLater();
cryptDone(result);
});
task->start();
}
void removeLastResultItem()
{
if (mLastResultWidget) {
mStatusLay->removeWidget(mLastResultWidget);
delete mLastResultWidget;
mLastResultWidget = nullptr;
}
}
void doCryptoCommon()
{
mCryptBtn->setEnabled(false);
mDecryptBtn->setEnabled(false);
mImportBtn->setEnabled(false);
mProgressBar->setVisible(true);
mProgressLabel->setVisible(true);
mInputData = mEdit->toPlainText().toUtf8();
removeLastResultItem();
}
void doEncryptSign()
{
if (DeVSCompliance::isActive() && !DeVSCompliance::isCompliant()) {
KMessageBox::error(q->topLevelWidget(),
xi18nc("@info %1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
"<para>Sorry! You cannot use <application>Kleopatra</application> for signing or encryption "
"because the <application>GnuPG</application> system used by <application>Kleopatra</application> is not %1.</para>",
DeVSCompliance::name(true)));
return;
}
mSigEncWidget->saveOwnKeys();
doCryptoCommon();
switch (mSigEncWidget->currentOp()) {
case SignEncryptWidget::Sign:
mProgressLabel->setText(i18nc("@info:progress", "Signing notepad..."));
break;
case SignEncryptWidget::Encrypt:
mProgressLabel->setText(i18nc("@info:progress", "Encrypting notepad..."));
break;
case SignEncryptWidget::SignAndEncrypt:
mProgressLabel->setText(i18nc("@info:progress", "Signing and encrypting notepad..."));
break;
default:;
};
auto input = Input::createFromByteArray(&mInputData, i18n("Notepad"));
auto output = Output::createFromByteArray(&mOutputData, i18n("Notepad"));
auto task = new SignEncryptTask();
task->setInput(input);
task->setOutput(output);
- const auto sigKey = mSigEncWidget->signKey();
+ const auto sigKey = mSigEncWidget->signUserId().parent();
const std::vector<GpgME::Key> recipients = mSigEncWidget->recipients();
const bool encrypt = mSigEncWidget->encryptSymmetric() || !recipients.empty();
const bool sign = !sigKey.isNull();
if (sign) {
task->setSign(true);
std::vector<GpgME::Key> signVector;
signVector.push_back(sigKey);
task->setSigners(signVector);
} else {
task->setSign(false);
}
task->setEncrypt(encrypt);
task->setRecipients(recipients);
task->setEncryptSymmetric(mSigEncWidget->encryptSymmetric());
task->setAsciiArmor(true);
if (sign && !encrypt && sigKey.protocol() == GpgME::OpenPGP) {
task->setClearsign(true);
}
connect(task, &Task::result, q, [this, task](const std::shared_ptr<const Kleo::Crypto::Task::Result> &result) {
qCDebug(KLEOPATRA_LOG) << "Encrypt / Sign done. Err:" << result->error().code();
task->deleteLater();
cryptDone(result);
});
task->start();
}
void doImport()
{
doCryptoCommon();
mProgressLabel->setText(i18n("Importing..."));
auto cmd = new Kleo::ImportCertificateFromDataCommand(mInputData, mImportProto);
connect(cmd, &Kleo::ImportCertificatesCommand::finished, q, [this]() {
updateButtons();
mProgressBar->setVisible(false);
mProgressLabel->setVisible(false);
mRevertBtn->setVisible(true);
mEdit->setPlainText(QString());
});
cmd->start();
}
void checkImportProtocol()
{
QGpgME::QByteArrayDataProvider dp(mEdit->toPlainText().toUtf8());
GpgME::Data data(&dp);
auto type = data.type();
if (type == GpgME::Data::PGPKey) {
mImportProto = GpgME::OpenPGP;
} else if (type == GpgME::Data::X509Cert || type == GpgME::Data::PKCS12) {
mImportProto = GpgME::CMS;
} else {
mImportProto = GpgME::UnknownProtocol;
}
}
void updateButtons()
{
mAdditionalInfoLabel->setVisible(false);
mDecryptBtn->setEnabled(mEdit->document() && !mEdit->document()->isEmpty());
checkImportProtocol();
mImportBtn->setEnabled(mImportProto != GpgME::UnknownProtocol);
mCryptBtn->setEnabled(mSigEncWidget->currentOp() != SignEncryptWidget::NoOperation);
switch (mSigEncWidget->currentOp()) {
case SignEncryptWidget::Sign:
mCryptBtn->setText(i18nc("@action:button", "Sign Notepad"));
break;
case SignEncryptWidget::Encrypt:
mCryptBtn->setText(i18nc("@action:button", "Encrypt Notepad"));
break;
case SignEncryptWidget::SignAndEncrypt:
default:
mCryptBtn->setText(i18nc("@action:button", "Sign / Encrypt Notepad"));
};
if (!mSigEncWidget->isComplete()) {
mCryptBtn->setEnabled(false);
}
if (DeVSCompliance::isActive()) {
const bool de_vs = DeVSCompliance::isCompliant() && mSigEncWidget->isDeVsAndValid();
DeVSCompliance::decorate(mCryptBtn, de_vs);
mAdditionalInfoLabel->setText(DeVSCompliance::name(de_vs));
mAdditionalInfoLabel->setVisible(true);
if (!DeVSCompliance::isCompliant()) {
mCryptBtn->setEnabled(false);
}
mMessageWidget->setVisible(!DeVSCompliance::isCompliant());
}
}
private:
PadWidget *const q;
QTextEdit *mEdit;
QPushButton *mCryptBtn;
QPushButton *mDecryptBtn;
QPushButton *mImportBtn;
QPushButton *mRevertBtn;
KMessageWidget *mMessageWidget;
QLabel *mAdditionalInfoLabel;
QByteArray mInputData;
QByteArray mOutputData;
SignEncryptWidget *mSigEncWidget;
QProgressBar *mProgressBar;
QLabel *mProgressLabel;
QVBoxLayout *mStatusLay;
ResultItemWidget *mLastResultWidget;
QList<GpgME::Key> mAutoAddedKeys;
QRadioButton *mPGPRB;
QRadioButton *mCMSRB;
GpgME::Protocol mImportProto;
};
PadWidget::PadWidget(QWidget *parent)
: QWidget(parent)
, d(new Private(this))
{
}
void PadWidget::focusFirstChild(Qt::FocusReason reason)
{
d->mEdit->setFocus(reason);
}
#include "moc_padwidget.cpp"

File Metadata

Mime Type
text/x-diff
Expires
Thu, Dec 4, 1:05 PM (1 d, 7 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
b4/7f/ac4d3ead21d23e9eca839a80bd9b

Event Timeline