diff options
author | saturneric <[email protected]> | 2024-11-26 22:04:00 +0000 |
---|---|---|
committer | saturneric <[email protected]> | 2024-11-26 22:07:04 +0000 |
commit | af776283bd5d50363c8777dbb3524fde417cc368 (patch) | |
tree | a633e2e8161dc7b9048af25d81e284c116e217cc /src/core | |
parent | feat: support eml data decryption (diff) | |
download | GpgFrontend-af776283bd5d50363c8777dbb3524fde417cc368.tar.gz GpgFrontend-af776283bd5d50363c8777dbb3524fde417cc368.zip |
feat: support uid revoke and uid delete operations
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/function/gpg/GpgAutomatonHandler.cpp | 157 | ||||
-rw-r--r-- | src/core/function/gpg/GpgAutomatonHandler.h | 110 | ||||
-rw-r--r-- | src/core/function/gpg/GpgKeyManager.cpp | 327 | ||||
-rw-r--r-- | src/core/function/gpg/GpgKeyManager.h | 57 | ||||
-rw-r--r-- | src/core/function/gpg/GpgUIDOperator.cpp | 220 | ||||
-rw-r--r-- | src/core/function/gpg/GpgUIDOperator.h | 25 |
6 files changed, 632 insertions, 264 deletions
diff --git a/src/core/function/gpg/GpgAutomatonHandler.cpp b/src/core/function/gpg/GpgAutomatonHandler.cpp new file mode 100644 index 00000000..9b318e02 --- /dev/null +++ b/src/core/function/gpg/GpgAutomatonHandler.cpp @@ -0,0 +1,157 @@ +/** + * 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 "GpgAutomatonHandler.h" + +#include <utility> + +#include "core/model/GpgData.h" +#include "core/model/GpgKey.h" +#include "core/utils/GpgUtils.h" + +namespace GpgFrontend { + +GpgAutomatonHandler::GpgAutomatonHandler(int channel) + : SingletonFunctionObject<GpgAutomatonHandler>(channel) {} + +auto GpgAutomatonHandler::interator_cb_func(void* handle, const char* status, + const char* args, + int fd) -> gpgme_error_t { + auto* handle_struct = static_cast<AutomatonHandelStruct*>(handle); + QString status_s = status; + QString args_s = args; + + if (status_s == "KEY_CONSIDERED") { + auto tokens = QString(args).split(' '); + + if (tokens.empty() || tokens[0] != handle_struct->KeyFpr()) { + LOG_W() << "handle struct key fpr " << handle_struct->KeyFpr() + << "mismatch token: " << tokens[0] << ", exit..."; + + return -1; + } + + return 0; + } + + if (status_s == "GOT_IT" || status_s.isEmpty()) { + FLOG_D("gpg reply is GOT_IT, continue..."); + return 0; + } + + LOG_D() << "current state" << handle_struct->CurrentStatus() + << "gpg status: " << status_s << ", args: " << args_s; + + AutomatonState next_state = handle_struct->NextState(status_s, args_s); + if (next_state == AS_ERROR) { + FLOG_D("handle struct next state caught error, abort..."); + return -1; + } + LOG_D() << "next state" << next_state; + + if (next_state == AS_SAVE) { + handle_struct->SetSuccess(true); + } + + // set state and preform action + handle_struct->SetStatus(next_state); + Command cmd = handle_struct->Action(); + + LOG_D() << "next action, cmd:" << cmd; + + if (!cmd.isEmpty()) { + auto btye_array = cmd.toUtf8(); + gpgme_io_write(fd, btye_array, btye_array.size()); + gpgme_io_write(fd, "\n", 1); + } else if (status_s == "GET_LINE") { + // avoid trapping in this state + return GPG_ERR_FALSE; + } + + return 0; +} + +auto GpgAutomatonHandler::DoInteract( + const GpgKey& key, AutomatonNextStateHandler next_state_handler, + AutomatonActionHandler action_handler) -> bool { + auto key_fpr = key.GetFingerprint(); + AutomatonHandelStruct handel_struct(key_fpr); + handel_struct.SetHandler(std::move(next_state_handler), + std::move(action_handler)); + + GpgData data_out; + + auto err = + gpgme_op_interact(ctx_.DefaultContext(), static_cast<gpgme_key_t>(key), 0, + GpgAutomatonHandler::interator_cb_func, + static_cast<void*>(&handel_struct), data_out); + return CheckGpgError(err) == GPG_ERR_NO_ERROR && handel_struct.Success(); +} + +auto GpgAutomatonHandler::AutomatonHandelStruct::NextState( + QString gpg_status, QString args) -> AutomatonState { + return next_state_handler_(current_state_, std::move(gpg_status), + std::move(args)); +} + +void GpgAutomatonHandler::AutomatonHandelStruct::SetHandler( + AutomatonNextStateHandler next_state_handler, + AutomatonActionHandler action_handler) { + next_state_handler_ = std::move(next_state_handler); + action_handler_ = std::move(action_handler); +} + +auto GpgAutomatonHandler::AutomatonHandelStruct::CurrentStatus() + -> AutomatonState { + return current_state_; +} + +void GpgAutomatonHandler::AutomatonHandelStruct::SetStatus( + AutomatonState next_state) { + current_state_ = next_state; +} + +auto GpgAutomatonHandler::AutomatonHandelStruct::Action() -> Command { + return action_handler_(*this, current_state_); +} + +void GpgAutomatonHandler::AutomatonHandelStruct::SetSuccess(bool success) { + success_ = success; +} + +auto GpgAutomatonHandler::AutomatonHandelStruct::Success() const -> bool { + return success_; +} +auto GpgAutomatonHandler::AutomatonHandelStruct::KeyFpr() -> QString { + return key_fpr_; +} + +GpgAutomatonHandler::AutomatonHandelStruct::AutomatonHandelStruct( + QString key_fpr) + : key_fpr_(std::move(key_fpr)) {} +} // namespace GpgFrontend
\ No newline at end of file diff --git a/src/core/function/gpg/GpgAutomatonHandler.h b/src/core/function/gpg/GpgAutomatonHandler.h new file mode 100644 index 00000000..075edb82 --- /dev/null +++ b/src/core/function/gpg/GpgAutomatonHandler.h @@ -0,0 +1,110 @@ + + +/** + * 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 + * + */ + +#pragma once + +#include "core/GpgFrontendCore.h" +#include "core/function/basic/GpgFunctionObject.h" +#include "core/function/gpg/GpgContext.h" +#include "core/typedef/GpgTypedef.h" + +namespace GpgFrontend { + +class GpgAutomatonHandler + : public SingletonFunctionObject<GpgAutomatonHandler> { + public: + using Command = QString; + using AutomatonState = enum { + AS_START, + AS_SELECT, + AS_COMMAND, + AS_VALUE, + AS_REASON_CODE, + AS_REASON_TEXT, + AS_REALLY_ULTIMATE, + AS_SAVE, + AS_ERROR, + AS_QUIT, + }; + + struct AutomatonHandelStruct; + + using AutomatonActionHandler = + std::function<Command(AutomatonHandelStruct&, AutomatonState)>; + using AutomatonNextStateHandler = + std::function<AutomatonState(AutomatonState, QString, QString)>; + + struct AutomatonHandelStruct { + explicit AutomatonHandelStruct(QString key_fpr); + void SetStatus(AutomatonState next_state); + auto CurrentStatus() -> AutomatonState; + void SetHandler(AutomatonNextStateHandler next_state_handler, + AutomatonActionHandler action_handler); + auto NextState(QString gpg_status, QString args) -> AutomatonState; + auto Action() -> Command; + void SetSuccess(bool success); + [[nodiscard]] auto Success() const -> bool; + auto KeyFpr() -> QString; + + private: + AutomatonState current_state_ = AS_START; + AutomatonNextStateHandler next_state_handler_; + AutomatonActionHandler action_handler_; + bool success_ = false; + QString key_fpr_; + }; + + /** + * @brief Construct a new Gpg Key Manager object + * + * @param channel + */ + explicit GpgAutomatonHandler( + int channel = SingletonFunctionObject::GetDefaultChannel()); + + auto DoInteract(const GpgKey& key, + AutomatonNextStateHandler next_state_handler, + AutomatonActionHandler action_handler) -> bool; + + private: + static auto interator_cb_func(void* handle, const char* status, + const char* args, int fd) -> gpgme_error_t; + + GpgContext& ctx_ = + GpgContext::GetInstance(SingletonFunctionObject::GetChannel()); ///< +}; + +using AutomatonNextStateHandler = + GpgAutomatonHandler::AutomatonNextStateHandler; +using AutomatonState = GpgAutomatonHandler::AutomatonState; +using AutomatonActionHandler = GpgAutomatonHandler::AutomatonActionHandler; +using AutomatonHandelStruct = GpgAutomatonHandler::AutomatonHandelStruct; + +} // namespace GpgFrontend
\ No newline at end of file diff --git a/src/core/function/gpg/GpgKeyManager.cpp b/src/core/function/gpg/GpgKeyManager.cpp index 753b668f..630b2b0c 100644 --- a/src/core/function/gpg/GpgKeyManager.cpp +++ b/src/core/function/gpg/GpgKeyManager.cpp @@ -29,74 +29,19 @@ #include "GpgKeyManager.h" #include "core/GpgModel.h" +#include "core/function/gpg/GpgAutomatonHandler.h" #include "core/function/gpg/GpgBasicOperator.h" #include "core/function/gpg/GpgKeyGetter.h" #include "core/utils/GpgUtils.h" -GpgFrontend::GpgKeyManager::GpgKeyManager(int channel) - : SingletonFunctionObject<GpgKeyManager>(channel) {} - -auto GpgFrontend::GpgKeyManager::interactor_cb_fnc(void* handle, - const char* status, - const char* args, - int fd) -> gpgme_error_t { - auto* handle_struct = static_cast<AutomatonHandelStruct*>(handle); - QString status_s = status; - QString args_s = args; - - if (status_s == "KEY_CONSIDERED") { - auto tokens = QString(args).split(' '); - - if (tokens.empty() || tokens[0] != handle_struct->KeyFpr()) { - LOG_W() << "handle struct key fpr " << handle_struct->KeyFpr() - << "mismatch token: " << tokens[0] << ", exit..."; - - return -1; - } - - return 0; - } - - if (status_s == "GOT_IT" || status_s.isEmpty()) { - FLOG_D("gpg reply is GOT_IT, continue..."); - return 0; - } - - LOG_D() << "current state" << handle_struct->CurrentStatus() - << "gpg status: " << status_s << ", args: " << args_s; - - AutomatonState next_state = handle_struct->NextState(status_s, args_s); - if (next_state == AS_ERROR) { - FLOG_D("handle struct next state caught error, abort..."); - return -1; - } - LOG_D() << "next state" << next_state; - - if (next_state == AS_SAVE) { - handle_struct->SetSuccess(true); - } - - // set state and preform action - handle_struct->SetStatus(next_state); - Command cmd = handle_struct->Action(); +namespace GpgFrontend { - LOG_D() << "next action, cmd:" << cmd; - - if (!cmd.isEmpty()) { - auto btye_array = cmd.toUtf8(); - gpgme_io_write(fd, btye_array, btye_array.size()); - gpgme_io_write(fd, "\n", 1); - } else if (status_s == "GET_LINE") { - // avoid trapping in this state - return GPG_ERR_FALSE; - } - - return 0; -} +GpgKeyManager::GpgKeyManager(int channel) + : SingletonFunctionObject<GpgKeyManager>(channel) {} -auto GpgFrontend::GpgKeyManager::SignKey( - const GpgFrontend::GpgKey& target, GpgFrontend::KeyArgsList& keys, - const QString& uid, const std::unique_ptr<QDateTime>& expires) -> bool { +auto GpgKeyManager::SignKey(const GpgKey& target, KeyArgsList& keys, + const QString& uid, + const std::unique_ptr<QDateTime>& expires) -> bool { GpgBasicOperator::GetInstance(GetChannel()).SetSigners(keys, true); unsigned int flags = 0; @@ -115,9 +60,8 @@ auto GpgFrontend::GpgKeyManager::SignKey( return CheckGpgError(err) == GPG_ERR_NO_ERROR; } -auto GpgFrontend::GpgKeyManager::RevSign( - const GpgFrontend::GpgKey& key, - const GpgFrontend::SignIdArgsListPtr& signature_id) -> bool { +auto GpgKeyManager::RevSign(const GpgKey& key, + const SignIdArgsListPtr& signature_id) -> bool { auto& key_getter = GpgKeyGetter::GetInstance(GetChannel()); for (const auto& sign_id : *signature_id) { @@ -132,9 +76,9 @@ auto GpgFrontend::GpgKeyManager::RevSign( return true; } -auto GpgFrontend::GpgKeyManager::SetExpire( - const GpgFrontend::GpgKey& key, std::unique_ptr<GpgSubKey>& subkey, - std::unique_ptr<QDateTime>& expires) -> bool { +auto GpgKeyManager::SetExpire(const GpgKey& key, + std::unique_ptr<GpgSubKey>& subkey, + std::unique_ptr<QDateTime>& expires) -> bool { unsigned long expires_time = 0; if (expires != nullptr) expires_time = expires->toSecsSinceEpoch(); @@ -150,73 +94,73 @@ auto GpgFrontend::GpgKeyManager::SetExpire( return CheckGpgError(err) == GPG_ERR_NO_ERROR; } -auto GpgFrontend::GpgKeyManager::SetOwnerTrustLevel(const GpgKey& key, - int trust_level) -> bool { +auto GpgKeyManager::SetOwnerTrustLevel(const GpgKey& key, + int trust_level) -> bool { if (trust_level < 1 || trust_level > 5) { FLOG_W("illegal owner trust level: %d", trust_level); } - AutomatonNextStateHandler next_state_handler = + GpgAutomatonHandler::AutomatonNextStateHandler next_state_handler = [](AutomatonState state, QString status, QString args) { auto tokens = args.split(' '); switch (state) { - case AS_START: + case GpgAutomatonHandler::AS_START: if (status == "GET_LINE" && args == "keyedit.prompt") { - return AS_COMMAND; + return GpgAutomatonHandler::AS_COMMAND; } - return AS_ERROR; - case AS_COMMAND: + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_COMMAND: if (status == "GET_LINE" && args == "edit_ownertrust.value") { - return AS_VALUE; + return GpgAutomatonHandler::AS_VALUE; } - return AS_ERROR; - case AS_VALUE: + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_VALUE: if (status == "GET_LINE" && args == "keyedit.prompt") { - return AS_QUIT; + return GpgAutomatonHandler::AS_QUIT; } else if (status == "GET_BOOL" && args == "edit_ownertrust.set_ultimate.okay") { - return AS_REALLY_ULTIMATE; + return GpgAutomatonHandler::AS_REALLY_ULTIMATE; } - return AS_ERROR; - case AS_REALLY_ULTIMATE: + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_REALLY_ULTIMATE: if (status == "GET_LINE" && args == "keyedit.prompt") { - return AS_QUIT; + return GpgAutomatonHandler::AS_QUIT; } - return AS_ERROR; - case AS_QUIT: + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_QUIT: if (status == "GET_BOOL" && args == "keyedit.save.okay") { - return AS_SAVE; + return GpgAutomatonHandler::AS_SAVE; } - return AS_ERROR; - case AS_ERROR: + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_ERROR: if (status == "GET_LINE" && args == "keyedit.prompt") { - return AS_QUIT; + return GpgAutomatonHandler::AS_QUIT; } - return AS_ERROR; + return GpgAutomatonHandler::AS_ERROR; default: - return AS_ERROR; + return GpgAutomatonHandler::AS_ERROR; }; }; AutomatonActionHandler action_handler = [trust_level](AutomatonHandelStruct& handler, AutomatonState state) { switch (state) { - case AS_COMMAND: + case GpgAutomatonHandler::AS_COMMAND: return QString("trust"); - case AS_VALUE: + case GpgAutomatonHandler::AS_VALUE: handler.SetSuccess(true); return QString::number(trust_level); - case AS_REALLY_ULTIMATE: + case GpgAutomatonHandler::AS_REALLY_ULTIMATE: handler.SetSuccess(true); return QString("Y"); - case AS_QUIT: + case GpgAutomatonHandler::AS_QUIT: return QString("quit"); - case AS_SAVE: + case GpgAutomatonHandler::AS_SAVE: handler.SetSuccess(true); return QString("Y"); - case AS_START: - case AS_ERROR: + case GpgAutomatonHandler::AS_START: + case GpgAutomatonHandler::AS_ERROR: return QString(""); default: return QString(""); @@ -224,23 +168,14 @@ auto GpgFrontend::GpgKeyManager::SetOwnerTrustLevel(const GpgKey& key, return QString(""); }; - auto key_fpr = key.GetFingerprint(); - AutomatonHandelStruct handel_struct(key_fpr); - handel_struct.SetHandler(next_state_handler, action_handler); - - GpgData data_out; - - auto err = - gpgme_op_interact(ctx_.DefaultContext(), static_cast<gpgme_key_t>(key), 0, - GpgKeyManager::interactor_cb_fnc, - static_cast<void*>(&handel_struct), data_out); - return CheckGpgError(err) == GPG_ERR_NO_ERROR && handel_struct.Success(); + return GpgAutomatonHandler::GetInstance(GetChannel()) + .DoInteract(key, next_state_handler, action_handler); } -auto GpgFrontend::GpgKeyManager::DeleteSubkey(const GpgKey& key, - int subkey_index) -> bool { +auto GpgKeyManager::DeleteSubkey(const GpgKey& key, int subkey_index) -> bool { if (subkey_index < 0 || subkey_index >= key.GetSubKeys()->size()) { LOG_W() << "illegal subkey index: " << subkey_index; + return false; } AutomatonNextStateHandler next_state_handler = @@ -248,61 +183,61 @@ auto GpgFrontend::GpgKeyManager::DeleteSubkey(const GpgKey& key, auto tokens = args.split(' '); switch (state) { - case AS_START: + case GpgAutomatonHandler::AS_START: if (status == "GET_LINE" && args == "keyedit.prompt") { - return AS_SELECT; + return GpgAutomatonHandler::AS_SELECT; } - return AS_ERROR; - case AS_SELECT: + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_SELECT: if (status == "GET_LINE" && args == "keyedit.prompt") { - return AS_COMMAND; + return GpgAutomatonHandler::AS_COMMAND; } - return AS_ERROR; - case AS_COMMAND: + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_COMMAND: if (status == "GET_LINE" && args == "keyedit.prompt") { - return AS_QUIT; + return GpgAutomatonHandler::AS_QUIT; } else if (status == "GET_BOOL" && args == "keyedit.remove.subkey.okay") { - return AS_REALLY_ULTIMATE; + return GpgAutomatonHandler::AS_REALLY_ULTIMATE; } - return AS_ERROR; - case AS_REALLY_ULTIMATE: + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_REALLY_ULTIMATE: if (status == "GET_LINE" && args == "keyedit.prompt") { - return AS_QUIT; + return GpgAutomatonHandler::AS_QUIT; } - return AS_ERROR; - case AS_QUIT: + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_QUIT: if (status == "GET_BOOL" && args == "keyedit.save.okay") { - return AS_SAVE; + return GpgAutomatonHandler::AS_SAVE; } - return AS_ERROR; - case AS_ERROR: + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_ERROR: if (status == "GET_LINE" && args == "keyedit.prompt") { - return AS_QUIT; + return GpgAutomatonHandler::AS_QUIT; } - return AS_ERROR; + return GpgAutomatonHandler::AS_ERROR; default: - return AS_ERROR; + return GpgAutomatonHandler::AS_ERROR; }; }; AutomatonActionHandler action_handler = [subkey_index](AutomatonHandelStruct& handler, AutomatonState state) { switch (state) { - case AS_SELECT: + case GpgAutomatonHandler::AS_SELECT: return QString("key %1").arg(subkey_index); - case AS_COMMAND: + case GpgAutomatonHandler::AS_COMMAND: return QString("delkey"); - case AS_REALLY_ULTIMATE: + case GpgAutomatonHandler::AS_REALLY_ULTIMATE: handler.SetSuccess(true); return QString("Y"); - case AS_QUIT: + case GpgAutomatonHandler::AS_QUIT: return QString("quit"); - case AS_SAVE: + case GpgAutomatonHandler::AS_SAVE: handler.SetSuccess(true); return QString("Y"); - case AS_START: - case AS_ERROR: + case GpgAutomatonHandler::AS_START: + case GpgAutomatonHandler::AS_ERROR: return QString(""); default: return QString(""); @@ -316,29 +251,30 @@ auto GpgFrontend::GpgKeyManager::DeleteSubkey(const GpgKey& key, GpgData data_out; - auto err = - gpgme_op_interact(ctx_.DefaultContext(), static_cast<gpgme_key_t>(key), 0, - GpgKeyManager::interactor_cb_fnc, - static_cast<void*>(&handel_struct), data_out); - return CheckGpgError(err) == GPG_ERR_NO_ERROR && handel_struct.Success(); + return GpgAutomatonHandler::GetInstance(GetChannel()) + .DoInteract(key, next_state_handler, action_handler); } -auto GpgFrontend::GpgKeyManager::RevokeSubkey( - const GpgKey& key, int subkey_index, int reason_code, - const QString& reason_text) -> bool { +auto GpgKeyManager::RevokeSubkey(const GpgKey& key, int subkey_index, + int reason_code, + const QString& reason_text) -> bool { if (subkey_index < 0 || subkey_index >= key.GetSubKeys()->size()) { LOG_W() << "illegal subkey index: " << subkey_index; + return false; + } + + if (reason_code < 0 || reason_code > 3) { + LOG_W() << "illegal reason code: " << reason_code; + return false; } // dealing with reason text #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 4) - auto reason_text_lines = - GpgFrontend::SecureCreateSharedObject<QList<QString>>( - reason_text.split('\n', Qt::SkipEmptyParts).toVector()); + auto reason_text_lines = SecureCreateSharedObject<QList<QString>>( + reason_text.split('\n', Qt::SkipEmptyParts).toVector()); #else - auto reason_text_lines = - GpgFrontend::SecureCreateSharedObject<QVector<QString>>( - reason_text.split('\n', Qt::SkipEmptyParts).toVector()); + auto reason_text_lines = SecureCreateSharedObject<QVector<QString>>( + reason_text.split('\n', Qt::SkipEmptyParts).toVector()); #endif AutomatonNextStateHandler next_state_handler = @@ -346,63 +282,63 @@ auto GpgFrontend::GpgKeyManager::RevokeSubkey( auto tokens = args.split(' '); switch (state) { - case AS_START: + case GpgAutomatonHandler::AS_START: if (status == "GET_LINE" && args == "keyedit.prompt") { - return AS_SELECT; + return GpgAutomatonHandler::AS_SELECT; } - return AS_ERROR; - case AS_SELECT: + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_SELECT: if (status == "GET_LINE" && args == "keyedit.prompt") { - return AS_COMMAND; + return GpgAutomatonHandler::AS_COMMAND; } - return AS_ERROR; - case AS_COMMAND: + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_COMMAND: if (status == "GET_LINE" && args == "keyedit.prompt") { - return AS_QUIT; + return GpgAutomatonHandler::AS_QUIT; } else if (status == "GET_BOOL" && args == "keyedit.revoke.subkey.okay") { - return AS_REALLY_ULTIMATE; + return GpgAutomatonHandler::AS_REALLY_ULTIMATE; } - return AS_ERROR; - case AS_REASON_CODE: + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_REASON_CODE: if (status == "GET_LINE" && args == "keyedit.prompt") { - return AS_QUIT; + return GpgAutomatonHandler::AS_QUIT; } else if (status == "GET_LINE" && args == "ask_revocation_reason.text") { - return AS_REASON_TEXT; + return GpgAutomatonHandler::AS_REASON_TEXT; } - return AS_ERROR; - case AS_REASON_TEXT: + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_REASON_TEXT: if (status == "GET_LINE" && args == "keyedit.prompt") { - return AS_QUIT; + return GpgAutomatonHandler::AS_QUIT; } else if (status == "GET_LINE" && args == "ask_revocation_reason.text") { - return AS_REASON_TEXT; + return GpgAutomatonHandler::AS_REASON_TEXT; } else if (status == "GET_BOOL" && args == "ask_revocation_reason.okay") { - return AS_REALLY_ULTIMATE; + return GpgAutomatonHandler::AS_REALLY_ULTIMATE; } - return AS_ERROR; - case AS_REALLY_ULTIMATE: + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_REALLY_ULTIMATE: if (status == "GET_LINE" && args == "keyedit.prompt") { - return AS_QUIT; + return GpgAutomatonHandler::AS_QUIT; } else if (status == "GET_LINE" && args == "ask_revocation_reason.code") { - return AS_REASON_CODE; + return GpgAutomatonHandler::AS_REASON_CODE; } - return AS_ERROR; - case AS_QUIT: + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_QUIT: if (status == "GET_BOOL" && args == "keyedit.save.okay") { - return AS_SAVE; + return GpgAutomatonHandler::AS_SAVE; } - return AS_ERROR; - case AS_ERROR: + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_ERROR: if (status == "GET_LINE" && args == "keyedit.prompt") { - return AS_QUIT; + return GpgAutomatonHandler::AS_QUIT; } - return AS_ERROR; + return GpgAutomatonHandler::AS_ERROR; default: - return AS_ERROR; + return GpgAutomatonHandler::AS_ERROR; }; }; @@ -410,25 +346,25 @@ auto GpgFrontend::GpgKeyManager::RevokeSubkey( [subkey_index, reason_code, reason_text_lines]( AutomatonHandelStruct& handler, AutomatonState state) { switch (state) { - case AS_SELECT: + case GpgAutomatonHandler::AS_SELECT: return QString("key %1").arg(subkey_index); - case AS_COMMAND: + case GpgAutomatonHandler::AS_COMMAND: return QString("revkey"); - case AS_REASON_CODE: + case GpgAutomatonHandler::AS_REASON_CODE: return QString::number(reason_code); - case AS_REASON_TEXT: + case GpgAutomatonHandler::AS_REASON_TEXT: return reason_text_lines->isEmpty() ? QString("") : QString(reason_text_lines->takeFirst().toUtf8()); - case AS_REALLY_ULTIMATE: + case GpgAutomatonHandler::AS_REALLY_ULTIMATE: return QString("Y"); - case AS_QUIT: + case GpgAutomatonHandler::AS_QUIT: return QString("quit"); - case AS_SAVE: + case GpgAutomatonHandler::AS_SAVE: handler.SetSuccess(true); return QString("Y"); - case AS_START: - case AS_ERROR: + case GpgAutomatonHandler::AS_START: + case GpgAutomatonHandler::AS_ERROR: return QString(""); default: return QString(""); @@ -442,9 +378,8 @@ auto GpgFrontend::GpgKeyManager::RevokeSubkey( GpgData data_out; - auto err = - gpgme_op_interact(ctx_.DefaultContext(), static_cast<gpgme_key_t>(key), 0, - GpgKeyManager::interactor_cb_fnc, - static_cast<void*>(&handel_struct), data_out); - return CheckGpgError(err) == GPG_ERR_NO_ERROR && handel_struct.Success(); + return GpgAutomatonHandler::GetInstance(GetChannel()) + .DoInteract(key, next_state_handler, action_handler); } + +} // namespace GpgFrontend
\ No newline at end of file diff --git a/src/core/function/gpg/GpgKeyManager.h b/src/core/function/gpg/GpgKeyManager.h index 986e835f..7bd45a1c 100644 --- a/src/core/function/gpg/GpgKeyManager.h +++ b/src/core/function/gpg/GpgKeyManager.h @@ -28,8 +28,6 @@ #pragma once -#include <functional> - #include "core/function/basic/GpgFunctionObject.h" #include "core/function/gpg/GpgContext.h" #include "core/typedef/GpgTypedef.h" @@ -113,61 +111,6 @@ class GPGFRONTEND_CORE_EXPORT GpgKeyManager const QString& reason_text) -> bool; private: - static auto interactor_cb_fnc(void* handle, const char* status, - const char* args, int fd) -> gpgme_error_t; - - using Command = QString; - using AutomatonState = enum { - AS_START, - AS_SELECT, - AS_COMMAND, - AS_VALUE, - AS_REASON_CODE, - AS_REASON_TEXT, - AS_REALLY_ULTIMATE, - AS_SAVE, - AS_ERROR, - AS_QUIT, - }; - - struct AutomatonHandelStruct; - - using AutomatonActionHandler = - std::function<Command(AutomatonHandelStruct&, AutomatonState)>; - using AutomatonNextStateHandler = - std::function<AutomatonState(AutomatonState, QString, QString)>; - - struct AutomatonHandelStruct { - void SetStatus(AutomatonState next_state) { current_state_ = next_state; } - auto CurrentStatus() -> AutomatonState { return current_state_; } - void SetHandler(AutomatonNextStateHandler next_state_handler, - AutomatonActionHandler action_handler) { - next_state_handler_ = std::move(next_state_handler); - action_handler_ = std::move(action_handler); - } - auto NextState(QString gpg_status, QString args) -> AutomatonState { - return next_state_handler_(current_state_, std::move(gpg_status), - std::move(args)); - } - auto Action() -> Command { return action_handler_(*this, current_state_); } - - void SetSuccess(bool success) { success_ = success; } - - [[nodiscard]] auto Success() const -> bool { return success_; } - - auto KeyFpr() -> QString { return key_fpr_; } - - explicit AutomatonHandelStruct(QString key_fpr) - : key_fpr_(std::move(key_fpr)) {} - - private: - AutomatonState current_state_ = AS_START; - AutomatonNextStateHandler next_state_handler_; - AutomatonActionHandler action_handler_; - bool success_ = false; - QString key_fpr_; - }; - GpgContext& ctx_ = GpgContext::GetInstance(SingletonFunctionObject::GetChannel()); ///< }; diff --git a/src/core/function/gpg/GpgUIDOperator.cpp b/src/core/function/gpg/GpgUIDOperator.cpp index 90034413..afcd3bae 100644 --- a/src/core/function/gpg/GpgUIDOperator.cpp +++ b/src/core/function/gpg/GpgUIDOperator.cpp @@ -29,6 +29,7 @@ #include "GpgUIDOperator.h" #include "core/GpgModel.h" +#include "core/function/gpg/GpgAutomatonHandler.h" #include "core/utils/GpgUtils.h" namespace GpgFrontend { @@ -42,12 +43,6 @@ auto GpgUIDOperator::AddUID(const GpgKey& key, const QString& uid) -> bool { return CheckGpgError(err) == GPG_ERR_NO_ERROR; } -auto GpgUIDOperator::RevUID(const GpgKey& key, const QString& uid) -> bool { - auto err = CheckGpgError(gpgme_op_revuid( - ctx_.DefaultContext(), static_cast<gpgme_key_t>(key), uid.toUtf8(), 0)); - return CheckGpgError(err) == GPG_ERR_NO_ERROR; -} - auto GpgUIDOperator::SetPrimaryUID(const GpgKey& key, const QString& uid) -> bool { auto err = CheckGpgError(gpgme_op_set_uid_flag( @@ -55,6 +50,7 @@ auto GpgUIDOperator::SetPrimaryUID(const GpgKey& key, "primary", nullptr)); return CheckGpgError(err) == GPG_ERR_NO_ERROR; } + auto GpgUIDOperator::AddUID(const GpgKey& key, const QString& name, const QString& comment, const QString& email) -> bool { @@ -62,4 +58,216 @@ auto GpgUIDOperator::AddUID(const GpgKey& key, const QString& name, return AddUID(key, QString("%1(%2)<%3>").arg(name).arg(comment).arg(email)); } +auto GpgUIDOperator::DeleteUID(const GpgKey& key, int uid_index) -> bool { + if (uid_index < 2 || uid_index > key.GetUIDs()->size()) { + LOG_W() << "illegal uid_index index: " << uid_index; + return false; + } + + AutomatonNextStateHandler next_state_handler = [](AutomatonState state, + QString status, + QString args) { + auto tokens = args.split(' '); + + switch (state) { + case GpgAutomatonHandler::AS_START: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return GpgAutomatonHandler::AS_SELECT; + } + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_SELECT: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return GpgAutomatonHandler::AS_COMMAND; + } + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_COMMAND: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return GpgAutomatonHandler::AS_QUIT; + } else if (status == "GET_BOOL" && args == "keyedit.remove.uid.okay") { + return GpgAutomatonHandler::AS_REALLY_ULTIMATE; + } + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_REALLY_ULTIMATE: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return GpgAutomatonHandler::AS_QUIT; + } + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_QUIT: + if (status == "GET_BOOL" && args == "keyedit.save.okay") { + return GpgAutomatonHandler::AS_SAVE; + } + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_ERROR: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return GpgAutomatonHandler::AS_QUIT; + } + return GpgAutomatonHandler::AS_ERROR; + default: + return GpgAutomatonHandler::AS_ERROR; + }; + }; + + AutomatonActionHandler action_handler = + [uid_index](AutomatonHandelStruct& handler, AutomatonState state) { + switch (state) { + case GpgAutomatonHandler::AS_SELECT: + return QString("uid %1").arg(uid_index); + case GpgAutomatonHandler::AS_COMMAND: + return QString("deluid"); + case GpgAutomatonHandler::AS_REALLY_ULTIMATE: + handler.SetSuccess(true); + return QString("Y"); + case GpgAutomatonHandler::AS_QUIT: + return QString("quit"); + case GpgAutomatonHandler::AS_SAVE: + handler.SetSuccess(true); + return QString("Y"); + case GpgAutomatonHandler::AS_START: + case GpgAutomatonHandler::AS_ERROR: + return QString(""); + default: + return QString(""); + } + return QString(""); + }; + + auto key_fpr = key.GetFingerprint(); + AutomatonHandelStruct handel_struct(key_fpr); + handel_struct.SetHandler(next_state_handler, action_handler); + + GpgData data_out; + + return GpgAutomatonHandler::GetInstance(GetChannel()) + .DoInteract(key, next_state_handler, action_handler); +} + +auto GpgUIDOperator::RevokeUID(const GpgKey& key, int uid_index, + int reason_code, + const QString& reason_text) -> bool { + if (uid_index < 2 || uid_index > key.GetUIDs()->size()) { + LOG_W() << "illegal uid index: " << uid_index; + return false; + } + + if (reason_code != 0 && reason_code != 4) { + LOG_W() << "illegal reason code: " << reason_code; + return false; + } + + // dealing with reason text +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 4) + auto reason_text_lines = + GpgFrontend::SecureCreateSharedObject<QList<QString>>( + reason_text.split('\n', Qt::SkipEmptyParts).toVector()); +#else + auto reason_text_lines = + GpgFrontend::SecureCreateSharedObject<QVector<QString>>( + reason_text.split('\n', Qt::SkipEmptyParts).toVector()); +#endif + + AutomatonNextStateHandler next_state_handler = [](AutomatonState state, + QString status, + QString args) { + auto tokens = args.split(' '); + + switch (state) { + case GpgAutomatonHandler::AS_START: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return GpgAutomatonHandler::AS_SELECT; + } + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_SELECT: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return GpgAutomatonHandler::AS_COMMAND; + } + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_COMMAND: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return GpgAutomatonHandler::AS_QUIT; + } else if (status == "GET_BOOL" && args == "keyedit.revoke.uid.okay") { + return GpgAutomatonHandler::AS_REALLY_ULTIMATE; + } + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_REASON_CODE: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return GpgAutomatonHandler::AS_QUIT; + } else if (status == "GET_LINE" && + args == "ask_revocation_reason.text") { + return GpgAutomatonHandler::AS_REASON_TEXT; + } + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_REASON_TEXT: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return GpgAutomatonHandler::AS_QUIT; + } else if (status == "GET_LINE" && + args == "ask_revocation_reason.text") { + return GpgAutomatonHandler::AS_REASON_TEXT; + } else if (status == "GET_BOOL" && + args == "ask_revocation_reason.okay") { + return GpgAutomatonHandler::AS_REALLY_ULTIMATE; + } + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_REALLY_ULTIMATE: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return GpgAutomatonHandler::AS_QUIT; + } else if (status == "GET_LINE" && + args == "ask_revocation_reason.code") { + return GpgAutomatonHandler::AS_REASON_CODE; + } + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_QUIT: + if (status == "GET_BOOL" && args == "keyedit.save.okay") { + return GpgAutomatonHandler::AS_SAVE; + } + return GpgAutomatonHandler::AS_ERROR; + case GpgAutomatonHandler::AS_ERROR: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return GpgAutomatonHandler::AS_QUIT; + } + return GpgAutomatonHandler::AS_ERROR; + default: + return GpgAutomatonHandler::AS_ERROR; + }; + }; + + AutomatonActionHandler action_handler = + [uid_index, reason_code, reason_text_lines]( + AutomatonHandelStruct& handler, AutomatonState state) { + switch (state) { + case GpgAutomatonHandler::AS_SELECT: + return QString("uid %1").arg(uid_index); + case GpgAutomatonHandler::AS_COMMAND: + return QString("revuid"); + case GpgAutomatonHandler::AS_REASON_CODE: + return QString::number(reason_code); + case GpgAutomatonHandler::AS_REASON_TEXT: + return reason_text_lines->isEmpty() + ? QString("") + : QString(reason_text_lines->takeFirst().toUtf8()); + case GpgAutomatonHandler::AS_REALLY_ULTIMATE: + return QString("Y"); + case GpgAutomatonHandler::AS_QUIT: + return QString("quit"); + case GpgAutomatonHandler::AS_SAVE: + handler.SetSuccess(true); + return QString("Y"); + case GpgAutomatonHandler::AS_START: + case GpgAutomatonHandler::AS_ERROR: + return QString(""); + default: + return QString(""); + } + return QString(""); + }; + + auto key_fpr = key.GetFingerprint(); + AutomatonHandelStruct handel_struct(key_fpr); + handel_struct.SetHandler(next_state_handler, action_handler); + + GpgData data_out; + + return GpgAutomatonHandler::GetInstance(GetChannel()) + .DoInteract(key, next_state_handler, action_handler); +} + } // namespace GpgFrontend diff --git a/src/core/function/gpg/GpgUIDOperator.h b/src/core/function/gpg/GpgUIDOperator.h index 87d112ac..f0e925ed 100644 --- a/src/core/function/gpg/GpgUIDOperator.h +++ b/src/core/function/gpg/GpgUIDOperator.h @@ -67,12 +67,27 @@ class GPGFRONTEND_CORE_EXPORT GpgUIDOperator const QString& email) -> bool; /** - * Revoke(Delete) UID from certain key pair - * @param key target key pair - * @param uid target uid - * @return if successful + * @brief + * + * @param key + * @param uid_index + * @return true + * @return false + */ + auto DeleteUID(const GpgKey& key, int uid_index) -> bool; + + /** + * @brief + * + * @param key + * @param uid_index + * @param reason_code + * @param reason_text + * @return true + * @return false */ - auto RevUID(const GpgKey& key, const QString& uid) -> bool; + auto RevokeUID(const GpgKey& key, int uid_index, int reason_code, + const QString& reason_text) -> bool; /** * Set one of a uid of a key pair as primary |