diff --git a/CMakeLists.txt b/CMakeLists.txt index d7d1cfc94..90d313a32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,201 +1,202 @@ cmake_minimum_required(VERSION 3.16 FATAL_ERROR) # The RELEASE_SERVICE_VERSION is used by Gpg4win to add the Gpg4win version set(RELEASE_SERVICE_VERSION_MAJOR "21") set(RELEASE_SERVICE_VERSION_MINOR "11") set(RELEASE_SERVICE_VERSION_PATCH "40") set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_PATCH}") if(RELEASE_SERVICE_VERSION_PATCH LESS 10) set(KDE_APPLICATIONS_COMPACT_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}${RELEASE_SERVICE_VERSION_MINOR}0${RELEASE_SERVICE_VERSION_PATCH}") else() set(KDE_APPLICATIONS_COMPACT_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}${RELEASE_SERVICE_VERSION_MINOR}${RELEASE_SERVICE_VERSION_PATCH}") endif() set(KLEOPATRA_VERSION_MAJOR "3") set(KLEOPATRA_VERSION_MINOR "1") set(KLEOPATRA_VERSION_PATCH "16") set(kleopatra_version "${KLEOPATRA_VERSION_MAJOR}.${KLEOPATRA_VERSION_MINOR}.${KLEOPATRA_VERSION_PATCH}.${KDE_APPLICATIONS_COMPACT_VERSION}") # The following is for Windows set(kleopatra_version_win "${KLEOPATRA_VERSION_MAJOR}.${KLEOPATRA_VERSION_MINOR}.${KLEOPATRA_VERSION_PATCH}") set(kleopatra_fileversion_win "${KLEOPATRA_VERSION_MAJOR},${KLEOPATRA_VERSION_MINOR},${KLEOPATRA_VERSION_PATCH},0") project(kleopatra VERSION ${kleopatra_version}) option(DISABLE_KWATCHGNUPG "Don't build the kwatchgnupg tool [default=OFF]" OFF) # Standalone build. Find / include everything necessary. set(KF5_MIN_VERSION "5.85.0") set(KMIME_VERSION "5.18.40") set(LIBKLEO_VERSION "5.18.40") set(QT_REQUIRED_VERSION "5.15.2") set(GPGME_REQUIRED_VERSION "1.13.1") set(BOOST_REQUIRED_VERSION "1.58") if (WIN32) set(KF5_WANT_VERSION "5.70.0") set(KMIME_WANT_VERSION "5.12.0") else () set(KF5_WANT_VERSION ${KF5_MIN_VERSION}) set(KMIME_WANT_VERSION ${KMIME_VERSION}) endif () find_package(ECM ${KF5_WANT_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH}) include(ECMInstallIcons) include(ECMSetupVersion) include(ECMAddTests) include(GenerateExportHeader) include(ECMGenerateHeaders) include(FeatureSummary) include(CheckFunctionExists) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMAddAppIcon) include(ECMQtDeclareLoggingCategory) # Find KF5 packages find_package(KF5WidgetsAddons ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5ConfigWidgets ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5CoreAddons ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5Codecs ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5Config ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5I18n ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5IconThemes ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5ItemModels ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5XmlGui ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5WindowSystem ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5DocTools ${KF5_WANT_VERSION} CONFIG) find_package(KF5Crash ${KF5_WANT_VERSION} REQUIRED) set_package_properties(KF5DocTools PROPERTIES DESCRIPTION "Documentation tools" PURPOSE "Required to generate Kleopatra documentation." TYPE OPTIONAL) # Optional packages if (WIN32) # Only a replacement available for Windows so this # is required on other platforms. find_package(KF5DBusAddons ${KF5_WANT_VERSION} CONFIG) set_package_properties(KF5DBusAddons PROPERTIES DESCRIPTION "Support library to work with DBus" PURPOSE "DBus session integration" URL "https://inqlude.org/libraries/kdbusaddons.html" TYPE OPTIONAL) else() find_package(KF5DBusAddons ${KF5_WANT_VERSION} CONFIG REQUIRED) set(_kleopatra_dbusaddons_libs KF5::DBusAddons) endif() set(HAVE_QDBUS ${Qt5DBus_FOUND}) find_package(Gpgmepp ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED) if (Gpgmepp_VERSION VERSION_GREATER_EQUAL "1.16.0") set(GPGMEPP_SUPPORTS_TRUST_SIGNATURES 1) endif() find_package(QGpgme ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED) if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.16.0") set(QGPGME_SUPPORTS_TRUST_SIGNATURES 1) set(QGPGME_SUPPORTS_SIGNATURE_EXPIRATION 1) endif() if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.16.1") set(QGPGME_SUPPORTS_CHANGING_EXPIRATION_OF_COMPLETE_KEY 1) set(QGPGME_CRYPTOCONFIGENTRY_HAS_DEFAULT_VALUE 1) endif() # Kdepimlibs packages find_package(KF5Libkleo ${LIBKLEO_VERSION} CONFIG REQUIRED) find_package(KF5Mime ${KMIME_WANT_VERSION} CONFIG REQUIRED) -find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets Test Network PrintSupport) +find_package(Qt5Widgets ${QT_REQUIRED_VERSION} CONFIG REQUIRED Private) +find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Test Network PrintSupport) find_package(Assuan2 REQUIRED) find_package(Boost ${BOOST_REQUIRED_VERSION} MODULE REQUIRED) find_path(Boost_TOPOLOGICAL_SORT_DIR NAMES boost/graph/topological_sort.hpp PATHS ${Boost_INCLUDE_DIRS}) if(NOT Boost_TOPOLOGICAL_SORT_DIR) message(FATAL_ERROR "The Boost Topological_sort header was NOT found. Should be part of Boost graph module.") endif() set(kleopatra_release FALSE) if(NOT kleopatra_release) find_package(Git) if(GIT_FOUND) execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} RESULT_VARIABLE rc ERROR_QUIET) if(rc EQUAL 0) execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%h ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE Kleopatra_WC_REVISION) string(REGEX REPLACE "\n" "" Kleopatra_WC_REVISION "${Kleopatra_WC_REVISION}") execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%cI ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE Kleopatra_WC_LAST_CHANGED_DATE) string(REGEX REPLACE "^([0-9]+)-([0-9]+)-([0-9]+)T([0-9]+):([0-9]+):([0-9]+).*$" "\\1\\2\\3T\\4\\5\\6" Kleopatra_WC_LAST_CHANGED_DATE "${Kleopatra_WC_LAST_CHANGED_DATE}") set(kleopatra_version "${kleopatra_version}+git${Kleopatra_WC_LAST_CHANGED_DATE}~${Kleopatra_WC_REVISION}") endif() endif() endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version-kleopatra.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/version-kleopatra.h) include (ConfigureChecks.cmake) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-kleopatra.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kleopatra.h) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${Boost_INCLUDE_DIRS} ${ASSUAN2_INCLUDES} ) add_definitions(-D_ASSUAN_ONLY_GPG_ERRORS) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050e00) add_definitions(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x055600) if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-braces -Wno-parentheses -Wno-ignored-qualifiers") endif() add_definitions(-DQT_NO_EMIT) remove_definitions(-DQT_NO_FOREACH) kde_enable_exceptions() option(USE_UNITY_CMAKE_SUPPORT "Use UNITY cmake support (speedup compile time)" OFF) set(COMPILE_WITH_UNITY_CMAKE_SUPPORT OFF) if (USE_UNITY_CMAKE_SUPPORT) set(COMPILE_WITH_UNITY_CMAKE_SUPPORT ON) endif() add_subdirectory(pics) add_subdirectory(src) if(BUILD_TESTING) add_subdirectory(tests) add_subdirectory(autotests) endif() ecm_qt_install_logging_categories( EXPORT KLEOPATRA FILE kleopatra.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR} ) ki18n_install(po) if(KF5DocTools_FOUND) kdoctools_install(po) add_subdirectory(doc) endif() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6040c32c5..995daa9e7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,387 +1,392 @@ add_subdirectory(icons) include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(SYSTEM ${Qt5Widgets_PRIVATE_INCLUDE_DIRS}) if (NOT DISABLE_KWATCHGNUPG) add_subdirectory(kwatchgnupg) endif() add_subdirectory(libkleopatraclient) add_subdirectory(conf) add_subdirectory(kconf_update) if(WIN32) set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_win.cpp) set(_kleopatra_extra_SRCS utils/gnupg-registry.c selftest/registrycheck.cpp utils/windowsprocessdevice.cpp utils/userinfo_win.cpp ) else() set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_unix.cpp) set(_kleopatra_extra_SRCS) endif() set(_kleopatra_uiserver_SRCS uiserver/sessiondata.cpp uiserver/uiserver.cpp ${_kleopatra_extra_uiserver_SRCS} uiserver/assuanserverconnection.cpp uiserver/echocommand.cpp uiserver/decryptverifycommandemailbase.cpp uiserver/decryptverifycommandfilesbase.cpp uiserver/signcommand.cpp uiserver/signencryptfilescommand.cpp uiserver/prepencryptcommand.cpp uiserver/prepsigncommand.cpp uiserver/encryptcommand.cpp uiserver/selectcertificatecommand.cpp uiserver/importfilescommand.cpp uiserver/createchecksumscommand.cpp uiserver/verifychecksumscommand.cpp selftest/uiservercheck.cpp ) if(ASSUAN2_FOUND) include_directories(${ASSUAN2_INCLUDES}) set(_kleopatra_uiserver_extra_libs ${ASSUAN2_LIBRARIES}) else() include_directories(${ASSUAN_INCLUDES}) if(WIN32) set(_kleopatra_uiserver_extra_libs ${ASSUAN_VANILLA_LIBRARIES}) else() set(_kleopatra_uiserver_extra_libs ${ASSUAN_PTHREAD_LIBRARIES}) endif() endif() if(HAVE_GPG_ERR_SOURCE_KLEO) add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO) add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO) else() add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1) add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1) endif() ki18n_wrap_ui(_kleopatra_uiserver_SRCS crypto/gui/signingcertificateselectionwidget.ui) if("${Gpgmepp_VERSION}" VERSION_GREATER_EQUAL "1.14.1") set(_kleopatra_deviceinfowatcher_files smartcard/deviceinfowatcher.cpp ) else() set(_kleopatra_deviceinfowatcher_files) endif() set(_kleopatra_SRCS utils/gui-helper.cpp utils/filedialog.cpp utils/kdpipeiodevice.cpp utils/headerview.cpp utils/scrollarea.cpp utils/dragqueen.cpp utils/multivalidator.cpp utils/systemtrayicon.cpp utils/hex.cpp utils/path-helper.cpp utils/input.cpp utils/output.cpp utils/validation.cpp utils/wsastarter.cpp utils/iodevicelogger.cpp utils/log.cpp utils/action_data.cpp utils/types.cpp utils/archivedefinition.cpp utils/auditlog.cpp utils/clipboardmenu.cpp utils/kuniqueservice.cpp utils/tags.cpp utils/writecertassuantransaction.cpp utils/keyparameters.cpp utils/userinfo.cpp selftest/selftest.cpp selftest/enginecheck.cpp selftest/gpgconfcheck.cpp selftest/gpgagentcheck.cpp selftest/libkleopatrarccheck.cpp ${_kleopatra_extra_SRCS} view/keylistcontroller.cpp view/keytreeview.cpp view/searchbar.cpp view/smartcardwidget.cpp view/openpgpkeycardwidget.cpp view/padwidget.cpp view/pgpcardwidget.cpp view/pivcardwidget.cpp view/p15cardwidget.cpp view/netkeywidget.cpp view/nullpinwidget.cpp view/tabwidget.cpp view/keycacheoverlay.cpp + view/urllabel.cpp view/waitwidget.cpp view/welcomewidget.cpp dialogs/certificateselectiondialog.cpp dialogs/certifywidget.cpp dialogs/expirydialog.cpp dialogs/lookupcertificatesdialog.cpp dialogs/ownertrustdialog.cpp dialogs/selftestdialog.cpp dialogs/certifycertificatedialog.cpp dialogs/revokecertificationwidget.cpp dialogs/revokecertificationdialog.cpp dialogs/adduseriddialog.cpp dialogs/addemaildialog.cpp dialogs/deletecertificatesdialog.cpp dialogs/setinitialpindialog.cpp dialogs/certificatedetailsdialog.cpp dialogs/certificatedetailswidget.cpp dialogs/trustchainwidget.cpp dialogs/weboftrustwidget.cpp dialogs/weboftrustdialog.cpp dialogs/exportdialog.cpp dialogs/subkeyswidget.cpp dialogs/gencardkeydialog.cpp dialogs/updatenotification.cpp dialogs/pivcardapplicationadministrationkeyinputdialog.cpp dialogs/certificatedetailsinputwidget.cpp dialogs/createcsrforcardkeydialog.cpp dialogs/groupdetailsdialog.cpp dialogs/editgroupdialog.cpp crypto/controller.cpp crypto/certificateresolver.cpp crypto/sender.cpp crypto/recipient.cpp crypto/task.cpp crypto/taskcollection.cpp crypto/decryptverifytask.cpp crypto/decryptverifyemailcontroller.cpp crypto/decryptverifyfilescontroller.cpp crypto/autodecryptverifyfilescontroller.cpp crypto/encryptemailtask.cpp crypto/encryptemailcontroller.cpp crypto/newsignencryptemailcontroller.cpp crypto/signencrypttask.cpp crypto/signencryptfilescontroller.cpp crypto/signemailtask.cpp crypto/signemailcontroller.cpp crypto/createchecksumscontroller.cpp crypto/verifychecksumscontroller.cpp crypto/gui/wizard.cpp crypto/gui/wizardpage.cpp crypto/gui/certificateselectionline.cpp crypto/gui/certificatelineedit.cpp crypto/gui/signingcertificateselectionwidget.cpp crypto/gui/signingcertificateselectiondialog.cpp crypto/gui/resultitemwidget.cpp crypto/gui/resultlistwidget.cpp crypto/gui/resultpage.cpp crypto/gui/newresultpage.cpp crypto/gui/signencryptfileswizard.cpp crypto/gui/signencryptemailconflictdialog.cpp crypto/gui/decryptverifyoperationwidget.cpp crypto/gui/decryptverifyfileswizard.cpp crypto/gui/decryptverifyfilesdialog.cpp crypto/gui/objectspage.cpp crypto/gui/resolverecipientspage.cpp crypto/gui/signerresolvepage.cpp crypto/gui/encryptemailwizard.cpp crypto/gui/signemailwizard.cpp crypto/gui/signencryptwidget.cpp crypto/gui/signencryptwizard.cpp crypto/gui/unknownrecipientwidget.cpp crypto/gui/verifychecksumsdialog.cpp commands/command.cpp commands/gnupgprocesscommand.cpp commands/detailscommand.cpp commands/exportcertificatecommand.cpp commands/importcertificatescommand.cpp commands/importcertificatefromfilecommand.cpp commands/importcertificatefromclipboardcommand.cpp commands/importcertificatefromdatacommand.cpp commands/lookupcertificatescommand.cpp commands/reloadkeyscommand.cpp commands/refreshx509certscommand.cpp commands/refreshopenpgpcertscommand.cpp commands/deletecertificatescommand.cpp commands/decryptverifyfilescommand.cpp commands/signencryptfilescommand.cpp commands/signencryptfoldercommand.cpp commands/encryptclipboardcommand.cpp commands/signclipboardcommand.cpp commands/decryptverifyclipboardcommand.cpp commands/clearcrlcachecommand.cpp commands/dumpcrlcachecommand.cpp commands/dumpcertificatecommand.cpp commands/importcrlcommand.cpp commands/changeexpirycommand.cpp commands/changeownertrustcommand.cpp commands/changeroottrustcommand.cpp commands/changepassphrasecommand.cpp commands/certifycertificatecommand.cpp commands/revokecertificationcommand.cpp commands/selftestcommand.cpp commands/exportsecretkeycommand.cpp commands/exportopenpgpcertstoservercommand.cpp commands/adduseridcommand.cpp commands/newcertificatecommand.cpp commands/setinitialpincommand.cpp commands/learncardkeyscommand.cpp commands/checksumcreatefilescommand.cpp commands/checksumverifyfilescommand.cpp commands/exportpaperkeycommand.cpp commands/importpaperkeycommand.cpp commands/genrevokecommand.cpp commands/keytocardcommand.cpp commands/cardcommand.cpp commands/pivgeneratecardkeycommand.cpp commands/changepincommand.cpp commands/authenticatepivcardapplicationcommand.cpp commands/setpivcardapplicationadministrationkeycommand.cpp commands/certificatetopivcardcommand.cpp commands/importcertificatefrompivcardcommand.cpp commands/createopenpgpkeyfromcardkeyscommand.cpp commands/createcsrforcardkeycommand.cpp ${_kleopatra_uiserver_files} conf/configuredialog.cpp conf/groupsconfigdialog.cpp conf/groupsconfigpage.cpp conf/groupsconfigwidget.cpp newcertificatewizard/listwidget.cpp newcertificatewizard/newcertificatewizard.cpp smartcard/readerstatus.cpp smartcard/card.cpp smartcard/openpgpcard.cpp smartcard/netkeycard.cpp smartcard/pivcard.cpp smartcard/p15card.cpp smartcard/keypairinfo.cpp smartcard/utils.cpp ${_kleopatra_deviceinfowatcher_files} + accessibility/accessiblerichtextlabel.cpp + accessibility/accessiblewidgetfactory.cpp + aboutdata.cpp systrayicon.cpp kleopatraapplication.cpp mainwindow.cpp main.cpp kleopatra.qrc ) if(WIN32) configure_file (versioninfo.rc.in versioninfo.rc) set(_kleopatra_SRCS ${CMAKE_CURRENT_BINARY_DIR}/versioninfo.rc ${_kleopatra_SRCS}) endif() set (_kleopatra_SRCS conf/kleopageconfigdialog.cpp ${_kleopatra_SRCS}) ecm_qt_declare_logging_category(_kleopatra_SRCS HEADER kleopatra_debug.h IDENTIFIER KLEOPATRA_LOG CATEGORY_NAME org.kde.pim.kleopatra DESCRIPTION "kleopatra (kleopatra)" OLD_CATEGORY_NAMES log_kleopatra EXPORT KLEOPATRA ) if(KLEO_MODEL_TEST) add_definitions(-DKLEO_MODEL_TEST) set(_kleopatra_SRCS ${_kleopatra_SRCS} models/modeltest.cpp) endif() ki18n_wrap_ui(_kleopatra_SRCS dialogs/lookupcertificatesdialog.ui dialogs/ownertrustdialog.ui dialogs/selectchecklevelwidget.ui dialogs/selftestdialog.ui dialogs/adduseriddialog.ui dialogs/setinitialpindialog.ui dialogs/trustchainwidget.ui dialogs/subkeyswidget.ui newcertificatewizard/listwidget.ui newcertificatewizard/chooseprotocolpage.ui newcertificatewizard/enterdetailspage.ui newcertificatewizard/keycreationpage.ui newcertificatewizard/resultpage.ui newcertificatewizard/advancedsettingsdialog.ui ) kconfig_add_kcfg_files(_kleopatra_SRCS kcfg/tooltippreferences.kcfgc kcfg/emailoperationspreferences.kcfgc kcfg/fileoperationspreferences.kcfgc kcfg/smimevalidationpreferences.kcfgc kcfg/tagspreferences.kcfgc kcfg/settings.kcfgc ) file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/icons/*-apps-kleopatra.png") ecm_add_app_icon(_kleopatra_SRCS ICONS ${ICONS_SRCS}) add_executable(kleopatra_bin ${_kleopatra_SRCS} ${_kleopatra_uiserver_SRCS}) # For the ConfigureDialog & KCMs target_link_libraries(kleopatra_bin kcm_kleopatra_static) #if (COMPILE_WITH_UNITY_CMAKE_SUPPORT) # set_target_properties(kleopatra_bin PROPERTIES UNITY_BUILD ON) #endif() set_target_properties(kleopatra_bin PROPERTIES OUTPUT_NAME kleopatra) if (WIN32) set(_kleopatra_platform_libs "secur32") endif () target_link_libraries(kleopatra_bin Gpgmepp QGpgme ${_kleopatra_extra_libs} KF5::Libkleo KF5::Mime KF5::I18n KF5::XmlGui KF5::IconThemes KF5::WindowSystem KF5::CoreAddons KF5::ItemModels KF5::Crash Qt::Network Qt::PrintSupport # Printing secret keys ${_kleopatra_uiserver_extra_libs} ${_kleopatra_dbusaddons_libs} kleopatraclientcore ${_kleopatra_platform_libs} ) install(TARGETS kleopatra_bin ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install( PROGRAMS data/org.kde.kleopatra.desktop data/kleopatra_import.desktop DESTINATION ${KDE_INSTALL_APPDIR} ) install(FILES data/org.kde.kleopatra.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) install( FILES data/kleopatra_signencryptfiles.desktop data/kleopatra_signencryptfolders.desktop data/kleopatra_decryptverifyfiles.desktop data/kleopatra_decryptverifyfolders.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}/ServiceMenus ) diff --git a/src/accessibility/accessiblerichtextlabel.cpp b/src/accessibility/accessiblerichtextlabel.cpp new file mode 100644 index 000000000..bc84374c8 --- /dev/null +++ b/src/accessibility/accessiblerichtextlabel.cpp @@ -0,0 +1,165 @@ +/* + accessibility/accessiblerichtextlabel.cpp + + This file is part of Kleopatra, the KDE keymanager + SPDX-FileCopyrightText: 2021 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include + +#include "accessiblerichtextlabel_p.h" + +#include +#include + +using namespace Kleo; + +AccessibleRichTextLabel::AccessibleRichTextLabel(QWidget *w) + : QAccessibleWidget{w, QAccessible::StaticText} +{ + Q_ASSERT(qobject_cast(w)); +} + +void *AccessibleRichTextLabel::interface_cast(QAccessible::InterfaceType t) +{ + if (t == QAccessible::TextInterface) + return static_cast(this); + return QAccessibleWidget::interface_cast(t); +} + +QAccessible::State AccessibleRichTextLabel::state() const +{ + QAccessible::State state = QAccessibleWidget::state(); + state.readOnly = true; + state.selectableText = true; + return state; +} + +QString AccessibleRichTextLabel::text(QAccessible::Text t) const +{ + QString str; + switch (t) { + case QAccessible::Name: + str = widget()->accessibleName(); + if (str.isEmpty()) { + str = displayText(); + } + break; + default: + break; + } + if (str.isEmpty()) + str = QAccessibleWidget::text(t); + return str; +} + +void AccessibleRichTextLabel::selection(int selectionIndex, int *startOffset, int *endOffset) const +{ + *startOffset = *endOffset = 0; + if (selectionIndex != 0) + return; + + *startOffset = label()->selectionStart(); + *endOffset = *startOffset + label()->selectedText().size(); +} + +int AccessibleRichTextLabel::selectionCount() const +{ + return label()->hasSelectedText() ? 1 : 0; +} + +void AccessibleRichTextLabel::addSelection(int startOffset, int endOffset) +{ + setSelection(0, startOffset, endOffset); +} + +void AccessibleRichTextLabel::removeSelection(int selectionIndex) +{ + if (selectionIndex != 0) + return; + + label()->setSelection(-1, -1); +} + +void AccessibleRichTextLabel::setSelection(int selectionIndex, int startOffset, int endOffset) +{ + if (selectionIndex != 0) + return; + + label()->setSelection(startOffset, endOffset - startOffset); +} + +int AccessibleRichTextLabel::cursorPosition() const +{ + return label()->hasSelectedText() ? label()->selectionStart() + label()->selectedText().size() : 0; +} + +void AccessibleRichTextLabel::setCursorPosition(int position) +{ + Q_UNUSED(position) +} + +QString AccessibleRichTextLabel::text(int startOffset, int endOffset) const +{ + if (startOffset > endOffset) + return {}; + + // most likely the client is asking for the selected text, so return it + // instead of a slice of displayText() if the offsets match the selection + if (startOffset == label()->selectionStart() + && endOffset == startOffset + label()->selectedText().size()) { + return label()->selectedText(); + } + return displayText().mid(startOffset, endOffset - startOffset); +} + +int AccessibleRichTextLabel::characterCount() const +{ + return displayText().size(); +} + +QRect AccessibleRichTextLabel::characterRect(int offset) const +{ + Q_UNUSED(offset) + return {}; +} + +int AccessibleRichTextLabel::offsetAtPoint(const QPoint &point) const +{ + Q_UNUSED(point) + return -1; +} + +QString AccessibleRichTextLabel::attributes(int offset, int *startOffset, int *endOffset) const +{ + *startOffset = *endOffset = offset; + return {}; +} + +void AccessibleRichTextLabel::scrollToSubstring(int startIndex, int endIndex) +{ + Q_UNUSED(startIndex) + Q_UNUSED(endIndex) +} + +QLabel *AccessibleRichTextLabel::label() const +{ + return qobject_cast(object()); +} + +QString AccessibleRichTextLabel::displayText() const +{ + // calculate an approximation of the displayed text without using private + // information of QLabel + QString str = label()->text(); + if (label()->textFormat() == Qt::RichText + || (label()->textFormat() == Qt::AutoText && Qt::mightBeRichText(str))) { + QTextDocument doc; + doc.setHtml(str); + str = doc.toPlainText(); + } + return str; +} diff --git a/src/accessibility/accessiblerichtextlabel_p.h b/src/accessibility/accessiblerichtextlabel_p.h new file mode 100644 index 000000000..2ab7d64bc --- /dev/null +++ b/src/accessibility/accessiblerichtextlabel_p.h @@ -0,0 +1,57 @@ +/* + accessibility/accessiblerichtextlabel_p.h + + This file is part of Kleopatra, the KDE keymanager + SPDX-FileCopyrightText: 2021 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include + +class QLabel; + +namespace Kleo +{ + +class AccessibleRichTextLabel : public QAccessibleWidget, public QAccessibleTextInterface +{ +public: + explicit AccessibleRichTextLabel(QWidget *o); + + void *interface_cast(QAccessible::InterfaceType t) override; + QAccessible::State state() const override; + QString text(QAccessible::Text t) const override; + + // QAccessibleTextInterface + // selection + void selection(int selectionIndex, int *startOffset, int *endOffset) const override; + int selectionCount() const override; + void addSelection(int startOffset, int endOffset) override; + void removeSelection(int selectionIndex) override; + void setSelection(int selectionIndex, int startOffset, int endOffset) override; + + // cursor + int cursorPosition() const override; + void setCursorPosition(int position) override; + + // text + QString text(int startOffset, int endOffset) const override; + int characterCount() const override; + + // character <-> geometry + QRect characterRect(int offset) const override; + int offsetAtPoint(const QPoint &point) const override; + + void scrollToSubstring(int startIndex, int endIndex) override; + QString attributes(int offset, int *startOffset, int *endOffset) const override; + +private: + QLabel *label() const; + QString displayText() const; +}; + +} diff --git a/src/accessibility/accessiblewidgetfactory.cpp b/src/accessibility/accessiblewidgetfactory.cpp new file mode 100644 index 000000000..5e9d27152 --- /dev/null +++ b/src/accessibility/accessiblewidgetfactory.cpp @@ -0,0 +1,38 @@ +/* + accessibility/accessiblewidgetfactory.cpp + + This file is part of Kleopatra, the KDE keymanager + SPDX-FileCopyrightText: 2021 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "accessiblewidgetfactory.h" + +#include "accessiblerichtextlabel_p.h" +#include "view/urllabel.h" + +#include "private/qwidget_p.h" + +QAccessibleInterface *Kleo::accessibleWidgetFactory(const QString &classname, QObject *object) +{ + QAccessibleInterface *iface = nullptr; + if (!object || !object->isWidgetType()) + return iface; + + QWidget *widget = static_cast(object); + // QWidget emits destroyed() from its destructor instead of letting the QObject + // destructor do it, which means the QWidget is unregistered from the accessibility + // cache. But QWidget destruction also emits enter and leave events, which may end + // up here, so we have to ensure that we don't fill the cache with an entry of + // a widget that is going away. + if (QWidgetPrivate::get(widget)->data.in_destructor) + return iface; + + if (classname == QString::fromLatin1(Kleo::UrlLabel::staticMetaObject.className())) { + iface = new AccessibleRichTextLabel{widget}; + } + + return iface; +} diff --git a/src/accessibility/accessiblewidgetfactory.h b/src/accessibility/accessiblewidgetfactory.h new file mode 100644 index 000000000..6ae8bfe66 --- /dev/null +++ b/src/accessibility/accessiblewidgetfactory.h @@ -0,0 +1,20 @@ +/* + accessibility/accessiblewidgetfactory.h + + This file is part of Kleopatra, the KDE keymanager + SPDX-FileCopyrightText: 2021 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include + +namespace Kleo +{ + +QAccessibleInterface *accessibleWidgetFactory(const QString &classname, QObject *object); + +} diff --git a/src/crypto/gui/resultitemwidget.cpp b/src/crypto/gui/resultitemwidget.cpp index 8aa8c7189..bc70ce568 100644 --- a/src/crypto/gui/resultitemwidget.cpp +++ b/src/crypto/gui/resultitemwidget.cpp @@ -1,356 +1,351 @@ /* -*- 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 #include "resultitemwidget.h" #include "utils/auditlog.h" #include "commands/command.h" #include "commands/importcertificatefromfilecommand.h" #include "commands/lookupcertificatescommand.h" #include "crypto/decryptverifytask.h" +#include "view/urllabel.h" #include #include #include #include #include #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include 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: return KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::PositiveText).color(); 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 &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 m_result; QLabel *m_detailsLabel = nullptr; - QLabel *m_actionsLabel = nullptr; + UrlLabel *m_auditLogLabel = nullptr; QPushButton *m_closeButton = 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(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(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(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() { - if (!m_actionsLabel || !m_detailsLabel) { - return; - } - - const auto parentTask = m_result->parentTask(); - QString auditLogLink; - if (m_result->hasError()) { - auditLogLink = m_result->auditLog().formatLink(auditlog_url_template(), i18n("Diagnostics")); - } else { - auditLogLink = m_result->auditLog().formatLink(auditlog_url_template()); - } - m_actionsLabel->setText(auditLogLink); + 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 &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 QString styleSheet = 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 QLabel; overview->setWordWrap(true); overview->setTextFormat(Qt::RichText); overview->setText(d->m_result->overview()); overview->setFocusPolicy(Qt::StrongFocus); overview->setStyleSheet(styleSheet); connect(overview, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString))); vlay->addWidget(overview); layout->addLayout(vlay); const QString details = d->m_result->details(); 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_actionsLabel = new QLabel; - connect(d->m_actionsLabel, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString))); - actionLayout->addWidget(d->m_actionsLabel); - d->m_actionsLabel->setFocusPolicy(Qt::StrongFocus); - d->m_actionsLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); - d->m_actionsLabel->setStyleSheet(styleSheet); + d->m_auditLogLabel = new UrlLabel; + connect(d->m_auditLogLabel, &UrlLabel::linkActivated, + this, [this](const auto &link) { d->slotLinkActivated(link); }); + actionLayout->addWidget(d->m_auditLogLabel); + d->m_auditLogLabel->setStyleSheet(styleSheet); d->m_detailsLabel = new QLabel; d->m_detailsLabel->setWordWrap(true); d->m_detailsLabel->setTextFormat(Qt::RichText); d->m_detailsLabel->setText(details); d->m_detailsLabel->setFocusPolicy(Qt::StrongFocus); d->m_detailsLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); d->m_detailsLabel->setStyleSheet(styleSheet); connect(d->m_detailsLabel, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString))); vlay->addWidget(d->m_detailsLabel); 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); } 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() { MessageBox::auditLog(parentWidget(), d->m_result->auditLog().text()); } #include "moc_resultitemwidget.cpp" diff --git a/src/main.cpp b/src/main.cpp index 6ae5d0e78..ea59eba4c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,269 +1,272 @@ /* main.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2001, 2002, 2004, 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 #include "aboutdata.h" #include "kleopatraapplication.h" #include "mainwindow.h" +#include "accessibility/accessiblewidgetfactory.h" + #include #if KCOREADDONS_VERSION < QT_VERSION_CHECK(6, 0, 0) #include #endif #include #include #include #include #include "utils/kuniqueservice.h" #include "utils/userinfo.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" #include "kleopatra_options.h" #include #include #include +#include #include // for Qt::escape #include #include #include #include #include #include #include #include #include #include #include static bool selfCheck() { Kleo::Commands::SelfTestCommand cmd(nullptr); cmd.setAutoDelete(false); cmd.setAutomaticMode(true); QEventLoop loop; QObject::connect(&cmd, &Kleo::Commands::SelfTestCommand::finished, &loop, &QEventLoop::quit); QTimer::singleShot(0, &cmd, &Kleo::Command::start); // start() may Q_EMIT finished()... loop.exec(); if (cmd.isCanceled()) { return false; } else { return true; } } static void fillKeyCache(Kleo::UiServer *server) { auto cmd = new Kleo::ReloadKeysCommand(nullptr); QObject::connect(cmd, SIGNAL(finished()), server, SLOT(enableCryptoCommands())); cmd->start(); } int main(int argc, char **argv) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); KleopatraApplication app(argc, argv); KCrash::initialize(); - + QAccessible::installFactory(Kleo::accessibleWidgetFactory); QElapsedTimer timer; timer.start(); KLocalizedString::setApplicationDomain("kleopatra"); if (Kleo::userIsElevated()) { /* This is a safeguard against bugreports that something fails because * of permission problems on windows. Some users still have the Windows * Vista behavior of running things as Administrator. This can break * GnuPG in horrible ways for example if a stale lockfile is left that * can't be removed without another elevation. * * Note: This is not the same as running as root on Linux. Elevated means * that you are temporarily running with the "normal" user environment but * with elevated permissions. * */ if (KMessageBox::warningContinueCancel(nullptr, xi18nc("@info", "Kleopatra cannot be run as adminstrator without " "breaking file permissions in the GnuPG data folder." "To manage keys for other users please manage them as a normal user and " "copy the AppData\\Roaming\\gnupg directory with proper permissions.") + xi18n("Are you sure that you want to continue?"), i18nc("@title", "Running as Administrator")) != KMessageBox::Continue) { return EXIT_FAILURE; } qCWarning(KLEOPATRA_LOG) << "User is running with administrative permissions."; } KUniqueService service; QObject::connect(&service, &KUniqueService::activateRequested, &app, &KleopatraApplication::slotActivateRequested); QObject::connect(&app, &KleopatraApplication::setExitValue, &service, [&service](int i) { service.setExitValue(i); }); // Delay init after KUniqueservice call as this might already // have terminated us and so we can avoid overhead (e.g. keycache // setup / systray icon). qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: Service created"; app.init(); qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: Application initialized"; AboutData aboutData; KAboutData::setApplicationData(aboutData); QCommandLineParser parser; aboutData.setupCommandLine(&parser); kleopatra_options(&parser); parser.process(QApplication::arguments()); aboutData.processCommandLine(&parser); #if KCOREADDONS_VERSION < QT_VERSION_CHECK(6, 0, 0) Kdelibs4ConfigMigrator migrate(QStringLiteral("kleopatra")); migrate.setConfigFiles(QStringList() << QStringLiteral("kleopatrarc") << QStringLiteral("libkleopatrarc")); migrate.setUiFiles(QStringList() << QStringLiteral("kleopatra.rc")); migrate.migrate(); #endif qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: Application created"; // Initialize GpgME const GpgME::Error gpgmeInitError = GpgME::initializeLibrary(0); { const unsigned int threads = QThreadPool::globalInstance()->maxThreadCount(); QThreadPool::globalInstance()->setMaxThreadCount(qMax(2U, threads)); } if (gpgmeInitError) { KMessageBox::sorry(nullptr, xi18nc("@info", "The version of the GpgME library you are running against " "is older than the one that the GpgME++ library was built against." "Kleopatra will not function in this setting." "Please ask your administrator for help in resolving this issue."), i18nc("@title", "GpgME Too Old")); return EXIT_FAILURE; } qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: GPGME Initialized"; Kleo::ChecksumDefinition::setInstallPath(Kleo::gpg4winInstallPath()); Kleo::ArchiveDefinition::setInstallPath(Kleo::gnupgInstallPath()); int rc; Kleo::UiServer server(parser.value(QStringLiteral("uiserver-socket"))); try { qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: UiServer created"; QObject::connect(&server, &Kleo::UiServer::startKeyManagerRequested, &app, &KleopatraApplication::openOrRaiseMainWindow); QObject::connect(&server, &Kleo::UiServer::startConfigDialogRequested, &app, &KleopatraApplication::openOrRaiseConfigDialog); #define REGISTER( Command ) server.registerCommandFactory( std::shared_ptr( new Kleo::GenericAssuanCommandFactory ) ) REGISTER(CreateChecksumsCommand); REGISTER(DecryptCommand); REGISTER(DecryptFilesCommand); REGISTER(DecryptVerifyFilesCommand); REGISTER(EchoCommand); REGISTER(EncryptCommand); REGISTER(EncryptFilesCommand); REGISTER(EncryptSignFilesCommand); REGISTER(ImportFilesCommand); REGISTER(PrepEncryptCommand); REGISTER(PrepSignCommand); REGISTER(SelectCertificateCommand); REGISTER(SignCommand); REGISTER(SignEncryptFilesCommand); REGISTER(SignFilesCommand); REGISTER(VerifyChecksumsCommand); REGISTER(VerifyCommand); REGISTER(VerifyFilesCommand); #undef REGISTER server.start(); qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: UiServer started"; } catch (const std::exception &e) { qCDebug(KLEOPATRA_LOG) << "Failed to start UI Server: " << e.what(); #ifdef Q_OS_WIN // Once there actually is a plugin for other systems then Windows this // error should probably be shown, too. But currently only Windows users need // to care. QMessageBox::information(nullptr, i18n("GPG UI Server Error"), i18n("The Kleopatra GPG UI Server Module could not be initialized.
" "The error given was: %1
" "You can use Kleopatra as a certificate manager, but cryptographic plugins that " "rely on a GPG UI Server being present might not work correctly, or at all.
", QString::fromUtf8(e.what()).toHtmlEscaped())); #endif } const bool daemon = parser.isSet(QStringLiteral("daemon")); if (!daemon && app.isSessionRestored()) { app.restoreMainWindow(); } if (!selfCheck()) { return EXIT_FAILURE; } qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: SelfCheck completed"; fillKeyCache(&server); #ifndef QT_NO_SYSTEMTRAYICON app.startMonitoringSmartCard(); #endif app.setIgnoreNewInstance(false); if (!daemon) { const QString err = app.newInstance(parser); if (!err.isEmpty()) { std::cerr << i18n("Invalid arguments: %1", err).toLocal8Bit().constData() << "\n"; return EXIT_FAILURE; } qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: new instance created"; } rc = app.exec(); app.setIgnoreNewInstance(true); QObject::disconnect(&server, &Kleo::UiServer::startKeyManagerRequested, &app, &KleopatraApplication::openOrRaiseMainWindow); QObject::disconnect(&server, &Kleo::UiServer::startConfigDialogRequested, &app, &KleopatraApplication::openOrRaiseConfigDialog); server.stop(); server.waitForStopped(); return rc; } diff --git a/src/utils/auditlog.cpp b/src/utils/auditlog.cpp index a21ecbe19..744c41291 100644 --- a/src/utils/auditlog.cpp +++ b/src/utils/auditlog.cpp @@ -1,60 +1,57 @@ /* -*- mode: c++; c-basic-offset:4 -*- utils/auditlog.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 #include "auditlog.h" #include #include #include #include "kleopatra_debug.h" #include using namespace Kleo; AuditLog AuditLog::fromJob(const QGpgME::Job *job) { if (job) { return AuditLog(job->auditLogAsHtml(), job->auditLogError()); } else { return AuditLog(); } } -QString AuditLog::formatLink(const QUrl &urlTemplate, const QString &caption) const +QUrl AuditLog::asUrl(const QUrl &urlTemplate) const { // more or less the same as // kmail/objecttreeparser.cpp:makeShowAuditLogLink(), so any bug // fixed here equally applies there: if (const int code = m_error.code()) { if (code == GPG_ERR_NOT_IMPLEMENTED) { qCDebug(KLEOPATRA_LOG) << "not showing link (not implemented)"; } else if (code == GPG_ERR_NO_DATA) { qCDebug(KLEOPATRA_LOG) << "not showing link (not available)"; } else { qCDebug(KLEOPATRA_LOG) << "Error Retrieving Audit Log:" << QString::fromLocal8Bit(m_error.asString()); } - return QString(); + return {}; } - - if (!m_text.isEmpty()) { - QUrl url = urlTemplate; - QUrlQuery urlQuery(url); - urlQuery.addQueryItem(QStringLiteral("log"), m_text); - url.setQuery(urlQuery); - return QLatin1String("") + - (caption.isNull() ? i18nc("The Audit Log is a detailed error log from the gnupg backend", "Show Audit Log") : caption) + - QLatin1String(""); + if (m_text.isEmpty()) { + return {}; } - return QString(); + QUrl url = urlTemplate; + QUrlQuery urlQuery{url}; + urlQuery.addQueryItem(QStringLiteral("log"), m_text); + url.setQuery(urlQuery); + return url; } diff --git a/src/utils/auditlog.h b/src/utils/auditlog.h index 855da3540..1896c7cec 100644 --- a/src/utils/auditlog.h +++ b/src/utils/auditlog.h @@ -1,54 +1,54 @@ /* -*- mode: c++; c-basic-offset:4 -*- utils/auditlog.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 #include #include class QUrl; namespace QGpgME { class Job; } namespace Kleo { class AuditLog { public: AuditLog() : m_text(), m_error() {} explicit AuditLog(const GpgME::Error &error) : m_text(), m_error(error) {} AuditLog(const QString &text, const GpgME::Error &error) : m_text(text), m_error(error) {} static AuditLog fromJob(const QGpgME::Job *); GpgME::Error error() const { return m_error; } QString text() const { return m_text; } - QString formatLink(const QUrl &urlTemplate, const QString &caption = QString()) const; + QUrl asUrl(const QUrl &urlTemplate) const; private: QString m_text; GpgME::Error m_error; }; } diff --git a/src/view/urllabel.cpp b/src/view/urllabel.cpp new file mode 100644 index 000000000..877dd4819 --- /dev/null +++ b/src/view/urllabel.cpp @@ -0,0 +1,57 @@ +/* + view/urllabel.cpp + + This file is part of Kleopatra, the KDE keymanager + SPDX-FileCopyrightText: 2021 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include + +#include "urllabel.h" + +#include + +using namespace Kleo; + +UrlLabel::UrlLabel(QWidget *parent) + : QLabel{parent} +{ + setTextFormat(Qt::RichText); + setTextInteractionFlags(Qt::TextBrowserInteraction); +} + +void UrlLabel::setUrl(const QUrl &url, const QString &text) +{ + // we prepend a zero-width-space character to work around a bug in QLabel::focusNextPrevChild(false) + // which makes it impossible to leave the label with Shift+Tab if the text starts with a link + static const QString templateString{QLatin1String{"​%2"}}; + + if (url.isEmpty()) { + clear(); + return; + } + + setText(templateString.arg(url.url(QUrl::FullyEncoded), text.isEmpty() ? url.toDisplayString().toHtmlEscaped() : text.toHtmlEscaped())); +} + +void UrlLabel::focusInEvent(QFocusEvent *event) +{ + // immediately focus the URL when the label get focus + QLabel::focusInEvent(event); + if (!hasSelectedText()) { + focusNextPrevChild(true); + } +} + +bool UrlLabel::focusNextPrevChild(bool next) +{ + const bool result = QLabel::focusNextPrevChild(next); + if (hasFocus() && hasSelectedText()) { + QAccessibleTextSelectionEvent ev(this, selectionStart(), selectionStart() + selectedText().size()); + QAccessible::updateAccessibility(&ev); + } + return result; +} diff --git a/src/view/urllabel.h b/src/view/urllabel.h new file mode 100644 index 000000000..3ffeb4124 --- /dev/null +++ b/src/view/urllabel.h @@ -0,0 +1,34 @@ +/* + view/urllabel.h + + This file is part of Kleopatra, the KDE keymanager + SPDX-FileCopyrightText: 2021 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include + +namespace Kleo +{ + +class UrlLabel : public QLabel +{ + Q_OBJECT +public: + explicit UrlLabel(QWidget *parent = nullptr); + + void setUrl(const QUrl &url, const QString &text = {}); + +protected: + void focusInEvent(QFocusEvent *event) override; + bool focusNextPrevChild(bool next) override; + +private: + using QLabel::setText; +}; + +}