Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34132177
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
65 KB
Subscribers
None
View Options
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5c708c2f9..6a24cf439 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,212 +1,213 @@
# SPDX-FileCopyrightText: none
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
set(RELEASE_SERVICE_VERSION_MAJOR "22")
set(RELEASE_SERVICE_VERSION_MINOR "07")
set(RELEASE_SERVICE_VERSION_MICRO "70")
# The RELEASE_SERVICE_VERSION is used by Gpg4win to add the Gpg4win version
if (NOT RELEASE_SERVICE_VERSION)
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
endif()
if(RELEASE_SERVICE_VERSION_MICRO LESS 10)
set(KDE_APPLICATIONS_COMPACT_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}${RELEASE_SERVICE_VERSION_MINOR}0${RELEASE_SERVICE_VERSION_MICRO}")
else()
set(KDE_APPLICATIONS_COMPACT_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}${RELEASE_SERVICE_VERSION_MINOR}${RELEASE_SERVICE_VERSION_MICRO}")
endif()
set(KLEOPATRA_VERSION_MAJOR "3")
set(KLEOPATRA_VERSION_MINOR "1")
set(KLEOPATRA_VERSION_MICRO "22")
set(kleopatra_version "${KLEOPATRA_VERSION_MAJOR}.${KLEOPATRA_VERSION_MINOR}.${KLEOPATRA_VERSION_MICRO}.${KDE_APPLICATIONS_COMPACT_VERSION}")
# The following is for Windows
set(kleopatra_version_win "${KLEOPATRA_VERSION_MAJOR}.${KLEOPATRA_VERSION_MINOR}.${KLEOPATRA_VERSION_MICRO}")
set(kleopatra_fileversion_win "${KLEOPATRA_VERSION_MAJOR},${KLEOPATRA_VERSION_MINOR},${KLEOPATRA_VERSION_MICRO},0")
project(kleopatra VERSION ${kleopatra_version})
option(DISABLE_KWATCHGNUPG "Don't build the kwatchgnupg tool [default=OFF]" OFF)
# Standalone build. Find / include everything necessary.
set(KF5_MIN_VERSION "5.93.0")
set(KMIME_VERSION "5.20.40")
set(LIBKLEO_VERSION "5.20.42")
set(QT_REQUIRED_VERSION "5.15.2")
set(GPGME_REQUIRED_VERSION "1.16.0")
if (WIN32)
set(KF5_WANT_VERSION "5.70.0")
set(KMIME_WANT_VERSION "5.12.0")
else ()
set(KF5_WANT_VERSION ${KF5_MIN_VERSION})
set(KMIME_WANT_VERSION ${KMIME_VERSION})
endif ()
find_package(ECM ${KF5_WANT_VERSION} CONFIG REQUIRED)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
include(ECMInstallIcons)
include(ECMSetupVersion)
include(ECMAddTests)
include(GenerateExportHeader)
include(ECMGenerateHeaders)
include(FeatureSummary)
include(CheckFunctionExists)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(ECMAddAppIcon)
include(ECMQtDeclareLoggingCategory)
include(ECMDeprecationSettings)
# Find KF5 packages
find_package(KF5WidgetsAddons ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5ConfigWidgets ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5CoreAddons ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5Codecs ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5Config ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5I18n ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5IconThemes ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5ItemModels ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5XmlGui ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5WindowSystem ${KF5_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5DocTools ${KF5_WANT_VERSION} CONFIG)
find_package(KF5Crash ${KF5_WANT_VERSION} REQUIRED)
set_package_properties(KF5DocTools PROPERTIES
DESCRIPTION "Documentation tools"
PURPOSE "Required to generate Kleopatra documentation."
TYPE OPTIONAL)
# Optional packages
if (WIN32)
# Only a replacement available for Windows so this
# is required on other platforms.
find_package(KF5DBusAddons ${KF5_WANT_VERSION} CONFIG)
set_package_properties(KF5DBusAddons PROPERTIES DESCRIPTION "Support library to work with DBus"
PURPOSE "DBus session integration"
URL "https://inqlude.org/libraries/kdbusaddons.html"
TYPE OPTIONAL)
else()
find_package(KF5DBusAddons ${KF5_WANT_VERSION} CONFIG REQUIRED)
set(_kleopatra_dbusaddons_libs KF5::DBusAddons)
endif()
set(HAVE_QDBUS ${Qt${QT_MAJOR_VERSION}DBus_FOUND})
find_package(Gpgmepp ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
find_package(QGpgme ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.17.0")
set(QGPGME_SUPPORTS_CHANGING_EXPIRATION_OF_COMPLETE_KEY 1)
set(QGPGME_CRYPTOCONFIGENTRY_HAS_DEFAULT_VALUE 1)
set(QGPGME_SUPPORTS_WKDLOOKUP 1)
set(QGPGME_SUPPORTS_IMPORT_WITH_FILTER 1)
set(QGPGME_SUPPORTS_IMPORT_WITH_KEY_ORIGIN 1)
set(QGPGME_SUPPORTS_SECRET_KEY_EXPORT 1)
set(QGPGME_SUPPORTS_SECRET_SUBKEY_EXPORT 1)
set(QGPGME_SUPPORTS_RECEIVING_KEYS_BY_KEY_ID 1)
endif()
if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.17.2")
set(QGPGME_SUPPORTS_KEY_REVOCATION 1)
+ set(QGPGME_SUPPORTS_KEY_REFRESH 1)
endif()
# Kdepimlibs packages
find_package(KF5Libkleo ${LIBKLEO_VERSION} CONFIG REQUIRED)
find_package(KF5Mime ${KMIME_WANT_VERSION} CONFIG REQUIRED)
find_package(Qt${QT_MAJOR_VERSION} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets Test Network PrintSupport)
find_package(Assuan2 REQUIRED)
set(kleopatra_release FALSE)
if(NOT kleopatra_release)
find_package(Git)
if(GIT_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE rc
ERROR_QUIET)
if(rc EQUAL 0)
execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%h ${CMAKE_CURRENT_SOURCE_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE Kleopatra_WC_REVISION)
string(REGEX REPLACE "\n" "" Kleopatra_WC_REVISION "${Kleopatra_WC_REVISION}")
execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%cI ${CMAKE_CURRENT_SOURCE_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE Kleopatra_WC_LAST_CHANGED_DATE)
string(REGEX REPLACE "^([0-9]+)-([0-9]+)-([0-9]+)T([0-9]+):([0-9]+):([0-9]+).*$" "\\1\\2\\3T\\4\\5\\6"
Kleopatra_WC_LAST_CHANGED_DATE "${Kleopatra_WC_LAST_CHANGED_DATE}")
set(kleopatra_version "${kleopatra_version}+git${Kleopatra_WC_LAST_CHANGED_DATE}~${Kleopatra_WC_REVISION}")
endif()
endif()
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version-kleopatra.h.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}
${ASSUAN2_INCLUDES}
)
add_definitions(-D_ASSUAN_ONLY_GPG_ERRORS)
ecm_set_disabled_deprecation_versions(QT 5.14.0 KF 5.93.0)
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-braces -Wno-parentheses -Wno-ignored-qualifiers")
endif()
add_definitions(-DQT_NO_EMIT)
remove_definitions(-DQT_NO_FOREACH)
# Disable the use of QStringBuilder for operator+ to prevent crashes when
# returning the result of concatenating string temporaries in lambdas. We do
# this for example in some std::transform expressions.
# This is a known issue: https://bugreports.qt.io/browse/QTBUG-47066
# Alternatively, one would always have to remember to force the lambdas to
# return a QString instead of QStringBuilder, but that's just too easy to
# forget and, unfortunately, the compiler doesn't issue a warning if one forgets
# this. So, it's just too dangerous.
# One can still use QStringBuilder explicitly with the operator% if necessary.
remove_definitions(-DQT_USE_FAST_OPERATOR_PLUS)
remove_definitions(-DQT_USE_QSTRINGBUILDER)
kde_enable_exceptions()
option(USE_UNITY_CMAKE_SUPPORT "Use UNITY cmake support (speedup compile time)" OFF)
set(COMPILE_WITH_UNITY_CMAKE_SUPPORT OFF)
if (USE_UNITY_CMAKE_SUPPORT)
set(COMPILE_WITH_UNITY_CMAKE_SUPPORT ON)
endif()
add_subdirectory(pics)
add_subdirectory(src)
if(BUILD_TESTING)
add_subdirectory(tests)
add_subdirectory(autotests)
endif()
ecm_qt_install_logging_categories(
EXPORT KLEOPATRA
FILE kleopatra.categories
DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
)
ki18n_install(po)
if(KF5DocTools_FOUND)
kdoctools_install(po)
add_subdirectory(doc)
endif()
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/config-kleopatra.h.cmake b/config-kleopatra.h.cmake
index fd9a9edb4..93bed2da0 100644
--- a/config-kleopatra.h.cmake
+++ b/config-kleopatra.h.cmake
@@ -1,52 +1,55 @@
/* Define to 1 if you have a recent enough libassuan */
#cmakedefine HAVE_USABLE_ASSUAN 1
/* Define to 1 if you have libassuan v2 */
#cmakedefine HAVE_ASSUAN2 1
#ifndef HAVE_ASSUAN2
/* Define to 1 if your libassuan has the assuan_fd_t type */
#cmakedefine HAVE_ASSUAN_FD_T 1
/* Define to 1 if your libassuan has the assuan_inquire_ext function */
#cmakedefine HAVE_ASSUAN_INQUIRE_EXT 1
/* Define to 1 if your assuan_inquire_ext puts the buffer arguments into the callback signature */
#cmakedefine HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT 1
/* Define to 1 if your libassuan has the assuan_sock_get_nonce function */
#cmakedefine HAVE_ASSUAN_SOCK_GET_NONCE 1
#endif
/* Define to 1 if you build libkleopatraclient */
#cmakedefine HAVE_KLEOPATRACLIENT_LIBRARY 1
/* DBus available */
#cmakedefine01 HAVE_QDBUS
/* Defined if QGpgME supports changing the expiration date of the primary key and the subkeys simultaneously */
#cmakedefine QGPGME_SUPPORTS_CHANGING_EXPIRATION_OF_COMPLETE_KEY 1
/* Defined if QGpgME supports retrieving the default value of a config entry */
#cmakedefine QGPGME_CRYPTOCONFIGENTRY_HAS_DEFAULT_VALUE 1
/* Defined if QGpgME supports WKD lookup */
#cmakedefine QGPGME_SUPPORTS_WKDLOOKUP 1
/* Defined if QGpgME supports specifying an import filter when importing keys */
#cmakedefine QGPGME_SUPPORTS_IMPORT_WITH_FILTER 1
/* Defined if QGpgME supports setting key origin when importing keys */
#cmakedefine QGPGME_SUPPORTS_IMPORT_WITH_KEY_ORIGIN 1
/* Defined if QGpgME supports the export of secret keys */
#cmakedefine QGPGME_SUPPORTS_SECRET_KEY_EXPORT 1
/* Defined if QGpgME supports the export of secret subkeys */
#cmakedefine QGPGME_SUPPORTS_SECRET_SUBKEY_EXPORT 1
/* Defined if QGpgME supports receiving keys by their key ids */
#cmakedefine QGPGME_SUPPORTS_RECEIVING_KEYS_BY_KEY_ID 1
/* Defined if QGpgME supports revoking own OpenPGP keys */
#cmakedefine QGPGME_SUPPORTS_KEY_REVOCATION 1
+
+/* Defined if QGpgME supports refreshing keys */
+#cmakedefine QGPGME_SUPPORTS_KEY_REFRESH 1
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c0c22a3b4..002b4ed9b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,394 +1,395 @@
# SPDX-FileCopyrightText: none
# SPDX-License-Identifier: BSD-3-Clause
add_subdirectory(icons)
add_subdirectory(mimetypes)
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
utils/userinfo_win.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)
add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO)
else()
add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1)
add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1)
endif()
ki18n_wrap_ui(_kleopatra_uiserver_SRCS crypto/gui/signingcertificateselectionwidget.ui)
set(_kleopatra_SRCS
utils/accessibility.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/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/tags.cpp
utils/writecertassuantransaction.cpp
utils/keyparameters.cpp
utils/userinfo.cpp
utils/keys.cpp
selftest/selftest.cpp
selftest/enginecheck.cpp
selftest/gpgconfcheck.cpp
selftest/gpgagentcheck.cpp
selftest/libkleopatrarccheck.cpp
selftest/compliancecheck.cpp
${_kleopatra_extra_SRCS}
view/errorlabel.cpp
view/formtextinput.cpp
view/htmllabel.cpp
view/keylistcontroller.cpp
view/keytreeview.cpp
view/searchbar.cpp
view/smartcardwidget.cpp
view/openpgpkeycardwidget.cpp
view/padwidget.cpp
view/pgpcardwidget.cpp
view/pivcardwidget.cpp
view/p15cardwidget.cpp
view/netkeywidget.cpp
view/nullpinwidget.cpp
view/tabwidget.cpp
view/keycacheoverlay.cpp
view/urllabel.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/revokecertificationwidget.cpp
dialogs/revokecertificationdialog.cpp
dialogs/adduseriddialog.cpp
dialogs/deletecertificatesdialog.cpp
dialogs/setinitialpindialog.cpp
dialogs/certificatedetailsdialog.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
dialogs/pivcardapplicationadministrationkeyinputdialog.cpp
dialogs/certificatedetailsinputwidget.cpp
dialogs/createcsrforcardkeydialog.cpp
dialogs/groupdetailsdialog.cpp
dialogs/editgroupdialog.cpp
dialogs/revokekeydialog.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/exportgroupscommand.cpp
commands/importcertificatescommand.cpp
commands/importcertificatefromfilecommand.cpp
commands/importcertificatefromclipboardcommand.cpp
commands/importcertificatefromdatacommand.cpp
commands/importcertificatefromkeyservercommand.cpp
commands/lookupcertificatescommand.cpp
commands/reloadkeyscommand.cpp
+ commands/refreshcertificatecommand.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/revokecertificationcommand.cpp
commands/selftestcommand.cpp
commands/exportsecretkeycommand.cpp
commands/exportsecretkeycommand_old.cpp
commands/exportsecretsubkeycommand.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
commands/cardcommand.cpp
commands/pivgeneratecardkeycommand.cpp
commands/changepincommand.cpp
commands/authenticatepivcardapplicationcommand.cpp
commands/setpivcardapplicationadministrationkeycommand.cpp
commands/certificatetopivcardcommand.cpp
commands/importcertificatefrompivcardcommand.cpp
commands/createopenpgpkeyfromcardkeyscommand.cpp
commands/createcsrforcardkeycommand.cpp
commands/revokekeycommand.cpp
commands/revokeuseridcommand.cpp
${_kleopatra_uiserver_files}
conf/configuredialog.cpp
conf/groupsconfigdialog.cpp
conf/groupsconfigpage.cpp
conf/groupsconfigwidget.cpp
newcertificatewizard/listwidget.cpp
newcertificatewizard/newcertificatewizard.cpp
smartcard/readerstatus.cpp
smartcard/card.cpp
smartcard/openpgpcard.cpp
smartcard/netkeycard.cpp
smartcard/pivcard.cpp
smartcard/p15card.cpp
smartcard/keypairinfo.cpp
smartcard/utils.cpp
smartcard/deviceinfowatcher.cpp
accessibility/accessiblerichtextlabel.cpp
accessibility/accessiblewidgetfactory.cpp
aboutdata.cpp
systrayicon.cpp
kleopatraapplication.cpp
mainwindow.cpp
main.cpp
kleopatra.qrc
)
if(WIN32)
configure_file (versioninfo.rc.in versioninfo.rc)
set(_kleopatra_SRCS ${CMAKE_CURRENT_BINARY_DIR}/versioninfo.rc ${_kleopatra_SRCS})
endif()
set (_kleopatra_SRCS conf/kleopageconfigdialog.cpp ${_kleopatra_SRCS})
ecm_qt_declare_logging_category(_kleopatra_SRCS HEADER kleopatra_debug.h IDENTIFIER KLEOPATRA_LOG CATEGORY_NAME org.kde.pim.kleopatra
DESCRIPTION "kleopatra (kleopatra)"
OLD_CATEGORY_NAMES log_kleopatra
EXPORT 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/ownertrustdialog.ui
dialogs/selectchecklevelwidget.ui
dialogs/selftestdialog.ui
dialogs/setinitialpindialog.ui
dialogs/trustchainwidget.ui
dialogs/subkeyswidget.ui
newcertificatewizard/listwidget.ui
newcertificatewizard/chooseprotocolpage.ui
newcertificatewizard/enterdetailspage.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
kcfg/tagspreferences.kcfgc
kcfg/settings.kcfgc
)
file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/icons/*-apps-kleopatra.png")
ecm_add_app_icon(_kleopatra_SRCS ICONS ${ICONS_SRCS})
add_executable(kleopatra_bin ${_kleopatra_SRCS} ${_kleopatra_uiserver_SRCS})
# For the ConfigureDialog & KCMs
target_link_libraries(kleopatra_bin kcm_kleopatra_static)
#if (COMPILE_WITH_UNITY_CMAKE_SUPPORT)
# set_target_properties(kleopatra_bin PROPERTIES UNITY_BUILD ON)
#endif()
set_target_properties(kleopatra_bin PROPERTIES OUTPUT_NAME kleopatra)
if (WIN32)
set(_kleopatra_platform_libs "secur32")
endif ()
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
Qt${QT_MAJOR_VERSION}::Network
Qt${QT_MAJOR_VERSION}::PrintSupport # Printing secret keys
${_kleopatra_uiserver_extra_libs}
${_kleopatra_dbusaddons_libs}
kleopatraclientcore
${_kleopatra_platform_libs}
)
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(
PROGRAMS data/kleopatra_signencryptfiles.desktop
data/kleopatra_signencryptfolders.desktop
data/kleopatra_decryptverifyfiles.desktop
data/kleopatra_decryptverifyfolders.desktop
DESTINATION ${KDE_INSTALL_DATADIR}/kio/servicemenus
)
diff --git a/src/commands/refreshcertificatecommand.cpp b/src/commands/refreshcertificatecommand.cpp
new file mode 100644
index 000000000..aa6b1ee12
--- /dev/null
+++ b/src/commands/refreshcertificatecommand.cpp
@@ -0,0 +1,193 @@
+/* -*- mode: c++; c-basic-offset:4 -*-
+ commands/refreshcertificatecommand.cpp
+
+ This file is part of Kleopatra, the KDE keymanager
+ SPDX-FileCopyrightText: 2022 g10 Code GmbH
+ SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
+
+ SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#include <config-kleopatra.h>
+
+#include "refreshcertificatecommand.h"
+#include "command_p.h"
+
+#include <KLocalizedString>
+#include <KMessageBox>
+
+#include <QGpgME/Protocol>
+#ifdef QGPGME_SUPPORTS_KEY_REFRESH
+#include <QGpgME/RefreshKeysJob>
+#endif
+
+#include "kleopatra_debug.h"
+
+using namespace Kleo;
+using namespace GpgME;
+
+class RefreshCertificateCommand::Private : public Command::Private
+{
+ friend class ::RefreshCertificateCommand;
+ RefreshCertificateCommand *q_func() const
+ {
+ return static_cast<RefreshCertificateCommand *>(q);
+ }
+public:
+ explicit Private(RefreshCertificateCommand *qq);
+ ~Private() override;
+
+ void start();
+ void cancel();
+
+#ifdef QGPGME_SUPPORTS_KEY_REFRESH
+ std::unique_ptr<QGpgME::RefreshKeysJob> startJob();
+#endif
+ void onJobResult(const Error &err);
+ void showError(const Error &err);
+
+private:
+ Key key;
+#ifdef QGPGME_SUPPORTS_KEY_REFRESH
+ QPointer<QGpgME::RefreshKeysJob> job;
+#endif
+};
+
+RefreshCertificateCommand::Private *RefreshCertificateCommand::d_func()
+{
+ return static_cast<Private *>(d.get());
+}
+const RefreshCertificateCommand::Private *RefreshCertificateCommand::d_func() const
+{
+ return static_cast<const Private *>(d.get());
+}
+
+#define d d_func()
+#define q q_func()
+
+RefreshCertificateCommand::Private::Private(RefreshCertificateCommand *qq)
+ : Command::Private{qq}
+{
+}
+
+RefreshCertificateCommand::Private::~Private() = default;
+
+namespace
+{
+Key getKey(const std::vector<Key> &keys)
+{
+ if (keys.size() != 1) {
+ qCWarning(KLEOPATRA_LOG) << "Expected exactly one key, but got" << keys.size();
+ return {};
+ }
+ const Key key = keys.front();
+ if (key.protocol() == GpgME::UnknownProtocol) {
+ qCWarning(KLEOPATRA_LOG) << "Key has unknown protocol";
+ return {};
+ }
+ return key;
+}
+}
+
+void RefreshCertificateCommand::Private::start()
+{
+ key = getKey(keys());
+ if (key.isNull()) {
+ finished();
+ return;
+ }
+
+#ifdef QGPGME_SUPPORTS_KEY_REFRESH
+ auto refreshJob = startJob();
+ if (!refreshJob) {
+ finished();
+ return;
+ }
+ job = refreshJob.release();
+#else
+ KMessageBox::error(parentWidgetOrView(), i18n("The backend does not support refreshing individual certificates."));
+ finished();
+#endif
+}
+
+void RefreshCertificateCommand::Private::cancel()
+{
+#ifdef QGPGME_SUPPORTS_KEY_REFRESH
+ if (job) {
+ job->slotCancel();
+ }
+ job.clear();
+#endif
+}
+
+#ifdef QGPGME_SUPPORTS_KEY_REFRESH
+std::unique_ptr<QGpgME::RefreshKeysJob> RefreshCertificateCommand::Private::startJob()
+{
+ auto jobFactory = key.protocol() == GpgME::OpenPGP ? QGpgME::openpgp() : QGpgME::smime();
+ Q_ASSERT(jobFactory);
+ std::unique_ptr<QGpgME::RefreshKeysJob> refreshJob{jobFactory->refreshKeysJob()};
+ Q_ASSERT(refreshJob);
+
+ connect(refreshJob.get(), &QGpgME::RefreshKeysJob::result,
+ q, [this](const GpgME::Error &err) {
+ onJobResult(err);
+ });
+ connect(refreshJob.get(), &QGpgME::Job::progress,
+ q, &Command::progress);
+
+ const GpgME::Error err = refreshJob->start({key});
+ if (err) {
+ showError(err);
+ return {};
+ }
+ Q_EMIT q->info(i18nc("@info:status", "Refreshing key..."));
+
+ return refreshJob;
+}
+#endif
+
+void RefreshCertificateCommand::Private::onJobResult(const Error &err)
+{
+ if (err) {
+ showError(err);
+ finished();
+ return;
+ }
+
+ if (!err.isCanceled()) {
+ information(i18nc("@info", "The key was refreshed successfully."),
+ i18nc("@title:window", "Key Refreshed"));
+ }
+ finished();
+}
+
+void RefreshCertificateCommand::Private::showError(const Error &err)
+{
+ error(xi18nc("@info",
+ "<para>An error occurred while refreshing the key:</para>"
+ "<para><message>%1</message></para>",
+ QString::fromLocal8Bit(err.asString())),
+ i18nc("@title:window", "Refreshing Failed"));
+}
+
+RefreshCertificateCommand::RefreshCertificateCommand(const GpgME::Key &key)
+ : Command{key, new Private{this}}
+{
+}
+
+RefreshCertificateCommand::~RefreshCertificateCommand() = default;
+
+void RefreshCertificateCommand::doStart()
+{
+ d->start();
+}
+
+void RefreshCertificateCommand::doCancel()
+{
+ d->cancel();
+}
+
+#undef d
+#undef q
+
+#include "moc_refreshcertificatecommand.cpp"
diff --git a/src/commands/refreshcertificatecommand.h b/src/commands/refreshcertificatecommand.h
new file mode 100644
index 000000000..33c70eeeb
--- /dev/null
+++ b/src/commands/refreshcertificatecommand.h
@@ -0,0 +1,33 @@
+/* -*- mode: c++; c-basic-offset:4 -*-
+ commands/refreshcertificatecommand.h
+
+ This file is part of Kleopatra, the KDE keymanager
+ SPDX-FileCopyrightText: 2022 g10 Code GmbH
+ SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
+
+ SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#pragma once
+
+#include "command.h"
+
+namespace Kleo
+{
+class RefreshCertificateCommand : public Command
+{
+ Q_OBJECT
+public:
+ explicit RefreshCertificateCommand(const GpgME::Key &key);
+ ~RefreshCertificateCommand() override;
+
+private:
+ void doStart() override;
+ void doCancel() override;
+
+private:
+ class Private;
+ inline Private *d_func();
+ inline const Private *d_func() const;
+};
+}
diff --git a/src/dialogs/certificatedetailswidget.cpp b/src/dialogs/certificatedetailswidget.cpp
index 7294aeef3..925c2abfd 100644
--- a/src/dialogs/certificatedetailswidget.cpp
+++ b/src/dialogs/certificatedetailswidget.cpp
@@ -1,1000 +1,1022 @@
/*
dialogs/certificatedetailswidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2016 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2017 Intevation GmbH
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "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/refreshcertificatecommand.h"
#include "commands/revokecertificationcommand.h"
#include "commands/revokeuseridcommand.h"
#include "commands/adduseridcommand.h"
#include "commands/genrevokecommand.h"
#include "commands/detailscommand.h"
#include "commands/dumpcertificatecommand.h"
#include "utils/keys.h"
#include "utils/tags.h"
#include <Libkleo/Formatting>
#include <Libkleo/Dn>
#include <Libkleo/KeyCache>
#include <Libkleo/GnuPG>
#include <KLocalizedString>
#include <KMessageBox>
#include <gpgme++/context.h>
#include <gpgme++/key.h>
#include <gpgme++/keylistresult.h>
#include <gpgme++/tofuinfo.h>
#include <QGpgME/Debug>
#include <QGpgME/Protocol>
#include <QGpgME/KeyListJob>
#include <QClipboard>
#include <QDateTime>
#include <QGridLayout>
#include <QGroupBox>
#include <QGuiApplication>
#include <QHBoxLayout>
#include <QLabel>
#include <QLocale>
#include <QMenu>
#include <QPushButton>
#include <QSpacerItem>
#include <QStringBuilder>
#include <QTreeWidget>
#include <set>
#define HIDE_ROW(row) \
ui.row->setVisible(false); \
ui.row##Lbl->setVisible(false);
#define SHOW_ROW(row) \
ui.row->setVisible(true); \
ui.row##Lbl->setVisible(true);
Q_DECLARE_METATYPE(GpgME::UserID)
using namespace Kleo;
class CertificateDetailsWidget::Private
{
public:
Private(CertificateDetailsWidget *qq);
void setupCommonProperties();
void setupPGPProperties();
void setupSMIMEProperties();
void revokeUserID(const GpgME::UserID &uid);
void genRevokeCert();
+ void refreshCertificate();
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 updateKey();
void setUpdatedKey(const GpgME::Key &key);
void keyListDone(const GpgME::KeyListResult &,
const std::vector<GpgME::Key> &, const QString &,
const GpgME::Error &);
void copyFingerprintToClipboard();
private:
CertificateDetailsWidget *const q;
public:
GpgME::Key key;
bool updateInProgress = false;
private:
struct UI {
QGridLayout *gridLayout_2;
QHBoxLayout *hboxLayout_1;
QPushButton *addUserIDBtn;
QPushButton *changePassphraseBtn;
QPushButton *trustChainDetailsBtn;
QPushButton *genRevokeBtn;
+ QPushButton *refreshBtn;
QPushButton *certifyBtn;
QGroupBox *groupBox;
QGridLayout *gridLayout;
QLabel *validFromLbl;
QLabel *validFrom;
QSpacerItem *horizontalSpacer_3;
QLabel *expiresLbl;
QHBoxLayout *horizontalLayout_3;
QLabel *expires;
QPushButton *changeExpirationBtn;
QLabel *typeLbl;
QLabel *type;
QLabel *fingerprintLbl;
QLabel *fingerprint;
QPushButton *copyFingerprintBtn;
QLabel *publishingLbl;
QPushButton *publishing;
QLabel *smimeIssuerLbl;
QLabel *smimeIssuer;
QLabel *compliance;
QLabel *complianceLbl;
QLabel *trustedIntroducerLbl;
QLabel *trustedIntroducer;
QHBoxLayout *horizontalLayout;
QPushButton *moreDetailsBtn;
QPushButton *exportBtn;
QPushButton *webOfTrustBtn;
QSpacerItem *horizontalSpacer;
QTreeWidget *userIDTable;
QLabel *label;
QLabel *smimeOwnerLbl;
QLabel *smimeRelatedAddresses;
QLabel *smimeOwner;
void setupUi(QWidget *parent)
{
gridLayout_2 = new QGridLayout(parent);
gridLayout_2->setContentsMargins(0, 0, 0, 0);
hboxLayout_1 = new QHBoxLayout();
addUserIDBtn = new QPushButton(i18nc("@action:button", "Add User ID"), parent);
hboxLayout_1->addWidget(addUserIDBtn);
changePassphraseBtn = new QPushButton(i18nc("@action:button", "Change Passphrase"), parent);
hboxLayout_1->addWidget(changePassphraseBtn);
trustChainDetailsBtn = new QPushButton(i18nc("@action:button", "Trust Chain Details..."), parent);
hboxLayout_1->addWidget(trustChainDetailsBtn);
genRevokeBtn = new QPushButton(i18nc("@action:button", "Generate Revocation Certificate"), parent);
genRevokeBtn->setToolTip(u"<html>" %
i18n("A revocation certificate is a file that serves as a \"kill switch\" to publicly "
"declare that a key shall not anymore be used. It is not possible "
"to retract such a revocation certificate once it has been published.") %
u"</html>");
hboxLayout_1->addWidget(genRevokeBtn);
+ refreshBtn = new QPushButton{i18nc("@action:button", "Refresh"), parent};
+#ifndef QGPGME_SUPPORTS_KEY_REFRESH
+ refreshBtn->setVisible(false);
+#endif
+ hboxLayout_1->addWidget(refreshBtn);
+
certifyBtn = new QPushButton(i18nc("@action:button", "Certify"), parent);
hboxLayout_1->addWidget(certifyBtn);
gridLayout_2->addLayout(hboxLayout_1, 4, 0, 1, 3);
groupBox = new QGroupBox(i18n("Certificate Details"), parent);
groupBox->setFlat(false);
gridLayout = new QGridLayout(groupBox);
int row = 0;
validFromLbl = new QLabel(i18n("Valid from:"), groupBox);
gridLayout->addWidget(validFromLbl, row, 0, 1, 1);
validFrom = new QLabel(groupBox);
validFrom->setTextInteractionFlags(Qt::TextSelectableByMouse);
gridLayout->addWidget(validFrom, row, 1, 1, 1);
horizontalSpacer_3 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
gridLayout->addItem(horizontalSpacer_3, row, 2, 1, 1);
row++;
expiresLbl = new QLabel(i18n("Expires:"), groupBox);
gridLayout->addWidget(expiresLbl, row, 0, 1, 1);
horizontalLayout_3 = new QHBoxLayout();
expires = new QLabel(groupBox);
expires->setTextInteractionFlags(Qt::TextSelectableByMouse);
horizontalLayout_3->addWidget(expires);
changeExpirationBtn = new QPushButton(groupBox);
changeExpirationBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
changeExpirationBtn->setIcon(QIcon::fromTheme(QStringLiteral("editor")));
changeExpirationBtn->setToolTip(i18nc("@info:tooltip", "Change the expiration date"));
horizontalLayout_3->addWidget(changeExpirationBtn);
gridLayout->addLayout(horizontalLayout_3, row, 1, 1, 1);
row++;
typeLbl = new QLabel(i18n("Type:"), groupBox);
gridLayout->addWidget(typeLbl, row, 0, 1, 1);
type = new QLabel(groupBox);
type->setTextInteractionFlags(Qt::TextSelectableByMouse);
gridLayout->addWidget(type, row, 1, 1, 1);
row++;
fingerprintLbl = new QLabel(i18n("Fingerprint:"), groupBox);
gridLayout->addWidget(fingerprintLbl, row, 0, 1, 1);
{
auto hbox = new QHBoxLayout;
fingerprint = new QLabel{groupBox};
fingerprint->setTextInteractionFlags(Qt::TextSelectableByMouse);
hbox->addWidget(fingerprint);
copyFingerprintBtn = new QPushButton{groupBox};
copyFingerprintBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
copyFingerprintBtn->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
copyFingerprintBtn->setToolTip(i18nc("@info:tooltip", "Copy the fingerprint to the clipboard"));
copyFingerprintBtn->setVisible(QGuiApplication::clipboard());
hbox->addWidget(copyFingerprintBtn);
hbox->addStretch();
gridLayout->addLayout(hbox, row, 1, 1, 2);
}
row++;
publishingLbl = new QLabel(i18n("Publishing:"), groupBox);
gridLayout->addWidget(publishingLbl, row, 0, 1, 1);
publishing = new QPushButton(i18nc("@action:button", "Publish Certificate"), groupBox);
gridLayout->addWidget(publishing, row, 1, 1, 1);
row++;
smimeIssuerLbl = new QLabel(i18n("Issuer:"), groupBox);
gridLayout->addWidget(smimeIssuerLbl, row, 0, 1, 1);
smimeIssuer = new QLabel(groupBox);
smimeIssuer->setWordWrap(true);
smimeIssuer->setTextInteractionFlags(Qt::TextBrowserInteraction);
gridLayout->addWidget(smimeIssuer, row, 1, 1, 2);
row++;
compliance = new QLabel(i18n("Compliance:"), groupBox);
compliance->setWordWrap(true);
compliance->setTextInteractionFlags(Qt::TextBrowserInteraction);
gridLayout->addWidget(compliance, row, 0, 1, 1);
complianceLbl = new QLabel(groupBox);
complianceLbl->setWordWrap(true);
complianceLbl->setTextInteractionFlags(Qt::TextBrowserInteraction);
gridLayout->addWidget(complianceLbl, row, 1, 1, 2);
row++;
trustedIntroducerLbl = new QLabel(i18n("Trusted introducer for:"), groupBox);
trustedIntroducerLbl->setToolTip(i18n("See certifications for details."));
trustedIntroducerLbl->setTextInteractionFlags(Qt::TextBrowserInteraction);
gridLayout->addWidget(trustedIntroducerLbl, row, 0, 1, 1);
trustedIntroducer = new QLabel(groupBox);
trustedIntroducer->setWordWrap(true);
trustedIntroducer->setToolTip(i18n("See certifications for details."));
trustedIntroducer->setTextInteractionFlags(Qt::TextBrowserInteraction);
gridLayout->addWidget(trustedIntroducer, row, 1, 1, 2);
row++;
horizontalLayout = new QHBoxLayout();
moreDetailsBtn = new QPushButton(i18nc("@action:button", "More Details..."), groupBox);
horizontalLayout->addWidget(moreDetailsBtn);
exportBtn = new QPushButton(i18nc("@action:button", "Export..."), groupBox);
horizontalLayout->addWidget(exportBtn);
webOfTrustBtn = new QPushButton(i18nc("@action:button", "Certifications..."), groupBox);
horizontalLayout->addWidget(webOfTrustBtn);
horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
horizontalLayout->addItem(horizontalSpacer);
gridLayout->addLayout(horizontalLayout, row, 0, 1, 3);
gridLayout_2->addWidget(groupBox, 6, 0, 1, 3);
userIDTable = new QTreeWidget(parent);
QTreeWidgetItem *__qtreewidgetitem = new QTreeWidgetItem();
__qtreewidgetitem->setText(0, QString::fromUtf8("1"));
userIDTable->setHeaderItem(__qtreewidgetitem);
userIDTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
userIDTable->setSelectionMode(QAbstractItemView::SingleSelection);
userIDTable->setRootIsDecorated(false);
userIDTable->setUniformRowHeights(true);
userIDTable->setAllColumnsShowFocus(true);
gridLayout_2->addWidget(userIDTable, 3, 0, 1, 3);
label = new QLabel(i18n("You can use this certificate to secure communication with the following email addresses:"), parent);
label->setWordWrap(true);
gridLayout_2->addWidget(label, 0, 0, 1, 3);
smimeOwnerLbl = new QLabel(i18n("Owner:"), parent);
gridLayout_2->addWidget(smimeOwnerLbl, 1, 0, 1, 1);
smimeRelatedAddresses = new QLabel(i18n("Related addresses:"), parent);
QFont font;
font.setBold(true);
font.setWeight(75);
smimeRelatedAddresses->setFont(font);
gridLayout_2->addWidget(smimeRelatedAddresses, 2, 0, 1, 1);
smimeOwner = new QLabel(parent);
smimeOwner->setWordWrap(true);
smimeOwner->setTextInteractionFlags(Qt::TextBrowserInteraction);
gridLayout_2->addWidget(smimeOwner, 1, 1, 1, 2);
}
} ui;
};
CertificateDetailsWidget::Private::Private(CertificateDetailsWidget *qq)
: q{qq}
{
ui.setupUi(q);
connect(ui.userIDTable, &QAbstractItemView::customContextMenuRequested,
q, [this](const QPoint &p) { userIDTableContextMenuRequested(p); });
connect(ui.addUserIDBtn, &QPushButton::clicked,
q, [this]() { addUserID(); });
connect(ui.changePassphraseBtn, &QPushButton::clicked,
q, [this]() { changePassphrase(); });
connect(ui.genRevokeBtn, &QPushButton::clicked,
q, [this]() { genRevokeCert(); });
connect(ui.changeExpirationBtn, &QPushButton::clicked,
q, [this]() { changeExpiration(); });
connect(ui.smimeOwner, &QLabel::linkActivated,
q, [this](const QString &link) { smimeLinkActivated(link); });
connect(ui.smimeIssuer, &QLabel::linkActivated,
q, [this](const QString &link) { smimeLinkActivated(link); });
connect(ui.trustChainDetailsBtn, &QPushButton::pressed,
q, [this]() { showTrustChainDialog(); });
connect(ui.moreDetailsBtn, &QPushButton::pressed,
q, [this]() { showMoreDetails(); });
connect(ui.publishing, &QPushButton::pressed,
q, [this]() { publishCertificate(); });
+ connect(ui.refreshBtn, &QPushButton::clicked,
+ q, [this]() { refreshCertificate(); });
connect(ui.certifyBtn, &QPushButton::clicked,
q, [this]() { certifyClicked(); });
connect(ui.webOfTrustBtn, &QPushButton::clicked,
q, [this]() { webOfTrustClicked(); });
connect(ui.exportBtn, &QPushButton::clicked,
q, [this]() { exportClicked(); });
connect(ui.copyFingerprintBtn, &QPushButton::clicked,
q, [this]() { copyFingerprintToClipboard(); });
connect(Kleo::KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged,
q, [this]() { keysMayHaveChanged(); });
}
void CertificateDetailsWidget::Private::setupCommonProperties()
{
// TODO: Enable once implemented
HIDE_ROW(publishing)
const bool hasSecret = key.hasSecret();
const bool isOpenPGP = key.protocol() == GpgME::OpenPGP;
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::gnupgIsDeVsCompliant()) {
HIDE_ROW(compliance)
} else {
ui.complianceLbl->setText(Kleo::Formatting::complianceStringForKey(key));
}
ui.userIDTable->clear();
QStringList headers = { i18n("Email"), i18n("Name"), i18n("Trust Level"), i18n("Tags") };
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 tagList;
for (const auto &tag: uid.remarks(Tags::tagKeys(), err)) {
if (err) {
qCWarning(KLEOPATRA_LOG) << "Getting remarks for user ID" << uid.id() << "failed:" << err;
}
tagList << QString::fromStdString(tag);
}
qCDebug(KLEOPATRA_LOG) << "tagList:" << tagList;
const auto tags = tagList.join(QStringLiteral("; "));
item->setData(3, Qt::DisplayRole, tags);
item->setData(3, Qt::ToolTipRole, toolTip);
ui.userIDTable->addTopLevelItem(item);
}
if (!Tags::tagsEnabled()) {
ui.userIDTable->hideColumn(3);
}
}
void CertificateDetailsWidget::Private::revokeUserID(const GpgME::UserID &userId)
{
const QString message = xi18nc(
"@info",
"<para>Do you really want to revoke the user ID<nl/><emphasis>%1</emphasis> ?</para>",
QString::fromUtf8(userId.id()));
auto confirmButton = KStandardGuiItem::yes();
confirmButton.setText(i18nc("@action:button", "Revoke User ID"));
confirmButton.setToolTip({});
const auto choice = KMessageBox::questionYesNo(
q->window(), message, i18nc("@title:window", "Confirm Revocation"),
confirmButton, KStandardGuiItem::cancel(), {}, KMessageBox::Notify | KMessageBox::WindowModal);
if (choice != KMessageBox::Yes) {
return;
}
auto cmd = new Commands::RevokeUserIDCommand(userId);
cmd->setParentWidget(q);
ui.userIDTable->setEnabled(false);
connect(cmd, &Command::finished,
q, [this]() {
ui.userIDTable->setEnabled(true);
updateKey();
});
cmd->start();
}
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::refreshCertificate()
+{
+ auto cmd = new Kleo::RefreshCertificateCommand{key};
+ QObject::connect(cmd, &Kleo::RefreshCertificateCommand::finished,
+ q, [this]() {
+ ui.refreshBtn->setEnabled(true);
+ });
+ ui.refreshBtn->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<WebOfTrustDialog> dlg(new WebOfTrustDialog(q));
dlg->setKey(key);
dlg->exec();
}
void CertificateDetailsWidget::Private::exportClicked()
{
QScopedPointer<ExportDialog> 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);
updateKey();
});
ui.addUserIDBtn->setEnabled(false);
cmd->start();
}
namespace
{
void ensureThatKeyDetailsAreLoaded(GpgME::Key &key)
{
if (key.userID(0).numSignatures() == 0) {
key.update();
}
}
}
void CertificateDetailsWidget::Private::keysMayHaveChanged()
{
auto newKey = Kleo::KeyCache::instance()->findByFingerprint(key.primaryFingerprint());
if (!newKey.isNull()) {
ensureThatKeyDetailsAreLoaded(newKey);
setUpdatedKey(newKey);
}
}
void CertificateDetailsWidget::Private::showTrustChainDialog()
{
QScopedPointer<TrustChainDialog> dlg(new TrustChainDialog(q));
dlg->setKey(key);
dlg->exec();
}
void CertificateDetailsWidget::Private::publishCertificate()
{
qCWarning(KLEOPATRA_LOG) << "publishCertificateis not implemented.";
//TODO
}
namespace
{
bool isLastValidUserID(const GpgME::UserID &userId)
{
if (isRevokedOrExpired(userId)) {
return false;
}
const auto userIds = userId.parent().userIDs();
const int numberOfValidUserIds = std::count_if(std::begin(userIds), std::end(userIds),
[](const auto &u) {
return !isRevokedOrExpired(u);
});
return numberOfValidUserIds == 1;
}
bool canRevokeUserID(const GpgME::UserID &userId)
{
const auto key = userId.parent();
return (!userId.isNull() //
&& key.protocol() == GpgME::OpenPGP
&& canCreateCertifications(key)
&& !isLastValidUserID(userId));
}
}
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<GpgME::UserID>();
auto 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);
updateKey();
});
cmd->start();
});
{
auto action = menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-revoke")),
i18nc("@action:inmenu", "Revoke User ID"),
q, [this, userID]() {
revokeUserID(userID);
});
action->setEnabled(canRevokeUserID(userID));
}
if (Kleo::Commands::RevokeCertificationCommand::isSupported()) {
menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-revoke")),
i18n("Revoke Certification..."),
q, [this, userID]() {
auto cmd = new Kleo::Commands::RevokeCertificationCommand(userID);
ui.userIDTable->setEnabled(false);
connect(cmd, &Kleo::Commands::RevokeCertificationCommand::finished,
q, [this]() {
ui.userIDTable->setEnabled(true);
updateKey();
});
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<SubKeysDialog> 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("<table border=\"0\" cell-padding=\"5\">");
const auto appendRow = [&html](const QString &lbl, const QString &val) {
html += QStringLiteral("<tr>"
"<th style=\"text-align: right; padding-right: 5px; white-space: nowrap;\">%1:</th>"
"<td style=\"white-space: nowrap;\">%2</td>"
"</tr>")
.arg(lbl, val);
};
const auto appendHeader = [this, &html](const QString &hdr) {
html += QStringLiteral("<tr><th colspan=\"2\" style=\"background-color: %1; color: %2\">%3</th></tr>")
.arg(q->palette().highlight().color().name(),
q->palette().highlightedText().color().name(),
hdr);
};
const auto dateTime = [](long ts) {
QLocale l;
return ts == 0 ? i18n("never") : l.toString(QDateTime::fromSecsSinceEpoch(ts), QLocale::ShortFormat);
};
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("</table>");
// 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("<!-- %1 //-->").arg(QString::fromUtf8(uid.id()));
return html;
}
namespace
{
auto isGood(const GpgME::UserID::Signature &signature)
{
return signature.status() == GpgME::UserID::Signature::NoError &&
!signature.isInvalid() &&
0x10 <= signature.certClass() && signature.certClass() <= 0x13;
}
auto accumulateTrustDomains(const std::vector<GpgME::UserID::Signature> &signatures)
{
return std::accumulate(
std::begin(signatures), std::end(signatures),
std::set<QString>(),
[] (auto domains, const auto &signature) {
if (isGood(signature) && signature.isTrustSignature()) {
domains.insert(Formatting::trustSignatureDomain(signature));
}
return domains;
}
);
}
auto accumulateTrustDomains(const std::vector<GpgME::UserID> &userIds)
{
return std::accumulate(
std::begin(userIds), std::end(userIds),
std::set<QString>(),
[] (auto domains, const auto &userID) {
const auto newDomains = accumulateTrustDomains(userID.signatures());
std::copy(std::begin(newDomains), std::end(newDomains), std::inserter(domains, std::end(domains)));
return domains;
}
);
}
}
void CertificateDetailsWidget::Private::setupPGPProperties()
{
HIDE_ROW(smimeOwner)
HIDE_ROW(smimeIssuer)
ui.smimeRelatedAddresses->setVisible(false);
ui.trustChainDetailsBtn->setVisible(false);
ui.userIDTable->setContextMenuPolicy(Qt::CustomContextMenu);
const auto trustDomains = accumulateTrustDomains(key.userIDs());
if (trustDomains.empty()) {
HIDE_ROW(trustedIntroducer)
} else {
SHOW_ROW(trustedIntroducer)
ui.trustedIntroducer->setText(QStringList(std::begin(trustDomains), std::end(trustDomains)).join(u", "));
}
}
static QString formatDNToolTip(const Kleo::DN &dn)
{
QString html = QStringLiteral("<table border=\"0\" cell-spacing=15>");
const auto appendRow = [&html, dn](const QString &lbl, const QString &attr) {
const QString val = dn[attr];
if (!val.isEmpty()) {
html += QStringLiteral(
"<tr><th style=\"text-align: left; white-space: nowrap\">%1:</th>"
"<td style=\"white-space: nowrap\">%2</td>"
"</tr>").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("</table>");
return html;
}
void CertificateDetailsWidget::Private::setupSMIMEProperties()
{
HIDE_ROW(publishing)
HIDE_ROW(trustedIntroducer)
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("<name> of <company>", "%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("<a href=\"#issuerDetails\">%1</a>").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;
}
void CertificateDetailsWidget::Private::copyFingerprintToClipboard()
{
if (auto clipboard = QGuiApplication::clipboard()) {
clipboard->setText(QString::fromLatin1(key.primaryFingerprint()));
}
}
CertificateDetailsWidget::CertificateDetailsWidget(QWidget *parent)
: QWidget{parent}
, d{std::make_unique<Private>(this)}
{
}
CertificateDetailsWidget::~CertificateDetailsWidget() = default;
void CertificateDetailsWidget::Private::keyListDone(const GpgME::KeyListResult &,
const std::vector<GpgME::Key> &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::updateKey()
{
key.update();
setUpdatedKey(key);
}
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::SignatureNotations);
if (key.hasSecret()) {
ctx->addKeyListMode(GpgME::WithSecret);
}
// Windows QGpgME new style connect problem makes this necessary.
connect(job, SIGNAL(result(GpgME::KeyListResult,std::vector<GpgME::Key>,QString,GpgME::Error)),
this, SLOT(keyListDone(GpgME::KeyListResult,std::vector<GpgME::Key>,QString,GpgME::Error)));
job->start(QStringList() << QLatin1String(key.primaryFingerprint()));
}
GpgME::Key CertificateDetailsWidget::key() const
{
return d->key;
}
#include "moc_certificatedetailswidget.cpp"
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Dec 8, 3:27 AM (6 m, 46 s)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
04/b2/e4cdedb9c236fe93c6cf64f97042
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment