aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsaturneric <[email protected]>2024-11-26 22:04:00 +0000
committersaturneric <[email protected]>2024-11-26 22:07:04 +0000
commitaf776283bd5d50363c8777dbb3524fde417cc368 (patch)
treea633e2e8161dc7b9048af25d81e284c116e217cc
parentfeat: support eml data decryption (diff)
downloadGpgFrontend-af776283bd5d50363c8777dbb3524fde417cc368.tar.gz
GpgFrontend-af776283bd5d50363c8777dbb3524fde417cc368.zip
feat: support uid revoke and uid delete operations
-rw-r--r--src/core/function/gpg/GpgAutomatonHandler.cpp157
-rw-r--r--src/core/function/gpg/GpgAutomatonHandler.h110
-rw-r--r--src/core/function/gpg/GpgKeyManager.cpp327
-rw-r--r--src/core/function/gpg/GpgKeyManager.h57
-rw-r--r--src/core/function/gpg/GpgUIDOperator.cpp220
-rw-r--r--src/core/function/gpg/GpgUIDOperator.h25
-rw-r--r--src/test/core/GpgCoreTestKeyManagement.cpp8
-rw-r--r--src/test/core/GpgCoreTestKeyUIDOpera.cpp136
-rw-r--r--src/ui/dialog/RevocationOptionsDialog.cpp7
-rw-r--r--src/ui/dialog/RevocationOptionsDialog.h2
-rw-r--r--src/ui/dialog/keypair_details/KeyPairOperaTab.cpp5
-rw-r--r--src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp7
-rw-r--r--src/ui/dialog/keypair_details/KeyPairUIDTab.cpp286
-rw-r--r--src/ui/dialog/keypair_details/KeyPairUIDTab.h119
-rw-r--r--src/ui/dialog/keypair_details/KeyUIDSignDialog.cpp18
-rw-r--r--src/ui/dialog/keypair_details/KeyUIDSignDialog.h4
-rw-r--r--src/ui/main_window/MainWindow.h2
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.