diff --git a/CMakeLists.txt b/CMakeLists.txt index 22d9ccf63..0340b421f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,151 +1,151 @@ set(kleopatra_version 3.1.8) # The following is for Windows. Keep in line with kleopatra_version. set(kleopatra_fileversion 3,1,8,0) cmake_minimum_required(VERSION 3.5) project(kleopatra VERSION ${kleopatra_version}) # Add version suffix for internal usage of the version string set(kleopatra_version ${kleopatra_version}${KLEOPATRA_VERSION_SUFFIX}) set(RELEASE_SERVICE_VERSION "20.03.70") 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.64.0") set(KMIME_VERSION "5.13.40") -set(LIBKLEO_VERSION "5.13.40") +set(LIBKLEO_VERSION "5.13.41") set(QT_REQUIRED_VERSION "5.12.0") set(GPGME_REQUIRED_VERSION "1.8.0") find_package(ECM ${KF5_MIN_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_MIN_VERSION} CONFIG REQUIRED) endif() find_package(KF5WidgetsAddons ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5ConfigWidgets ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5CoreAddons ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5Codecs ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5Config ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5I18n ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5IconThemes ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5ItemModels ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5XmlGui ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5WindowSystem ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5DocTools ${KF5_MIN_VERSION} CONFIG) find_package(KF5Crash ${KF5_MIN_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_MIN_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_MIN_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_VERSION} CONFIG REQUIRED) find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets Test Network PrintSupport) find_package(Assuan2 REQUIRED) set(HAVE_KCMUTILS ${KF5KCMUtils_FOUND}) 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=0x060000) add_definitions(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x060000) if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-braces -Wno-parentheses -Wno-ignored-qualifiers") endif() kde_enable_exceptions() add_subdirectory(pics) add_subdirectory(src) if(BUILD_TESTING) add_subdirectory(tests) add_subdirectory(autotests) endif() install(FILES kleopatra.renamecategories kleopatra.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}) if(KF5DocTools_FOUND) add_subdirectory(doc) endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9e463c2de..fc2d0e5ec 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,333 +1,334 @@ add_subdirectory(icons) include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) if (NOT DISABLE_KWATCHGNUPG) add_subdirectory(kwatchgnupg) endif() add_subdirectory(libkleopatraclient) add_subdirectory(conf) add_subdirectory(kconf_update) if(WIN32) set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_win.cpp) set(_kleopatra_extra_SRCS utils/gnupg-registry.c selftest/registrycheck.cpp utils/windowsprocessdevice.cpp ) else() set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_unix.cpp) set(_kleopatra_extra_SRCS) endif() set(_kleopatra_uiserver_SRCS uiserver/sessiondata.cpp uiserver/uiserver.cpp ${_kleopatra_extra_uiserver_SRCS} uiserver/assuanserverconnection.cpp uiserver/echocommand.cpp uiserver/decryptverifycommandemailbase.cpp uiserver/decryptverifycommandfilesbase.cpp uiserver/signcommand.cpp uiserver/signencryptfilescommand.cpp uiserver/prepencryptcommand.cpp uiserver/prepsigncommand.cpp uiserver/encryptcommand.cpp uiserver/selectcertificatecommand.cpp uiserver/importfilescommand.cpp uiserver/createchecksumscommand.cpp uiserver/verifychecksumscommand.cpp selftest/uiservercheck.cpp ) if(ASSUAN2_FOUND) include_directories(${ASSUAN2_INCLUDES}) set(_kleopatra_uiserver_extra_libs ${ASSUAN2_LIBRARIES}) else() include_directories(${ASSUAN_INCLUDES}) if(WIN32) set(_kleopatra_uiserver_extra_libs ${ASSUAN_VANILLA_LIBRARIES}) else() set(_kleopatra_uiserver_extra_libs ${ASSUAN_PTHREAD_LIBRARIES}) endif() endif() if(HAVE_GPG_ERR_SOURCE_KLEO) add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO) else() add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1) endif() ki18n_wrap_ui(_kleopatra_uiserver_SRCS crypto/gui/signingcertificateselectionwidget.ui) set(_kleopatra_SRCS utils/gnupg-helper.cpp utils/gui-helper.cpp utils/filedialog.cpp utils/kdpipeiodevice.cpp utils/headerview.cpp utils/scrollarea.cpp utils/dragqueen.cpp utils/multivalidator.cpp utils/systemtrayicon.cpp utils/hex.cpp utils/path-helper.cpp utils/input.cpp utils/output.cpp utils/validation.cpp utils/wsastarter.cpp utils/iodevicelogger.cpp utils/log.cpp utils/action_data.cpp utils/types.cpp utils/archivedefinition.cpp utils/auditlog.cpp utils/clipboardmenu.cpp utils/kuniqueservice.cpp + utils/remarks.cpp selftest/selftest.cpp selftest/enginecheck.cpp selftest/gpgconfcheck.cpp selftest/gpgagentcheck.cpp selftest/libkleopatrarccheck.cpp ${_kleopatra_extra_SRCS} view/keylistcontroller.cpp view/keytreeview.cpp view/searchbar.cpp view/smartcardwidget.cpp view/padwidget.cpp view/pgpcardwidget.cpp view/netkeywidget.cpp view/nullpinwidget.cpp view/tabwidget.cpp view/keycacheoverlay.cpp view/waitwidget.cpp view/welcomewidget.cpp dialogs/certificateselectiondialog.cpp + dialogs/certifywidget.cpp dialogs/expirydialog.cpp dialogs/lookupcertificatesdialog.cpp dialogs/ownertrustdialog.cpp dialogs/selftestdialog.cpp dialogs/certifycertificatedialog.cpp dialogs/adduseriddialog.cpp dialogs/addemaildialog.cpp dialogs/exportcertificatesdialog.cpp dialogs/deletecertificatesdialog.cpp dialogs/setinitialpindialog.cpp dialogs/certificatedetailswidget.cpp dialogs/trustchainwidget.cpp dialogs/weboftrustwidget.cpp dialogs/weboftrustdialog.cpp dialogs/exportdialog.cpp dialogs/subkeyswidget.cpp dialogs/gencardkeydialog.cpp dialogs/updatenotification.cpp crypto/controller.cpp crypto/certificateresolver.cpp crypto/sender.cpp crypto/recipient.cpp crypto/task.cpp crypto/taskcollection.cpp crypto/decryptverifytask.cpp crypto/decryptverifyemailcontroller.cpp crypto/decryptverifyfilescontroller.cpp crypto/autodecryptverifyfilescontroller.cpp crypto/encryptemailtask.cpp crypto/encryptemailcontroller.cpp crypto/newsignencryptemailcontroller.cpp crypto/signencrypttask.cpp crypto/signencryptfilescontroller.cpp crypto/signemailtask.cpp crypto/signemailcontroller.cpp crypto/createchecksumscontroller.cpp crypto/verifychecksumscontroller.cpp crypto/gui/wizard.cpp crypto/gui/wizardpage.cpp crypto/gui/certificateselectionline.cpp crypto/gui/certificatelineedit.cpp crypto/gui/signingcertificateselectionwidget.cpp crypto/gui/signingcertificateselectiondialog.cpp crypto/gui/resultitemwidget.cpp crypto/gui/resultlistwidget.cpp crypto/gui/resultpage.cpp crypto/gui/newresultpage.cpp crypto/gui/signencryptfileswizard.cpp crypto/gui/signencryptemailconflictdialog.cpp crypto/gui/decryptverifyoperationwidget.cpp crypto/gui/decryptverifyfileswizard.cpp crypto/gui/decryptverifyfilesdialog.cpp crypto/gui/objectspage.cpp crypto/gui/resolverecipientspage.cpp crypto/gui/signerresolvepage.cpp crypto/gui/encryptemailwizard.cpp crypto/gui/signemailwizard.cpp crypto/gui/signencryptwidget.cpp crypto/gui/signencryptwizard.cpp crypto/gui/unknownrecipientwidget.cpp crypto/gui/verifychecksumsdialog.cpp commands/command.cpp commands/gnupgprocesscommand.cpp commands/detailscommand.cpp commands/exportcertificatecommand.cpp commands/importcertificatescommand.cpp commands/importcertificatefromfilecommand.cpp commands/importcertificatefromclipboardcommand.cpp commands/importcertificatefromdatacommand.cpp commands/lookupcertificatescommand.cpp commands/reloadkeyscommand.cpp commands/refreshx509certscommand.cpp commands/refreshopenpgpcertscommand.cpp commands/deletecertificatescommand.cpp commands/decryptverifyfilescommand.cpp commands/signencryptfilescommand.cpp commands/signencryptfoldercommand.cpp commands/encryptclipboardcommand.cpp commands/signclipboardcommand.cpp commands/decryptverifyclipboardcommand.cpp commands/clearcrlcachecommand.cpp commands/dumpcrlcachecommand.cpp commands/dumpcertificatecommand.cpp commands/importcrlcommand.cpp commands/changeexpirycommand.cpp commands/changeownertrustcommand.cpp commands/changeroottrustcommand.cpp commands/changepassphrasecommand.cpp commands/certifycertificatecommand.cpp commands/selftestcommand.cpp commands/exportsecretkeycommand.cpp commands/exportopenpgpcertstoservercommand.cpp commands/adduseridcommand.cpp commands/newcertificatecommand.cpp commands/setinitialpincommand.cpp commands/learncardkeyscommand.cpp commands/checksumcreatefilescommand.cpp commands/checksumverifyfilescommand.cpp commands/exportpaperkeycommand.cpp commands/importpaperkeycommand.cpp commands/genrevokecommand.cpp commands/keytocardcommand.cpp ${_kleopatra_uiserver_files} conf/configuredialog.cpp newcertificatewizard/listwidget.cpp newcertificatewizard/newcertificatewizard.cpp smartcard/readerstatus.cpp smartcard/card.cpp smartcard/openpgpcard.cpp smartcard/netkeycard.cpp aboutdata.cpp systrayicon.cpp kleopatraapplication.cpp mainwindow.cpp main.cpp ) if(WIN32) configure_file (versioninfo.rc.in versioninfo.rc) set(_kleopatra_SRCS ${CMAKE_CURRENT_BINARY_DIR}/versioninfo.rc ${_kleopatra_SRCS}) endif() if(HAVE_KCMUTILS) set (_kleopatra_extra_libs KF5::KCMUtils) else() set (_kleopatra_SRCS conf/kleopageconfigdialog.cpp ${_kleopatra_SRCS}) endif() ecm_qt_declare_logging_category(_kleopatra_SRCS HEADER kleopatra_debug.h IDENTIFIER KLEOPATRA_LOG CATEGORY_NAME org.kde.pim.kleopatra) if(KLEO_MODEL_TEST) add_definitions(-DKLEO_MODEL_TEST) set(_kleopatra_SRCS ${_kleopatra_SRCS} models/modeltest.cpp) endif() ki18n_wrap_ui(_kleopatra_SRCS - dialogs/certificationoptionswidget.ui dialogs/expirydialog.ui dialogs/lookupcertificatesdialog.ui dialogs/ownertrustdialog.ui dialogs/selectchecklevelwidget.ui dialogs/selftestdialog.ui dialogs/adduseriddialog.ui dialogs/setinitialpindialog.ui dialogs/certificatedetailswidget.ui dialogs/trustchainwidget.ui dialogs/subkeyswidget.ui newcertificatewizard/listwidget.ui newcertificatewizard/chooseprotocolpage.ui newcertificatewizard/enterdetailspage.ui newcertificatewizard/overviewpage.ui newcertificatewizard/keycreationpage.ui newcertificatewizard/resultpage.ui newcertificatewizard/advancedsettingsdialog.ui ) kconfig_add_kcfg_files(_kleopatra_SRCS kcfg/tooltippreferences.kcfgc kcfg/emailoperationspreferences.kcfgc kcfg/fileoperationspreferences.kcfgc kcfg/smimevalidationpreferences.kcfgc ) file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/icons/*-apps-kleopatra.png") ecm_add_app_icon(_kleopatra_SRCS ICONS ${ICONS_SRCS}) qt5_add_resources(_kleopatra_SRCS kleopatra.qrc) add_executable(kleopatra_bin ${_kleopatra_SRCS} ${_kleopatra_uiserver_SRCS}) set_target_properties(kleopatra_bin PROPERTIES OUTPUT_NAME kleopatra) target_link_libraries(kleopatra_bin Gpgmepp QGpgme ${_kleopatra_extra_libs} KF5::Libkleo KF5::Mime KF5::I18n KF5::XmlGui KF5::IconThemes KF5::WindowSystem KF5::CoreAddons KF5::ItemModels KF5::Crash Qt5::Network Qt5::PrintSupport # Printing secret keys ${_kleopatra_uiserver_extra_libs} ${_kleopatra_dbusaddons_libs} kleopatraclientcore ) install(TARGETS kleopatra_bin ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install( PROGRAMS data/org.kde.kleopatra.desktop data/kleopatra_import.desktop DESTINATION ${KDE_INSTALL_APPDIR} ) install(FILES data/org.kde.kleopatra.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) install( FILES data/kleopatra_signencryptfiles.desktop data/kleopatra_signencryptfolders.desktop data/kleopatra_decryptverifyfiles.desktop data/kleopatra_decryptverifyfolders.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) diff --git a/src/commands/certifycertificatecommand.cpp b/src/commands/certifycertificatecommand.cpp index f44aa73f6..8479592e9 100644 --- a/src/commands/certifycertificatecommand.cpp +++ b/src/commands/certifycertificatecommand.cpp @@ -1,344 +1,369 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/signcertificatecommand.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB + 2019 g10code GmbH Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Softwarls Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "certifycertificatecommand.h" #include "newcertificatecommand.h" #include "command_p.h" #include "exportopenpgpcertstoservercommand.h" - -#include +#include "dialogs/certifycertificatedialog.h" +#include "utils/remarks.h" #include #include #include #include #include #include #include #include "kleopatra_debug.h" +#include +#if GPGMEPP_VERSION >= 0x10E00 // 1.14.0 +# define GPGME_HAS_REMARKS +#endif using namespace Kleo; using namespace Kleo::Commands; -using namespace Kleo::Dialogs; using namespace GpgME; using namespace QGpgME; class CertifyCertificateCommand::Private : public Command::Private { friend class ::Kleo::Commands::CertifyCertificateCommand; CertifyCertificateCommand *q_func() const { return static_cast(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 uids; QPointer dialog; QPointer job; }; CertifyCertificateCommand::Private *CertifyCertificateCommand::d_func() { return static_cast(d.get()); } const CertifyCertificateCommand::Private *CertifyCertificateCommand::d_func() const { return static_cast(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(1, uid).swap(d->uids); d->init(); } CertifyCertificateCommand::CertifyCertificateCommand(const std::vector &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::setCertificationExportable(bool on) { Q_UNUSED(on); } void CertifyCertificateCommand::setCertificationRevocable(bool on) { Q_UNUSED(on); } void CertifyCertificateCommand::setCertifyingKey(const Key &signer) { Q_UNUSED(signer); } void CertifyCertificateCommand::setUserIDs(const std::vector &uids) { d->uids = uids; if (!uids.empty() && d->key().isNull()) { setKey(uids.front().parent()); } } void CertifyCertificateCommand::setUserID(const UserID &uid) { setUserIDs(std::vector(1, uid)); } void CertifyCertificateCommand::doStart() { const std::vector keys = d->keys(); if (keys.size() != 1 || keys.front().protocol() != GpgME::OpenPGP) { d->finished(); return; } std::vector 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("

") + 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); - d->dialog->setCertificateToCertify(d->key()); - d->dialog->setSelectedUserIDs(d->uids); - d->dialog->setCertificatesWithSecretKeys(secKeys); + + 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()) { ExportOpenPGPCertsToServerCommand *const cmd = new ExportOpenPGPCertsToServerCommand(key()); cmd->start(); + } else if (!err) { + information(i18n("Certification successfully."), + i18n("Certification Succeeded")); + } else { + error(i18n("

An error occurred while trying to certify

" + "%1:

\t%2

", + Formatting::formatForComboBox(key()), + QString::fromUtf8(err.asString())), + i18n("Certification Error")); + } + if (!dialog->remarks().isEmpty()) { + Remarks::enableRemarks(true); } 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()); - - dialog->connectJob(job); +#ifdef GPGME_HAS_REMARKS + job->setRemark(dialog->remarks()); + // This also came with 1.14.0 + job->setDupeOk(true); +#endif if (const Error err = job->start(key())) { - dialog->setError(err); - finished(); + 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); - dialog->setAttribute(Qt::WA_DeleteOnClose); connect(dialog, SIGNAL(rejected()), q, SLOT(slotDialogRejected())); - connect(dialog, SIGNAL(certificationPrepared()), q, SLOT(slotCertificationPrepared())); + connect(dialog, SIGNAL(accepted()), q, SLOT(slotCertificationPrepared())); } void CertifyCertificateCommand::Private::createJob() { - if (dialog) { - disconnect(dialog, SIGNAL(certificationPrepared()), q, SLOT(slotCertificationPrepared())); - } - 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/certificatedetailswidget.cpp b/src/dialogs/certificatedetailswidget.cpp index 2e2aa2a7c..3b0676a46 100644 --- a/src/dialogs/certificatedetailswidget.cpp +++ b/src/dialogs/certificatedetailswidget.cpp @@ -1,629 +1,648 @@ /* Copyright (c) 2016 Klarälvdalens Datakonsult AB 2017 Intevation GmbH Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "certificatedetailswidget.h" #include "ui_certificatedetailswidget.h" #include "kleopatra_debug.h" #include "exportdialog.h" #include "trustchainwidget.h" #include "subkeyswidget.h" #include "weboftrustdialog.h" #include "commands/changepassphrasecommand.h" #include "commands/changeexpirycommand.h" #include "commands/certifycertificatecommand.h" #include "commands/adduseridcommand.h" #include "commands/genrevokecommand.h" #include "commands/detailscommand.h" #include "commands/dumpcertificatecommand.h" +#include "utils/remarks.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define HIDE_ROW(row) \ ui.row->setVisible(false); \ ui.row##Lbl->setVisible(false); Q_DECLARE_METATYPE(GpgME::UserID) using namespace Kleo; class CertificateDetailsWidget::Private { public: Private(CertificateDetailsWidget *parent) : q(parent) {} void setupCommonProperties(); void setupPGPProperties(); void setupSMIMEProperties(); void revokeUID(const GpgME::UserID &uid); void genRevokeCert(); void certifyClicked(); void webOfTrustClicked(); void exportClicked(); void addUserID(); void changePassphrase(); void changeExpiration(); void keysMayHaveChanged(); void showTrustChainDialog(); void showMoreDetails(); void publishCertificate(); void userIDTableContextMenuRequested(const QPoint &p); QString tofuTooltipString(const GpgME::UserID &uid) const; void smimeLinkActivated(const QString &link); void setUpdatedKey(const GpgME::Key &key); void keyListDone(const GpgME::KeyListResult &, const std::vector &, const QString &, const GpgME::Error &); Ui::CertificateDetailsWidget ui; GpgME::Key key; bool updateInProgress; private: CertificateDetailsWidget *q; }; void CertificateDetailsWidget::Private::setupCommonProperties() { // TODO: Enable once implemented HIDE_ROW(publishing) const bool hasSecret = key.hasSecret(); const bool isOpenPGP = key.protocol() == GpgME::OpenPGP; // TODO: Enable once implemented const bool canRevokeUID = false; // isOpenPGP && hasSecret ui.changePassphraseBtn->setVisible(hasSecret); ui.genRevokeBtn->setVisible(isOpenPGP && hasSecret); ui.certifyBtn->setVisible(isOpenPGP && !hasSecret); ui.changeExpirationBtn->setVisible(isOpenPGP && hasSecret); ui.addUserIDBtn->setVisible(hasSecret && isOpenPGP); ui.webOfTrustBtn->setVisible(isOpenPGP); ui.hboxLayout_1->addStretch(1); ui.validFrom->setText(Kleo::Formatting::creationDateString(key)); const QString expiry = Kleo::Formatting::expirationDateString(key); ui.expires->setText(expiry.isEmpty() ? i18nc("Expires", "never") : expiry); ui.type->setText(Kleo::Formatting::type(key)); ui.fingerprint->setText(Formatting::prettyID(key.primaryFingerprint())); if (Kleo::Formatting::complianceMode().isEmpty()) { HIDE_ROW(compliance) } else { ui.complianceLbl->setText(Kleo::Formatting::complianceStringForKey(key)); } ui.userIDTable->clear(); - QStringList headers = { i18n("Email"), i18n("Name"), i18n("Trust Level") }; + QStringList headers = { i18n("Email"), i18n("Name"), i18n("Trust Level"), i18n("Tags") }; if (canRevokeUID) { headers << QString(); } ui.userIDTable->setColumnCount(headers.count()); ui.userIDTable->setColumnWidth(0, 200); ui.userIDTable->setColumnWidth(1, 200); ui.userIDTable->setHeaderLabels(headers); const auto uids = key.userIDs(); for (unsigned int i = 0; i < uids.size(); ++i) { const auto &uid = uids[i]; auto item = new QTreeWidgetItem; const QString toolTip = tofuTooltipString(uid); item->setData(0, Qt::UserRole, QVariant::fromValue(uid)); auto pMail = Kleo::Formatting::prettyEMail(uid); auto pName = Kleo::Formatting::prettyName(uid); if (!isOpenPGP && pMail.isEmpty() && !pName.isEmpty()) { // S/MIME UserIDs are sometimes split, with one userID // containing the name another the Mail, we merge these // UID's into a single item. if (i + 1 < uids.size()) { pMail = Kleo::Formatting::prettyEMail(uids[i + 1]); // skip next uid ++i; } } if (!isOpenPGP && pMail.isEmpty() && pName.isEmpty()) { // S/MIME certificates sometimes contain urls where both // name and mail is empty. In that case we print whatever // the uid is as name. // // Can be ugly like (3:uri24:http://ca.intevation.org), but // this is better then showing an empty entry. pName = QString::fromLatin1(uid.id()); } item->setData(0, Qt::DisplayRole, pMail); item->setData(0, Qt::ToolTipRole, toolTip); item->setData(1, Qt::DisplayRole, pName); item->setData(1, Qt::ToolTipRole, toolTip); QIcon trustIcon; if (updateInProgress) { trustIcon = QIcon::fromTheme(QStringLiteral("emblem-question")); item->setData(2, Qt::DisplayRole, i18n("Updating...")); } else { switch (uid.validity()) { case GpgME::UserID::Unknown: case GpgME::UserID::Undefined: trustIcon = QIcon::fromTheme(QStringLiteral("emblem-question")); break; case GpgME::UserID::Never: trustIcon = QIcon::fromTheme(QStringLiteral("emblem-error")); break; case GpgME::UserID::Marginal: trustIcon = QIcon::fromTheme(QStringLiteral("emblem-warning")); break; case GpgME::UserID::Full: case GpgME::UserID::Ultimate: trustIcon = QIcon::fromTheme(QStringLiteral("emblem-success")); break; } item->setData(2, Qt::DisplayRole, Kleo::Formatting::validityShort(uid)); } item->setData(2, Qt::DecorationRole, trustIcon); item->setData(2, Qt::ToolTipRole, toolTip); + GpgME::Error err; + QStringList remarkList; + for (const auto &rem: uid.remarks(Remarks::remarkKeys(), err)) { + remarkList << QString::fromStdString(rem); + } + const auto remark = remarkList.join(QStringLiteral("; ")); + item->setData(3, Qt::DisplayRole, remark); + item->setData(3, Qt::ToolTipRole, toolTip); + ui.userIDTable->addTopLevelItem(item); if (canRevokeUID) { auto button = new QPushButton; button->setIcon(QIcon::fromTheme(QStringLiteral("entry-delete"))); button->setToolTip(i18n("Revoke this User ID")); button->setMaximumWidth(32); QObject::connect(button, &QPushButton::clicked, q, [this, uid]() { revokeUID(uid); }); ui.userIDTable->setItemWidget(item, 4, button); } } + if (!Remarks::remarksEnabled()) { + ui.userIDTable->hideColumn(3); + } } void CertificateDetailsWidget::Private::revokeUID(const GpgME::UserID &uid) { Q_UNUSED(uid); qCWarning(KLEOPATRA_LOG) << "Revoking UserID is not implemented. How did you even get here?!?!"; } void CertificateDetailsWidget::Private::changeExpiration() { auto cmd = new Kleo::Commands::ChangeExpiryCommand(key); QObject::connect(cmd, &Kleo::Commands::ChangeExpiryCommand::finished, q, [this]() { ui.changeExpirationBtn->setEnabled(true); }); ui.changeExpirationBtn->setEnabled(false); cmd->start(); } void CertificateDetailsWidget::Private::changePassphrase() { auto cmd = new Kleo::Commands::ChangePassphraseCommand(key); QObject::connect(cmd, &Kleo::Commands::ChangePassphraseCommand::finished, q, [this]() { ui.changePassphraseBtn->setEnabled(true); }); ui.changePassphraseBtn->setEnabled(false); cmd->start(); } void CertificateDetailsWidget::Private::genRevokeCert() { auto cmd = new Kleo::Commands::GenRevokeCommand(key); QObject::connect(cmd, &Kleo::Commands::GenRevokeCommand::finished, q, [this]() { ui.genRevokeBtn->setEnabled(true); }); ui.genRevokeBtn->setEnabled(false); cmd->start(); } void CertificateDetailsWidget::Private::certifyClicked() { auto cmd = new Kleo::Commands::CertifyCertificateCommand(key); QObject::connect(cmd, &Kleo::Commands::CertifyCertificateCommand::finished, q, [this]() { ui.certifyBtn->setEnabled(true); }); ui.certifyBtn->setEnabled(false); cmd->start(); } void CertificateDetailsWidget::Private::webOfTrustClicked() { QScopedPointer dlg(new WebOfTrustDialog(q)); dlg->setKey(key); dlg->exec(); } void CertificateDetailsWidget::Private::exportClicked() { QScopedPointer dlg(new ExportDialog(q)); dlg->setKey(key); dlg->exec(); } void CertificateDetailsWidget::Private::addUserID() { auto cmd = new Kleo::Commands::AddUserIDCommand(key); QObject::connect(cmd, &Kleo::Commands::AddUserIDCommand::finished, q, [this]() { ui.addUserIDBtn->setEnabled(true); key.update(); q->setKey(key); }); ui.addUserIDBtn->setEnabled(false); cmd->start(); } void CertificateDetailsWidget::Private::keysMayHaveChanged() { auto newKey = Kleo::KeyCache::instance()->findByFingerprint(key.primaryFingerprint()); if (!newKey.isNull()) { setUpdatedKey(newKey); } } void CertificateDetailsWidget::Private::showTrustChainDialog() { QScopedPointer dlg(new TrustChainDialog(q)); dlg->setKey(key); dlg->exec(); } void CertificateDetailsWidget::Private::publishCertificate() { qCWarning(KLEOPATRA_LOG) << "publishCertificateis not implemented."; //TODO } void CertificateDetailsWidget::Private::userIDTableContextMenuRequested(const QPoint &p) { auto item = ui.userIDTable->itemAt(p); if (!item) { return; } const auto userID = item->data(0, Qt::UserRole).value(); QMenu *menu = new QMenu(q); menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-sign")), i18n("Certify ..."), q, [this, userID]() { auto cmd = new Kleo::Commands::CertifyCertificateCommand(userID); ui.userIDTable->setEnabled(false); connect(cmd, &Kleo::Commands::CertifyCertificateCommand::finished, - q, [this]() { ui.userIDTable->setEnabled(true); }); + q, [this]() { + ui.userIDTable->setEnabled(true); + // Trigger an update when done + q->setKey(key); + }); cmd->start(); }); connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater); menu->popup(ui.userIDTable->viewport()->mapToGlobal(p)); } void CertificateDetailsWidget::Private::showMoreDetails() { ui.moreDetailsBtn->setEnabled(false); if (key.protocol() == GpgME::CMS) { auto cmd = new Kleo::Commands::DumpCertificateCommand(key); connect(cmd, &Kleo::Commands::DumpCertificateCommand::finished, q, [this]() { ui.moreDetailsBtn->setEnabled(true); }); cmd->setUseDialog(true); cmd->start(); } else { QScopedPointer dlg(new SubKeysDialog(q)); dlg->setKey(key); dlg->exec(); ui.moreDetailsBtn->setEnabled(true); } } QString CertificateDetailsWidget::Private::tofuTooltipString(const GpgME::UserID &uid) const { const auto tofu = uid.tofuInfo(); if (tofu.isNull()) { return QString(); } QString html = QStringLiteral(""); const auto appendRow = [&html](const QString &lbl, const QString &val) { html += QStringLiteral("" "" "" "") .arg(lbl, val); }; const auto appendHeader = [this, &html](const QString &hdr) { html += QStringLiteral("") .arg(q->palette().highlight().color().name(), q->palette().highlightedText().color().name(), hdr); }; const auto dateTime = [](long ts) { return ts == 0 ? i18n("never") : QDateTime::fromSecsSinceEpoch(ts).toString(Qt::SystemLocaleShortDate); }; appendHeader(i18n("Signing")); appendRow(i18n("First message"), dateTime(tofu.signFirst())); appendRow(i18n("Last message"), dateTime(tofu.signLast())); appendRow(i18n("Message count"), QString::number(tofu.signCount())); appendHeader(i18n("Encryption")); appendRow(i18n("First message"), dateTime(tofu.encrFirst())); appendRow(i18n("Last message"), dateTime(tofu.encrLast())); appendRow(i18n("Message count"), QString::number(tofu.encrCount())); html += QStringLiteral("
%1:%2
%3
"); // Make sure the tooltip string is different for each UserID, even if the // data are the same, otherwise the tooltip is not updated and moved when // user moves mouse from one row to another. html += QStringLiteral("").arg(QString::fromUtf8(uid.id())); return html; } void CertificateDetailsWidget::Private::setupPGPProperties() { HIDE_ROW(smimeOwner) HIDE_ROW(smimeIssuer) ui.smimeRelatedAddresses->setVisible(false); ui.trustChainDetailsBtn->setVisible(false); ui.userIDTable->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui.userIDTable, &QAbstractItemView::customContextMenuRequested, q, [this](const QPoint &p) { userIDTableContextMenuRequested(p); }); } static QString formatDNToolTip(const Kleo::DN &dn) { QString html = QStringLiteral(""); const auto appendRow = [&html, dn](const QString &lbl, const QString &attr) { const QString val = dn[attr]; if (!val.isEmpty()) { html += QStringLiteral( "" "" "").arg(lbl, val); } }; appendRow(i18n("Common Name"), QStringLiteral("CN")); appendRow(i18n("Organization"), QStringLiteral("O")); appendRow(i18n("Street"), QStringLiteral("STREET")); appendRow(i18n("City"), QStringLiteral("L")); appendRow(i18n("State"), QStringLiteral("ST")); appendRow(i18n("Country"), QStringLiteral("C")); html += QStringLiteral("
%1:%2
"); return html; } void CertificateDetailsWidget::Private::setupSMIMEProperties() { HIDE_ROW(publishing) const auto ownerId = key.userID(0); const Kleo::DN dn(ownerId.id()); const QString cn = dn[QStringLiteral("CN")]; const QString o = dn[QStringLiteral("O")]; const QString dnEmail = dn[QStringLiteral("EMAIL")]; const QString name = cn.isEmpty() ? dnEmail : cn; QString owner; if (name.isEmpty()) { owner = dn.dn(); } else if (o.isEmpty()) { owner = name; } else { owner = i18nc(" of ", "%1 of %2", name, o); } ui.smimeOwner->setText(owner); ui.smimeOwner->setTextInteractionFlags(Qt::TextBrowserInteraction); const Kleo::DN issuerDN(key.issuerName()); const QString issuerCN = issuerDN[QStringLiteral("CN")]; const QString issuer = issuerCN.isEmpty() ? QString::fromUtf8(key.issuerName()) : issuerCN; ui.smimeIssuer->setText(QStringLiteral("%1").arg(issuer)); ui.smimeIssuer->setToolTip(formatDNToolTip(issuerDN)); ui.smimeOwner->setToolTip(formatDNToolTip(dn)); } void CertificateDetailsWidget::Private::smimeLinkActivated(const QString &link) { if (link == QLatin1String("#issuerDetails")) { const auto parentKey = KeyCache::instance()->findIssuers(key, KeyCache::NoOption); if (!parentKey.size()) { return; } auto cmd = new Kleo::Commands::DetailsCommand(parentKey[0], nullptr); cmd->setParentWidget(q); cmd->start(); return; } qCWarning(KLEOPATRA_LOG) << "Unknown link activated:" << link; } CertificateDetailsWidget::CertificateDetailsWidget(QWidget *parent) : QWidget(parent) , d(new Private(this)) { d->ui.setupUi(this); connect(d->ui.addUserIDBtn, &QPushButton::clicked, this, [this]() { d->addUserID(); }); connect(d->ui.changePassphraseBtn, &QPushButton::clicked, this, [this]() { d->changePassphrase(); }); connect(d->ui.genRevokeBtn, &QPushButton::clicked, this, [this]() { d->genRevokeCert(); }); connect(d->ui.changeExpirationBtn, &QPushButton::clicked, this, [this]() { d->changeExpiration(); }); connect(d->ui.smimeOwner, &QLabel::linkActivated, this, [this](const QString &link) { d->smimeLinkActivated(link); }); connect(d->ui.smimeIssuer, &QLabel::linkActivated, this, [this](const QString &link) { d->smimeLinkActivated(link); }); connect(d->ui.trustChainDetailsBtn, &QPushButton::pressed, this, [this]() { d->showTrustChainDialog(); }); connect(d->ui.moreDetailsBtn, &QPushButton::pressed, this, [this]() { d->showMoreDetails(); }); connect(d->ui.publishing, &QPushButton::pressed, this, [this]() { d->publishCertificate(); }); connect(d->ui.certifyBtn, &QPushButton::clicked, this, [this]() { d->certifyClicked(); }); connect(d->ui.webOfTrustBtn, &QPushButton::clicked, this, [this]() { d->webOfTrustClicked(); }); connect(d->ui.exportBtn, &QPushButton::clicked, this, [this]() { d->exportClicked(); }); connect(Kleo::KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, this, [this]() { d->keysMayHaveChanged(); }); } CertificateDetailsWidget::~CertificateDetailsWidget() { } void CertificateDetailsWidget::Private::keyListDone(const GpgME::KeyListResult &, const std::vector &keys, const QString &, const GpgME::Error &) { updateInProgress = false; if (keys.size() != 1) { qCWarning(KLEOPATRA_LOG) << "Invalid keylist result in update."; return; } // As we listen for keysmayhavechanged we get the update // after updating the keycache. KeyCache::mutableInstance()->insert(keys); } void CertificateDetailsWidget::Private::setUpdatedKey(const GpgME::Key &k) { key = k; setupCommonProperties(); if (key.protocol() == GpgME::OpenPGP) { setupPGPProperties(); } else { setupSMIMEProperties(); } } void CertificateDetailsWidget::setKey(const GpgME::Key &key) { if (key.protocol() == GpgME::CMS) { // For everything but S/MIME this should be quick // and we don't need to show another status. d->updateInProgress = true; } d->setUpdatedKey(key); // Run a keylistjob with full details (TOFU / Validate) QGpgME::KeyListJob *job = key.protocol() == GpgME::OpenPGP ? QGpgME::openpgp()->keyListJob(false, true, true) : QGpgME::smime()->keyListJob(false, true, true); auto ctx = QGpgME::Job::context(job); ctx->addKeyListMode(GpgME::WithTofu); + ctx->addKeyListMode(GpgME::Signatures); + ctx->addKeyListMode(GpgME::SignatureNotations); // Windows QGpgME new style connect problem makes this necessary. connect(job, SIGNAL(result(GpgME::KeyListResult,std::vector,QString,GpgME::Error)), this, SLOT(keyListDone(GpgME::KeyListResult,std::vector,QString,GpgME::Error))); job->start(QStringList() << QLatin1String(key.primaryFingerprint()), key.hasSecret()); } GpgME::Key CertificateDetailsWidget::key() const { return d->key; } CertificateDetailsDialog::CertificateDetailsDialog(QWidget *parent) : QDialog(parent) { setWindowTitle(i18nc("@title:window", "Certificate Details")); auto l = new QVBoxLayout(this); l->addWidget(new CertificateDetailsWidget(this)); auto bbox = new QDialogButtonBox(this); auto btn = bbox->addButton(QDialogButtonBox::Close); connect(btn, &QPushButton::pressed, this, &QDialog::accept); l->addWidget(bbox); readConfig(); } CertificateDetailsDialog::~CertificateDetailsDialog() { writeConfig(); } void CertificateDetailsDialog::readConfig() { KConfigGroup dialog(KSharedConfig::openConfig(), "CertificateDetailsDialog"); const QSize size = dialog.readEntry("Size", QSize(730, 280)); if (size.isValid()) { resize(size); } } void CertificateDetailsDialog::writeConfig() { KConfigGroup dialog(KSharedConfig::openConfig(), "CertificateDetailsDialog"); dialog.writeEntry("Size", size()); dialog.sync(); } void CertificateDetailsDialog::setKey(const GpgME::Key &key) { auto w = findChild(); Q_ASSERT(w); w->setKey(key); } GpgME::Key CertificateDetailsDialog::key() const { auto w = findChild(); Q_ASSERT(w); return w->key(); } #include "moc_certificatedetailswidget.cpp" diff --git a/src/dialogs/certificationoptionswidget.ui b/src/dialogs/certificationoptionswidget.ui deleted file mode 100644 index 57111ca46..000000000 --- a/src/dialogs/certificationoptionswidget.ui +++ /dev/null @@ -1,153 +0,0 @@ - - Kleo::Dialogs::CertifyCertificateDialogPrivate::CertificationOptionsWidget - - - - 0 - 0 - 879 - 523 - - - - - - - <b>Step 2:</b> Choose how to certify. - - - - - - - 1 - - - - - - - Choose which of your certificates to sign with: - - - - - - - - - - - - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - - - - - - - Certify only for myself - - - true - - - - - - - Certify for everyone to see - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - false - - - Send certified certificate to server afterwards - - - true - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - exportableSignatureRB - toggled(bool) - sendToServerCB - setEnabled(bool) - - - 439 - 338 - - - 455 - 368 - - - - - diff --git a/src/dialogs/certifycertificatedialog.cpp b/src/dialogs/certifycertificatedialog.cpp index 053ca68a5..87905d23e 100644 --- a/src/dialogs/certifycertificatedialog.cpp +++ b/src/dialogs/certifycertificatedialog.cpp @@ -1,507 +1,173 @@ /* -*- mode: c++; c-basic-offset:4 -*- dialogs/signcertificatedialog.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB + 2019 g10code GmbH Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include -#include "certifycertificatedialog.h" +#include "kleopatra_debug.h" -#include "certifycertificatedialog_p.h" +#include "certifycertificatedialog.h" -#include +#include "certifywidget.h" #include #include #include +#include +#include +#include +#include -#include -#include -#include #include -#include -#include -#include - -#include // Qt::escape +#include +#include #include using namespace GpgME; using namespace Kleo; -using namespace Kleo::Dialogs; -using namespace Kleo::Dialogs::CertifyCertificateDialogPrivate; - -void UserIDModel::setCertificateToCertify(const Key &key) -{ - m_key = key; - clear(); - const std::vector ids = key.userIDs(); - for (unsigned int i = 0; i < ids.size(); ++i) { - QStandardItem *const item = new QStandardItem; - item->setText(Formatting::prettyUserID(key.userID(i))); - item->setData(i, UserIDIndex); - item->setCheckable(true); - item->setEditable(false); - appendRow(item); - } -} - -void UserIDModel::setCheckedUserIDs(const std::vector &uids) -{ - std::vector 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 UserIDModel::checkedUserIDs() const -{ - std::vector ids; - for (int i = 0; i < rowCount(); ++i) - if (item(i)->checkState() == Qt::Checked) { - ids.push_back(item(i)->data(UserIDIndex).toUInt()); +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); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel | + QDialogButtonBox::Ok); + KGuiItem::assign(buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok()); + KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); + + buttonBox->button(QDialogButtonBox::Ok)->setText(i18n("Certify")); + connect(buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, + this, [this] () { + KConfigGroup conf(KSharedConfig::openConfig(), "CertifySettings"); + const auto lastKey = mCertWidget->secKey(); + // Do not accept if the keys are the same. + if (!lastKey.isNull() && !mCertWidget->target().isNull() && + !strcmp(lastKey.primaryFingerprint(), + mCertWidget->target().primaryFingerprint())) { + KMessageBox::error(this, i18n("You cannot certify using the same key."), + i18n("Invalid Selection"), KMessageBox::Notify); + return; } - return ids; -} - -void SecretKeysModel::setSecretKeys(const std::vector &keys) -{ - clear(); - m_secretKeys = keys; - for (unsigned int i = 0; i < m_secretKeys.size(); ++i) { - const Key key = m_secretKeys[i]; - QStandardItem *const item = new QStandardItem; - item->setText(Formatting::formatForComboBox(key)); - item->setData(i, IndexRole); - item->setEditable(false); - appendRow(item); - } -} - -std::vector SecretKeysModel::secretKeys() const -{ - return m_secretKeys; -} - -Key SecretKeysModel::keyFromItem(const QStandardItem *item) const -{ - Q_ASSERT(item); - const unsigned int idx = item->data(IndexRole).toUInt(); - Q_ASSERT(idx < m_secretKeys.size()); - return m_secretKeys[idx]; -} - -Key SecretKeysModel::keyFromIndex(const QModelIndex &idx) const -{ - return keyFromItem(itemFromIndex(idx)); -} - -SelectUserIDsPage::SelectUserIDsPage(QWidget *parent) : QWizardPage(parent), m_userIDModel() -{ - QVBoxLayout *const layout = new QVBoxLayout(this); - QLabel *const label = new QLabel; - label->setText(i18n("Step 1: Please select the names and email addresses you wish to certify.")); - layout->addWidget(label); - m_listView = new QListView; - m_listView->setModel(&m_userIDModel); - layout->addWidget(m_listView, 1); - m_label = new QLabel; - layout->addWidget(m_label); - connect(&m_userIDModel, &QStandardItemModel::itemChanged, this, &QWizardPage::completeChanged); -} - -bool SelectUserIDsPage::isComplete() const -{ - return !selectedUserIDs().empty(); -} - -void SelectUserIDsPage::setSelectedUserIDs(const std::vector &uids) -{ - m_userIDModel.setCheckedUserIDs(uids); -} -std::vector SelectUserIDsPage::selectedUserIDs() const -{ - return m_userIDModel.checkedUserIDs(); -} - -void SelectUserIDsPage::setCertificateToCertify(const Key &key) -{ - m_label->setText(i18n("Fingerprint: %1", - Formatting::prettyID(key.primaryFingerprint())) + QStringLiteral("
") + - i18n("Only the fingerprint clearly identifies the key and its owner.")); - m_userIDModel.setCertificateToCertify(key); - -} - -SelectCheckLevelPage::SelectCheckLevelPage(QWidget *parent) : QWizardPage(parent), m_ui() -{ - m_ui.setupUi(this); -} - -unsigned int SelectCheckLevelPage::checkLevel() const -{ - if (m_ui.checkLevelNotCheckedRB->isChecked()) { - return 1; - } - if (m_ui.checkLevelCasualRB->isChecked()) { - return 2; - } - if (m_ui.checkLevelThoroughlyRB->isChecked()) { - return 3; - } - Q_ASSERT(!"No check level radiobutton checked"); - return 0; -} - -OptionsPage::OptionsPage(QWidget *parent) : QWizardPage(parent), m_ui() -{ - m_ui.setupUi(this); - m_ui.keyListView->setModel(&m_model); - connect(m_ui.keyListView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QWizardPage::completeChanged); - setCommitPage(true); - setButtonText(QWizard::CommitButton, i18n("Certify")); -} - -bool OptionsPage::exportableCertificationSelected() const -{ - return m_ui.exportableSignatureRB->isChecked(); -} - -void OptionsPage::setCertificatesWithSecretKeys(const std::vector &keys) -{ - Q_ASSERT(!keys.empty()); - m_model.setSecretKeys(keys); - if (keys.size() == 1) { - m_ui.stackedWidget->setCurrentWidget(m_ui.singleKeyPage); - m_ui.singleKeyLabel->setText(i18n("Certification will be performed using certificate %1.", Formatting::prettyNameAndEMail(keys[0]))); - } else { - m_ui.stackedWidget->setCurrentWidget(m_ui.multipleKeysPage); - } - Q_EMIT completeChanged(); -} - -Key OptionsPage::selectedSecretKey() const -{ - if (m_model.secretKeys().size() == 1) { - return m_model.secretKeys().at(0); - } - const QModelIndexList idxs = m_ui.keyListView->selectionModel()->selectedIndexes(); - Q_ASSERT(idxs.size() <= 1); - return idxs.isEmpty() ? Key() : m_model.keyFromIndex(idxs[0]); -} - -bool OptionsPage::sendToServer() const -{ - return m_ui.sendToServerCB->isChecked(); -} - -bool OptionsPage::validatePage() -{ - Q_EMIT nextClicked(); - return true; -} - -bool OptionsPage::isComplete() const -{ - return !selectedSecretKey().isNull(); -} - -SummaryPage::SummaryPage(QWidget *parent) : QWizardPage(parent), m_complete(false) -{ - QGridLayout *const layout = new QGridLayout(this); - QLabel *const uidLabelLabel = new QLabel(i18n("Signed user IDs:")); - uidLabelLabel->setAlignment(Qt::AlignTop); - int row = 0; - layout->addWidget(new QLabel(i18n("Summary:")), row, 0, 1, 2); - layout->addWidget(uidLabelLabel, ++row, 0); - layout->addWidget(m_userIDsLabel = new QLabel, row, 1); -#ifdef KLEO_SIGN_KEY_CERTLEVEL_SUPPORT - layout->addWidget(new QLabel(i18n("Check level:")), ++row, 0); - layout->addWidget(m_checkLevelLabel = new QLabel, row, 1); -#else - m_checkLevelLabel = nullptr; -#endif - layout->addWidget(new QLabel(i18n("Selected secret key:")), ++row, 0); - layout->addWidget(m_secretKeyLabel = new QLabel, row, 1); - m_secretKeyLabel->setTextFormat(Qt::PlainText); - layout->addWidget(m_resultLabel = new QLabel, ++row, 0, 1, 2, Qt::AlignCenter); - m_resultLabel->setWordWrap(true); - layout->setRowStretch(row, 1); - m_resultLabel->setAlignment(Qt::AlignCenter); -} - -bool SummaryPage::isComplete() const -{ - return m_complete; -} - -void SummaryPage::setSummary(const SummaryPage::Summary &sum) -{ - const Key key = sum.certificateToCertify; - QStringList ids; - Q_FOREACH (const unsigned int i, sum.selectedUserIDs) { - ids += Formatting::prettyUserID(key.userID(i)).toHtmlEscaped(); - } - m_userIDsLabel->setText(QLatin1String("") + ids.join(QLatin1String("
")) + QLatin1String("
")); - m_secretKeyLabel->setText(sum.secretKey.isNull() ? i18n("Default certificate") : Formatting::prettyNameAndEMail(sum.secretKey)); -#ifdef KLEO_SIGN_KEY_CERTLEVEL_SUPPORT - switch (sum.checkLevel) { - case 0: - m_checkLevelLabel->setText(i18n("No statement made")); - break; - case 1: - m_checkLevelLabel->setText(i18n("Not checked")); - break; - case 2: - m_checkLevelLabel->setText(i18n("Casually checked")); - break; - case 3: - m_checkLevelLabel->setText(i18n("Thoroughly checked")); - break; - } -#endif -} + if (!lastKey.isNull()) { + conf.writeEntry("LastKey", lastKey.primaryFingerprint()); + } + conf.writeEntry("ExportCheckState", mCertWidget->exportableSelected()); + conf.writeEntry("PublishCheckState", mCertWidget->publishSelected()); + accept(); + }); + connect(buttonBox->button(QDialogButtonBox::Cancel), &QAbstractButton::clicked, + this, [this] () { + close(); + }); -void SummaryPage::setComplete(bool complete) -{ - if (complete == m_complete) { + mainLay->addWidget(buttonBox); + KConfigGroup cfgGroup(KSharedConfig::openConfig(), "CertifyDialog"); + const QByteArray geom = cfgGroup.readEntry("geometry", QByteArray()); + if (!geom.isEmpty()) { + restoreGeometry(geom); return; } - m_complete = complete; - Q_EMIT completeChanged(); + resize(QSize(640, 480)); } -void SummaryPage::setResult(const Error &err) -{ - if (err && !err.isCanceled()) - if (err.code() == GPG_ERR_USER_1) { - m_resultLabel->setText(i18n("The certificate was not certified because it was already certified by the same certificate.")); - } else { - m_resultLabel->setText(i18n("The certificate could not be certified. Error: %1", QString::fromLocal8Bit(err.asString()).toHtmlEscaped())); - } - else if (err.isCanceled()) { - m_resultLabel->setText(i18n("Certification canceled.")); - } else { - m_resultLabel->setText(i18n("Certification successful.")); - } -} - -class CertifyCertificateDialog::Private -{ - friend class ::Kleo::Dialogs::CertifyCertificateDialog; - CertifyCertificateDialog *const q; - -public: - explicit Private(CertifyCertificateDialog *qq) - : q(qq), - summaryPageId(0), - selectUserIDsPage(nullptr), - selectCheckLevelPage(nullptr), - optionsPage(nullptr), - summaryPage(nullptr) - { - selectUserIDsPage = new SelectUserIDsPage(q); - q->addPage(selectUserIDsPage); - //selectCheckLevelPage = new SelectCheckLevelPage( q ); - //setting the cert level explicitly is not supported by the backend, - //thus we omit the page from the UI - //q->addPage( selectCheckLevelPage ); - optionsPage = new OptionsPage(q); - q->addPage(optionsPage); - summaryPage = new SummaryPage(q); - summaryPageId = q->addPage(summaryPage); - connect(optionsPage, &OptionsPage::nextClicked, q, &CertifyCertificateDialog::certificationPrepared); - } - - Key key() const - { - return selectUserIDsPage ? selectUserIDsPage->certificateToCertify() : Key(); - } - - void ensureSummaryPageVisible(); - - void certificationResult(const Error &error); - - void setOperationCompleted() - { - summaryPage->setComplete(true); - } - SummaryPage::Summary createSummary() const - { - SummaryPage::Summary sum; - sum.selectedUserIDs = selectUserIDsPage->selectedUserIDs(); - sum.secretKey = optionsPage->selectedSecretKey(); - sum.certificateToCertify = selectUserIDsPage->certificateToCertify(); - //PENDING -#ifdef KLEO_SIGN_KEY_CERTLEVEL_SUPPORT - sum.checkLevel = selectCheckLevelPage->checkLevel(); -#else - sum.checkLevel = 0; -#endif - - sum.exportable = optionsPage->exportableCertificationSelected(); - sum.sendToServer = optionsPage->sendToServer(); - return sum; - } - - int summaryPageId; - SelectUserIDsPage *selectUserIDsPage; - SelectCheckLevelPage *selectCheckLevelPage; - OptionsPage *optionsPage; - SummaryPage *summaryPage; -}; - -CertifyCertificateDialog::CertifyCertificateDialog(QWidget *p, Qt::WindowFlags f) - : QWizard(p, f), d(new Private(this)) +CertifyCertificateDialog::~CertifyCertificateDialog() { + KConfigGroup cfgGroup(KSharedConfig::openConfig(), "CertifyDialog"); + cfgGroup.writeEntry("geometry", saveGeometry()); + cfgGroup.sync(); } -CertifyCertificateDialog::~CertifyCertificateDialog() {} - void CertifyCertificateDialog::setCertificateToCertify(const Key &key) { setWindowTitle(i18nc("@title:window arg is name, email of certificate holder", "Certify Certificate: %1", Formatting::prettyName(key))); - d->selectUserIDsPage->setCertificateToCertify(key); -} - -void CertifyCertificateDialog::setCertificatesWithSecretKeys(const std::vector &keys) -{ - d->optionsPage->setCertificatesWithSecretKeys(keys); + mCertWidget->setTarget(key); } bool CertifyCertificateDialog::exportableCertificationSelected() const { - return d->optionsPage->exportableCertificationSelected(); + return mCertWidget->exportableSelected(); } bool CertifyCertificateDialog::trustCertificationSelected() const { return false; } bool CertifyCertificateDialog::nonRevocableCertificationSelected() const { return false; } Key CertifyCertificateDialog::selectedSecretKey() const { - return d->optionsPage->selectedSecretKey(); + return mCertWidget->secKey(); } bool CertifyCertificateDialog::sendToServer() const { - return d->optionsPage->sendToServer(); + return mCertWidget->publishSelected(); } unsigned int CertifyCertificateDialog::selectedCheckLevel() const { //PENDING #ifdef KLEO_SIGN_KEY_CERTLEVEL_SUPPORT return d->selectCheckLevelPage->checkLevel(); #endif return 0; } -void CertifyCertificateDialog::connectJob(QGpgME::SignKeyJob *job) -{ - connect(job, SIGNAL(result(GpgME::Error)), this, SLOT(certificationResult(GpgME::Error))); - d->summaryPage->setSummary(d->createSummary()); -} - -void CertifyCertificateDialog::setError(const Error &error) -{ - d->setOperationCompleted(); - d->summaryPage->setResult(error); - d->ensureSummaryPageVisible(); - if (error.isCanceled()) { - close(); - } -} - -void CertifyCertificateDialog::Private::certificationResult(const Error &err) -{ - setOperationCompleted(); - summaryPage->setResult(err); - ensureSummaryPageVisible(); -} - -namespace -{ -static bool uidEqual(const UserID &lhs, const UserID &rhs) -{ - return qstrcmp(lhs.parent().primaryFingerprint(), - rhs.parent().primaryFingerprint()) == 0 - && qstrcmp(lhs.id(), rhs.id()) == 0; -} -} - void CertifyCertificateDialog::setSelectedUserIDs(const std::vector &uids) { - const Key key = d->key(); - const char *const fpr = key.primaryFingerprint(); - - const std::vector all = key.userIDs(); - - std::vector indexes; - indexes.reserve(uids.size()); - - Q_FOREACH (const UserID &uid, uids) { - kleo_assert(qstrcmp(uid.parent().primaryFingerprint(), fpr) == 0); - const unsigned int idx = - std::distance(all.cbegin(), std::find_if(all.cbegin(), all.cend(), - [uid](const UserID &other) { return uidEqual(uid, other); })); - if (idx < all.size()) { - indexes.push_back(idx); - } - } - - d->selectUserIDsPage->setSelectedUserIDs(indexes); + mCertWidget->selectUserIDs(uids); } std::vector CertifyCertificateDialog::selectedUserIDs() const { - return d->selectUserIDsPage->selectedUserIDs(); + return mCertWidget->selectedUserIDs(); } -void CertifyCertificateDialog::Private::ensureSummaryPageVisible() +QString CertifyCertificateDialog::remarks() const { - while (q->currentId() != summaryPageId) { - q->next(); - } + return mCertWidget->remarks(); } - -#include "moc_certifycertificatedialog.cpp" -#include "moc_certifycertificatedialog_p.cpp" diff --git a/src/dialogs/certifycertificatedialog.h b/src/dialogs/certifycertificatedialog.h index 9b5df576b..e72dae412 100644 --- a/src/dialogs/certifycertificatedialog.h +++ b/src/dialogs/certifycertificatedialog.h @@ -1,96 +1,88 @@ /* -*- mode: c++; c-basic-offset:4 -*- dialogs/signcertificatedialog.h This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifndef __KLEOPATRA_DIALOGS_CERTIFYCERTIFICATEDIALOG_H__ #define __KLEOPATRA_DIALOGS_CERTIFYCERTIFICATEDIALOG_H__ #include #include #include #include namespace GpgME { class Error; } namespace Kleo { -namespace Dialogs -{ +class CertifyWidget; -class CertifyCertificateDialog : public QWizard +class CertifyCertificateDialog : public QDialog { Q_OBJECT public: explicit CertifyCertificateDialog(QWidget *parent = nullptr, Qt::WindowFlags f = {}); ~CertifyCertificateDialog(); bool exportableCertificationSelected() const; bool trustCertificationSelected() const; bool nonRevocableCertificationSelected() const; void setSelectedUserIDs(const std::vector &uids); std::vector selectedUserIDs() const; void setCertificatesWithSecretKeys(const std::vector &keys); GpgME::Key selectedSecretKey() const; bool sendToServer() const; unsigned int selectedCheckLevel() const; void setCertificateToCertify(const GpgME::Key &key); - void connectJob(QGpgME::SignKeyJob *job); - void setError(const GpgME::Error &error); - -Q_SIGNALS: - void certificationPrepared(); + QString remarks() const; private: - class Private; - kdtools::pimpl_ptr d; - Q_PRIVATE_SLOT(d, void certificationResult(GpgME::Error)) + CertifyWidget *mCertWidget; }; -} } #endif /* __KLEOPATRA_DIALOGS_CERTIFYCERTIFICATEDIALOG_H__ */ diff --git a/src/dialogs/certifycertificatedialog_p.h b/src/dialogs/certifycertificatedialog_p.h deleted file mode 100644 index 6329c04ca..000000000 --- a/src/dialogs/certifycertificatedialog_p.h +++ /dev/null @@ -1,179 +0,0 @@ -/* -*- mode: c++; c-basic-offset:4 -*- - dialogs/signcertificatedialog_p.h - - This file is part of Kleopatra, the KDE keymanager - Copyright (c) 2008 Klarälvdalens Datakonsult AB - - Kleopatra is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - Kleopatra is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - - In addition, as a special exception, the copyright holders give - permission to link the code of this program with any edition of - the Qt library by Trolltech AS, Norway (or with modified versions - of Qt that use the same license as Qt), and distribute linked - combinations including the two. You must obey the GNU General - Public License in all respects for all of the code used other than - Qt. If you modify this file, you may extend this exception to - your version of the file, but you are not obligated to do so. If - you do not wish to do so, delete this exception statement from - your version. -*/ - -#ifndef __KLEOPATRA_DIALOGS_CERTIFYCERTIFICATEDIALOG_P_H -#define __KLEOPATRA_DIALOGS_CERTIFYCERTIFICATEDIALOG_P_H - -#include "ui_selectchecklevelwidget.h" -#include "ui_certificationoptionswidget.h" - -#include - -#include - -#include -#include - -class QListView; -class QLabel; -class QCheckBox; - -namespace Kleo -{ -namespace Dialogs -{ -namespace CertifyCertificateDialogPrivate -{ -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 setCertificateToCertify(const GpgME::Key &key); - void setCheckedUserIDs(const std::vector &uids); - std::vector checkedUserIDs() const; - -private: - GpgME::Key m_key; -}; - -class SecretKeysModel : public QStandardItemModel -{ - Q_OBJECT -public: - enum Role { - IndexRole = Qt::UserRole - }; - explicit SecretKeysModel(QObject *parent = nullptr) : QStandardItemModel(parent) {} - void setSecretKeys(const std::vector &keys); - std::vector secretKeys() const; - GpgME::Key keyFromItem(const QStandardItem *item) const; - GpgME::Key keyFromIndex(const QModelIndex &index) const; - -private: - std::vector m_secretKeys; -}; - -class SelectUserIDsPage : public QWizardPage -{ - Q_OBJECT -public: - explicit SelectUserIDsPage(QWidget *parent = nullptr); - bool isComplete() const override; - - void setSelectedUserIDs(const std::vector &indexes); - std::vector selectedUserIDs() const; - void setCertificateToCertify(const GpgME::Key &ids); - GpgME::Key certificateToCertify() const - { - return m_userIDModel.certificateToCertify(); - } - -private: - QListView *m_listView; - QLabel *m_label; - UserIDModel m_userIDModel; -}; - -class SelectCheckLevelPage : public QWizardPage -{ - Q_OBJECT -public: - explicit SelectCheckLevelPage(QWidget *parent = nullptr); - unsigned int checkLevel() const; -private: - Ui::SelectCheckLevelWidget m_ui; -}; - -class OptionsPage : public QWizardPage -{ - Q_OBJECT -public: - explicit OptionsPage(QWidget *parent = nullptr); - - bool exportableCertificationSelected() const; - void setCertificatesWithSecretKeys(const std::vector &keys); - GpgME::Key selectedSecretKey() const; - bool sendToServer() const; - - bool validatePage() override; - bool isComplete() const override; - -Q_SIGNALS: - void nextClicked(); - -private: - Ui::CertificationOptionsWidget m_ui; - SecretKeysModel m_model; -}; - -class SummaryPage : public QWizardPage -{ - Q_OBJECT -public: - explicit SummaryPage(QWidget *parent = nullptr); - bool isComplete() const override; - void setComplete(bool complete); - - void setResult(const GpgME::Error &err); - - struct Summary { - std::vector selectedUserIDs; - unsigned int checkLevel; - GpgME::Key certificateToCertify; - GpgME::Key secretKey; - bool exportable; - bool sendToServer; - }; - - void setSummary(const Summary &summary); - -private: - bool m_complete; - QLabel *m_userIDsLabel; - QLabel *m_secretKeyLabel; - QLabel *m_checkLevelLabel; - QLabel *m_resultLabel; -}; -} -} -} - -#endif - diff --git a/src/dialogs/certifywidget.cpp b/src/dialogs/certifywidget.cpp new file mode 100644 index 000000000..bd65021ab --- /dev/null +++ b/src/dialogs/certifywidget.cpp @@ -0,0 +1,492 @@ +/* dialogs/certifywidget.cpp + + This file is part of Kleopatra, the KDE keymanager + Copyright (c) 2019 by g10code GmbH + + Kleopatra is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Kleopatra is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "certifywidget.h" + +#include "kleopatra_debug.h" + +#include "utils/remarks.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#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(), + const int animationDuration = 300, + 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, + const int animationDuration, QWidget *parent): + QWidget(parent), + animationDuration(animationDuration) +{ + 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(toggleAnimation.animationAt(i)); + expanderAnimation->setDuration(animationDuration); + expanderAnimation->setStartValue(collapsedHeight); + expanderAnimation->setEndValue(collapsedHeight + contentHeight); + } + auto contentAnimation = static_cast(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); + } +}; + +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 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; + } + QStandardItem *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 &uids) + { + std::vector 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 checkedUserIDs() const + { + std::vector 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; +} + +} // anonymous namespace + + +// Use of pimpl as this might be moved to libkleo +class CertifyWidget::Private +{ +public: + Private(CertifyWidget *qq) : q(qq), + mFprLabel(new QLabel), + remarkTextChanged(false) + { + QVBoxLayout *mainLay = new QVBoxLayout(q); + mainLay->addWidget(mFprLabel); + + auto secKeyLay = new QHBoxLayout; + secKeyLay->addWidget(new QLabel(i18n("Certify with:"))); + + mSecKeySelect = new KeySelectionCombo(true); + mSecKeySelect->setKeyFilter(std::shared_ptr(new SecKeyFilter())); + + secKeyLay->addWidget(mSecKeySelect, 1); + mainLay->addLayout(secKeyLay); + + auto splitLine = new QFrame; + splitLine->setFrameShape(QFrame::HLine); + splitLine->setFrameShadow(QFrame::Sunken); + splitLine->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + + mainLay->addWidget(splitLine); + + auto listView = new QListView; + listView->setModel(&mUserIDModel); + mainLay->addWidget(listView, 1); + + // Setup the advanced area + auto expander = new AnimatedExpander(i18n("Advanced")); + mainLay->addWidget(expander); + + auto advLay = new QVBoxLayout; + + mExportCB = new QCheckBox(i18n("Certify for everyone to see. (exportable)")); + mPublishCB = new QCheckBox(i18n("Publish on keyserver afterwards.")); + auto publishLay = new QHBoxLayout; + publishLay->addSpacing(20); + publishLay->addWidget(mPublishCB); + + mRemarkLE = new QLineEdit; + mRemarkLE->setPlaceholderText(i18n("Tags")); + + auto infoBtn = new QPushButton; + infoBtn->setIcon(QIcon::fromTheme(QStringLiteral("help-contextual"))); + infoBtn->setFlat(true); + + connect(infoBtn, &QPushButton::clicked, q, [this, infoBtn] () { + const QString msg = i18n("You can use this to add additional info to a " + "certification.") + QStringLiteral("

") + + i18n("Tags created by anyone with full certification trust " + "are shown in the keylist and can be searched."); + QToolTip::showText(infoBtn->mapToGlobal(QPoint()) + QPoint(infoBtn->width(), 0), + msg, infoBtn, QRect(), 30000); + }); + + auto remarkLay = new QHBoxLayout; + remarkLay->addWidget(infoBtn); + remarkLay->addWidget(mRemarkLE); + + advLay->addWidget(mExportCB); + advLay->addLayout(publishLay); + advLay->addLayout(remarkLay); + +#ifndef GPGME_HAS_REMARKS + // Hide it if we do not have remark support + mRemarkLE->setVisible(false); + infoBtn->setVisible(false); +#endif + + expander->setContentLayout(advLay); + + mPublishCB->setEnabled(false); + + connect(mExportCB, &QCheckBox::toggled, [this] (bool on) { + mPublishCB->setEnabled(on); + }); + + connect(mSecKeySelect, &KeySelectionCombo::currentKeyChanged, [this] (const GpgME::Key &) { +#ifdef GPGME_HAS_REMARKS + updateRemark(); +#endif + }); + + loadConfig(); + } + + ~Private() + { + } + + 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 updateRemark() + { + if (mRemarkLE->isModified()) { + return; + } +#ifdef GPGME_HAS_REMARKS + GpgME::Key remarkKey = mSecKeySelect->currentKey(); + + if (!remarkKey.isNull()) { + std::vector 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); + } + mRemarkLE->setText(remark); + } +#endif + } + + void setTarget(const GpgME::Key &key) + { + mFprLabel->setText(i18n("Fingerprint: %1", + Formatting::prettyID(key.primaryFingerprint())) + QStringLiteral("
") + + i18n("Only the fingerprint clearly identifies the key and its owner.")); + mUserIDModel.setKey(key); + mTarget = key; + + updateRemark(); + } + + GpgME::Key secKey() const + { + return mSecKeySelect->currentKey(); + } + + void selectUserIDs(const std::vector &uids) + { + const auto all = mTarget.userIDs(); + + std::vector 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 selectedUserIDs() const + { + return mUserIDModel.checkedUserIDs(); + } + + bool exportableSelected() const + { + return mExportCB->isChecked(); + } + + bool publishSelected() const + { + return mPublishCB->isChecked(); + } + + QString remarks() const + { + return mRemarkLE->text().trimmed(); + } + + GpgME::Key target() const + { + return mTarget; + } + +private: + CertifyWidget *q; + QLabel *mFprLabel; + KeySelectionCombo *mSecKeySelect; + QCheckBox *mExportCB; + QCheckBox *mPublishCB; + QLineEdit *mRemarkLE; + + UserIDModel mUserIDModel; + GpgME::Key mTarget; + bool remarkTextChanged; +}; + +CertifyWidget::CertifyWidget(QWidget *parent) : + QWidget(parent), + d(new Private(this)) +{ +} + +void CertifyWidget::setTarget(const GpgME::Key &key) +{ + d->setTarget(key); +} + +GpgME::Key CertifyWidget::target() const +{ + return d->target(); +} + +void CertifyWidget::selectUserIDs(const std::vector &uids) +{ + d->selectUserIDs(uids); +} + +std::vector CertifyWidget::selectedUserIDs() const +{ + return d->selectedUserIDs(); +} + +GpgME::Key CertifyWidget::secKey() const +{ + return d->secKey(); +} + +bool CertifyWidget::exportableSelected() const +{ + return d->exportableSelected(); +} + +QString CertifyWidget::remarks() const +{ + return d->remarks(); +} + +bool CertifyWidget::publishSelected() const +{ + return d->publishSelected(); +} + +// For UserID model +#include "certifywidget.moc" diff --git a/src/dialogs/certifycertificatedialog.h b/src/dialogs/certifywidget.h similarity index 54% copy from src/dialogs/certifycertificatedialog.h copy to src/dialogs/certifywidget.h index 9b5df576b..effc0b604 100644 --- a/src/dialogs/certifycertificatedialog.h +++ b/src/dialogs/certifywidget.h @@ -1,96 +1,85 @@ -/* -*- mode: c++; c-basic-offset:4 -*- - dialogs/signcertificatedialog.h +#ifndef SRC_VIEW_CERTIFYWIDGET_H +#define SRC_VIEW_CERTIFYWIDGET_H +/* dialogs/certifywidget.cpp This file is part of Kleopatra, the KDE keymanager - Copyright (c) 2008 Klarälvdalens Datakonsult AB + Copyright (c) 2019 by g10code GmbH Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ -#ifndef __KLEOPATRA_DIALOGS_CERTIFYCERTIFICATEDIALOG_H__ -#define __KLEOPATRA_DIALOGS_CERTIFYCERTIFICATEDIALOG_H__ +#include -#include - -#include - -#include - -#include +#include +#include namespace GpgME { -class Error; -} + class Key; + class UserID; +} // namespace GpgME namespace Kleo { - -namespace Dialogs -{ - -class CertifyCertificateDialog : public QWizard +/** Widget for OpenPGP certification. */ +class CertifyWidget : public QWidget { Q_OBJECT public: - explicit CertifyCertificateDialog(QWidget *parent = nullptr, Qt::WindowFlags f = {}); - ~CertifyCertificateDialog(); + explicit CertifyWidget(QWidget *parent = nullptr); - bool exportableCertificationSelected() const; + /* Set the key to certify */ + void setTarget(const GpgME::Key &key); - bool trustCertificationSelected() const; + /* Get the key to certify */ + GpgME::Key target() const; - bool nonRevocableCertificationSelected() const; + /* Select specific user ids. Default: all */ + void selectUserIDs(const std::vector &uids); - void setSelectedUserIDs(const std::vector &uids); + /* The user ids that should be signed */ std::vector selectedUserIDs() const; - void setCertificatesWithSecretKeys(const std::vector &keys); - GpgME::Key selectedSecretKey() const; - - bool sendToServer() const; + /* The secret key selected */ + GpgME::Key secKey() const; - unsigned int selectedCheckLevel() const; + /* Should the signature be exportable */ + bool exportableSelected() const; - void setCertificateToCertify(const GpgME::Key &key); + /* Additional remarks (search tags) for the key */ + QString remarks() const; - void connectJob(QGpgME::SignKeyJob *job); - void setError(const GpgME::Error &error); - -Q_SIGNALS: - void certificationPrepared(); + /* Should the signed key be be published */ + bool publishSelected() const; private: class Private; - kdtools::pimpl_ptr d; - Q_PRIVATE_SLOT(d, void certificationResult(GpgME::Error)) + std::shared_ptr d; }; -} -} - -#endif /* __KLEOPATRA_DIALOGS_CERTIFYCERTIFICATEDIALOG_H__ */ +} // namespace Kleo +#endif // SRC_VIEW_CERTIFYWIDGET_H diff --git a/src/utils/remarks.cpp b/src/utils/remarks.cpp new file mode 100644 index 000000000..8f933478e --- /dev/null +++ b/src/utils/remarks.cpp @@ -0,0 +1,89 @@ +/* utils/remarks.cpp + + This file is part of Kleopatra, the KDE keymanager + Copyright (c) 2019 by g10code GmbH + + Kleopatra is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Kleopatra is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "remarks.h" +#include "kleopatra_debug.h" + +#include +#include +#include + +using namespace Kleo; + +bool Remarks::remarksEnabled() +{ + const KConfigGroup conf(KSharedConfig::openConfig(), "RemarkSettings"); + return conf.readEntry("RemarksEnabled", false); +} + +void Remarks::enableRemarks(bool enable) +{ + KConfigGroup conf(KSharedConfig::openConfig(), "RemarkSettings"); + conf.writeEntry("RemarksEnabled", enable); + KeyCache::mutableInstance()->enableRemarks(enable); +} + +GpgME::Key Remarks::remarkKey() +{ + const KConfigGroup conf(KSharedConfig::openConfig(), "RemarkSettings"); + const auto remarkKeyFpr = conf.readEntry("RemarkKeyFpr", QString()); + GpgME::Key key; + if (remarkKeyFpr.isEmpty()) { + return key; + } + key = KeyCache::instance()->findByKeyIDOrFingerprint(remarkKeyFpr.toLatin1().constData()); + if (key.isNull()) { + qCDebug(KLEOPATRA_LOG) << "Failed to find remark key: " << remarkKeyFpr; + return key; + } + return key; +} + +std::vector Remarks::remarkKeys() +{ + std::vector ret; + for (const auto &key: KeyCache::instance()->keys()) { + if (key.isNull() || key.isRevoked() || key.isExpired() || + key.isDisabled() || key.isInvalid() || key.protocol() != GpgME::OpenPGP) { + continue; + } + if (key.ownerTrust() >= GpgME::Key::Full) { + ret.push_back(key); + } + } + return ret; +} + +void Remarks::setRemarkKey(const GpgME::Key &key) +{ + KConfigGroup conf(KSharedConfig::openConfig(), "RemarkSettings"); + conf.writeEntry("RemarkKeyFpr", key.isNull() ? QString() : QString::fromLatin1(key.primaryFingerprint())); +} diff --git a/src/dialogs/certifycertificatedialog.h b/src/utils/remarks.h similarity index 50% copy from src/dialogs/certifycertificatedialog.h copy to src/utils/remarks.h index 9b5df576b..7751990d4 100644 --- a/src/dialogs/certifycertificatedialog.h +++ b/src/utils/remarks.h @@ -1,96 +1,54 @@ -/* -*- mode: c++; c-basic-offset:4 -*- - dialogs/signcertificatedialog.h +#ifndef REMARKS_H +#define REMARKS_H +/* utils/remarks.h This file is part of Kleopatra, the KDE keymanager - Copyright (c) 2008 Klarälvdalens Datakonsult AB + Copyright (c) 2019 by g10code GmbH Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ -#ifndef __KLEOPATRA_DIALOGS_CERTIFYCERTIFICATEDIALOG_H__ -#define __KLEOPATRA_DIALOGS_CERTIFYCERTIFICATEDIALOG_H__ - -#include - -#include - #include -#include - -namespace GpgME -{ -class Error; -} +#include namespace Kleo { - -namespace Dialogs -{ - -class CertifyCertificateDialog : public QWizard +namespace Remarks { - Q_OBJECT -public: - explicit CertifyCertificateDialog(QWidget *parent = nullptr, Qt::WindowFlags f = {}); - ~CertifyCertificateDialog(); - - bool exportableCertificationSelected() const; - - bool trustCertificationSelected() const; - - bool nonRevocableCertificationSelected() const; - - void setSelectedUserIDs(const std::vector &uids); - std::vector selectedUserIDs() const; - - void setCertificatesWithSecretKeys(const std::vector &keys); - GpgME::Key selectedSecretKey() const; - - bool sendToServer() const; - - unsigned int selectedCheckLevel() const; - - void setCertificateToCertify(const GpgME::Key &key); - - void connectJob(QGpgME::SignKeyJob *job); - void setError(const GpgME::Error &error); - -Q_SIGNALS: - void certificationPrepared(); - -private: - class Private; - kdtools::pimpl_ptr d; - Q_PRIVATE_SLOT(d, void certificationResult(GpgME::Error)) -}; - +/* Helper functions to work with remark configuration */ +bool remarksEnabled(); +void enableRemarks(bool enable); +/* Read / write a single remark key into configuration. */ +GpgME::Key remarkKey(); +void setRemarkKey(const GpgME::Key &key); + +/* Get multiple keys to use for remarks. Currently + * this returns all fully trusted OpenPGP Keys. */ +std::vector remarkKeys(); } -} - -#endif /* __KLEOPATRA_DIALOGS_CERTIFYCERTIFICATEDIALOG_H__ */ - +} // namespace Kleo +#endif // REMARKS_H