diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6db92c23c..41769980a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,336 +1,339 @@ # 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/checksumdefinition.h kleo/debug.cpp kleo/debug.h kleo/defaultkeyfilter.cpp kleo/defaultkeyfilter.h kleo/defaultkeygenerationjob.cpp kleo/defaultkeygenerationjob.h kleo/dn.cpp kleo/dn.h kleo/enum.cpp kleo/enum.h kleo/kconfigbasedkeyfilter.cpp kleo/kconfigbasedkeyfilter.h kleo/keyfilter.h kleo/keyfiltermanager.cpp kleo/keyfiltermanager.h kleo/keygroup.cpp kleo/keygroup.h + kleo/keygroupconfig.cpp + kleo/keygroupconfig.h kleo/keyresolver.cpp kleo/keyresolver.h kleo/keyresolvercore.cpp kleo/keyresolvercore.h kleo/keyserverconfig.cpp kleo/keyserverconfig.h kleo/kleoexception.cpp kleo/kleoexception.h kleo/oidmap.cpp kleo/oidmap.h kleo/predicates.h kleo/stl_util.h models/keycache.cpp models/keycache.h models/keycache_p.h models/keylist.h models/keylistmodel.cpp models/keylistmodel.h models/keylistmodelinterface.cpp models/keylistmodelinterface.h models/keylistsortfilterproxymodel.cpp models/keylistsortfilterproxymodel.h models/keyrearrangecolumnsproxymodel.cpp models/keyrearrangecolumnsproxymodel.h models/subkeylistmodel.cpp models/subkeylistmodel.h models/useridlistmodel.cpp models/useridlistmodel.h utils/algorithm.h utils/assuan.cpp utils/assuan.h utils/classify.cpp utils/classify.h utils/compat.cpp utils/compat.h utils/cryptoconfig.cpp utils/cryptoconfig.h utils/cryptoconfig_p.h utils/filesystemwatcher.cpp utils/filesystemwatcher.h utils/formatting.cpp utils/formatting.h utils/gnupg-registry.c utils/gnupg-registry.h utils/gnupg.cpp utils/gnupg.h utils/hex.cpp utils/hex.h utils/qtstlhelpers.cpp utils/qtstlhelpers.h utils/scdaemon.cpp utils/scdaemon.h utils/stringutils.cpp utils/stringutils.h utils/test.cpp utils/test.h ) 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/auditlogviewer.cpp ui/auditlogviewer.h ui/cryptoconfigentryreaderport.cpp ui/cryptoconfigentryreaderport_p.h ui/cryptoconfigmodule.cpp ui/cryptoconfigmodule.h ui/cryptoconfigmodule_p.h ui/directoryserviceswidget.cpp ui/directoryserviceswidget.h ui/dnattributeorderconfigwidget.cpp ui/dnattributeorderconfigwidget.h ui/editdirectoryservicedialog.cpp ui/editdirectoryservicedialog.h ui/filenamerequester.cpp ui/filenamerequester.h ui/kdhorizontalline.cpp ui/kdhorizontalline.h ui/messagebox.cpp ui/messagebox.h ui/progressbar.cpp ui/progressbar.h ui/progressdialog.cpp ui/progressdialog.h ) 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/keyapprovaldialog.cpp ui/keyapprovaldialog.h ui/keylistview.cpp ui/keylistview.h ui/keyrequester.cpp ui/keyrequester.h ui/keyselectioncombo.cpp ui/keyselectioncombo.h ui/keyselectiondialog.cpp ui/keyselectiondialog.h ui/newkeyapprovaldialog.cpp ui/newkeyapprovaldialog.h ) 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 "$") ecm_generate_headers(libkleo_CamelCase_HEADERS HEADER_NAMES ChecksumDefinition Debug DefaultKeyFilter DefaultKeyGenerationJob Dn Enum KConfigBasedKeyFilter KeyFilter KeyFilterManager KeyGroup + KeyGroupConfig 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 Compat CryptoConfig FileSystemWatcher Formatting GnuPG QtStlHelpers SCDaemon StringUtils 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 KeyRequester KeySelectionCombo KeySelectionDialog MessageBox NewKeyApprovalDialog 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/kleo/keygroupconfig.cpp b/src/kleo/keygroupconfig.cpp new file mode 100644 index 000000000..b929088ce --- /dev/null +++ b/src/kleo/keygroupconfig.cpp @@ -0,0 +1,190 @@ +/* + kleo/keygroupconfig.cpp + + This file is part of libkleopatra, the KDE keymanagement library + SPDX-FileCopyrightText: 2021 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "keygroupconfig.h" + +#include "debug.h" +#include "keygroup.h" + +#include "models/keycache.h" +#include "utils/qtstlhelpers.h" + +#include +#include + +#include + +#include + +#include "libkleo_debug.h" + +using namespace Kleo; +using namespace GpgME; + +static const QString groupNamePrefix = QStringLiteral("Group-"); + +class KeyGroupConfig::Private +{ +public: + explicit Private(const QString &filename); + + std::vector readGroups() const; + KeyGroup writeGroup(const KeyGroup &group); + bool removeGroup(const KeyGroup &group); + +private: + KeyGroup readGroup(const KSharedConfigPtr &groupsConfig, const QString &groupId) const; + +private: + QString filename; +}; + +KeyGroupConfig::Private::Private(const QString &filename) + : filename{filename} +{ + if (filename.isEmpty()) { + qCWarning(LIBKLEO_LOG) << __func__ << "Warning: name of configuration file is empty"; + } +} + +KeyGroup KeyGroupConfig::Private::readGroup(const KSharedConfigPtr &groupsConfig, const QString &groupId) const +{ + const KConfigGroup configGroup = groupsConfig->group(groupNamePrefix + groupId); + + const QString groupName = configGroup.readEntry("Name", QString()); + const auto fingerprints = toStdStrings(configGroup.readEntry("Keys", QStringList())); + const std::vector groupKeys = KeyCache::instance()->findByFingerprint(fingerprints); + + // treat group as immutable if any of its entries is immutable + const QStringList entries = configGroup.keyList(); + const bool isImmutable = configGroup.isImmutable() || std::any_of(entries.begin(), entries.end(), + [configGroup] (const QString &entry) { + return configGroup.isEntryImmutable(entry); + }); + + KeyGroup g(groupId, groupName, groupKeys, KeyGroup::ApplicationConfig); + g.setIsImmutable(isImmutable); + qCDebug(LIBKLEO_LOG) << "Read group" << g; + + return g; +} + +std::vector KeyGroupConfig::Private::readGroups() const +{ + std::vector groups; + + if (filename.isEmpty()) { + return groups; + } + + const KSharedConfigPtr groupsConfig = KSharedConfig::openConfig(filename); + const QStringList configGroups = groupsConfig->groupList(); + for (const QString &configGroupName : configGroups) { + qCDebug(LIBKLEO_LOG) << "Reading config group" << configGroupName; + if (configGroupName.startsWith(groupNamePrefix)) { + const QString keyGroupId = configGroupName.mid(groupNamePrefix.size()); + if (keyGroupId.isEmpty()) { + qCWarning(LIBKLEO_LOG) << "Config group" << configGroupName << "has empty group id"; + continue; + } + KeyGroup group = readGroup(groupsConfig, keyGroupId); + groups.push_back(group); + } + } + + return groups; +} + +namespace +{ +auto getFingerprints(const KeyGroup::Keys &keys) +{ + QStringList fingerprints; + + fingerprints.reserve(keys.size()); + std::transform(std::begin(keys), std::end(keys), + std::back_inserter(fingerprints), + [](const auto &key) { + return QString::fromLatin1(key.primaryFingerprint()); + }); + + return fingerprints; +} +} + +KeyGroup KeyGroupConfig::Private::writeGroup(const KeyGroup &group) +{ + if (filename.isEmpty()) { + return {}; + } + + if (group.isNull()) { + qCDebug(LIBKLEO_LOG) << __func__ << "Error: group is null"; + return group; + } + + KSharedConfigPtr groupsConfig = KSharedConfig::openConfig(filename); + KConfigGroup configGroup = groupsConfig->group(groupNamePrefix + group.id()); + + qCDebug(LIBKLEO_LOG) << __func__ << "Writing config group" << configGroup.name(); + configGroup.writeEntry("Name", group.name()); + configGroup.writeEntry("Keys", getFingerprints(group.keys())); + + // reread group to ensure that it reflects the saved group in case of immutable entries + return readGroup(groupsConfig, group.id()); +} + +bool KeyGroupConfig::Private::removeGroup(const KeyGroup &group) +{ + if (filename.isEmpty()) { + return false; + } + + if (group.isNull()) { + qCDebug(LIBKLEO_LOG) << __func__ << "Error: group is null"; + return false; + } + + KSharedConfigPtr groupsConfig = KSharedConfig::openConfig(filename); + KConfigGroup configGroup = groupsConfig->group(groupNamePrefix + group.id()); + + qCDebug(LIBKLEO_LOG) << __func__ << "Removing config group" << configGroup.name(); + configGroup.deleteGroup(); + + return true; +} + +KeyGroupConfig::KeyGroupConfig(const QString &filename) + : d{std::make_unique(filename)} +{ +} + +KeyGroupConfig::~KeyGroupConfig() = default; + +std::vector KeyGroupConfig::readGroups() const +{ + return d->readGroups(); +} + +KeyGroup KeyGroupConfig::writeGroup(const KeyGroup &group) +{ + return d->writeGroup(group); +} + +void KeyGroupConfig::writeGroups(const std::vector &groups) +{ + std::for_each(std::begin(groups), std::end(groups), + [this](const auto &group) { d->writeGroup(group); }); +} + +bool KeyGroupConfig::removeGroup(const KeyGroup &group) +{ + return d->removeGroup(group); +} diff --git a/src/kleo/keygroupconfig.h b/src/kleo/keygroupconfig.h new file mode 100644 index 000000000..f67f0dcc5 --- /dev/null +++ b/src/kleo/keygroupconfig.h @@ -0,0 +1,48 @@ +/* + kleo/keygroupconfig.h + + This file is part of libkleopatra, the KDE keymanagement library + SPDX-FileCopyrightText: 2021 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include "kleo_export.h" + +#include +#include + +class QString; + +namespace GpgME +{ +class Key; +} + +namespace Kleo +{ +class KeyGroup; + +class KLEO_EXPORT KeyGroupConfig +{ +public: + explicit KeyGroupConfig(const QString &filename); + ~KeyGroupConfig(); + + std::vector readGroups() const; + + void writeGroups(const std::vector &groups); + + KeyGroup writeGroup(const KeyGroup &group); + + bool removeGroup(const KeyGroup &group); + +private: + class Private; + std::unique_ptr d; +}; + +} diff --git a/src/models/keycache.cpp b/src/models/keycache.cpp index a0bb6f1d0..95f0e70a9 100644 --- a/src/models/keycache.cpp +++ b/src/models/keycache.cpp @@ -1,1826 +1,1751 @@ /* -*- mode: c++; c-basic-offset:4 -*- models/keycache.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007, 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2018 Intevation GmbH SPDX-FileCopyrightText: 2020 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "keycache.h" #include "keycache_p.h" #include "kleo/enum.h" #include "kleo/keygroup.h" +#include "kleo/keygroupconfig.h" #include "kleo/predicates.h" #include "kleo/stl_util.h" #include "kleo/dn.h" #include "utils/compat.h" #include "utils/filesystemwatcher.h" +#include "utils/qtstlhelpers.h" #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include #include #include "kleo/debug.h" #include "libkleo_debug.h" #include + using namespace std::chrono_literals; using namespace Kleo; using namespace GpgME; using namespace KMime::Types; static const unsigned int hours2ms = 1000 * 60 * 60; -static const QString groupNamePrefix = QStringLiteral("Group-"); - // // // KeyCache // // namespace { make_comparator_str(ByEMail, .first.c_str()); -QStringList getFingerprints(const KeyGroup::Keys &keys) -{ - QStringList fingerprints; - fingerprints.reserve(keys.size()); - std::transform(keys.cbegin(), keys.cend(), - std::back_inserter(fingerprints), - [] (const Key &key) { - return QString::fromLatin1(key.primaryFingerprint()); - }); - return fingerprints; -} - } class KeyCache::Private { friend class ::Kleo::KeyCache; KeyCache *const q; public: explicit Private(KeyCache *qq) : q(qq), m_refreshInterval(1), m_initalized(false), m_pgpOnly(true), m_remarks_enabled(false) { connect(&m_autoKeyListingTimer, &QTimer::timeout, q, [this]() { q->startKeyListing(); }); updateAutoKeyListingTimer(); } ~Private() { if (m_refreshJob) { m_refreshJob->cancel(); } } template < template