Page MenuHome GnuPG

No OneTemporary

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a4009562f..e4195c614 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,177 +1,177 @@
set(kleopatra_version 3.1.15)
# The following is for Windows. Keep in line with kleopatra_version.
set(kleopatra_fileversion 3,1,15,0)
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(kleopatra VERSION ${kleopatra_version})
# The RELEASE_SERVICE_VERSION is used by Gpg4win to add the Gpg4win version
if (NOT RELEASE_SERVICE_VERSION)
set (RELEASE_SERVICE_VERSION "21.03.80")
endif()
option(FORCE_DISABLE_KCMUTILS "Force building Kleopatra without KCMUtils. Doing this will disable configuration KCM Plugins. [default=OFF]" OFF)
option(DISABLE_KWATCHGNUPG "Don't build the kwatchgnupg tool [default=OFF]" OFF)
# Standalone build. Find / include everything necessary.
set(KF5_MIN_VERSION "5.79.0")
set(KMIME_VERSION "5.16.40")
-set(LIBKLEO_VERSION "5.16.52")
+set(LIBKLEO_VERSION "5.16.53")
set(QT_REQUIRED_VERSION "5.14.0")
set(GPGME_REQUIRED_VERSION "1.13.1")
if (WIN32)
set(KF5_WANT_VERSION "5.70.0")
set(KMIME_WANT_VERSION "5.12.0")
else ()
set(KF5_WANT_VERSION ${KF5_MIN_VERSION})
set(KMIME_WANT_VERSION ${KMIME_VERSION})
endif ()
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(ECMGeneratePriFile)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE)
include(ECMAddAppIcon)
include(ECMQtDeclareLoggingCategory)
# Find KF5 packages
if (NOT FORCE_DISABLE_KCMUTILS)
find_package(KF5KCMUtils ${KF5_WANT_VERSION} CONFIG REQUIRED)
endif()
find_package(KF5WidgetsAddons ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5ConfigWidgets ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5CoreAddons ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5Codecs ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5Config ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5I18n ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5IconThemes ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5ItemModels ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5XmlGui ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5WindowSystem ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5DocTools ${KF5_WANT_VERSION} CONFIG)
find_package(KF5Crash ${KF5_WANT_VERSION} REQUIRED)
set_package_properties(KF5DocTools PROPERTIES DESCRIPTION "Documentation tools" TYPE OPTIONAL PURPOSE "Required to generate Kleopatra documentation.")
# Optional packages
if (WIN32)
# Only a replacement available for Windows so this
# is required on other platforms.
find_package(KF5DBusAddons ${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(KF5DBusAddons ${KF5_WANT_VERSION} CONFIG REQUIRED)
set(_kleopatra_dbusaddons_libs KF5::DBusAddons)
endif()
set(HAVE_QDBUS ${Qt5DBus_FOUND})
find_package(Gpgmepp ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
find_package(QGpgme ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
# Kdepimlibs packages
find_package(KF5Libkleo ${LIBKLEO_VERSION} CONFIG REQUIRED)
find_package(KF5Mime ${KMIME_WANT_VERSION} CONFIG REQUIRED)
find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets Test Network PrintSupport)
find_package(Assuan2 REQUIRED)
set(HAVE_KCMUTILS ${KF5KCMUtils_FOUND})
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Boost 1.34.0 REQUIRED)
find_path(Boost_TOPOLOGICAL_SORT_DIR NAMES boost/graph/topological_sort.hpp PATHS ${Boost_INCLUDE_DIRS})
if(NOT Boost_TOPOLOGICAL_SORT_DIR)
message(FATAL_ERROR "The Boost Topological_sort header was NOT found. Should be part of Boost graph module.")
endif()
set(kleopatra_release FALSE)
if(NOT kleopatra_release)
if(GIT_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%h ${CMAKE_CURRENT_SOURCE_DIR}
WORKING_DIRECTORY ${kdepim_SOURCE_DIR}/kleopatra
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 ${kdepim_SOURCE_DIR}/kleopatra
OUTPUT_VARIABLE Kleopatra_WC_LAST_CHANGED_DATE)
string(REGEX REPLACE " [-0-9:+ ]*\n" "" Kleopatra_WC_LAST_CHANGED_DATE "${Kleopatra_WC_LAST_CHANGED_DATE}")
set(kleopatra_version "${kleopatra_version}-git${Kleopatra_WC_REVISION} (${Kleopatra_WC_LAST_CHANGED_DATE})")
endif()
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version-kleopatra.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/version-kleopatra.h)
include (ConfigureChecks.cmake)
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}
${Boost_INCLUDE_DIR}
${ASSUAN2_INCLUDES}
)
add_definitions(-D_ASSUAN_ONLY_GPG_ERRORS)
#add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050f00)
add_definitions(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x055000)
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-braces -Wno-parentheses -Wno-ignored-qualifiers")
endif()
add_definitions(-DQT_NO_EMIT)
remove_definitions(-DQT_NO_FOREACH)
kde_enable_exceptions()
option(USE_UNITY_CMAKE_SUPPORT "Use UNITY cmake support (speedup compile time)" FALSE)
set(COMPILE_WITH_UNITY_CMAKE_SUPPORT false)
if (USE_UNITY_CMAKE_SUPPORT)
if(${CMAKE_VERSION} VERSION_LESS "3.16.0")
message(STATUS "CMAKE version is less than 3.16.0 . We can't use cmake unify build support")
else()
set(COMPILE_WITH_UNITY_CMAKE_SUPPORT true)
endif()
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}
)
if(KF5DocTools_FOUND)
add_subdirectory(doc)
endif()
diff --git a/src/commands/lookupcertificatescommand.cpp b/src/commands/lookupcertificatescommand.cpp
index 64c024431..8719c7f41 100644
--- a/src/commands/lookupcertificatescommand.cpp
+++ b/src/commands/lookupcertificatescommand.cpp
@@ -1,403 +1,404 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/lookupcertificatescommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008, 2009 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "lookupcertificatescommand.h"
#include "importcertificatescommand_p.h"
#include "detailscommand.h"
+#include <Libkleo/Compat>
#include <Libkleo/GnuPG>
#include <dialogs/lookupcertificatesdialog.h>
#include <Libkleo/Formatting>
#include <Libkleo/Stl_Util>
#include <QGpgME/CryptoConfig>
#include <QGpgME/Protocol>
#include <QGpgME/KeyListJob>
#include <QGpgME/ImportFromKeyserverJob>
#include <gpgme++/key.h>
#include <gpgme++/keylistresult.h>
#include <gpgme++/importresult.h>
#include <KLocalizedString>
#include <KMessageBox>
#include "kleopatra_debug.h"
#include <QRegExp>
#include <vector>
#include <map>
#include <algorithm>
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::Dialogs;
using namespace GpgME;
using namespace QGpgME;
class LookupCertificatesCommand::Private : public ImportCertificatesCommand::Private
{
friend class ::Kleo::Commands::LookupCertificatesCommand;
LookupCertificatesCommand *q_func() const
{
return static_cast<LookupCertificatesCommand *>(q);
}
public:
explicit Private(LookupCertificatesCommand *qq, KeyListController *c);
~Private();
QString query;
void init();
private:
void slotSearchTextChanged(const QString &str);
void slotNextKey(const Key &key)
{
keyListing.keys.push_back(key);
}
void slotKeyListResult(const KeyListResult &result);
void slotImportRequested(const std::vector<Key> &keys);
void slotDetailsRequested(const Key &key);
void slotSaveAsRequested(const std::vector<Key> &keys);
void slotDialogRejected()
{
canceled();
}
private:
using ImportCertificatesCommand::Private::showError;
void showError(QWidget *parent, const KeyListResult &result);
void showResult(QWidget *parent, const KeyListResult &result);
void createDialog();
KeyListJob *createKeyListJob(GpgME::Protocol proto) const
{
const auto cbp = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
return cbp ? cbp->keyListJob(true) : nullptr;
}
ImportFromKeyserverJob *createImportJob(GpgME::Protocol proto) const
{
const auto cbp = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
return cbp ? cbp->importFromKeyserverJob() : nullptr;
}
void startKeyListJob(GpgME::Protocol proto, const QString &str);
bool checkConfig() const;
QWidget *dialogOrParentWidgetOrView() const
{
if (dialog) {
return dialog;
} else {
return parentWidgetOrView();
}
}
private:
QPointer<LookupCertificatesDialog> dialog;
struct KeyListingVariables {
QPointer<KeyListJob> cms, openpgp;
KeyListResult result;
std::vector<Key> keys;
void reset()
{
*this = KeyListingVariables();
}
} keyListing;
};
LookupCertificatesCommand::Private *LookupCertificatesCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const LookupCertificatesCommand::Private *LookupCertificatesCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
LookupCertificatesCommand::Private::Private(LookupCertificatesCommand *qq, KeyListController *c)
: ImportCertificatesCommand::Private(qq, c),
dialog()
{
}
LookupCertificatesCommand::Private::~Private()
{
qCDebug(KLEOPATRA_LOG);
delete dialog;
}
LookupCertificatesCommand::LookupCertificatesCommand(KeyListController *c)
: ImportCertificatesCommand(new Private(this, c))
{
d->init();
}
LookupCertificatesCommand::LookupCertificatesCommand(const QString &query, KeyListController *c)
: ImportCertificatesCommand(new Private(this, c))
{
d->init();
d->query = query;
}
LookupCertificatesCommand::LookupCertificatesCommand(QAbstractItemView *v, KeyListController *c)
: ImportCertificatesCommand(v, new Private(this, c))
{
d->init();
}
void LookupCertificatesCommand::Private::init()
{
}
LookupCertificatesCommand::~LookupCertificatesCommand()
{
qCDebug(KLEOPATRA_LOG);
}
void LookupCertificatesCommand::doStart()
{
if (!d->checkConfig()) {
d->finished();
return;
}
d->createDialog();
Q_ASSERT(d->dialog);
// if we have a prespecified query, load it into find field
// and start the search
if (!d->query.isEmpty()) {
d->dialog->setSearchText(d->query);
d->slotSearchTextChanged(d->query);
} else {
d->dialog->setPassive(false);
}
d->dialog->show();
}
void LookupCertificatesCommand::Private::createDialog()
{
if (dialog) {
return;
}
dialog = new LookupCertificatesDialog;
applyWindowID(dialog);
dialog->setAttribute(Qt::WA_DeleteOnClose);
connect(dialog, SIGNAL(searchTextChanged(QString)),
q, SLOT(slotSearchTextChanged(QString)));
connect(dialog, SIGNAL(saveAsRequested(std::vector<GpgME::Key>)),
q, SLOT(slotSaveAsRequested(std::vector<GpgME::Key>)));
connect(dialog, SIGNAL(importRequested(std::vector<GpgME::Key>)),
q, SLOT(slotImportRequested(std::vector<GpgME::Key>)));
connect(dialog, SIGNAL(detailsRequested(GpgME::Key)),
q, SLOT(slotDetailsRequested(GpgME::Key)));
connect(dialog, SIGNAL(rejected()),
q, SLOT(slotDialogRejected()));
}
void LookupCertificatesCommand::Private::slotSearchTextChanged(const QString &str)
{
// pressing return might trigger both search and dialog destruction (search focused and default key set)
// On Windows, the dialog is then destroyed before this slot is called
if (dialog) { //thus test
dialog->setPassive(true);
dialog->setCertificates(std::vector<Key>());
}
query = str;
startKeyListJob(CMS, str);
const QRegExp rx(QLatin1String("(?:0x|0X)?[0-9a-fA-F]{6,}"));
if (rx.exactMatch(query) && !str.startsWith(QLatin1String("0x"), Qt::CaseInsensitive)) {
qCDebug(KLEOPATRA_LOG) << "Adding 0x prefix to query";
startKeyListJob(OpenPGP, QStringLiteral("0x") + str);
} else {
startKeyListJob(OpenPGP, str);
}
}
void LookupCertificatesCommand::Private::startKeyListJob(GpgME::Protocol proto, const QString &str)
{
KeyListJob *const klj = createKeyListJob(proto);
if (!klj) {
return;
}
connect(klj, SIGNAL(result(GpgME::KeyListResult)),
q, SLOT(slotKeyListResult(GpgME::KeyListResult)));
connect(klj, SIGNAL(nextKey(GpgME::Key)),
q, SLOT(slotNextKey(GpgME::Key)));
if (const Error err = klj->start(QStringList(str))) {
keyListing.result.mergeWith(KeyListResult(err));
} else if (proto == CMS) {
keyListing.cms = klj;
} else {
keyListing.openpgp = klj;
}
}
void LookupCertificatesCommand::Private::slotKeyListResult(const KeyListResult &r)
{
if (q->sender() == keyListing.cms) {
keyListing.cms = nullptr;
} else if (q->sender() == keyListing.openpgp) {
keyListing.openpgp = nullptr;
} else {
qCDebug(KLEOPATRA_LOG) << "unknown sender()" << q->sender();
}
keyListing.result.mergeWith(r);
if (keyListing.cms || keyListing.openpgp) { // still waiting for jobs to complete
return;
}
if (keyListing.result.error() && !keyListing.result.error().isCanceled()) {
showError(dialog, keyListing.result);
}
if (keyListing.result.isTruncated()) {
showResult(dialog, keyListing.result);
}
if (dialog) {
dialog->setPassive(false);
dialog->setCertificates(keyListing.keys);
} else {
finished();
}
keyListing.reset();
}
void LookupCertificatesCommand::Private::slotImportRequested(const std::vector<Key> &keys)
{
dialog = nullptr;
Q_ASSERT(!keys.empty());
Q_ASSERT(std::none_of(keys.cbegin(), keys.cend(), [](const Key &key) { return key.isNull(); }));
std::vector<Key> pgp, cms;
pgp.reserve(keys.size());
cms.reserve(keys.size());
kdtools::separate_if(keys.begin(), keys.end(),
std::back_inserter(pgp),
std::back_inserter(cms),
[](const Key &key) {
return key.protocol() == GpgME::OpenPGP;
});
setWaitForMoreJobs(true);
if (!pgp.empty())
startImport(OpenPGP, pgp,
i18nc("@title %1:\"OpenPGP\" or \"CMS\"",
"%1 Certificate Server",
Formatting::displayName(OpenPGP)));
if (!cms.empty())
startImport(CMS, cms,
i18nc("@title %1:\"OpenPGP\" or \"CMS\"",
"%1 Certificate Server",
Formatting::displayName(CMS)));
setWaitForMoreJobs(false);
}
void LookupCertificatesCommand::Private::slotSaveAsRequested(const std::vector<Key> &keys)
{
Q_UNUSED(keys)
qCDebug(KLEOPATRA_LOG) << "not implemented";
}
void LookupCertificatesCommand::Private::slotDetailsRequested(const Key &key)
{
Command *const cmd = new DetailsCommand(key, view(), controller());
cmd->setParentWidget(dialogOrParentWidgetOrView());
cmd->start();
}
void LookupCertificatesCommand::doCancel()
{
ImportCertificatesCommand::doCancel();
if (QDialog *const dlg = d->dialog) {
d->dialog = nullptr;
dlg->close();
}
}
void LookupCertificatesCommand::Private::showError(QWidget *parent, const KeyListResult &result)
{
if (!result.error()) {
return;
}
KMessageBox::information(parent, i18nc("@info",
"Failed to search on certificate server. The error returned was:\n%1",
QString::fromLocal8Bit(result.error().asString())));
}
void LookupCertificatesCommand::Private::showResult(QWidget *parent, const KeyListResult &result)
{
if (result.isTruncated())
KMessageBox::information(parent,
xi18nc("@info",
"<para>The query result has been truncated.</para>"
"<para>Either the local or a remote limit on "
"the maximum number of returned hits has "
"been exceeded.</para>"
"<para>You can try to increase the local limit "
"in the configuration dialog, but if one "
"of the configured servers is the limiting "
"factor, you have to refine your search.</para>"),
i18nc("@title", "Result Truncated"),
QStringLiteral("lookup-certificates-truncated-result"));
}
static bool haveX509DirectoryServerConfigured()
{
const QGpgME::CryptoConfig *const config = QGpgME::cryptoConfig();
if (!config) {
return false;
}
- const QGpgME::CryptoConfigEntry *entry = config->entry(QStringLiteral("dirmngr"), QStringLiteral("LDAP"), QStringLiteral("LDAP Server"));
+ const QGpgME::CryptoConfigEntry *entry = getCryptoConfigEntry(config, "dirmngr", "LDAP Server");
bool entriesExist = entry && !entry->urlValueList().empty();
- entry = config->entry(QStringLiteral("gpgsm"), QStringLiteral("Configuration"), QStringLiteral("keyserver"));
+ entry = getCryptoConfigEntry(config, "gpgsm", "keyserver");
entriesExist |= entry && !entry->urlValueList().empty();
return entriesExist;
}
bool LookupCertificatesCommand::Private::checkConfig() const
{
const bool ok = haveKeyserverConfigured() || haveX509DirectoryServerConfigured();
if (!ok)
information(xi18nc("@info",
"<para>You do not have any directory servers configured.</para>"
"<para>You need to configure at least one directory server to "
"search on one.</para>"
"<para>You can configure directory servers here: "
"<interface>Settings->Configure Kleopatra</interface>.</para>"),
i18nc("@title", "No Directory Servers Configured"));
return ok;
}
#undef d
#undef q
#include "moc_lookupcertificatescommand.cpp"
diff --git a/src/conf/dirservconfigpage.cpp b/src/conf/dirservconfigpage.cpp
index 7e5d0cb10..d90cb0e17 100644
--- a/src/conf/dirservconfigpage.cpp
+++ b/src/conf/dirservconfigpage.cpp
@@ -1,417 +1,400 @@
/* -*- mode: c++; c-basic-offset:4 -*-
conf/dirservconfigpage.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2004, 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "dirservconfigpage.h"
+
+#include "compat.h"
+
+#include <Libkleo/Compat>
#include <Libkleo/DirectoryServicesWidget>
#include <Libkleo/CryptoConfigModule>
#include <QGpgME/Protocol>
#include <KMessageBox>
#include <KLocalizedString>
#include "kleopatra_debug.h"
#include <KConfig>
#include <QSpinBox>
#include <QLabel>
#include <QCheckBox>
#include <QLayout>
#include <QTimeEdit>
using namespace Kleo;
using namespace QGpgME;
#if 0 // disabled, since it is apparently confusing
// For sync'ing kabldaprc
class KABSynchronizer
{
public:
KABSynchronizer()
: mConfig("kabldaprc")
{
mConfig.setGroup("LDAP");
}
KUrl::List readCurrentList() const
{
KUrl::List lst;
// stolen from kabc/ldapclient.cpp
const uint numHosts = mConfig.readEntry("NumSelectedHosts");
for (uint j = 0; j < numHosts; j++) {
const QString num = QString::number(j);
KUrl url;
url.setProtocol("ldap");
url.setPath("/"); // workaround KUrl parsing bug
const QString host = mConfig.readEntry(QString("SelectedHost") + num).trimmed();
url.setHost(host);
const int port = mConfig.readEntry(QString("SelectedPort") + num);
if (port != 0) {
url.setPort(port);
}
const QString base = mConfig.readEntry(QString("SelectedBase") + num).trimmed();
url.setQuery(base);
const QString bindDN = mConfig.readEntry(QString("SelectedBind") + num).trimmed();
url.setUser(bindDN);
const QString pwdBindDN = mConfig.readEntry(QString("SelectedPwdBind") + num).trimmed();
url.setPass(pwdBindDN);
lst.append(url);
}
return lst;
}
void writeList(const KUrl::List &lst)
{
mConfig.writeEntry("NumSelectedHosts", lst.count());
KUrl::List::const_iterator it = lst.begin();
KUrl::List::const_iterator end = lst.end();
unsigned j = 0;
for (; it != end; ++it, ++j) {
const QString num = QString::number(j);
KUrl url = *it;
Q_ASSERT(url.scheme() == "ldap");
mConfig.writeEntry(QString("SelectedHost") + num, url.host());
mConfig.writeEntry(QString("SelectedPort") + num, url.port());
// KUrl automatically encoded the query (e.g. for spaces inside it),
// so decode it before writing it out
const QString base = KUrl::decode_string(url.query().mid(1));
mConfig.writeEntry(QString("SelectedBase") + num, base);
mConfig.writeEntry(QString("SelectedBind") + num, url.user());
mConfig.writeEntry(QString("SelectedPwdBind") + num, url.pass());
}
mConfig.sync();
}
private:
KConfig mConfig;
};
#endif
-static const char s_x509services_componentName[] = "dirmngr";
-static const char s_x509services_groupName[] = "LDAP";
-static const char s_x509services_entryName[] = "LDAP Server";
+static const char s_x509services_componentName[] = "gpgsm";
+static const char s_x509services_entryName[] = "keyserver";
-static const char s_x509services_new_componentName[] = "gpgsm";
-static const char s_x509services_new_groupName[] = "Configuration";
-static const char s_x509services_new_entryName[] = "keyserver";
+static const char s_x509services_legacy_componentName[] = "dirmngr";
+static const char s_x509services_legacy_entryName[] = "LDAP Server";
static const char s_pgpservice_componentName[] = "dirmngr";
-static const char s_pgpservice_groupName[] = "Keyserver";
static const char s_pgpservice_entryName[] = "keyserver";
// legacy config entry used until GnuPG 2.2
static const char s_pgpservice_legacy_componentName[] = "gpg";
-static const char s_pgpservice_legacy_groupName[] = "Keyserver";
static const char s_pgpservice_legacy_entryName[] = "keyserver";
static const char s_timeout_componentName[] = "dirmngr";
-static const char s_timeout_groupName[] = "LDAP";
static const char s_timeout_entryName[] = "ldaptimeout";
static const char s_maxitems_componentName[] = "dirmngr";
-static const char s_maxitems_groupName[] = "Configuration";
static const char s_maxitems_entryName[] = "max-replies";
-// legacy config entry used until GnuPG 2.2
-static const char s_maxitems_legacy_componentName[] = "dirmngr";
-static const char s_maxitems_legacy_groupName[] = "LDAP";
-static const char s_maxitems_legacy_entryName[] = "max-replies";
-
#ifdef NOT_USEFUL_CURRENTLY
static const char s_addnewservers_componentName[] = "dirmngr";
-static const char s_addnewservers_groupName[] = "LDAP";
static const char s_addnewservers_entryName[] = "add-servers";
#endif
DirectoryServicesConfigurationPage::DirectoryServicesConfigurationPage(QWidget *parent, const QVariantList &args)
: KCModule(parent, args)
{
mConfig = QGpgME::cryptoConfig();
QGridLayout *glay = new QGridLayout(this);
glay->setContentsMargins(0, 0, 0, 0);
int row = 0;
mWidget = new Kleo::DirectoryServicesWidget(this);
if (QLayout *l = mWidget->layout()) {
l->setContentsMargins(0, 0, 0, 0);
}
glay->addWidget(mWidget, row, 0, 1, 3);
connect(mWidget, SIGNAL(changed()), this, SLOT(changed()));
// LDAP timeout
++row;
QLabel *label = new QLabel(i18n("LDAP &timeout (minutes:seconds):"), this);
mTimeout = new QTimeEdit(this);
mTimeout->setDisplayFormat(QStringLiteral("mm:ss"));
connect(mTimeout, SIGNAL(timeChanged(QTime)), this, SLOT(changed()));
label->setBuddy(mTimeout);
glay->addWidget(label, row, 0);
glay->addWidget(mTimeout, row, 1);
// Max number of items returned by queries
++row;
mMaxItemsLabel = new QLabel(i18n("&Maximum number of items returned by query:"), this);
mMaxItems = new QSpinBox(this);
mMaxItems->setMinimum(0);
mMaxItemsLabel->setBuddy(mMaxItems);
connect(mMaxItems, SIGNAL(valueChanged(int)), this, SLOT(changed()));
glay->addWidget(mMaxItemsLabel, row, 0);
glay->addWidget(mMaxItems, row, 1);
#ifdef NOT_USEFUL_CURRENTLY
++row
mAddNewServersCB = new QCheckBox(i18n("Automatically add &new servers discovered in CRL distribution points"), this);
connect(mAddNewServersCB, SIGNAL(clicked()), this, SLOT(changed()));
glay->addWidget(mAddNewServersCB, row, 0, 1, 3);
#endif
glay->setRowStretch(++row, 1);
glay->setColumnStretch(2, 1);
load();
}
static QList<QUrl> string2urls(const QString &str)
{
QList<QUrl> ret;
if (str.isEmpty()) {
return ret;
}
ret << QUrl::fromEncoded(str.toLocal8Bit());
return ret;
}
void DirectoryServicesConfigurationPage::load()
{
-
mWidget->clear();
- // gpgsm/Configuration/keyserver is not provided by older gpgconf versions;
- if ((mX509ServicesEntry = configEntry(s_x509services_new_componentName, s_x509services_new_groupName, s_x509services_new_entryName,
+ // gpgsm's keyserver option is not provided by very old gpgconf versions
+ if ((mX509ServicesEntry = configEntry(s_x509services_componentName, s_x509services_entryName,
CryptoConfigEntry::ArgType_LDAPURL, ListValue, DoNotShowError))) {
mWidget->addX509Services(mX509ServicesEntry->urlValueList());
- } else if ((mX509ServicesEntry = configEntry(s_x509services_componentName, s_x509services_groupName, s_x509services_entryName,
- CryptoConfigEntry::ArgType_LDAPURL, ListValue, DoShowError))) {
+ } else if ((mX509ServicesEntry = configEntry(s_x509services_legacy_componentName, s_x509services_legacy_entryName,
+ CryptoConfigEntry::ArgType_LDAPURL, ListValue, DoShowError))) {
mWidget->addX509Services(mX509ServicesEntry->urlValueList());
}
-
mWidget->setX509ReadOnly(mX509ServicesEntry && mX509ServicesEntry->isReadOnly());
{
- auto *const newEntry = configEntry(s_pgpservice_componentName, s_pgpservice_groupName, s_pgpservice_entryName,
+ auto *const newEntry = configEntry(s_pgpservice_componentName, s_pgpservice_entryName,
CryptoConfigEntry::ArgType_String, SingleValue, DoNotShowError);
- auto *const legacyEntry = configEntry(s_pgpservice_legacy_componentName, s_pgpservice_legacy_groupName, s_pgpservice_legacy_entryName,
+ auto *const legacyEntry = configEntry(s_pgpservice_legacy_componentName, s_pgpservice_legacy_entryName,
CryptoConfigEntry::ArgType_String, SingleValue, DoNotShowError);
mOpenPGPServiceEntry = newEntry ? newEntry : legacyEntry;
QString stringValue;
if (newEntry && legacyEntry && !newEntry->isSet() && legacyEntry->isSet()) {
// use value of legacy entry if value of new entry is unset
qCDebug(KLEOPATRA_LOG) << "Using value of legacy entry for config entry"
- << s_pgpservice_componentName << "/" << s_pgpservice_groupName << "/" << s_pgpservice_entryName;
+ << s_pgpservice_componentName << "/" << s_pgpservice_entryName;
stringValue = legacyEntry->stringValue();
} else if (mOpenPGPServiceEntry) {
stringValue = mOpenPGPServiceEntry->stringValue();
} else {
qCWarning(KLEOPATRA_LOG) << "Unknown or wrong typed config entry"
- << s_pgpservice_componentName << "/" << s_pgpservice_groupName << "/" << s_pgpservice_entryName;
+ << s_pgpservice_componentName << "/" << s_pgpservice_entryName;
}
mWidget->addOpenPGPServices(string2urls(parseKeyserver(stringValue).url));
mWidget->setOpenPGPReadOnly(mOpenPGPServiceEntry && mOpenPGPServiceEntry->isReadOnly());
}
if (mX509ServicesEntry)
if (mOpenPGPServiceEntry) {
mWidget->setAllowedProtocols(DirectoryServicesWidget::AllProtocols);
} else {
mWidget->setAllowedProtocols(DirectoryServicesWidget::X509Protocol);
}
else if (mOpenPGPServiceEntry) {
mWidget->setAllowedProtocols(DirectoryServicesWidget::OpenPGPProtocol);
} else {
mWidget->setDisabled(true);
}
DirectoryServicesWidget::Protocols readOnlyProtocols;
if (mX509ServicesEntry && mX509ServicesEntry->isReadOnly()) {
readOnlyProtocols = DirectoryServicesWidget::X509Protocol;
}
// read LDAP timeout
// first try to read the config entry as int (GnuPG 2.3)
- mTimeoutConfigEntry = configEntry(s_timeout_componentName, s_timeout_groupName, s_timeout_entryName, CryptoConfigEntry::ArgType_Int, SingleValue, DoNotShowError);
+ mTimeoutConfigEntry = configEntry(s_timeout_componentName, s_timeout_entryName, CryptoConfigEntry::ArgType_Int, SingleValue, DoNotShowError);
if (!mTimeoutConfigEntry) {
// if this fails, then try to read the config entry as unsigned int (GnuPG <= 2.2)
- mTimeoutConfigEntry = configEntry(s_timeout_componentName, s_timeout_groupName, s_timeout_entryName, CryptoConfigEntry::ArgType_UInt, SingleValue, DoShowError);
+ mTimeoutConfigEntry = configEntry(s_timeout_componentName, s_timeout_entryName, CryptoConfigEntry::ArgType_UInt, SingleValue, DoShowError);
}
if (mTimeoutConfigEntry) {
const int ldapTimeout = mTimeoutConfigEntry->argType() == CryptoConfigEntry::ArgType_Int ?
mTimeoutConfigEntry->intValue() :
static_cast<int>(mTimeoutConfigEntry->uintValue());
const QTime time = QTime(0, 0, 0, 0).addSecs(ldapTimeout);
//qCDebug(KLEOPATRA_LOG) <<"timeout:" << mTimeoutConfigEntry->uintValue() <<" ->" << time;
mTimeout->setTime(time);
}
- {
- auto *const newEntry = configEntry(s_maxitems_componentName, s_maxitems_groupName, s_maxitems_entryName,
- CryptoConfigEntry::ArgType_Int, SingleValue, DoNotShowError);
- auto *const legacyEntry = configEntry(s_maxitems_legacy_componentName, s_maxitems_legacy_groupName, s_maxitems_legacy_entryName,
- CryptoConfigEntry::ArgType_UInt, SingleValue, DoNotShowError);
- mMaxItemsConfigEntry = newEntry ? newEntry : legacyEntry;
- if (!mMaxItemsConfigEntry) {
- qCWarning(KLEOPATRA_LOG) << "Unknown or wrong typed config entry"
- << s_maxitems_componentName << "/" << s_maxitems_groupName << "/" << s_maxitems_entryName;
- }
+ // read max-replies config entry
+ // first try to read the config entry as int (GnuPG 2.3)
+ mMaxItemsConfigEntry = configEntry(s_maxitems_componentName, s_maxitems_entryName, CryptoConfigEntry::ArgType_Int, SingleValue, DoNotShowError);
+ if (!mMaxItemsConfigEntry) {
+ // if this fails, then try to read the config entry as unsigned int (GnuPG <= 2.2)
+ mMaxItemsConfigEntry = configEntry(s_maxitems_componentName, s_maxitems_entryName, CryptoConfigEntry::ArgType_UInt, SingleValue, DoShowError);
}
if (mMaxItemsConfigEntry) {
const int value = mMaxItemsConfigEntry->argType() == CryptoConfigEntry::ArgType_Int ?
mMaxItemsConfigEntry->intValue() :
static_cast<int>(mMaxItemsConfigEntry->uintValue());
mMaxItems->blockSignals(true); // KNumInput emits valueChanged from setValue!
mMaxItems->setValue(value);
mMaxItems->blockSignals(false);
}
const bool maxItemsEnabled = mMaxItemsConfigEntry && !mMaxItemsConfigEntry->isReadOnly();
mMaxItems->setEnabled(maxItemsEnabled);
mMaxItemsLabel->setEnabled(maxItemsEnabled);
#ifdef NOT_USEFUL_CURRENTLY
mAddNewServersConfigEntry = configEntry(s_addnewservers_componentName, s_addnewservers_groupName, s_addnewservers_entryName, CryptoConfigEntry::ArgType_None, SingleValue, DoShowError);
if (mAddNewServersConfigEntry) {
mAddNewServersCB->setChecked(mAddNewServersConfigEntry->boolValue());
}
#endif
}
void DirectoryServicesConfigurationPage::save()
{
if (mX509ServicesEntry) {
mX509ServicesEntry->setURLValueList(mWidget->x509Services());
}
if (mOpenPGPServiceEntry) {
const QList<QUrl> serv = mWidget->openPGPServices();
if (serv.empty()) {
mOpenPGPServiceEntry->setStringValue(QString());
} else {
ParsedKeyserver pks = parseKeyserver(mOpenPGPServiceEntry->stringValue());
pks.url = serv.front().url();
mOpenPGPServiceEntry->setStringValue(assembleKeyserver(pks));
}
}
QTime time(mTimeout->time());
unsigned int timeout = time.minute() * 60 + time.second();
if (mTimeoutConfigEntry && mTimeoutConfigEntry->uintValue() != timeout) {
mTimeoutConfigEntry->setUIntValue(timeout);
}
if (mMaxItemsConfigEntry && mMaxItemsConfigEntry->uintValue() != (uint)mMaxItems->value()) {
mMaxItemsConfigEntry->setUIntValue(mMaxItems->value());
}
#ifdef NOT_USEFUL_CURRENTLY
if (mAddNewServersConfigEntry && mAddNewServersConfigEntry->boolValue() != mAddNewServersCB->isChecked()) {
mAddNewServersConfigEntry->setBoolValue(mAddNewServersCB->isChecked());
}
#endif
mConfig->sync(true);
#if 0
// Also write the LDAP URLs to kabldaprc so that they are used by kaddressbook
KABSynchronizer sync;
const KUrl::List toAdd = mWidget->urlList();
KUrl::List currentList = sync.readCurrentList();
KUrl::List::const_iterator it = toAdd.begin();
KUrl::List::const_iterator end = toAdd.end();
for (; it != end; ++it) {
// check if the URL is already in currentList
if (currentList.find(*it) == currentList.end())
// if not, add it
{
currentList.append(*it);
}
}
sync.writeList(currentList);
#endif
}
void DirectoryServicesConfigurationPage::defaults()
{
// these guys don't have a default, to clear them:
if (mX509ServicesEntry) {
mX509ServicesEntry->setURLValueList(QList<QUrl>());
}
if (mOpenPGPServiceEntry) {
mOpenPGPServiceEntry->setStringValue(QString());
}
// these presumably have a default, use that one:
if (mTimeoutConfigEntry) {
mTimeoutConfigEntry->resetToDefault();
}
if (mMaxItemsConfigEntry) {
mMaxItemsConfigEntry->resetToDefault();
}
#ifdef NOT_USEFUL_CURRENTLY
if (mAddNewServersConfigEntry) {
mAddNewServersConfigEntry->resetToDefault();
}
#endif
load();
}
extern "C"
{
Q_DECL_EXPORT KCModule *create_kleopatra_config_dirserv(QWidget *parent = nullptr, const QVariantList &args = QVariantList())
{
DirectoryServicesConfigurationPage *page =
new DirectoryServicesConfigurationPage(parent, args);
page->setObjectName(QStringLiteral("kleopatra_config_dirserv"));
return page;
}
}
// Find config entry for ldap servers. Implements runtime checks on the configuration option.
CryptoConfigEntry *DirectoryServicesConfigurationPage::configEntry(const char *componentName,
- const char *groupName,
const char *entryName,
CryptoConfigEntry::ArgType argType,
EntryMultiplicity multiplicity,
ShowError showError)
{
- CryptoConfigEntry *entry = mConfig->entry(QLatin1String(componentName), QLatin1String(groupName), QLatin1String(entryName));
-
+ CryptoConfigEntry *const entry = Kleo::getCryptoConfigEntry(mConfig, componentName, entryName);
if (!entry) {
if (showError == DoShowError) {
- KMessageBox::error(this, i18n("Backend error: gpgconf does not seem to know the entry for %1/%2/%3", QLatin1String(componentName), QLatin1String(groupName), QLatin1String(entryName)));
+ KMessageBox::error(this, i18n("Backend error: gpgconf does not seem to know the entry for %1/%2", QLatin1String(componentName), QLatin1String(entryName)));
}
return nullptr;
}
if (entry->argType() != argType || entry->isList() != bool(multiplicity)) {
if (showError == DoShowError) {
- KMessageBox::error(this, i18n("Backend error: gpgconf has wrong type for %1/%2/%3: %4 %5", QLatin1String(componentName), QLatin1String(groupName), QLatin1String(entryName), entry->argType(), entry->isList()));
+ KMessageBox::error(this, i18n("Backend error: gpgconf has wrong type for %1/%2: %3 %4", QLatin1String(componentName), QLatin1String(entryName), entry->argType(), entry->isList()));
}
return nullptr;
}
return entry;
}
-
diff --git a/src/conf/dirservconfigpage.h b/src/conf/dirservconfigpage.h
index 5736ef2a3..cfd0a06c0 100644
--- a/src/conf/dirservconfigpage.h
+++ b/src/conf/dirservconfigpage.h
@@ -1,73 +1,72 @@
/* -*- mode: c++; c-basic-offset:4 -*-
conf/dirservconfigpage.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2004, 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef DIRSERVCONFIGPAGE_H
#define DIRSERVCONFIGPAGE_H
#include <KCModule>
#include <QGpgME/CryptoConfig>
class QCheckBox;
class QLabel;
class QTimeEdit;
class QSpinBox;
namespace Kleo
{
class DirectoryServicesWidget;
}
/**
* "Directory Services" configuration page for kleopatra's configuration dialog
* The user can configure LDAP servers in this page, to be used for listing/fetching
* remote certificates in kleopatra.
*/
class DirectoryServicesConfigurationPage : public KCModule
{
Q_OBJECT
public:
explicit DirectoryServicesConfigurationPage(QWidget *parent = nullptr, const QVariantList &args = QVariantList());
void load() override;
void save() override;
void defaults() override;
private:
enum EntryMultiplicity {
SingleValue,
ListValue
};
enum ShowError {
DoNotShowError,
DoShowError
};
QGpgME::CryptoConfigEntry *configEntry(const char *componentName,
- const char *groupName,
const char *entryName,
QGpgME::CryptoConfigEntry::ArgType argType,
EntryMultiplicity multiplicity,
ShowError showError);
Kleo::DirectoryServicesWidget *mWidget;
QTimeEdit *mTimeout;
QSpinBox *mMaxItems;
QLabel *mMaxItemsLabel;
QCheckBox *mAddNewServersCB;
QGpgME::CryptoConfigEntry *mX509ServicesEntry;
QGpgME::CryptoConfigEntry *mOpenPGPServiceEntry;
QGpgME::CryptoConfigEntry *mTimeoutConfigEntry;
QGpgME::CryptoConfigEntry *mMaxItemsConfigEntry;
QGpgME::CryptoConfigEntry *mAddNewServersConfigEntry;
QGpgME::CryptoConfig *mConfig;
};
#endif
diff --git a/src/conf/smimevalidationconfigurationwidget.cpp b/src/conf/smimevalidationconfigurationwidget.cpp
index d4e52e956..2e2b36347 100644
--- a/src/conf/smimevalidationconfigurationwidget.cpp
+++ b/src/conf/smimevalidationconfigurationwidget.cpp
@@ -1,385 +1,384 @@
/* -*- mode: c++; c-basic-offset:4 -*-
conf/smimevalidationconfigurationwidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "smimevalidationconfigurationwidget.h"
#include "ui_smimevalidationconfigurationwidget.h"
#include "smimevalidationpreferences.h"
+#include <Libkleo/Compat>
+
#include <QGpgME/CryptoConfig>
#include <QGpgME/Protocol>
#include <KLocalizedString>
#include "kleopatra_debug.h"
#if HAVE_QDBUS
# include <QDBusConnection>
#endif
using namespace Kleo;
using namespace Kleo::Config;
using namespace QGpgME;
class SMimeValidationConfigurationWidget::Private
{
friend class ::Kleo::Config::SMimeValidationConfigurationWidget;
SMimeValidationConfigurationWidget *const q;
public:
explicit Private(SMimeValidationConfigurationWidget *qq)
: q(qq),
customHTTPProxyWritable(false),
ui(q)
{
#if HAVE_QDBUS
QDBusConnection::sessionBus().connect(QString(), QString(), QStringLiteral("org.kde.kleo.CryptoConfig"), QStringLiteral("changed"), q, SLOT(load()));
#endif
}
bool customHTTPProxyWritable;
private:
void enableDisableActions()
{
ui.customHTTPProxy->setEnabled(ui.useCustomHTTPProxyRB->isChecked() &&
!ui.disableHTTPCB->isChecked() &&
customHTTPProxyWritable);
}
private:
struct UI : Ui_SMimeValidationConfigurationWidget {
explicit UI(SMimeValidationConfigurationWidget *q)
: Ui_SMimeValidationConfigurationWidget()
{
setupUi(q);
if (QLayout *l = q->layout()) {
l->setContentsMargins(0, 0, 0, 0);
}
const struct {
QObject *object;
const char *signal;
} sources[] = {
{ intervalRefreshCB, SIGNAL(toggled(bool)) },
{ intervalRefreshSB, SIGNAL(valueChanged(int)) },
{ CRLRB, SIGNAL(toggled(bool)) },
{ OCSPRB, SIGNAL(toggled(bool)) },
{ OCSPResponderURL, SIGNAL(textChanged(QString)) },
{ OCSPResponderSignature, SIGNAL(selectedCertificatesChanged(QStringList)) },
{ doNotCheckCertPolicyCB, SIGNAL(toggled(bool)) },
{ neverConsultCB, SIGNAL(toggled(bool)) },
{ allowMarkTrustedCB, SIGNAL(toggled(bool)) },
{ fetchMissingCB, SIGNAL(toggled(bool)) },
{ ignoreServiceURLCB, SIGNAL(toggled(bool)) },
{ ignoreHTTPDPCB, SIGNAL(toggled(bool)) },
{ disableHTTPCB, SIGNAL(toggled(bool)) },
{ honorHTTPProxyRB, SIGNAL(toggled(bool)) },
{ useCustomHTTPProxyRB, SIGNAL(toggled(bool)) },
{ customHTTPProxy, SIGNAL(textChanged(QString)) },
{ ignoreLDAPDPCB, SIGNAL(toggled(bool)) },
{ disableLDAPCB, SIGNAL(toggled(bool)) },
{ customLDAPProxy, SIGNAL(textChanged(QString)) },
};
for (unsigned int i = 0; i < sizeof sources / sizeof * sources; ++i) {
connect(sources[i].object, sources[i].signal, q, SIGNAL(changed()));
}
connect(useCustomHTTPProxyRB, SIGNAL(toggled(bool)),
q, SLOT(enableDisableActions()));
connect(disableHTTPCB, SIGNAL(toggled(bool)),
q, SLOT(enableDisableActions()));
OCSPResponderSignature->setOnlyX509CertificatesAllowed(true);
OCSPResponderSignature->setOnlySigningCertificatesAllowed(true);
OCSPResponderSignature->setMultipleCertificatesAllowed(false);
//OCSPResponderSignature->setAllowedKeys( KeySelectionDialog::TrustedKeys|KeySelectionDialog::ValidKeys );
}
} ui;
};
SMimeValidationConfigurationWidget::SMimeValidationConfigurationWidget(QWidget *p, Qt::WindowFlags f)
: QWidget(p, f), d(new Private(this))
{
}
SMimeValidationConfigurationWidget::~SMimeValidationConfigurationWidget() {}
static void disableDirmngrWidget(QWidget *w)
{
w->setEnabled(false);
w->setWhatsThis(i18n("This option requires dirmngr >= 0.9.0"));
}
static void initializeDirmngrCheckbox(QCheckBox *cb, CryptoConfigEntry *entry)
{
if (entry) {
cb->setChecked(entry->boolValue());
}
if (!entry || entry->isReadOnly()) {
disableDirmngrWidget(cb);
}
}
struct SMIMECryptoConfigEntries {
SMIMECryptoConfigEntries(CryptoConfig *config)
: mConfig(config),
// Checkboxes
- mCheckUsingOCSPConfigEntry(configEntry("gpgsm", "Security", "enable-ocsp", CryptoConfigEntry::ArgType_None, false)),
- mEnableOCSPsendingConfigEntry(configEntry("dirmngr", "OCSP", "allow-ocsp", CryptoConfigEntry::ArgType_None, false)),
- mDoNotCheckCertPolicyConfigEntry(configEntry("gpgsm", "Security", "disable-policy-checks", CryptoConfigEntry::ArgType_None, false)),
- mNeverConsultConfigEntry(configEntry("gpgsm", "Security", "disable-crl-checks", CryptoConfigEntry::ArgType_None, false)),
- mAllowMarkTrustedConfigEntry(configEntry("gpg-agent", "Security", "allow-mark-trusted", CryptoConfigEntry::ArgType_None, false)),
- mFetchMissingConfigEntry(configEntry("gpgsm", "Security", "auto-issuer-key-retrieve", CryptoConfigEntry::ArgType_None, false)),
- mNoAllowMarkTrustedConfigEntry(configEntry("gpg-agent", "Security", "no-allow-mark-trusted", CryptoConfigEntry::ArgType_None, false)),
+ mCheckUsingOCSPConfigEntry(configEntry("gpgsm", "enable-ocsp", CryptoConfigEntry::ArgType_None, false)),
+ mEnableOCSPsendingConfigEntry(configEntry("dirmngr", "allow-ocsp", CryptoConfigEntry::ArgType_None, false)),
+ mDoNotCheckCertPolicyConfigEntry(configEntry("gpgsm", "disable-policy-checks", CryptoConfigEntry::ArgType_None, false)),
+ mNeverConsultConfigEntry(configEntry("gpgsm", "disable-crl-checks", CryptoConfigEntry::ArgType_None, false)),
+ mAllowMarkTrustedConfigEntry(configEntry("gpg-agent", "allow-mark-trusted", CryptoConfigEntry::ArgType_None, false)),
+ mFetchMissingConfigEntry(configEntry("gpgsm", "auto-issuer-key-retrieve", CryptoConfigEntry::ArgType_None, false)),
+ mNoAllowMarkTrustedConfigEntry(configEntry("gpg-agent", "no-allow-mark-trusted", CryptoConfigEntry::ArgType_None, false)),
// dirmngr-0.9.0 options
- mIgnoreServiceURLEntry(configEntry("dirmngr", "OCSP", "ignore-ocsp-service-url", CryptoConfigEntry::ArgType_None, false)),
- mIgnoreHTTPDPEntry(configEntry("dirmngr", "HTTP", "ignore-http-dp", CryptoConfigEntry::ArgType_None, false)),
- mDisableHTTPEntry(configEntry("dirmngr", "HTTP", "disable-http", CryptoConfigEntry::ArgType_None, false)),
- mHonorHTTPProxy(configEntry("dirmngr", "HTTP", "honor-http-proxy", CryptoConfigEntry::ArgType_None, false)),
- mIgnoreLDAPDPEntry(configEntry("dirmngr", "LDAP", "ignore-ldap-dp", CryptoConfigEntry::ArgType_None, false)),
- mDisableLDAPEntry(configEntry("dirmngr", "LDAP", "disable-ldap", CryptoConfigEntry::ArgType_None, false)),
+ mIgnoreServiceURLEntry(configEntry("dirmngr", "ignore-ocsp-service-url", CryptoConfigEntry::ArgType_None, false)),
+ mIgnoreHTTPDPEntry(configEntry("dirmngr", "ignore-http-dp", CryptoConfigEntry::ArgType_None, false)),
+ mDisableHTTPEntry(configEntry("dirmngr", "disable-http", CryptoConfigEntry::ArgType_None, false)),
+ mHonorHTTPProxy(configEntry("dirmngr", "honor-http-proxy", CryptoConfigEntry::ArgType_None, false)),
+ mIgnoreLDAPDPEntry(configEntry("dirmngr", "ignore-ldap-dp", CryptoConfigEntry::ArgType_None, false)),
+ mDisableLDAPEntry(configEntry("dirmngr", "disable-ldap", CryptoConfigEntry::ArgType_None, false)),
// Other widgets
- mOCSPResponderURLConfigEntry(configEntry("dirmngr", "OCSP", "ocsp-responder", CryptoConfigEntry::ArgType_String, false)),
- mOCSPResponderSignature(configEntry("dirmngr", "OCSP", "ocsp-signer", CryptoConfigEntry::ArgType_String, false)),
- mCustomHTTPProxy(configEntry("dirmngr", "HTTP", "http-proxy", CryptoConfigEntry::ArgType_String, false)),
- mCustomLDAPProxy(configEntry("dirmngr", "LDAP", "ldap-proxy", CryptoConfigEntry::ArgType_String, false))
+ mOCSPResponderURLConfigEntry(configEntry("dirmngr", "ocsp-responder", CryptoConfigEntry::ArgType_String, false)),
+ mOCSPResponderSignature(configEntry("dirmngr", "ocsp-signer", CryptoConfigEntry::ArgType_String, false)),
+ mCustomHTTPProxy(configEntry("dirmngr", "http-proxy", CryptoConfigEntry::ArgType_String, false)),
+ mCustomLDAPProxy(configEntry("dirmngr", "ldap-proxy", CryptoConfigEntry::ArgType_String, false))
{
-
}
CryptoConfigEntry *configEntry(const char *componentName,
- const char *groupName,
const char *entryName,
int argType,
bool isList);
CryptoConfig *const mConfig;
// Checkboxes
CryptoConfigEntry *const mCheckUsingOCSPConfigEntry;
CryptoConfigEntry *const mEnableOCSPsendingConfigEntry;
CryptoConfigEntry *const mDoNotCheckCertPolicyConfigEntry;
CryptoConfigEntry *const mNeverConsultConfigEntry;
CryptoConfigEntry *const mAllowMarkTrustedConfigEntry;
CryptoConfigEntry *const mFetchMissingConfigEntry;
// gnupg 2.0.17+ option that should inhibit allow-mark-trusted display
CryptoConfigEntry *const mNoAllowMarkTrustedConfigEntry;
// dirmngr-0.9.0 options
CryptoConfigEntry *const mIgnoreServiceURLEntry;
CryptoConfigEntry *const mIgnoreHTTPDPEntry;
CryptoConfigEntry *const mDisableHTTPEntry;
CryptoConfigEntry *const mHonorHTTPProxy;
CryptoConfigEntry *const mIgnoreLDAPDPEntry;
CryptoConfigEntry *const mDisableLDAPEntry;
// Other widgets
CryptoConfigEntry *const mOCSPResponderURLConfigEntry;
CryptoConfigEntry *const mOCSPResponderSignature;
CryptoConfigEntry *const mCustomHTTPProxy;
CryptoConfigEntry *const mCustomLDAPProxy;
};
void SMimeValidationConfigurationWidget::defaults()
{
qCDebug(KLEOPATRA_LOG) << "not implemented";
}
void SMimeValidationConfigurationWidget::load()
{
const SMimeValidationPreferences preferences;
const unsigned int refreshInterval = preferences.refreshInterval();
d->ui.intervalRefreshCB->setChecked(refreshInterval > 0);
d->ui.intervalRefreshSB->setValue(refreshInterval);
CryptoConfig *const config = QGpgME::cryptoConfig();
if (!config) {
setEnabled(false);
return;
}
#if 0
// crashes other pages' save() by nuking the CryptoConfigEntries under their feet.
// This was probably not a problem in KMail, where this code comes
// from. But here, it's fatal.
// Force re-parsing gpgconf data, in case e.g. kleopatra or "configure backend" was used
// (which ends up calling us via D-Bus)
config->clear();
#endif
// Create config entries
// Don't keep them around, they'll get deleted by clear(), which could be
// done by the "configure backend" button even before we save().
const SMIMECryptoConfigEntries e(config);
// Initialize GUI items from the config entries
if (e.mCheckUsingOCSPConfigEntry) {
const bool b = e.mCheckUsingOCSPConfigEntry->boolValue();
d->ui.OCSPRB->setChecked(b);
d->ui.CRLRB->setChecked(!b);
d->ui.OCSPGroupBox->setEnabled(b);
} else {
d->ui.OCSPGroupBox->setEnabled(false);
}
if (e.mDoNotCheckCertPolicyConfigEntry) {
d->ui.doNotCheckCertPolicyCB->setChecked(e.mDoNotCheckCertPolicyConfigEntry->boolValue());
}
if (e.mNeverConsultConfigEntry) {
d->ui.neverConsultCB->setChecked(e.mNeverConsultConfigEntry->boolValue());
}
if (e.mNoAllowMarkTrustedConfigEntry) {
d->ui.allowMarkTrustedCB->hide(); // this option was only here to _enable_ allow-mark-trusted, and makes no sense if it's already default on
}
if (e.mAllowMarkTrustedConfigEntry) {
d->ui.allowMarkTrustedCB->setChecked(e.mAllowMarkTrustedConfigEntry->boolValue());
}
if (e.mFetchMissingConfigEntry) {
d->ui.fetchMissingCB->setChecked(e.mFetchMissingConfigEntry->boolValue());
}
if (e.mOCSPResponderURLConfigEntry) {
d->ui.OCSPResponderURL->setText(e.mOCSPResponderURLConfigEntry->stringValue());
}
if (e.mOCSPResponderSignature) {
d->ui.OCSPResponderSignature->setSelectedCertificate(e.mOCSPResponderSignature->stringValue());
}
// dirmngr-0.9.0 options
initializeDirmngrCheckbox(d->ui.ignoreServiceURLCB, e.mIgnoreServiceURLEntry);
initializeDirmngrCheckbox(d->ui.ignoreHTTPDPCB, e.mIgnoreHTTPDPEntry);
initializeDirmngrCheckbox(d->ui.disableHTTPCB, e.mDisableHTTPEntry);
initializeDirmngrCheckbox(d->ui.ignoreLDAPDPCB, e.mIgnoreLDAPDPEntry);
initializeDirmngrCheckbox(d->ui.disableLDAPCB, e.mDisableLDAPEntry);
if (e.mCustomHTTPProxy) {
QString systemProxy = QString::fromLocal8Bit(qgetenv("http_proxy"));
if (systemProxy.isEmpty()) {
systemProxy = i18n("no proxy");
}
d->ui.systemHTTPProxy->setText(i18n("(Current system setting: %1)", systemProxy));
const bool honor = e.mHonorHTTPProxy && e.mHonorHTTPProxy->boolValue();
d->ui.honorHTTPProxyRB->setChecked(honor);
d->ui.useCustomHTTPProxyRB->setChecked(!honor);
d->ui.customHTTPProxy->setText(e.mCustomHTTPProxy->stringValue());
}
d->customHTTPProxyWritable = e.mCustomHTTPProxy && !e.mCustomHTTPProxy->isReadOnly();
if (!d->customHTTPProxyWritable) {
disableDirmngrWidget(d->ui.honorHTTPProxyRB);
disableDirmngrWidget(d->ui.useCustomHTTPProxyRB);
disableDirmngrWidget(d->ui.systemHTTPProxy);
disableDirmngrWidget(d->ui.customHTTPProxy);
}
if (e.mCustomLDAPProxy) {
d->ui.customLDAPProxy->setText(e.mCustomLDAPProxy->stringValue());
}
if (!e.mCustomLDAPProxy || e.mCustomLDAPProxy->isReadOnly()) {
disableDirmngrWidget(d->ui.customLDAPProxy);
disableDirmngrWidget(d->ui.customLDAPLabel);
}
d->enableDisableActions();
}
static void saveCheckBoxToKleoEntry(QCheckBox *cb, CryptoConfigEntry *entry)
{
const bool b = cb->isChecked();
if (entry && entry->boolValue() != b) {
entry->setBoolValue(b);
}
}
void SMimeValidationConfigurationWidget::save() const
{
CryptoConfig *const config = QGpgME::cryptoConfig();
if (!config) {
return;
}
{
SMimeValidationPreferences preferences;
preferences.setRefreshInterval(d->ui.intervalRefreshCB->isChecked() ? d->ui.intervalRefreshSB->value() : 0);
preferences.save();
}
// Create config entries
// Don't keep them around, they'll get deleted by clear(), which could be done by the
// "configure backend" button.
const SMIMECryptoConfigEntries e(config);
const bool b = d->ui.OCSPRB->isChecked();
if (e.mCheckUsingOCSPConfigEntry && e.mCheckUsingOCSPConfigEntry->boolValue() != b) {
e.mCheckUsingOCSPConfigEntry->setBoolValue(b);
}
// Set allow-ocsp together with enable-ocsp
if (e.mEnableOCSPsendingConfigEntry && e.mEnableOCSPsendingConfigEntry->boolValue() != b) {
e.mEnableOCSPsendingConfigEntry->setBoolValue(b);
}
saveCheckBoxToKleoEntry(d->ui.doNotCheckCertPolicyCB, e.mDoNotCheckCertPolicyConfigEntry);
saveCheckBoxToKleoEntry(d->ui.neverConsultCB, e.mNeverConsultConfigEntry);
saveCheckBoxToKleoEntry(d->ui.allowMarkTrustedCB, e.mAllowMarkTrustedConfigEntry);
saveCheckBoxToKleoEntry(d->ui.fetchMissingCB, e.mFetchMissingConfigEntry);
QString txt = d->ui.OCSPResponderURL->text();
if (e.mOCSPResponderURLConfigEntry && e.mOCSPResponderURLConfigEntry->stringValue() != txt) {
e.mOCSPResponderURLConfigEntry->setStringValue(txt);
}
txt = d->ui.OCSPResponderSignature->selectedCertificate();
if (e.mOCSPResponderSignature && e.mOCSPResponderSignature->stringValue() != txt) {
e.mOCSPResponderSignature->setStringValue(txt);
}
//dirmngr-0.9.0 options
saveCheckBoxToKleoEntry(d->ui.ignoreServiceURLCB, e.mIgnoreServiceURLEntry);
saveCheckBoxToKleoEntry(d->ui.ignoreHTTPDPCB, e.mIgnoreHTTPDPEntry);
saveCheckBoxToKleoEntry(d->ui.disableHTTPCB, e.mDisableHTTPEntry);
saveCheckBoxToKleoEntry(d->ui.ignoreLDAPDPCB, e.mIgnoreLDAPDPEntry);
saveCheckBoxToKleoEntry(d->ui.disableLDAPCB, e.mDisableLDAPEntry);
if (e.mCustomHTTPProxy) {
const bool honor = d->ui.honorHTTPProxyRB->isChecked();
if (e.mHonorHTTPProxy && e.mHonorHTTPProxy->boolValue() != honor) {
e.mHonorHTTPProxy->setBoolValue(honor);
}
const QString chosenProxy = d->ui.customHTTPProxy->text();
if (chosenProxy != e.mCustomHTTPProxy->stringValue()) {
e.mCustomHTTPProxy->setStringValue(chosenProxy);
}
}
txt = d->ui.customLDAPProxy->text();
if (e.mCustomLDAPProxy && e.mCustomLDAPProxy->stringValue() != txt) {
e.mCustomLDAPProxy->setStringValue(d->ui.customLDAPProxy->text());
}
config->sync(true);
}
CryptoConfigEntry *SMIMECryptoConfigEntries::configEntry(const char *componentName,
- const char *groupName,
const char *entryName,
int /*CryptoConfigEntry::ArgType*/ argType,
bool isList)
{
- CryptoConfigEntry *const entry = mConfig->entry(QLatin1String(componentName), QLatin1String(groupName), QLatin1String(entryName));
+ CryptoConfigEntry *const entry = getCryptoConfigEntry(mConfig, componentName, entryName);
if (!entry) {
- qCWarning(KLEOPATRA_LOG) << QStringLiteral("Backend error: gpgconf doesn't seem to know the entry for %1/%2/%3").arg(QLatin1String(componentName), QLatin1String(groupName), QLatin1String(entryName));
+ qCWarning(KLEOPATRA_LOG) << QStringLiteral("Backend error: gpgconf doesn't seem to know the entry for %1/%2").arg(QLatin1String(componentName), QLatin1String(entryName));
return nullptr;
}
if (entry->argType() != argType || entry->isList() != isList) {
- qCWarning(KLEOPATRA_LOG) << QStringLiteral("Backend error: gpgconf has wrong type for %1/%2/%3: %4 %5").arg(QLatin1String(componentName), QLatin1String(groupName), QLatin1String(entryName)).arg(entry->argType()).arg(entry->isList());
+ qCWarning(KLEOPATRA_LOG) << QStringLiteral("Backend error: gpgconf has wrong type for %1/%2: %3 %4").arg(QLatin1String(componentName), QLatin1String(entryName)).arg(entry->argType()).arg(entry->isList());
return nullptr;
}
return entry;
}
#include "moc_smimevalidationconfigurationwidget.cpp"
diff --git a/src/dialogs/updatenotification.cpp b/src/dialogs/updatenotification.cpp
index cec4de693..6de7cc4b4 100644
--- a/src/dialogs/updatenotification.cpp
+++ b/src/dialogs/updatenotification.cpp
@@ -1,221 +1,220 @@
/* dialogs/updatenotification.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "updatenotification.h"
+#include <Libkleo/Compat>
#include <Libkleo/GnuPG>
#include "kleopatra_debug.h"
#include <QIcon>
#include <QGridLayout>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QDesktopServices>
#include <QProcess>
#include <QProgressDialog>
#include <QPushButton>
#include <QLabel>
#include <QUrl>
#include <QDateTime>
#include <KIconLoader>
#include <KLocalizedString>
#include <KConfigGroup>
#include <KSharedConfig>
#include <KMessageBox>
#include <QGpgME/CryptoConfig>
#include <QGpgME/Protocol>
#include <gpgme++/gpgmefw.h>
#include <gpgme++/swdbresult.h>
#include <gpgme++/error.h>
using namespace Kleo;
namespace
{
static void gpgconf_set_update_check(bool value)
{
auto conf = QGpgME::cryptoConfig();
- auto entry = conf->entry(QStringLiteral("dirmngr"),
- QStringLiteral("Enforcement"),
- QStringLiteral("allow-version-check"));
+ auto entry = getCryptoConfigEntry(conf, "dirmngr", "allow-version-check");
if (!entry) {
qCDebug(KLEOPATRA_LOG) << "allow-version-check entry not found";
return;
}
if (entry->boolValue() != value) {
entry->setBoolValue(value);
conf->sync(true);
}
}
} // namespace
void UpdateNotification::forceUpdateCheck(QWidget *parent)
{
auto proc = new QProcess;
proc->setProgram(gnupgInstallPath() + QStringLiteral("/gpg-connect-agent.exe"));
proc->setArguments(QStringList() << QStringLiteral("--dirmngr")
<< QStringLiteral("loadswdb --force")
<< QStringLiteral("/bye"));
auto progress = new QProgressDialog(i18n("Searching for updates..."),
i18n("Cancel"), 0, 0, parent);
progress->setMinimumDuration(0);
progress->show();
connect(progress, &QProgressDialog::canceled, [ proc] () {
proc->kill();
qCDebug(KLEOPATRA_LOG) << "Update force canceled. Output:"
<< QString::fromLocal8Bit(proc->readAllStandardOutput())
<< "stderr:"
<< QString::fromLocal8Bit(proc->readAllStandardError());
});
connect(proc, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
[parent, progress, proc](int exitCode, QProcess::ExitStatus exitStatus) {
qCDebug(KLEOPATRA_LOG) << "Update force exited with status:" << exitStatus
<< "code:" << exitCode;
delete progress;
proc->deleteLater();
UpdateNotification::checkUpdate(parent, exitStatus == QProcess::NormalExit);
});
qCDebug(KLEOPATRA_LOG) << "Starting:" << proc->program() << "args" << proc->arguments();
proc->start();
}
void UpdateNotification::checkUpdate(QWidget *parent, bool force)
{
#ifdef Q_OS_WIN
KConfigGroup updatecfg(KSharedConfig::openConfig(), "UpdateNotification");
if (updatecfg.readEntry("NeverShow", false) && !force) {
return;
}
// Gpg defaults to no update check. For Gpg4win we want this
// enabled if the user does not explicitly disable update
// checks neverShow would be true in that case or
// we would have set AllowVersionCheck once and the user
// explicitly removed that.
if (force || updatecfg.readEntry("AllowVersionCheckSetOnce", false)) {
gpgconf_set_update_check (true);
updatecfg.writeEntry("AllowVersionCheckSetOnce", true);
}
const auto current = gpg4winVersion();
GpgME::Error err;
const auto lastshown = updatecfg.readEntry("LastShown", QDateTime());
if (!force && lastshown.isValid() &&
lastshown.addSecs(20 * 60 * 60) > QDateTime::currentDateTime()) {
qDebug() << QDateTime::currentDateTime().addSecs(20 * 60 * 60);
return;
}
const auto results = GpgME::SwdbResult::query("gpg4win",
current.toUtf8().constData(),
&err);
if (err) {
qCDebug(KLEOPATRA_LOG) << "update check failed: " << err.asString();
return;
}
if (results.size() != 1) {
/* Should not happen */
qCDebug(KLEOPATRA_LOG) << "more then one result";
return;
}
const auto result = results[0];
if (result.update()) {
const QString newVersion = QStringLiteral("%1.%2.%3").arg(result.version().major)
.arg(result.version().minor)
.arg(result.version().patch);
qCDebug(KLEOPATRA_LOG) << "Have update to version:" << newVersion;
UpdateNotification notifier(parent, newVersion);
notifier.exec();
updatecfg.writeEntry("LastShown", QDateTime::currentDateTime());
updatecfg.sync();
} else {
qCDebug(KLEOPATRA_LOG) << "No update for:" << current;
if (force) {
KMessageBox::information(parent,
i18nc("@info",
"No update found in the available version database."),
i18nc("@title", "Up to date"));
}
}
#else
Q_UNUSED(parent)
Q_UNUSED(force)
#endif
}
UpdateNotification::UpdateNotification(QWidget *parent, const QString &version) :
QDialog(parent)
{
resize(400, 200);
auto lay = new QGridLayout(this);
auto logo = new QLabel;
logo->setMaximumWidth(110);
setAttribute(Qt::WA_QuitOnClose, false);
KIconLoader *const il = KIconLoader::global();
const QString iconPath = il->iconPath(QStringLiteral("gpg4win"),
KIconLoader::User);
logo->setPixmap(QIcon(iconPath).pixmap(100, 100));
auto label = new QLabel;
const QString boldVersion = QStringLiteral("<b>%1</b>").arg(version);
label->setText (i18nc("%1 is the version number", "Version %1 is available.", boldVersion) +
QStringLiteral("<br><br>") +
i18nc("Link to NEWS style changelog",
"See the <a href=\"https://www.gpg4win.org/change-history.html\">new features</a>."));
label->setOpenExternalLinks(true);
label->setTextInteractionFlags(Qt::TextBrowserInteraction);
label->setWordWrap(true);
setWindowTitle(i18nc("@title:window", "Update Available"));
setWindowIcon(QIcon(QLatin1String("gpg4win")));
lay->addWidget(logo, 0, 0);
lay->addWidget(label, 0, 1);
const auto chk = new QCheckBox (i18n("Show this notification for future updates."));
lay->addWidget(chk, 1, 0, 1, -1);
KConfigGroup updatecfg(KSharedConfig::openConfig(), "UpdateNotification");
chk->setChecked(!updatecfg.readEntry("NeverShow", false));
const auto bb = new QDialogButtonBox();
const auto b = bb->addButton(i18n("&Get update"), QDialogButtonBox::AcceptRole);
b->setDefault(true);
b->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down")));
bb->addButton(QDialogButtonBox::Cancel);
lay->addWidget(bb, 2, 0, 1, -1);
connect (bb, &QDialogButtonBox::accepted, this, [this, chk]() {
QDesktopServices::openUrl(QUrl(QStringLiteral("https://www.gpg4win.org/download.html")));
KConfigGroup updatecfg(KSharedConfig::openConfig(), "UpdateNotification");
updatecfg.writeEntry("NeverShow", !chk->isChecked());
gpgconf_set_update_check (chk->isChecked());
QDialog::accept();
});
connect (bb, &QDialogButtonBox::rejected, this, [this, chk]() {
KConfigGroup updatecfg(KSharedConfig::openConfig(), "UpdateNotification");
updatecfg.writeEntry("NeverShow", !chk->isChecked());
gpgconf_set_update_check (chk->isChecked());
QDialog::reject();
});
}
diff --git a/src/newcertificatewizard/newcertificatewizard.cpp b/src/newcertificatewizard/newcertificatewizard.cpp
index fe7c24063..354b8a88a 100644
--- a/src/newcertificatewizard/newcertificatewizard.cpp
+++ b/src/newcertificatewizard/newcertificatewizard.cpp
@@ -1,1913 +1,1912 @@
/* -*- mode: c++; c-basic-offset:4 -*-
newcertificatewizard/newcertificatewizard.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016, 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 "newcertificatewizard.h"
#include "ui_chooseprotocolpage.h"
#include "ui_enterdetailspage.h"
#include "ui_keycreationpage.h"
#include "ui_resultpage.h"
#include "ui_advancedsettingsdialog.h"
#include "commands/exportsecretkeycommand.h"
#include "commands/exportopenpgpcertstoservercommand.h"
#include "commands/exportcertificatecommand.h"
#include "kleopatraapplication.h"
#include "utils/validation.h"
#include "utils/filedialog.h"
#include "utils/keyparameters.h"
#include "utils/userinfo.h"
+#include <Libkleo/Compat>
#include <Libkleo/GnuPG>
#include <Libkleo/Stl_Util>
#include <Libkleo/Dn>
#include <Libkleo/OidMap>
#include <Libkleo/KeyCache>
#include <Libkleo/Formatting>
#include <QGpgME/KeyGenerationJob>
#include <QGpgME/Protocol>
#include <QGpgME/CryptoConfig>
#include <gpgme++/global.h>
#include <gpgme++/keygenerationresult.h>
#include <gpgme++/context.h>
#include <gpgme++/interfaces/passphraseprovider.h>
#include <KConfigGroup>
#include <KLocalizedString>
#include "kleopatra_debug.h"
#include <QTemporaryDir>
#include <KMessageBox>
#include <QIcon>
#include <QRegExpValidator>
#include <QLineEdit>
#include <QMetaProperty>
#include <QDir>
#include <QFile>
#include <QUrl>
#include <QDesktopServices>
#include <QUrlQuery>
#include <algorithm>
#include <KSharedConfig>
#include <QLocale>
using namespace Kleo;
using namespace Kleo::NewCertificateUi;
using namespace Kleo::Commands;
using namespace GpgME;
static const char RSA_KEYSIZES_ENTRY[] = "RSAKeySizes";
static const char DSA_KEYSIZES_ENTRY[] = "DSAKeySizes";
static const char ELG_KEYSIZES_ENTRY[] = "ELGKeySizes";
static const char RSA_KEYSIZE_LABELS_ENTRY[] = "RSAKeySizeLabels";
static const char DSA_KEYSIZE_LABELS_ENTRY[] = "DSAKeySizeLabels";
static const char ELG_KEYSIZE_LABELS_ENTRY[] = "ELGKeySizeLabels";
static const char PGP_KEY_TYPE_ENTRY[] = "PGPKeyType";
static const char CMS_KEY_TYPE_ENTRY[] = "CMSKeyType";
// This should come from gpgme in the future
// For now we only support the basic 2.1 curves and check
// for GnuPG 2.1. The whole subkey / usage generation needs
// new api and a reworked dialog. (ah 10.3.16)
// EDDSA should be supported, too.
static const QStringList curveNames {
{ QStringLiteral("brainpoolP256r1") },
{ QStringLiteral("brainpoolP384r1") },
{ QStringLiteral("brainpoolP512r1") },
{ QStringLiteral("NIST P-256") },
{ QStringLiteral("NIST P-384") },
{ QStringLiteral("NIST P-521") },
};
class EmptyPassphraseProvider: public PassphraseProvider
{
public:
char *getPassphrase(const char * /*useridHint*/, const char * /*description*/,
bool /*previousWasBad*/, bool &/*canceled*/) Q_DECL_OVERRIDE
{
return gpgrt_strdup ("");
}
};
static void set_tab_order(const QList<QWidget *> &wl)
{
kdtools::for_each_adjacent_pair(wl.begin(), wl.end(), &QWidget::setTabOrder);
}
enum KeyAlgo { RSA, DSA, ELG, ECDSA, ECDH, EDDSA };
static bool is_algo(Subkey::PubkeyAlgo algo, KeyAlgo what)
{
switch (algo) {
case Subkey::AlgoRSA:
case Subkey::AlgoRSA_E:
case Subkey::AlgoRSA_S:
return what == RSA;
case Subkey::AlgoELG_E:
case Subkey::AlgoELG:
return what == ELG;
case Subkey::AlgoDSA:
return what == DSA;
case Subkey::AlgoECDSA:
return what == ECDSA;
case Subkey::AlgoECDH:
return what == ECDH;
case Subkey::AlgoEDDSA:
return what == EDDSA;
default:
break;
}
return false;
}
static bool is_rsa(unsigned int algo)
{
return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), RSA);
}
static bool is_dsa(unsigned int algo)
{
return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), DSA);
}
static bool is_elg(unsigned int algo)
{
return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), ELG);
}
static bool is_ecdsa(unsigned int algo)
{
return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), ECDSA);
}
static bool is_eddsa(unsigned int algo)
{
return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), EDDSA);
}
static bool is_ecdh(unsigned int algo)
{
return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), ECDH);
}
static void force_set_checked(QAbstractButton *b, bool on)
{
// work around Qt bug (tested: 4.1.4, 4.2.3, 4.3.4)
const bool autoExclusive = b->autoExclusive();
b->setAutoExclusive(false);
b->setChecked(b->isEnabled() && on);
b->setAutoExclusive(autoExclusive);
}
static void set_keysize(QComboBox *cb, unsigned int strength)
{
if (!cb) {
return;
}
const int idx = cb->findData(static_cast<int>(strength));
cb->setCurrentIndex(idx);
}
static unsigned int get_keysize(const QComboBox *cb)
{
if (!cb) {
return 0;
}
const int idx = cb->currentIndex();
if (idx < 0) {
return 0;
}
return cb->itemData(idx).toInt();
}
static void set_curve(QComboBox *cb, const QString &curve)
{
if (!cb) {
return;
}
const int idx = cb->findText(curve);
if (idx < 0) {
// Can't happen as we don't have them configurable.
qCWarning(KLEOPATRA_LOG) << "curve " << curve << " not allowed";
}
cb->setCurrentIndex(idx);
}
static QString get_curve(const QComboBox *cb)
{
if (!cb) {
return QString();
}
return cb->currentText();
}
// Extract the algo information from default_pubkey_algo format
//
// and put it into the return values size, algo and curve.
//
// Values look like:
// RSA-2048
// rsa2048/cert,sign+rsa2048/enc
// brainpoolP256r1+brainpoolP256r1
static void parseAlgoString(const QString &algoString, int *size, Subkey::PubkeyAlgo *algo, QString &curve)
{
const auto split = algoString.split(QLatin1Char('/'));
bool isEncrypt = split.size() == 2 && split[1].contains(QLatin1String("enc"));
// Normalize
const auto lowered = split[0].toLower().remove(QLatin1Char('-'));
if (!algo || !size) {
return;
}
*algo = Subkey::AlgoUnknown;
if (lowered.startsWith(QLatin1String("rsa"))) {
*algo = Subkey::AlgoRSA;
} else if (lowered.startsWith(QLatin1String("dsa"))) {
*algo = Subkey::AlgoDSA;
} else if (lowered.startsWith(QLatin1String("elg"))) {
*algo = Subkey::AlgoELG;
}
if (*algo != Subkey::AlgoUnknown) {
bool ok;
*size = lowered.rightRef(lowered.size() - 3).toInt(&ok);
if (!ok) {
qCWarning(KLEOPATRA_LOG) << "Could not extract size from: " << lowered;
*size = 3072;
}
return;
}
// Now the ECC Algorithms
if (lowered.startsWith(QLatin1String("ed25519"))) {
// Special handling for this as technically
// this is a cv25519 curve used for EDDSA
curve = split[0];
*algo = Subkey::AlgoEDDSA;
return;
}
if (lowered.startsWith(QLatin1String("cv25519")) ||
lowered.startsWith(QLatin1String("nist")) ||
lowered.startsWith(QLatin1String("brainpool")) ||
lowered.startsWith(QLatin1String("secp"))) {
curve = split[0];
*algo = isEncrypt ? Subkey::AlgoECDH : Subkey::AlgoECDSA;
return;
}
qCWarning(KLEOPATRA_LOG) << "Failed to parse default_pubkey_algo:" << algoString;
}
Q_DECLARE_METATYPE(GpgME::Subkey::PubkeyAlgo)
namespace Kleo
{
namespace NewCertificateUi
{
class WizardPage : public QWizardPage
{
Q_OBJECT
protected:
explicit WizardPage(QWidget *parent = nullptr)
: QWizardPage(parent) {}
NewCertificateWizard *wizard() const
{
Q_ASSERT(static_cast<NewCertificateWizard *>(QWizardPage::wizard()) == qobject_cast<NewCertificateWizard *>(QWizardPage::wizard()));
return static_cast<NewCertificateWizard *>(QWizardPage::wizard());
}
QAbstractButton *button(QWizard::WizardButton button) const
{
return QWizardPage::wizard() ? QWizardPage::wizard()->button(button) : nullptr;
}
bool isButtonVisible(QWizard::WizardButton button) const
{
if (const QAbstractButton *const b = this->button(button)) {
return b->isVisible();
} else {
return false;
}
}
QDir tmpDir() const;
protected Q_SLOTS:
void setButtonVisible(QWizard::WizardButton button, bool visible)
{
if (QAbstractButton *const b = this->button(button)) {
b->setVisible(visible);
}
}
protected:
#define FIELD(type, name) type name() const { return field( QStringLiteral(#name) ).value<type>(); }
FIELD(bool, pgp)
FIELD(bool, signingAllowed)
FIELD(bool, encryptionAllowed)
FIELD(bool, certificationAllowed)
FIELD(bool, authenticationAllowed)
FIELD(QString, name)
FIELD(QString, email)
FIELD(QString, dn)
FIELD(bool, protectedKey)
FIELD(Subkey::PubkeyAlgo, keyType)
FIELD(int, keyStrength)
FIELD(QString, keyCurve)
FIELD(Subkey::PubkeyAlgo, subkeyType)
FIELD(int, subkeyStrength)
FIELD(QString, subkeyCurve)
FIELD(QDate, expiryDate)
FIELD(QStringList, additionalUserIDs)
FIELD(QStringList, additionalEMailAddresses)
FIELD(QStringList, dnsNames)
FIELD(QStringList, uris)
FIELD(QString, url)
FIELD(QString, error)
FIELD(QString, result)
FIELD(QString, fingerprint)
#undef FIELD
};
} // namespace NewCertificateUi
} // namespace Kleo
using namespace Kleo::NewCertificateUi;
namespace
{
class AdvancedSettingsDialog : public QDialog
{
Q_OBJECT
Q_PROPERTY(QStringList additionalUserIDs READ additionalUserIDs WRITE setAdditionalUserIDs)
Q_PROPERTY(QStringList additionalEMailAddresses READ additionalEMailAddresses WRITE setAdditionalEMailAddresses)
Q_PROPERTY(QStringList dnsNames READ dnsNames WRITE setDnsNames)
Q_PROPERTY(QStringList uris READ uris WRITE setUris)
Q_PROPERTY(uint keyStrength READ keyStrength WRITE setKeyStrength)
Q_PROPERTY(Subkey::PubkeyAlgo keyType READ keyType WRITE setKeyType)
Q_PROPERTY(QString keyCurve READ keyCurve WRITE setKeyCurve)
Q_PROPERTY(uint subkeyStrength READ subkeyStrength WRITE setSubkeyStrength)
Q_PROPERTY(QString subkeyCurve READ subkeyCurve WRITE setSubkeyCurve)
Q_PROPERTY(Subkey::PubkeyAlgo subkeyType READ subkeyType WRITE setSubkeyType)
Q_PROPERTY(bool signingAllowed READ signingAllowed WRITE setSigningAllowed)
Q_PROPERTY(bool encryptionAllowed READ encryptionAllowed WRITE setEncryptionAllowed)
Q_PROPERTY(bool certificationAllowed READ certificationAllowed WRITE setCertificationAllowed)
Q_PROPERTY(bool authenticationAllowed READ authenticationAllowed WRITE setAuthenticationAllowed)
Q_PROPERTY(QDate expiryDate READ expiryDate WRITE setExpiryDate)
public:
explicit AdvancedSettingsDialog(QWidget *parent = nullptr)
: QDialog(parent),
protocol(UnknownProtocol),
pgpDefaultAlgorithm(Subkey::AlgoELG_E),
cmsDefaultAlgorithm(Subkey::AlgoRSA),
keyTypeImmutable(false),
ui(),
mECCSupported(engineIsVersion(2, 1, 0)),
mEdDSASupported(engineIsVersion(2, 1, 15))
{
qRegisterMetaType<Subkey::PubkeyAlgo>("Subkey::PubkeyAlgo");
ui.setupUi(this);
const QDate today = QDate::currentDate();
ui.expiryDE->setMinimumDate(today);
ui.expiryDE->setDate(today.addYears(2));
ui.expiryCB->setChecked(true);
ui.emailLW->setDefaultValue(i18n("new email"));
ui.dnsLW->setDefaultValue(i18n("new dns name"));
ui.uriLW->setDefaultValue(i18n("new uri"));
fillKeySizeComboBoxen();
}
void setProtocol(GpgME::Protocol proto)
{
if (protocol == proto) {
return;
}
protocol = proto;
loadDefaultKeyType();
}
void setAdditionalUserIDs(const QStringList &items)
{
ui.uidLW->setItems(items);
}
QStringList additionalUserIDs() const
{
return ui.uidLW->items();
}
void setAdditionalEMailAddresses(const QStringList &items)
{
ui.emailLW->setItems(items);
}
QStringList additionalEMailAddresses() const
{
return ui.emailLW->items();
}
void setDnsNames(const QStringList &items)
{
ui.dnsLW->setItems(items);
}
QStringList dnsNames() const
{
return ui.dnsLW->items();
}
void setUris(const QStringList &items)
{
ui.uriLW->setItems(items);
}
QStringList uris() const
{
return ui.uriLW->items();
}
void setKeyStrength(unsigned int strength)
{
set_keysize(ui.rsaKeyStrengthCB, strength);
set_keysize(ui.dsaKeyStrengthCB, strength);
}
unsigned int keyStrength() const
{
return
ui.dsaRB->isChecked() ? get_keysize(ui.dsaKeyStrengthCB) :
ui.rsaRB->isChecked() ? get_keysize(ui.rsaKeyStrengthCB) : 0;
}
void setKeyType(Subkey::PubkeyAlgo algo)
{
QRadioButton *const rb =
is_rsa(algo) ? ui.rsaRB :
is_dsa(algo) ? ui.dsaRB :
is_ecdsa(algo) || is_eddsa(algo) ? ui.ecdsaRB : nullptr;
if (rb) {
rb->setChecked(true);
}
}
Subkey::PubkeyAlgo keyType() const
{
return
ui.dsaRB->isChecked() ? Subkey::AlgoDSA :
ui.rsaRB->isChecked() ? Subkey::AlgoRSA :
ui.ecdsaRB->isChecked() ?
ui.ecdsaKeyCurvesCB->currentText() == QLatin1String("ed25519") ? Subkey::AlgoEDDSA :
Subkey::AlgoECDSA :
Subkey::AlgoUnknown;
}
void setKeyCurve(const QString &curve)
{
set_curve(ui.ecdsaKeyCurvesCB, curve);
}
QString keyCurve() const
{
return get_curve(ui.ecdsaKeyCurvesCB);
}
void setSubkeyType(Subkey::PubkeyAlgo algo)
{
ui.elgCB->setChecked(is_elg(algo));
ui.rsaSubCB->setChecked(is_rsa(algo));
ui.ecdhCB->setChecked(is_ecdh(algo));
}
Subkey::PubkeyAlgo subkeyType() const
{
if (ui.elgCB->isChecked()) {
return Subkey::AlgoELG_E;
} else if (ui.rsaSubCB->isChecked()) {
return Subkey::AlgoRSA;
} else if (ui.ecdhCB->isChecked()) {
return Subkey::AlgoECDH;
}
return Subkey::AlgoUnknown;
}
void setSubkeyCurve(const QString &curve)
{
set_curve(ui.ecdhKeyCurvesCB, curve);
}
QString subkeyCurve() const
{
return get_curve(ui.ecdhKeyCurvesCB);
}
void setSubkeyStrength(unsigned int strength)
{
if (subkeyType() == Subkey::AlgoRSA) {
set_keysize(ui.rsaKeyStrengthSubCB, strength);
} else {
set_keysize(ui.elgKeyStrengthCB, strength);
}
}
unsigned int subkeyStrength() const
{
if (subkeyType() == Subkey::AlgoRSA) {
return get_keysize(ui.rsaKeyStrengthSubCB);
}
return get_keysize(ui.elgKeyStrengthCB);
}
void setSigningAllowed(bool on)
{
ui.signingCB->setChecked(on);
}
bool signingAllowed() const
{
return ui.signingCB->isChecked();
}
void setEncryptionAllowed(bool on)
{
ui.encryptionCB->setChecked(on);
}
bool encryptionAllowed() const
{
return ui.encryptionCB->isChecked();
}
void setCertificationAllowed(bool on)
{
ui.certificationCB->setChecked(on);
}
bool certificationAllowed() const
{
return ui.certificationCB->isChecked();
}
void setAuthenticationAllowed(bool on)
{
ui.authenticationCB->setChecked(on);
}
bool authenticationAllowed() const
{
return ui.authenticationCB->isChecked();
}
void setExpiryDate(QDate date)
{
if (date.isValid()) {
ui.expiryDE->setDate(date);
} else {
ui.expiryCB->setChecked(false);
}
}
QDate expiryDate() const
{
return ui.expiryCB->isChecked() ? ui.expiryDE->date() : QDate();
}
Q_SIGNALS:
void changed();
private Q_SLOTS:
void slotKeyMaterialSelectionChanged()
{
const unsigned int algo = keyType();
const unsigned int sk_algo = subkeyType();
if (protocol == OpenPGP) {
if (!keyTypeImmutable) {
ui.elgCB->setEnabled(is_dsa(algo));
ui.rsaSubCB->setEnabled(is_rsa(algo));
ui.ecdhCB->setEnabled(is_ecdsa(algo) || is_eddsa(algo));
if (sender() == ui.dsaRB || sender() == ui.rsaRB || sender() == ui.ecdsaRB) {
ui.elgCB->setChecked(is_dsa(algo));
ui.ecdhCB->setChecked(is_ecdsa(algo) || is_eddsa(algo));
ui.rsaSubCB->setChecked(is_rsa(algo));
}
if (is_rsa(algo)) {
ui.encryptionCB->setEnabled(true);
ui.encryptionCB->setChecked(true);
ui.signingCB->setEnabled(true);
ui.signingCB->setChecked(true);
ui.authenticationCB->setEnabled(true);
if (is_rsa(sk_algo)) {
ui.encryptionCB->setEnabled(false);
ui.encryptionCB->setChecked(true);
} else {
ui.encryptionCB->setEnabled(true);
}
} else if (is_dsa(algo)) {
ui.encryptionCB->setEnabled(false);
if (is_elg(sk_algo)) {
ui.encryptionCB->setChecked(true);
} else {
ui.encryptionCB->setChecked(false);
}
} else if (is_ecdsa(algo) || is_eddsa(algo)) {
ui.signingCB->setEnabled(true);
ui.signingCB->setChecked(true);
ui.authenticationCB->setEnabled(true);
ui.encryptionCB->setEnabled(false);
ui.encryptionCB->setChecked(is_ecdh(sk_algo));
}
}
} else {
//assert( is_rsa( keyType() ) ); // it can happen through misconfiguration by the admin that no key type is selectable at all
}
}
void slotSigningAllowedToggled(bool on)
{
if (!on && protocol == CMS && !encryptionAllowed()) {
setEncryptionAllowed(true);
}
}
void slotEncryptionAllowedToggled(bool on)
{
if (!on && protocol == CMS && !signingAllowed()) {
setSigningAllowed(true);
}
}
private:
void fillKeySizeComboBoxen();
void loadDefaultKeyType();
void loadDefaultGnuPGKeyType();
void updateWidgetVisibility();
private:
GpgME::Protocol protocol;
unsigned int pgpDefaultAlgorithm;
unsigned int cmsDefaultAlgorithm;
bool keyTypeImmutable;
Ui_AdvancedSettingsDialog ui;
bool mECCSupported;
bool mEdDSASupported;
};
class ChooseProtocolPage : public WizardPage
{
Q_OBJECT
public:
explicit ChooseProtocolPage(QWidget *p = nullptr)
: WizardPage(p),
initialized(false),
ui()
{
ui.setupUi(this);
registerField(QStringLiteral("pgp"), ui.pgpCLB);
}
void setProtocol(Protocol proto)
{
if (proto == OpenPGP) {
ui.pgpCLB->setChecked(true);
} else if (proto == CMS) {
ui.x509CLB->setChecked(true);
} else {
force_set_checked(ui.pgpCLB, false);
force_set_checked(ui.x509CLB, false);
}
}
Protocol protocol() const
{
return
ui.pgpCLB->isChecked() ? OpenPGP :
ui.x509CLB->isChecked() ? CMS : UnknownProtocol;
}
void initializePage() override {
if (!initialized)
{
connect(ui.pgpCLB, &QAbstractButton::clicked, wizard(), &QWizard::next, Qt::QueuedConnection);
connect(ui.x509CLB, &QAbstractButton::clicked, wizard(), &QWizard::next, Qt::QueuedConnection);
}
initialized = true;
}
bool isComplete() const override
{
return protocol() != UnknownProtocol;
}
private:
bool initialized : 1;
Ui_ChooseProtocolPage ui;
};
struct Line {
QString attr;
QString label;
QString regex;
QLineEdit *edit;
};
class EnterDetailsPage : public WizardPage
{
Q_OBJECT
public:
explicit EnterDetailsPage(QWidget *p = nullptr)
: WizardPage(p), dialog(this), ui()
{
ui.setupUi(this);
// set errorLB to have a fixed height of two lines:
ui.errorLB->setText(QStringLiteral("2<br>1"));
ui.errorLB->setFixedHeight(ui.errorLB->minimumSizeHint().height());
ui.errorLB->clear();
connect(ui.resultLE, &QLineEdit::textChanged,
this, &QWizardPage::completeChanged);
// The email doesn't necessarily show up in ui.resultLE:
connect(ui.emailLE, &QLineEdit::textChanged,
this, &QWizardPage::completeChanged);
registerDialogPropertiesAsFields();
registerField(QStringLiteral("dn"), ui.resultLE);
registerField(QStringLiteral("name"), ui.nameLE);
registerField(QStringLiteral("email"), ui.emailLE);
registerField(QStringLiteral("protectedKey"), ui.withPassCB);
updateForm();
setCommitPage(true);
setButtonText(QWizard::CommitButton, i18nc("@action", "Create"));
const auto conf = QGpgME::cryptoConfig();
if (!conf) {
qCWarning(KLEOPATRA_LOG) << "Failed to obtain cryptoConfig.";
return;
}
- const auto entry = conf->entry(QStringLiteral("gpg-agent"),
- QStringLiteral("Passphrase policy"),
- QStringLiteral("enforce-passphrase-constraints"));
+ const auto entry = getCryptoConfigEntry(conf, "gpg-agent", "enforce-passphrase-constraints");
if (entry && entry->boolValue()) {
qCDebug(KLEOPATRA_LOG) << "Disabling passphrace cb because of agent config.";
ui.withPassCB->setEnabled(false);
ui.withPassCB->setChecked(true);
} else {
const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard");
ui.withPassCB->setChecked(config.readEntry("WithPassphrase", false));
ui.withPassCB->setEnabled(!config.isEntryImmutable("WithPassphrase"));
}
}
bool isComplete() const override;
void initializePage() override {
updateForm();
dialog.setProtocol(pgp() ? OpenPGP : CMS);
}
void cleanupPage() override {
saveValues();
}
private:
void updateForm();
void clearForm();
void saveValues();
void registerDialogPropertiesAsFields();
private:
QString pgpUserID() const;
QString cmsDN() const;
private Q_SLOTS:
void slotAdvancedSettingsClicked();
void slotUpdateResultLabel()
{
ui.resultLE->setText(pgp() ? pgpUserID() : cmsDN());
ui.withPassCB->setVisible(pgp());
}
private:
QVector<Line> lineList;
QList<QWidget *> dynamicWidgets;
QMap<QString, QString> savedValues;
AdvancedSettingsDialog dialog;
Ui_EnterDetailsPage ui;
};
class KeyCreationPage : public WizardPage
{
Q_OBJECT
public:
explicit KeyCreationPage(QWidget *p = nullptr)
: WizardPage(p),
ui()
{
ui.setupUi(this);
}
bool isComplete() const override
{
return !job;
}
void initializePage() override {
startJob();
}
private:
void startJob()
{
const auto proto = pgp() ? QGpgME::openpgp() : QGpgME::smime();
if (!proto) {
return;
}
QGpgME::KeyGenerationJob *const j = proto->keyGenerationJob();
if (!j) {
return;
}
if (!protectedKey() && pgp()) {
auto ctx = QGpgME::Job::context(j);
ctx->setPassphraseProvider(&mEmptyPWProvider);
ctx->setPinentryMode(Context::PinentryLoopback);
}
connect(j, &QGpgME::KeyGenerationJob::result,
this, &KeyCreationPage::slotResult);
if (const Error err = j->start(createGnupgKeyParms()))
setField(QStringLiteral("error"), i18n("Could not start key pair creation: %1",
QString::fromLocal8Bit(err.asString())));
else {
job = j;
}
}
QStringList keyUsages() const;
QStringList subkeyUsages() const;
QString createGnupgKeyParms() const;
EmptyPassphraseProvider mEmptyPWProvider;
private Q_SLOTS:
void slotResult(const GpgME::KeyGenerationResult &result, const QByteArray &request, const QString &auditLog)
{
Q_UNUSED(auditLog)
if (result.error().code() || (pgp() && !result.fingerprint())) {
setField(QStringLiteral("error"), result.error().isCanceled()
? i18n("Operation canceled.")
: i18n("Could not create key pair: %1",
QString::fromLocal8Bit(result.error().asString())));
setField(QStringLiteral("url"), QString());
setField(QStringLiteral("result"), QString());
} else if (pgp()) {
setField(QStringLiteral("error"), QString());
setField(QStringLiteral("url"), QString());
setField(QStringLiteral("result"), i18n("Key pair created successfully.\n"
"Fingerprint: %1", QLatin1String(result.fingerprint())));
} else {
QFile file(tmpDir().absoluteFilePath(QStringLiteral("request.p10")));
if (!file.open(QIODevice::WriteOnly)) {
setField(QStringLiteral("error"), i18n("Could not write output file %1: %2",
file.fileName(), file.errorString()));
setField(QStringLiteral("url"), QString());
setField(QStringLiteral("result"), QString());
} else {
file.write(request);
setField(QStringLiteral("error"), QString());
setField(QStringLiteral("url"), QUrl::fromLocalFile(file.fileName()).toString());
setField(QStringLiteral("result"), i18n("Key pair created successfully."));
}
}
// Ensure that we have the key in the keycache
if (pgp() && !result.error().code() && result.fingerprint()) {
auto ctx = Context::createForProtocol(OpenPGP);
if (ctx) {
// Check is pretty useless something very buggy in that case.
Error e;
const auto key = ctx->key(result.fingerprint(), e, true);
if (!key.isNull()) {
KeyCache::mutableInstance()->insert(key);
} else {
qCDebug(KLEOPATRA_LOG) << "Failed to find newly generated key.";
}
delete ctx;
}
}
setField(QStringLiteral("fingerprint"), result.fingerprint() ?
QString::fromLatin1(result.fingerprint()) : QString());
job = nullptr;
Q_EMIT completeChanged();
const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard");
if (config.readEntry("SkipResultPage", false)) {
if (result.fingerprint()) {
KleopatraApplication::instance()->slotActivateRequested(QStringList() <<
QStringLiteral("kleopatra") << QStringLiteral("--query") << QLatin1String(result.fingerprint()), QString());
QMetaObject::invokeMethod(wizard(), "close", Qt::QueuedConnection);
} else {
QMetaObject::invokeMethod(wizard(), "next", Qt::QueuedConnection);
}
} else {
QMetaObject::invokeMethod(wizard(), "next", Qt::QueuedConnection);
}
}
private:
QPointer<QGpgME::KeyGenerationJob> job;
Ui_KeyCreationPage ui;
};
class ResultPage : public WizardPage
{
Q_OBJECT
public:
explicit ResultPage(QWidget *p = nullptr)
: WizardPage(p),
initialized(false),
successfullyCreatedSigningCertificate(false),
successfullyCreatedEncryptionCertificate(false),
ui()
{
ui.setupUi(this);
ui.dragQueen->setPixmap(QIcon::fromTheme(QStringLiteral("kleopatra")).pixmap(64, 64));
registerField(QStringLiteral("error"), ui.errorTB, "plainText");
registerField(QStringLiteral("result"), ui.resultTB, "plainText");
registerField(QStringLiteral("url"), ui.dragQueen, "url");
// hidden field, since QWizard can't deal with non-widget-backed fields...
QLineEdit *le = new QLineEdit(this);
le->hide();
registerField(QStringLiteral("fingerprint"), le);
}
void initializePage() override {
const bool error = isError();
if (error)
{
setTitle(i18nc("@title", "Key Creation Failed"));
setSubTitle(i18n("Key pair creation failed. Please find details about the failure below."));
} else {
setTitle(i18nc("@title", "Key Pair Successfully Created"));
setSubTitle(i18n("Your new key pair was created successfully. Please find details on the result and some suggested next steps below."));
}
ui.resultTB ->setVisible(!error);
ui.errorTB ->setVisible(error);
ui.dragQueen ->setVisible(!error &&!pgp());
ui.restartWizardPB ->setVisible(error);
ui.nextStepsGB ->setVisible(!error);
ui.saveRequestToFilePB ->setVisible(!pgp());
ui.makeBackupPB ->setVisible(pgp());
ui.createRevocationRequestPB->setVisible(pgp() &&false); // not implemented
ui.sendCertificateByEMailPB ->setVisible(pgp());
ui.sendRequestByEMailPB ->setVisible(!pgp());
ui.uploadToKeyserverPB ->setVisible(pgp());
if (!error && !pgp())
{
if (signingAllowed() && !encryptionAllowed()) {
successfullyCreatedSigningCertificate = true;
} else if (!signingAllowed() && encryptionAllowed()) {
successfullyCreatedEncryptionCertificate = true;
} else {
successfullyCreatedEncryptionCertificate = successfullyCreatedSigningCertificate = true;
}
}
ui.createSigningCertificatePB->setVisible(successfullyCreatedEncryptionCertificate &&!successfullyCreatedSigningCertificate);
ui.createEncryptionCertificatePB->setVisible(successfullyCreatedSigningCertificate &&!successfullyCreatedEncryptionCertificate);
setButtonVisible(QWizard::CancelButton, error);
if (!initialized)
connect(ui.restartWizardPB, &QAbstractButton::clicked,
wizard(), &QWizard::restart);
initialized = true;
}
void cleanupPage() override {
setButtonVisible(QWizard::CancelButton, true);
}
bool isError() const
{
return !ui.errorTB->document()->isEmpty();
}
bool isComplete() const override
{
return !isError();
}
private:
Key key() const
{
return KeyCache::instance()->findByFingerprint(fingerprint().toLatin1().constData());
}
private Q_SLOTS:
void slotSaveRequestToFile()
{
QString fileName = FileDialog::getSaveFileName(this, i18nc("@title", "Save Request"),
QStringLiteral("imp"), i18n("PKCS#10 Requests (*.p10)"));
if (fileName.isEmpty()) {
return;
}
if (!fileName.endsWith(QLatin1String(".p10"), Qt::CaseInsensitive)) {
fileName += QLatin1String(".p10");
}
QFile src(QUrl(url()).toLocalFile());
if (!src.copy(fileName))
KMessageBox::error(this,
xi18nc("@info",
"Could not copy temporary file <filename>%1</filename> "
"to file <filename>%2</filename>: <message>%3</message>",
src.fileName(), fileName, src.errorString()),
i18nc("@title", "Error Saving Request"));
else
KMessageBox::information(this,
xi18nc("@info",
"<para>Successfully wrote request to <filename>%1</filename>.</para>"
"<para>You should now send the request to the Certification Authority (CA).</para>",
fileName),
i18nc("@title", "Request Saved"));
}
void slotSendRequestByEMail()
{
if (pgp()) {
return;
}
const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard");
invokeMailer(config.readEntry("CAEmailAddress"), // to
i18n("Please process this certificate."), // subject
i18n("Please process this certificate and inform the sender about the location to fetch the resulting certificate.\n\nThanks,\n"), // body
QUrl(url()).toLocalFile()); // attachment
}
void slotSendCertificateByEMail()
{
if (!pgp() || exportCertificateCommand) {
return;
}
ExportCertificateCommand *cmd = new ExportCertificateCommand(key());
connect(cmd, &ExportCertificateCommand::finished, this, &ResultPage::slotSendCertificateByEMailContinuation);
cmd->setOpenPGPFileName(tmpDir().absoluteFilePath(fingerprint() + QLatin1String(".asc")));
cmd->start();
exportCertificateCommand = cmd;
}
void slotSendCertificateByEMailContinuation()
{
if (!exportCertificateCommand) {
return;
}
// ### better error handling?
const QString fileName = exportCertificateCommand->openPGPFileName();
qCDebug(KLEOPATRA_LOG) << "fileName" << fileName;
exportCertificateCommand = nullptr;
if (fileName.isEmpty()) {
return;
}
invokeMailer(QString(), // to
i18n("My new public OpenPGP key"), // subject
i18n("Please find attached my new public OpenPGP key."), // body
fileName);
}
QByteArray ol_quote(QByteArray str)
{
#ifdef Q_OS_WIN
return "\"\"" + str.replace('"', "\\\"") + "\"\"";
//return '"' + str.replace( '"', "\\\"" ) + '"';
#else
return str;
#endif
}
void invokeMailer(const QString &to, const QString &subject, const QString &body, const QString &attachment)
{
qCDebug(KLEOPATRA_LOG) << "to:" << to << "subject:" << subject
<< "body:" << body << "attachment:" << attachment;
// RFC 2368 says body's linebreaks need to be encoded as
// "%0D%0A", so normalize body to CRLF:
//body.replace(QLatin1Char('\n'), QStringLiteral("\r\n")).remove(QStringLiteral("\r\r"));
QUrlQuery query;
query.addQueryItem(QStringLiteral("subject"), subject);
query.addQueryItem(QStringLiteral("body"), body);
if (!attachment.isEmpty()) {
query.addQueryItem(QStringLiteral("attach"), attachment);
}
QUrl url;
url.setScheme(QStringLiteral("mailto"));
url.setQuery(query);
qCDebug(KLEOPATRA_LOG) << "openUrl" << url;
QDesktopServices::openUrl(url);
KMessageBox::information(this,
xi18nc("@info",
"<para><application>Kleopatra</application> tried to send a mail via your default mail client.</para>"
"<para>Some mail clients are known not to support attachments when invoked this way.</para>"
"<para>If your mail client does not have an attachment, then drag the <application>Kleopatra</application> icon and drop it on the message compose window of your mail client.</para>"
"<para>If that does not work, either, save the request to a file, and then attach that.</para>"),
i18nc("@title", "Sending Mail"),
QStringLiteral("newcertificatewizard-mailto-troubles"));
}
void slotUploadCertificateToDirectoryServer()
{
if (pgp()) {
(new ExportOpenPGPCertsToServerCommand(key()))->start();
}
}
void slotBackupCertificate()
{
if (pgp()) {
(new ExportSecretKeyCommand(key()))->start();
}
}
void slotCreateRevocationRequest()
{
}
void slotCreateSigningCertificate()
{
if (successfullyCreatedSigningCertificate) {
return;
}
toggleSignEncryptAndRestart();
}
void slotCreateEncryptionCertificate()
{
if (successfullyCreatedEncryptionCertificate) {
return;
}
toggleSignEncryptAndRestart();
}
private:
void toggleSignEncryptAndRestart()
{
if (!wizard()) {
return;
}
if (KMessageBox::warningContinueCancel(
this,
i18nc("@info",
"This operation will delete the certification request. "
"Please make sure that you have sent or saved it before proceeding."),
i18nc("@title", "Certification Request About To Be Deleted")) != KMessageBox::Continue) {
return;
}
const bool sign = signingAllowed();
const bool encr = encryptionAllowed();
setField(QStringLiteral("signingAllowed"), !sign);
setField(QStringLiteral("encryptionAllowed"), !encr);
// restart and skip to enter details Page:
wizard()->restart();
for (int i = wizard()->currentId(); i < NewCertificateWizard::EnterDetailsPageId; ++i) {
wizard()->next();
}
}
private:
bool initialized : 1;
bool successfullyCreatedSigningCertificate : 1;
bool successfullyCreatedEncryptionCertificate : 1;
QPointer<ExportCertificateCommand> exportCertificateCommand;
Ui_ResultPage ui;
};
}
class NewCertificateWizard::Private
{
friend class ::Kleo::NewCertificateWizard;
friend class ::Kleo::NewCertificateUi::WizardPage;
NewCertificateWizard *const q;
public:
explicit Private(NewCertificateWizard *qq)
: q(qq),
tmp(QDir::temp().absoluteFilePath(QStringLiteral("kleo-"))),
ui(q)
{
q->setWindowTitle(i18nc("@title:window", "Key Pair Creation Wizard"));
}
private:
QTemporaryDir tmp;
struct Ui {
ChooseProtocolPage chooseProtocolPage;
EnterDetailsPage enterDetailsPage;
KeyCreationPage keyCreationPage;
ResultPage resultPage;
explicit Ui(NewCertificateWizard *q)
: chooseProtocolPage(q),
enterDetailsPage(q),
keyCreationPage(q),
resultPage(q)
{
KDAB_SET_OBJECT_NAME(chooseProtocolPage);
KDAB_SET_OBJECT_NAME(enterDetailsPage);
KDAB_SET_OBJECT_NAME(keyCreationPage);
KDAB_SET_OBJECT_NAME(resultPage);
q->setOptions(DisabledBackButtonOnLastPage);
q->setPage(ChooseProtocolPageId, &chooseProtocolPage);
q->setPage(EnterDetailsPageId, &enterDetailsPage);
q->setPage(KeyCreationPageId, &keyCreationPage);
q->setPage(ResultPageId, &resultPage);
q->setStartId(ChooseProtocolPageId);
}
} ui;
};
NewCertificateWizard::NewCertificateWizard(QWidget *p)
: QWizard(p), d(new Private(this))
{
}
NewCertificateWizard::~NewCertificateWizard() {}
void NewCertificateWizard::setProtocol(Protocol proto)
{
d->ui.chooseProtocolPage.setProtocol(proto);
setStartId(proto == UnknownProtocol ? ChooseProtocolPageId : EnterDetailsPageId);
}
Protocol NewCertificateWizard::protocol() const
{
return d->ui.chooseProtocolPage.protocol();
}
static QString pgpLabel(const QString &attr)
{
if (attr == QLatin1String("NAME")) {
return i18n("Name");
}
if (attr == QLatin1String("EMAIL")) {
return i18n("EMail");
}
return QString();
}
static QString attributeLabel(const QString &attr, bool pgp)
{
if (attr.isEmpty()) {
return QString();
}
const QString label = pgp ? pgpLabel(attr) : Kleo::DNAttributeMapper::instance()->name2label(attr);
if (!label.isEmpty())
if (pgp) {
return label;
} else
return i18nc("Format string for the labels in the \"Your Personal Data\" page",
"%1 (%2)", label, attr);
else {
return attr;
}
}
#if 0
//Not used anywhere
static QString attributeLabelWithColor(const QString &attr, bool pgp)
{
const QString result = attributeLabel(attr, pgp);
if (result.isEmpty()) {
return QString();
} else {
return result + ':';
}
}
#endif
static QString attributeFromKey(QString key)
{
return key.remove(QLatin1Char('!'));
}
QDir WizardPage::tmpDir() const
{
return wizard() ? QDir(wizard()->d->tmp.path()) : QDir::home();
}
void EnterDetailsPage::registerDialogPropertiesAsFields()
{
const QMetaObject *const mo = dialog.metaObject();
for (unsigned int i = mo->propertyOffset(), end = i + mo->propertyCount(); i != end; ++i) {
const QMetaProperty mp = mo->property(i);
if (mp.isValid()) {
registerField(QLatin1String(mp.name()), &dialog, mp.name(), SIGNAL(accepted()));
}
}
}
void EnterDetailsPage::saveValues()
{
for (const Line &line : qAsConst(lineList)) {
savedValues[ attributeFromKey(line.attr) ] = line.edit->text().trimmed();
}
}
void EnterDetailsPage::clearForm()
{
qDeleteAll(dynamicWidgets);
dynamicWidgets.clear();
lineList.clear();
ui.nameLE->hide();
ui.nameLE->clear();
ui.nameLB->hide();
ui.nameRequiredLB->hide();
ui.emailLE->hide();
ui.emailLE->clear();
ui.emailLB->hide();
ui.emailRequiredLB->hide();
}
static int row_index_of(QWidget *w, QGridLayout *l)
{
const int idx = l->indexOf(w);
int r, c, rs, cs;
l->getItemPosition(idx, &r, &c, &rs, &cs);
return r;
}
static QLineEdit *adjust_row(QGridLayout *l, int row, const QString &label, const QString &preset, QValidator *validator, bool readonly, bool required)
{
Q_ASSERT(l);
Q_ASSERT(row >= 0);
Q_ASSERT(row < l->rowCount());
QLabel *lb = qobject_cast<QLabel *>(l->itemAtPosition(row, 0)->widget());
Q_ASSERT(lb);
QLineEdit *le = qobject_cast<QLineEdit *>(l->itemAtPosition(row, 1)->widget());
Q_ASSERT(le);
lb->setBuddy(le); // For better accessibility
QLabel *reqLB = qobject_cast<QLabel *>(l->itemAtPosition(row, 2)->widget());
Q_ASSERT(reqLB);
lb->setText(i18nc("interpunctation for labels", "%1:", label));
le->setText(preset);
reqLB->setText(required ? i18n("(required)") : i18n("(optional)"));
delete le->validator();
if (validator) {
if (!validator->parent()) {
validator->setParent(le);
}
le->setValidator(validator);
}
le->setReadOnly(readonly && le->hasAcceptableInput());
lb->show();
le->show();
reqLB->show();
return le;
}
static int add_row(QGridLayout *l, QList<QWidget *> *wl)
{
Q_ASSERT(l);
Q_ASSERT(wl);
const int row = l->rowCount();
QWidget *w1, *w2, *w3;
l->addWidget(w1 = new QLabel(l->parentWidget()), row, 0);
l->addWidget(w2 = new QLineEdit(l->parentWidget()), row, 1);
l->addWidget(w3 = new QLabel(l->parentWidget()), row, 2);
wl->push_back(w1);
wl->push_back(w2);
wl->push_back(w3);
return row;
}
void EnterDetailsPage::updateForm()
{
clearForm();
const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard");
QStringList attrOrder = config.readEntry(pgp() ? "OpenPGPAttributeOrder" : "DNAttributeOrder", QStringList());
if (attrOrder.empty()) {
if (pgp()) {
attrOrder << QStringLiteral("NAME") << QStringLiteral("EMAIL");
} else {
attrOrder << QStringLiteral("CN!") << QStringLiteral("L") << QStringLiteral("OU") << QStringLiteral("O") << QStringLiteral("C") << QStringLiteral("EMAIL!");
}
}
QList<QWidget *> widgets;
widgets.push_back(ui.nameLE);
widgets.push_back(ui.emailLE);
QMap<int, Line> lines;
for (const QString &rawKey : qAsConst(attrOrder)) {
const QString key = rawKey.trimmed().toUpper();
const QString attr = attributeFromKey(key);
if (attr.isEmpty()) {
continue;
}
const QString preset = savedValues.value(attr, config.readEntry(attr, QString()));
const bool required = key.endsWith(QLatin1Char('!'));
const bool readonly = config.isEntryImmutable(attr);
const QString label = config.readEntry(attr + QLatin1String("_label"),
attributeLabel(attr, pgp()));
const QString regex = config.readEntry(attr + QLatin1String("_regex"));
int row;
bool known = true;
QValidator *validator = nullptr;
if (attr == QLatin1String("EMAIL")) {
row = row_index_of(ui.emailLE, ui.gridLayout);
validator = regex.isEmpty() ? Validation::email() : Validation::email(QRegExp(regex));
} else if (attr == QLatin1String("NAME") || attr == QLatin1String("CN")) {
if ((pgp() && attr == QLatin1String("CN")) || (!pgp() && attr == QLatin1String("NAME"))) {
continue;
}
if (pgp()) {
validator = regex.isEmpty() ? Validation::pgpName() : Validation::pgpName(QRegExp(regex));
}
row = row_index_of(ui.nameLE, ui.gridLayout);
} else {
known = false;
row = add_row(ui.gridLayout, &dynamicWidgets);
}
if (!validator && !regex.isEmpty()) {
validator = new QRegExpValidator(QRegExp(regex), nullptr);
}
QLineEdit *le = adjust_row(ui.gridLayout, row, label, preset, validator, readonly, required);
const Line line = { key, label, regex, le };
lines[row] = line;
if (!known) {
widgets.push_back(le);
}
// don't connect twice:
disconnect(le, &QLineEdit::textChanged, this, &EnterDetailsPage::slotUpdateResultLabel);
connect(le, &QLineEdit::textChanged, this, &EnterDetailsPage::slotUpdateResultLabel);
}
// create lineList in visual order, so requirementsAreMet()
// complains from top to bottom:
lineList.reserve(lines.count());
std::copy(lines.cbegin(), lines.cend(), std::back_inserter(lineList));
widgets.push_back(ui.resultLE);
widgets.push_back(ui.advancedPB);
if (ui.nameLE->text().isEmpty()) {
ui.nameLE->setText(userFullName());
}
if (ui.emailLE->text().isEmpty()) {
ui.emailLE->setText(userEmailAddress());
}
set_tab_order(widgets);
}
QString EnterDetailsPage::cmsDN() const
{
DN dn;
for (QVector<Line>::const_iterator it = lineList.begin(), end = lineList.end(); it != end; ++it) {
const QString text = it->edit->text().trimmed();
if (text.isEmpty()) {
continue;
}
QString attr = attributeFromKey(it->attr);
if (attr == QLatin1String("EMAIL")) {
continue;
}
if (const char *const oid = oidForAttributeName(attr)) {
attr = QString::fromUtf8(oid);
}
dn.append(DN::Attribute(attr, text));
}
return dn.dn();
}
QString EnterDetailsPage::pgpUserID() const
{
return Formatting::prettyNameAndEMail(OpenPGP, QString(),
ui.nameLE->text().trimmed(),
ui.emailLE->text().trimmed(),
QString());
}
static bool has_intermediate_input(const QLineEdit *le)
{
QString text = le->text();
int pos = le->cursorPosition();
const QValidator *const v = le->validator();
return v && v->validate(text, pos) == QValidator::Intermediate;
}
static bool requirementsAreMet(const QVector<Line> &list, QString &error)
{
bool allEmpty = true;
for (const Line &line : list) {
const QLineEdit *le = line.edit;
if (!le) {
continue;
}
const QString key = line.attr;
qCDebug(KLEOPATRA_LOG) << "requirementsAreMet(): checking \"" << key << "\" against \"" << le->text() << "\":";
if (le->text().trimmed().isEmpty()) {
if (key.endsWith(QLatin1Char('!'))) {
if (line.regex.isEmpty()) {
error = xi18nc("@info", "<interface>%1</interface> is required, but empty.", line.label);
} else
error = xi18nc("@info", "<interface>%1</interface> is required, but empty.<nl/>"
"Local Admin rule: <icode>%2</icode>", line.label, line.regex);
return false;
}
} else if (has_intermediate_input(le)) {
if (line.regex.isEmpty()) {
error = xi18nc("@info", "<interface>%1</interface> is incomplete.", line.label);
} else
error = xi18nc("@info", "<interface>%1</interface> is incomplete.<nl/>"
"Local Admin rule: <icode>%2</icode>", line.label, line.regex);
return false;
} else if (!le->hasAcceptableInput()) {
if (line.regex.isEmpty()) {
error = xi18nc("@info", "<interface>%1</interface> is invalid.", line.label);
} else
error = xi18nc("@info", "<interface>%1</interface> is invalid.<nl/>"
"Local Admin rule: <icode>%2</icode>", line.label, line.regex);
return false;
} else {
allEmpty = false;
}
}
// Ensure that at least one value is acceptable
return !allEmpty;
}
bool EnterDetailsPage::isComplete() const
{
QString error;
const bool ok = requirementsAreMet(lineList, error);
ui.errorLB->setText(error);
return ok;
}
void EnterDetailsPage::slotAdvancedSettingsClicked()
{
dialog.exec();
}
QStringList KeyCreationPage::keyUsages() const
{
QStringList usages;
if (signingAllowed()) {
usages << QStringLiteral("sign");
}
if (encryptionAllowed() && !is_ecdh(subkeyType()) &&
!is_dsa(keyType()) && !is_rsa(subkeyType())) {
usages << QStringLiteral("encrypt");
}
if (authenticationAllowed()) {
usages << QStringLiteral("auth");
}
if (usages.empty() && certificationAllowed()) {
/* Empty usages cause an error so we need to
* add at least certify if nothing else is selected */
usages << QStringLiteral("cert");
}
return usages;
}
QStringList KeyCreationPage::subkeyUsages() const
{
QStringList usages;
if (encryptionAllowed() && (is_dsa(keyType()) || is_rsa(subkeyType()) ||
is_ecdh(subkeyType()))) {
Q_ASSERT(subkeyType());
usages << QStringLiteral("encrypt");
}
return usages;
}
namespace
{
template <typename T = QString>
struct Row {
QString key;
T value;
Row(const QString &k, const T &v) : key(k), value(v) {}
};
template <typename T>
QTextStream &operator<<(QTextStream &s, const Row<T> &row)
{
if (row.key.isEmpty()) {
return s;
} else {
return s << "<tr><td>" << row.key << "</td><td>" << row.value << "</td></tr>";
}
}
}
QString KeyCreationPage::createGnupgKeyParms() const
{
KeyParameters keyParameters(pgp() ? KeyParameters::OpenPGP : KeyParameters::CMS);
keyParameters.setKeyType(keyType());
if (is_ecdsa(keyType()) || is_eddsa(keyType())) {
keyParameters.setKeyCurve(keyCurve());
} else if (const unsigned int strength = keyStrength()) {
keyParameters.setKeyLength(strength);
}
keyParameters.setKeyUsages(keyUsages());
if (subkeyType()) {
keyParameters.setSubkeyType(subkeyType());
if (is_ecdh(subkeyType())) {
keyParameters.setSubkeyCurve(subkeyCurve());
} else if (const unsigned int strength = subkeyStrength()) {
keyParameters.setSubkeyLength(strength);
}
keyParameters.setSubkeyUsages(subkeyUsages());
}
if (pgp()) {
if (expiryDate().isValid()) {
keyParameters.setExpirationDate(expiryDate());
}
if (!name().isEmpty()) {
keyParameters.setName(name());
}
if (!email().isEmpty()) {
keyParameters.setEmail(email());
}
} else {
keyParameters.setDN(dn());
keyParameters.setEmail(email());
Q_FOREACH (const QString &email, additionalEMailAddresses()) {
keyParameters.addEmail(email);
}
Q_FOREACH (const QString &dns, dnsNames()) {
keyParameters.addDomainName(dns);
}
Q_FOREACH (const QString &uri, uris()) {
keyParameters.addURI(uri);
}
}
const QString result = keyParameters.toString();
qCDebug(KLEOPATRA_LOG) << '\n' << result;
return result;
}
static void fill_combobox(QComboBox &cb, const QList<int> &sizes, const QStringList &labels)
{
cb.clear();
for (int i = 0, end = sizes.size(); i != end; ++i) {
const int size = std::abs(sizes[i]);
/* As we respect the defaults configurable in GnuPG, and we also have configurable
* defaults in Kleopatra its difficult to print out "default" here. To avoid confusion
* about that its better not to show any default indication. */
cb.addItem(i < labels.size() && !labels[i].trimmed().isEmpty()
? i18ncp("%2: some admin-supplied text, %1: key size in bits", "%2 (1 bit)", "%2 (%1 bits)", size, labels[i].trimmed())
: i18ncp("%1: key size in bits", "1 bit", "%1 bits", size),
size);
if (sizes[i] < 0) {
cb.setCurrentIndex(cb.count() - 1);
}
}
}
void AdvancedSettingsDialog::fillKeySizeComboBoxen()
{
const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard");
QList<int> rsaKeySizes = config.readEntry(RSA_KEYSIZES_ENTRY, QList<int>() << 2048 << -3072 << 4096);
if (Kleo::gpgComplianceP("de-vs")) {
rsaKeySizes = config.readEntry(RSA_KEYSIZES_ENTRY, QList<int>() << -3072 << 4096);
}
const QList<int> dsaKeySizes = config.readEntry(DSA_KEYSIZES_ENTRY, QList<int>() << -2048);
const QList<int> elgKeySizes = config.readEntry(ELG_KEYSIZES_ENTRY, QList<int>() << -2048 << 3072 << 4096);
const QStringList rsaKeySizeLabels = config.readEntry(RSA_KEYSIZE_LABELS_ENTRY, QStringList());
const QStringList dsaKeySizeLabels = config.readEntry(DSA_KEYSIZE_LABELS_ENTRY, QStringList());
const QStringList elgKeySizeLabels = config.readEntry(ELG_KEYSIZE_LABELS_ENTRY, QStringList());
fill_combobox(*ui.rsaKeyStrengthCB, rsaKeySizes, rsaKeySizeLabels);
fill_combobox(*ui.rsaKeyStrengthSubCB, rsaKeySizes, rsaKeySizeLabels);
fill_combobox(*ui.dsaKeyStrengthCB, dsaKeySizes, dsaKeySizeLabels);
fill_combobox(*ui.elgKeyStrengthCB, elgKeySizes, elgKeySizeLabels);
if (mEdDSASupported) {
// If supported we recommend cv25519
ui.ecdsaKeyCurvesCB->addItem(QStringLiteral("ed25519"));
ui.ecdhKeyCurvesCB->addItem(QStringLiteral("cv25519"));
}
ui.ecdhKeyCurvesCB->addItems(curveNames);
ui.ecdsaKeyCurvesCB->addItems(curveNames);
}
// Try to load the default key type from GnuPG
void AdvancedSettingsDialog::loadDefaultGnuPGKeyType()
{
const auto conf = QGpgME::cryptoConfig();
if (!conf) {
qCWarning(KLEOPATRA_LOG) << "Failed to obtain cryptoConfig.";
return;
}
const auto entry = conf->entry(protocol == CMS ? QStringLiteral("gpgsm") : QStringLiteral("gpg"),
QStringLiteral("Configuration"),
QStringLiteral("default_pubkey_algo"));
if (!entry) {
qCDebug(KLEOPATRA_LOG) << "GnuPG does not have default key type. Fallback to RSA";
setKeyType(Subkey::AlgoRSA);
setSubkeyType(Subkey::AlgoRSA);
return;
}
qCDebug(KLEOPATRA_LOG) << "Have default key type: " << entry->stringValue();
// Format is <primarytype>[/usage]+<subkeytype>[/usage]
const auto split = entry->stringValue().split(QLatin1Char('+'));
int size = 0;
Subkey::PubkeyAlgo algo = Subkey::AlgoUnknown;
QString curve;
parseAlgoString(split[0], &size, &algo, curve);
if (algo == Subkey::AlgoUnknown) {
setSubkeyType(Subkey::AlgoRSA);
return;
}
setKeyType(algo);
if (is_rsa(algo) || is_elg(algo) || is_dsa(algo)) {
setKeyStrength(size);
} else {
setKeyCurve(curve);
}
if (split.size() == 2) {
auto algoString = split[1];
// If it has no usage we assume encrypt subkey
if (!algoString.contains(QLatin1Char('/'))) {
algoString += QStringLiteral("/enc");
}
parseAlgoString(algoString, &size, &algo, curve);
if (algo == Subkey::AlgoUnknown) {
setSubkeyType(Subkey::AlgoRSA);
return;
}
setSubkeyType(algo);
if (is_rsa(algo) || is_elg(algo)) {
setSubkeyStrength(size);
} else {
setSubkeyCurve(curve);
}
}
}
void AdvancedSettingsDialog::loadDefaultKeyType()
{
if (protocol != CMS && protocol != OpenPGP) {
return;
}
const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard");
const QString entry = protocol == CMS ? QLatin1String(CMS_KEY_TYPE_ENTRY) : QLatin1String(PGP_KEY_TYPE_ENTRY);
const QString keyType = config.readEntry(entry).trimmed().toUpper();
if (protocol == OpenPGP && keyType == QLatin1String("DSA")) {
setKeyType(Subkey::AlgoDSA);
setSubkeyType(Subkey::AlgoUnknown);
} else if (protocol == OpenPGP && keyType == QLatin1String("DSA+ELG")) {
setKeyType(Subkey::AlgoDSA);
setSubkeyType(Subkey::AlgoELG_E);
} else if (keyType.isEmpty() && engineIsVersion(2, 1, 17)) {
loadDefaultGnuPGKeyType();
} else {
if (!keyType.isEmpty() && keyType != QLatin1String("RSA"))
qCWarning(KLEOPATRA_LOG) << "invalid value \"" << qPrintable(keyType)
<< "\" for entry \"[CertificateCreationWizard]"
<< qPrintable(entry) << "\"";
setKeyType(Subkey::AlgoRSA);
setSubkeyType(Subkey::AlgoRSA);
}
keyTypeImmutable = config.isEntryImmutable(entry);
updateWidgetVisibility();
}
void AdvancedSettingsDialog::updateWidgetVisibility()
{
// Personal Details Page
if (protocol == OpenPGP) { // ### hide until multi-uid is implemented
if (ui.tabWidget->indexOf(ui.personalTab) != -1) {
ui.tabWidget->removeTab(ui.tabWidget->indexOf(ui.personalTab));
}
} else {
if (ui.tabWidget->indexOf(ui.personalTab) == -1) {
ui.tabWidget->addTab(ui.personalTab, tr2i18n("Personal Details", nullptr));
}
}
ui.uidGB->setVisible(protocol == OpenPGP);
ui.uidGB->setEnabled(false);
ui.uidGB->setToolTip(i18nc("@info:tooltip", "Adding more than one User ID is not yet implemented."));
ui.emailGB->setVisible(protocol == CMS);
ui.dnsGB->setVisible(protocol == CMS);
ui.uriGB->setVisible(protocol == CMS);
ui.ecdhCB->setVisible(mECCSupported);
ui.ecdhKeyCurvesCB->setVisible(mECCSupported);
ui.ecdsaKeyCurvesCB->setVisible(mECCSupported);
ui.ecdsaRB->setVisible(mECCSupported);
if (mEdDSASupported) {
// We use the same radio button for EdDSA as we use for
// ECDSA GnuPG does the same and this is really super technical
// land.
ui.ecdsaRB->setText(QStringLiteral("ECDSA/EdDSA"));
}
bool deVsHack = Kleo::gpgComplianceP("de-vs");
if (deVsHack) {
// GnuPG Provides no API to query which keys are compliant for
// a mode. If we request a different one it will error out so
// we have to remove the options.
//
// Does anyone want to use NIST anyway?
int i;
while ((i = ui.ecdsaKeyCurvesCB->findText(QStringLiteral("NIST"), Qt::MatchStartsWith)) != -1 ||
(i = ui.ecdsaKeyCurvesCB->findText(QStringLiteral("25519"), Qt::MatchEndsWith)) != -1) {
ui.ecdsaKeyCurvesCB->removeItem(i);
}
while ((i = ui.ecdhKeyCurvesCB->findText(QStringLiteral("NIST"), Qt::MatchStartsWith)) != -1 ||
(i = ui.ecdhKeyCurvesCB->findText(QStringLiteral("25519"), Qt::MatchEndsWith)) != -1) {
ui.ecdhKeyCurvesCB->removeItem(i);
}
}
// Technical Details Page
if (keyTypeImmutable) {
ui.rsaRB->setEnabled(false);
ui.rsaSubCB->setEnabled(false);
ui.dsaRB->setEnabled(false);
ui.elgCB->setEnabled(false);
ui.ecdsaRB->setEnabled(false);
ui.ecdhCB->setEnabled(false);
} else {
ui.rsaRB->setEnabled(true);
ui.rsaSubCB->setEnabled(protocol == OpenPGP);
ui.dsaRB->setEnabled(protocol == OpenPGP && !deVsHack);
ui.elgCB->setEnabled(protocol == OpenPGP && !deVsHack);
ui.ecdsaRB->setEnabled(protocol == OpenPGP);
ui.ecdhCB->setEnabled(protocol == OpenPGP);
}
ui.certificationCB->setVisible(protocol == OpenPGP); // gpgsm limitation?
ui.authenticationCB->setVisible(protocol == OpenPGP);
if (protocol == OpenPGP) { // pgp keys must have certify capability
ui.certificationCB->setChecked(true);
ui.certificationCB->setEnabled(false);
}
if (protocol == CMS) {
ui.encryptionCB->setEnabled(true);
ui.rsaSubCB->setChecked(false);
ui.rsaKeyStrengthSubCB->setEnabled(false);
}
ui.expiryDE->setVisible(protocol == OpenPGP);
ui.expiryCB->setVisible(protocol == OpenPGP);
slotKeyMaterialSelectionChanged();
}
#include "newcertificatewizard.moc"

File Metadata

Mime Type
text/x-diff
Expires
Sun, Dec 28, 10:36 PM (2 h, 56 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
0d/a5/d44ae69c67c6016967598a073779

Event Timeline