File indexing completed on 2024-11-17 04:50:27

0001 /*
0002     kleo/keygroupimportexport.cpp
0003 
0004     This file is part of libkleopatra, the KDE keymanagement library
0005     SPDX-FileCopyrightText: 2021 g10 Code GmbH
0006     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include <config-libkleo.h>
0012 
0013 #include "keygroupimportexport.h"
0014 
0015 #include "debug.h"
0016 #include "keygroup.h"
0017 
0018 #include <libkleo/keycache.h>
0019 #include <libkleo/keyhelpers.h>
0020 #include <libkleo/qtstlhelpers.h>
0021 
0022 #include <libkleo_debug.h>
0023 
0024 #include <QFile>
0025 #include <QSettings>
0026 #include <QString>
0027 
0028 using namespace Kleo;
0029 using namespace GpgME;
0030 
0031 // use a different, less generic prefix for the config group names than in
0032 // KeyGroupConfig to avoid problems with "Group-*" config groups created by
0033 // other applications; this means that the key groups stored in the normal group
0034 // configuration file cannot be read with the below functions, but that's a good
0035 // thing because the ini files created by KConfig are incompatible with QSettings
0036 static const QString keyGroupNamePrefix = QStringLiteral("KeyGroup-");
0037 
0038 namespace
0039 {
0040 
0041 QString readString(const QSettings &settings, const QString &key)
0042 {
0043     return settings.value(key, QString{}).toString();
0044 }
0045 
0046 QStringList readStringList(const QSettings &settings, const QString &key)
0047 {
0048     auto variant = settings.value(key);
0049     if (!variant.isValid()) {
0050         return {};
0051     }
0052     if ((variant.userType() == QMetaType::QString) && variant.toString().isEmpty()) {
0053         // interpret empty string value as empty list instead of as list with an empty string
0054         return {};
0055     }
0056     // opportunistically, interpret the value as string list
0057     return variant.toStringList();
0058 }
0059 
0060 void writeString(QSettings &settings, const QString &key, const QString &string)
0061 {
0062     settings.setValue(key, string);
0063 }
0064 
0065 void writeStringList(QSettings &settings, const QString &key, const QStringList &list)
0066 {
0067     // write empty list as empty string to avoid Qt's "@Invalid()"
0068     if (list.empty()) {
0069         writeString(settings, key, {});
0070     } else {
0071         settings.setValue(key, list);
0072     }
0073 }
0074 
0075 KeyGroup readGroup(const QSettings &groupsConfig, const QString &groupId)
0076 {
0077     const QString configGroupPath = keyGroupNamePrefix + groupId + QLatin1Char{'/'};
0078 
0079     const auto groupName = readString(groupsConfig, configGroupPath + QLatin1StringView{"Name"});
0080     const auto fingerprints = readStringList(groupsConfig, configGroupPath + QLatin1StringView{"Keys"});
0081     const std::vector<Key> groupKeys = KeyCache::instance()->findByFingerprint(toStdStrings(fingerprints));
0082 
0083     KeyGroup g(groupId, groupName, groupKeys, KeyGroup::ApplicationConfig);
0084     qCDebug(LIBKLEO_LOG) << __func__ << "Read group" << g;
0085 
0086     return g;
0087 }
0088 
0089 void writeGroup(QSettings &groupsConfig, const KeyGroup &group)
0090 {
0091     if (group.isNull()) {
0092         qCDebug(LIBKLEO_LOG) << __func__ << "Error: group is null";
0093         return;
0094     }
0095 
0096     const QString configGroupName = keyGroupNamePrefix + group.id();
0097     qCDebug(LIBKLEO_LOG) << __func__ << "Writing config group" << configGroupName;
0098     const QString configGroupPath = configGroupName + QLatin1Char{'/'};
0099     writeString(groupsConfig, configGroupPath + QLatin1StringView{"Name"}, group.name());
0100     writeStringList(groupsConfig, configGroupPath + QLatin1StringView{"Keys"}, Kleo::getFingerprints(group.keys()));
0101 }
0102 
0103 } // namespace
0104 
0105 std::vector<KeyGroup> Kleo::readKeyGroups(const QString &filename)
0106 {
0107     std::vector<KeyGroup> groups;
0108 
0109     if (filename.isEmpty()) {
0110         return groups;
0111     }
0112 
0113     if (!QFile::exists(filename)) {
0114         qCWarning(LIBKLEO_LOG) << __func__ << "File" << filename << "does not exist";
0115         return groups;
0116     }
0117 
0118     const QSettings groupsConfig{filename, QSettings::IniFormat};
0119     const QStringList configGroups = groupsConfig.childGroups();
0120     for (const QString &configGroupName : configGroups) {
0121         if (configGroupName.startsWith(keyGroupNamePrefix)) {
0122             qCDebug(LIBKLEO_LOG) << __func__ << "Reading config group" << configGroupName;
0123             const QString keyGroupId = configGroupName.mid(keyGroupNamePrefix.size());
0124             if (keyGroupId.isEmpty()) {
0125                 qCWarning(LIBKLEO_LOG) << __func__ << "Config group" << configGroupName << "has empty group id";
0126                 continue;
0127             }
0128             groups.push_back(readGroup(groupsConfig, keyGroupId));
0129         }
0130     }
0131 
0132     return groups;
0133 }
0134 
0135 Kleo::WriteKeyGroups Kleo::writeKeyGroups(const QString &filename, const std::vector<KeyGroup> &groups)
0136 {
0137     if (filename.isEmpty()) {
0138         return WriteKeyGroups::InvalidFilename;
0139     }
0140 
0141     QSettings groupsConfig{filename, QSettings::IniFormat};
0142     for (const auto &group : groups) {
0143         writeGroup(groupsConfig, group);
0144     }
0145     // ensure that the data is written to disk before calling status()
0146     groupsConfig.sync();
0147     qCDebug(LIBKLEO_LOG) << __func__ << "groupsConfig.status():" << groupsConfig.status();
0148     return groupsConfig.status() == QSettings::NoError ? WriteKeyGroups::Success : WriteKeyGroups::Error;
0149 }