Page MenuHome GnuPG

No OneTemporary

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8410317d2..aabc9be16 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,289 +1,291 @@
# SPDX-FileCopyrightText: none
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
set(RELEASE_SERVICE_VERSION_MAJOR "23")
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 "27")
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")
if (NOT KLEOPATRA_DISTRIBUTION_TEXT)
# This is only used on Windows for the file attributes of Kleopatra
set(KLEOPATRA_DISTRIBUTION_TEXT "KDE")
endif()
project(kleopatra VERSION ${kleopatra_version})
option(DISABLE_KWATCHGNUPG "Don't build the kwatchgnupg tool [default=OFF]" OFF)
# Standalone build. Find / include everything necessary.
set(KF_MIN_VERSION "5.105.0")
set(KIDENTITYMANAGEMENT_VERSION "5.23.40")
set(KMAILTRANSPORT_VERSION "5.23.40")
set(AKONADI_MIME_VERSION "5.23.41")
set(KMIME_VERSION "5.23.40")
+set(MIMETREEPARSER_VERSION "5.23.40")
set(LIBKLEO_VERSION "5.23.46")
set(QT_REQUIRED_VERSION "5.15.2")
if (QT_MAJOR_VERSION STREQUAL "6")
set(QT_REQUIRED_VERSION "6.4.0")
endif()
set(GPGME_REQUIRED_VERSION "1.16.0")
set(LIBASSUAN_REQUIRED_VERSION "2.4.2")
set(GPG_ERROR_REQUIRED_VERSION "1.36")
if (WIN32)
set(KF5_WANT_VERSION "5.70.0")
set(KMIME_WANT_VERSION "5.12.0")
else ()
set(KF5_WANT_VERSION ${KF_MIN_VERSION})
set(KMIME_WANT_VERSION ${KMIME_VERSION})
endif ()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
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)
include(KDEClangFormat)
if (QT_MAJOR_VERSION STREQUAL "6")
set(QT_REQUIRED_VERSION "6.4.0")
set(KF_MIN_VERSION "5.240.0")
set(KF_MAJOR_VERSION "6")
else()
set(KF_MIN_VERSION "5.105.0")
set(KF_MAJOR_VERSION "5")
endif()
# Find KF5 packages
find_package(KF${KF_MAJOR_VERSION} ${KF5_WANT_VERSION}
REQUIRED COMPONENTS
Codecs
Config
ConfigWidgets
CoreAddons
Crash
I18n
IconThemes
ItemModels
KCMUtils
KIO
WidgetsAddons
WindowSystem
XmlGui
OPTIONAL_COMPONENTS
DocTools
)
set_package_properties(KF${KF_MAJOR_VERSION}DocTools 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(KF${KF_MAJOR_VERSION}DBusAddons ${KF5_WANT_VERSION} CONFIG)
set_package_properties(KF${KF_MAJOR_VERSION}DBusAddons PROPERTIES DESCRIPTION "Support library to work with DBus"
PURPOSE "DBus session integration"
URL "https://inqlude.org/libraries/kdbusaddons.html"
TYPE OPTIONAL)
else()
find_package(KF${KF_MAJOR_VERSION}DBusAddons ${KF5_WANT_VERSION} CONFIG REQUIRED)
set(_kleopatra_dbusaddons_libs KF${KF_MAJOR_VERSION}::DBusAddons)
endif()
set(HAVE_QDBUS ${Qt${QT_MAJOR_VERSION}DBus_FOUND})
find_package(Gpgmepp ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
if (Gpgmepp_VERSION VERSION_GREATER_EQUAL "1.19.0")
set(GPGMEPP_SUPPORTS_SET_CURVE 1)
endif()
if (Gpgmepp_VERSION VERSION_GREATER_EQUAL "1.19.1")
set(GPGMEPP_KEY_CANSIGN_IS_FIXED 1)
endif()
if (QT_MAJOR_VERSION STREQUAL "6")
find_package(QGpgmeQt6 ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
else()
find_package(QGpgme ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
endif()
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.18.0")
set(QGPGME_SUPPORTS_KEY_REVOCATION 1)
set(QGPGME_SUPPORTS_KEY_REFRESH 1)
set(QGPGME_SUPPORTS_SET_FILENAME 1)
set(QGPGME_SUPPORTS_SET_PRIMARY_UID 1)
endif()
if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.19.0")
set(QGPGME_SUPPORTS_DEFERRED_IMPORT_JOB 1)
set(QGPGME_SUPPORTS_ARCHIVE_JOBS 1)
set(QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS 1)
endif()
if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.21.0")
set(QGPGME_ARCHIVE_JOBS_SUPPORT_OUTPUT_FILENAME 1)
set(QGPGME_ARCHIVE_JOBS_SUPPORT_INPUT_FILENAME 1)
endif()
if (QT_MAJOR_VERSION STREQUAL "6")
find_package(Qt6Core5Compat)
endif()
# Kdepimlibs packages
find_package(KPim${KF_MAJOR_VERSION}Libkleo ${LIBKLEO_VERSION} CONFIG REQUIRED)
find_package(KPim${KF_MAJOR_VERSION}Mime ${KMIME_WANT_VERSION} CONFIG REQUIRED)
find_package(KPim${KF_MAJOR_VERSION}IdentityManagement ${KIDENTITYMANAGEMENT_VERSION} CONFIG)
find_package(KPim${KF_MAJOR_VERSION}MailTransport ${KMAILTRANSPORT_VERSION} CONFIG)
find_package(KPim${KF_MAJOR_VERSION}AkonadiMime ${AKONADI_MIME_VERSION} CONFIG)
+find_package(KPim${KF_MAJOR_VERSION}MimeTreeParserWidgets ${MIMETREEPARSER_VERSION} CONFIG)
find_package(Qt${QT_MAJOR_VERSION} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets Test Network PrintSupport)
find_package(LibAssuan ${LIBASSUAN_REQUIRED_VERSION} REQUIRED)
set_package_properties(LibAssuan PROPERTIES
TYPE REQUIRED
PURPOSE "Needed for Kleopatra to act as the GnuPG UI Server"
)
find_package(LibGpgError ${GPG_ERROR_REQUIRED_VERSION} REQUIRED)
set_package_properties(LibGpgError PROPERTIES
TYPE 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)
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}
)
if (WIN32)
# On Windows, we need to use stuff deprecated since Qt 5.11, e.g. from QDesktopWidget
ecm_set_disabled_deprecation_versions(QT 5.10.0 KF 5.103.0)
else ()
ecm_set_disabled_deprecation_versions(QT 6.4 KF 5.103.0)
endif ()
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-braces -Wno-parentheses -Wno-ignored-qualifiers")
endif()
if(MINGW)
# we do not care about different signedness of passed pointer arguments
add_compile_options($<$<COMPILE_LANGUAGE:C>:-Wno-pointer-sign>)
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(KF${KF_MAJOR_VERSION}DocTools_FOUND)
kdoctools_install(po)
add_subdirectory(doc)
endif()
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
# add clang-format target for all our real source files
file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h *.c)
kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9cd2745c7..38323eac2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,619 +1,623 @@
# 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
selftest/registrycheck.cpp selftest/registrycheck.h
utils/gnupg-registry.c
utils/userinfo_win.cpp
utils/windowsprocessdevice.cpp utils/windowsprocessdevice.h
versioninfo.rc kleopatra.w32-manifest
)
else()
set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_unix.cpp)
set(_kleopatra_extra_SRCS)
endif()
set(_kleopatra_uiserver_SRCS
${_kleopatra_extra_uiserver_SRCS}
selftest/uiservercheck.cpp selftest/uiservercheck.h
uiserver/assuanserverconnection.cpp uiserver/assuanserverconnection.h
uiserver/createchecksumscommand.cpp uiserver/createchecksumscommand.h
uiserver/decryptverifycommandemailbase.cpp uiserver/decryptverifycommandemailbase.h
uiserver/decryptverifycommandfilesbase.cpp uiserver/decryptverifycommandfilesbase.h
uiserver/echocommand.cpp uiserver/echocommand.h
uiserver/encryptcommand.cpp uiserver/encryptcommand.h
uiserver/importfilescommand.cpp uiserver/importfilescommand.h
uiserver/prepencryptcommand.cpp uiserver/prepencryptcommand.h
uiserver/prepsigncommand.cpp uiserver/prepsigncommand.h
uiserver/selectcertificatecommand.cpp
uiserver/sessiondata.cpp uiserver/sessiondata.h
uiserver/signcommand.cpp uiserver/signcommand.h
uiserver/signencryptfilescommand.cpp
uiserver/uiserver.cpp
uiserver/verifychecksumscommand.cpp uiserver/verifychecksumscommand.h
)
set(_kleopatra_uiserver_extra_libs LibAssuan::LibAssuan LibGpgError::LibGpgError)
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()
if(KPim${KF_MAJOR_VERSION}IdentityManagement_FOUND AND KPim${KF_MAJOR_VERSION}MailTransport_FOUND AND KPim${KF_MAJOR_VERSION}AkonadiMime_FOUND)
set(_kleopatra_mail_libs
KPim${KF_MAJOR_VERSION}::IdentityManagement # Export OpenPGP keys using WKS
KPim${KF_MAJOR_VERSION}::MailTransport
KPim${KF_MAJOR_VERSION}::AkonadiMime
)
add_definitions(-DMAILAKONADI_ENABLED)
endif()
ki18n_wrap_ui(_kleopatra_uiserver_SRCS crypto/gui/signingcertificateselectionwidget.ui)
set(_kleopatra_SRCS
${_kleopatra_extra_SRCS}
accessibility/accessiblelink.cpp
accessibility/accessiblelink_p.h
accessibility/accessiblerichtextlabel.cpp
accessibility/accessiblerichtextlabel_p.h
accessibility/accessiblevaluelabel.cpp
accessibility/accessiblevaluelabel_p.h
accessibility/accessiblewidgetfactory.cpp
accessibility/accessiblewidgetfactory.h
commands/adduseridcommand.cpp
commands/adduseridcommand.h
commands/authenticatepivcardapplicationcommand.cpp
commands/authenticatepivcardapplicationcommand.h
commands/cardcommand.cpp
commands/cardcommand.h
commands/certificatetopivcardcommand.cpp
commands/certificatetopivcardcommand.h
commands/certifycertificatecommand.cpp
commands/certifycertificatecommand.h
commands/changeexpirycommand.cpp
commands/changeexpirycommand.h
commands/changeownertrustcommand.cpp
commands/changeownertrustcommand.h
commands/changepassphrasecommand.cpp
commands/changepassphrasecommand.h
commands/changepincommand.cpp
commands/changepincommand.h
commands/changeroottrustcommand.cpp
commands/changeroottrustcommand.h
commands/checksumcreatefilescommand.cpp
commands/checksumcreatefilescommand.h
commands/checksumverifyfilescommand.cpp
commands/checksumverifyfilescommand.h
commands/clearcrlcachecommand.cpp
commands/clearcrlcachecommand.h
commands/command.cpp
commands/command.h
commands/createcsrforcardkeycommand.cpp
commands/createcsrforcardkeycommand.h
commands/createopenpgpkeyfromcardkeyscommand.cpp
commands/createopenpgpkeyfromcardkeyscommand.h
commands/decryptverifyclipboardcommand.cpp
commands/decryptverifyclipboardcommand.h
commands/decryptverifyfilescommand.cpp
commands/decryptverifyfilescommand.h
commands/deletecertificatescommand.cpp
commands/deletecertificatescommand.h
commands/detailscommand.cpp
commands/detailscommand.h
commands/dumpcertificatecommand.cpp
commands/dumpcertificatecommand.h
commands/dumpcrlcachecommand.cpp
commands/dumpcrlcachecommand.h
commands/encryptclipboardcommand.cpp
commands/encryptclipboardcommand.h
commands/exportcertificatecommand.cpp
commands/exportcertificatecommand.h
commands/exportgroupscommand.cpp
commands/exportgroupscommand.h
commands/exportopenpgpcertstoservercommand.cpp
commands/exportopenpgpcertstoservercommand.h
commands/exportopenpgpcerttoprovidercommand.cpp
commands/exportopenpgpcerttoprovidercommand.h
commands/exportpaperkeycommand.cpp
commands/exportpaperkeycommand.h
commands/exportsecretkeycommand.cpp
commands/exportsecretkeycommand.h
commands/exportsecretkeycommand_old.cpp
commands/exportsecretkeycommand_old.h
commands/exportsecretsubkeycommand.cpp
commands/exportsecretsubkeycommand.h
commands/genrevokecommand.cpp
commands/genrevokecommand.h
commands/gnupgprocesscommand.cpp
commands/gnupgprocesscommand.h
commands/importcertificatefromclipboardcommand.cpp
commands/importcertificatefromclipboardcommand.h
commands/importcertificatefromdatacommand.cpp
commands/importcertificatefromdatacommand.h
commands/importcertificatefromfilecommand.cpp
commands/importcertificatefromfilecommand.h
commands/importcertificatefromkeyservercommand.cpp
commands/importcertificatefromkeyservercommand.h
commands/importcertificatefrompivcardcommand.cpp
commands/importcertificatefrompivcardcommand.h
commands/importcertificatescommand.cpp
commands/importcertificatescommand.h
commands/importcrlcommand.cpp
commands/importcrlcommand.h
commands/importpaperkeycommand.cpp
commands/importpaperkeycommand.h
commands/keytocardcommand.cpp
commands/keytocardcommand.h
commands/learncardkeyscommand.cpp
commands/learncardkeyscommand.h
commands/lookupcertificatescommand.cpp
commands/lookupcertificatescommand.h
commands/newcertificatesigningrequestcommand.cpp
commands/newcertificatesigningrequestcommand.h
commands/newopenpgpcertificatecommand.cpp
commands/newopenpgpcertificatecommand.h
commands/openpgpgeneratecardkeycommand.cpp
commands/openpgpgeneratecardkeycommand.h
commands/pivgeneratecardkeycommand.cpp
commands/pivgeneratecardkeycommand.h
commands/refreshcertificatecommand.cpp
commands/refreshcertificatecommand.h
commands/refreshopenpgpcertscommand.cpp
commands/refreshopenpgpcertscommand.h
commands/refreshx509certscommand.cpp
commands/refreshx509certscommand.h
commands/reloadkeyscommand.cpp
commands/reloadkeyscommand.h
commands/revokecertificationcommand.cpp
commands/revokecertificationcommand.h
commands/revokekeycommand.cpp
commands/revokekeycommand.h
commands/revokeuseridcommand.cpp
commands/revokeuseridcommand.h
commands/selftestcommand.cpp
commands/selftestcommand.h
commands/setinitialpincommand.cpp
commands/setinitialpincommand.h
commands/setpivcardapplicationadministrationkeycommand.cpp
commands/setpivcardapplicationadministrationkeycommand.h
commands/setprimaryuseridcommand.cpp
commands/setprimaryuseridcommand.h
commands/signclipboardcommand.cpp
commands/signclipboardcommand.h
commands/signencryptfilescommand.cpp
commands/signencryptfilescommand.h
commands/signencryptfoldercommand.cpp
commands/signencryptfoldercommand.h
conf/configuredialog.cpp
conf/configuredialog.h
conf/groupsconfigdialog.cpp
conf/groupsconfigdialog.h
conf/groupsconfigpage.cpp
conf/groupsconfigpage.h
conf/groupsconfigwidget.cpp
conf/groupsconfigwidget.h
crypto/autodecryptverifyfilescontroller.cpp
crypto/autodecryptverifyfilescontroller.h
crypto/certificateresolver.cpp
crypto/certificateresolver.h
crypto/checksumsutils_p.cpp
crypto/checksumsutils_p.h
crypto/controller.cpp
crypto/controller.h
crypto/createchecksumscontroller.cpp
crypto/createchecksumscontroller.h
crypto/decryptverifyemailcontroller.cpp
crypto/decryptverifyemailcontroller.h
crypto/decryptverifyfilescontroller.cpp
crypto/decryptverifyfilescontroller.h
crypto/decryptverifytask.cpp
crypto/decryptverifytask.h
crypto/encryptemailcontroller.cpp
crypto/encryptemailcontroller.h
crypto/encryptemailtask.cpp
crypto/encryptemailtask.h
crypto/gui/certificatelineedit.cpp
crypto/gui/certificatelineedit.h
crypto/gui/certificateselectionline.cpp
crypto/gui/certificateselectionline.h
crypto/gui/decryptverifyfilesdialog.cpp
crypto/gui/decryptverifyfilesdialog.h
crypto/gui/decryptverifyfileswizard.cpp
crypto/gui/decryptverifyfileswizard.h
crypto/gui/decryptverifyoperationwidget.cpp
crypto/gui/decryptverifyoperationwidget.h
crypto/gui/encryptemailwizard.cpp
crypto/gui/encryptemailwizard.h
crypto/gui/newresultpage.cpp
crypto/gui/newresultpage.h
crypto/gui/objectspage.cpp
crypto/gui/objectspage.h
crypto/gui/resolverecipientspage.cpp
crypto/gui/resolverecipientspage.h
crypto/gui/resultitemwidget.cpp
crypto/gui/resultitemwidget.h
crypto/gui/resultlistwidget.cpp
crypto/gui/resultlistwidget.h
crypto/gui/resultpage.cpp
crypto/gui/resultpage.h
crypto/gui/signemailwizard.cpp
crypto/gui/signemailwizard.h
crypto/gui/signencryptemailconflictdialog.cpp
crypto/gui/signencryptemailconflictdialog.h
crypto/gui/signencryptfileswizard.cpp
crypto/gui/signencryptfileswizard.h
crypto/gui/signencryptwidget.cpp
crypto/gui/signencryptwidget.h
crypto/gui/signencryptwizard.cpp
crypto/gui/signencryptwizard.h
crypto/gui/signerresolvepage.cpp
crypto/gui/signerresolvepage.h
crypto/gui/signingcertificateselectiondialog.cpp
crypto/gui/signingcertificateselectiondialog.h
crypto/gui/signingcertificateselectionwidget.cpp
crypto/gui/signingcertificateselectionwidget.h
crypto/gui/unknownrecipientwidget.cpp
crypto/gui/unknownrecipientwidget.h
crypto/gui/verifychecksumsdialog.cpp
crypto/gui/verifychecksumsdialog.h
crypto/gui/wizard.cpp
crypto/gui/wizard.h
crypto/gui/wizardpage.cpp
crypto/gui/wizardpage.h
crypto/newsignencryptemailcontroller.cpp
crypto/newsignencryptemailcontroller.h
crypto/recipient.cpp
crypto/recipient.h
crypto/sender.cpp
crypto/sender.h
crypto/signemailcontroller.cpp
crypto/signemailcontroller.h
crypto/signemailtask.cpp
crypto/signemailtask.h
crypto/signencryptfilescontroller.cpp
crypto/signencryptfilescontroller.h
crypto/signencrypttask.cpp
crypto/signencrypttask.h
crypto/task.cpp
crypto/task.h
crypto/taskcollection.cpp
crypto/taskcollection.h
crypto/verifychecksumscontroller.cpp
crypto/verifychecksumscontroller.h
dialogs/adduseriddialog.cpp
dialogs/adduseriddialog.h
dialogs/certificatedetailsdialog.cpp
dialogs/certificatedetailsdialog.h
dialogs/certificatedetailsinputwidget.cpp
dialogs/certificatedetailsinputwidget.h
dialogs/certificatedetailswidget.cpp
dialogs/certificatedetailswidget.h
dialogs/certificateselectiondialog.cpp
dialogs/certificateselectiondialog.h
dialogs/certifycertificatedialog.cpp
dialogs/certifycertificatedialog.h
dialogs/certifywidget.cpp
dialogs/certifywidget.h
dialogs/createcsrforcardkeydialog.cpp
dialogs/createcsrforcardkeydialog.h
dialogs/deletecertificatesdialog.cpp
dialogs/deletecertificatesdialog.h
dialogs/editgroupdialog.cpp
dialogs/editgroupdialog.h
dialogs/expirydialog.cpp
dialogs/expirydialog.h
dialogs/exportdialog.cpp
dialogs/exportdialog.h
dialogs/gencardkeydialog.cpp
dialogs/gencardkeydialog.h
dialogs/groupdetailsdialog.cpp
dialogs/groupdetailsdialog.h
dialogs/lookupcertificatesdialog.cpp
dialogs/lookupcertificatesdialog.h
+ dialogs/messageviewerdialog.cpp
+ dialogs/messageviewerdialog.h
dialogs/nameandemailwidget.cpp
dialogs/nameandemailwidget.h
dialogs/newopenpgpcertificatedetailsdialog.cpp
dialogs/newopenpgpcertificatedetailsdialog.h
dialogs/pivcardapplicationadministrationkeyinputdialog.cpp
dialogs/pivcardapplicationadministrationkeyinputdialog.h
dialogs/revokekeydialog.cpp
dialogs/revokekeydialog.h
dialogs/selftestdialog.cpp
dialogs/selftestdialog.h
dialogs/setinitialpindialog.cpp
dialogs/setinitialpindialog.h
dialogs/subkeyswidget.cpp
dialogs/subkeyswidget.h
dialogs/trustchainwidget.cpp
dialogs/trustchainwidget.h
dialogs/updatenotification.cpp
dialogs/updatenotification.h
dialogs/weboftrustdialog.cpp
dialogs/weboftrustdialog.h
dialogs/weboftrustwidget.cpp
dialogs/weboftrustwidget.h
interfaces/anchorprovider.h
interfaces/focusfirstchild.h
newcertificatewizard/advancedsettingsdialog.cpp
newcertificatewizard/advancedsettingsdialog_p.h
newcertificatewizard/enterdetailspage.cpp
newcertificatewizard/enterdetailspage_p.h
newcertificatewizard/keyalgo.cpp
newcertificatewizard/keyalgo_p.h
newcertificatewizard/keycreationpage.cpp
newcertificatewizard/keycreationpage_p.h
newcertificatewizard/listwidget.cpp
newcertificatewizard/listwidget.h
newcertificatewizard/newcertificatewizard.cpp
newcertificatewizard/newcertificatewizard.h
newcertificatewizard/resultpage.cpp
newcertificatewizard/resultpage_p.h
newcertificatewizard/wizardpage.cpp
newcertificatewizard/wizardpage_p.h
selftest/compliancecheck.cpp
selftest/compliancecheck.h
selftest/enginecheck.cpp
selftest/enginecheck.h
selftest/gpgagentcheck.cpp
selftest/gpgagentcheck.h
selftest/gpgconfcheck.cpp
selftest/gpgconfcheck.h
selftest/libkleopatrarccheck.cpp
selftest/libkleopatrarccheck.h
selftest/selftest.cpp
selftest/selftest.h
smartcard/algorithminfo.h
smartcard/card.cpp
smartcard/card.h
smartcard/deviceinfowatcher.cpp
smartcard/deviceinfowatcher.h
smartcard/keypairinfo.cpp
smartcard/keypairinfo.h
smartcard/netkeycard.cpp
smartcard/netkeycard.h
smartcard/openpgpcard.cpp
smartcard/openpgpcard.h
smartcard/p15card.cpp
smartcard/p15card.h
smartcard/pivcard.cpp
smartcard/pivcard.h
smartcard/readerstatus.cpp
smartcard/readerstatus.h
smartcard/utils.cpp
smartcard/utils.h
utils/accessibility.cpp
utils/accessibility.h
utils/action_data.cpp
utils/action_data.h
utils/applicationstate.cpp
utils/applicationstate.h
utils/archivedefinition.cpp
utils/archivedefinition.h
utils/clipboardmenu.cpp
utils/clipboardmenu.h
utils/debug-helpers.cpp
utils/debug-helpers.h
utils/dragqueen.cpp
utils/dragqueen.h
utils/email.cpp
utils/email.h
utils/emptypassphraseprovider.cpp
utils/emptypassphraseprovider.h
utils/filedialog.cpp
utils/filedialog.h
utils/gui-helper.cpp
utils/gui-helper.h
utils/headerview.cpp
utils/headerview.h
utils/input.cpp
utils/input.h
utils/iodevicelogger.cpp
utils/iodevicelogger.h
utils/kdpipeiodevice.cpp
utils/kdpipeiodevice.h
utils/keyparameters.cpp
utils/keyparameters.h
utils/keys.cpp
utils/keys.h
utils/kuniqueservice.cpp
utils/kuniqueservice.h
utils/log.cpp
utils/log.h
utils/memory-helpers.h
utils/multivalidator.cpp
utils/multivalidator.h
utils/output.cpp
utils/output.h
utils/overwritedialog.cpp
utils/overwritedialog.h
utils/path-helper.cpp
utils/path-helper.h
utils/scrollarea.cpp
utils/scrollarea.h
utils/systemtrayicon.cpp
utils/systemtrayicon.h
utils/tags.cpp
utils/tags.h
utils/types.cpp
utils/types.h
utils/userinfo.cpp
utils/userinfo.h
utils/validation.cpp
utils/validation.h
utils/writecertassuantransaction.cpp
utils/writecertassuantransaction.h
utils/wsastarter.cpp
utils/wsastarter.h
view/anchorcache.cpp
view/anchorcache_p.h
view/errorlabel.cpp
view/errorlabel.h
view/formtextinput.cpp
view/formtextinput.h
view/htmllabel.cpp
view/htmllabel.h
view/infofield.cpp
view/infofield.h
view/keycacheoverlay.cpp
view/keycacheoverlay.h
view/keylistcontroller.cpp
view/keylistcontroller.h
view/keytreeview.cpp
view/keytreeview.h
view/netkeywidget.cpp
view/netkeywidget.h
view/nullpinwidget.cpp
view/nullpinwidget.h
view/openpgpkeycardwidget.cpp
view/openpgpkeycardwidget.h
view/p15cardwidget.cpp
view/p15cardwidget.h
view/padwidget.cpp
view/padwidget.h
view/pgpcardwidget.cpp
view/pgpcardwidget.h
view/pivcardwidget.cpp
view/pivcardwidget.h
view/searchbar.cpp
view/searchbar.h
view/smartcardwidget.cpp
view/smartcardwidget.h
view/tabwidget.cpp
view/tabwidget.h
view/urllabel.cpp
view/urllabel.h
view/waitwidget.cpp
view/waitwidget.h
view/welcomewidget.cpp
view/welcomewidget.h
aboutdata.cpp
aboutdata.h
kleopatra.qrc
kleopatraapplication.cpp
kleopatraapplication.h
main.cpp
mainwindow.cpp
mainwindow.h
systrayicon.cpp
systrayicon.h
)
if(WIN32)
configure_file (versioninfo.rc.in versioninfo.rc)
set(_kleopatra_SRCS ${CMAKE_CURRENT_BINARY_DIR}/versioninfo.rc ${_kleopatra_SRCS})
configure_file (kleopatra.w32-manifest.in kleopatra.w32-manifest)
set(_kleopatra_SRCS ${CMAKE_CURRENT_BINARY_DIR}/kleopatra.w32-manifest ${_kleopatra_SRCS})
endif()
set (_kleopatra_SRCS conf/kleopageconfigdialog.cpp conf/kleopageconfigdialog.h ${_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/setinitialpindialog.ui
dialogs/trustchainwidget.ui
newcertificatewizard/listwidget.ui
)
kconfig_add_kcfg_files(_kleopatra_SRCS
kcfg/emailoperationspreferences.kcfgc
kcfg/fileoperationspreferences.kcfgc
kcfg/settings.kcfgc
kcfg/smimevalidationpreferences.kcfgc
kcfg/tagspreferences.kcfgc
kcfg/tooltippreferences.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
KPim${KF_MAJOR_VERSION}::Libkleo
KPim${KF_MAJOR_VERSION}::Mime
+ KPim${KF_MAJOR_VERSION}::MimeTreeParserWidgets
KF${KF_MAJOR_VERSION}::Codecs
KF${KF_MAJOR_VERSION}::CoreAddons
KF${KF_MAJOR_VERSION}::Crash
KF${KF_MAJOR_VERSION}::I18n
KF${KF_MAJOR_VERSION}::IconThemes
KF${KF_MAJOR_VERSION}::ItemModels
KF${KF_MAJOR_VERSION}::KIOCore
KF${KF_MAJOR_VERSION}::KIOWidgets
KF${KF_MAJOR_VERSION}::WindowSystem
KF${KF_MAJOR_VERSION}::XmlGui
Qt::Network
Qt::PrintSupport # Printing secret keys
kleopatraclientcore
${_kleopatra_extra_libs}
${_kleopatra_mail_libs}
${_kleopatra_uiserver_extra_libs}
${_kleopatra_dbusaddons_libs}
${_kleopatra_platform_libs}
)
if (QT_MAJOR_VERSION STREQUAL "6")
target_link_libraries(kleopatra_bin Qt6::Core5Compat)
target_link_libraries(kleopatra_bin QGpgmeQt6)
else()
target_link_libraries(kleopatra_bin QGpgme)
endif()
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-mime.xml DESTINATION ${KDE_INSTALL_MIMEDIR})
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/crypto/decryptverifytask.cpp b/src/crypto/decryptverifytask.cpp
index 8fc925169..12b8a6d21 100644
--- a/src/crypto/decryptverifytask.cpp
+++ b/src/crypto/decryptverifytask.cpp
@@ -1,1788 +1,1818 @@
/* -*- mode: c++; c-basic-offset:4 -*-
decryptverifytask.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "decryptverifytask.h"
#include <QGpgME/Protocol>
#include <QGpgME/VerifyOpaqueJob>
#include <QGpgME/VerifyDetachedJob>
#include <QGpgME/DecryptJob>
#include <QGpgME/DecryptVerifyJob>
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
#include <QGpgME/DecryptVerifyArchiveJob>
#endif
#include <Libkleo/AuditLogEntry>
#include <Libkleo/Compliance>
#include <Libkleo/Dn>
#include <Libkleo/KleoException>
#include <Libkleo/Stl_Util>
#include <Libkleo/KeyCache>
#include <Libkleo/Predicates>
#include <Libkleo/Formatting>
#include <Libkleo/Classify>
#include <utils/detail_p.h>
#include <utils/input.h>
#include <utils/output.h>
#include <utils/kleo_assert.h>
#include <Libkleo/GnuPG>
#include <KMime/HeaderParsing>
#include <gpgme++/error.h>
#include <gpgme++/key.h>
#include <gpgme++/verificationresult.h>
#include <gpgme++/decryptionresult.h>
#include <gpgme++/context.h>
#include <gpg-error.h>
#include "kleopatra_debug.h"
#include <KFileUtils>
#include <KLocalizedString>
#include <QLocale>
#include <QByteArray>
#include <QDateTime>
#include <QDir>
#include <QFileInfo>
#include <QIODevice>
+#include <QMimeDatabase>
#include <QStringList>
#include <QTextDocument> // Qt::escape
#include <algorithm>
#include <sstream>
using namespace Kleo::Crypto;
using namespace Kleo;
using namespace GpgME;
using namespace KMime::Types;
namespace
{
static AuditLogEntry auditLogFromSender(QObject *sender)
{
return AuditLogEntry::fromJob(qobject_cast<const QGpgME::Job *>(sender));
}
static bool addrspec_equal(const AddrSpec &lhs, const AddrSpec &rhs, Qt::CaseSensitivity cs)
{
return lhs.localPart.compare(rhs.localPart, cs) == 0 && lhs.domain.compare(rhs.domain, Qt::CaseInsensitive) == 0;
}
static bool mailbox_equal(const Mailbox &lhs, const Mailbox &rhs, Qt::CaseSensitivity cs)
{
return addrspec_equal(lhs.addrSpec(), rhs.addrSpec(), cs);
}
static std::string stripAngleBrackets(const std::string &str)
{
if (str.empty()) {
return str;
}
if (str[0] == '<' && str[str.size() - 1] == '>') {
return str.substr(1, str.size() - 2);
}
return str;
}
static std::string email(const UserID &uid)
{
if (uid.parent().protocol() == OpenPGP) {
if (const char *const email = uid.email()) {
return stripAngleBrackets(email);
} else {
return std::string();
}
}
Q_ASSERT(uid.parent().protocol() == CMS);
if (const char *const id = uid.id())
if (*id == '<') {
return stripAngleBrackets(id);
} else {
return DN(id)[QStringLiteral("EMAIL")].trimmed().toUtf8().constData();
}
else {
return std::string();
}
}
static Mailbox mailbox(const UserID &uid)
{
const std::string e = email(uid);
Mailbox mbox;
if (!e.empty()) {
mbox.setAddress(e.c_str());
}
return mbox;
}
static std::vector<Mailbox> extractMailboxes(const Key &key)
{
std::vector<Mailbox> res;
const auto userIDs{key.userIDs()};
for (const UserID &id : userIDs) {
const Mailbox mbox = mailbox(id);
if (!mbox.addrSpec().isEmpty()) {
res.push_back(mbox);
}
}
return res;
}
static std::vector<Mailbox> extractMailboxes(const std::vector<Key> &signers)
{
std::vector<Mailbox> res;
for (const Key &i : signers) {
const std::vector<Mailbox> bxs = extractMailboxes(i);
res.insert(res.end(), bxs.begin(), bxs.end());
}
return res;
}
static bool keyContainsMailbox(const Key &key, const Mailbox &mbox)
{
const std::vector<Mailbox> mbxs = extractMailboxes(key);
return std::find_if(mbxs.cbegin(), mbxs.cend(),
[mbox](const Mailbox &m) {
return mailbox_equal(mbox, m, Qt::CaseInsensitive);
}) != mbxs.cend();
}
static bool keysContainMailbox(const std::vector<Key> &keys, const Mailbox &mbox)
{
return std::find_if(keys.cbegin(), keys.cend(),
[mbox](const Key &key) {
return keyContainsMailbox(key, mbox);
}) != keys.cend();
}
static bool relevantInDecryptVerifyContext(const VerificationResult &r)
{
// for D/V operations, we ignore verification results which are not errors and contain
// no signatures (which means that the data was just not signed)
return (r.error() && r.error().code() != GPG_ERR_DECRYPT_FAILED) || r.numSignatures() > 0;
}
static QString signatureSummaryToString(int summary)
{
if (summary & Signature::None) {
return i18n("Error: Signature not verified");
} else if (summary & Signature::Valid || summary & Signature::Green) {
return i18n("Good signature");
} else if (summary & Signature::KeyRevoked) {
return i18n("Signing certificate was revoked");
} else if (summary & Signature::KeyExpired) {
return i18n("Signing certificate is expired");
} else if (summary & Signature::KeyMissing) {
return i18n("Certificate is not available");
} else if (summary & Signature::SigExpired) {
return i18n("Signature expired");
} else if (summary & Signature::CrlMissing) {
return i18n("CRL missing");
} else if (summary & Signature::CrlTooOld) {
return i18n("CRL too old");
} else if (summary & Signature::BadPolicy) {
return i18n("Bad policy");
} else if (summary & Signature::SysError) {
return i18n("System error"); //### retrieve system error details?
} else if (summary & Signature::Red) {
return i18n("Bad signature");
}return QString();
}
static QString formatValidSignatureWithTrustLevel(const UserID &id)
{
if (id.isNull()) {
return QString();
}
switch (id.validity()) {
case UserID::Marginal:
return i18n("The signature is valid but the trust in the certificate's validity is only marginal.");
case UserID::Full:
return i18n("The signature is valid and the certificate's validity is fully trusted.");
case UserID::Ultimate:
return i18n("The signature is valid and the certificate's validity is ultimately trusted.");
case UserID::Never:
return i18n("The signature is valid but the certificate's validity is <em>not trusted</em>.");
case UserID::Unknown:
return i18n("The signature is valid but the certificate's validity is unknown.");
case UserID::Undefined:
default:
return i18n("The signature is valid but the certificate's validity is undefined.");
}
}
static QString renderKeyLink(const QString &fpr, const QString &text)
{
return QStringLiteral("<a href=\"key:%1\">%2</a>").arg(fpr, text);
}
static QString renderKey(const Key &key)
{
if (key.isNull()) {
return i18n("Unknown certificate");
}
if (key.primaryFingerprint() && strlen(key.primaryFingerprint()) > 16 && key.numUserIDs()) {
const QString text = QStringLiteral("%1 (%2)").arg(Formatting::prettyNameAndEMail(key).toHtmlEscaped()).arg(
Formatting::prettyID(QString::fromLocal8Bit(key.primaryFingerprint()).right(16).toLatin1().constData()));
return renderKeyLink(QLatin1String(key.primaryFingerprint()), text);
}
return renderKeyLink(QLatin1String(key.primaryFingerprint()), Formatting::prettyID(key.primaryFingerprint()));
}
static QString renderKeyEMailOnlyNameAsFallback(const Key &key)
{
if (key.isNull()) {
return i18n("Unknown certificate");
}
const QString email = Formatting::prettyEMail(key);
const QString user = !email.isEmpty() ? email : Formatting::prettyName(key);
return renderKeyLink(QLatin1String(key.primaryFingerprint()), user);
}
static QString formatDate(const QDateTime &dt)
{
return QLocale().toString(dt);
}
static QString formatSigningInformation(const Signature &sig)
{
if (sig.isNull()) {
return QString();
}
const QDateTime dt = sig.creationTime() != 0 ? QDateTime::fromSecsSinceEpoch(quint32(sig.creationTime())) : QDateTime();
QString text;
Key key = sig.key();
if (dt.isValid()) {
text = i18nc("1 is a date", "Signature created on %1", formatDate(dt)) + QStringLiteral("<br>");
}
if (key.isNull()) {
return text += i18n("With unavailable certificate:") + QStringLiteral("<br>ID: 0x%1").arg(QString::fromLatin1(sig.fingerprint()).toUpper());
}
text += i18n("With certificate:") + QStringLiteral("<br>") + renderKey(key);
if (DeVSCompliance::isCompliant()) {
text +=
(QStringLiteral("<br/>")
+ (sig.isDeVs()
? i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
"The signature is %1", DeVSCompliance::name(true))
: i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
"The signature <b>is not</b> %1.", DeVSCompliance::name(true))));
}
return text;
}
static QString strikeOut(const QString &str, bool strike)
{
return QString(strike ? QStringLiteral("<s>%1</s>") : QStringLiteral("%1")).arg(str.toHtmlEscaped());
}
static QString formatInputOutputLabel(const QString &input, const QString &output, bool inputDeleted, bool outputDeleted)
{
if (output.isEmpty()) {
return strikeOut(input, inputDeleted);
}
return i18nc("Input file --> Output file (rarr is arrow", "%1 &rarr; %2",
strikeOut(input, inputDeleted),
strikeOut(output, outputDeleted));
}
static bool IsErrorOrCanceled(const GpgME::Error &err)
{
return err || err.isCanceled();
}
static bool IsErrorOrCanceled(const Result &res)
{
return IsErrorOrCanceled(res.error());
}
static bool IsBad(const Signature &sig)
{
return sig.summary() & Signature::Red;
}
static bool IsGoodOrValid(const Signature &sig)
{
return (sig.summary() & Signature::Valid) || (sig.summary() & Signature::Green);
}
static UserID findUserIDByMailbox(const Key &key, const Mailbox &mbox)
{
const auto userIDs{key.userIDs()};
for (const UserID &id : userIDs)
if (mailbox_equal(mailbox(id), mbox, Qt::CaseInsensitive)) {
return id;
}
return UserID();
}
static void updateKeys(const VerificationResult &result) {
// This little hack works around the problem that GnuPG / GpgME does not
// provide Key information in a verification result. The Key object is
// a dummy just holding the KeyID. This hack ensures that all available
// keys are fetched from the backend and are populated
for (const auto &sig: result.signatures()) {
// Update key information
sig.key(true, true);
}
}
static QString ensureUniqueDirectory(const QString &path)
{
// make sure that we don't use an existing directory
QString uniquePath = path;
const QFileInfo outputInfo{path};
if (outputInfo.exists()) {
const auto uniqueName = KFileUtils::suggestName(QUrl::fromLocalFile(outputInfo.absolutePath()), outputInfo.fileName());
uniquePath = outputInfo.dir().filePath(uniqueName);
}
if (!QDir{}.mkpath(uniquePath)) {
return {};
}
return uniquePath;
}
+static bool mimeTypeInherits(const QMimeType &mimeType, const QString &mimeTypeName)
+{
+ // inherits is expensive on an invalid mimeType
+ return mimeType.isValid() && mimeType.inherits(mimeTypeName);
+}
+
}
class DecryptVerifyResult::SenderInfo
{
public:
explicit SenderInfo(const Mailbox &infSender, const std::vector<Key> &signers_) : informativeSender(infSender), signers(signers_) {}
const Mailbox informativeSender;
const std::vector<Key> signers;
bool hasInformativeSender() const
{
return !informativeSender.addrSpec().isEmpty();
}
bool conflicts() const
{
return hasInformativeSender() && hasKeys() && !keysContainMailbox(signers, informativeSender);
}
bool hasKeys() const
{
return std::any_of(signers.cbegin(), signers.cend(), [](const Key &key) { return !key.isNull(); });
}
std::vector<Mailbox> signerMailboxes() const
{
return extractMailboxes(signers);
}
};
namespace
{
static Task::Result::VisualCode codeForVerificationResult(const VerificationResult &res)
{
if (res.isNull()) {
return Task::Result::NeutralSuccess;
}
const std::vector<Signature> sigs = res.signatures();
if (sigs.empty()) {
return Task::Result::Warning;
}
if (std::find_if(sigs.begin(), sigs.end(), IsBad) != sigs.end()) {
return Task::Result::Danger;
}
if ((size_t)std::count_if(sigs.begin(), sigs.end(), IsGoodOrValid) == sigs.size()) {
return Task::Result::AllGood;
}
return Task::Result::Warning;
}
static QString formatVerificationResultOverview(const VerificationResult &res, const DecryptVerifyResult::SenderInfo &info)
{
if (res.isNull()) {
return QString();
}
const Error err = res.error();
if (err.isCanceled()) {
return i18n("<b>Verification canceled.</b>");
} else if (err) {
return i18n("<b>Verification failed: %1.</b>", Formatting::errorAsString(err).toHtmlEscaped());
}
const std::vector<Signature> sigs = res.signatures();
if (sigs.empty()) {
return i18n("<b>No signatures found.</b>");
}
const uint bad = std::count_if(sigs.cbegin(), sigs.cend(), IsBad);
if (bad > 0) {
return i18np("<b>Invalid signature.</b>", "<b>%1 invalid signatures.</b>", bad);
}
const uint warn = std::count_if(sigs.cbegin(), sigs.cend(), [](const Signature &sig) { return !IsGoodOrValid(sig); });
if (warn == sigs.size()) {
return i18np("<b>The data could not be verified.</b>", "<b>%1 signatures could not be verified.</b>", warn);
}
//Good signature:
QString text;
if (sigs.size() == 1) {
text = i18n("<b>Valid signature by %1</b>", renderKeyEMailOnlyNameAsFallback(sigs[0].key()));
if (info.conflicts())
text += i18n("<br/><b>Warning:</b> The sender's mail address is not stored in the %1 used for signing.",
renderKeyLink(QLatin1String(sigs[0].key().primaryFingerprint()), i18n("certificate")));
} else {
text = i18np("<b>Valid signature.</b>", "<b>%1 valid signatures.</b>", sigs.size());
if (info.conflicts()) {
text += i18n("<br/><b>Warning:</b> The sender's mail address is not stored in the certificates used for signing.");
}
}
return text;
}
static QString formatDecryptionResultOverview(const DecryptionResult &result, const QString &errorString = QString())
{
const Error err = result.error();
if (err.isCanceled()) {
return i18n("<b>Decryption canceled.</b>");
}
else if (result.isLegacyCipherNoMDC()) {
return i18n("<b>Decryption failed: %1.</b>", i18n("No integrity protection (MDC)."));
}
else if (!errorString.isEmpty()) {
return i18n("<b>Decryption failed: %1.</b>", errorString.toHtmlEscaped());
} else if (err) {
return i18n("<b>Decryption failed: %1.</b>", Formatting::errorAsString(err).toHtmlEscaped());
}
return i18n("<b>Decryption succeeded.</b>");
}
static QString formatSignature(const Signature &sig, const DecryptVerifyResult::SenderInfo &info)
{
if (sig.isNull()) {
return QString();
}
const QString text = formatSigningInformation(sig) + QLatin1String("<br/>");
const Key key = sig.key();
// Green
if (sig.summary() & Signature::Valid) {
const UserID id = findUserIDByMailbox(key, info.informativeSender);
return text + formatValidSignatureWithTrustLevel(!id.isNull() ? id : key.userID(0));
}
// Red
if ((sig.summary() & Signature::Red)) {
const QString ret = text + i18n("The signature is invalid: %1", signatureSummaryToString(sig.summary()));
if (sig.summary() & Signature::SysError) {
return ret + QStringLiteral(" (%1)").arg(Formatting::errorAsString(sig.status()));
}
return ret;
}
// Key missing
if ((sig.summary() & Signature::KeyMissing)) {
return text + i18n("You can search the certificate on a keyserver or import it from a file.");
}
// Yellow
if ((sig.validity() & Signature::Validity::Undefined) ||
(sig.validity() & Signature::Validity::Unknown) ||
(sig.summary() == Signature::Summary::None)) {
return text + (key.protocol() == OpenPGP ? i18n("The used key is not certified by you or any trusted person.") :
i18n("The used certificate is not certified by a trustworthy Certificate Authority or the Certificate Authority is unknown."));
}
// Catch all fall through
const QString ret = text + i18n("The signature is invalid: %1", signatureSummaryToString(sig.summary()));
if (sig.summary() & Signature::SysError) {
return ret + QStringLiteral(" (%1)").arg(Formatting::errorAsString(sig.status()));
}
return ret;
}
static QStringList format(const std::vector<Mailbox> &mbxs)
{
QStringList res;
std::transform(mbxs.cbegin(), mbxs.cend(), std::back_inserter(res),
[](const Mailbox &mbox) {
return mbox.prettyAddress();
});
return res;
}
static QString formatVerificationResultDetails(const VerificationResult &res, const DecryptVerifyResult::SenderInfo &info, const QString &errorString)
{
if ((res.error().code() == GPG_ERR_EIO || res.error().code() == GPG_ERR_NO_DATA) && !errorString.isEmpty()) {
return i18n("Input error: %1", errorString);
}
const std::vector<Signature> sigs = res.signatures();
QString details;
for (const Signature &sig : sigs) {
details += formatSignature(sig, info) + QLatin1Char('\n');
}
details = details.trimmed();
details.replace(QLatin1Char('\n'), QStringLiteral("<br/><br/>"));
if (info.conflicts()) {
details += i18n("<p>The sender's address %1 is not stored in the certificate. Stored: %2</p>", info.informativeSender.prettyAddress(), format(info.signerMailboxes()).join(i18nc("separator for a list of e-mail addresses", ", ")));
}
return details;
}
static QString formatRecipientsDetails(const std::vector<Key> &knownRecipients, unsigned int numRecipients)
{
if (numRecipients == 0) {
return {};
}
if (knownRecipients.empty()) {
return QLatin1String("<i>") +
i18np("One unknown recipient.", "%1 unknown recipients.", numRecipients) +
QLatin1String("</i>");
}
QString details = i18np("Recipient:", "Recipients:", numRecipients);
if (numRecipients == 1) {
details += QLatin1Char(' ') + renderKey(knownRecipients.front());
} else {
details += QLatin1String("<ul>");
for (const Key &key : knownRecipients) {
details += QLatin1String("<li>") + renderKey(key) + QLatin1String("</li>");
}
if (knownRecipients.size() < numRecipients) {
details += QLatin1String("<li><i>") +
i18np("One unknown recipient", "%1 unknown recipients",
numRecipients - knownRecipients.size()) +
QLatin1String("</i></li>");
}
details += QLatin1String("</ul>");
}
return details;
}
static QString formatDecryptionResultDetails(const DecryptionResult &res, const std::vector<Key> &recipients,
const QString &errorString, bool isSigned, const QPointer<Task> &task)
{
if ((res.error().code() == GPG_ERR_EIO || res.error().code() == GPG_ERR_NO_DATA) && !errorString.isEmpty()) {
return i18n("Input error: %1", errorString);
}
if (res.isNull() || res.error() || res.error().isCanceled()) {
return formatRecipientsDetails(recipients, res.numRecipients());
}
QString details;
if (DeVSCompliance::isCompliant()) {
details += ((res.isDeVs()
? i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
"The decryption is %1.", DeVSCompliance::name(true))
: i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
"The decryption <b>is not</b> %1.", DeVSCompliance::name(true)))
+ QStringLiteral("<br/>"));
}
if (res.fileName()) {
const auto decVerifyTask = qobject_cast<AbstractDecryptVerifyTask*> (task.data());
if (decVerifyTask) {
const auto embedFileName = QString::fromUtf8(res.fileName()).toHtmlEscaped();
if (embedFileName != decVerifyTask->outputLabel()) {
details += i18n("Embedded file name: '%1'", embedFileName);
details += QStringLiteral("<br/>");
}
}
}
if (!isSigned) {
details += i18n("<b>Note:</b> You cannot be sure who encrypted this message as it is not signed.")
+ QLatin1String("<br/>");
}
if (res.isLegacyCipherNoMDC()) {
details += i18nc("Integrity protection was missing because an old cipher was used.",
"<b>Hint:</b> If this file was encrypted before the year 2003 it is "
"likely that the file is legitimate. This is because back "
"then integrity protection was not widely used.") + QStringLiteral("<br/><br/>") +
i18nc("The user is offered to force decrypt a non integrity protected message. With the strong advice to re-encrypt it.",
"If you are confident that the file was not manipulated you should re-encrypt it after you have forced the decryption.") +
QStringLiteral("<br/><br/>");
}
details += formatRecipientsDetails(recipients, res.numRecipients());
return details;
}
static QString formatDecryptVerifyResultOverview(const DecryptionResult &dr, const VerificationResult &vr, const DecryptVerifyResult::SenderInfo &info)
{
if (IsErrorOrCanceled(dr) || !relevantInDecryptVerifyContext(vr)) {
return formatDecryptionResultOverview(dr);
}
return formatVerificationResultOverview(vr, info);
}
static QString formatDecryptVerifyResultDetails(const DecryptionResult &dr,
const VerificationResult &vr,
const std::vector<Key> &recipients,
const DecryptVerifyResult::SenderInfo &info,
const QString &errorString,
const QPointer<Task> &task)
{
const QString drDetails = formatDecryptionResultDetails(dr, recipients, errorString, relevantInDecryptVerifyContext(vr), task);
if (IsErrorOrCanceled(dr) || !relevantInDecryptVerifyContext(vr)) {
return drDetails;
}
return drDetails + (drDetails.isEmpty() ? QString() : QStringLiteral("<br/>")) + formatVerificationResultDetails(vr, info, errorString);
}
} // anon namespace
class DecryptVerifyResult::Private
{
DecryptVerifyResult *const q;
public:
Private(DecryptVerifyOperation type,
const VerificationResult &vr,
const DecryptionResult &dr,
const QByteArray &stuff,
const QString &fileName,
const GpgME::Error &error,
const QString &errString,
const QString &input,
const QString &output,
const AuditLogEntry &auditLog,
Task *parentTask,
const Mailbox &informativeSender,
DecryptVerifyResult *qq) :
q(qq),
m_type(type),
m_verificationResult(vr),
m_decryptionResult(dr),
m_stuff(stuff),
m_fileName(fileName),
m_error(error),
m_errorString(errString),
m_inputLabel(input),
m_outputLabel(output),
m_auditLog(auditLog),
m_parentTask(QPointer<Task>(parentTask)),
m_informativeSender(informativeSender)
{
}
QString label() const
{
return formatInputOutputLabel(m_inputLabel, m_outputLabel, false, q->hasError());
}
DecryptVerifyResult::SenderInfo makeSenderInfo() const;
bool isDecryptOnly() const
{
return m_type == Decrypt;
}
bool isVerifyOnly() const
{
return m_type == Verify;
}
bool isDecryptVerify() const
{
return m_type == DecryptVerify;
}
DecryptVerifyOperation m_type;
VerificationResult m_verificationResult;
DecryptionResult m_decryptionResult;
QByteArray m_stuff;
QString m_fileName;
GpgME::Error m_error;
QString m_errorString;
QString m_inputLabel;
QString m_outputLabel;
const AuditLogEntry m_auditLog;
QPointer <Task> m_parentTask;
const Mailbox m_informativeSender;
};
DecryptVerifyResult::SenderInfo DecryptVerifyResult::Private::makeSenderInfo() const
{
return SenderInfo(m_informativeSender, KeyCache::instance()->findSigners(m_verificationResult));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromDecryptResult(const DecryptionResult &dr, const QByteArray &plaintext, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(
Decrypt,
VerificationResult(),
dr,
plaintext,
{},
{},
QString(),
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromDecryptResult(const GpgME::Error &err, const QString &what, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(
Decrypt,
VerificationResult(),
DecryptionResult(err),
QByteArray(),
{},
err,
what,
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromDecryptVerifyResult(const DecryptionResult &dr, const VerificationResult &vr, const QByteArray &plaintext, const QString &fileName, const AuditLogEntry &auditLog)
{
const auto err = dr.error().code() ? dr.error() : vr.error();
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(
DecryptVerify,
vr,
dr,
plaintext,
fileName,
err,
QString(),
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromDecryptVerifyResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(
DecryptVerify,
VerificationResult(),
DecryptionResult(err),
QByteArray(),
{},
err,
details,
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromVerifyOpaqueResult(const VerificationResult &vr, const QByteArray &plaintext, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(
Verify,
vr,
DecryptionResult(),
plaintext,
{},
{},
QString(),
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromVerifyOpaqueResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(
Verify,
VerificationResult(err),
DecryptionResult(),
QByteArray(),
{},
err,
details,
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromVerifyDetachedResult(const VerificationResult &vr, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(
Verify,
vr,
DecryptionResult(),
QByteArray(),
{},
{},
QString(),
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromVerifyDetachedResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(
Verify,
VerificationResult(err),
DecryptionResult(),
QByteArray(),
{},
err,
details,
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
DecryptVerifyResult::DecryptVerifyResult(DecryptVerifyOperation type,
const VerificationResult &vr,
const DecryptionResult &dr,
const QByteArray &stuff,
const QString &fileName,
const GpgME::Error &error,
const QString &errString,
const QString &inputLabel,
const QString &outputLabel,
const AuditLogEntry &auditLog,
Task *parentTask,
const Mailbox &informativeSender)
: Task::Result(), d(new Private(type, vr, dr, stuff, fileName, error, errString, inputLabel, outputLabel, auditLog, parentTask, informativeSender, this))
{
}
+Task::Result::ContentType DecryptVerifyResult::viewableContentType() const
+{
+ if (decryptionResult().isMime()) {
+ return Task::Result::ContentType::Mime;
+ }
+
+ if (fileName().endsWith(QStringLiteral("openpgp-encrypted-message"))) {
+ return Task::Result::ContentType::Mime;
+ }
+
+ QMimeDatabase mimeDatabase;
+ const auto mimeType = mimeDatabase.mimeTypeForFile(fileName());
+ if (mimeTypeInherits(mimeType, QStringLiteral("message/rfc822"))) {
+ return Task::Result::ContentType::Mime;
+ }
+
+ if (mimeTypeInherits(mimeType, QStringLiteral("application/mbox"))) {
+ return Task::Result::ContentType::Mbox;
+ }
+
+ return Task::Result::ContentType::None;
+}
+
QString DecryptVerifyResult::overview() const
{
QString ov;
if (d->isDecryptOnly()) {
ov += formatDecryptionResultOverview(d->m_decryptionResult);
} else if (d->isVerifyOnly()) {
ov += formatVerificationResultOverview(d->m_verificationResult, d->makeSenderInfo());
} else {
ov += formatDecryptVerifyResultOverview(d->m_decryptionResult, d->m_verificationResult, d->makeSenderInfo());
}
if (ov.size() + d->label().size() > 120) {
// Avoid ugly breaks
ov = QStringLiteral("<br>") + ov;
}
return i18nc("label: result example: foo.sig: Verification failed. ", "%1: %2", d->label(), ov);
}
QString DecryptVerifyResult::details() const
{
if (d->isDecryptOnly()) {
return formatDecryptionResultDetails(d->m_decryptionResult,
KeyCache::instance()->findRecipients(d->m_decryptionResult),
errorString(), false, d->m_parentTask);
}
if (d->isVerifyOnly()) {
return formatVerificationResultDetails(d->m_verificationResult, d->makeSenderInfo(), errorString());
}
return formatDecryptVerifyResultDetails(d->m_decryptionResult,
d->m_verificationResult, KeyCache::instance()->findRecipients(
d->m_decryptionResult), d->makeSenderInfo(), errorString(),
d->m_parentTask);
}
GpgME::Error DecryptVerifyResult::error() const
{
return d->m_error;
}
QString DecryptVerifyResult::errorString() const
{
return d->m_errorString;
}
AuditLogEntry DecryptVerifyResult::auditLog() const
{
return d->m_auditLog;
}
QPointer<Task> DecryptVerifyResult::parentTask() const
{
return d->m_parentTask;
}
Task::Result::VisualCode DecryptVerifyResult::code() const
{
if ((d->m_type == DecryptVerify || d->m_type == Verify) && relevantInDecryptVerifyContext(verificationResult())) {
return codeForVerificationResult(verificationResult());
}
return hasError() ? NeutralError : NeutralSuccess;
}
GpgME::VerificationResult DecryptVerifyResult::verificationResult() const
{
return d->m_verificationResult;
}
GpgME::DecryptionResult DecryptVerifyResult::decryptionResult() const
{
return d->m_decryptionResult;
}
QString DecryptVerifyResult::fileName() const
{
return d->m_fileName;
}
class AbstractDecryptVerifyTask::Private
{
public:
Mailbox informativeSender;
QPointer<QGpgME::Job> job;
};
AbstractDecryptVerifyTask::AbstractDecryptVerifyTask(QObject *parent) : Task(parent), d(new Private) {}
AbstractDecryptVerifyTask::~AbstractDecryptVerifyTask() {}
void AbstractDecryptVerifyTask::cancel()
{
qCDebug(KLEOPATRA_LOG) << this << __func__;
if (d->job) {
d->job->slotCancel();
}
}
Mailbox AbstractDecryptVerifyTask::informativeSender() const
{
return d->informativeSender;
}
void AbstractDecryptVerifyTask::setInformativeSender(const Mailbox &sender)
{
d->informativeSender = sender;
}
QGpgME::Job *AbstractDecryptVerifyTask::job() const
{
return d->job;
}
void AbstractDecryptVerifyTask::setJob(QGpgME::Job *job)
{
d->job = job;
}
class DecryptVerifyTask::Private
{
DecryptVerifyTask *const q;
public:
explicit Private(DecryptVerifyTask *qq)
: q{qq}
{
}
void startDecryptVerifyJob();
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
void startDecryptVerifyArchiveJob();
#endif
void slotResult(const DecryptionResult &, const VerificationResult &, const QByteArray & = {});
std::shared_ptr<Input> m_input;
std::shared_ptr<Output> m_output;
const QGpgME::Protocol *m_backend = nullptr;
Protocol m_protocol = UnknownProtocol;
bool m_ignoreMDCError = false;
bool m_extractArchive = false;
QString m_inputFilePath;
QString m_outputDirectory;
};
void DecryptVerifyTask::Private::slotResult(const DecryptionResult &dr, const VerificationResult &vr, const QByteArray &plainText)
{
updateKeys(vr);
{
std::stringstream ss;
ss << dr << '\n' << vr;
qCDebug(KLEOPATRA_LOG) << ss.str().c_str();
}
const AuditLogEntry auditLog = auditLogFromSender(q->sender());
if (m_output) {
if (dr.error().code() || vr.error().code()) {
m_output->cancel();
} else {
try {
kleo_assert(!dr.isNull() || !vr.isNull());
m_output->finalize();
} catch (const GpgME::Exception &e) {
q->emitResult(q->fromDecryptResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
return;
} catch (const std::exception &e) {
q->emitResult(q->fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), auditLog));
return;
} catch (...) {
q->emitResult(q->fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
return;
}
}
}
const int drErr = dr.error().code();
const QString errorString = m_output ? m_output->errorString() : QString{};
if (((drErr == GPG_ERR_EIO || drErr == GPG_ERR_NO_DATA) && !errorString.isEmpty()) ||
(m_output && m_output->failed())) {
q->emitResult(q->fromDecryptResult(drErr ? dr.error() : Error::fromCode(GPG_ERR_EIO),
errorString, auditLog));
return;
}
q->emitResult(q->fromDecryptVerifyResult(dr, vr, plainText, m_output ? m_output->fileName() : QString{}, auditLog));
}
DecryptVerifyTask::DecryptVerifyTask(QObject *parent) : AbstractDecryptVerifyTask(parent), d(new Private(this))
{
}
DecryptVerifyTask::~DecryptVerifyTask()
{
}
void DecryptVerifyTask::setInput(const std::shared_ptr<Input> &input)
{
d->m_input = input;
kleo_assert(d->m_input && d->m_input->ioDevice());
}
void DecryptVerifyTask::setOutput(const std::shared_ptr<Output> &output)
{
d->m_output = output;
kleo_assert(d->m_output && d->m_output->ioDevice());
}
void DecryptVerifyTask::setProtocol(Protocol prot)
{
kleo_assert(prot != UnknownProtocol);
d->m_protocol = prot;
d->m_backend = prot == GpgME::OpenPGP ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(d->m_backend);
}
void DecryptVerifyTask::autodetectProtocolFromInput()
{
if (!d->m_input) {
return;
}
const Protocol p = findProtocol(d->m_input->classification());
if (p == UnknownProtocol) {
throw Exception(gpg_error(GPG_ERR_NOTHING_FOUND), i18n("Could not determine whether this is an S/MIME or an OpenPGP signature/ciphertext - maybe it is neither ciphertext nor a signature?"), Exception::MessageOnly);
}
setProtocol(p);
}
QString DecryptVerifyTask::label() const
{
return i18n("Decrypting: %1...", d->m_input->label());
}
unsigned long long DecryptVerifyTask::inputSize() const
{
return d->m_input ? d->m_input->size() : 0;
}
QString DecryptVerifyTask::inputLabel() const
{
return d->m_input ? d->m_input->label() : QString();
}
QString DecryptVerifyTask::outputLabel() const
{
return d->m_output ? d->m_output->label() : d->m_outputDirectory;
}
Protocol DecryptVerifyTask::protocol() const
{
return d->m_protocol;
}
static void ensureIOOpen(QIODevice *input, QIODevice *output)
{
if (input && !input->isOpen()) {
input->open(QIODevice::ReadOnly);
}
if (output && !output->isOpen()) {
output->open(QIODevice::WriteOnly);
}
}
void DecryptVerifyTask::setIgnoreMDCError(bool value)
{
d->m_ignoreMDCError = value;
}
void DecryptVerifyTask::setExtractArchive(bool extract)
{
d->m_extractArchive = extract;
}
void DecryptVerifyTask::setInputFile(const QString &path)
{
d->m_inputFilePath = path;
}
void DecryptVerifyTask::setOutputDirectory(const QString &directory)
{
d->m_outputDirectory = directory;
}
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
static bool archiveJobsCanBeUsed(GpgME::Protocol protocol)
{
return (protocol == GpgME::OpenPGP) && QGpgME::DecryptVerifyArchiveJob::isSupported();
}
#endif
void DecryptVerifyTask::doStart()
{
kleo_assert(d->m_backend);
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
if (d->m_extractArchive && archiveJobsCanBeUsed(d->m_protocol)) {
d->startDecryptVerifyArchiveJob();
} else
#endif
{
d->startDecryptVerifyJob();
}
}
static void setIgnoreMDCErrorFlag(QGpgME::Job *job, bool ignoreMDCError)
{
if (ignoreMDCError) {
qCDebug(KLEOPATRA_LOG) << "Modifying job to ignore MDC errors.";
auto ctx = QGpgME::Job::context(job);
if (!ctx) {
qCWarning(KLEOPATRA_LOG) << "Failed to get context for job";
} else {
const auto err = ctx->setFlag("ignore-mdc-error", "1");
if (err) {
qCWarning(KLEOPATRA_LOG) << "Failed to set ignore mdc errors" << Formatting::errorAsString(err);
}
}
}
}
void DecryptVerifyTask::Private::startDecryptVerifyJob()
{
try {
std::unique_ptr<QGpgME::DecryptVerifyJob> job{m_backend->decryptVerifyJob()};
kleo_assert(job);
setIgnoreMDCErrorFlag(job.get(), m_ignoreMDCError);
QObject::connect(job.get(), &QGpgME::DecryptVerifyJob::result, q, [this](const GpgME::DecryptionResult &decryptResult, const GpgME::VerificationResult &verifyResult, const QByteArray &plainText) {
slotResult(decryptResult, verifyResult, plainText);
});
#if QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS
connect(job.get(), &QGpgME::Job::jobProgress, q, &DecryptVerifyTask::setProgress);
#else
QObject::connect(job.get(), &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
q->setProgress(processed, total);
});
#endif
ensureIOOpen(m_input->ioDevice().get(), m_output->ioDevice().get());
job->start(m_input->ioDevice(), m_output->ioDevice());
q->setJob(job.release());
} catch (const GpgME::Exception &e) {
q->emitResult(q->fromDecryptVerifyResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLogEntry()));
} catch (const std::exception &e) {
q->emitResult(q->fromDecryptVerifyResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLogEntry()));
} catch (...) {
q->emitResult(q->fromDecryptVerifyResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLogEntry()));
}
}
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
void DecryptVerifyTask::Private::startDecryptVerifyArchiveJob()
{
std::unique_ptr<QGpgME::DecryptVerifyArchiveJob> job{m_backend->decryptVerifyArchiveJob()};
kleo_assert(job);
setIgnoreMDCErrorFlag(job.get(), m_ignoreMDCError);
connect(job.get(), &QGpgME::DecryptVerifyArchiveJob::result, q, [this](const GpgME::DecryptionResult &decryptResult, const GpgME::VerificationResult &verifyResult) {
slotResult(decryptResult, verifyResult);
});
#if QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS
connect(job.get(), &QGpgME::Job::jobProgress, q, &DecryptVerifyTask::setProgress);
#else
connect(job.get(), &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
q->setProgress(processed, total);
});
#endif
#if QGPGME_ARCHIVE_JOBS_SUPPORT_INPUT_FILENAME
// make sure that we don't use an existing output directory
const auto outputDirectory = ensureUniqueDirectory(m_outputDirectory);
if (outputDirectory.isEmpty()) {
q->emitResult(q->fromDecryptVerifyResult(Error::fromCode(GPG_ERR_GENERAL), {}, {}));
return;
}
m_outputDirectory = outputDirectory;
job->setInputFile(m_inputFilePath);
job->setOutputDirectory(m_outputDirectory);
const auto err = job->startIt();
#else
ensureIOOpen(m_input->ioDevice().get(), nullptr);
job->setOutputDirectory(m_outputDirectory);
const auto err = job->start(m_input->ioDevice());
#endif
q->setJob(job.release());
if (err) {
q->emitResult(q->fromDecryptVerifyResult(err, {}, {}));
}
}
#endif
class DecryptTask::Private
{
DecryptTask *const q;
public:
explicit Private(DecryptTask *qq)
: q{qq}
{
}
void slotResult(const DecryptionResult &, const QByteArray &);
void registerJob(QGpgME::DecryptJob *job)
{
q->connect(job, SIGNAL(result(GpgME::DecryptionResult,QByteArray)),
q, SLOT(slotResult(GpgME::DecryptionResult,QByteArray)));
#if QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS
q->connect(job, &QGpgME::Job::jobProgress, q, &DecryptTask::setProgress);
#else
q->connect(job, &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
q->setProgress(processed, total);
});
#endif
}
std::shared_ptr<Input> m_input;
std::shared_ptr<Output> m_output;
const QGpgME::Protocol *m_backend = nullptr;
Protocol m_protocol = UnknownProtocol;
};
void DecryptTask::Private::slotResult(const DecryptionResult &result, const QByteArray &plainText)
{
{
std::stringstream ss;
ss << result;
qCDebug(KLEOPATRA_LOG) << ss.str().c_str();
}
const AuditLogEntry auditLog = auditLogFromSender(q->sender());
if (result.error().code()) {
m_output->cancel();
} else {
try {
kleo_assert(!result.isNull());
m_output->finalize();
} catch (const GpgME::Exception &e) {
q->emitResult(q->fromDecryptResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
return;
} catch (const std::exception &e) {
q->emitResult(q->fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), auditLog));
return;
} catch (...) {
q->emitResult(q->fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
return;
}
}
const int drErr = result.error().code();
const QString errorString = m_output->errorString();
if (((drErr == GPG_ERR_EIO || drErr == GPG_ERR_NO_DATA) && !errorString.isEmpty()) ||
m_output->failed()) {
q->emitResult(q->fromDecryptResult(result.error() ? result.error() : Error::fromCode(GPG_ERR_EIO),
errorString, auditLog));
return;
}
q->emitResult(q->fromDecryptResult(result, plainText, auditLog));
}
DecryptTask::DecryptTask(QObject *parent) : AbstractDecryptVerifyTask(parent), d(new Private(this))
{
}
DecryptTask::~DecryptTask()
{
}
void DecryptTask::setInput(const std::shared_ptr<Input> &input)
{
d->m_input = input;
kleo_assert(d->m_input && d->m_input->ioDevice());
}
void DecryptTask::setOutput(const std::shared_ptr<Output> &output)
{
d->m_output = output;
kleo_assert(d->m_output && d->m_output->ioDevice());
}
void DecryptTask::setProtocol(Protocol prot)
{
kleo_assert(prot != UnknownProtocol);
d->m_protocol = prot;
d->m_backend = (prot == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(d->m_backend);
}
void DecryptTask::autodetectProtocolFromInput()
{
if (!d->m_input) {
return;
}
const Protocol p = findProtocol(d->m_input->classification());
if (p == UnknownProtocol) {
throw Exception(gpg_error(GPG_ERR_NOTHING_FOUND), i18n("Could not determine whether this was S/MIME- or OpenPGP-encrypted - maybe it is not ciphertext at all?"), Exception::MessageOnly);
}
setProtocol(p);
}
QString DecryptTask::label() const
{
return i18n("Decrypting: %1...", d->m_input->label());
}
unsigned long long DecryptTask::inputSize() const
{
return d->m_input ? d->m_input->size() : 0;
}
QString DecryptTask::inputLabel() const
{
return d->m_input ? d->m_input->label() : QString();
}
QString DecryptTask::outputLabel() const
{
return d->m_output ? d->m_output->label() : QString();
}
Protocol DecryptTask::protocol() const
{
return d->m_protocol;
}
void DecryptTask::doStart()
{
kleo_assert(d->m_backend);
try {
std::unique_ptr<QGpgME::DecryptJob> job{d->m_backend->decryptJob()};
kleo_assert(job);
d->registerJob(job.get());
ensureIOOpen(d->m_input->ioDevice().get(), d->m_output->ioDevice().get());
job->start(d->m_input->ioDevice(), d->m_output->ioDevice());
setJob(job.release());
} catch (const GpgME::Exception &e) {
emitResult(fromDecryptResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLogEntry()));
} catch (const std::exception &e) {
emitResult(fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLogEntry()));
} catch (...) {
emitResult(fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLogEntry()));
}
}
class VerifyOpaqueTask::Private
{
VerifyOpaqueTask *const q;
public:
explicit Private(VerifyOpaqueTask *qq)
: q{qq}
{
}
void startVerifyOpaqueJob();
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
void startDecryptVerifyArchiveJob();
#endif
void slotResult(const VerificationResult &, const QByteArray & = {});
std::shared_ptr<Input> m_input;
std::shared_ptr<Output> m_output;
const QGpgME::Protocol *m_backend = nullptr;
Protocol m_protocol = UnknownProtocol;
bool m_extractArchive = false;
QString m_inputFilePath;
QString m_outputDirectory;
};
void VerifyOpaqueTask::Private::slotResult(const VerificationResult &result, const QByteArray &plainText)
{
updateKeys(result);
{
std::stringstream ss;
ss << result;
qCDebug(KLEOPATRA_LOG) << ss.str().c_str();
}
const AuditLogEntry auditLog = auditLogFromSender(q->sender());
if (m_output) {
if (result.error().code()) {
m_output->cancel();
} else {
try {
kleo_assert(!result.isNull());
m_output->finalize();
} catch (const GpgME::Exception &e) {
q->emitResult(q->fromVerifyOpaqueResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
return;
} catch (const std::exception &e) {
q->emitResult(q->fromVerifyOpaqueResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), auditLog));
return;
} catch (...) {
q->emitResult(q->fromVerifyOpaqueResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
return;
}
}
}
const int drErr = result.error().code();
const QString errorString = m_output ? m_output->errorString() : QString{};
if (((drErr == GPG_ERR_EIO || drErr == GPG_ERR_NO_DATA) && !errorString.isEmpty()) ||
(m_output && m_output->failed())) {
q->emitResult(q->fromVerifyOpaqueResult(result.error() ? result.error() : Error::fromCode(GPG_ERR_EIO),
errorString, auditLog));
return;
}
q->emitResult(q->fromVerifyOpaqueResult(result, plainText, auditLog));
}
VerifyOpaqueTask::VerifyOpaqueTask(QObject *parent) : AbstractDecryptVerifyTask(parent), d(new Private(this))
{
}
VerifyOpaqueTask::~VerifyOpaqueTask()
{
}
void VerifyOpaqueTask::setInput(const std::shared_ptr<Input> &input)
{
d->m_input = input;
kleo_assert(d->m_input && d->m_input->ioDevice());
}
void VerifyOpaqueTask::setOutput(const std::shared_ptr<Output> &output)
{
d->m_output = output;
kleo_assert(d->m_output && d->m_output->ioDevice());
}
void VerifyOpaqueTask::setProtocol(Protocol prot)
{
kleo_assert(prot != UnknownProtocol);
d->m_protocol = prot;
d->m_backend = (prot == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(d->m_backend);
}
void VerifyOpaqueTask::autodetectProtocolFromInput()
{
if (!d->m_input) {
return;
}
const Protocol p = findProtocol(d->m_input->classification());
if (p == UnknownProtocol) {
throw Exception(gpg_error(GPG_ERR_NOTHING_FOUND), i18n("Could not determine whether this is an S/MIME or an OpenPGP signature - maybe it is not a signature at all?"), Exception::MessageOnly);
}
setProtocol(p);
}
QString VerifyOpaqueTask::label() const
{
return i18n("Verifying: %1...", d->m_input->label());
}
unsigned long long VerifyOpaqueTask::inputSize() const
{
return d->m_input ? d->m_input->size() : 0;
}
QString VerifyOpaqueTask::inputLabel() const
{
return d->m_input ? d->m_input->label() : QString();
}
QString VerifyOpaqueTask::outputLabel() const
{
return d->m_output ? d->m_output->label() : d->m_outputDirectory;
}
Protocol VerifyOpaqueTask::protocol() const
{
return d->m_protocol;
}
void VerifyOpaqueTask::setExtractArchive(bool extract)
{
d->m_extractArchive = extract;
}
void VerifyOpaqueTask::setInputFile(const QString &path)
{
d->m_inputFilePath = path;
}
void VerifyOpaqueTask::setOutputDirectory(const QString &directory)
{
d->m_outputDirectory = directory;
}
void VerifyOpaqueTask::doStart()
{
kleo_assert(d->m_backend);
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
if (d->m_extractArchive && archiveJobsCanBeUsed(d->m_protocol)) {
d->startDecryptVerifyArchiveJob();
} else
#endif
{
d->startVerifyOpaqueJob();
}
}
void VerifyOpaqueTask::Private::startVerifyOpaqueJob()
{
try {
std::unique_ptr<QGpgME::VerifyOpaqueJob> job{m_backend->verifyOpaqueJob()};
kleo_assert(job);
connect(job.get(), &QGpgME::VerifyOpaqueJob::result, q, [this](const GpgME::VerificationResult &result, const QByteArray &plainText) {
slotResult(result, plainText);
});
#if QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS
connect(job.get(), &QGpgME::Job::jobProgress, q, &VerifyOpaqueTask::setProgress);
#else
connect(job.get(), &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
q->setProgress(processed, total);
});
#endif
ensureIOOpen(m_input->ioDevice().get(), m_output ? m_output->ioDevice().get() : nullptr);
job->start(m_input->ioDevice(), m_output ? m_output->ioDevice() : std::shared_ptr<QIODevice>());
q->setJob(job.release());
} catch (const GpgME::Exception &e) {
q->emitResult(q->fromVerifyOpaqueResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLogEntry()));
} catch (const std::exception &e) {
q->emitResult(q->fromVerifyOpaqueResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLogEntry()));
} catch (...) {
q->emitResult(q->fromVerifyOpaqueResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLogEntry()));
}
}
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
void VerifyOpaqueTask::Private::startDecryptVerifyArchiveJob()
{
std::unique_ptr<QGpgME::DecryptVerifyArchiveJob> job{m_backend->decryptVerifyArchiveJob()};
kleo_assert(job);
connect(job.get(), &QGpgME::DecryptVerifyArchiveJob::result, q, [this](const DecryptionResult &, const VerificationResult &verifyResult) {
slotResult(verifyResult);
});
connect(job.get(), &QGpgME::DecryptVerifyArchiveJob::dataProgress, q, &VerifyOpaqueTask::setProgress);
#if QGPGME_ARCHIVE_JOBS_SUPPORT_INPUT_FILENAME
// make sure that we don't use an existing output directory
const auto outputDirectory = ensureUniqueDirectory(m_outputDirectory);
if (outputDirectory.isEmpty()) {
q->emitResult(q->fromDecryptVerifyResult(Error::fromCode(GPG_ERR_GENERAL), {}, {}));
return;
}
m_outputDirectory = outputDirectory;
job->setInputFile(m_inputFilePath);
job->setOutputDirectory(m_outputDirectory);
const auto err = job->startIt();
#else
ensureIOOpen(m_input->ioDevice().get(), nullptr);
job->setOutputDirectory(m_outputDirectory);
const auto err = job->start(m_input->ioDevice());
#endif
q->setJob(job.release());
if (err) {
q->emitResult(q->fromVerifyOpaqueResult(err, {}, {}));
}
}
#endif
class VerifyDetachedTask::Private
{
VerifyDetachedTask *const q;
public:
explicit Private(VerifyDetachedTask *qq)
: q{qq}
{
}
void slotResult(const VerificationResult &);
void registerJob(QGpgME::VerifyDetachedJob *job)
{
q->connect(job, SIGNAL(result(GpgME::VerificationResult)),
q, SLOT(slotResult(GpgME::VerificationResult)));
#if QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS
q->connect(job, &QGpgME::Job::jobProgress, q, &VerifyDetachedTask::setProgress);
#else
q->connect(job, &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
q->setProgress(processed, total);
});
#endif
}
std::shared_ptr<Input> m_input, m_signedData;
const QGpgME::Protocol *m_backend = nullptr;
Protocol m_protocol = UnknownProtocol;
};
void VerifyDetachedTask::Private::slotResult(const VerificationResult &result)
{
updateKeys(result);
{
std::stringstream ss;
ss << result;
qCDebug(KLEOPATRA_LOG) << ss.str().c_str();
}
const AuditLogEntry auditLog = auditLogFromSender(q->sender());
try {
kleo_assert(!result.isNull());
q->emitResult(q->fromVerifyDetachedResult(result, auditLog));
} catch (const GpgME::Exception &e) {
q->emitResult(q->fromVerifyDetachedResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
} catch (const std::exception &e) {
q->emitResult(q->fromVerifyDetachedResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), auditLog));
} catch (...) {
q->emitResult(q->fromVerifyDetachedResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
}
}
VerifyDetachedTask::VerifyDetachedTask(QObject *parent) : AbstractDecryptVerifyTask(parent), d(new Private(this))
{
}
VerifyDetachedTask::~VerifyDetachedTask()
{
}
void VerifyDetachedTask::setInput(const std::shared_ptr<Input> &input)
{
d->m_input = input;
kleo_assert(d->m_input && d->m_input->ioDevice());
}
void VerifyDetachedTask::setSignedData(const std::shared_ptr<Input> &signedData)
{
d->m_signedData = signedData;
kleo_assert(d->m_signedData && d->m_signedData->ioDevice());
}
void VerifyDetachedTask::setProtocol(Protocol prot)
{
kleo_assert(prot != UnknownProtocol);
d->m_protocol = prot;
d->m_backend = (prot == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(d->m_backend);
}
void VerifyDetachedTask::autodetectProtocolFromInput()
{
if (!d->m_input) {
return;
}
const Protocol p = findProtocol(d->m_input->classification());
if (p == UnknownProtocol) {
throw Exception(gpg_error(GPG_ERR_NOTHING_FOUND), i18n("Could not determine whether this is an S/MIME or an OpenPGP signature - maybe it is not a signature at all?"), Exception::MessageOnly);
}
setProtocol(p);
}
unsigned long long VerifyDetachedTask::inputSize() const
{
return d->m_signedData ? d->m_signedData->size() : 0;
}
QString VerifyDetachedTask::label() const
{
if (d->m_signedData) {
return xi18nc("Verification of a detached signature in progress. The first file contains the data."
"The second file is the signature file.",
"Verifying: <filename>%1</filename> with <filename>%2</filename>...",
d->m_signedData->label(),
d->m_input->label());
}
return i18n("Verifying signature: %1...", d->m_input->label());
}
QString VerifyDetachedTask::inputLabel() const
{
if (d->m_signedData && d->m_input) {
return xi18nc("Verification of a detached signature summary. The first file contains the data."
"The second file is signature.",
"Verified <filename>%1</filename> with <filename>%2</filename>",
d->m_signedData->label(),
d->m_input->label());
}
return d->m_input ? d->m_input->label() : QString();
}
QString VerifyDetachedTask::outputLabel() const
{
return QString();
}
Protocol VerifyDetachedTask::protocol() const
{
return d->m_protocol;
}
void VerifyDetachedTask::doStart()
{
kleo_assert(d->m_backend);
try {
std::unique_ptr<QGpgME::VerifyDetachedJob> job{d->m_backend->verifyDetachedJob()};
kleo_assert(job);
d->registerJob(job.get());
ensureIOOpen(d->m_input->ioDevice().get(), nullptr);
ensureIOOpen(d->m_signedData->ioDevice().get(), nullptr);
job->start(d->m_input->ioDevice(), d->m_signedData->ioDevice());
setJob(job.release());
} catch (const GpgME::Exception &e) {
emitResult(fromVerifyDetachedResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLogEntry()));
} catch (const std::exception &e) {
emitResult(fromVerifyDetachedResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLogEntry()));
} catch (...) {
emitResult(fromVerifyDetachedResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLogEntry()));
}
}
#include "moc_decryptverifytask.cpp"
diff --git a/src/crypto/decryptverifytask.h b/src/crypto/decryptverifytask.h
index e658c546a..7cf52559c 100644
--- a/src/crypto/decryptverifytask.h
+++ b/src/crypto/decryptverifytask.h
@@ -1,261 +1,262 @@
/* -*- mode: c++; c-basic-offset:4 -*-
decryptverifytask.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "task.h"
#include <utils/types.h>
#include <gpgme++/verificationresult.h>
#include <memory>
namespace KMime
{
namespace Types
{
class Mailbox;
}
}
namespace GpgME
{
class DecryptionResult;
class VerificationResult;
class Key;
class Signature;
}
namespace QGpgME
{
class Job;
}
namespace Kleo
{
class Input;
class Output;
class AuditLogEntry;
}
namespace Kleo
{
namespace Crypto
{
class DecryptVerifyResult;
class AbstractDecryptVerifyTask : public Task
{
Q_OBJECT
public:
explicit AbstractDecryptVerifyTask(QObject *parent = nullptr);
~AbstractDecryptVerifyTask() override;
virtual void autodetectProtocolFromInput() = 0;
KMime::Types::Mailbox informativeSender() const;
void setInformativeSender(const KMime::Types::Mailbox &senders);
virtual QString inputLabel() const = 0;
virtual QString outputLabel() const = 0;
public Q_SLOTS:
void cancel() override;
protected:
std::shared_ptr<DecryptVerifyResult> fromDecryptResult(const GpgME::DecryptionResult &dr, const QByteArray &plaintext, const AuditLogEntry &auditLog);
std::shared_ptr<DecryptVerifyResult> fromDecryptResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog);
std::shared_ptr<DecryptVerifyResult> fromDecryptVerifyResult(const GpgME::DecryptionResult &dr, const GpgME::VerificationResult &vr, const QByteArray &plaintext, const QString &fileName, const AuditLogEntry &auditLog);
std::shared_ptr<DecryptVerifyResult> fromDecryptVerifyResult(const GpgME::Error &err, const QString &what, const AuditLogEntry &auditLog);
std::shared_ptr<DecryptVerifyResult> fromVerifyOpaqueResult(const GpgME::VerificationResult &vr, const QByteArray &plaintext, const AuditLogEntry &auditLog);
std::shared_ptr<DecryptVerifyResult> fromVerifyOpaqueResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog);
std::shared_ptr<DecryptVerifyResult> fromVerifyDetachedResult(const GpgME::VerificationResult &vr, const AuditLogEntry &auditLog);
std::shared_ptr<DecryptVerifyResult> fromVerifyDetachedResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog);
public:
// public to allow access from the Private classes of the concrete tasks
QGpgME::Job *job() const;
void setJob(QGpgME::Job *job);
private:
class Private;
kdtools::pimpl_ptr<Private> d;
};
class DecryptTask : public AbstractDecryptVerifyTask
{
Q_OBJECT
public:
explicit DecryptTask(QObject *parent = nullptr);
~DecryptTask() override;
void setInput(const std::shared_ptr<Input> &input);
void setOutput(const std::shared_ptr<Output> &output);
void setProtocol(GpgME::Protocol prot);
void autodetectProtocolFromInput() override;
QString label() const override;
GpgME::Protocol protocol() const override;
QString inputLabel() const override;
QString outputLabel() const override;
private:
void doStart() override;
unsigned long long inputSize() const override;
private:
class Private;
kdtools::pimpl_ptr<Private> d;
Q_PRIVATE_SLOT(d, void slotResult(GpgME::DecryptionResult, QByteArray))
};
class VerifyDetachedTask : public AbstractDecryptVerifyTask
{
Q_OBJECT
public:
explicit VerifyDetachedTask(QObject *parent = nullptr);
~VerifyDetachedTask() override;
void setInput(const std::shared_ptr<Input> &input);
void setSignedData(const std::shared_ptr<Input> &signedData);
void setProtocol(GpgME::Protocol prot);
void autodetectProtocolFromInput() override;
QString label() const override;
GpgME::Protocol protocol() const override;
QString inputLabel() const override;
QString outputLabel() const override;
private:
void doStart() override;
unsigned long long inputSize() const override;
private:
class Private;
kdtools::pimpl_ptr<Private> d;
Q_PRIVATE_SLOT(d, void slotResult(GpgME::VerificationResult))
};
class VerifyOpaqueTask : public AbstractDecryptVerifyTask
{
Q_OBJECT
public:
explicit VerifyOpaqueTask(QObject *parent = nullptr);
~VerifyOpaqueTask() override;
void setInput(const std::shared_ptr<Input> &input);
void setOutput(const std::shared_ptr<Output> &output);
void setProtocol(GpgME::Protocol prot);
void autodetectProtocolFromInput()override;
QString label() const override;
GpgME::Protocol protocol() const override;
void setExtractArchive(bool extract);
void setInputFile(const QString &path);
void setOutputDirectory(const QString &directory);
QString inputLabel() const override;
QString outputLabel() const override;
private:
void doStart() override;
unsigned long long inputSize() const override;
private:
class Private;
kdtools::pimpl_ptr<Private> d;
Q_PRIVATE_SLOT(d, void slotResult(GpgME::VerificationResult, QByteArray))
};
class DecryptVerifyTask : public AbstractDecryptVerifyTask
{
Q_OBJECT
public:
explicit DecryptVerifyTask(QObject *parent = nullptr);
~DecryptVerifyTask() override;
void setInput(const std::shared_ptr<Input> &input);
void setOutput(const std::shared_ptr<Output> &output);
void setProtocol(GpgME::Protocol prot);
void autodetectProtocolFromInput() override;
QString label() const override;
GpgME::Protocol protocol() const override;
void setIgnoreMDCError(bool value);
void setExtractArchive(bool extract);
void setInputFile(const QString &path);
void setOutputDirectory(const QString &directory);
QString inputLabel() const override;
QString outputLabel() const override;
private:
void doStart() override;
unsigned long long inputSize() const override;
private:
class Private;
kdtools::pimpl_ptr<Private> d;
Q_PRIVATE_SLOT(d, void slotResult(GpgME::DecryptionResult, GpgME::VerificationResult, QByteArray))
};
class DecryptVerifyResult : public Task::Result
{
friend class ::Kleo::Crypto::AbstractDecryptVerifyTask;
public:
class SenderInfo;
QString overview() const override;
QString details() const override;
GpgME::Error error() const override;
QString errorString() const override;
VisualCode code() const override;
AuditLogEntry auditLog() const override;
QPointer<Task> parentTask() const override;
+ Task::Result::ContentType viewableContentType() const override;
GpgME::VerificationResult verificationResult() const;
GpgME::DecryptionResult decryptionResult() const;
QString fileName() const;
private:
DecryptVerifyResult();
DecryptVerifyResult(const DecryptVerifyResult &);
DecryptVerifyResult &operator=(const DecryptVerifyResult &other);
DecryptVerifyResult(DecryptVerifyOperation op,
const GpgME::VerificationResult &vr,
const GpgME::DecryptionResult &dr,
const QByteArray &stuff,
const QString &fileName,
const GpgME::Error &error,
const QString &errString,
const QString &inputLabel,
const QString &outputLabel,
const AuditLogEntry &auditLog,
Task *parentTask,
const KMime::Types::Mailbox &informativeSender);
private:
class Private;
kdtools::pimpl_ptr<Private> d;
};
}
}
diff --git a/src/crypto/gui/decryptverifyfilesdialog.cpp b/src/crypto/gui/decryptverifyfilesdialog.cpp
index bcc2b8e86..991f240fe 100644
--- a/src/crypto/gui/decryptverifyfilesdialog.cpp
+++ b/src/crypto/gui/decryptverifyfilesdialog.cpp
@@ -1,241 +1,254 @@
/* crypto/gui/decryptverifyfilesdialog.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "decryptverifyfilesdialog.h"
#include "kleopatra_debug.h"
#include "crypto/taskcollection.h"
#include "crypto/decryptverifytask.h"
#include "crypto/gui/resultpage.h"
#include "crypto/gui/resultlistwidget.h"
#include "utils/path-helper.h"
+#include "dialogs/messageviewerdialog.h"
#include <Libkleo/FileNameRequester>
#include <QWindow>
#include <QVBoxLayout>
#include <QProgressBar>
#include <QLabel>
#include <QPushButton>
#include <vector>
#include <KLocalizedString>
+#include <KLocalizedContext>
#include <KMessageBox>
#include <KConfigGroup>
#include <KSharedConfig>
#include <KWindowConfig>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace Kleo::Crypto::Gui;
DecryptVerifyFilesDialog::DecryptVerifyFilesDialog(const std::shared_ptr<TaskCollection> &coll, QWidget *parent)
: QDialog(parent), m_tasks(coll), m_buttonBox(new QDialogButtonBox)
{
readConfig();
auto vLay = new QVBoxLayout(this);
auto labels = new QWidget;
auto outputLayout = new QHBoxLayout;
m_outputLocationFNR = new FileNameRequester;
auto outLabel = new QLabel(i18n("&Output folder:"));
outLabel->setBuddy(m_outputLocationFNR);
outputLayout->addWidget(outLabel);
outputLayout->addWidget(m_outputLocationFNR);
m_outputLocationFNR->setFilter(QDir::Dirs);
vLay->addLayout(outputLayout);
m_progressLabelLayout = new QVBoxLayout(labels);
vLay->addWidget(labels);
m_progressBar = new QProgressBar;
vLay->addWidget(m_progressBar);
m_resultList = new ResultListWidget;
+ connect(m_resultList, &ResultListWidget::showButtonClicked,
+ this, &DecryptVerifyFilesDialog::showContent);
vLay->addWidget(m_resultList);
m_tasks = coll;
Q_ASSERT(m_tasks);
m_resultList->setTaskCollection(coll);
connect(m_tasks.get(), &TaskCollection::progress, this, &DecryptVerifyFilesDialog::progress);
connect(m_tasks.get(), &TaskCollection::done, this, &DecryptVerifyFilesDialog::allDone);
connect(m_tasks.get(), &TaskCollection::started, this, &DecryptVerifyFilesDialog::started);
connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(m_buttonBox, &QDialogButtonBox::clicked, this, &DecryptVerifyFilesDialog::btnClicked);
layout()->addWidget(m_buttonBox);
bool hasOutputs = false;
for (const auto &t: coll->tasks()) {
if (!qobject_cast<VerifyDetachedTask *>(t.get())) {
hasOutputs = true;
break;
}
}
if (hasOutputs) {
setWindowTitle(i18nc("@title:window", "Decrypt/Verify Files"));
m_saveButton = QDialogButtonBox::SaveAll;
m_buttonBox->addButton(QDialogButtonBox::Discard);
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &DecryptVerifyFilesDialog::checkAccept);
} else {
outLabel->setVisible(false);
m_outputLocationFNR->setVisible(false);
setWindowTitle(i18nc("@title:window", "Verify Files"));
m_buttonBox->addButton(QDialogButtonBox::Close);
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
}
if (m_saveButton) {
m_buttonBox->addButton(m_saveButton);
m_buttonBox->button(m_saveButton)->setEnabled(false);
}
}
DecryptVerifyFilesDialog::~DecryptVerifyFilesDialog()
{
qCDebug(KLEOPATRA_LOG);
writeConfig();
}
void DecryptVerifyFilesDialog::allDone()
{
qCDebug(KLEOPATRA_LOG) << "All done";
Q_ASSERT(m_tasks);
m_progressBar->setRange(0, 100);
m_progressBar->setValue(100);
for (const auto &i: m_progressLabelByTag.keys()) {
if (!i.isEmpty()) {
m_progressLabelByTag.value(i)->setText(i18n("%1: All operations completed.", i));
} else {
m_progressLabelByTag.value(i)->setText(i18n("All operations completed."));
}
}
if (m_tasks->allTasksHaveErrors()) {
return;
}
if (m_saveButton != QDialogButtonBox::NoButton) {
m_buttonBox->button(m_saveButton)->setEnabled(true);
} else {
m_buttonBox->removeButton(m_buttonBox->button(QDialogButtonBox::Close));
m_buttonBox->addButton(QDialogButtonBox::Ok);
}
}
void DecryptVerifyFilesDialog::started(const std::shared_ptr<Task> &task)
{
Q_ASSERT(task);
const auto tag = task->tag();
auto label = labelForTag(tag);
Q_ASSERT(label);
if (tag.isEmpty()) {
label->setText(i18nc("number, operation description", "Operation %1: %2", m_tasks->numberOfCompletedTasks() + 1, task->label()));
} else {
label->setText(i18nc(R"(tag( "OpenPGP" or "CMS"), operation description)", "%1: %2", tag, task->label()));
}
if (m_saveButton != QDialogButtonBox::NoButton) {
m_buttonBox->button(m_saveButton)->setEnabled(false);
} else if (m_buttonBox->button(QDialogButtonBox::Ok)) {
m_buttonBox->removeButton(m_buttonBox->button(QDialogButtonBox::Ok));
m_buttonBox->addButton(QDialogButtonBox::Close);
}
}
QLabel *DecryptVerifyFilesDialog::labelForTag(const QString &tag)
{
if (QLabel *const label = m_progressLabelByTag.value(tag)) {
return label;
}
auto label = new QLabel;
label->setTextFormat(Qt::RichText);
label->setWordWrap(true);
m_progressLabelLayout->addWidget(label);
m_progressLabelByTag.insert(tag, label);
return label;
}
void DecryptVerifyFilesDialog::progress(int progress, int total)
{
Q_ASSERT(progress >= 0);
Q_ASSERT(total >= 0);
m_progressBar->setRange(0, total);
m_progressBar->setValue(progress);
}
void DecryptVerifyFilesDialog::setOutputLocation(const QString &dir)
{
m_outputLocationFNR->setFileName(dir);
}
QString DecryptVerifyFilesDialog::outputLocation() const
{
return m_outputLocationFNR->fileName();
}
void DecryptVerifyFilesDialog::btnClicked(QAbstractButton *btn)
{
if (m_buttonBox->buttonRole(btn) == QDialogButtonBox::DestructiveRole) {
close();
}
}
void DecryptVerifyFilesDialog::checkAccept() {
const auto outLoc = outputLocation();
if (outLoc.isEmpty()) {
KMessageBox::information(this, i18n("Please select an output folder."),
i18nc("@title:window", "No Output Folder"));
return;
}
const QFileInfo fi(outLoc);
if (!fi.exists()) {
qCDebug(KLEOPATRA_LOG) << "Output dir does not exist. Trying to create.";
const QDir dir(outLoc);
if (!dir.mkdir(outLoc)) {
KMessageBox::information(this, xi18nc("@info", "<para>Failed to create output folder <filename>%1</filename>.</para><para>Please select a different output folder.</para>", outLoc),
i18nc("@title:window", "Unusable Output Folder"));
} else {
accept();
}
} else if (!fi.isDir()) {
KMessageBox::information(this, i18n("Please select a different output folder."),
i18nc("@title:window", "Invalid Output Folder"));
} else if (!Kleo::isWritable(fi)) {
KMessageBox::information(this, xi18nc("@info", "<para>Cannot write in the output folder <filename>%1</filename>.</para><para>Please select a different output folder.</para>", outLoc),
i18nc("@title:window", "Unusable Output Folder"));
} else {
accept();
}
}
void DecryptVerifyFilesDialog::readConfig()
{
winId(); // ensure there's a window created
// set default window size
windowHandle()->resize(640, 480);
// restore size from config file
KConfigGroup cfgGroup(KSharedConfig::openStateConfig(), "DecryptVerifyFilesDialog");
KWindowConfig::restoreWindowSize(windowHandle(), cfgGroup);
// NOTICE: QWindow::setGeometry() does NOT impact the backing QWidget geometry even if the platform
// window was created -> QTBUG-40584. We therefore copy the size here.
// TODO: remove once this was resolved in QWidget QPA
resize(windowHandle()->size());
}
void DecryptVerifyFilesDialog::writeConfig()
{
KConfigGroup cfgGroup(KSharedConfig::openStateConfig(), "DecryptVerifyFilesDialog");
KWindowConfig::saveWindowSize(windowHandle(), cfgGroup);
cfgGroup.sync();
}
+
+void DecryptVerifyFilesDialog::showContent(const std::shared_ptr<const Task::Result> &result)
+{
+ if (auto decryptVerifyResult = std::dynamic_pointer_cast<const DecryptVerifyResult>(result)) {
+ auto dialog = new MessageViewerDialog(decryptVerifyResult->fileName());
+ dialog->setAttribute(Qt::WA_DeleteOnClose);
+ dialog->show();
+ }
+}
diff --git a/src/crypto/gui/decryptverifyfilesdialog.h b/src/crypto/gui/decryptverifyfilesdialog.h
index c752e3164..b32c5008a 100644
--- a/src/crypto/gui/decryptverifyfilesdialog.h
+++ b/src/crypto/gui/decryptverifyfilesdialog.h
@@ -1,76 +1,77 @@
/* crypto/gui/decryptverifyfilesdialog.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QDialog>
#include <QString>
#include <QDialogButtonBox>
#include <QHash>
#include "crypto/task.h"
#include <memory>
class QVBoxLayout;
class QProgressBar;
template <typename K, typename U> class QHash;
class QLabel;
namespace Kleo
{
class FileNameRequester;
namespace Crypto
{
class TaskCollection;
namespace Gui
{
class ResultListWidget;
class DecryptVerifyFilesDialog : public QDialog
{
Q_OBJECT
public:
explicit DecryptVerifyFilesDialog(const std::shared_ptr<TaskCollection> &coll,
QWidget *parent = nullptr);
~DecryptVerifyFilesDialog() override;
void setOutputLocation(const QString &dir);
QString outputLocation() const;
protected Q_SLOTS:
void progress(int progress, int total);
void started(const std::shared_ptr<Task> &result);
void allDone();
void btnClicked(QAbstractButton *btn);
void checkAccept();
+ void showContent(const std::shared_ptr<const Task::Result> &result);
protected:
void readConfig();
void writeConfig();
protected:
QLabel *labelForTag(const QString &tag);
private:
std::shared_ptr<TaskCollection> m_tasks;
QProgressBar *m_progressBar;
QHash<QString, QLabel *> m_progressLabelByTag;
QVBoxLayout *m_progressLabelLayout;
int m_lastErrorItemIndex;
ResultListWidget *m_resultList;
FileNameRequester *m_outputLocationFNR;
QDialogButtonBox::StandardButton m_saveButton = QDialogButtonBox::NoButton;
QDialogButtonBox *const m_buttonBox;
};
} // namespace Gui
} //namespace Crypto;
} // namespace Kleo
diff --git a/src/crypto/gui/resultitemwidget.cpp b/src/crypto/gui/resultitemwidget.cpp
index 65f189437..0c6b94266 100644
--- a/src/crypto/gui/resultitemwidget.cpp
+++ b/src/crypto/gui/resultitemwidget.cpp
@@ -1,360 +1,372 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/gui/resultitemwidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "resultitemwidget.h"
#include "commands/command.h"
#include "commands/importcertificatefromfilecommand.h"
#include "commands/lookupcertificatescommand.h"
#include "crypto/decryptverifytask.h"
#include "view/htmllabel.h"
#include "view/urllabel.h"
#include <Libkleo/AuditLogEntry>
#include <Libkleo/AuditLogViewer>
#include <Libkleo/Classify>
#include <Libkleo/SystemInfo>
#include <gpgme++/key.h>
#include <gpgme++/decryptionresult.h>
#include <KLocalizedString>
#include <QPushButton>
#include <KStandardGuiItem>
#include "kleopatra_debug.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QUrl>
#include <QVBoxLayout>
#include <KGuiItem>
#include <KColorScheme>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace Kleo::Crypto::Gui;
namespace
{
// TODO move out of here
static QColor colorForVisualCode(Task::Result::VisualCode code)
{
switch (code) {
case Task::Result::AllGood:
return KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::PositiveBackground).color();
case Task::Result::NeutralError:
case Task::Result::Warning:
return KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NormalBackground).color();
case Task::Result::Danger:
return KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NegativeBackground).color();
case Task::Result::NeutralSuccess:
default:
return QColor(0x00, 0x80, 0xFF); // light blue
}
}
static QColor txtColorForVisualCode(Task::Result::VisualCode code)
{
switch (code) {
case Task::Result::AllGood:
case Task::Result::NeutralError:
case Task::Result::Warning:
return KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::NormalText).color();
case Task::Result::Danger:
return KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::NegativeText).color();
case Task::Result::NeutralSuccess:
default:
return QColor(0xFF, 0xFF, 0xFF); // white
}
}
}
class ResultItemWidget::Private
{
ResultItemWidget *const q;
public:
explicit Private(const std::shared_ptr<const Task::Result> &result, ResultItemWidget *qq) : q(qq), m_result(result)
{
Q_ASSERT(m_result);
}
void slotLinkActivated(const QString &);
void updateShowDetailsLabel();
void addKeyImportButton(QBoxLayout *lay, bool search);
void addIgnoreMDCButton(QBoxLayout *lay);
void oneImportFinished();
const std::shared_ptr<const Task::Result> m_result;
UrlLabel *m_auditLogLabel = nullptr;
QPushButton *m_closeButton = nullptr;
+ QPushButton *m_showButton = nullptr;
bool m_importCanceled = false;
};
void ResultItemWidget::Private::oneImportFinished()
{
if (m_importCanceled) {
return;
}
if (m_result->parentTask()) {
m_result->parentTask()->start();
}
q->setVisible(false);
}
void ResultItemWidget::Private::addIgnoreMDCButton(QBoxLayout *lay)
{
if (!m_result || !lay) {
return;
}
const auto dvResult = dynamic_cast<const DecryptVerifyResult *>(m_result.get());
if (!dvResult) {
return;
}
const auto decResult = dvResult->decryptionResult();
if (decResult.isNull() || !decResult.error() || !decResult.isLegacyCipherNoMDC())
{
return;
}
auto btn = new QPushButton(i18n("Force decryption"));
btn->setFixedSize(btn->sizeHint());
connect (btn, &QPushButton::clicked, q, [this] () {
if (m_result->parentTask()) {
const auto dvTask = dynamic_cast<DecryptVerifyTask*>(m_result->parentTask().data());
dvTask->setIgnoreMDCError(true);
dvTask->start();
q->setVisible(false);
} else {
qCWarning(KLEOPATRA_LOG) << "Failed to get parent task";
}
});
lay->addWidget(btn);
}
void ResultItemWidget::Private::addKeyImportButton(QBoxLayout *lay, bool search)
{
if (!m_result || !lay) {
return;
}
const auto dvResult = dynamic_cast<const DecryptVerifyResult *>(m_result.get());
if (!dvResult) {
return;
}
const auto verifyResult = dvResult->verificationResult();
if (verifyResult.isNull()) {
return;
}
for (const auto &sig: verifyResult.signatures()) {
if (!(sig.summary() & GpgME::Signature::KeyMissing)) {
continue;
}
auto btn = new QPushButton;
QString suffix;
const auto keyid = QLatin1String(sig.fingerprint());
if (verifyResult.numSignatures() > 1) {
suffix = QLatin1Char(' ') + keyid;
}
btn = new QPushButton(search ? i18nc("1 is optional keyid. No space is intended as it can be empty.",
"Search%1", suffix)
: i18nc("1 is optional keyid. No space is intended as it can be empty.",
"Import%1", suffix));
if (search) {
btn->setIcon(QIcon::fromTheme(QStringLiteral("edit-find")));
connect (btn, &QPushButton::clicked, q, [this, btn, keyid] () {
btn->setEnabled(false);
m_importCanceled = false;
auto cmd = new Kleo::Commands::LookupCertificatesCommand(keyid, nullptr);
connect(cmd, &Kleo::Commands::LookupCertificatesCommand::canceled,
q, [this]() { m_importCanceled = true; });
connect(cmd, &Kleo::Commands::LookupCertificatesCommand::finished,
q, [this, btn]() {
btn->setEnabled(true);
oneImportFinished();
});
cmd->setParentWidget(q);
cmd->start();
});
} else {
btn->setIcon(QIcon::fromTheme(QStringLiteral("view-certificate-import")));
connect (btn, &QPushButton::clicked, q, [this, btn] () {
btn->setEnabled(false);
m_importCanceled = false;
auto cmd = new Kleo::ImportCertificateFromFileCommand();
connect(cmd, &Kleo::ImportCertificateFromFileCommand::canceled,
q, [this]() { m_importCanceled = true; });
connect(cmd, &Kleo::ImportCertificateFromFileCommand::finished,
q, [this, btn]() {
btn->setEnabled(true);
oneImportFinished();
});
cmd->setParentWidget(q);
cmd->start();
});
}
btn->setFixedSize(btn->sizeHint());
lay->addWidget(btn);
}
}
static QUrl auditlog_url_template()
{
QUrl url(QStringLiteral("kleoresultitem://showauditlog"));
return url;
}
void ResultItemWidget::Private::updateShowDetailsLabel()
{
const auto auditLogUrl = m_result->auditLog().asUrl(auditlog_url_template());
const auto auditLogLinkText =
m_result->hasError() ? i18n("Diagnostics")
: i18nc("The Audit Log is a detailed error log from the gnupg backend",
"Show Audit Log");
m_auditLogLabel->setUrl(auditLogUrl, auditLogLinkText);
m_auditLogLabel->setVisible(!auditLogUrl.isEmpty());
}
ResultItemWidget::ResultItemWidget(const std::shared_ptr<const Task::Result> &result, QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags), d(new Private(result, this))
{
const QColor color = colorForVisualCode(d->m_result->code());
const QColor txtColor = txtColorForVisualCode(d->m_result->code());
const QColor linkColor = txtColor;
const QString styleSheet = SystemInfo::isHighContrastModeActive()
? QStringLiteral(
"QFrame,QLabel { margin: 0px; }"
"QFrame#resultFrame{ border-style: solid; border-radius: 3px; border-width: 1px }"
"QLabel { padding: 5px; border-radius: 3px }")
: QStringLiteral(
"QFrame,QLabel { background-color: %1; margin: 0px; }"
"QFrame#resultFrame{ border-color: %2; border-style: solid; border-radius: 3px; border-width: 1px }"
"QLabel { color: %3; padding: 5px; border-radius: 3px }")
.arg(color.name())
.arg(color.darker(150).name())
.arg(txtColor.name());
auto topLayout = new QVBoxLayout(this);
auto frame = new QFrame;
frame->setObjectName(QStringLiteral("resultFrame"));
frame->setStyleSheet(styleSheet);
topLayout->addWidget(frame);
auto layout = new QHBoxLayout(frame);
auto vlay = new QVBoxLayout();
auto overview = new HtmlLabel;
overview->setWordWrap(true);
overview->setHtml(d->m_result->overview());
overview->setStyleSheet(styleSheet);
overview->setLinkColor(linkColor);
setFocusPolicy(overview->focusPolicy());
setFocusProxy(overview);
connect(overview, &QLabel::linkActivated,
this, [this](const auto &link) { d->slotLinkActivated(link); });
vlay->addWidget(overview);
layout->addLayout(vlay);
auto actionLayout = new QVBoxLayout;
layout->addLayout(actionLayout);
d->addKeyImportButton(actionLayout, false);
// TODO: Only show if auto-key-retrieve is not set.
d->addKeyImportButton(actionLayout, true);
d->addIgnoreMDCButton(actionLayout);
d->m_auditLogLabel = new UrlLabel;
connect(d->m_auditLogLabel, &QLabel::linkActivated,
this, [this](const auto &link) { d->slotLinkActivated(link); });
actionLayout->addWidget(d->m_auditLogLabel);
d->m_auditLogLabel->setStyleSheet(styleSheet);
d->m_auditLogLabel->setLinkColor(linkColor);
auto detailsLabel = new HtmlLabel;
detailsLabel->setWordWrap(true);
detailsLabel->setHtml(d->m_result->details());
detailsLabel->setStyleSheet(styleSheet);
detailsLabel->setLinkColor(linkColor);
connect(detailsLabel, &QLabel::linkActivated,
this, [this](const auto &link) { d->slotLinkActivated(link); });
vlay->addWidget(detailsLabel);
+ d->m_showButton = new QPushButton;
+ d->m_showButton->setVisible(false);
+ connect(d->m_showButton, &QAbstractButton::clicked, this, &ResultItemWidget::showButtonClicked);
+ actionLayout->addWidget(d->m_showButton);
+
d->m_closeButton = new QPushButton;
KGuiItem::assign(d->m_closeButton, KStandardGuiItem::close());
d->m_closeButton->setFixedSize(d->m_closeButton->sizeHint());
connect(d->m_closeButton, &QAbstractButton::clicked, this, &ResultItemWidget::closeButtonClicked);
actionLayout->addWidget(d->m_closeButton);
d->m_closeButton->setVisible(false);
layout->setStretch(0, 1);
actionLayout->addStretch(-1);
vlay->addStretch(-1);
d->updateShowDetailsLabel();
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum);
}
ResultItemWidget::~ResultItemWidget()
{
}
void ResultItemWidget::showCloseButton(bool show)
{
d->m_closeButton->setVisible(show);
}
+void ResultItemWidget::setShowButton(const QString &text, bool show)
+{
+ d->m_showButton->setText(text);
+ d->m_showButton->setVisible(show);
+}
+
bool ResultItemWidget::hasErrorResult() const
{
return d->m_result->hasError();
}
void ResultItemWidget::Private::slotLinkActivated(const QString &link)
{
Q_ASSERT(m_result);
qCDebug(KLEOPATRA_LOG) << "Link activated: " << link;
if (link.startsWith(QLatin1String("key:"))) {
auto split = link.split(QLatin1Char(':'));
auto fpr = split.value(1);
if (split.size() == 2 && isFingerprint(fpr)) {
/* There might be a security consideration here if somehow
* a short keyid is used in a link and it collides with another.
* So we additionally check that it really is a fingerprint. */
auto cmd = Command::commandForQuery(fpr);
cmd->setParentWId(q->effectiveWinId());
cmd->start();
} else {
qCWarning(KLEOPATRA_LOG) << "key link invalid " << link;
}
return;
}
const QUrl url(link);
if (url.host() == QLatin1String("showauditlog")) {
q->showAuditLog();
return;
}
qCWarning(KLEOPATRA_LOG) << "Unexpected link scheme: " << link;
}
void ResultItemWidget::showAuditLog()
{
AuditLogViewer::showAuditLog(parentWidget(), d->m_result->auditLog());
}
#include "moc_resultitemwidget.cpp"
diff --git a/src/crypto/gui/resultitemwidget.h b/src/crypto/gui/resultitemwidget.h
index e2e1346b9..0d14a695a 100644
--- a/src/crypto/gui/resultitemwidget.h
+++ b/src/crypto/gui/resultitemwidget.h
@@ -1,58 +1,60 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/gui/resultitemwidget.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QWidget>
#include <crypto/task.h>
#include <utils/pimpl_ptr.h>
#include <memory>
class QString;
namespace Kleo
{
namespace Crypto
{
class Task;
namespace Gui
{
class ResultItemWidget : public QWidget
{
Q_OBJECT
public:
explicit ResultItemWidget(const std::shared_ptr<const Task::Result> &result, QWidget *parent = nullptr, Qt::WindowFlags flags = {});
~ResultItemWidget() override;
bool hasErrorResult() const;
void showCloseButton(bool show);
+ void setShowButton(const QString &text, bool show);
public Q_SLOTS:
void showAuditLog();
Q_SIGNALS:
void linkActivated(const QString &link);
void closeButtonClicked();
+ void showButtonClicked();
private:
class Private;
kdtools::pimpl_ptr<Private> d;
Q_PRIVATE_SLOT(d, void slotLinkActivated(QString))
};
}
}
}
diff --git a/src/crypto/gui/resultlistwidget.cpp b/src/crypto/gui/resultlistwidget.cpp
index b4f2f8f0b..8663500f0 100644
--- a/src/crypto/gui/resultlistwidget.cpp
+++ b/src/crypto/gui/resultlistwidget.cpp
@@ -1,210 +1,222 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/gui/resultlistwidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "resultlistwidget.h"
#include "emailoperationspreferences.h"
#include <crypto/gui/resultitemwidget.h>
+#include <crypto/decryptverifytask.h>
#include <utils/gui-helper.h>
#include <utils/scrollarea.h>
#include <Libkleo/Stl_Util>
#include <KLocalizedString>
#include <QPushButton>
#include <KStandardGuiItem>
#include <QLabel>
#include <QVBoxLayout>
#include <KGuiItem>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace Kleo::Crypto::Gui;
class ResultListWidget::Private
{
ResultListWidget *const q;
public:
explicit Private(ResultListWidget *qq);
void result(const std::shared_ptr<const Task::Result> &result);
void started(const std::shared_ptr<Task> &task);
void allTasksDone();
void addResultWidget(ResultItemWidget *widget);
void resizeIfStandalone();
std::vector< std::shared_ptr<TaskCollection> > m_collections;
bool m_standaloneMode = false;
int m_lastErrorItemIndex = 0;
ScrollArea *m_scrollArea = nullptr;
QPushButton *m_closeButton = nullptr;
QVBoxLayout *m_layout = nullptr;
QLabel *m_progressLabel = nullptr;
};
ResultListWidget::Private::Private(ResultListWidget *qq)
: q(qq),
m_collections()
{
m_layout = new QVBoxLayout(q);
m_layout->setContentsMargins(0, 0, 0, 0);
m_layout->setSpacing(0);
m_scrollArea = new ScrollArea;
m_scrollArea->setFocusPolicy(Qt::NoFocus);
auto scrollAreaLayout = qobject_cast<QBoxLayout *>(m_scrollArea->widget()->layout());
Q_ASSERT(scrollAreaLayout);
scrollAreaLayout->setContentsMargins(0, 0, 0, 0);
scrollAreaLayout->setSpacing(2);
scrollAreaLayout->addStretch();
m_layout->addWidget(m_scrollArea);
m_progressLabel = new QLabel;
m_progressLabel->setWordWrap(true);
m_layout->addWidget(m_progressLabel);
m_progressLabel->setVisible(false);
m_closeButton = new QPushButton;
KGuiItem::assign(m_closeButton, KStandardGuiItem::close());
q->connect(m_closeButton, &QPushButton::clicked, q, &ResultListWidget::close);
m_layout->addWidget(m_closeButton);
m_closeButton->setVisible(false);
m_closeButton->setEnabled(false);
}
ResultListWidget::ResultListWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f), d(new Private(this))
{
}
ResultListWidget::~ResultListWidget()
{
if (!d->m_standaloneMode) {
return;
}
EMailOperationsPreferences prefs;
prefs.setDecryptVerifyPopupGeometry(geometry());
prefs.save();
}
void ResultListWidget::Private::resizeIfStandalone()
{
if (m_standaloneMode) {
q->resize(q->size().expandedTo(q->sizeHint()));
}
}
void ResultListWidget::Private::addResultWidget(ResultItemWidget *widget)
{
Q_ASSERT(widget);
Q_ASSERT(std::any_of(m_collections.cbegin(), m_collections.cend(),
[](const std::shared_ptr<TaskCollection> &t) { return !t->isEmpty(); }));
Q_ASSERT(m_scrollArea);
Q_ASSERT(m_scrollArea->widget());
auto scrollAreaLayout = qobject_cast<QBoxLayout *>(m_scrollArea->widget()->layout());
Q_ASSERT(scrollAreaLayout);
// insert new widget after last widget showing error or before the trailing stretch
const auto insertIndex = widget->hasErrorResult() ? m_lastErrorItemIndex++ : scrollAreaLayout->count() - 1;
scrollAreaLayout->insertWidget(insertIndex, widget);
if (insertIndex == 0) {
forceSetTabOrder(m_scrollArea->widget(), widget);
} else {
auto previousResultWidget = qobject_cast<ResultItemWidget *>(scrollAreaLayout->itemAt(insertIndex - 1)->widget());
QWidget::setTabOrder(previousResultWidget, widget);
}
widget->show();
resizeIfStandalone();
}
void ResultListWidget::Private::allTasksDone()
{
if (!q->isComplete()) {
return;
}
m_progressLabel->setVisible(false);
resizeIfStandalone();
Q_EMIT q->completeChanged();
}
void ResultListWidget::Private::result(const std::shared_ptr<const Task::Result> &result)
{
Q_ASSERT(result);
Q_ASSERT(std::any_of(m_collections.cbegin(), m_collections.cend(),
[](const std::shared_ptr<TaskCollection> &t) { return !t->isEmpty(); }));
auto wid = new ResultItemWidget(result);
q->connect(wid, &ResultItemWidget::linkActivated, q, &ResultListWidget::linkActivated);
q->connect(wid, &ResultItemWidget::closeButtonClicked, q, &ResultListWidget::close);
+
+ const auto viewableContentType = result->viewableContentType();
+
+ if (viewableContentType == Task::Result::ContentType::Mime
+ || viewableContentType == Task::Result::ContentType::Mbox) {
+ wid->setShowButton(i18nc("@action:button", "Show Email"), true);
+ q->connect(wid, &ResultItemWidget::showButtonClicked, q, [this, result] {
+ Q_EMIT q->showButtonClicked(result);
+ });
+ }
+
addResultWidget(wid);
}
bool ResultListWidget::isComplete() const
{
return std::all_of(d->m_collections.cbegin(), d->m_collections.cend(),
std::mem_fn(&TaskCollection::allTasksCompleted));
}
unsigned int ResultListWidget::totalNumberOfTasks() const
{
return kdtools::accumulate_transform(d->m_collections.cbegin(),
d->m_collections.cend(),
std::mem_fn(&TaskCollection::size), 0U);
}
unsigned int ResultListWidget::numberOfCompletedTasks() const
{
return kdtools::accumulate_transform(d->m_collections.cbegin(), d->m_collections.cend(),
std::mem_fn(&TaskCollection::numberOfCompletedTasks), 0U);
}
void ResultListWidget::setTaskCollection(const std::shared_ptr<TaskCollection> &coll)
{
//clear(); ### PENDING(marc) implement
addTaskCollection(coll);
}
void ResultListWidget::addTaskCollection(const std::shared_ptr<TaskCollection> &coll)
{
Q_ASSERT(coll); Q_ASSERT(!coll->isEmpty());
d->m_collections.push_back(coll);
connect(coll.get(), SIGNAL(result(std::shared_ptr<const Kleo::Crypto::Task::Result>)),
this, SLOT(result(std::shared_ptr<const Kleo::Crypto::Task::Result>)));
connect(coll.get(), SIGNAL(started(std::shared_ptr<Kleo::Crypto::Task>)),
this, SLOT(started(std::shared_ptr<Kleo::Crypto::Task>)));
connect(coll.get(), SIGNAL(done()), this, SLOT(allTasksDone()));
setStandaloneMode(d->m_standaloneMode);
}
void ResultListWidget::Private::started(const std::shared_ptr<Task> &task)
{
Q_ASSERT(task);
Q_ASSERT(m_progressLabel);
m_progressLabel->setText(i18nc("number, operation description", "Operation %1: %2", q->numberOfCompletedTasks() + 1, task->label()));
resizeIfStandalone();
}
void ResultListWidget::setStandaloneMode(bool standalone)
{
d->m_standaloneMode = standalone;
if (totalNumberOfTasks() == 0) {
return;
}
d->m_closeButton->setVisible(standalone);
d->m_closeButton->setEnabled(standalone);
d->m_progressLabel->setVisible(standalone);
}
#include "moc_resultlistwidget.cpp"
diff --git a/src/crypto/gui/resultlistwidget.h b/src/crypto/gui/resultlistwidget.h
index cbc904d5f..8be2bf29b 100644
--- a/src/crypto/gui/resultlistwidget.h
+++ b/src/crypto/gui/resultlistwidget.h
@@ -1,63 +1,64 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/gui/resultlistwidget.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QWidget>
#include <crypto/taskcollection.h>
#include <utils/pimpl_ptr.h>
#include <memory>
class QString;
namespace Kleo
{
namespace Crypto
{
class TaskCollection;
namespace Gui
{
class ResultListWidget : public QWidget
{
Q_OBJECT
public:
explicit ResultListWidget(QWidget *parent = nullptr, Qt::WindowFlags flags = {});
~ResultListWidget() override;
void setTaskCollection(const std::shared_ptr<TaskCollection> &coll);
void addTaskCollection(const std::shared_ptr<TaskCollection> &coll);
void setStandaloneMode(bool standalone);
bool isComplete() const;
unsigned int totalNumberOfTasks() const;
unsigned int numberOfCompletedTasks() const;
Q_SIGNALS:
void linkActivated(const QString &link);
+ void showButtonClicked(const std::shared_ptr<const Task::Result> &result);
void completeChanged();
private:
class Private;
kdtools::pimpl_ptr<Private> d;
Q_PRIVATE_SLOT(d, void result(std::shared_ptr<const Kleo::Crypto::Task::Result>))
Q_PRIVATE_SLOT(d, void started(std::shared_ptr<Kleo::Crypto::Task>))
Q_PRIVATE_SLOT(d, void allTasksDone())
};
}
}
}
diff --git a/src/crypto/task.cpp b/src/crypto/task.cpp
index b1eed7f7b..b55285ca1 100644
--- a/src/crypto/task.cpp
+++ b/src/crypto/task.cpp
@@ -1,228 +1,233 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/task.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "task.h"
#include "task_p.h"
#include "kleopatra_debug.h"
#include <Libkleo/KleoException>
#include <Libkleo/AuditLogEntry>
#include <Libkleo/GnuPG>
#include <gpgme++/exception.h>
#include <gpg-error.h>
#include <KIconLoader>
#include <KLocalizedString>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace GpgME;
namespace
{
class ErrorResult : public Task::Result
{
public:
ErrorResult(const GpgME::Error &error, const QString &details)
: Task::Result(), m_error(error), m_details(details) {}
QString overview() const override
{
return makeOverview(m_details);
}
QString details() const override
{
return QString();
}
GpgME::Error error() const override
{
return m_error;
}
QString errorString() const override
{
return m_details;
}
VisualCode code() const override
{
return NeutralError;
}
AuditLogEntry auditLog() const override
{
return AuditLogEntry();
}
private:
const GpgME::Error m_error;
const QString m_details;
};
}
class Task::Private
{
friend class ::Kleo::Crypto::Task;
Task *const q;
public:
explicit Private(Task *qq);
private:
int m_progress;
int m_totalProgress;
bool m_asciiArmor;
int m_id;
};
namespace
{
static int nextTaskId = 0;
}
Task::Private::Private(Task *qq)
: q(qq), m_progress(0), m_totalProgress(0), m_asciiArmor(false), m_id(nextTaskId++)
{
}
Task::Task(QObject *p)
: QObject(p), d(new Private(this))
{
}
Task::~Task() {}
void Task::setAsciiArmor(bool armor)
{
d->m_asciiArmor = armor;
}
bool Task::asciiArmor() const
{
return d->m_asciiArmor;
}
std::shared_ptr<Task> Task::makeErrorTask(const GpgME::Error &error, const QString &details, const QString &label)
{
const std::shared_ptr<SimpleTask> t(new SimpleTask(label));
t->setResult(t->makeErrorResult(error, details));
return t;
}
int Task::id() const
{
return d->m_id;
}
int Task::currentProgress() const
{
return d->m_progress;
}
int Task::totalProgress() const
{
return d->m_totalProgress;
}
QString Task::tag() const
{
return QString();
}
void Task::setProgress(int processed, int total)
{
d->m_progress = processed;
d->m_totalProgress = total;
Q_EMIT progress(processed, total, QPrivateSignal());
}
void Task::start()
{
try {
doStart();
} catch (const Kleo::Exception &e) {
QMetaObject::invokeMethod(this, "emitError", Qt::QueuedConnection, Q_ARG(GpgME::Error, e.error()), Q_ARG(QString, e.message()));
} catch (const GpgME::Exception &e) {
QMetaObject::invokeMethod(this, "emitError", Qt::QueuedConnection, Q_ARG(GpgME::Error, e.error()), Q_ARG(QString, QString::fromLocal8Bit(e.what())));
} catch (const std::exception &e) {
QMetaObject::invokeMethod(this, "emitError", Qt::QueuedConnection, Q_ARG(GpgME::Error, Error::fromCode(GPG_ERR_UNEXPECTED)), Q_ARG(QString, QString::fromLocal8Bit(e.what())));
} catch (...) {
QMetaObject::invokeMethod(this, "emitError", Qt::QueuedConnection, Q_ARG(GpgME::Error, Error::fromCode(GPG_ERR_UNEXPECTED)), Q_ARG(QString, i18n("Unknown exception in Task::start()")));
}
Q_EMIT started(QPrivateSignal());
}
void Task::emitError(const GpgME::Error &error, const QString &details)
{
emitResult(makeErrorResult(error, details));
}
void Task::emitResult(const std::shared_ptr<const Task::Result> &r)
{
d->m_progress = d->m_totalProgress;
Q_EMIT progress(currentProgress(), totalProgress(), QPrivateSignal());
Q_EMIT result(r, QPrivateSignal());
}
std::shared_ptr<Task::Result> Task::makeErrorResult(const GpgME::Error &error, const QString &details)
{
return std::shared_ptr<Task::Result>(new ErrorResult(error, details));
}
class Task::Result::Private
{
public:
Private() {}
};
Task::Result::Result() : d(new Private()) {}
Task::Result::~Result() {}
bool Task::Result::hasError() const
{
return error().code() != 0;
}
+Task::Result::ContentType Task::Result::viewableContentType() const
+{
+ return Task::Result::ContentType::None;
+}
+
static QString image(const char *img)
{
// ### escape?
return KIconLoader::global()->iconPath(QLatin1String(img), KIconLoader::Small);
}
QString Task::Result::makeOverview(const QString &msg)
{
return QLatin1String("<b>") + msg + QLatin1String("</b>");
}
QString Task::Result::iconPath(VisualCode code)
{
switch (code) {
case Danger:
return image("dialog-error");
case AllGood:
return image("dialog-ok");
case Warning:
return image("dialog-warning");
case NeutralError:
case NeutralSuccess:
default:
return QString();
}
}
QString Task::Result::icon() const
{
return iconPath(code());
}
#include "moc_task_p.cpp"
diff --git a/src/crypto/task.h b/src/crypto/task.h
index 91aa30457..45b7dd4c2 100644
--- a/src/crypto/task.h
+++ b/src/crypto/task.h
@@ -1,130 +1,138 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/task.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QObject>
#include <QString>
#include <utils/pimpl_ptr.h>
#include <gpgme++/global.h>
#include <memory>
#include <QPointer>
namespace Kleo
{
class AuditLogEntry;
}
namespace Kleo
{
namespace Crypto
{
class Task : public QObject
{
Q_OBJECT
public:
explicit Task(QObject *parent = nullptr);
~Task() override;
class Result;
void setAsciiArmor(bool armor);
bool asciiArmor() const;
virtual GpgME::Protocol protocol() const = 0;
void start();
virtual QString label() const = 0;
virtual QString tag() const;
int currentProgress() const;
int totalProgress() const;
int id() const;
static std::shared_ptr<Task> makeErrorTask(const GpgME::Error &error, const QString &details, const QString &label);
public Q_SLOTS:
virtual void cancel() = 0;
Q_SIGNALS:
void progress(int processed, int total, QPrivateSignal);
void result(const std::shared_ptr<const Kleo::Crypto::Task::Result> &, QPrivateSignal);
void started(QPrivateSignal);
protected:
std::shared_ptr<Result> makeErrorResult(const GpgME::Error &error, const QString &details);
void emitResult(const std::shared_ptr<const Task::Result> &result);
protected Q_SLOTS:
void setProgress(int processed, int total);
private Q_SLOTS:
void emitError(const GpgME::Error &error, const QString &details);
private:
virtual void doStart() = 0;
virtual unsigned long long inputSize() const = 0;
private:
class Private;
kdtools::pimpl_ptr<Private> d;
};
class Task::Result
{
const QString m_nonce;
public:
+ class Content;
+
Result();
virtual ~Result();
const QString &nonce() const
{
return m_nonce;
}
bool hasError() const;
enum VisualCode {
AllGood,
Warning,
Danger,
NeutralSuccess,
NeutralError
};
+ enum class ContentType {
+ None,
+ Mime,
+ Mbox,
+ };
+
virtual QString icon() const;
virtual QString overview() const = 0;
virtual QString details() const = 0;
virtual GpgME::Error error() const = 0;
virtual QString errorString() const = 0;
virtual VisualCode code() const = 0;
virtual AuditLogEntry auditLog() const = 0;
virtual QPointer<Task> parentTask() const {return QPointer<Task>();}
+ virtual ContentType viewableContentType() const;
protected:
static QString iconPath(VisualCode code);
static QString makeOverview(const QString &msg);
private:
class Private;
kdtools::pimpl_ptr<Private> d;
};
-
}
}
diff --git a/src/data/kleopatra-mime.xml b/src/data/kleopatra-mime.xml
new file mode 100644
index 000000000..9400c11cb
--- /dev/null
+++ b/src/data/kleopatra-mime.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+SPDX-FileCopyrightText: 2003 Jonathan Blandford <jrb@redhat.com>
+SPDX-FileCopyrightText: 2023 Carl Schwan <carl.schwan@gnupg.com>
+SPDX-License-Identifier: GPL-2.0-or-later
+-->
+<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
+ <mime-type type="message/rfc822">
+ <comment>email message</comment>
+ <generic-icon name="text-x-generic"/>
+ <sub-class-of type="text/plain"/>
+ <magic>
+ <match type="string" value="#! rnews" offset="0"/>
+ <match type="string" value="Content-Type:" offset="0"/>
+ <match type="string" value="Forward to" offset="0"/>
+ <match type="string" value="From:" offset="0"/>
+ <match type="string" value="N#! rnews" offset="0"/>
+ <match type="string" value="Pipe to" offset="0"/>
+ <match type="string" value="Received:" offset="0"/>
+ <match type="string" value="Relay-Version:" offset="0"/>
+ <match type="string" value="Return-Path:" offset="0"/>
+ <match type="string" value="Return-path:" offset="0"/>
+ <match type="string" value="Subject: " offset="0"/>
+ <match type="string" value="To:" offset="0"/>
+ </magic>
+ <glob pattern="*.eml"/>
+ </mime-type>
+</mime-info>
diff --git a/src/dialogs/messageviewerdialog.cpp b/src/dialogs/messageviewerdialog.cpp
new file mode 100644
index 000000000..66f0bb80b
--- /dev/null
+++ b/src/dialogs/messageviewerdialog.cpp
@@ -0,0 +1,40 @@
+// This file is part of Kleopatra, the KDE keymanager
+// SPDX-FileCopyrightText: 2023 Carl Schwan <carl.schwan@gnupg.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "messageviewerdialog.h"
+
+#include <QDialogButtonBox>
+#include <QFile>
+#include <QPushButton>
+#include <QVBoxLayout>
+
+#include <KMime/Message>
+#include <MimeTreeParserWidgets/MessageViewer>
+
+MessageViewerDialog::MessageViewerDialog(const QString &fileName, QWidget *parent)
+ : QDialog(parent)
+{
+ auto layout = new QVBoxLayout(this);
+
+ QFile file(fileName);
+ file.open(QIODevice::ReadOnly);
+ const auto content = file.readAll();
+ file.close();
+
+ KMime::Message::Ptr message(new KMime::Message);
+ message->setContent(content);
+ message->parse();
+
+ auto messageViewer = new MimeTreeParser::Widgets::MessageViewer(this);
+ messageViewer->setMessage(message);
+ layout->addWidget(messageViewer);
+
+ auto buttonBox = new QDialogButtonBox(this);
+ auto closeButton = buttonBox->addButton(QDialogButtonBox::Close);
+ connect(closeButton, &QPushButton::pressed, this, &QDialog::accept);
+ connect(closeButton, &QPushButton::pressed, this, &QDialog::close);
+ layout->addWidget(buttonBox);
+}
+
+MessageViewerDialog::~MessageViewerDialog() = default;
diff --git a/src/dialogs/messageviewerdialog.h b/src/dialogs/messageviewerdialog.h
new file mode 100644
index 000000000..28f68313a
--- /dev/null
+++ b/src/dialogs/messageviewerdialog.h
@@ -0,0 +1,17 @@
+// This file is part of Kleopatra, the KDE keymanager
+// SPDX-FileCopyrightText: 2023 Carl Schwan <carl.schwan@gnupg.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <QDialog>
+#include <QDialog>
+
+class MessageViewerDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ MessageViewerDialog(const QString &fileName, QWidget *parent = nullptr);
+ ~MessageViewerDialog() override;
+};

File Metadata

Mime Type
text/x-diff
Expires
Sat, May 10, 8:27 AM (1 d, 11 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
a3/31/954444c70a33b2c48dfa4872f565

Event Timeline