diff options
Diffstat (limited to 'src/core/function/gpg/GpgKeyGroupGetter.cpp')
-rw-r--r-- | src/core/function/gpg/GpgKeyGroupGetter.cpp | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/src/core/function/gpg/GpgKeyGroupGetter.cpp b/src/core/function/gpg/GpgKeyGroupGetter.cpp new file mode 100644 index 00000000..47878414 --- /dev/null +++ b/src/core/function/gpg/GpgKeyGroupGetter.cpp @@ -0,0 +1,311 @@ +/** + * Copyright (C) 2021-2024 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GpgFrontend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#include "GpgKeyGroupGetter.h" + +#include "core/function/CacheManager.h" +#include "core/function/gpg/GpgAbstractKeyGetter.h" +#include "core/struct/cache_object/KeyGroupsCO.h" +#include "utils/GpgUtils.h" + +namespace GpgFrontend { + +GpgKeyGroupGetter::GpgKeyGroupGetter(int channel) + : SingletonFunctionObject<GpgKeyGroupGetter>(channel) { + fetch_key_groups(); +} + +auto GpgKeyGroupGetter::Fetch() -> QContainer<QSharedPointer<GpgKeyGroup>> { + QContainer<QSharedPointer<GpgKeyGroup>> ret; + for (const auto& node : key_groups_forest_) { + ret.push_back(node->key_group); + } + return ret; +} + +void GpgKeyGroupGetter::Remove(const QString& id) { + if (id.isEmpty() || !key_groups_forest_.contains(id)) return; + + auto target_node = key_groups_forest_.value(id); + + for (const auto& node : key_groups_forest_) { + if (target_node == node) continue; + node->RemoveChildren(target_node.get()); + } + + key_groups_forest_.remove(id); + FlushCache(); +} + +void GpgKeyGroupGetter::fetch_key_groups() { + key_groups_forest_.clear(); + auto key = QString("kgs:%1").arg(ctx_.KeyDBName()); + auto json = cm_.LoadDurableCache(key); + + auto key_groups = KeyGroupsCO(json.object()); + if (key_groups.key_db_name != ctx_.KeyDBName()) return; + + for (const auto& key_group : key_groups.key_groups) { + if (key_group.id.isEmpty()) continue; + + LOG_D() << "load raw key group:" << key_group.id + << "key ids: " << key_group.key_ids; + + key_groups_forest_.insert( + key_group.id, + QSharedPointer<GpgKeyGroupTreeNode>::create(GpgKeyGroup{key_group})); + } + + build_gpg_key_group_tree(); + check_all_key_groups(); +} + +void GpgKeyGroupGetter::persist_key_groups() { + auto key = QString("kgs:%1").arg(ctx_.KeyDBName()); + + KeyGroupsCO key_groups; + key_groups.key_db_name = ctx_.KeyDBName(); + + for (const auto& node : key_groups_forest_) { + if (node->key_group == nullptr) continue; + LOG_D() << "persist key group: " << node->key_group->ID() + << "key ids:" << node->key_group->KeyIds(); + key_groups.key_groups.push_back(node->key_group->ToCacheObject()); + } + + cm_.SaveDurableCache(key, QJsonDocument{key_groups.ToJson()}, true); +} + +auto GpgKeyGroupGetter::KeyGroup(const QString& id) + -> QSharedPointer<GpgKeyGroup> { + if (!key_groups_forest_.contains(id)) return nullptr; + return key_groups_forest_.value(id)->key_group; +} + +void GpgKeyGroupGetter::check_key_group( + const QSharedPointer<GpgKeyGroup>& key_group) { + if (key_group == nullptr || key_group->IsDisabled()) return; + + for (const auto& key_id : key_group->KeyIds()) { + LOG_D() << "check" << key_id << "of" << key_group->UID(); + + if (IsKeyGroupID(key_id) || key_id == key_group->ID()) { + if (!key_groups_forest_.contains(key_id)) { + key_group->SetDisabled(true); + return; + } + + auto s_node = key_groups_forest_.value(key_id, nullptr); + check_key_group(s_node->key_group); + + if (s_node->key_group->IsDisabled()) { + key_group->SetDisabled(true); + return; + } + + } else { + auto key = GpgKeyGetter::GetInstance(GetChannel()).GetKeyPtr(key_id); + if (key == nullptr || !key->IsHasEncrCap()) { + key_group->SetDisabled(true); + return; + } + } + } +} + +void GpgKeyGroupGetter::check_all_key_groups() { + for (const auto& node : key_groups_forest_) { + node->key_group->SetDisabled(false); + } + + for (const auto& node : key_groups_forest_) { + auto key_group = node->key_group; + check_key_group(key_group); + + LOG_D() << "key group" << key_group->ID() << "ids: " << key_group->KeyIds() + << "status: " << key_group->IsDisabled(); + } +} + +auto GpgKeyGroupGetter::FlushCache() -> bool { + check_all_key_groups(); + persist_key_groups(); + return true; +} + +void GpgKeyGroupGetter::build_gpg_key_group_tree() { + for (const auto& node : key_groups_forest_) { + LOG_D() << "load key group: " << node->key_group->ID() + << "ids: " << node->key_group->KeyIds(); + for (const auto& key_id : node->key_group->KeyIds()) { + if (!IsKeyGroupID(key_id) || !key_groups_forest_.contains(key_id)) { + continue; + } + + auto target = key_groups_forest_.value(key_id); + if (!node->AddChildren(target.get())) { + LOG_E() << "found ring in key groups relations, key group:" + << node->key_group->ID() << "child: " << key_id; + continue; + } + } + node->Apply(); + } +} + +void GpgKeyGroupGetter::AddKeyGroup(const GpgKeyGroup& key_group) { + auto node = QSharedPointer<GpgKeyGroupTreeNode>::create(key_group); + + key_groups_forest_.insert(node->key_group->ID(), node); + LOG_D() << "add new key group" << key_group.ID() + << "key ids:" << key_group.KeyIds(); + + for (const auto& key_id : node->key_group->KeyIds()) { + if (!IsKeyGroupID(key_id) || !key_groups_forest_.contains(key_id)) continue; + + auto target = key_groups_forest_.value(key_id); + node->AddChildren(target.get()); + } + node->Apply(); + FlushCache(); +} + +auto GpgKeyGroupGetter::AddKey2KeyGroup(const QString& id, + const GpgAbstractKeyPtr& key) -> bool { + if (!key_groups_forest_.contains(id)) return false; + auto key_group = key_groups_forest_.value(id); + + if (key->KeyType() != GpgAbstractKeyType::kGPG_KEYGROUP) { + auto ret = key_group->AddNonKeyGroupKey(key); + FlushCache(); + return ret; + } + + if (!key_groups_forest_.contains(key->ID())) { + LOG_E() << "try adding invalid key group id:" << key->ID(); + return false; + } + + auto s_key_group = key_groups_forest_.value(key->ID()); + auto ret = key_group->AddChildren(s_key_group.get()); + FlushCache(); + return ret; +} + +auto GpgKeyGroupGetter::RemoveKeyFromKeyGroup(const QString& id, + const QString& key_id) -> bool { + if (!key_groups_forest_.contains(id)) return false; + auto key_group = key_groups_forest_.value(id); + + if (!IsKeyGroupID(key_id)) { + LOG_D() << "removing non key group id" << key_id << "form key group" << id; + key_group->RemoveNonKeyGroupKey(key_id); + FlushCache(); + return true; + } + + if (!key_groups_forest_.contains(key_id)) { + LOG_E() << "try remove invalid key group id:" << key_id; + return false; + } + + auto s_key_group = key_groups_forest_.value(key_id); + auto ret = key_group->RemoveChildren(s_key_group.get()); + FlushCache(); + return ret; +} + +GpgKeyGroupTreeNode::GpgKeyGroupTreeNode(GpgKeyGroup kg) + : key_group(QSharedPointer<GpgKeyGroup>::create(kg)) { + for (const auto& key_id : key_group->KeyIds()) { + if (!IsKeyGroupID(key_id)) { + non_key_group_ids.push_back(key_id); + } + } +} + +void GpgKeyGroupTreeNode::Apply() { + QStringList key_ids; + for (const auto& child : children) { + key_ids.push_back(child->key_group->ID()); + } + + for (const auto& key_id : non_key_group_ids) { + key_ids.push_back(key_id); + } + + key_group->SetKeyIds(key_ids); +} + +auto GpgKeyGroupTreeNode::AddNonKeyGroupKey(const GpgAbstractKeyPtr& key) + -> bool { + if (key->KeyType() == GpgAbstractKeyType::kGPG_KEYGROUP || + non_key_group_ids.contains(key->ID())) { + return false; + } + non_key_group_ids.push_back(key->ID()); + non_key_group_ids.removeDuplicates(); + Apply(); + return true; +} + +auto GpgKeyGroupTreeNode::HasAncestor(GpgKeyGroupTreeNode* target) -> bool { + for (const auto& parent : parents) { + if (parent == target || parent->HasAncestor(target)) { + return true; + } + } + return false; +} + +auto GpgKeyGroupTreeNode::AddChildren(GpgKeyGroupTreeNode* target) -> bool { + if (target == this || children.contains(target) || HasAncestor(target)) { + return false; + } + + children.push_back(target); + target->parents.push_back(this); + Apply(); + return true; +} + +auto GpgKeyGroupTreeNode::RemoveChildren(GpgKeyGroupTreeNode* target) -> bool { + if (!children.contains(target)) return false; + children.removeAll(target); + target->parents.removeAll(this); + Apply(); + return true; +} + +auto GpgKeyGroupTreeNode::RemoveNonKeyGroupKey(const QString& key) -> bool { + if (!non_key_group_ids.contains(key)) return false; + non_key_group_ids.removeAll(key); + Apply(); + return true; +} +} // namespace GpgFrontend
\ No newline at end of file |