diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ba85f6cb4..69c3b146c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,271 +1,273 @@ # target_include_directories does not handle empty include paths include_directories( ${Boost_INCLUDE_DIRS} ${GPGME_INCLUDES} ) add_definitions(-DTRANSLATION_DOMAIN=\"libkleopatra\") #add_definitions( -DQT_NO_CAST_FROM_ASCII ) #add_definitions( -DQT_NO_CAST_TO_ASCII ) kde_enable_exceptions() add_definitions( -DGPGMEPP_ERR_SOURCE_DEFAULT=13 ) # 13 is GPG_ERR_SOURCE_KLEO, even if gpg-error's too old to know about add_subdirectory( pics ) if (BUILD_TESTING) add_subdirectory( tests ) endif() add_library(KF5Libkleo) add_library(KF5::Libkleo ALIAS KF5Libkleo) ########### next target ############### target_sources(KF5Libkleo PRIVATE kleo/checksumdefinition.cpp kleo/debug.cpp kleo/defaultkeyfilter.cpp kleo/defaultkeygenerationjob.cpp kleo/dn.cpp kleo/enum.cpp kleo/kconfigbasedkeyfilter.cpp kleo/keyfiltermanager.cpp kleo/keygroup.cpp kleo/keyresolver.cpp kleo/keyresolvercore.cpp kleo/keyserverconfig.cpp kleo/kleoexception.cpp kleo/oidmap.cpp models/keycache.cpp models/keylistmodel.cpp models/keylistmodelinterface.cpp models/keylistsortfilterproxymodel.cpp models/keyrearrangecolumnsproxymodel.cpp models/subkeylistmodel.cpp models/useridlistmodel.cpp + utils/assuan.cpp utils/filesystemwatcher.cpp utils/formatting.cpp utils/classify.cpp utils/gnupg.cpp utils/gnupg-registry.c utils/hex.cpp utils/compat.cpp utils/cryptoconfig.cpp utils/test.cpp ) ecm_qt_declare_logging_category(KF5Libkleo HEADER libkleo_debug.h IDENTIFIER LIBKLEO_LOG CATEGORY_NAME org.kde.pim.libkleo DESCRIPTION "libkleo (kleo_core)" EXPORT LIBKLEO ) target_sources(KF5Libkleo PRIVATE ui/dnattributeorderconfigwidget.cpp ui/kdhorizontalline.cpp ui/filenamerequester.cpp ui/messagebox.cpp ui/cryptoconfigmodule.cpp ui/directoryserviceswidget.cpp ui/progressbar.cpp ui/progressdialog.cpp ui/auditlogviewer.cpp ui/editdirectoryservicedialog.cpp ) ecm_qt_declare_logging_category(KF5Libkleo HEADER kleo_ui_debug.h IDENTIFIER KLEO_UI_LOG CATEGORY_NAME org.kde.pim.kleo_ui DESCRIPTION "libkleo (kleo_ui)" OLD_CATEGORY_NAMES log_kleo_ui EXPORT LIBKLEO ) target_sources(KF5Libkleo PRIVATE # make this a separate lib. ui/keylistview.cpp ui/keyselectiondialog.cpp ui/keyrequester.cpp ui/keyapprovaldialog.cpp ui/newkeyapprovaldialog.cpp ui/keyselectioncombo.cpp ) target_link_libraries(KF5Libkleo PUBLIC QGpgme Gpgmepp PRIVATE Qt::Widgets KF5::I18n KF5::Completion KF5::ConfigCore KF5::CoreAddons KF5::WidgetsAddons KF5::ItemModels KF5::Codecs) # Boost::headers may not be available for old versions of Boost if (TARGET Boost::headers) target_link_libraries(KF5Libkleo PRIVATE Boost::headers) endif() if (KF5PimTextEdit_FOUND) add_definitions(-DHAVE_PIMTEXTEDIT) target_link_libraries(KF5Libkleo PRIVATE KF5::PimTextEdit) endif() if (COMPILE_WITH_UNITY_CMAKE_SUPPORT) set_target_properties(KF5Libkleo PROPERTIES UNITY_BUILD ON) endif() generate_export_header(KF5Libkleo BASE_NAME kleo) if(WIN32) target_link_libraries(KF5Libkleo ${GPGME_VANILLA_LIBRARIES} ) endif() set_target_properties(KF5Libkleo PROPERTIES VERSION ${LIBKLEO_VERSION} SOVERSION ${LIBKLEO_SOVERSION} EXPORT_NAME Libkleo ) install(TARGETS KF5Libkleo EXPORT KF5LibkleoTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS} ) target_include_directories(KF5Libkleo PUBLIC "$") target_include_directories(KF5Libkleo INTERFACE "$") ecm_generate_headers(libkleo_CamelCase_HEADERS HEADER_NAMES ChecksumDefinition Debug DefaultKeyFilter DefaultKeyGenerationJob Dn Enum KConfigBasedKeyFilter KeyFilter KeyFilterManager KeyGroup KeyResolver KeyResolverCore KeyserverConfig KleoException OidMap Predicates Stl_Util REQUIRED_HEADERS libkleo_HEADERS PREFIX Libkleo RELATIVE kleo ) ecm_generate_headers(libkleo_CamelCase_models_HEADERS HEADER_NAMES KeyCache KeyList KeyListModel KeyListModelInterface KeyListSortFilterProxyModel KeyRearrangeColumnsProxyModel SubkeyListModel UserIDListModel REQUIRED_HEADERS libkleo_models_HEADERS PREFIX Libkleo RELATIVE models ) ecm_generate_headers(libkleo_CamelCase_utils_HEADERS HEADER_NAMES Algorithm + Assuan Classify CryptoConfig FileSystemWatcher Formatting GnuPG Compat Test REQUIRED_HEADERS libkleo_utils_HEADERS PREFIX Libkleo RELATIVE utils ) ecm_generate_headers(libkleo_CamelCase_ui_HEADERS HEADER_NAMES CryptoConfigModule DNAttributeOrderConfigWidget DirectoryServicesWidget EditDirectoryServiceDialog FileNameRequester KDHorizontalLine KeyApprovalDialog NewKeyApprovalDialog KeyRequester KeySelectionCombo KeySelectionDialog MessageBox ProgressDialog REQUIRED_HEADERS libkleo_ui_HEADERS PREFIX Libkleo RELATIVE ui ) ecm_generate_pri_file(BASE_NAME Libkleo LIB_NAME KF5Libkleo DEPS "QGpgme" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/Libkleo ) install(FILES ${libkleo_CamelCase_HEADERS} ${libkleo_CamelCase_models_HEADERS} ${libkleo_CamelCase_ui_HEADERS} ${libkleo_CamelCase_utils_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/Libkleo COMPONENT Devel ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kleo_export.h ${libkleo_HEADERS} ${libkleo_models_HEADERS} ${libkleo_ui_HEADERS} ${libkleo_utils_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/libkleo COMPONENT Devel ) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) if ( WIN32 ) install ( FILES libkleopatrarc-win32.desktop DESTINATION ${KDE_INSTALL_CONFDIR} RENAME libkleopatrarc ) else () install ( FILES libkleopatrarc.desktop DESTINATION ${KDE_INSTALL_CONFDIR} RENAME libkleopatrarc ) endif () if (BUILD_QCH) ecm_add_qch( KF5Libkleo_QCH NAME KF5Libkleo BASE_NAME KF5Libkleo VERSION ${PIM_VERSION} ORG_DOMAIN org.kde SOURCES # using only public headers, to cover only public API ${libkleo_HEADERS} ${libkleo_models_HEADERS} ${libkleo_ui_HEADERS} ${libkleo_utils_HEADERS} #MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md" #IMAGE_DIRS "${CMAKE_SOURCE_DIR}/docs/pics" LINK_QCHS Qt5Core_QCH Qt5Gui_QCH Qt5Widgets_QCH INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR} BLANK_MACROS KLEO_EXPORT TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} COMPONENT Devel ) endif() diff --git a/src/utils/assuan.cpp b/src/utils/assuan.cpp new file mode 100644 index 000000000..1cd56c918 --- /dev/null +++ b/src/utils/assuan.cpp @@ -0,0 +1,120 @@ +/* + utils/assuan.cpp + + This file is part of libkleopatra + SPDX-FileCopyrightText: 2021 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include + +#include "assuan.h" + +#include + +#if __has_include() +# include +#endif + +#include +#include +#include + +#include "libkleo_debug.h" + +using namespace GpgME; +using namespace Kleo; +using namespace Kleo::Assuan; + +namespace +{ +static QDebug operator<<(QDebug s, const std::string &string) +{ + return s << QString::fromStdString(string); +} + +static QDebug operator<<(QDebug s, const std::vector< std::pair > &v) +{ + using pair = std::pair; + s << '('; + for (const pair &p : v) { + s << "status(" << QString::fromStdString(p.first) << ") =" << QString::fromStdString(p.second) << '\n'; + } + return s << ')'; +} +} + +std::unique_ptr Kleo::Assuan::sendCommand(std::shared_ptr &context, const std::string &command, std::unique_ptr transaction, GpgME::Error &err) +{ + qCDebug(LIBKLEO_LOG) << __func__ << command; + err = context->assuanTransact(command.c_str(), std::move(transaction)); + + static int cnt = 0; + while (err.code() == GPG_ERR_ASS_CONNECT_FAILED && cnt < 5) { + // Esp. on Windows the agent processes may take their time so we try + // in increasing waits for them to start up + qCDebug(LIBKLEO_LOG) << "Waiting for the daemons to start up"; + cnt++; + QThread::msleep(250 * cnt); + err = context->assuanTransact(command.c_str(), context->takeLastAssuanTransaction()); + } + if (err.code()) { + qCDebug(LIBKLEO_LOG) << __func__ << command << "failed:" << err; + if (err.code() >= GPG_ERR_ASS_GENERAL && err.code() <= GPG_ERR_ASS_UNKNOWN_INQUIRE) { + qCDebug(LIBKLEO_LOG) << "Assuan problem, killing context"; + context.reset(); + } + return {}; + } + return context->takeLastAssuanTransaction(); +} + +std::unique_ptr Kleo::Assuan::sendCommand(std::shared_ptr &context, const std::string &command, Error &err) +{ + std::unique_ptr t = sendCommand(context, command, std::make_unique(), err); + return std::unique_ptr(dynamic_cast(t.release())); +} + +std::string Kleo::Assuan::sendDataCommand(std::shared_ptr context, const std::string &command, Error &err) +{ + std::string data; + const std::unique_ptr t = sendCommand(context, command, err); + if (t.get()) { + data = t->data(); + qCDebug(LIBKLEO_LOG) << __func__ << command << ": got" << QString::fromStdString(data); + } else { + qCDebug(LIBKLEO_LOG) << __func__ << command << ": t == NULL"; + } + return data; +} + +std::vector> Kleo::Assuan::sendStatusLinesCommand(std::shared_ptr context, const std::string &command, Error &err) +{ + std::vector> statusLines; + const std::unique_ptr t = sendCommand(context, command, err); + if (t.get()) { + statusLines = t->statusLines(); + qCDebug(LIBKLEO_LOG) << __func__ << command << ": got" << statusLines; + } else { + qCDebug(LIBKLEO_LOG) << __func__ << command << ": t == NULL"; + } + return statusLines; +} + +std::string Kleo::Assuan::sendStatusCommand(const std::shared_ptr &context, const std::string &command, Error &err) +{ + const auto lines = sendStatusLinesCommand(context, command, err); + // The status is only the last attribute + // e.g. for SCD SERIALNO it would only be "SERIALNO" and for SCD GETATTR FOO + // it would only be FOO + const auto lastSpace = command.rfind(' '); + const auto needle = lastSpace == std::string::npos ? command : command.substr(lastSpace + 1); + for (const auto &pair: lines) { + if (pair.first == needle) { + return pair.second; + } + } + return {}; +} diff --git a/src/utils/assuan.h b/src/utils/assuan.h new file mode 100644 index 000000000..7280f8d4d --- /dev/null +++ b/src/utils/assuan.h @@ -0,0 +1,64 @@ +/* + utils/assuan.h + + This file is part of libkleopatra + SPDX-FileCopyrightText: 2021 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include +#include + +#include "kleo_export.h" + +namespace GpgME +{ +class AssuanTransaction; +class Context; +class DefaultAssuanTransaction; +class Error; +} + +namespace Kleo +{ +/** The Assuan namespace collects functions for communicating with the GnuPG + * agent via the Assuan protocol. */ +namespace Assuan +{ + +/** Sends the Assuan @p command using the @p transaction and the @p assuanContext + * to the GnuPG agent and waits for the result. The returned transaction can be used + * to retrieve the result. + * If an error occurred, then @p err provides details. */ +KLEO_EXPORT std::unique_ptr sendCommand(std::shared_ptr &assuanContext, const std::string &command, std::unique_ptr transaction, GpgME::Error &err); + +/** Sends the Assuan @p command using a default Assuan transaction and the @p assuanContext + * to the GnuPG agent and waits for the result. The returned transaction can be used + * to retrieve the result. + * If an error occurred, then @p err provides details. */ +KLEO_EXPORT std::unique_ptr sendCommand(std::shared_ptr &assuanContext, const std::string &command, GpgME::Error &err); + +/** Sends the Assuan @p command using a default Assuan transaction and the @p assuanContext + * to the GnuPG agent and waits for the result. Returns the data that was sent by + * GnuPG agent in response to the @p command. + * If an error occurred, then @p err provides details. */ +KLEO_EXPORT std::string sendDataCommand(std::shared_ptr assuanContext, const std::string &command, GpgME::Error &err); + +/** Sends the Assuan @p command using a default Assuan transaction and the @p assuanContext + * to the GnuPG agent and waits for the result. Returns the status lines that were sent by + * GnuPG agent in response to the @p command. + * If an error occurred, then @p err provides details. */ +KLEO_EXPORT std::vector> sendStatusLinesCommand(std::shared_ptr assuanContext, const std::string &command, GpgME::Error &err); + +/** Sends the Assuan @p command using a default Assuan transaction and the @p assuanContext + * to the GnuPG agent and waits for the result. Returns the status that was sent by + * GnuPG agent in response to the @p command. + * If an error occurred, then @p err provides details. */ +KLEO_EXPORT std::string sendStatusCommand(const std::shared_ptr &assuanContext, const std::string &command, GpgME::Error &err); + +} +}