diff --git a/CMakeLists.txt b/CMakeLists.txt index 57aa5b462..f75239101 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,223 +1,223 @@ # SPDX-FileCopyrightText: none # SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.16 FATAL_ERROR) set(RELEASE_SERVICE_VERSION_MAJOR "22") set(RELEASE_SERVICE_VERSION_MINOR "03") 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 "20") set(kleopatra_version "${KLEOPATRA_VERSION_MAJOR}.${KLEOPATRA_VERSION_MINOR}.${KLEOPATRA_VERSION_MICRO}.${KDE_APPLICATIONS_COMPACT_VERSION}") # The following is for Windows set(kleopatra_version_win "${KLEOPATRA_VERSION_MAJOR}.${KLEOPATRA_VERSION_MINOR}.${KLEOPATRA_VERSION_MICRO}") set(kleopatra_fileversion_win "${KLEOPATRA_VERSION_MAJOR},${KLEOPATRA_VERSION_MINOR},${KLEOPATRA_VERSION_MICRO},0") project(kleopatra VERSION ${kleopatra_version}) option(DISABLE_KWATCHGNUPG "Don't build the kwatchgnupg tool [default=OFF]" OFF) # Standalone build. Find / include everything necessary. set(KF5_MIN_VERSION "5.90.0") set(KMIME_VERSION "5.19.40") -set(LIBKLEO_VERSION "5.19.48") +set(LIBKLEO_VERSION "5.19.49") set(QT_REQUIRED_VERSION "5.15.2") set(GPGME_REQUIRED_VERSION "1.15.0") 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 ${Qt${QT_MAJOR_VERSION}DBus_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) 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) endif() # Kdepimlibs packages find_package(KF5Libkleo ${LIBKLEO_VERSION} CONFIG REQUIRED) find_package(KF5Mime ${KMIME_WANT_VERSION} CONFIG REQUIRED) find_package(Qt${QT_MAJOR_VERSION} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets Test Network PrintSupport) find_package(Assuan2 REQUIRED) 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=0x055B00) if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-braces -Wno-parentheses -Wno-ignored-qualifiers") endif() add_definitions(-DQT_NO_EMIT) remove_definitions(-DQT_NO_FOREACH) # Disable the use of QStringBuilder for operator+ to prevent crashes when # returning the result of concatenating string temporaries in lambdas. We do # this for example in some std::transform expressions. # This is a known issue: https://bugreports.qt.io/browse/QTBUG-47066 # Alternatively, one would always have to remember to force the lambdas to # return a QString instead of QStringBuilder, but that's just too easy to # forget and, unfortunately, the compiler doesn't issue a warning if one forgets # this. So, it's just too dangerous. # One can still use QStringBuilder explicitly with the operator% if necessary. remove_definitions(-DQT_USE_FAST_OPERATOR_PLUS) remove_definitions(-DQT_USE_QSTRINGBUILDER) kde_enable_exceptions() option(USE_UNITY_CMAKE_SUPPORT "Use UNITY cmake support (speedup compile time)" OFF) set(COMPILE_WITH_UNITY_CMAKE_SUPPORT OFF) if (USE_UNITY_CMAKE_SUPPORT) set(COMPILE_WITH_UNITY_CMAKE_SUPPORT ON) endif() add_subdirectory(pics) add_subdirectory(src) if(BUILD_TESTING) add_subdirectory(tests) add_subdirectory(autotests) endif() ecm_qt_install_logging_categories( EXPORT KLEOPATRA FILE kleopatra.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR} ) ki18n_install(po) if(KF5DocTools_FOUND) kdoctools_install(po) add_subdirectory(doc) endif() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fbeb0410d..e792ccb2c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,390 +1,389 @@ # SPDX-FileCopyrightText: none # SPDX-License-Identifier: BSD-3-Clause add_subdirectory(icons) add_subdirectory(mimetypes) include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) if (NOT DISABLE_KWATCHGNUPG) add_subdirectory(kwatchgnupg) endif() add_subdirectory(libkleopatraclient) add_subdirectory(conf) add_subdirectory(kconf_update) if(WIN32) set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_win.cpp) set(_kleopatra_extra_SRCS utils/gnupg-registry.c selftest/registrycheck.cpp utils/windowsprocessdevice.cpp utils/userinfo_win.cpp ) else() set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_unix.cpp) set(_kleopatra_extra_SRCS) endif() set(_kleopatra_uiserver_SRCS uiserver/sessiondata.cpp uiserver/uiserver.cpp ${_kleopatra_extra_uiserver_SRCS} uiserver/assuanserverconnection.cpp uiserver/echocommand.cpp uiserver/decryptverifycommandemailbase.cpp uiserver/decryptverifycommandfilesbase.cpp uiserver/signcommand.cpp uiserver/signencryptfilescommand.cpp uiserver/prepencryptcommand.cpp uiserver/prepsigncommand.cpp uiserver/encryptcommand.cpp uiserver/selectcertificatecommand.cpp uiserver/importfilescommand.cpp uiserver/createchecksumscommand.cpp uiserver/verifychecksumscommand.cpp selftest/uiservercheck.cpp ) if(ASSUAN2_FOUND) include_directories(${ASSUAN2_INCLUDES}) set(_kleopatra_uiserver_extra_libs ${ASSUAN2_LIBRARIES}) else() include_directories(${ASSUAN_INCLUDES}) if(WIN32) set(_kleopatra_uiserver_extra_libs ${ASSUAN_VANILLA_LIBRARIES}) else() set(_kleopatra_uiserver_extra_libs ${ASSUAN_PTHREAD_LIBRARIES}) endif() endif() if(HAVE_GPG_ERR_SOURCE_KLEO) add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO) add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO) else() add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1) add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1) endif() ki18n_wrap_ui(_kleopatra_uiserver_SRCS crypto/gui/signingcertificateselectionwidget.ui) set(_kleopatra_SRCS utils/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 selftest/compliancecheck.cpp ${_kleopatra_extra_SRCS} view/htmllabel.cpp view/keylistcontroller.cpp view/keytreeview.cpp view/searchbar.cpp view/smartcardwidget.cpp view/openpgpkeycardwidget.cpp view/padwidget.cpp view/pgpcardwidget.cpp view/pivcardwidget.cpp view/p15cardwidget.cpp view/netkeywidget.cpp view/nullpinwidget.cpp view/tabwidget.cpp view/keycacheoverlay.cpp view/urllabel.cpp view/waitwidget.cpp view/welcomewidget.cpp dialogs/certificateselectiondialog.cpp dialogs/certifywidget.cpp dialogs/expirydialog.cpp dialogs/lookupcertificatesdialog.cpp dialogs/ownertrustdialog.cpp dialogs/selftestdialog.cpp dialogs/certifycertificatedialog.cpp dialogs/revokecertificationwidget.cpp dialogs/revokecertificationdialog.cpp dialogs/adduseriddialog.cpp dialogs/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/exportgroupscommand.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/exportsecretkeycommand_old.cpp commands/exportsecretsubkeycommand.cpp commands/exportopenpgpcertstoservercommand.cpp commands/adduseridcommand.cpp commands/newcertificatecommand.cpp commands/setinitialpincommand.cpp commands/learncardkeyscommand.cpp commands/checksumcreatefilescommand.cpp commands/checksumverifyfilescommand.cpp commands/exportpaperkeycommand.cpp commands/importpaperkeycommand.cpp commands/genrevokecommand.cpp commands/keytocardcommand.cpp commands/cardcommand.cpp commands/pivgeneratecardkeycommand.cpp commands/changepincommand.cpp commands/authenticatepivcardapplicationcommand.cpp commands/setpivcardapplicationadministrationkeycommand.cpp commands/certificatetopivcardcommand.cpp commands/importcertificatefrompivcardcommand.cpp commands/createopenpgpkeyfromcardkeyscommand.cpp commands/createcsrforcardkeycommand.cpp commands/listreaderscommand.cpp ${_kleopatra_uiserver_files} conf/configuredialog.cpp conf/groupsconfigdialog.cpp conf/groupsconfigpage.cpp conf/groupsconfigwidget.cpp newcertificatewizard/listwidget.cpp newcertificatewizard/newcertificatewizard.cpp smartcard/readerstatus.cpp smartcard/card.cpp smartcard/openpgpcard.cpp smartcard/netkeycard.cpp smartcard/pivcard.cpp smartcard/p15card.cpp smartcard/keypairinfo.cpp smartcard/utils.cpp smartcard/deviceinfowatcher.cpp accessibility/accessiblerichtextlabel.cpp accessibility/accessiblewidgetfactory.cpp aboutdata.cpp systrayicon.cpp kleopatraapplication.cpp mainwindow.cpp main.cpp kleopatra.qrc ) if(WIN32) configure_file (versioninfo.rc.in versioninfo.rc) set(_kleopatra_SRCS ${CMAKE_CURRENT_BINARY_DIR}/versioninfo.rc ${_kleopatra_SRCS}) endif() set (_kleopatra_SRCS conf/kleopageconfigdialog.cpp ${_kleopatra_SRCS}) ecm_qt_declare_logging_category(_kleopatra_SRCS HEADER kleopatra_debug.h IDENTIFIER KLEOPATRA_LOG CATEGORY_NAME org.kde.pim.kleopatra DESCRIPTION "kleopatra (kleopatra)" OLD_CATEGORY_NAMES log_kleopatra EXPORT KLEOPATRA ) if(KLEO_MODEL_TEST) add_definitions(-DKLEO_MODEL_TEST) set(_kleopatra_SRCS ${_kleopatra_SRCS} models/modeltest.cpp) endif() ki18n_wrap_ui(_kleopatra_SRCS dialogs/ownertrustdialog.ui dialogs/selectchecklevelwidget.ui dialogs/selftestdialog.ui dialogs/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${QT_MAJOR_VERSION}::Network Qt${QT_MAJOR_VERSION}::PrintSupport # Printing secret keys ${_kleopatra_uiserver_extra_libs} ${_kleopatra_dbusaddons_libs} kleopatraclientcore ${_kleopatra_platform_libs} ) install(TARGETS kleopatra_bin ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install( PROGRAMS data/org.kde.kleopatra.desktop data/kleopatra_import.desktop DESTINATION ${KDE_INSTALL_APPDIR} ) install(FILES data/org.kde.kleopatra.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) install( PROGRAMS data/kleopatra_signencryptfiles.desktop data/kleopatra_signencryptfolders.desktop data/kleopatra_decryptverifyfiles.desktop data/kleopatra_decryptverifyfolders.desktop DESTINATION ${KDE_INSTALL_DATADIR}/kio/servicemenus ) diff --git a/src/kwatchgnupg/CMakeLists.txt b/src/kwatchgnupg/CMakeLists.txt index 7c9e1ea42..049b77950 100644 --- a/src/kwatchgnupg/CMakeLists.txt +++ b/src/kwatchgnupg/CMakeLists.txt @@ -1,50 +1,49 @@ # SPDX-FileCopyrightText: none # SPDX-License-Identifier: BSD-3-Clause set(kwatchgnupg_version 1.0) find_package(KF5Notifications ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5TextWidgets ${KF5_MIN_VERSION} CONFIG REQUIRED) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version-kwatchgnupg.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/version-kwatchgnupg.h) set(kwatchgnupg_SRCS - ../utils/hex.cpp ../utils/kuniqueservice.cpp ../kleopatra_debug.cpp kwatchgnupgmainwin.cpp kwatchgnupgconfig.cpp aboutdata.cpp tray.cpp main.cpp kwatchgnupg.qrc ) ecm_qt_declare_logging_category(kwatchgnupg_SRCS HEADER kwatchgnupg_debug.h IDENTIFIER KWATCHGNUPG_LOG CATEGORY_NAME org.kde.pim.kwatchgnupg DESCRIPTION "kwatchgnupg (kwatchgnupg)" OLD_CATEGORY_NAMES log_kwatchgnupg EXPORT KLEOPATRA ) if(WIN32) set(kwatchgnupg_SRCS ${kwatchgnupg_SRCS} ../utils/gnupg-registry.c) endif() add_executable(kwatchgnupg ${kwatchgnupg_SRCS}) if (COMPILE_WITH_UNITY_CMAKE_SUPPORT) set_target_properties(kwatchgnupg PROPERTIES UNITY_BUILD ON) endif() target_link_libraries(kwatchgnupg KF5::DBusAddons KF5::XmlGui KF5::Notifications KF5::TextWidgets KF5::IconThemes KF5::Libkleo KF5::Crash ) install(TARGETS kwatchgnupg ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES images/kwatchgnupg.png images/kwatchgnupg2.png DESTINATION ${KDE_INSTALL_DATADIR}/kwatchgnupg/pics) diff --git a/src/selftest/gpgconfcheck.cpp b/src/selftest/gpgconfcheck.cpp index b28a7507f..075c348f2 100644 --- a/src/selftest/gpgconfcheck.cpp +++ b/src/selftest/gpgconfcheck.cpp @@ -1,89 +1,89 @@ /* -*- mode: c++; c-basic-offset:4 -*- selftest/gpgconfcheck.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 "gpgconfcheck.h" #include "implementation_p.h" #include -#include +#include #include "kleopatra_debug.h" #include #include #include using namespace Kleo; using namespace Kleo::_detail; namespace { class GpgConfCheck : public SelfTestImplementation { QString m_component; public: explicit GpgConfCheck(const char *component) : SelfTestImplementation(i18nc("@title", "%1 Configuration Check", component && * component ? QLatin1String(component) : QLatin1String("gpgconf"))), m_component(QLatin1String(component)) { runTest(); } void runTest() { const auto conf = QGpgME::cryptoConfig(); QString message; m_passed = true; if (!conf) { message = QStringLiteral ("Could not be started."); m_passed = false; } else if (m_component.isEmpty() && conf->componentList().empty()) { message = QStringLiteral ("Could not list components."); m_passed = false; } else if (!m_component.isEmpty()) { const auto comp = conf->component (m_component); if (!comp) { message = QStringLiteral ("Binary could not be found."); m_passed = false; } else if (comp->groupList().empty()) { // If we don't have any group it means that list-options // for this component failed. message = QStringLiteral ("The configuration file is invalid."); m_passed = false; } } if (!m_passed) { m_error = i18nc("self-test did not pass", "Failed"); m_explanation = i18n("There was an error executing the GnuPG configuration self-check for %2:\n" " %1\n" "You might want to execute \"gpgconf %3\" on the command line.\n", message, m_component.isEmpty() ? QStringLiteral("GnuPG") : m_component, QStringLiteral("--check-options ") + (m_component.isEmpty() ? QString() : m_component)); // To avoid modifying the l10n m_explanation.replace(QLatin1Char('\n'), QStringLiteral("
")); } } }; } std::shared_ptr Kleo::makeGpgConfCheckConfigurationSelfTest(const char *component) { return std::shared_ptr(new GpgConfCheck(component)); } diff --git a/src/uiserver/assuanserverconnection.cpp b/src/uiserver/assuanserverconnection.cpp index 7900d87b4..2cc3390a3 100644 --- a/src/uiserver/assuanserverconnection.cpp +++ b/src/uiserver/assuanserverconnection.cpp @@ -1,1690 +1,1690 @@ /* -*- mode: c++; c-basic-offset:4 -*- uiserver/assuanserverconnection.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 */ #ifndef QT_NO_CAST_TO_ASCII # define QT_NO_CAST_TO_ASCII #endif #ifndef QT_NO_CAST_FROM_ASCII # define QT_NO_CAST_FROM_ASCII #endif #include #include #include "assuanserverconnection.h" #include "assuancommand.h" #include "sessiondata.h" #include #include #include #include -#include #include #include +#include #include #include #include #include #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __GLIBCXX__ # include // for is_sorted #endif #ifdef Q_OS_WIN32 # include # include #else # include # include #endif using namespace Kleo; static const unsigned int INIT_SOCKET_FLAGS = 3; // says info assuan... //static int(*USE_DEFAULT_HANDLER)(assuan_context_t,char*) = 0; static const int FOR_READING = 0; static const unsigned int MAX_ACTIVE_FDS = 32; #ifdef HAVE_ASSUAN2 static void my_assuan_release(assuan_context_t ctx) { if (ctx) { assuan_release(ctx); } } #endif // std::shared_ptr for assuan_context_t w/ deleter enforced to assuan_deinit_server: using AssuanContextBase = std::shared_ptr::type>; struct AssuanContext : AssuanContextBase { AssuanContext() : AssuanContextBase() {} #ifndef HAVE_ASSUAN2 explicit AssuanContext(assuan_context_t ctx) : AssuanContextBase(ctx, &assuan_deinit_server) {} #else explicit AssuanContext(assuan_context_t ctx) : AssuanContextBase(ctx, &my_assuan_release) {} #endif #ifndef HAVE_ASSUAN2 void reset(assuan_context_t ctx = 0) { AssuanContextBase::reset(ctx, &assuan_deinit_server); } #else void reset(assuan_context_t ctx = nullptr) { AssuanContextBase::reset(ctx, &my_assuan_release); } #endif }; static inline gpg_error_t assuan_process_done_msg(assuan_context_t ctx, gpg_error_t err, const char *err_msg) { return assuan_process_done(ctx, assuan_set_error(ctx, err, err_msg)); } static inline gpg_error_t assuan_process_done_msg(assuan_context_t ctx, gpg_error_t err, const std::string &err_msg) { return assuan_process_done_msg(ctx, err, err_msg.c_str()); } static inline gpg_error_t assuan_process_done_msg(assuan_context_t ctx, gpg_error_t err, const QString &err_msg) { return assuan_process_done_msg(ctx, err, err_msg.toUtf8().constData()); } static std::map upcase_option(const char *option, std::map options) { std::string value; bool value_found = false; auto it = options.begin(); while (it != options.end()) if (qstricmp(it->first.c_str(), option) == 0) { value = it->second; options.erase(it++); value_found = true; } else { ++it; } if (value_found) { options[option] = value; } return options; } static std::map parse_commandline(const char *line) { std::map result; if (line) { const char *begin = line; const char *lastEQ = nullptr; while (*line) { if (*line == ' ' || *line == '\t') { if (begin != line) { if (begin[0] == '-' && begin[1] == '-') { begin += 2; // skip initial "--" } if (lastEQ && lastEQ > begin) { result[ std::string(begin, lastEQ - begin) ] = hexdecode(std::string(lastEQ + 1, line - (lastEQ + 1))); } else { result[ std::string(begin, line - begin) ] = std::string(); } } begin = line + 1; } else if (*line == '=') { if (line == begin) throw Exception(gpg_error(GPG_ERR_ASS_SYNTAX), i18n("No option name given")); else { lastEQ = line; } } ++line; } if (begin != line) { if (begin[0] == '-' && begin[1] == '-') { begin += 2; // skip initial "--" } if (lastEQ && lastEQ > begin) { result[ std::string(begin, lastEQ - begin) ] = hexdecode(std::string(lastEQ + 1, line - (lastEQ + 1))); } else { result[ begin ] = std::string(); } } } return result; } static WId wid_from_string(const QString &winIdStr, bool *ok = nullptr) { return static_cast(winIdStr.toULongLong(ok, 16)); } static void apply_window_id(QWidget *widget, const QString &winIdStr) { if (!widget || winIdStr.isEmpty()) { return; } bool ok = false; const WId wid = wid_from_string(winIdStr, &ok); if (!ok) { qCDebug(KLEOPATRA_LOG) << "window-id value" << wid << "doesn't look like a number"; return; } if (QWidget *pw = QWidget::find(wid)) { widget->setParent(pw, widget->windowFlags()); } else { widget->setAttribute(Qt::WA_NativeWindow, true); KWindowSystem::setMainWindow(widget->windowHandle(), wid); } } // // // AssuanServerConnection: // // class AssuanServerConnection::Private : public QObject { Q_OBJECT friend class ::Kleo::AssuanServerConnection; friend class ::Kleo::AssuanCommandFactory; friend class ::Kleo::AssuanCommand; AssuanServerConnection *const q; public: Private(assuan_fd_t fd_, const std::vector< std::shared_ptr > &factories_, AssuanServerConnection *qq); ~Private() override; Q_SIGNALS: void startKeyManager(); public Q_SLOTS: void slotReadActivity(int) { Q_ASSERT(ctx); #ifndef HAVE_ASSUAN2 if (const int err = assuan_process_next(ctx.get())) { #else int done = false; if (const int err = assuan_process_next(ctx.get(), &done) || done) { #endif //if ( err == -1 || gpg_err_code(err) == GPG_ERR_EOF ) { topHalfDeletion(); if (nohupedCommands.empty()) { bottomHalfDeletion(); } //} else { //assuan_process_done( ctx.get(), err ); //return; //} } } int startCommandBottomHalf(); private: void nohupDone(AssuanCommand *cmd) { const auto it = std::find_if(nohupedCommands.begin(), nohupedCommands.end(), [cmd](const std::shared_ptr &other) { return other.get() == cmd; }); Q_ASSERT(it != nohupedCommands.end()); nohupedCommands.erase(it); if (nohupedCommands.empty() && closed) { bottomHalfDeletion(); } } void commandDone(AssuanCommand *cmd) { if (!cmd || cmd != currentCommand.get()) { return; } currentCommand.reset(); } void topHalfDeletion() { if (currentCommand) { currentCommand->canceled(); } if (fd != ASSUAN_INVALID_FD) { #if defined(Q_OS_WIN32) CloseHandle(fd); #else ::close(fd); #endif } notifiers.clear(); closed = true; } void bottomHalfDeletion() { if (sessionId) { SessionDataHandler::instance()->exitSession(sessionId); } cleanup(); const QPointer that = this; Q_EMIT q->closed(q); if (that) { // still there q->deleteLater(); } } private: #ifndef HAVE_ASSUAN2 static void reset_handler(assuan_context_t ctx_) { #else static gpg_error_t reset_handler(assuan_context_t ctx_, char *) { #endif Q_ASSERT(assuan_get_pointer(ctx_)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx_)); conn.reset(); #ifdef HAVE_ASSUAN2 return 0; #endif } #ifndef HAVE_ASSUAN2 static int option_handler(assuan_context_t ctx_, const char *key, const char *value) { #else static gpg_error_t option_handler(assuan_context_t ctx_, const char *key, const char *value) { #endif Q_ASSERT(assuan_get_pointer(ctx_)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx_)); if (key && key[0] == '-' && key[1] == '-') { key += 2; // skip "--" } conn.options[key] = QString::fromUtf8(value); return 0; //return gpg_error( GPG_ERR_UNKNOWN_OPTION ); } #ifndef HAVE_ASSUAN2 static int session_handler(assuan_context_t ctx_, char *line) { #else static gpg_error_t session_handler(assuan_context_t ctx_, char *line) { #endif Q_ASSERT(assuan_get_pointer(ctx_)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx_)); const QString str = QString::fromUtf8(line); QRegExp rx(QLatin1String("(\\d+)(?:\\s+(.*))?")); if (!rx.exactMatch(str)) { static const QString errorString = i18n("Parse error"); return assuan_process_done_msg(ctx_, gpg_error(GPG_ERR_ASS_SYNTAX), errorString); } bool ok = false; if (const qulonglong id = rx.cap(1).toULongLong(&ok)) { if (ok && id <= std::numeric_limits::max()) { SessionDataHandler::instance()->enterSession(id); conn.sessionId = id; } else { static const QString errorString = i18n("Parse error: numeric session id too large"); return assuan_process_done_msg(ctx_, gpg_error(GPG_ERR_ASS_SYNTAX), errorString); } } if (!rx.cap(2).isEmpty()) { conn.sessionTitle = rx.cap(2); } qCDebug(KLEOPATRA_LOG) << "session_handler: " << "id=" << static_cast(conn.sessionId) << ", title=" << qPrintable(conn.sessionTitle); return assuan_process_done(ctx_, 0); } #ifndef HAVE_ASSUAN2 static int capabilities_handler(assuan_context_t ctx_, char *line) { #else static gpg_error_t capabilities_handler(assuan_context_t ctx_, char *line) { #endif if (!QByteArray(line).trimmed().isEmpty()) { static const QString errorString = i18n("CAPABILITIES does not take arguments"); return assuan_process_done_msg(ctx_, gpg_error(GPG_ERR_ASS_PARAMETER), errorString); } static const char capabilities[] = "SENDER=info\n" "RECIPIENT=info\n" "SESSION\n" ; return assuan_process_done(ctx_, assuan_send_data(ctx_, capabilities, sizeof capabilities - 1)); } #ifndef HAVE_ASSUAN2 static int getinfo_handler(assuan_context_t ctx_, char *line) { #else static gpg_error_t getinfo_handler(assuan_context_t ctx_, char *line) { #endif Q_ASSERT(assuan_get_pointer(ctx_)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx_)); if (qstrcmp(line, "version") == 0) { static const char version[] = "Kleopatra " KLEOPATRA_VERSION_STRING; return assuan_process_done(ctx_, assuan_send_data(ctx_, version, sizeof version - 1)); } QByteArray ba; if (qstrcmp(line, "pid") == 0) { ba = QByteArray::number(QCoreApplication::applicationPid()); } else if (qstrcmp(line, "options") == 0) { ba = conn.dumpOptions(); } else if (qstrcmp(line, "x-mementos") == 0) { ba = conn.dumpMementos(); } else if (qstrcmp(line, "senders") == 0) { ba = conn.dumpSenders(); } else if (qstrcmp(line, "recipients") == 0) { ba = conn.dumpRecipients(); } else if (qstrcmp(line, "x-files") == 0) { ba = conn.dumpFiles(); } else { static const QString errorString = i18n("Unknown value for WHAT"); return assuan_process_done_msg(ctx_, gpg_error(GPG_ERR_ASS_PARAMETER), errorString); } return assuan_process_done(ctx_, assuan_send_data(ctx_, ba.constData(), ba.size())); } #ifndef HAVE_ASSUAN2 static int start_keymanager_handler(assuan_context_t ctx_, char *line) { #else static gpg_error_t start_keymanager_handler(assuan_context_t ctx_, char *line) { #endif Q_ASSERT(assuan_get_pointer(ctx_)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx_)); if (line && *line) { static const QString errorString = i18n("START_KEYMANAGER does not take arguments"); return assuan_process_done_msg(ctx_, gpg_error(GPG_ERR_ASS_PARAMETER), errorString); } Q_EMIT conn.q->startKeyManagerRequested(); return assuan_process_done(ctx_, 0); } #ifndef HAVE_ASSUAN2 static int start_confdialog_handler(assuan_context_t ctx_, char *line) { #else static gpg_error_t start_confdialog_handler(assuan_context_t ctx_, char *line) { #endif Q_ASSERT(assuan_get_pointer(ctx_)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx_)); if (line && *line) { static const QString errorString = i18n("START_CONFDIALOG does not take arguments"); return assuan_process_done_msg(ctx_, gpg_error(GPG_ERR_ASS_PARAMETER), errorString); } Q_EMIT conn.q->startConfigDialogRequested(); return assuan_process_done(ctx_, 0); } template struct Input_or_Output : std::conditional {}; // format: TAG (FD|FD=\d+|FILE=...) template #ifndef HAVE_ASSUAN2 static int IO_handler(assuan_context_t ctx_, char *line_, T_memptr which) { #else static gpg_error_t IO_handler(assuan_context_t ctx_, char *line_, T_memptr which) { #endif Q_ASSERT(assuan_get_pointer(ctx_)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx_)); char *binOpt = strstr(line_, "--binary"); if (binOpt && !in) { /* Note there is also --armor and --base64 allowed but we don't need * to parse those because they are default. * We remove it here so that it is not parsed as an Option.*/ memset(binOpt, ' ', 8); } try { /*const*/ std::map options = upcase_option("FD", upcase_option("FILE", parse_commandline(line_))); if (options.size() < 1 || options.size() > 2) { throw gpg_error(GPG_ERR_ASS_SYNTAX); } std::shared_ptr< typename Input_or_Output::type > io; if (options.count("FD")) { if (options.count("FILE")) { throw gpg_error(GPG_ERR_CONFLICT); } assuan_fd_t fd = ASSUAN_INVALID_FD; const std::string fdstr = options["FD"]; if (fdstr.empty()) { if (const gpg_error_t err = assuan_receivefd(conn.ctx.get(), &fd)) { throw err; } } else { #if defined(Q_OS_WIN32) fd = (assuan_fd_t)std::stoi(fdstr); #else fd = std::stoi(fdstr); #endif } io = Input_or_Output::type::createFromPipeDevice(fd, in ? i18n("Message #%1", (conn.*which).size() + 1) : QString()); options.erase("FD"); } else if (options.count("FILE")) { if (options.count("FD")) { throw gpg_error(GPG_ERR_CONFLICT); } const QString filePath = QFile::decodeName(options["FILE"].c_str()); if (filePath.isEmpty()) { throw Exception(gpg_error(GPG_ERR_ASS_SYNTAX), i18n("Empty file path")); } const QFileInfo fi(filePath); if (!fi.isAbsolute()) { throw Exception(gpg_error(GPG_ERR_INV_ARG), i18n("Only absolute file paths are allowed")); } if (!fi.isFile()) { throw Exception(gpg_error(GPG_ERR_INV_ARG), i18n("Only files are allowed in INPUT/OUTPUT FILE")); } else { io = Input_or_Output::type::createFromFile(fi.absoluteFilePath(), true); } options.erase("FILE"); } else { throw gpg_error(GPG_ERR_ASS_PARAMETER); } if (options.size()) { throw gpg_error(GPG_ERR_UNKNOWN_OPTION); } (conn.*which).push_back(io); if (binOpt && !in) { auto out = reinterpret_cast (io.get()); out->setBinaryOpt(true); qCDebug(KLEOPATRA_LOG) << "Configured output for binary data"; } qCDebug(KLEOPATRA_LOG) << "AssuanServerConnection: added" << io->label(); return assuan_process_done(conn.ctx.get(), 0); } catch (const GpgME::Exception &e) { return assuan_process_done_msg(conn.ctx.get(), e.error().encodedError(), e.message().c_str()); } catch (const std::exception &) { return assuan_process_done(conn.ctx.get(), gpg_error(GPG_ERR_ASS_SYNTAX)); } catch (const gpg_error_t &e) { return assuan_process_done(conn.ctx.get(), e); } catch (...) { return assuan_process_done_msg(conn.ctx.get(), gpg_error(GPG_ERR_UNEXPECTED), "unknown exception caught"); } } #ifndef HAVE_ASSUAN2 static int input_handler(assuan_context_t ctx, char *line) { #else static gpg_error_t input_handler(assuan_context_t ctx, char *line) { #endif return IO_handler(ctx, line, &Private::inputs); } #ifndef HAVE_ASSUAN2 static int output_handler(assuan_context_t ctx, char *line) { #else static gpg_error_t output_handler(assuan_context_t ctx, char *line) { #endif return IO_handler(ctx, line, &Private::outputs); } #ifndef HAVE_ASSUAN2 static int message_handler(assuan_context_t ctx, char *line) { #else static gpg_error_t message_handler(assuan_context_t ctx, char *line) { #endif return IO_handler(ctx, line, &Private::messages); } #ifndef HAVE_ASSUAN2 static int file_handler(assuan_context_t ctx_, char *line) { #else static gpg_error_t file_handler(assuan_context_t ctx_, char *line) { #endif Q_ASSERT(assuan_get_pointer(ctx_)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx_)); try { const QFileInfo fi(QFile::decodeName(hexdecode(line).c_str())); if (!fi.isAbsolute()) { throw Exception(gpg_error(GPG_ERR_INV_ARG), i18n("Only absolute file paths are allowed")); } if (!fi.exists()) { throw gpg_error(GPG_ERR_ENOENT); } if (!fi.isReadable() || (fi.isDir() && !fi.isExecutable())) { throw gpg_error(GPG_ERR_EPERM); } conn.files.push_back(fi.absoluteFilePath()); return assuan_process_done(conn.ctx.get(), 0); } catch (const Exception &e) { return assuan_process_done_msg(conn.ctx.get(), e.error().encodedError(), e.message().toUtf8().constData()); } catch (const gpg_error_t &e) { return assuan_process_done(conn.ctx.get(), e); } catch (...) { return assuan_process_done_msg(conn.ctx.get(), gpg_error(GPG_ERR_UNEXPECTED), i18n("unknown exception caught").toUtf8().constData()); } } static bool parse_informative(const char *&begin, GpgME::Protocol &protocol) { protocol = GpgME::UnknownProtocol; bool informative = false; const char *pos = begin; while (true) { while (*pos == ' ' || *pos == '\t') { ++pos; } if (qstrnicmp(pos, "--info", strlen("--info")) == 0) { informative = true; pos += strlen("--info"); if (*pos == '=') { ++pos; break; } } else if (qstrnicmp(pos, "--protocol=", strlen("--protocol=")) == 0) { pos += strlen("--protocol="); if (qstrnicmp(pos, "OpenPGP", strlen("OpenPGP")) == 0) { protocol = GpgME::OpenPGP; pos += strlen("OpenPGP"); } else if (qstrnicmp(pos, "CMS", strlen("CMS")) == 0) { protocol = GpgME::CMS; pos += strlen("CMS"); } else { ; } } else if (qstrncmp(pos, "-- ", strlen("-- ")) == 0) { pos += 3; while (*pos == ' ' || *pos == '\t') { ++pos; } break; } else { break; } } begin = pos; return informative; } template #ifndef HAVE_ASSUAN2 static int recipient_sender_handler(T_memptr mp, T_memptr2 info, assuan_context_t ctx, char *line, bool sender = false) { #else static gpg_error_t recipient_sender_handler(T_memptr mp, T_memptr2 info, assuan_context_t ctx, char *line, bool sender = false) { #endif Q_ASSERT(assuan_get_pointer(ctx)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx)); if (!line || !*line) { return assuan_process_done(conn.ctx.get(), gpg_error(GPG_ERR_INV_ARG)); } const char *begin = line; const char *const end = begin + qstrlen(line); GpgME::Protocol proto = GpgME::UnknownProtocol; const bool informative = parse_informative(begin, proto); if (!(conn.*mp).empty() && informative != (conn.*info)) return assuan_process_done_msg(conn.ctx.get(), gpg_error(GPG_ERR_CONFLICT), i18n("Cannot mix --info with non-info SENDER or RECIPIENT").toUtf8().constData()); KMime::Types::Mailbox mb; if (!KMime::HeaderParsing::parseMailbox(begin, end, mb)) return assuan_process_done_msg(conn.ctx.get(), gpg_error(GPG_ERR_INV_ARG), i18n("Argument is not a valid RFC-2822 mailbox").toUtf8().constData()); if (begin != end) return assuan_process_done_msg(conn.ctx.get(), gpg_error(GPG_ERR_INV_ARG), i18n("Garbage after valid RFC-2822 mailbox detected").toUtf8().constData()); (conn.*info) = informative; (conn.*mp).push_back(mb); const QString email = mb.addrSpec().asString(); (void)assuan_write_line(conn.ctx.get(), qPrintable(QString::asprintf("# ok, parsed as \"%s\"", qPrintable(email)))); if (sender && !informative) { return AssuanCommandFactory::_handle(conn.ctx.get(), line, "PREP_SIGN"); } else { return assuan_process_done(ctx, 0); } } #ifndef HAVE_ASSUAN2 static int recipient_handler(assuan_context_t ctx, char *line) { #else static gpg_error_t recipient_handler(assuan_context_t ctx, char *line) { #endif return recipient_sender_handler(&Private::recipients, &Private::informativeRecipients, ctx, line); } #ifndef HAVE_ASSUAN2 static int sender_handler(assuan_context_t ctx, char *line) { #else static gpg_error_t sender_handler(assuan_context_t ctx, char *line) { #endif return recipient_sender_handler(&Private::senders, &Private::informativeSenders, ctx, line, true); } QByteArray dumpOptions() const { QByteArray result; for (auto it = options.begin(), end = options.end(); it != end; ++it) { result += it->first.c_str() + it->second.toString().toUtf8() + '\n'; } return result; } static QByteArray dumpStringList(const QStringList &sl) { return sl.join(QLatin1Char('\n')).toUtf8(); } template static QByteArray dumpStringList(const T_container &c) { QStringList sl; std::copy(c.begin(), c.end(), std::back_inserter(sl)); return dumpStringList(sl); } template static QByteArray dumpMailboxes(const T_container &c) { QStringList sl; std::transform(c.begin(), c.end(), std::back_inserter(sl), [](typename T_container::const_reference val) { return val.prettyAddress(); }); return dumpStringList(sl); } QByteArray dumpSenders() const { return dumpMailboxes(senders); } QByteArray dumpRecipients() const { return dumpMailboxes(recipients); } QByteArray dumpMementos() const { QByteArray result; for (auto it = mementos.begin(), end = mementos.end(); it != end; ++it) { char buf[2 + 2 * sizeof(void *) + 2]; sprintf(buf, "0x%p\n", (void *)it->second.get()); buf[sizeof(buf) - 1] = '\0'; result += it->first + QByteArray::fromRawData(buf, sizeof buf); } return result; } QByteArray dumpFiles() const { QStringList rv; rv.reserve(files.size()); std::copy(files.cbegin(), files.cend(), std::back_inserter(rv)); return dumpStringList(rv); } void cleanup(); void reset() { options.clear(); senders.clear(); informativeSenders = false; recipients.clear(); informativeRecipients = false; sessionTitle.clear(); sessionId = 0; mementos.clear(); files.clear(); std::for_each(inputs.begin(), inputs.end(), std::mem_fn(&Input::finalize)); inputs.clear(); std::for_each(outputs.begin(), outputs.end(), std::mem_fn(&Output::finalize)); outputs.clear(); std::for_each(messages.begin(), messages.end(), std::mem_fn(&Input::finalize)); messages.clear(); bias = GpgME::UnknownProtocol; } assuan_fd_t fd; AssuanContext ctx; bool closed : 1; bool cryptoCommandsEnabled : 1; bool commandWaitingForCryptoCommandsEnabled : 1; bool currentCommandIsNohup : 1; bool informativeSenders; // address taken, so no : 1 bool informativeRecipients; // address taken, so no : 1 GpgME::Protocol bias; QString sessionTitle; unsigned int sessionId; std::vector< std::shared_ptr > notifiers; std::vector< std::shared_ptr > factories; // sorted: _detail::ByName std::shared_ptr currentCommand; std::vector< std::shared_ptr > nohupedCommands; std::map options; std::vector senders, recipients; std::vector< std::shared_ptr > inputs, messages; std::vector< std::shared_ptr > outputs; std::vector files; std::map< QByteArray, std::shared_ptr > mementos; }; void AssuanServerConnection::Private::cleanup() { Q_ASSERT(nohupedCommands.empty()); reset(); currentCommand.reset(); currentCommandIsNohup = false; commandWaitingForCryptoCommandsEnabled = false; notifiers.clear(); ctx.reset(); fd = ASSUAN_INVALID_FD; } AssuanServerConnection::Private::Private(assuan_fd_t fd_, const std::vector< std::shared_ptr > &factories_, AssuanServerConnection *qq) : QObject(), q(qq), fd(fd_), closed(false), cryptoCommandsEnabled(false), commandWaitingForCryptoCommandsEnabled(false), currentCommandIsNohup(false), informativeSenders(false), informativeRecipients(false), bias(GpgME::UnknownProtocol), sessionId(0), factories(factories_) { #ifdef __GLIBCXX__ Q_ASSERT(__gnu_cxx::is_sorted(factories_.begin(), factories_.end(), _detail::ByName())); #endif if (fd == ASSUAN_INVALID_FD) { throw Exception(gpg_error(GPG_ERR_INV_ARG), "pre-assuan_init_socket_server_ext"); } #ifndef HAVE_ASSUAN2 assuan_context_t naked_ctx = 0; if (const gpg_error_t err = assuan_init_socket_server_ext(&naked_ctx, fd, INIT_SOCKET_FLAGS)) #else { assuan_context_t naked_ctx = nullptr; if (const gpg_error_t err = assuan_new(&naked_ctx)) { throw Exception(err, "assuan_new"); } ctx.reset(naked_ctx); } if (const gpg_error_t err = assuan_init_socket_server(ctx.get(), fd, INIT_SOCKET_FLAGS)) #endif throw Exception(err, "assuan_init_socket_server_ext"); #ifndef HAVE_ASSUAN2 ctx.reset(naked_ctx); naked_ctx = 0; #endif // for callbacks, associate the context with this connection: assuan_set_pointer(ctx.get(), this); FILE *const logFile = Log::instance()->logFile(); assuan_set_log_stream(ctx.get(), logFile ? logFile : stderr); // register FDs with the event loop: assuan_fd_t fds[MAX_ACTIVE_FDS]; const int numFDs = assuan_get_active_fds(ctx.get(), FOR_READING, fds, MAX_ACTIVE_FDS); Q_ASSERT(numFDs != -1); // == 1 if (!numFDs || fds[0] != fd) { const std::shared_ptr sn(new QSocketNotifier((intptr_t)fd, QSocketNotifier::Read), std::mem_fn(&QObject::deleteLater)); connect(sn.get(), &QSocketNotifier::activated, this, &Private::slotReadActivity); notifiers.push_back(sn); } notifiers.reserve(notifiers.size() + numFDs); for (int i = 0; i < numFDs; ++i) { const std::shared_ptr sn(new QSocketNotifier((intptr_t)fds[i], QSocketNotifier::Read), std::mem_fn(&QObject::deleteLater)); connect(sn.get(), &QSocketNotifier::activated, this, &Private::slotReadActivity); notifiers.push_back(sn); } // register our INPUT/OUTPUT/MESSGAE/FILE handlers: #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "INPUT", input_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "INPUT", input_handler, "")) #endif throw Exception(err, "register \"INPUT\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "MESSAGE", message_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "MESSAGE", message_handler, "")) #endif throw Exception(err, "register \"MESSAGE\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "OUTPUT", output_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "OUTPUT", output_handler, "")) #endif throw Exception(err, "register \"OUTPUT\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "FILE", file_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "FILE", file_handler, "")) #endif throw Exception(err, "register \"FILE\" handler"); // register user-defined commands: for (std::shared_ptr fac : std::as_const(factories)) #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), fac->name(), fac->_handler())) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), fac->name(), fac->_handler(), "")) #endif throw Exception(err, std::string("register \"") + fac->name() + "\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "GETINFO", getinfo_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "GETINFO", getinfo_handler, "")) #endif throw Exception(err, "register \"GETINFO\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "START_KEYMANAGER", start_keymanager_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "START_KEYMANAGER", start_keymanager_handler, "")) #endif throw Exception(err, "register \"START_KEYMANAGER\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "START_CONFDIALOG", start_confdialog_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "START_CONFDIALOG", start_confdialog_handler, "")) #endif throw Exception(err, "register \"START_CONFDIALOG\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "RECIPIENT", recipient_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "RECIPIENT", recipient_handler, "")) #endif throw Exception(err, "register \"RECIPIENT\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "SENDER", sender_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "SENDER", sender_handler, "")) #endif throw Exception(err, "register \"SENDER\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "SESSION", session_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "SESSION", session_handler, "")) #endif throw Exception(err, "register \"SESSION\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "CAPABILITIES", capabilities_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "CAPABILITIES", capabilities_handler, "")) #endif throw Exception(err, "register \"CAPABILITIES\" handler"); assuan_set_hello_line(ctx.get(), "GPG UI server (Kleopatra/" KLEOPATRA_VERSION_STRING ") ready to serve"); //assuan_set_hello_line( ctx.get(), GPG UI server (qApp->applicationName() + " v" + kapp->applicationVersion() + "ready to serve" ) // some notifiers we're interested in: if (const gpg_error_t err = assuan_register_reset_notify(ctx.get(), reset_handler)) { throw Exception(err, "register reset notify"); } if (const gpg_error_t err = assuan_register_option_handler(ctx.get(), option_handler)) { throw Exception(err, "register option handler"); } // and last, we need to call assuan_accept, which doesn't block // (d/t INIT_SOCKET_FLAGS), but performs vital connection // establishing handling: if (const gpg_error_t err = assuan_accept(ctx.get())) { throw Exception(err, "assuan_accept"); } } AssuanServerConnection::Private::~Private() { cleanup(); } AssuanServerConnection::AssuanServerConnection(assuan_fd_t fd, const std::vector< std::shared_ptr > &factories, QObject *p) : QObject(p), d(new Private(fd, factories, this)) { } AssuanServerConnection::~AssuanServerConnection() {} void AssuanServerConnection::enableCryptoCommands(bool on) { if (on == d->cryptoCommandsEnabled) { return; } d->cryptoCommandsEnabled = on; if (d->commandWaitingForCryptoCommandsEnabled) { QTimer::singleShot(0, d.get(), &Private::startCommandBottomHalf); } } // // // AssuanCommand: // // namespace Kleo { class InquiryHandler : public QObject { Q_OBJECT public: #if defined(HAVE_ASSUAN2) || defined(HAVE_ASSUAN_INQUIRE_EXT) explicit InquiryHandler(const char *keyword_, QObject *p = nullptr) : QObject(p), # if !defined(HAVE_ASSUAN2) && !defined(HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT) buffer(0), buflen(0), # endif keyword(keyword_) { } # if defined(HAVE_ASSUAN2) || defined(HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT) # ifndef HAVE_ASSUAN2 static int handler(void *cb_data, int rc, unsigned char *buffer, size_t buflen) # else static gpg_error_t handler(void *cb_data, gpg_error_t rc, unsigned char *buffer, size_t buflen) # endif { Q_ASSERT(cb_data); auto this_ = static_cast(cb_data); Q_EMIT this_->signal(rc, QByteArray::fromRawData(reinterpret_cast(buffer), buflen), this_->keyword); std::free(buffer); delete this_; return 0; } # else static int handler(void *cb_data, int rc) { Q_ASSERT(cb_data); InquiryHandler *this_ = static_cast(cb_data); Q_EMIT this_->signal(rc, QByteArray::fromRawData(reinterpret_cast(this_->buffer), this_->buflen), this_->keyword); std::free(this_->buffer); delete this_; return 0; } # endif private: #if !defined(HAVE_ASSUAN2) && !defined(HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT) friend class ::Kleo::AssuanCommand; unsigned char *buffer; size_t buflen; #endif const char *keyword; #endif // defined(HAVE_ASSUAN2) || defined(HAVE_ASSUAN_INQUIRE_EXT) Q_SIGNALS: void signal(int rc, const QByteArray &data, const QByteArray &keyword); }; } // namespace Kleo class AssuanCommand::Private { public: Private() : informativeRecipients(false), informativeSenders(false), bias(GpgME::UnknownProtocol), done(false), nohup(false) { } std::map options; std::vector< std::shared_ptr > inputs, messages; std::vector< std::shared_ptr > outputs; std::vector files; std::vector recipients, senders; bool informativeRecipients, informativeSenders; GpgME::Protocol bias; QString sessionTitle; unsigned int sessionId; QByteArray utf8ErrorKeepAlive; AssuanContext ctx; bool done; bool nohup; }; AssuanCommand::AssuanCommand() : d(new Private) { } AssuanCommand::~AssuanCommand() { } int AssuanCommand::start() { try { if (const int err = doStart()) if (!d->done) { done(err); } return 0; } catch (const Exception &e) { if (!d->done) { done(e.error_code(), e.message()); } return 0; } catch (const GpgME::Exception &e) { if (!d->done) { done(e.error(), QString::fromLocal8Bit(e.message().c_str())); } return 0; } catch (const std::exception &e) { if (!d->done) { done(makeError(GPG_ERR_INTERNAL), i18n("Caught unexpected exception: %1", QString::fromLocal8Bit(e.what()))); } return 0; } catch (...) { if (!d->done) { done(makeError(GPG_ERR_INTERNAL), i18n("Caught unknown exception - please report this error to the developers.")); } return 0; } } void AssuanCommand::canceled() { d->done = true; doCanceled(); } // static int AssuanCommand::makeError(int code) { return makeGnuPGError(code); } bool AssuanCommand::hasOption(const char *opt) const { return d->options.count(opt); } QVariant AssuanCommand::option(const char *opt) const { const auto it = d->options.find(opt); if (it == d->options.end()) { return QVariant(); } else { return it->second; } } const std::map &AssuanCommand::options() const { return d->options; } namespace { template std::vector keys(const std::map &map) { std::vector result; result.resize(map.size()); for (typename std::map::const_iterator it = map.begin(), end = map.end(); it != end; ++it) { result.push_back(it->first); } return result; } } const std::map< QByteArray, std::shared_ptr > &AssuanCommand::mementos() const { // oh, hack :( Q_ASSERT(assuan_get_pointer(d->ctx.get())); const AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(d->ctx.get())); return conn.mementos; } bool AssuanCommand::hasMemento(const QByteArray &tag) const { if (const unsigned int id = sessionId()) { return SessionDataHandler::instance()->sessionData(id)->mementos.count(tag) || mementos().count(tag); } else { return mementos().count(tag); } } std::shared_ptr AssuanCommand::memento(const QByteArray &tag) const { if (const unsigned int id = sessionId()) { const std::shared_ptr sdh = SessionDataHandler::instance(); const std::shared_ptr sd = sdh->sessionData(id); const auto it = sd->mementos.find(tag); if (it != sd->mementos.end()) { return it->second; } } const auto it = mementos().find(tag); if (it == mementos().end()) { return std::shared_ptr(); } else { return it->second; } } QByteArray AssuanCommand::registerMemento(const std::shared_ptr &mem) { const QByteArray tag = QByteArray::number(reinterpret_cast(mem.get()), 36); return registerMemento(tag, mem); } QByteArray AssuanCommand::registerMemento(const QByteArray &tag, const std::shared_ptr &mem) { // oh, hack :( Q_ASSERT(assuan_get_pointer(d->ctx.get())); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(d->ctx.get())); if (const unsigned int id = sessionId()) { SessionDataHandler::instance()->sessionData(id)->mementos[tag] = mem; } else { conn.mementos[tag] = mem; } return tag; } void AssuanCommand::removeMemento(const QByteArray &tag) { // oh, hack :( Q_ASSERT(assuan_get_pointer(d->ctx.get())); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(d->ctx.get())); conn.mementos.erase(tag); if (const unsigned int id = sessionId()) { SessionDataHandler::instance()->sessionData(id)->mementos.erase(tag); } } const std::vector< std::shared_ptr > &AssuanCommand::inputs() const { return d->inputs; } const std::vector< std::shared_ptr > &AssuanCommand::messages() const { return d->messages; } const std::vector< std::shared_ptr > &AssuanCommand::outputs() const { return d->outputs; } QStringList AssuanCommand::fileNames() const { QStringList rv; rv.reserve(d->files.size()); std::copy(d->files.cbegin(), d->files.cend(), std::back_inserter(rv)); return rv; } unsigned int AssuanCommand::numFiles() const { return d->files.size(); } void AssuanCommand::sendStatus(const char *keyword, const QString &text) { sendStatusEncoded(keyword, text.toUtf8().constData()); } void AssuanCommand::sendStatusEncoded(const char *keyword, const std::string &text) { if (d->nohup) { return; } if (const int err = assuan_write_status(d->ctx.get(), keyword, text.c_str())) { throw Exception(err, i18n("Cannot send \"%1\" status", QString::fromLatin1(keyword))); } } void AssuanCommand::sendData(const QByteArray &data, bool moreToCome) { if (d->nohup) { return; } if (const gpg_error_t err = assuan_send_data(d->ctx.get(), data.constData(), data.size())) { throw Exception(err, i18n("Cannot send data")); } if (!moreToCome) if (const gpg_error_t err = assuan_send_data(d->ctx.get(), nullptr, 0)) { // flush throw Exception(err, i18n("Cannot flush data")); } } int AssuanCommand::inquire(const char *keyword, QObject *receiver, const char *slot, unsigned int maxSize) { Q_ASSERT(keyword); Q_ASSERT(receiver); Q_ASSERT(slot); if (d->nohup) { return makeError(GPG_ERR_INV_OP); } #if defined(HAVE_ASSUAN2) || defined(HAVE_ASSUAN_INQUIRE_EXT) std::unique_ptr ih(new InquiryHandler(keyword, receiver)); receiver->connect(ih.get(), SIGNAL(signal(int,QByteArray,QByteArray)), slot); if (const gpg_error_t err = assuan_inquire_ext(d->ctx.get(), keyword, # if !defined(HAVE_ASSUAN2) && !defined(HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT) &ih->buffer, &ih->buflen, # endif maxSize, InquiryHandler::handler, ih.get())) { return err; } ih.release(); return 0; #else return makeError(GPG_ERR_NOT_SUPPORTED); // libassuan too old #endif // defined(HAVE_ASSUAN2) || defined(HAVE_ASSUAN_INQUIRE_EXT) } void AssuanCommand::done(const GpgME::Error &err, const QString &details) { if (d->ctx && !d->done && !details.isEmpty()) { qCDebug(KLEOPATRA_LOG) << "Error: " << details; d->utf8ErrorKeepAlive = details.toUtf8(); if (!d->nohup) { assuan_set_error(d->ctx.get(), err.encodedError(), d->utf8ErrorKeepAlive.constData()); } } done(err); } void AssuanCommand::done(const GpgME::Error &err) { if (!d->ctx) { qCDebug(KLEOPATRA_LOG) << err.asString() << ": called with NULL ctx."; return; } if (d->done) { qCDebug(KLEOPATRA_LOG) << err.asString() << ": called twice!"; return; } d->done = true; std::for_each(d->messages.begin(), d->messages.end(), std::mem_fn(&Input::finalize)); std::for_each(d->inputs.begin(), d->inputs.end(), std::mem_fn(&Input::finalize)); std::for_each(d->outputs.begin(), d->outputs.end(), std::mem_fn(&Output::finalize)); d->messages.clear(); d->inputs.clear(); d->outputs.clear(); d->files.clear(); // oh, hack :( Q_ASSERT(assuan_get_pointer(d->ctx.get())); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(d->ctx.get())); if (d->nohup) { conn.nohupDone(this); return; } const gpg_error_t rc = assuan_process_done(d->ctx.get(), err.encodedError()); if (gpg_err_code(rc) != GPG_ERR_NO_ERROR) qFatal("AssuanCommand::done: assuan_process_done returned error %d (%s)", static_cast(rc), gpg_strerror(rc)); d->utf8ErrorKeepAlive.clear(); conn.commandDone(this); } void AssuanCommand::setNohup(bool nohup) { d->nohup = nohup; } bool AssuanCommand::isNohup() const { return d->nohup; } bool AssuanCommand::isDone() const { return d->done; } QString AssuanCommand::sessionTitle() const { return d->sessionTitle; } unsigned int AssuanCommand::sessionId() const { return d->sessionId; } bool AssuanCommand::informativeSenders() const { return d->informativeSenders; } bool AssuanCommand::informativeRecipients() const { return d->informativeRecipients; } const std::vector &AssuanCommand::recipients() const { return d->recipients; } const std::vector &AssuanCommand::senders() const { return d->senders; } #ifndef HAVE_ASSUAN2 int AssuanCommandFactory::_handle(assuan_context_t ctx, char *line, const char *commandName) { #else gpg_error_t AssuanCommandFactory::_handle(assuan_context_t ctx, char *line, const char *commandName) { #endif Q_ASSERT(assuan_get_pointer(ctx)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx)); try { const auto it = std::lower_bound(conn.factories.begin(), conn.factories.end(), commandName, _detail::ByName()); kleo_assert(it != conn.factories.end()); kleo_assert(*it); kleo_assert(qstricmp((*it)->name(), commandName) == 0); const std::shared_ptr cmd = (*it)->create(); kleo_assert(cmd); cmd->d->ctx = conn.ctx; cmd->d->options = conn.options; cmd->d->inputs.swap(conn.inputs); kleo_assert(conn.inputs.empty()); cmd->d->messages.swap(conn.messages); kleo_assert(conn.messages.empty()); cmd->d->outputs.swap(conn.outputs); kleo_assert(conn.outputs.empty()); cmd->d->files.swap(conn.files); kleo_assert(conn.files.empty()); cmd->d->senders.swap(conn.senders); kleo_assert(conn.senders.empty()); cmd->d->recipients.swap(conn.recipients); kleo_assert(conn.recipients.empty()); cmd->d->informativeRecipients = conn.informativeRecipients; cmd->d->informativeSenders = conn.informativeSenders; cmd->d->bias = conn.bias; cmd->d->sessionTitle = conn.sessionTitle; cmd->d->sessionId = conn.sessionId; const std::map cmdline_options = parse_commandline(line); for (auto it = cmdline_options.begin(), end = cmdline_options.end(); it != end; ++it) { cmd->d->options[it->first] = QString::fromUtf8(it->second.c_str()); } bool nohup = false; if (cmd->d->options.count("nohup")) { if (!cmd->d->options["nohup"].toString().isEmpty()) { return assuan_process_done_msg(conn.ctx.get(), gpg_error(GPG_ERR_ASS_PARAMETER), "--nohup takes no argument"); } nohup = true; cmd->d->options.erase("nohup"); } conn.currentCommand = cmd; conn.currentCommandIsNohup = nohup; QTimer::singleShot(0, &conn, &AssuanServerConnection::Private::startCommandBottomHalf); return 0; } catch (const Exception &e) { return assuan_process_done_msg(conn.ctx.get(), e.error_code(), e.message()); } catch (const std::exception &e) { return assuan_process_done_msg(conn.ctx.get(), gpg_error(GPG_ERR_UNEXPECTED), e.what()); } catch (...) { return assuan_process_done_msg(conn.ctx.get(), gpg_error(GPG_ERR_UNEXPECTED), i18n("Caught unknown exception")); } } int AssuanServerConnection::Private::startCommandBottomHalf() { commandWaitingForCryptoCommandsEnabled = currentCommand && !cryptoCommandsEnabled; if (!cryptoCommandsEnabled) { return 0; } const std::shared_ptr cmd = currentCommand; if (!cmd) { return 0; } currentCommand.reset(); const bool nohup = currentCommandIsNohup; currentCommandIsNohup = false; try { if (const int err = cmd->start()) { if (cmd->isDone()) { return err; } else { return assuan_process_done(ctx.get(), err); } } if (cmd->isDone()) { return 0; } if (nohup) { cmd->setNohup(true); nohupedCommands.push_back(cmd); return assuan_process_done_msg(ctx.get(), 0, "Command put in the background to continue executing after connection end."); } else { currentCommand = cmd; return 0; } } catch (const Exception &e) { return assuan_process_done_msg(ctx.get(), e.error_code(), e.message()); } catch (const std::exception &e) { return assuan_process_done_msg(ctx.get(), gpg_error(GPG_ERR_UNEXPECTED), e.what()); } catch (...) { return assuan_process_done_msg(ctx.get(), gpg_error(GPG_ERR_UNEXPECTED), i18n("Caught unknown exception")); } } // // // AssuanCommand convenience methods // // /*! Checks the \c --mode parameter. \returns The parameter as an AssuanCommand::Mode enum value. If no \c --mode was given, or it's value wasn't recognized, throws an Kleo::Exception. */ AssuanCommand::Mode AssuanCommand::checkMode() const { if (!hasOption("mode")) { throw Exception(makeError(GPG_ERR_MISSING_VALUE), i18n("Required --mode option missing")); } const QString modeString = option("mode").toString().toLower(); if (modeString == QLatin1String("filemanager")) { return FileManager; } if (modeString == QLatin1String("email")) { return EMail; } throw Exception(makeError(GPG_ERR_INV_ARG), i18n("invalid mode: \"%1\"", modeString)); } /*! Checks the \c --protocol parameter. \returns The parameter as a GpgME::Protocol enum value. If \c --protocol was given, but has an invalid value, throws an Kleo::Exception. If no \c --protocol was given, checks the connection bias, if available, otherwise, in FileManager mode, returns GpgME::UnknownProtocol, but if \a mode == \c EMail, throws an Kleo::Exception instead. */ GpgME::Protocol AssuanCommand::checkProtocol(Mode mode, int options) const { if (!hasOption("protocol")) if (d->bias != GpgME::UnknownProtocol) { return d->bias; } else if (mode == AssuanCommand::EMail && (options & AllowProtocolMissing) == 0) { throw Exception(makeError(GPG_ERR_MISSING_VALUE), i18n("Required --protocol option missing")); } else { return GpgME::UnknownProtocol; } else if (mode == AssuanCommand::FileManager) { throw Exception(makeError(GPG_ERR_INV_FLAG), i18n("--protocol is not allowed here")); } const QString protocolString = option("protocol").toString().toLower(); if (protocolString == QLatin1String("openpgp")) { return GpgME::OpenPGP; } if (protocolString == QLatin1String("cms")) { return GpgME::CMS; } throw Exception(makeError(GPG_ERR_INV_ARG), i18n("invalid protocol \"%1\"", protocolString)); } void AssuanCommand::doApplyWindowID(QWidget *widget) const { if (!widget || !hasOption("window-id")) { return; } apply_window_id(widget, option("window-id").toString()); } WId AssuanCommand::parentWId() const { return wid_from_string(option("window-id").toString()); } #include "assuanserverconnection.moc" diff --git a/src/uiserver/decryptverifycommandemailbase.cpp b/src/uiserver/decryptverifycommandemailbase.cpp index 349a7d39c..f1a85d57f 100644 --- a/src/uiserver/decryptverifycommandemailbase.cpp +++ b/src/uiserver/decryptverifycommandemailbase.cpp @@ -1,212 +1,212 @@ /* -*- mode: c++; c-basic-offset:4 -*- uiserver/decryptverifycommandemailbase.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 #include "decryptverifycommandemailbase.h" #include #include -#include #include #include #include +#include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Formatting; using namespace GpgME; class DecryptVerifyCommandEMailBase::Private : public QObject { Q_OBJECT friend class ::Kleo::DecryptVerifyCommandEMailBase; DecryptVerifyCommandEMailBase *const q; public: explicit Private(DecryptVerifyCommandEMailBase *qq) : QObject(), q(qq), controller() { } ~Private() override { } void checkForErrors() const; public Q_SLOTS: void slotProgress(const QString &what, int current, int total); void verificationResult(const GpgME::VerificationResult &); void slotDone() { q->done(); } void slotError(int err, const QString &details) { q->done(err, details); } public: private: std::shared_ptr controller; }; DecryptVerifyCommandEMailBase::DecryptVerifyCommandEMailBase() : AssuanCommandMixin(), d(new Private(this)) { } DecryptVerifyCommandEMailBase::~DecryptVerifyCommandEMailBase() {} int DecryptVerifyCommandEMailBase::doStart() { d->checkForErrors(); d->controller.reset(new DecryptVerifyEMailController(shared_from_this())); const QString st = sessionTitle(); if (!st.isEmpty()) Q_FOREACH (const std::shared_ptr &i, inputs()) { i->setLabel(st); } d->controller->setSessionId(sessionId()); d->controller->setOperation(operation()); d->controller->setVerificationMode(messages().empty() ? Opaque : Detached); d->controller->setInputs(inputs()); d->controller->setSignedData(messages()); d->controller->setOutputs(outputs()); d->controller->setWizardShown(!hasOption("silent")); d->controller->setProtocol(checkProtocol(mode())); if (informativeSenders()) { d->controller->setInformativeSenders(senders()); } QObject::connect(d->controller.get(), SIGNAL(done()), d.get(), SLOT(slotDone()), Qt::QueuedConnection); QObject::connect(d->controller.get(), SIGNAL(error(int,QString)), d.get(), SLOT(slotError(int,QString)), Qt::QueuedConnection); QObject::connect(d->controller.get(), &DecryptVerifyEMailController::verificationResult, d.get(), &Private::verificationResult, Qt::QueuedConnection); d->controller->start(); return 0; } void DecryptVerifyCommandEMailBase::Private::checkForErrors() const { if (!q->senders().empty() && !q->informativeSenders()) throw Kleo::Exception(q->makeError(GPG_ERR_CONFLICT), i18n("Cannot use non-info SENDER")); if (!q->recipients().empty() && !q->informativeRecipients()) throw Kleo::Exception(q->makeError(GPG_ERR_CONFLICT), i18n("Cannot use non-info RECIPIENT")); // ### use informative recipients and senders const unsigned int numInputs = q->inputs().size(); const unsigned int numMessages = q->messages().size(); const unsigned int numOutputs = q->outputs().size(); const unsigned int numInformativeSenders = q->informativeSenders() ? q->senders().size() : 0; const DecryptVerifyOperation op = q->operation(); const GpgME::Protocol proto = q->checkProtocol(q->mode()); const unsigned int numFiles = q->numFiles(); if (numFiles) { throw Kleo::Exception(q->makeError(GPG_ERR_CONFLICT), i18n("FILES present")); } if (!numInputs) throw Kleo::Exception(q->makeError(GPG_ERR_ASS_NO_INPUT), i18n("At least one INPUT needs to be provided")); if (numInformativeSenders != 0) if (numInformativeSenders != numInputs) throw Kleo::Exception(q->makeError(GPG_ERR_ASS_NO_INPUT), //TODO use better error code if possible i18n("INPUT/SENDER --info count mismatch")); if (numMessages) { if (numMessages != numInputs) throw Kleo::Exception(q->makeError(GPG_ERR_ASS_NO_INPUT), //TODO use better error code if possible i18n("INPUT/MESSAGE count mismatch")); else if (op != Verify) throw Kleo::Exception(q->makeError(GPG_ERR_CONFLICT), i18n("MESSAGE can only be given for detached signature verification")); } if (numOutputs) { if (numOutputs != numInputs) throw Kleo::Exception(q->makeError(GPG_ERR_ASS_NO_OUTPUT), //TODO use better error code if possible i18n("INPUT/OUTPUT count mismatch")); else if (numMessages) throw Kleo::Exception(q->makeError(GPG_ERR_CONFLICT), i18n("Cannot use OUTPUT and MESSAGE simultaneously")); } kleo_assert(proto != UnknownProtocol); const auto backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); if (!backend) throw Kleo::Exception(q->makeError(GPG_ERR_UNSUPPORTED_PROTOCOL), proto == OpenPGP ? i18n("No backend support for OpenPGP") : proto == CMS ? i18n("No backend support for S/MIME") : QString()); } void DecryptVerifyCommandEMailBase::doCanceled() { if (d->controller) { d->controller->cancel(); } } void DecryptVerifyCommandEMailBase::Private::slotProgress(const QString &what, int current, int total) { Q_UNUSED(what) Q_UNUSED(current) Q_UNUSED(total) // ### FIXME report progress, via sendStatus() } void DecryptVerifyCommandEMailBase::Private::verificationResult(const VerificationResult &vResult) { try { const std::vector sigs = vResult.signatures(); for (const Signature &sig : sigs) { const QString s = signatureToString(sig, sig.key(true, true)); const char *color = summaryToString(sig.summary()); q->sendStatusEncoded("SIGSTATUS", color + (' ' + hexencode(s.toUtf8().constData()))); } } catch (...) {} } #include "decryptverifycommandemailbase.moc" diff --git a/src/uiserver/decryptverifycommandfilesbase.cpp b/src/uiserver/decryptverifycommandfilesbase.cpp index b6963f705..88db10a6b 100644 --- a/src/uiserver/decryptverifycommandfilesbase.cpp +++ b/src/uiserver/decryptverifycommandfilesbase.cpp @@ -1,193 +1,193 @@ /* -*- mode: c++; c-basic-offset:4 -*- uiserver/decryptverifycommandfilesbase.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 #include "decryptverifycommandfilesbase.h" #include "fileoperationspreferences.h" #include #include "crypto/decryptverifyfilescontroller.h" #include "crypto/autodecryptverifyfilescontroller.h" -#include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Formatting; using namespace GpgME; class DecryptVerifyCommandFilesBase::Private : public QObject { Q_OBJECT friend class ::Kleo::DecryptVerifyCommandFilesBase; DecryptVerifyCommandFilesBase *const q; public: explicit Private(DecryptVerifyCommandFilesBase *qq) : QObject(), q(qq), controller() { } ~Private() override { } void checkForErrors() const; public Q_SLOTS: void slotProgress(const QString &what, int current, int total); void verificationResult(const GpgME::VerificationResult &); void slotDone() { q->done(); } void slotError(int err, const QString &details) { q->done(err, details); } public: private: std::shared_ptr controller; }; DecryptVerifyCommandFilesBase::DecryptVerifyCommandFilesBase() : AssuanCommandMixin(), d(new Private(this)) { } DecryptVerifyCommandFilesBase::~DecryptVerifyCommandFilesBase() {} int DecryptVerifyCommandFilesBase::doStart() { d->checkForErrors(); FileOperationsPreferences prefs; if (prefs.autoDecryptVerify()) { d->controller.reset(new AutoDecryptVerifyFilesController()); } else { d->controller.reset(new DecryptVerifyFilesController(shared_from_this())); } d->controller->setOperation(operation()); d->controller->setFiles(fileNames()); QObject::connect(d->controller.get(), SIGNAL(done()), d.get(), SLOT(slotDone()), Qt::QueuedConnection); QObject::connect(d->controller.get(), SIGNAL(error(int,QString)), d.get(), SLOT(slotError(int,QString)), Qt::QueuedConnection); QObject::connect(d->controller.get(), &DecryptVerifyFilesController::verificationResult, d.get(), &Private::verificationResult, Qt::QueuedConnection); d->controller->start(); return 0; } namespace { struct is_file : std::unary_function { bool operator()(const QString &file) const { return QFileInfo(file).isFile(); } }; } void DecryptVerifyCommandFilesBase::Private::checkForErrors() const { if (!q->senders().empty()) throw Kleo::Exception(q->makeError(GPG_ERR_CONFLICT), i18n("Cannot use SENDER")); if (!q->recipients().empty()) throw Kleo::Exception(q->makeError(GPG_ERR_CONFLICT), i18n("Cannot use RECIPIENT")); const unsigned int numInputs = q->inputs().size(); const unsigned int numMessages = q->messages().size(); const unsigned int numOutputs = q->outputs().size(); if (numInputs) { throw Kleo::Exception(q->makeError(GPG_ERR_CONFLICT), i18n("INPUT present")); } if (numMessages) { throw Kleo::Exception(q->makeError(GPG_ERR_CONFLICT), i18n("MESSAGE present")); } if (numOutputs) { throw Kleo::Exception(q->makeError(GPG_ERR_CONFLICT), i18n("OUTPUT present")); } const QStringList fileNames = q->fileNames(); if (fileNames.empty()) throw Exception(makeError(GPG_ERR_ASS_NO_INPUT), i18n("At least one FILE must be present")); if (!std::all_of(fileNames.cbegin(), fileNames.cend(), is_file())) throw Exception(makeError(GPG_ERR_INV_ARG), i18n("DECRYPT/VERIFY_FILES cannot use directories as input")); } void DecryptVerifyCommandFilesBase::doCanceled() { if (d->controller) { d->controller->cancel(); } } void DecryptVerifyCommandFilesBase::Private::slotProgress(const QString &what, int current, int total) { Q_UNUSED(what) Q_UNUSED(current) Q_UNUSED(total) // ### FIXME report progress, via sendStatus() } void DecryptVerifyCommandFilesBase::Private::verificationResult(const VerificationResult &vResult) { try { const std::vector sigs = vResult.signatures(); for (const Signature &sig : sigs) { const QString s = signatureToString(sig, sig.key(true, true)); const char *color = summaryToString(sig.summary()); q->sendStatusEncoded("SIGSTATUS", color + (' ' + hexencode(s.toUtf8().constData()))); } } catch (...) {} } #include "decryptverifycommandfilesbase.moc" diff --git a/src/utils/hex.cpp b/src/utils/hex.cpp deleted file mode 100644 index eea5d54f3..000000000 --- a/src/utils/hex.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* -*- mode: c++; c-basic-offset:4 -*- - utils/hex.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 - -#include "hex.h" - -#include - -#include - -#include -#include - -using namespace Kleo; - -static unsigned char unhex(unsigned char ch) -{ - if (ch >= '0' && ch <= '9') { - return ch - '0'; - } - if (ch >= 'A' && ch <= 'F') { - return ch - 'A' + 10; - } - if (ch >= 'a' && ch <= 'f') { - return ch - 'a' + 10; - } - const char cch = ch; - throw Exception(gpg_error(GPG_ERR_ASS_SYNTAX), - i18n("Invalid hex char '%1' in input stream.", - QString::fromLatin1(&cch, 1))); -} - -std::string Kleo::hexdecode(const std::string &in) -{ - std::string result; - result.reserve(in.size()); - for (std::string::const_iterator it = in.begin(), end = in.end(); it != end; ++it) - if (*it == '%') { - ++it; - unsigned char ch = '\0'; - if (it == end) - throw Exception(gpg_error(GPG_ERR_ASS_SYNTAX), - i18n("Premature end of hex-encoded char in input stream")); - ch |= unhex(*it) << 4; - ++it; - if (it == end) - throw Exception(gpg_error(GPG_ERR_ASS_SYNTAX), - i18n("Premature end of hex-encoded char in input stream")); - ch |= unhex(*it); - result.push_back(ch); - } else if (*it == '+') { - result += ' '; - } else { - result.push_back(*it); - } - return result; -} - -std::string Kleo::hexencode(const std::string &in) -{ - std::string result; - result.reserve(3 * in.size()); - - static const char hex[] = "0123456789ABCDEF"; - - for (std::string::const_iterator it = in.begin(), end = in.end(); it != end; ++it) - switch (const unsigned char ch = *it) { - default: - if ((ch >= '!' && ch <= '~') || ch > 0xA0) { - result += ch; - break; - } - // else fall through - case ' ': - result += '+'; - break; - case '"': - case '#': - case '$': - case '%': - case '\'': - case '+': - case '=': - result += '%'; - result += hex[(ch & 0xF0) >> 4 ]; - result += hex[(ch & 0x0F) ]; - break; - } - - return result; -} - -std::string Kleo::hexdecode(const char *in) -{ - if (!in) { - return std::string(); - } - return hexdecode(std::string(in)); -} - -std::string Kleo::hexencode(const char *in) -{ - if (!in) { - return std::string(); - } - return hexencode(std::string(in)); -} - -QByteArray Kleo::hexdecode(const QByteArray &in) -{ - if (in.isNull()) { - return QByteArray(); - } - const std::string result = hexdecode(std::string(in.constData())); - return QByteArray(result.data(), result.size()); -} - -QByteArray Kleo::hexencode(const QByteArray &in) -{ - if (in.isNull()) { - return QByteArray(); - } - const std::string result = hexencode(std::string(in.constData())); - return QByteArray(result.data(), result.size()); -} diff --git a/src/utils/hex.h b/src/utils/hex.h deleted file mode 100644 index d9420aadf..000000000 --- a/src/utils/hex.h +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- mode: c++; c-basic-offset:4 -*- - utils/hex.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 - -class QByteArray; - -namespace Kleo -{ - -std::string hexencode(const char *s); -std::string hexdecode(const char *s); - -std::string hexencode(const std::string &s); -std::string hexdecode(const std::string &s); - -QByteArray hexencode(const QByteArray &s); -QByteArray hexdecode(const QByteArray &s); - -} - diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1f0f0a486..86b56294d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,80 +1,79 @@ # SPDX-FileCopyrightText: none # SPDX-License-Identifier: BSD-3-Clause add_subdirectory(gnupg_home) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) include_directories( ${CMAKE_SOURCE_DIR}/src/ ${CMAKE_BINARY_DIR}/src/ ${Boost_INCLUDE_DIRS} ${GPGME_INCLUDES} ) ########### next target ############### set(test_verify_SRCS test_verify.cpp) add_definitions(-DKLEO_TEST_GNUPGHOME="${CMAKE_CURRENT_BINARY_DIR}/gnupg_home") add_definitions(-DKLEO_TEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}") add_executable(test_verify ${test_verify_SRCS}) add_test(NAME test_verify COMMAND test_verify) ecm_mark_as_test(test_verify) target_link_libraries(test_verify KF5::Libkleo Qt${QT_MAJOR_VERSION}::Test QGpgme KF5::CoreAddons KF5::I18n Qt${QT_MAJOR_VERSION}::Widgets ) ########### next target ############### if(USABLE_ASSUAN_FOUND) # this doesn't yet work on Windows add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_2) - set(test_uiserver_SRCS test_uiserver.cpp ${CMAKE_SOURCE_DIR}/src/utils/wsastarter.cpp - ${CMAKE_SOURCE_DIR}/src/utils/hex.cpp) + set(test_uiserver_SRCS test_uiserver.cpp ${CMAKE_SOURCE_DIR}/src/utils/wsastarter.cpp) #FIXME: omitting TEST makes test_uiserver print output again on a Win32 console; # find a better fix for this issue if(WIN32) add_executable(test_uiserver ${test_uiserver_SRCS}) else() add_executable(test_uiserver ${test_uiserver_SRCS}) endif() target_link_libraries(test_uiserver KF5::I18n) if(ASSUAN2_FOUND) target_link_libraries(test_uiserver KF5::Libkleo ${ASSUAN2_LIBRARIES} ) else() target_link_libraries(test_uiserver KF5::Libkleo ${ASSUAN_LIBRARIES} ) endif() if(WIN32) target_link_libraries(test_uiserver ${ASSUAN_VANILLA_LIBRARIES} QGpgme ws2_32 ) else() target_link_libraries(test_uiserver ${ASSUAN_PTHREAD_LIBRARIES} QGpgme ) endif() endif() diff --git a/tests/test_uiserver.cpp b/tests/test_uiserver.cpp index f9e3c4c77..6f7684b5e 100644 --- a/tests/test_uiserver.cpp +++ b/tests/test_uiserver.cpp @@ -1,298 +1,298 @@ /* -*- mode: c++; c-basic-offset:4 -*- tests/test_uiserver.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 */ // // Usage: test_uiserver --verify-detached // #include #include #include +#include #include #include "utils/wsastarter.h" -#include "utils/hex.h" #ifndef Q_OS_WIN32 # include # include # include # include # include #endif #include #include #include #include #include using namespace Kleo; #ifdef Q_OS_WIN32 static const bool HAVE_FD_PASSING = false; #else static const bool HAVE_FD_PASSING = true; #endif static const unsigned int ASSUAN_CONNECT_FLAGS = HAVE_FD_PASSING ? 1 : 0; static std::vector inFDs, outFDs, msgFDs; static std::vector inFiles, outFiles, msgFiles; static std::map inquireData; static void usage(const std::string &msg = std::string()) { std::cerr << msg << std::endl << "\n" "Usage: test_uiserver [] [] [] command []\n" "where:\n" #ifdef Q_OS_WIN32 " : [--input[-fd] ] [--output[-fd] ] [--message[-fd] ]\n" #else " : [--input ] [--output ] [--message ]\n" #endif " : *[--option name=value]\n" " : [--inquire keyword=]\n"; exit(1); } #ifndef HAVE_ASSUAN2 static assuan_error_t data(void *void_ctx, const void *buffer, size_t len) { #else static gpg_error_t data(void *void_ctx, const void *buffer, size_t len) { #endif (void)void_ctx; (void)buffer; (void)len; return 0; // ### implement me } #ifndef HAVE_ASSUAN2 static assuan_error_t status(void *void_ctx, const char *line) { #else static gpg_error_t status(void *void_ctx, const char *line) { #endif (void)void_ctx; (void)line; return 0; } #ifndef HAVE_ASSUAN2 static assuan_error_t inquire(void *void_ctx, const char *keyword) { #else static gpg_error_t inquire(void *void_ctx, const char *keyword) { #endif assuan_context_t ctx = (assuan_context_t)void_ctx; Q_ASSERT(ctx); const std::map::const_iterator it = inquireData.find(keyword); if (it == inquireData.end()) { return gpg_error(GPG_ERR_UNKNOWN_COMMAND); } if (!it->second.empty() && it->second[0] == '@') { return gpg_error(GPG_ERR_NOT_IMPLEMENTED); } if (const gpg_error_t err = assuan_send_data(ctx, it->second.c_str(), it->second.size())) { qDebug("assuan_write_data: %s", gpg_strerror(err)); return err; } return 0; } int main(int argc, char *argv[]) { const Kleo::WSAStarter _wsastarter; #ifndef HAVE_ASSUAN2 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT); #else assuan_set_gpg_err_source(GPG_ERR_SOURCE_DEFAULT); #endif if (argc < 3) { usage(); // need socket and command, at least } const char *socket = argv[1]; std::vector options; std::string command; for (int optind = 2; optind < argc; ++optind) { const char *const arg = argv[optind]; if (qstrcmp(arg, "--input") == 0) { const std::string file = argv[++optind]; inFiles.push_back(file); } else if (qstrcmp(arg, "--output") == 0) { const std::string file = argv[++optind]; outFiles.push_back(file); } else if (qstrcmp(arg, "--message") == 0) { const std::string file = argv[++optind]; msgFiles.push_back(file); #ifndef Q_OS_WIN32 } else if (qstrcmp(arg, "--input-fd") == 0) { int inFD; if ((inFD = open(argv[++optind], O_RDONLY)) == -1) { perror("--input-fd open()"); return 1; } inFDs.push_back(inFD); } else if (qstrcmp(arg, "--output-fd") == 0) { int outFD; if ((outFD = open(argv[++optind], O_WRONLY | O_CREAT, 0666)) == -1) { perror("--output-fd open()"); return 1; } outFDs.push_back(outFD); } else if (qstrcmp(arg, "--message-fd") == 0) { int msgFD; if ((msgFD = open(argv[++optind], O_RDONLY)) == -1) { perror("--message-fd open()"); return 1; } msgFDs.push_back(msgFD); #endif } else if (qstrcmp(arg, "--option") == 0) { options.push_back(argv[++optind]); } else if (qstrcmp(arg, "--inquire") == 0) { const std::string inqval = argv[++optind]; const size_t pos = inqval.find('='); // ### implement indirection with "@file"... inquireData[inqval.substr(0, pos)] = inqval.substr(pos + 1); } else { while (optind < argc) { if (!command.empty()) { command += ' '; } command += argv[optind++]; } } } if (command.empty()) { usage("Command expected, but only options found"); } assuan_context_t ctx = nullptr; #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_socket_connect_ext(&ctx, socket, -1, ASSUAN_CONNECT_FLAGS)) { qDebug("%s", Exception(err, "assuan_socket_connect_ext").what()); #else if (const gpg_error_t err = assuan_new(&ctx)) { qDebug("%s", Exception(err, "assuan_new").what()); return 1; } if (const gpg_error_t err = assuan_socket_connect(ctx, socket, -1, ASSUAN_CONNECT_FLAGS)) { qDebug("%s", Exception(err, "assuan_socket_connect").what()); #endif return 1; } assuan_set_log_stream(ctx, stderr); #ifndef Q_OS_WIN32 for (std::vector::const_iterator it = inFDs.begin(), end = inFDs.end(); it != end; ++it) { if (const gpg_error_t err = assuan_sendfd(ctx, *it)) { qDebug("%s", Exception(err, "assuan_sendfd( inFD )").what()); return 1; } if (const gpg_error_t err = assuan_transact(ctx, "INPUT FD", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) { qDebug("%s", Exception(err, "INPUT FD").what()); return 1; } } for (std::vector::const_iterator it = msgFDs.begin(), end = msgFDs.end(); it != end; ++it) { if (const gpg_error_t err = assuan_sendfd(ctx, *it)) { qDebug("%s", Exception(err, "assuan_sendfd( msgFD )").what()); return 1; } if (const gpg_error_t err = assuan_transact(ctx, "MESSAGE FD", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) { qDebug("%s", Exception(err, "MESSAGE FD").what()); return 1; } } for (std::vector::const_iterator it = outFDs.begin(), end = outFDs.end(); it != end; ++it) { if (const gpg_error_t err = assuan_sendfd(ctx, *it)) { qDebug("%s", Exception(err, "assuan_sendfd( outFD )").what()); return 1; } if (const gpg_error_t err = assuan_transact(ctx, "OUTPUT FD", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) { qDebug("%s", Exception(err, "OUTPUT FD").what()); return 1; } } #endif for (std::vector::const_iterator it = inFiles.begin(), end = inFiles.end(); it != end; ++it) { char buffer[1024]; sprintf(buffer, "INPUT FILE=%s", hexencode(*it).c_str()); if (const gpg_error_t err = assuan_transact(ctx, buffer, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) { qDebug("%s", Exception(err, buffer).what()); return 1; } } for (std::vector::const_iterator it = msgFiles.begin(), end = msgFiles.end(); it != end; ++it) { char buffer[1024]; sprintf(buffer, "MESSAGE FILE=%s", hexencode(*it).c_str()); if (const gpg_error_t err = assuan_transact(ctx, buffer, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) { qDebug("%s", Exception(err, buffer).what()); return 1; } } for (std::vector::const_iterator it = outFiles.begin(), end = outFiles.end(); it != end; ++it) { char buffer[1024]; sprintf(buffer, "OUTPUT FILE=%s", hexencode(*it).c_str()); if (const gpg_error_t err = assuan_transact(ctx, buffer, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) { qDebug("%s", Exception(err, buffer).what()); return 1; } } for (const char *opt : std::as_const(options)) { std::string line = "OPTION "; line += opt; if (const gpg_error_t err = assuan_transact(ctx, line.c_str(), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) { qDebug("%s", Exception(err, line).what()); return 1; } } if (const gpg_error_t err = assuan_transact(ctx, command.c_str(), data, ctx, inquire, ctx, status, ctx)) { qDebug("%s", Exception(err, command).what()); return 1; } #ifndef HAVE_ASSUAN2 assuan_disconnect(ctx); #else assuan_release(ctx); #endif return 0; }