Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F20064474
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
48 KB
Subscribers
None
View Options
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
Details
Attached
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
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment