Page MenuHome GnuPG

No OneTemporary

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4b31b47ad..e5254fcfb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,178 +1,179 @@
set(kleopatra_version 3.1.16)
# The following is for Windows. Keep in line with kleopatra_version.
set(kleopatra_fileversion 3,1,16,0)
cmake_minimum_required(VERSION 3.16 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.07.40")
endif()
option(DISABLE_KWATCHGNUPG "Don't build the kwatchgnupg tool [default=OFF]" OFF)
# Standalone build. Find / include everything necessary.
set(KF5_MIN_VERSION "5.83.0")
set(KMIME_VERSION "5.17.40")
set(LIBKLEO_VERSION "5.17.47")
set(QT_REQUIRED_VERSION "5.15.0")
set(GPGME_REQUIRED_VERSION "1.13.1")
set(BOOST_REQUIRED_VERSION "1.58")
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
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"
PURPOSE "Required to generate Kleopatra documentation."
TYPE OPTIONAL)
# Optional packages
if (WIN32)
# Only a replacement available for Windows so this
# is required on other platforms.
find_package(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)
if (Gpgmepp_VERSION VERSION_GREATER_EQUAL "1.15.2")
set(GPGMEPP_SUPPORTS_TRUST_SIGNATURES 1)
endif()
find_package(QGpgme ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.15.2")
set(QGPGME_SUPPORTS_TRUST_SIGNATURES 1)
+ set(QGPGME_SUPPORTS_SIGNATURE_EXPIRATION 1)
endif()
# 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(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Boost ${BOOST_REQUIRED_VERSION} MODULE 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_DIRS}
${ASSUAN2_INCLUDES}
)
add_definitions(-D_ASSUAN_ONLY_GPG_ERRORS)
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050e00)
add_definitions(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x055400)
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)" OFF)
set(COMPILE_WITH_UNITY_CMAKE_SUPPORT OFF)
if (USE_UNITY_CMAKE_SUPPORT)
set(COMPILE_WITH_UNITY_CMAKE_SUPPORT ON)
endif()
add_subdirectory(pics)
add_subdirectory(src)
if(BUILD_TESTING)
add_subdirectory(tests)
add_subdirectory(autotests)
endif()
ecm_qt_install_logging_categories(
EXPORT KLEOPATRA
FILE kleopatra.categories
DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
)
ki18n_install(po)
if(KF5DocTools_FOUND)
kdoctools_install(po)
add_subdirectory(doc)
endif()
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/config-kleopatra.h.cmake b/config-kleopatra.h.cmake
index caa9dbb5d..269c310ba 100644
--- a/config-kleopatra.h.cmake
+++ b/config-kleopatra.h.cmake
@@ -1,31 +1,34 @@
/* Define to 1 if you have a recent enough libassuan */
#cmakedefine HAVE_USABLE_ASSUAN 1
/* Define to 1 if you have libassuan v2 */
#cmakedefine HAVE_ASSUAN2 1
#ifndef HAVE_ASSUAN2
/* Define to 1 if your libassuan has the assuan_fd_t type */
#cmakedefine HAVE_ASSUAN_FD_T 1
/* Define to 1 if your libassuan has the assuan_inquire_ext function */
#cmakedefine HAVE_ASSUAN_INQUIRE_EXT 1
/* Define to 1 if your assuan_inquire_ext puts the buffer arguments into the callback signature */
#cmakedefine HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT 1
/* Define to 1 if your libassuan has the assuan_sock_get_nonce function */
#cmakedefine HAVE_ASSUAN_SOCK_GET_NONCE 1
#endif
/* Define to 1 if you build libkleopatraclient */
#cmakedefine HAVE_KLEOPATRACLIENT_LIBRARY 1
/* DBus available */
#cmakedefine01 HAVE_QDBUS
/* Defined if GpgME++ supports trust signatures */
#cmakedefine GPGMEPP_SUPPORTS_TRUST_SIGNATURES 1
/* Defined if QGpgME supports trust signatures */
#cmakedefine QGPGME_SUPPORTS_TRUST_SIGNATURES 1
+
+/* Defined if QGpgME supports setting an expiration data for signatures */
+#cmakedefine QGPGME_SUPPORTS_SIGNATURE_EXPIRATION 1
diff --git a/src/commands/certifycertificatecommand.cpp b/src/commands/certifycertificatecommand.cpp
index 93dfda90b..1c0bbda6d 100644
--- a/src/commands/certifycertificatecommand.cpp
+++ b/src/commands/certifycertificatecommand.cpp
@@ -1,326 +1,332 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/signcertificatecommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2019 g10code GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "certifycertificatecommand.h"
#include "newcertificatecommand.h"
#include "command_p.h"
#include "exportopenpgpcertstoservercommand.h"
#include "dialogs/certifycertificatedialog.h"
#include "utils/tags.h"
#include <Libkleo/KeyCache>
#include <Libkleo/Formatting>
#include <QGpgME/Protocol>
#include <QGpgME/SignKeyJob>
+#include <QDate>
#include <QEventLoop>
#include <gpgme++/key.h>
#include <KLocalizedString>
#include "kleopatra_debug.h"
#include <gpgme++/gpgmepp_version.h>
#if GPGMEPP_VERSION >= 0x10E00 // 1.14.0
# define GPGME_HAS_REMARKS
#endif
using namespace Kleo;
using namespace Kleo::Commands;
using namespace GpgME;
using namespace QGpgME;
class CertifyCertificateCommand::Private : public Command::Private
{
friend class ::Kleo::Commands::CertifyCertificateCommand;
CertifyCertificateCommand *q_func() const
{
return static_cast<CertifyCertificateCommand *>(q);
}
public:
explicit Private(CertifyCertificateCommand *qq, KeyListController *c);
~Private();
void init();
private:
void slotDialogRejected();
void slotResult(const Error &err);
void slotCertificationPrepared();
private:
void ensureDialogCreated();
void createJob();
private:
std::vector<UserID> uids;
QPointer<CertifyCertificateDialog> dialog;
QPointer<QGpgME::SignKeyJob> job;
};
CertifyCertificateCommand::Private *CertifyCertificateCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const CertifyCertificateCommand::Private *CertifyCertificateCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
CertifyCertificateCommand::Private::Private(CertifyCertificateCommand *qq, KeyListController *c)
: Command::Private(qq, c),
uids(),
dialog(),
job()
{
}
CertifyCertificateCommand::Private::~Private()
{
qCDebug(KLEOPATRA_LOG);
if (dialog) {
delete dialog;
dialog = nullptr;
}
}
CertifyCertificateCommand::CertifyCertificateCommand(KeyListController *c)
: Command(new Private(this, c))
{
d->init();
}
CertifyCertificateCommand::CertifyCertificateCommand(QAbstractItemView *v, KeyListController *c)
: Command(v, new Private(this, c))
{
d->init();
}
CertifyCertificateCommand::CertifyCertificateCommand(const GpgME::Key &key)
: Command(key, new Private(this, nullptr))
{
d->init();
}
CertifyCertificateCommand::CertifyCertificateCommand(const GpgME::UserID &uid)
: Command(uid.parent(), new Private(this, nullptr))
{
std::vector<UserID>(1, uid).swap(d->uids);
d->init();
}
CertifyCertificateCommand::CertifyCertificateCommand(const std::vector<GpgME::UserID> &uids)
: Command(uids.empty() ? Key() : uids.front().parent(), new Private(this, nullptr))
{
d->uids = uids;
d->init();
}
void CertifyCertificateCommand::Private::init()
{
}
CertifyCertificateCommand::~CertifyCertificateCommand()
{
qCDebug(KLEOPATRA_LOG);
}
void CertifyCertificateCommand::doStart()
{
const std::vector<Key> keys = d->keys();
if (keys.size() != 1 ||
keys.front().protocol() != GpgME::OpenPGP) {
d->finished();
return;
}
std::vector<Key> secKeys;
Q_FOREACH (const Key &secKey, KeyCache::instance()->secretKeys()) {
// Only include usable keys.
if (secKey.canCertify() && secKey.protocol() == OpenPGP && !secKey.isRevoked() &&
!secKey.isExpired() && !secKey.isInvalid()) {
secKeys.push_back(secKey);
}
}
if (secKeys.empty()) {
auto sel = KMessageBox::questionYesNo(d->parentWidgetOrView(),
xi18nc("@info", "To certify other certificates, you first need to create an OpenPGP certificate for yourself.") +
QStringLiteral("<br><br>") +
i18n("Do you wish to create one now?"),
i18n("Certification Not Possible"));
if (sel == KMessageBox::Yes) {
QEventLoop loop;
auto cmd = new Commands::NewCertificateCommand();
cmd->setParentWidget(d->parentWidgetOrView());
cmd->setProtocol(GpgME::OpenPGP);
loop.connect(cmd, SIGNAL(finished()), SLOT(quit()));
QMetaObject::invokeMethod(cmd, &Commands::NewCertificateCommand::start, Qt::QueuedConnection);
loop.exec();
} else {
Q_EMIT(canceled());
d->finished();
return;
}
Q_FOREACH (const Key &secKey, KeyCache::instance()->secretKeys()) {
// Check again for secret keys
if (secKey.canCertify() && secKey.protocol() == OpenPGP && !secKey.isRevoked() &&
!secKey.isExpired() && !secKey.isInvalid()) {
secKeys.push_back(secKey);
}
}
if (secKeys.empty()) {
qCDebug(KLEOPATRA_LOG) << "Sec Keys still empty after keygen.";
Q_EMIT(canceled());
d->finished();
return;
}
}
const Key &key = keys.front();
for (const UserID &uid : qAsConst(d->uids))
if (qstricmp(uid.parent().primaryFingerprint(), key.primaryFingerprint()) != 0) {
qCWarning(KLEOPATRA_LOG) << "User-ID <-> Key mismatch!";
d->finished();
return;
}
d->ensureDialogCreated();
Q_ASSERT(d->dialog);
Key target = d->key();
#ifdef GPGME_HAS_REMARKS
if (!(target.keyListMode() & GpgME::SignatureNotations)) {
target.update();
}
#endif
d->dialog->setCertificateToCertify(target);
if (d->uids.size()) {
d->dialog->setSelectedUserIDs(d->uids);
}
d->dialog->show();
}
void CertifyCertificateCommand::Private::slotDialogRejected()
{
Q_EMIT q->canceled();
finished();
}
void CertifyCertificateCommand::Private::slotResult(const Error &err)
{
if (!err && !err.isCanceled() && dialog && dialog->exportableCertificationSelected() && dialog->sendToServer()) {
auto const cmd = new ExportOpenPGPCertsToServerCommand(key());
cmd->start();
} else if (!err) {
information(i18n("Certification successful."),
i18n("Certification Succeeded"));
} else {
error(i18n("<p>An error occurred while trying to certify<br/><br/>"
"<b>%1</b>:</p><p>\t%2</p>",
Formatting::formatForComboBox(key()),
QString::fromUtf8(err.asString())),
i18n("Certification Error"));
}
if (!dialog->tags().isEmpty()) {
Tags::enableTags();
}
finished();
}
void CertifyCertificateCommand::Private::slotCertificationPrepared()
{
Q_ASSERT(dialog);
createJob();
Q_ASSERT(job);
job->setExportable(dialog->exportableCertificationSelected());
job->setNonRevocable(dialog->nonRevocableCertificationSelected());
job->setUserIDsToSign(dialog->selectedUserIDs());
job->setSigningKey(dialog->selectedSecretKey());
job->setCheckLevel(dialog->selectedCheckLevel());
#ifdef GPGME_HAS_REMARKS
if (!dialog->tags().isEmpty()) {
// do not set an empty remark to avoid an empty signature notation (GnuPG bug T5142)
job->setRemark(dialog->tags());
}
// This also came with 1.14.0
job->setDupeOk(true);
#endif
#ifdef QGPGME_SUPPORTS_TRUST_SIGNATURES
if (dialog->trustSignatureSelected() && !dialog->trustSignatureDomain().isEmpty()) {
// always create level 1 trust signatures with complete trust
job->setTrustSignature(TrustSignatureTrust::Complete, 1, dialog->trustSignatureDomain());
}
#endif
+#ifdef QGPGME_SUPPORTS_SIGNATURE_EXPIRATION
+ if (!dialog->expirationDate().isNull()) {
+ job->setExpirationDate(dialog->expirationDate());
+ }
+#endif
if (const Error err = job->start(key())) {
slotResult(err);
}
}
void CertifyCertificateCommand::doCancel()
{
qCDebug(KLEOPATRA_LOG);
if (d->job) {
d->job->slotCancel();
}
}
void CertifyCertificateCommand::Private::ensureDialogCreated()
{
if (dialog) {
return;
}
dialog = new CertifyCertificateDialog;
applyWindowID(dialog);
connect(dialog, SIGNAL(rejected()), q, SLOT(slotDialogRejected()));
connect(dialog, SIGNAL(accepted()), q, SLOT(slotCertificationPrepared()));
}
void CertifyCertificateCommand::Private::createJob()
{
Q_ASSERT(!job);
Q_ASSERT(key().protocol() == OpenPGP);
const auto backend = QGpgME::openpgp();
if (!backend) {
return;
}
SignKeyJob *const j = backend->signKeyJob();
if (!j) {
return;
}
connect(j, &Job::progress,
q, &Command::progress);
connect(j, SIGNAL(result(GpgME::Error)),
q, SLOT(slotResult(GpgME::Error)));
job = j;
}
#undef d
#undef q
#include "moc_certifycertificatecommand.cpp"
diff --git a/src/dialogs/certifycertificatedialog.cpp b/src/dialogs/certifycertificatedialog.cpp
index a7cb97072..b9494d7d0 100644
--- a/src/dialogs/certifycertificatedialog.cpp
+++ b/src/dialogs/certifycertificatedialog.cpp
@@ -1,156 +1,161 @@
/* -*- mode: c++; c-basic-offset:4 -*-
dialogs/signcertificatedialog.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2019 g10code GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "kleopatra_debug.h"
#include "certifycertificatedialog.h"
#include "certifywidget.h"
#include <Libkleo/Formatting>
#include <Libkleo/Stl_Util>
#include <KLocalizedString>
#include <KStandardGuiItem>
#include <KSharedConfig>
#include <KConfigGroup>
#include <KMessageBox>
#include <QVBoxLayout>
#include <QDialogButtonBox>
#include <QPushButton>
#include <gpg-error.h>
using namespace GpgME;
using namespace Kleo;
CertifyCertificateDialog::CertifyCertificateDialog(QWidget *p, Qt::WindowFlags f)
: QDialog(p, f)
{
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
// Setup GUI
auto mainLay = new QVBoxLayout(this);
mCertWidget = new CertifyWidget(this);
mainLay->addWidget(mCertWidget);
auto buttonBox = new QDialogButtonBox{this};
buttonBox->setStandardButtons(QDialogButtonBox::Cancel |
QDialogButtonBox::Ok);
const auto okButton = buttonBox->button(QDialogButtonBox::Ok);
KGuiItem::assign(okButton, KStandardGuiItem::ok());
okButton->setText(i18n("Certify"));
KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
connect(buttonBox, &QDialogButtonBox::accepted, this, &CertifyCertificateDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::close);
mainLay->addWidget(buttonBox);
okButton->setEnabled(mCertWidget->isValid());
connect(mCertWidget, &CertifyWidget::changed, this, [this, okButton] () {
okButton->setEnabled(mCertWidget->isValid());
});
KConfigGroup cfgGroup(KSharedConfig::openStateConfig(), "CertifyDialog");
const QByteArray geom = cfgGroup.readEntry("geometry", QByteArray());
if (!geom.isEmpty()) {
restoreGeometry(geom);
return;
}
resize(QSize(640, 480));
}
CertifyCertificateDialog::~CertifyCertificateDialog()
{
KConfigGroup cfgGroup(KSharedConfig::openStateConfig(), "CertifyDialog");
cfgGroup.writeEntry("geometry", saveGeometry());
cfgGroup.sync();
}
void CertifyCertificateDialog::setCertificateToCertify(const Key &key)
{
setWindowTitle(i18nc("@title:window arg is name, email of certificate holder", "Certify Certificate: %1", Formatting::prettyName(key)));
mCertWidget->setTarget(key);
}
bool CertifyCertificateDialog::exportableCertificationSelected() const
{
return mCertWidget->exportableSelected();
}
bool CertifyCertificateDialog::trustSignatureSelected() const
{
return mCertWidget->trustSignatureSelected();
}
QString CertifyCertificateDialog::trustSignatureDomain() const
{
return mCertWidget->trustSignatureDomain();
}
bool CertifyCertificateDialog::nonRevocableCertificationSelected() const
{
return false;
}
Key CertifyCertificateDialog::selectedSecretKey() const
{
return mCertWidget->secKey();
}
bool CertifyCertificateDialog::sendToServer() const
{
return mCertWidget->publishSelected();
}
unsigned int CertifyCertificateDialog::selectedCheckLevel() const
{
//PENDING
#ifdef KLEO_SIGN_KEY_CERTLEVEL_SUPPORT
return d->selectCheckLevelPage->checkLevel();
#endif
return 0;
}
void CertifyCertificateDialog::setSelectedUserIDs(const std::vector<UserID> &uids)
{
mCertWidget->selectUserIDs(uids);
}
std::vector<unsigned int> CertifyCertificateDialog::selectedUserIDs() const
{
return mCertWidget->selectedUserIDs();
}
QString CertifyCertificateDialog::tags() const
{
return mCertWidget->tags();
}
+QDate CertifyCertificateDialog::expirationDate() const
+{
+ return mCertWidget->expirationDate();
+}
+
void CertifyCertificateDialog::accept()
{
if (!mCertWidget->isValid()) {
return;
}
KConfigGroup conf(KSharedConfig::openConfig(), "CertifySettings");
const auto lastKey = mCertWidget->secKey();
if (!lastKey.isNull()) {
conf.writeEntry("LastKey", lastKey.primaryFingerprint());
}
conf.writeEntry("ExportCheckState", mCertWidget->exportableSelected());
conf.writeEntry("PublishCheckState", mCertWidget->publishSelected());
QDialog::accept();
}
diff --git a/src/dialogs/certifycertificatedialog.h b/src/dialogs/certifycertificatedialog.h
index d43eb0db4..ca0fbfd95 100644
--- a/src/dialogs/certifycertificatedialog.h
+++ b/src/dialogs/certifycertificatedialog.h
@@ -1,61 +1,63 @@
/* -*- mode: c++; c-basic-offset:4 -*-
dialogs/signcertificatedialog.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QWizard>
#include <QGpgME/SignKeyJob>
#include <gpgme++/key.h>
#include <utils/pimpl_ptr.h>
namespace Kleo
{
class CertifyWidget;
class CertifyCertificateDialog : public QDialog
{
Q_OBJECT
public:
explicit CertifyCertificateDialog(QWidget *parent = nullptr, Qt::WindowFlags f = {});
~CertifyCertificateDialog();
bool exportableCertificationSelected() const;
bool trustSignatureSelected() const;
QString trustSignatureDomain() const;
bool nonRevocableCertificationSelected() const;
void setSelectedUserIDs(const std::vector<GpgME::UserID> &uids);
std::vector<unsigned int> selectedUserIDs() const;
GpgME::Key selectedSecretKey() const;
bool sendToServer() const;
unsigned int selectedCheckLevel() const;
void setCertificateToCertify(const GpgME::Key &key);
QString tags() const;
+ QDate expirationDate() const;
+
public Q_SLOTS:
void accept() override;
private:
CertifyWidget *mCertWidget;
};
}
diff --git a/src/dialogs/certifywidget.cpp b/src/dialogs/certifywidget.cpp
index f6851d863..2a705f03c 100644
--- a/src/dialogs/certifywidget.cpp
+++ b/src/dialogs/certifywidget.cpp
@@ -1,604 +1,678 @@
/* dialogs/certifywidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2019 g 10code GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "certifywidget.h"
#include "kleopatra_debug.h"
#include <KLocalizedString>
#include <KConfigGroup>
+#include <KDateComboBox>
#include <KSeparator>
#include <KSharedConfig>
#include <Libkleo/DefaultKeyFilter>
#include <Libkleo/KeySelectionCombo>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/Predicates>
#include <QCheckBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QListView>
#include <QParallelAnimationGroup>
#include <QPropertyAnimation>
#include <QPushButton>
#include <QScrollArea>
#include <QStandardItemModel>
#include <QToolButton>
#include <QToolTip>
#include <QVBoxLayout>
#include <gpgme++/key.h>
#include <gpgme++/gpgmepp_version.h>
#if GPGMEPP_VERSION >= 0x10E00 // 1.14.0
# define GPGME_HAS_REMARKS
#endif
using namespace Kleo;
namespace {
// Maybe move this in its own file
// based on code from StackOverflow
class AnimatedExpander: public QWidget
{
Q_OBJECT
public:
explicit AnimatedExpander(const QString &title = QString(),
QWidget *parent = nullptr);
void setContentLayout(QLayout *contentLayout);
private:
QGridLayout mainLayout;
QToolButton toggleButton;
QFrame headerLine;
QParallelAnimationGroup toggleAnimation;
QScrollArea contentArea;
int animationDuration{300};
};
AnimatedExpander::AnimatedExpander(const QString &title, QWidget *parent):
QWidget(parent)
{
toggleButton.setStyleSheet(QStringLiteral("QToolButton { border: none; }"));
toggleButton.setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toggleButton.setArrowType(Qt::ArrowType::RightArrow);
toggleButton.setText(title);
toggleButton.setCheckable(true);
toggleButton.setChecked(false);
headerLine.setFrameShape(QFrame::HLine);
headerLine.setFrameShadow(QFrame::Sunken);
headerLine.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
contentArea.setStyleSheet(QStringLiteral("QScrollArea { border: none; }"));
contentArea.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
// start out collapsed
contentArea.setMaximumHeight(0);
contentArea.setMinimumHeight(0);
// let the entire widget grow and shrink with its content
toggleAnimation.addAnimation(new QPropertyAnimation(this, "minimumHeight"));
toggleAnimation.addAnimation(new QPropertyAnimation(this, "maximumHeight"));
toggleAnimation.addAnimation(new QPropertyAnimation(&contentArea, "maximumHeight"));
mainLayout.setVerticalSpacing(0);
mainLayout.setContentsMargins(0, 0, 0, 0);
int row = 0;
mainLayout.addWidget(&toggleButton, row, 0, 1, 1, Qt::AlignLeft);
mainLayout.addWidget(&headerLine, row++, 2, 1, 1);
mainLayout.addWidget(&contentArea, row, 0, 1, 3);
setLayout(&mainLayout);
QObject::connect(&toggleButton, &QToolButton::clicked, [this](const bool checked) {
toggleButton.setArrowType(checked ? Qt::ArrowType::DownArrow : Qt::ArrowType::RightArrow);
toggleAnimation.setDirection(checked ? QAbstractAnimation::Forward : QAbstractAnimation::Backward);
toggleAnimation.start();
});
}
void AnimatedExpander::setContentLayout(QLayout *contentLayout)
{
delete contentArea.layout();
contentArea.setLayout(contentLayout);
const auto collapsedHeight = sizeHint().height() - contentArea.maximumHeight();
auto contentHeight = contentLayout->sizeHint().height();
for (int i = 0; i < toggleAnimation.animationCount() - 1; ++i) {
auto expanderAnimation = static_cast<QPropertyAnimation *>(toggleAnimation.animationAt(i));
expanderAnimation->setDuration(animationDuration);
expanderAnimation->setStartValue(collapsedHeight);
expanderAnimation->setEndValue(collapsedHeight + contentHeight);
}
auto contentAnimation = static_cast<QPropertyAnimation *>(toggleAnimation.animationAt(toggleAnimation.animationCount() - 1));
contentAnimation->setDuration(animationDuration);
contentAnimation->setStartValue(0);
contentAnimation->setEndValue(contentHeight);
}
class SecKeyFilter: public DefaultKeyFilter
{
public:
SecKeyFilter() : DefaultKeyFilter()
{
setRevoked(DefaultKeyFilter::NotSet);
setExpired(DefaultKeyFilter::NotSet);
setHasSecret(DefaultKeyFilter::Set);
setCanCertify(DefaultKeyFilter::Set);
setIsOpenPGP(DefaultKeyFilter::Set);
}
bool matches(const GpgME::Key &key, Kleo::KeyFilter::MatchContexts contexts) const override
{
if (!(availableMatchContexts() & contexts)) {
return false;
}
if (_detail::ByFingerprint<std::equal_to>()(key, mExcludedKey)) {
return false;
}
return DefaultKeyFilter::matches(key, contexts);
}
void setExcludedKey(const GpgME::Key &key)
{
mExcludedKey = key;
}
private:
GpgME::Key mExcludedKey;
};
class UserIDModel : public QStandardItemModel
{
Q_OBJECT
public:
enum Role {
UserIDIndex = Qt::UserRole
};
explicit UserIDModel(QObject *parent = nullptr) : QStandardItemModel(parent) {}
GpgME::Key certificateToCertify() const
{
return m_key;
}
void setKey(const GpgME::Key &key)
{
m_key = key;
clear();
const std::vector<GpgME::UserID> ids = key.userIDs();
int i = 0;
for (const auto &uid: key.userIDs()) {
if (uid.isRevoked() || uid.isInvalid()) {
// Skip user ID's that cannot really be certified.
i++;
continue;
}
auto const item = new QStandardItem;
item->setText(Formatting::prettyUserID(uid));
item->setData(i, UserIDIndex);
item->setCheckable(true);
item->setEditable(false);
item->setCheckState(Qt::Checked);
appendRow(item);
i++;
}
}
void setCheckedUserIDs(const std::vector<unsigned int> &uids)
{
std::vector<unsigned int> sorted = uids;
std::sort(sorted.begin(), sorted.end());
for (int i = 0, end = rowCount(); i != end; ++i) {
item(i)->setCheckState(std::binary_search(sorted.begin(), sorted.end(), i) ? Qt::Checked : Qt::Unchecked);
}
}
std::vector<unsigned int> checkedUserIDs() const
{
std::vector<unsigned int> ids;
for (int i = 0; i < rowCount(); ++i) {
if (item(i)->checkState() == Qt::Checked) {
ids.push_back(item(i)->data(UserIDIndex).toUInt());
}
}
qCDebug(KLEOPATRA_LOG) << "Checked uids are: " << ids;
return ids;
}
private:
GpgME::Key m_key;
};
static bool uidEqual(const GpgME::UserID &lhs, const GpgME::UserID &rhs)
{
return qstrcmp(lhs.parent().primaryFingerprint(),
rhs.parent().primaryFingerprint()) == 0
&& qstrcmp(lhs.id(), rhs.id()) == 0;
}
auto checkBoxSize(const QCheckBox *checkBox)
{
QStyleOptionButton opt;
return checkBox->style()->sizeFromContents(QStyle::CT_CheckBox, &opt, QSize(), checkBox);
}
auto createInfoButton(const QString &text, QWidget *parent)
{
auto infoBtn = new QPushButton{parent};
infoBtn->setIcon(QIcon::fromTheme(QStringLiteral("help-contextual")));
infoBtn->setFlat(true);
QObject::connect(infoBtn, &QPushButton::clicked, infoBtn, [infoBtn, text] () {
QToolTip::showText(infoBtn->mapToGlobal(QPoint()) + QPoint(infoBtn->width(), 0),
text, infoBtn, QRect(), 30000);
});
return infoBtn;
}
+#ifdef QGPGME_SUPPORTS_SIGNATURE_EXPIRATION
+QString dateFormatWithFourDigitYear(QLocale::FormatType format)
+{
+ // Force the year to be formatted as four digit number, so that
+ // the user can distinguish between 2006 and 2106.
+ return QLocale{}.dateFormat(format).
+ replace(QLatin1String("yy"), QLatin1String("yyyy")).
+ replace(QLatin1String("yyyyyyyy"), QLatin1String("yyyy"));
+}
+
+QString formatDate(const QDate &date, QLocale::FormatType format)
+{
+ return QLocale{}.toString(date, dateFormatWithFourDigitYear(format));
+}
+#endif
+
}
class CertifyWidget::Private
{
public:
Private(CertifyWidget *qq)
: q{qq}
, mFprLabel{new QLabel{q}}
, mSecKeySelect{new KeySelectionCombo{/* secretOnly= */true, q}}
, mExportCB{new QCheckBox{q}}
, mPublishCB{new QCheckBox{q}}
, mTagsLE{new QLineEdit{q}}
, mTrustSignatureCB{new QCheckBox{q}}
, mTrustSignatureDomainLE{new QLineEdit{q}}
+ , mExpirationCheckBox{new QCheckBox{q}}
+ , mExpirationDateEdit{new KDateComboBox{q}}
{
auto mainLay = new QVBoxLayout{q};
mainLay->addWidget(mFprLabel);
auto secKeyLay = new QHBoxLayout{q};
secKeyLay->addWidget(new QLabel(i18n("Certify with:")));
mSecKeySelect->setKeyFilter(std::make_shared<SecKeyFilter>());
secKeyLay->addWidget(mSecKeySelect, 1);
mainLay->addLayout(secKeyLay);
mainLay->addWidget(new KSeparator{Qt::Horizontal, q});
auto listView = new QListView{q};
listView->setModel(&mUserIDModel);
mainLay->addWidget(listView, 1);
// Setup the advanced area
auto expander = new AnimatedExpander{i18n("Advanced"), q};
mainLay->addWidget(expander);
auto advLay = new QVBoxLayout{q};
mExportCB->setText(i18n("Certify for everyone to see (exportable)"));
advLay->addWidget(mExportCB);
{
auto layout = new QHBoxLayout{q};
mPublishCB->setText(i18n("Publish on keyserver afterwards"));
mPublishCB->setEnabled(mExportCB->isChecked());
layout->addSpacing(checkBoxSize(mExportCB).width());
layout->addWidget(mPublishCB);
advLay->addLayout(layout);
}
#ifndef GPGME_HAS_REMARKS
mTagsLE->setVisible(false);
#else
{
auto tagsLay = new QHBoxLayout{q};
mTagsLE->setPlaceholderText(i18n("Tags"));
auto infoBtn = createInfoButton(i18n("You can use this to add additional info to a certification.") +
QStringLiteral("<br/><br/>") +
i18n("Tags created by anyone with full certification trust "
"are shown in the keylist and can be searched."),
q);
tagsLay->addWidget(new QLabel{i18n("Tags:"), q});
tagsLay->addWidget(mTagsLE, 1);
tagsLay->addWidget(infoBtn);
advLay->addLayout(tagsLay);
}
#endif
+#ifndef QGPGME_SUPPORTS_SIGNATURE_EXPIRATION
+ mExpirationCheckBox->setVisible(false);
+ mExpirationDateEdit->setVisible(false);
+#else
+ {
+ auto layout = new QHBoxLayout{q};
+
+ mExpirationCheckBox->setText(i18n("Expiration:"));
+
+ mExpirationDateEdit->setOptions(KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker |
+ KDateComboBox::DateKeywords | KDateComboBox::WarnOnInvalid);
+ static const QDate maxAllowedDate{2106, 2, 6};
+ const QDate today = QDate::currentDate();
+ mExpirationDateEdit->setDateRange(today.addDays(1), maxAllowedDate,
+ i18n("The certification must be valid at least until tomorrow."),
+ i18n("The latest allowed certification date is %1.",
+ formatDate(maxAllowedDate, QLocale::ShortFormat)));
+ mExpirationDateEdit->setDateMap({
+ {today.addYears(2), i18nc("Date for expiration of certification", "Two years from now")},
+ {today.addYears(1), i18nc("Date for expiration of certification", "One year from now")}
+ });
+ mExpirationDateEdit->setDate(today.addYears(2));
+ mExpirationDateEdit->setEnabled(mExpirationCheckBox->isChecked());
+
+ auto infoBtn = createInfoButton(i18n("You can use this to set an expiration date for a certification.") +
+ QStringLiteral("<br/><br/>") +
+ i18n("By setting an expiration date, you can limit the validity of "
+ "your certification to a certain amount of time. Once the expiration "
+ "date has passed, your certification is no longer valid."),
+ q);
+
+ layout->addWidget(mExpirationCheckBox);
+ layout->addWidget(mExpirationDateEdit, 1);
+ layout->addWidget(infoBtn);
+
+ advLay->addLayout(layout);
+ }
+#endif
+
#ifndef QGPGME_SUPPORTS_TRUST_SIGNATURES
mTrustSignatureCB->setVisible(false);
mTrustSignatureDomainLE->setVisible(false);
#else
{
auto layout = new QHBoxLayout{q};
mTrustSignatureCB->setText(i18n("Certify as trusted introducer"));
auto infoBtn = createInfoButton(i18n("You can use this to certify a trusted introducer for a domain.") +
QStringLiteral("<br/><br/>") +
i18n("All certificates with email addresses belonging to the domain "
"that have been certified by the trusted introducer are treated "
"as certified, i.e. a trusted introducer acts as a kind of "
"intermediate CA for a domain."),
q);
layout->addWidget(mTrustSignatureCB, 1);
layout->addWidget(infoBtn);
advLay->addLayout(layout);
}
{
auto layout = new QHBoxLayout{q};
mTrustSignatureDomainLE->setPlaceholderText(i18n("Domain"));
mTrustSignatureDomainLE->setEnabled(mTrustSignatureCB->isChecked());
layout->addSpacing(checkBoxSize(mTrustSignatureCB).width());
layout->addWidget(mTrustSignatureDomainLE);
advLay->addLayout(layout);
}
#endif
expander->setContentLayout(advLay);
connect(&mUserIDModel, &QStandardItemModel::itemChanged, q, &CertifyWidget::changed);
connect(mExportCB, &QCheckBox::toggled, [this] (bool on) {
mPublishCB->setEnabled(on);
});
connect(mSecKeySelect, &KeySelectionCombo::currentKeyChanged, [this] (const GpgME::Key &) {
#ifdef GPGME_HAS_REMARKS
updateTags();
#endif
Q_EMIT q->changed();
});
+ connect(mExpirationCheckBox, &QCheckBox::toggled, q, [this] (bool checked) {
+ mExpirationDateEdit->setEnabled(checked);
+ Q_EMIT q->changed();
+ });
+ connect(mExpirationDateEdit, &KDateComboBox::dateChanged, q, &CertifyWidget::changed);
+
connect(mTrustSignatureCB, &QCheckBox::toggled, q, [this] (bool on) {
mTrustSignatureDomainLE->setEnabled(on);
Q_EMIT q->changed();
});
connect(mTrustSignatureDomainLE, &QLineEdit::textChanged, q, &CertifyWidget::changed);
loadConfig();
}
~Private() = default;
void loadConfig()
{
const KConfigGroup conf(KSharedConfig::openConfig(), "CertifySettings");
mSecKeySelect->setDefaultKey(conf.readEntry("LastKey", QString()));
mExportCB->setChecked(conf.readEntry("ExportCheckState", false));
mPublishCB->setChecked(conf.readEntry("PublishCheckState", false));
}
void updateTags()
{
if (mTagsLE->isModified()) {
return;
}
#ifdef GPGME_HAS_REMARKS
GpgME::Key remarkKey = mSecKeySelect->currentKey();
if (!remarkKey.isNull()) {
std::vector<GpgME::UserID> uidsWithRemark;
QString remark;
for (const auto &uid: mTarget.userIDs()) {
GpgME::Error err;
const char *c_remark = uid.remark(remarkKey, err);
if (c_remark) {
const QString candidate = QString::fromUtf8(c_remark);
if (candidate != remark) {
qCDebug(KLEOPATRA_LOG) << "Different remarks on user ids. Taking last.";
remark = candidate;
uidsWithRemark.clear();
}
uidsWithRemark.push_back(uid);
}
}
// Only select the user ids with the correct remark
if (!remark.isEmpty()) {
selectUserIDs(uidsWithRemark);
}
mTagsLE->setText(remark);
}
#endif
}
void updateTrustSignatureDomain()
{
if (mTrustSignatureDomainLE->text().isEmpty() && mTarget.numUserIDs() == 1) {
// try to guess the domain to use for the trust signature
const auto address = mTarget.userID(0).addrSpec();
const auto atPos = address.find('@');
if (atPos != std::string::npos) {
const auto domain = address.substr(atPos + 1);
mTrustSignatureDomainLE->setText(QString::fromUtf8(domain.c_str(), domain.size()));
}
}
}
void setTarget(const GpgME::Key &key)
{
mFprLabel->setText(i18n("Fingerprint: <b>%1</b>",
Formatting::prettyID(key.primaryFingerprint())) + QStringLiteral("<br/>") +
i18n("<i>Only the fingerprint clearly identifies the key and its owner.</i>"));
mUserIDModel.setKey(key);
mTarget = key;
auto keyFilter = std::make_shared<SecKeyFilter>();
keyFilter->setExcludedKey(mTarget);
mSecKeySelect->setKeyFilter(keyFilter);
updateTags();
updateTrustSignatureDomain();
}
GpgME::Key secKey() const
{
return mSecKeySelect->currentKey();
}
void selectUserIDs(const std::vector<GpgME::UserID> &uids)
{
const auto all = mTarget.userIDs();
std::vector<unsigned int> indexes;
indexes.reserve(uids.size());
for (const auto &uid: uids) {
const unsigned int idx =
std::distance(all.cbegin(), std::find_if(all.cbegin(), all.cend(),
[uid](const GpgME::UserID &other) { return uidEqual(uid, other); }));
if (idx < all.size()) {
indexes.push_back(idx);
}
}
mUserIDModel.setCheckedUserIDs(indexes);
}
std::vector<unsigned int> selectedUserIDs() const
{
return mUserIDModel.checkedUserIDs();
}
bool exportableSelected() const
{
return mExportCB->isChecked();
}
bool publishSelected() const
{
return mPublishCB->isChecked();
}
QString tags() const
{
return mTagsLE->text().trimmed();
}
GpgME::Key target() const
{
return mTarget;
}
bool isValid() const
{
static const QRegularExpression domainNameRegExp{QStringLiteral(R"(^\s*((xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}\s*$)"),
QRegularExpression::CaseInsensitiveOption};
// do not accept null keys
if (mTarget.isNull() || mSecKeySelect->currentKey().isNull()) {
return false;
}
// do not accept empty list of user ids
if (selectedUserIDs().empty()) {
return false;
}
// do not accept if the key to certify is selected as certification key;
// this shouldn't happen because the key to certify is excluded from the choice, but better safe than sorry
if (_detail::ByFingerprint<std::equal_to>()(mTarget, mSecKeySelect->currentKey())) {
return false;
}
+ if (mExpirationCheckBox->isChecked() && !mExpirationDateEdit->isValid()) {
+ return false;
+ }
if (mTrustSignatureCB->isChecked() && !domainNameRegExp.match(mTrustSignatureDomainLE->text()).hasMatch()) {
return false;
}
return true;
}
public:
CertifyWidget *const q;
QLabel *mFprLabel = nullptr;
KeySelectionCombo *mSecKeySelect = nullptr;
QCheckBox *mExportCB = nullptr;
QCheckBox *mPublishCB = nullptr;
QLineEdit *mTagsLE = nullptr;
QCheckBox *mTrustSignatureCB = nullptr;
QLineEdit *mTrustSignatureDomainLE = nullptr;
+ QCheckBox *mExpirationCheckBox = nullptr;
+ KDateComboBox *mExpirationDateEdit = nullptr;
UserIDModel mUserIDModel;
GpgME::Key mTarget;
};
CertifyWidget::CertifyWidget(QWidget *parent)
: QWidget{parent}
, d{std::make_unique<Private>(this)}
{
}
Kleo::CertifyWidget::~CertifyWidget() = default;
void CertifyWidget::setTarget(const GpgME::Key &key)
{
d->setTarget(key);
}
GpgME::Key CertifyWidget::target() const
{
return d->target();
}
void CertifyWidget::selectUserIDs(const std::vector<GpgME::UserID> &uids)
{
d->selectUserIDs(uids);
}
std::vector<unsigned int> CertifyWidget::selectedUserIDs() const
{
return d->selectedUserIDs();
}
GpgME::Key CertifyWidget::secKey() const
{
return d->secKey();
}
bool CertifyWidget::exportableSelected() const
{
return d->exportableSelected();
}
QString CertifyWidget::tags() const
{
return d->tags();
}
bool CertifyWidget::publishSelected() const
{
return d->publishSelected();
}
bool CertifyWidget::trustSignatureSelected() const
{
return d->mTrustSignatureCB->isChecked();
}
QString CertifyWidget::trustSignatureDomain() const
{
return d->mTrustSignatureDomainLE->text().trimmed();
}
+QDate CertifyWidget::expirationDate() const
+{
+ return d->mExpirationCheckBox->isChecked() ? d->mExpirationDateEdit->date() : QDate{};
+}
+
bool CertifyWidget::isValid() const
{
return d->isValid();
}
// For UserID model
#include "certifywidget.moc"
diff --git a/src/dialogs/certifywidget.h b/src/dialogs/certifywidget.h
index f239e8a08..f3b53d0a6 100644
--- a/src/dialogs/certifywidget.h
+++ b/src/dialogs/certifywidget.h
@@ -1,73 +1,76 @@
#pragma once
/* dialogs/certifywidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2019 g 10code GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <QWidget>
#include <vector>
#include <memory>
namespace GpgME
{
class Key;
class UserID;
} // namespace GpgME
namespace Kleo
{
/** Widget for OpenPGP certification. */
class CertifyWidget : public QWidget
{
Q_OBJECT
public:
explicit CertifyWidget(QWidget *parent = nullptr);
~CertifyWidget() override;
/* Set the key to certify */
void setTarget(const GpgME::Key &key);
/* Get the key to certify */
GpgME::Key target() const;
/* Select specific user ids. Default: all */
void selectUserIDs(const std::vector<GpgME::UserID> &uids);
/* The user ids that should be signed */
std::vector<unsigned int> selectedUserIDs() const;
/* The secret key selected */
GpgME::Key secKey() const;
/* Should the signature be exportable */
bool exportableSelected() const;
/* Additional tags for the key */
QString tags() const;
/* Should the signed key be be published */
bool publishSelected() const;
/* Whether a trust signature should be created */
bool trustSignatureSelected() const;
/* The domain to use to limit the scope of the trust signature */
QString trustSignatureDomain() const;
+ /* The expiration date to use for the certification */
+ QDate expirationDate() const;
+
bool isValid() const;
Q_SIGNALS:
void changed() const;
private:
class Private;
std::unique_ptr<Private> d;
};
} // namespace Kleo

File Metadata

Mime Type
text/x-diff
Expires
Sun, Feb 23, 7:15 PM (31 m, 11 s)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
3f/49/18d73c93aa4c576be067241b27d7

Event Timeline