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 | |
parent | feat: support eml data decryption (diff) | |
download | GpgFrontend-af776283bd5d50363c8777dbb3524fde417cc368.tar.gz GpgFrontend-af776283bd5d50363c8777dbb3524fde417cc368.zip |
feat: support uid revoke and uid delete operations
-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 | ||||
-rw-r--r-- | src/test/core/GpgCoreTestKeyManagement.cpp | 8 | ||||
-rw-r--r-- | src/test/core/GpgCoreTestKeyUIDOpera.cpp | 136 | ||||
-rw-r--r-- | src/ui/dialog/RevocationOptionsDialog.cpp | 7 | ||||
-rw-r--r-- | src/ui/dialog/RevocationOptionsDialog.h | 2 | ||||
-rw-r--r-- | src/ui/dialog/keypair_details/KeyPairOperaTab.cpp | 5 | ||||
-rw-r--r-- | src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp | 7 | ||||
-rw-r--r-- | src/ui/dialog/keypair_details/KeyPairUIDTab.cpp | 286 | ||||
-rw-r--r-- | src/ui/dialog/keypair_details/KeyPairUIDTab.h | 119 | ||||
-rw-r--r-- | src/ui/dialog/keypair_details/KeyUIDSignDialog.cpp | 18 | ||||
-rw-r--r-- | src/ui/dialog/keypair_details/KeyUIDSignDialog.h | 4 | ||||
-rw-r--r-- | src/ui/main_window/MainWindow.h | 2 |
17 files changed, 989 insertions, 501 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 diff --git a/src/test/core/GpgCoreTestKeyManagement.cpp b/src/test/core/GpgCoreTestKeyManagement.cpp index 135a7e51..4d80f7d0 100644 --- a/src/test/core/GpgCoreTestKeyManagement.cpp +++ b/src/test/core/GpgCoreTestKeyManagement.cpp @@ -35,7 +35,7 @@ #include "core/model/GpgImportInformation.h" #include "core/utils/GpgUtils.h" -const char *TEST_PRIVATE_KEY_DATA = R"( +static const char *test_private_key_data = R"( -----BEGIN PGP PRIVATE KEY BLOCK----- lQOYBGcSdI8BCACwi1n2Bx1v6qmQxRgrONYlmzKrSBvNyoSOdAVcTJDXYjdlNFCq @@ -110,7 +110,7 @@ namespace GpgFrontend::Test { TEST_F(GpgCoreTest, CoreDeleteSubkeyTestA) { auto info = GpgKeyImportExporter::GetInstance().ImportKey( - GFBuffer(QString::fromLatin1(TEST_PRIVATE_KEY_DATA))); + GFBuffer(QString::fromLatin1(test_private_key_data))); ASSERT_EQ(info->not_imported, 0); ASSERT_EQ(info->imported, 1); @@ -143,7 +143,7 @@ TEST_F(GpgCoreTest, CoreDeleteSubkeyTestA) { TEST_F(GpgCoreTest, CoreSetOwnerTrustA) { auto info = GpgKeyImportExporter::GetInstance().ImportKey( - GFBuffer(QString::fromLatin1(TEST_PRIVATE_KEY_DATA))); + GFBuffer(QString::fromLatin1(test_private_key_data))); ASSERT_EQ(info->not_imported, 0); ASSERT_EQ(info->imported, 1); @@ -217,7 +217,7 @@ TEST_F(GpgCoreTest, CoreSetOwnerTrustA) { TEST_F(GpgCoreTest, CoreRevokeSubkeyTestA) { auto info = GpgKeyImportExporter::GetInstance().ImportKey( - GFBuffer(QString::fromLatin1(TEST_PRIVATE_KEY_DATA))); + GFBuffer(QString::fromLatin1(test_private_key_data))); ASSERT_EQ(info->not_imported, 0); ASSERT_EQ(info->imported, 1); diff --git a/src/test/core/GpgCoreTestKeyUIDOpera.cpp b/src/test/core/GpgCoreTestKeyUIDOpera.cpp new file mode 100644 index 00000000..b4177dea --- /dev/null +++ b/src/test/core/GpgCoreTestKeyUIDOpera.cpp @@ -0,0 +1,136 @@ +/** + * 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 "GpgCoreTest.h" +#include "core/GpgConstants.h" +#include "core/function/gpg/GpgKeyGetter.h" +#include "core/function/gpg/GpgKeyImportExporter.h" +#include "core/function/gpg/GpgKeyOpera.h" +#include "core/function/gpg/GpgUIDOperator.h" +#include "core/model/GpgImportInformation.h" +#include "core/utils/GpgUtils.h" + +static const char *test_private_key_data = R"( +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lHQEZ0N2exMFK4EEAAoCAwTmBGWDMqzfdx1fkGjuWHP/R6F2ZvvlolcVNJAImWKl +ew0ncmSH1/pOQ7vAud9yPnkDZfhJpcKHl1G4q/Bu5YyiAAEA5sIYAJtXCFrI5upG +Lk8XmdqGCn5c8gKsWVUikrqdKkcOLrQbZmZmZmZmKGZmZmZmKTxmZmZmQGZmZi5m +ZmY+iJkEExMIAEEWIQQwn1gjkF+wDtnB6TLy2N+l8QneRwUCZ0N2ewIbIwUJA8Jm +8wULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRDy2N+l8QneR2ptAQCRE6T3 +HhNp7CHVk1DkxTIj4i7Mw1Em7Cwvctr8usPhBwD/YECZLMowPLNZO4GFhIH+1Etd +GZ2d0Gbb51DUlPZUO2K0HGdnZ2dnZyhnZ2dnZyk8Z2dnZ2dAZ2dnLmdnZz6ImQQT +EwgAQRYhBDCfWCOQX7AO2cHpMvLY36XxCd5HBQJnRjCtAhsjBQkDwmbzBQsJCAcC +AiICBhUKCQgLAgQWAgMBAh4HAheAAAoJEPLY36XxCd5HaD8BAPvBCdzFSeGXyFXh +4Sn4tVpwlnJOwzC0ECGYSieaNjocAQChWIXVinXAQ5U/oslqR2+Gg1s2o8gTKVbv +ZE6T+6Vvc7QgaGhoaGhoKGhoaGhoaGgpPGhoaGhoQGhoaGguaGhoaD6ImQQTEwgA +QRYhBDCfWCOQX7AO2cHpMvLY36XxCd5HBQJnRjC2AhsjBQkDwmbzBQsJCAcCAiIC +BhUKCQgLAgQWAgMBAh4HAheAAAoJEPLY36XxCd5HqE0A/R9wq+ZobC2Iztoudcg/ +eKXp1hs1D9zv7R6KkLdbB4zXAP0ch5qZnrb+U/wIuhq+oOwJknMsD/njB263eUgb +AXNh2rQdeXl5eXkoeXl5eXl5KTx5eXl5eUB5eXl5Lnl5eT6ImQQTEwgAQRYhBDCf +WCOQX7AO2cHpMvLY36XxCd5HBQJnRjDBAhsjBQkDwmbzBQsJCAcCAiICBhUKCQgL +AgQWAgMBAh4HAheAAAoJEPLY36XxCd5HcycA/RwytAvY8ryaR4qUPN8g1NSX3Dv8 +SHYYu2cZJuck+lVxAQDxcyljEIZKVqOpNfWRZyqcRvE8kr64PymJTAPYOVdBuA== +=4Hms +-----END PGP PRIVATE KEY BLOCK----- +)"; + +namespace GpgFrontend::Test { + +TEST_F(GpgCoreTest, CoreDeleteUIDTestA) { + auto info = GpgKeyImportExporter::GetInstance().ImportKey( + GFBuffer(QString::fromLatin1(test_private_key_data))); + + ASSERT_EQ(info->not_imported, 0); + ASSERT_EQ(info->imported, 1); + + auto key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel) + .GetKey("F2D8DFA5F109DE47"); + ASSERT_TRUE(key.IsGood()); + + auto uids = key.GetUIDs(); + + ASSERT_EQ(uids->size(), 4); + ASSERT_EQ((*uids)[2].GetUID(), "gggggg(ggggg)<[email protected]>"); + + auto res = GpgUIDOperator::GetInstance().DeleteUID(key, 3); + + ASSERT_TRUE(res); + + GpgKeyGetter::GetInstance().FlushKeyCache(); + key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel) + .GetKey("F2D8DFA5F109DE47"); + ASSERT_TRUE(key.IsGood()); + + uids = key.GetUIDs(); + + ASSERT_EQ(uids->size(), 3); + ASSERT_EQ((*uids)[2].GetUID(), "hhhhhh(hhhhhhh)<[email protected]>"); + + GpgKeyOpera::GetInstance().DeleteKey(key.GetId()); + GpgKeyGetter::GetInstance().FlushKeyCache(); +} + +TEST_F(GpgCoreTest, CoreRevokeUIDTestA) { + GpgKeyGetter::GetInstance().FlushKeyCache(); + auto info = GpgKeyImportExporter::GetInstance().ImportKey( + GFBuffer(QString::fromLatin1(test_private_key_data))); + + ASSERT_EQ(info->not_imported, 0); + ASSERT_EQ(info->imported, 1); + + auto key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel) + .GetKey("F2D8DFA5F109DE47"); + ASSERT_TRUE(key.IsGood()); + + auto uids = key.GetUIDs(); + + ASSERT_EQ(uids->size(), 4); + ASSERT_EQ((*uids)[2].GetUID(), "gggggg(ggggg)<[email protected]>"); + + auto res = GpgUIDOperator::GetInstance().RevokeUID( + key, 3, 4, "H\nEEEEEL\n\n\n\nL \n0\n"); + + ASSERT_TRUE(res); + + GpgKeyGetter::GetInstance().FlushKeyCache(); + key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel) + .GetKey("F2D8DFA5F109DE47"); + ASSERT_TRUE(key.IsGood()); + + uids = key.GetUIDs(); + + ASSERT_EQ(uids->size(), 4); + ASSERT_EQ((*uids)[2].GetUID(), "gggggg(ggggg)<[email protected]>"); + ASSERT_TRUE((*uids)[2].GetRevoked()); + + GpgKeyOpera::GetInstance().DeleteKey(key.GetId()); + GpgKeyGetter::GetInstance().FlushKeyCache(); +} + +} // namespace GpgFrontend::Test
\ No newline at end of file diff --git a/src/ui/dialog/RevocationOptionsDialog.cpp b/src/ui/dialog/RevocationOptionsDialog.cpp index ea11d747..fe91199d 100644 --- a/src/ui/dialog/RevocationOptionsDialog.cpp +++ b/src/ui/dialog/RevocationOptionsDialog.cpp @@ -32,16 +32,11 @@ #include "ui_RevocationOptionsDialog.h" GpgFrontend::UI::RevocationOptionsDialog::RevocationOptionsDialog( - QWidget *parent) + const QStringList& codes, QWidget* parent) : GeneralDialog("RevocationOptionsDialog", parent), ui_(GpgFrontend::SecureCreateSharedObject<Ui_RevocationOptionsDialog>()) { ui_->setupUi(this); - QStringList codes; - - codes << tr("0 -> No Reason.") << tr("1 -> This key is no more safe.") - << tr("2 -> Key is outdated.") << tr("3 -> Key is no longer used"); - ui_->rRcodeComboBox->addItems(codes); ui_->revocationReasonCodeLabel->setText(tr("Revocation Reason (Code)")); diff --git a/src/ui/dialog/RevocationOptionsDialog.h b/src/ui/dialog/RevocationOptionsDialog.h index 17356a4d..1ccdaac3 100644 --- a/src/ui/dialog/RevocationOptionsDialog.h +++ b/src/ui/dialog/RevocationOptionsDialog.h @@ -36,7 +36,7 @@ namespace GpgFrontend::UI { class RevocationOptionsDialog : public GeneralDialog { Q_OBJECT public: - explicit RevocationOptionsDialog(QWidget *parent); + explicit RevocationOptionsDialog(const QStringList& codes, QWidget* parent); [[nodiscard]] auto Code() const -> int; diff --git a/src/ui/dialog/keypair_details/KeyPairOperaTab.cpp b/src/ui/dialog/keypair_details/KeyPairOperaTab.cpp index ba3f299a..c849e43f 100644 --- a/src/ui/dialog/keypair_details/KeyPairOperaTab.cpp +++ b/src/ui/dialog/keypair_details/KeyPairOperaTab.cpp @@ -463,7 +463,10 @@ void KeyPairOperaTab::slot_update_key_from_server() { } void KeyPairOperaTab::slot_gen_revoke_cert() { - auto* revocation_options_dialog = new RevocationOptionsDialog(this); + QStringList codes; + codes << tr("0 -> No Reason.") << tr("1 -> This key is no more safe.") + << tr("2 -> Key is outdated.") << tr("3 -> Key is no longer used"); + auto* revocation_options_dialog = new RevocationOptionsDialog(codes, this); connect(revocation_options_dialog, &RevocationOptionsDialog::SignalRevokeOptionAccepted, this, diff --git a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp index ee637abb..a262c004 100644 --- a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp +++ b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp @@ -520,7 +520,7 @@ void KeyPairSubkeyTab::slot_revoke_subkey() { const auto subkeys = key_.GetSubKeys(); QString message = tr("<h3>Revoke Subkey Confirmation</h3><br />" - "<b>KeyID:</b> %1<br />2<br />" + "<b>KeyID:</b> %1<br /><br />" "Revoking a subkey will make it permanently unusable. " "This action is <b>irreversible</b>.<br />" "Are you sure you want to revoke this subkey?") @@ -547,7 +547,10 @@ void KeyPairSubkeyTab::slot_revoke_subkey() { return; } - auto* revocation_options_dialog = new RevocationOptionsDialog(this); + QStringList codes; + codes << tr("0 -> No Reason.") << tr("1 -> This key is no more safe.") + << tr("2 -> Key is outdated.") << tr("3 -> Key is no longer used"); + auto* revocation_options_dialog = new RevocationOptionsDialog(codes, this); connect(revocation_options_dialog, &RevocationOptionsDialog::SignalRevokeOptionAccepted, this, diff --git a/src/ui/dialog/keypair_details/KeyPairUIDTab.cpp b/src/ui/dialog/keypair_details/KeyPairUIDTab.cpp index fdae5223..47408a99 100644 --- a/src/ui/dialog/keypair_details/KeyPairUIDTab.cpp +++ b/src/ui/dialog/keypair_details/KeyPairUIDTab.cpp @@ -33,6 +33,9 @@ #include "core/function/gpg/GpgKeyManager.h" #include "core/function/gpg/GpgUIDOperator.h" #include "ui/UISignalStation.h" +#include "ui/dialog/RevocationOptionsDialog.h" +#include "ui/dialog/keypair_details/KeyNewUIDDialog.h" +#include "ui/dialog/keypair_details/KeyUIDSignDialog.h" #include "ui/widgets/TOFUInfoPage.h" namespace GpgFrontend::UI { @@ -47,24 +50,17 @@ KeyPairUIDTab::KeyPairUIDTab(int channel, const QString& key_id, create_uid_list(); create_sign_list(); - create_manage_uid_menu(); create_uid_popup_menu(); create_sign_popup_menu(); - auto* uid_buttons_layout = new QGridLayout(); + auto* uid_buttons_layout = new QHBoxLayout(); auto* add_uid_button = new QPushButton(tr("New UID")); - auto* manage_uid_button = new QPushButton(tr("UID Management")); - if (m_key_.IsHasMasterKey()) { - manage_uid_button->setMenu(manage_selected_uid_menu_); - } else { + if (!m_key_.IsHasMasterKey()) { add_uid_button->setDisabled(true); - manage_uid_button->setDisabled(true); } - - uid_buttons_layout->addWidget(add_uid_button, 0, 1); - uid_buttons_layout->addWidget(manage_uid_button, 0, 2); + uid_buttons_layout->addWidget(add_uid_button); auto* grid_layout = new QGridLayout(); @@ -200,14 +196,12 @@ void KeyPairUIDTab::slot_refresh_uid_list() { auto* tmp2 = new QTableWidgetItem(uid.GetComment()); uid_list_->setItem(row, 3, tmp2); - auto* tmp3 = new QTableWidgetItem(QString::number(row)); - tmp3->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | - Qt::ItemIsSelectable); + auto* tmp3 = new QTableWidgetItem(QString::number(row + 1)); + tmp3->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); tmp3->setTextAlignment(Qt::AlignCenter); - tmp3->setCheckState(Qt::Unchecked); uid_list_->setItem(row, 0, tmp3); - if (!row) { + if (row == 0) { for (auto i = 0; i < uid_list_->columnCount(); i++) { uid_list_->item(row, i)->setForeground(QColor(65, 105, 255)); } @@ -304,45 +298,13 @@ void KeyPairUIDTab::slot_refresh_sig_list() { } void KeyPairUIDTab::slot_add_sign() { - auto selected_uids = get_uid_checked(); - - if (selected_uids->empty()) { - QMessageBox::information( - nullptr, tr("Invalid Operation"), - tr("Please select one or more UIDs before doing this operation.")); - return; - } + const auto& target_uid = get_selected_uid(); auto* key_sign_dialog = new KeyUIDSignDialog( - current_gpg_context_channel_, m_key_, std::move(selected_uids), this); + current_gpg_context_channel_, m_key_, target_uid.GetUID(), this); key_sign_dialog->show(); } -auto KeyPairUIDTab::get_uid_checked() -> UIDArgsListPtr { - auto selected_uids = std::make_unique<UIDArgsList>(); - for (int i = 0; i < uid_list_->rowCount(); i++) { - if (uid_list_->item(i, 0)->checkState() == Qt::Checked) { - selected_uids->push_back(buffered_uids_[i].GetUID()); - } - } - return selected_uids; -} - -void KeyPairUIDTab::create_manage_uid_menu() { - manage_selected_uid_menu_ = new QMenu(this); - - auto* sign_uid_act = new QAction(tr("Sign Selected UID(s)"), this); - connect(sign_uid_act, &QAction::triggered, this, - &KeyPairUIDTab::slot_add_sign); - auto* del_uid_act = new QAction(tr("Delete Selected UID(s)"), this); - connect(del_uid_act, &QAction::triggered, this, &KeyPairUIDTab::slot_del_uid); - - if (m_key_.IsHasMasterKey()) { - manage_selected_uid_menu_->addAction(sign_uid_act); - manage_selected_uid_menu_->addAction(del_uid_act); - } -} - void KeyPairUIDTab::slot_add_uid() { auto* key_new_uid_dialog = new KeyNewUIDDialog(current_gpg_context_channel_, m_key_.GetId(), this); @@ -364,57 +326,58 @@ void KeyPairUIDTab::slot_add_uid_result(int result) { } void KeyPairUIDTab::slot_del_uid() { - auto selected_uids = get_uid_checked(); + const auto& target_uid = get_selected_uid(); - if (selected_uids->empty()) { - QMessageBox::information( - nullptr, tr("Invalid Operation"), - tr("Please select one or more UIDs before doing this operation.")); - return; + auto keynames = QString(); + + keynames.append(target_uid.GetUID().toHtmlEscaped()); + keynames.append("<br/>"); + + int index = 1; + for (const auto& uid : buffered_uids_) { + if (uid.GetUID() == target_uid.GetUID()) { + break; + } + index++; } - QString keynames; - for (auto& uid : *selected_uids) { - keynames.append(uid); - keynames.append("<br/>"); + if (index == 1) { + QMessageBox::information(nullptr, tr("Invalid Operation"), + tr("Cannot delete the Primary UID.")); + return; } int ret = QMessageBox::warning( this, tr("Deleting UIDs"), "<b>" + QString( - tr("Are you sure that you want to delete the following UIDs?")) + - "</b><br/><br/>" + keynames + +"<br/>" + + tr("Are you sure that you want to delete the following UID?")) + + "</b><br/><br/>" + target_uid.GetUID().toHtmlEscaped() + +"<br/>" + tr("The action can not be undone."), QMessageBox::No | QMessageBox::Yes); if (ret == QMessageBox::Yes) { - for (const auto& uid : *selected_uids) { - if (!GpgUIDOperator::GetInstance(current_gpg_context_channel_) - .RevUID(m_key_, uid)) { - QMessageBox::critical( - nullptr, tr("Operation Failed"), - tr("An error occurred during the delete %1 operation.").arg(uid)); - } + if (!GpgUIDOperator::GetInstance(current_gpg_context_channel_) + .DeleteUID(m_key_, index)) { + QMessageBox::critical( + nullptr, tr("Operation Failed"), + tr("An error occurred during the delete %1 operation.") + .arg(target_uid.GetUID().toHtmlEscaped())); } emit SignalUpdateUIDInfo(); } } void KeyPairUIDTab::slot_set_primary_uid() { - auto selected_uids = get_uid_selected(); + const auto& target_uid = get_selected_uid(); - if (selected_uids->empty()) { - auto* empty_uid_msg = new QMessageBox(); - empty_uid_msg->setText( - "Please select one UID before doing this operation."); - empty_uid_msg->exec(); + if (target_uid.GetUID() == buffered_uids_.front().GetUID()) { return; } QString keynames; - keynames.append(selected_uids->front()); + keynames.append(target_uid.GetUID().toHtmlEscaped()); keynames.append("<br/>"); int ret = QMessageBox::warning( @@ -424,25 +387,15 @@ void KeyPairUIDTab::slot_set_primary_uid() { tr("The action can not be undone."), QMessageBox::No | QMessageBox::Yes); - if (ret == QMessageBox::Yes) { - if (!GpgUIDOperator::GetInstance(current_gpg_context_channel_) - .SetPrimaryUID(m_key_, selected_uids->front())) { - QMessageBox::critical(nullptr, tr("Operation Failed"), - tr("An error occurred during the operation.")); - } else { - emit SignalUpdateUIDInfo(); - } - } -} + if (ret != QMessageBox::Yes) return; -auto KeyPairUIDTab::get_uid_selected() -> UIDArgsListPtr { - auto uids = std::make_unique<UIDArgsList>(); - for (int i = 0; i < uid_list_->rowCount(); i++) { - if (uid_list_->item(i, 0)->isSelected()) { - uids->push_back(buffered_uids_[i].GetUID()); - } + if (!GpgUIDOperator::GetInstance(current_gpg_context_channel_) + .SetPrimaryUID(m_key_, target_uid.GetUID())) { + QMessageBox::critical(nullptr, tr("Operation Failed"), + tr("An error occurred during the operation.")); + } else { + emit SignalUpdateUIDInfo(); } - return uids; } auto KeyPairUIDTab::get_sign_selected() -> SignIdArgsListPtr { @@ -459,79 +412,48 @@ auto KeyPairUIDTab::get_sign_selected() -> SignIdArgsListPtr { void KeyPairUIDTab::create_uid_popup_menu() { uid_popup_menu_ = new QMenu(this); - auto* ser_primary_uid_act = new QAction(tr("Set As Primary"), this); - connect(ser_primary_uid_act, &QAction::triggered, this, + set_primary_uid_act_ = new QAction(tr("Set As Primary"), this); + connect(set_primary_uid_act_, &QAction::triggered, this, &KeyPairUIDTab::slot_set_primary_uid); - auto* sign_uid_act = new QAction(tr("Sign UID"), this); - connect(sign_uid_act, &QAction::triggered, this, + sign_uid_act_ = new QAction(tr("Sign UID"), this); + connect(sign_uid_act_, &QAction::triggered, this, &KeyPairUIDTab::slot_add_sign_single); - auto* del_uid_act = new QAction(tr("Delete UID"), this); - connect(del_uid_act, &QAction::triggered, this, - &KeyPairUIDTab::slot_del_uid_single); + rev_uid_act_ = new QAction(tr("Revoke UID"), this); + connect(rev_uid_act_, &QAction::triggered, this, + &KeyPairUIDTab::slot_rev_uid); + del_uid_act_ = new QAction(tr("Delete UID"), this); + connect(del_uid_act_, &QAction::triggered, this, + &KeyPairUIDTab::slot_del_uid); if (m_key_.IsHasMasterKey()) { - uid_popup_menu_->addAction(ser_primary_uid_act); - uid_popup_menu_->addAction(sign_uid_act); - uid_popup_menu_->addAction(del_uid_act); + uid_popup_menu_->addAction(set_primary_uid_act_); + uid_popup_menu_->addAction(sign_uid_act_); + uid_popup_menu_->addAction(rev_uid_act_); + uid_popup_menu_->addAction(del_uid_act_); } } void KeyPairUIDTab::contextMenuEvent(QContextMenuEvent* event) { if (uid_list_->selectedItems().length() > 0 && sig_list_->selectedItems().isEmpty()) { + auto is_primary_uid = + get_selected_uid().GetUID() == buffered_uids_.front().GetUID(); + set_primary_uid_act_->setDisabled(is_primary_uid); + rev_uid_act_->setDisabled(is_primary_uid); + del_uid_act_->setDisabled(is_primary_uid); + uid_popup_menu_->exec(event->globalPos()); } } void KeyPairUIDTab::slot_add_sign_single() { - auto selected_uids = get_uid_selected(); - - if (selected_uids->empty()) { - QMessageBox::information( - nullptr, tr("Invalid Operation"), - tr("Please select one UID before doing this operation.")); - return; - } + const auto& target_uid = get_selected_uid(); auto* key_sign_dialog = new KeyUIDSignDialog( - current_gpg_context_channel_, m_key_, std::move(selected_uids), this); + current_gpg_context_channel_, m_key_, target_uid.GetUID(), this); key_sign_dialog->show(); } -void KeyPairUIDTab::slot_del_uid_single() { - auto selected_uids = get_uid_selected(); - if (selected_uids->empty()) { - QMessageBox::information( - nullptr, tr("Invalid Operation"), - tr("Please select one UID before doing this operation.")); - return; - } - - QString keynames; - - keynames.append(selected_uids->front()); - keynames.append("<br/>"); - - int ret = QMessageBox::warning( - this, tr("Deleting UID"), - "<b>" + - QString( - tr("Are you sure that you want to delete the following uid?")) + - "</b><br/><br/>" + keynames + +"<br/>" + - tr("The action can not be undone."), - QMessageBox::No | QMessageBox::Yes); - - if (ret == QMessageBox::Yes) { - if (!GpgUIDOperator::GetInstance(current_gpg_context_channel_) - .RevUID(m_key_, selected_uids->front())) { - QMessageBox::critical(nullptr, tr("Operation Failed"), - tr("An error occurred during the operation.")); - } else { - emit SignalUpdateUIDInfo(); - } - } -} - void KeyPairUIDTab::create_sign_popup_menu() { sign_popup_menu_ = new QMenu(this); @@ -595,4 +517,78 @@ void KeyPairUIDTab::slot_refresh_key() { this->slot_refresh_sig_list(); } +void KeyPairUIDTab::slot_rev_uid() { + const auto& target_uid = get_selected_uid(); + + if (target_uid.GetUID() == buffered_uids_.front().GetUID()) { + QMessageBox::information( + nullptr, tr("Invalid Operation"), + tr("Please select one UID before doing this operation.")); + return; + } + + const auto uids = m_key_.GetUIDs(); + + QString message = tr("<h3>Revoke UID Confirmation</h3><br />" + "<b>UID:</b> %1<br /><br />" + "Revoking a UID will make it permanently unusable. " + "This action is <b>irreversible</b>.<br />" + "Are you sure you want to revoke this UID?") + .arg(target_uid.GetUID().toHtmlEscaped()); + + int ret = QMessageBox::warning(this, tr("Revoke UID"), message, + QMessageBox::Cancel | QMessageBox::Yes, + QMessageBox::Cancel); + + if (ret != QMessageBox::Yes) return; + + int index = 1; + for (const auto& uid : buffered_uids_) { + if (uid.GetUID() == target_uid.GetUID()) { + break; + } + index++; + } + + if (index == 1) { + QMessageBox::information(nullptr, tr("Invalid Operation"), + tr("Cannot delete the Primary UID.")); + return; + } + + QStringList codes; + codes << tr("0 -> No Reason.") << tr("4 -> User ID is no longer valid."); + auto* revocation_options_dialog = new RevocationOptionsDialog(codes, this); + + connect(revocation_options_dialog, + &RevocationOptionsDialog::SignalRevokeOptionAccepted, this, + [key = m_key_, index, channel = current_gpg_context_channel_, this]( + int code, const QString& text) { + auto res = GpgUIDOperator::GetInstance(channel).RevokeUID( + key, index, code == 1 ? 4 : 0, text); + if (!res) { + QMessageBox::critical( + nullptr, tr("Revocation Failed"), + tr("Failed to revoke the UID. Please try again.")); + } else { + QMessageBox::information( + nullptr, tr("Revocation Successful"), + tr("The UID has been successfully revoked.")); + emit SignalUpdateUIDInfo(); + } + }); + + revocation_options_dialog->show(); +} + +auto KeyPairUIDTab::get_selected_uid() -> const GpgUID& { + int row = 0; + + for (int i = 0; i < uid_list_->rowCount(); i++) { + if (uid_list_->item(row, 0)->isSelected()) break; + row++; + } + + return buffered_uids_[row]; +} } // namespace GpgFrontend::UI diff --git a/src/ui/dialog/keypair_details/KeyPairUIDTab.h b/src/ui/dialog/keypair_details/KeyPairUIDTab.h index bf0988fa..d97ea2d8 100644 --- a/src/ui/dialog/keypair_details/KeyPairUIDTab.h +++ b/src/ui/dialog/keypair_details/KeyPairUIDTab.h @@ -28,9 +28,7 @@ #pragma once -#include "KeyNewUIDDialog.h" -#include "KeyUIDSignDialog.h" -#include "core/function/gpg/GpgContext.h" +#include "core/GpgModel.h" #include "ui/GpgFrontendUI.h" namespace GpgFrontend::UI { @@ -55,151 +53,142 @@ class KeyPairUIDTab : public QWidget { */ void SignalUpdateUIDInfo(); - private: - int current_gpg_context_channel_; - GpgKey m_key_; - QTableWidget* uid_list_{}; ///< - QTableWidget* sig_list_{}; ///< - QTabWidget* tofu_tabs_{}; ///< - QMenu* manage_selected_uid_menu_{}; ///< - QMenu* uid_popup_menu_{}; ///< - QMenu* sign_popup_menu_{}; ///< - std::vector<GpgUID> buffered_uids_; ///< - std::vector<GpgKeySignature> buffered_signatures_; ///< - - /** - * @brief Create a uid list object - * - */ - void create_uid_list(); - + protected: /** - * @brief Create a sign list object + * @brief * + * @param event */ - void create_sign_list(); + void contextMenuEvent(QContextMenuEvent* event) override; - /** - * @brief Create a manage uid menu object - * - */ - void create_manage_uid_menu(); + private slots: /** - * @brief Create a uid popup menu object + * @brief * */ - void create_uid_popup_menu(); + void slot_refresh_uid_list(); /** - * @brief Create a sign popup menu object + * @brief * */ - void create_sign_popup_menu(); + void slot_refresh_tofu_info(); /** - * @brief Get the uid checked object + * @brief * - * @return UIDArgsListPtr */ - UIDArgsListPtr get_uid_checked(); + void slot_refresh_sig_list(); /** - * @brief Get the uid selected object + * @brief * - * @return UIDArgsListPtr */ - UIDArgsListPtr get_uid_selected(); + void slot_add_sign(); /** - * @brief Get the sign selected object + * @brief * - * @return SignIdArgsListPtr */ - SignIdArgsListPtr get_sign_selected(); - - private slots: + void slot_add_sign_single(); /** * @brief * */ - void slot_refresh_uid_list(); + void slot_add_uid(); /** * @brief * */ - void slot_refresh_tofu_info(); + void slot_del_uid(); /** * @brief * */ - void slot_refresh_sig_list(); + void slot_rev_uid(); /** * @brief * */ - void slot_add_sign(); + void slot_set_primary_uid(); /** * @brief * */ - void slot_add_sign_single(); + void slot_del_sign(); /** * @brief * */ - void slot_add_uid(); + void slot_refresh_key(); /** * @brief * + * @param result */ - void slot_del_uid(); + static void slot_add_uid_result(int result); + + private: + int current_gpg_context_channel_; + GpgKey m_key_; + QTableWidget* uid_list_{}; ///< + QTableWidget* sig_list_{}; ///< + QTabWidget* tofu_tabs_{}; ///< + QMenu* uid_popup_menu_{}; ///< + QMenu* sign_popup_menu_{}; ///< + std::vector<GpgUID> buffered_uids_; ///< + std::vector<GpgKeySignature> buffered_signatures_; ///< + + QAction* set_primary_uid_act_; + QAction* sign_uid_act_; + QAction* rev_uid_act_; + QAction* del_uid_act_; /** - * @brief + * @brief Create a uid list object * */ - void slot_del_uid_single(); + void create_uid_list(); /** - * @brief + * @brief Create a sign list object * */ - void slot_set_primary_uid(); + void create_sign_list(); /** - * @brief + * @brief Create a uid popup menu object * */ - void slot_del_sign(); + void create_uid_popup_menu(); /** - * @brief + * @brief Create a sign popup menu object * */ - void slot_refresh_key(); + void create_sign_popup_menu(); /** - * @brief + * @brief Get the sign selected object * - * @param result + * @return SignIdArgsListPtr */ - static void slot_add_uid_result(int result); + auto get_sign_selected() -> SignIdArgsListPtr; - protected: /** - * @brief + * @brief Get the sign selected object * - * @param event + * @return SignIdArgsListPtr */ - void contextMenuEvent(QContextMenuEvent* event) override; + auto get_selected_uid() -> const GpgUID&; }; } // namespace GpgFrontend::UI diff --git a/src/ui/dialog/keypair_details/KeyUIDSignDialog.cpp b/src/ui/dialog/keypair_details/KeyUIDSignDialog.cpp index d34fb99a..1c99539b 100644 --- a/src/ui/dialog/keypair_details/KeyUIDSignDialog.cpp +++ b/src/ui/dialog/keypair_details/KeyUIDSignDialog.cpp @@ -36,10 +36,10 @@ namespace GpgFrontend::UI { KeyUIDSignDialog::KeyUIDSignDialog(int channel, const GpgKey& key, - UIDArgsListPtr uid, QWidget* parent) + const QString& uid, QWidget* parent) : GeneralDialog(typeid(KeyUIDSignDialog).name(), parent), current_gpg_context_channel_(channel), - m_uids_(std::move(uid)), + m_uid_(uid), m_key_(key) { assert(m_key_.IsGood()); @@ -114,14 +114,12 @@ void KeyUIDSignDialog::slot_sign_key(bool clicked) { auto expires = std::make_unique<QDateTime>(expires_edit_->dateTime()); - for (const auto& uid : *m_uids_) { - // Sign For mKey - if (!GpgKeyManager::GetInstance(current_gpg_context_channel_) - .SignKey(m_key_, *keys, uid, expires)) { - QMessageBox::critical( - nullptr, tr("Unsuccessful Operation"), - tr("Signature operation failed for UID %1").arg(uid)); - } + // Sign For mKey + if (!GpgKeyManager::GetInstance(current_gpg_context_channel_) + .SignKey(m_key_, *keys, m_uid_, expires)) { + QMessageBox::critical( + nullptr, tr("Unsuccessful Operation"), + tr("Signature operation failed for UID %1").arg(m_uid_)); } QMessageBox::information( diff --git a/src/ui/dialog/keypair_details/KeyUIDSignDialog.h b/src/ui/dialog/keypair_details/KeyUIDSignDialog.h index 326321a0..e40529fd 100644 --- a/src/ui/dialog/keypair_details/KeyUIDSignDialog.h +++ b/src/ui/dialog/keypair_details/KeyUIDSignDialog.h @@ -45,7 +45,7 @@ class KeyUIDSignDialog : public GeneralDialog { * @param uid * @param parent */ - explicit KeyUIDSignDialog(int channel, const GpgKey& key, UIDArgsListPtr uid, + explicit KeyUIDSignDialog(int channel, const GpgKey& key, const QString& uid, QWidget* parent = nullptr); signals: @@ -61,7 +61,7 @@ class KeyUIDSignDialog : public GeneralDialog { QPushButton* sign_key_button_; ///< QDateTimeEdit* expires_edit_; ///< QCheckBox* non_expire_check_; ///< - UIDArgsListPtr m_uids_; ///< + QString m_uid_; ///< const GpgKey& m_key_; ///< diff --git a/src/ui/main_window/MainWindow.h b/src/ui/main_window/MainWindow.h index 11c97884..ef3fda13 100644 --- a/src/ui/main_window/MainWindow.h +++ b/src/ui/main_window/MainWindow.h @@ -425,7 +425,7 @@ class MainWindow : public GeneralMainWindow { * @details Disable tab related actions, if number of tabs is 0. * @param number number of the opened tabs and -1, if no tab is opened */ - void slot_switch_menu_control_mode(int number); + void slot_switch_menu_control_mode(int); /** * @details called when need to upgrade. |