aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsaturneric <[email protected]>2024-11-18 16:53:29 +0000
committersaturneric <[email protected]>2024-11-18 16:53:55 +0000
commita5d7cc6aa2951b20064108fadd39eaae3b8472b2 (patch)
treecc93a20b8be1e1c8571bb8d9ea3c8eba379ed6a6
parentfeat: make primary key and subkey clearly (diff)
downloadGpgFrontend-a5d7cc6aa2951b20064108fadd39eaae3b8472b2.tar.gz
GpgFrontend-a5d7cc6aa2951b20064108fadd39eaae3b8472b2.zip
feat: add delete subkey function
-rw-r--r--src/core/function/gpg/GpgKeyManager.cpp186
-rw-r--r--src/core/function/gpg/GpgKeyManager.h13
-rw-r--r--src/test/core/GpgCoreTestKeyManagement.cpp216
-rw-r--r--src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp65
-rw-r--r--src/ui/dialog/keypair_details/KeyPairSubkeyTab.h15
-rw-r--r--src/ui/function/SetOwnerTrustLevel.cpp9
-rw-r--r--src/ui/main_window/MainWindow.cpp2
7 files changed, 443 insertions, 63 deletions
diff --git a/src/core/function/gpg/GpgKeyManager.cpp b/src/core/function/gpg/GpgKeyManager.cpp
index 18a8751e..bd52c341 100644
--- a/src/core/function/gpg/GpgKeyManager.cpp
+++ b/src/core/function/gpg/GpgKeyManager.cpp
@@ -94,60 +94,55 @@ auto GpgFrontend::GpgKeyManager::SetExpire(
auto GpgFrontend::GpgKeyManager::SetOwnerTrustLevel(const GpgKey& key,
int trust_level) -> bool {
- if (trust_level < 0 || trust_level > 5) {
+ if (trust_level < 1 || trust_level > 5) {
FLOG_W("illegal owner trust level: %d", trust_level);
}
- AutomatonNextStateHandler next_state_handler = [](AutomatonState state,
- QString status,
- QString args) {
- LOG_D() << "next_state_handler state: " << static_cast<unsigned int>(state)
- << ", gpg_status: " << status << ", args: " << args;
+ AutomatonNextStateHandler next_state_handler =
+ [](AutomatonState state, QString status, QString args) {
+ auto tokens = args.split(' ');
- auto tokens = args.split(' ');
-
- switch (state) {
- case AS_START:
- if (status == "GET_LINE" && args == "keyedit.prompt") {
- return AS_COMMAND;
- }
- return AS_ERROR;
- case AS_COMMAND:
- if (status == "GET_LINE" && args == "edit_ownertrust.value") {
- return AS_VALUE;
- }
- return AS_ERROR;
- case AS_VALUE:
- if (status == "GET_LINE" && args == "keyedit.prompt") {
- return AS_QUIT;
- } else if (status == "GET_BOOL" &&
- args == "edit_ownertrust.set_ultimate.okay") {
- return AS_REALLY_ULTIMATE;
- }
- return AS_ERROR;
- case AS_REALLY_ULTIMATE:
- if (status == "GET_LINE" && args == "keyedit.prompt") {
- return AS_QUIT;
- }
- return AS_ERROR;
- case AS_QUIT:
- if (status == "GET_LINE" && args == "keyedit.save.okay") {
- return AS_SAVE;
- }
- return AS_ERROR;
- case AS_ERROR:
- if (status == "GET_LINE" && args == "keyedit.prompt") {
- return AS_QUIT;
- }
- return AS_ERROR;
- default:
- return AS_ERROR;
- };
- };
+ switch (state) {
+ case AS_START:
+ if (status == "GET_LINE" && args == "keyedit.prompt") {
+ return AS_COMMAND;
+ }
+ return AS_ERROR;
+ case AS_COMMAND:
+ if (status == "GET_LINE" && args == "edit_ownertrust.value") {
+ return AS_VALUE;
+ }
+ return AS_ERROR;
+ case AS_VALUE:
+ if (status == "GET_LINE" && args == "keyedit.prompt") {
+ return AS_QUIT;
+ } else if (status == "GET_BOOL" &&
+ args == "edit_ownertrust.set_ultimate.okay") {
+ return AS_REALLY_ULTIMATE;
+ }
+ return AS_ERROR;
+ case AS_REALLY_ULTIMATE:
+ if (status == "GET_LINE" && args == "keyedit.prompt") {
+ return AS_QUIT;
+ }
+ return AS_ERROR;
+ case AS_QUIT:
+ if (status == "GET_BOOL" && args == "keyedit.save.okay") {
+ return AS_SAVE;
+ }
+ return AS_ERROR;
+ case AS_ERROR:
+ if (status == "GET_LINE" && args == "keyedit.prompt") {
+ return AS_QUIT;
+ }
+ return AS_ERROR;
+ default:
+ return AS_ERROR;
+ };
+ };
AutomatonActionHandler action_handler =
[trust_level](AutomatonHandelStruct& handler, AutomatonState state) {
- FLOG_D("action_handler state: %d", static_cast<unsigned int>(state));
switch (state) {
case AS_COMMAND:
return QString("trust");
@@ -210,11 +205,15 @@ auto GpgFrontend::GpgKeyManager::interactor_cb_fnc(void* handle,
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, skipping...");
- return GPG_ERR_FALSE;
+ 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);
@@ -224,6 +223,8 @@ auto GpgFrontend::GpgKeyManager::interactor_cb_fnc(void* handle,
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());
@@ -234,4 +235,89 @@ auto GpgFrontend::GpgKeyManager::interactor_cb_fnc(void* handle,
}
return 0;
-} \ No newline at end of file
+}
+auto GpgFrontend::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;
+ }
+
+ AutomatonNextStateHandler next_state_handler =
+ [](AutomatonState state, QString status, QString args) {
+ auto tokens = args.split(' ');
+
+ switch (state) {
+ case AS_START:
+ if (status == "GET_LINE" && args == "keyedit.prompt") {
+ return AS_SELECT;
+ }
+ return AS_ERROR;
+ case AS_SELECT:
+ if (status == "GET_LINE" && args == "keyedit.prompt") {
+ return AS_COMMAND;
+ }
+ return AS_ERROR;
+ case AS_COMMAND:
+ if (status == "GET_LINE" && args == "keyedit.prompt") {
+ return AS_QUIT;
+ } else if (status == "GET_BOOL" &&
+ args == "keyedit.remove.subkey.okay") {
+ return AS_REALLY_ULTIMATE;
+ }
+ return AS_ERROR;
+ case AS_REALLY_ULTIMATE:
+ if (status == "GET_LINE" && args == "keyedit.prompt") {
+ return AS_QUIT;
+ }
+ return AS_ERROR;
+ case AS_QUIT:
+ if (status == "GET_BOOL" && args == "keyedit.save.okay") {
+ return AS_SAVE;
+ }
+ return AS_ERROR;
+ case AS_ERROR:
+ if (status == "GET_LINE" && args == "keyedit.prompt") {
+ return AS_QUIT;
+ }
+ return AS_ERROR;
+ default:
+ return AS_ERROR;
+ };
+ };
+
+ AutomatonActionHandler action_handler =
+ [subkey_index](AutomatonHandelStruct& handler, AutomatonState state) {
+ switch (state) {
+ case AS_SELECT:
+ return QString("key %1").arg(subkey_index);
+ case AS_COMMAND:
+ return QString("delkey");
+ case AS_REALLY_ULTIMATE:
+ handler.SetSuccess(true);
+ return QString("Y");
+ case AS_QUIT:
+ return QString("quit");
+ case AS_SAVE:
+ handler.SetSuccess(true);
+ return QString("Y");
+ case AS_START:
+ case 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;
+
+ 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();
+}
diff --git a/src/core/function/gpg/GpgKeyManager.h b/src/core/function/gpg/GpgKeyManager.h
index a34b34c1..83a38d05 100644
--- a/src/core/function/gpg/GpgKeyManager.h
+++ b/src/core/function/gpg/GpgKeyManager.h
@@ -91,6 +91,16 @@ class GPGFRONTEND_CORE_EXPORT GpgKeyManager
*/
auto SetOwnerTrustLevel(const GpgKey& key, int trust_level) -> bool;
+ /**
+ * @brief
+ *
+ * @param key
+ * @param subkey_index
+ * @return true
+ * @return false
+ */
+ auto DeleteSubkey(const GpgKey& key, int subkey_index) -> bool;
+
private:
static auto interactor_cb_fnc(void* handle, const char* status,
const char* args, int fd) -> gpgme_error_t;
@@ -98,6 +108,7 @@ class GPGFRONTEND_CORE_EXPORT GpgKeyManager
using Command = QString;
using AutomatonState = enum {
AS_START,
+ AS_SELECT,
AS_COMMAND,
AS_VALUE,
AS_REALLY_ULTIMATE,
@@ -115,7 +126,7 @@ class GPGFRONTEND_CORE_EXPORT GpgKeyManager
struct AutomatonHandelStruct {
void SetStatus(AutomatonState next_state) { current_state_ = next_state; }
- auto CuurentStatus() -> AutomatonState { return current_state_; }
+ auto CurrentStatus() -> AutomatonState { return current_state_; }
void SetHandler(AutomatonNextStateHandler next_state_handler,
AutomatonActionHandler action_handler) {
next_state_handler_ = std::move(next_state_handler);
diff --git a/src/test/core/GpgCoreTestKeyManagement.cpp b/src/test/core/GpgCoreTestKeyManagement.cpp
new file mode 100644
index 00000000..114c237a
--- /dev/null
+++ b/src/test/core/GpgCoreTestKeyManagement.cpp
@@ -0,0 +1,216 @@
+/**
+ * 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/GpgKeyManager.h"
+#include "core/function/gpg/GpgKeyOpera.h"
+#include "core/model/GpgImportInformation.h"
+#include "core/utils/GpgUtils.h"
+
+const char *TEST_PRIVATE_KEY_DATA = R"(
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+
+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
+=OHnq
+-----END PGP PRIVATE KEY BLOCK-----
+)";
+
+namespace GpgFrontend::Test {
+TEST_F(GpgCoreTest, CoreDeleteSubkeyTestA) {
+ 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("822D7E13F5B85D7D");
+ ASSERT_TRUE(key.IsGood());
+
+ auto subkeys = key.GetSubKeys();
+
+ ASSERT_EQ(subkeys->size(), 5);
+ ASSERT_EQ((*subkeys)[2].GetID(), "2D1F9FC59B568A8C");
+
+ auto res = GpgKeyManager::GetInstance().DeleteSubkey(key, 2);
+
+ ASSERT_TRUE(res);
+
+ key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel)
+ .GetKey("822D7E13F5B85D7D");
+ ASSERT_TRUE(key.IsGood());
+
+ subkeys = key.GetSubKeys();
+
+ ASSERT_EQ(subkeys->size(), 4);
+ ASSERT_EQ((*subkeys)[2].GetID(), "CE038203C4D03C3D");
+
+ GpgKeyOpera::GetInstance().DeleteKey(key.GetId());
+}
+
+TEST_F(GpgCoreTest, CoreSetOwnerTrustA) {
+ 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("822D7E13F5B85D7D");
+ ASSERT_TRUE(key.IsGood());
+
+ auto res = GpgKeyManager::GetInstance().SetOwnerTrustLevel(key, 1);
+ ASSERT_TRUE(res);
+
+ GpgKeyGetter::GetInstance().FlushKeyCache();
+ key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel)
+ .GetKey("822D7E13F5B85D7D");
+ ASSERT_TRUE(key.IsGood());
+
+ // why?
+ ASSERT_EQ(key.GetOwnerTrustLevel(), 0);
+
+ res = GpgKeyManager::GetInstance().SetOwnerTrustLevel(key, 2);
+ ASSERT_TRUE(res);
+
+ GpgKeyGetter::GetInstance().FlushKeyCache();
+ key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel)
+ .GetKey("822D7E13F5B85D7D");
+ ASSERT_TRUE(key.IsGood());
+
+ ASSERT_EQ(key.GetOwnerTrustLevel(), 2);
+
+ res = GpgKeyManager::GetInstance().SetOwnerTrustLevel(key, 3);
+ ASSERT_TRUE(res);
+
+ GpgKeyGetter::GetInstance().FlushKeyCache();
+ key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel)
+ .GetKey("822D7E13F5B85D7D");
+ ASSERT_TRUE(key.IsGood());
+
+ ASSERT_EQ(key.GetOwnerTrustLevel(), 3);
+
+ res = GpgKeyManager::GetInstance().SetOwnerTrustLevel(key, 4);
+ ASSERT_TRUE(res);
+
+ GpgKeyGetter::GetInstance().FlushKeyCache();
+ key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel)
+ .GetKey("822D7E13F5B85D7D");
+ ASSERT_TRUE(key.IsGood());
+
+ ASSERT_EQ(key.GetOwnerTrustLevel(), 4);
+
+ res = GpgKeyManager::GetInstance().SetOwnerTrustLevel(key, 5);
+ ASSERT_TRUE(res);
+
+ GpgKeyGetter::GetInstance().FlushKeyCache();
+ key = GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel)
+ .GetKey("822D7E13F5B85D7D");
+ ASSERT_TRUE(key.IsGood());
+
+ ASSERT_EQ(key.GetOwnerTrustLevel(), 5);
+
+ res = GpgKeyManager::GetInstance().SetOwnerTrustLevel(key, 0);
+ ASSERT_FALSE(res);
+
+ res = GpgKeyManager::GetInstance().SetOwnerTrustLevel(key, -1);
+ ASSERT_FALSE(res);
+
+ res = GpgKeyManager::GetInstance().SetOwnerTrustLevel(key, 6);
+ ASSERT_FALSE(res);
+
+ GpgKeyOpera::GetInstance().DeleteKey(key.GetId());
+}
+
+} // namespace GpgFrontend::Test \ No newline at end of file
diff --git a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp
index 43bebe86..335f9c53 100644
--- a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp
+++ b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp
@@ -31,6 +31,7 @@
#include "core/GpgModel.h"
#include "core/function/gpg/GpgKeyGetter.h"
#include "core/function/gpg/GpgKeyImportExporter.h"
+#include "core/function/gpg/GpgKeyManager.h"
#include "core/utils/CommonUtils.h"
#include "core/utils/GpgUtils.h"
#include "core/utils/IOUtils.h"
@@ -149,6 +150,11 @@ KeyPairSubkeyTab::KeyPairSubkeyTab(int channel, const QString& key_id,
setAttribute(Qt::WA_DeleteOnClose, true);
slot_refresh_subkey_list();
+
+ // set up signal
+ connect(this, &KeyPairSubkeyTab::SignalKeyDatabaseRefresh,
+ UISignalStation::GetInstance(),
+ &UISignalStation::SignalKeyDatabaseRefresh);
}
void KeyPairSubkeyTab::create_subkey_list() {
@@ -338,8 +344,13 @@ void KeyPairSubkeyTab::create_subkey_opera_menu() {
connect(export_subkey_act_, &QAction::triggered, this,
&KeyPairSubkeyTab::slot_export_subkey);
+ delete_subkey_act_ = new QAction(tr("Delete"));
+ connect(delete_subkey_act_, &QAction::triggered, this,
+ &KeyPairSubkeyTab::slot_delete_subkey);
+
subkey_opera_menu_->addAction(export_subkey_act_);
subkey_opera_menu_->addAction(edit_subkey_act_);
+ subkey_opera_menu_->addAction(delete_subkey_act_);
}
void KeyPairSubkeyTab::slot_edit_subkey() {
@@ -359,6 +370,7 @@ void KeyPairSubkeyTab::contextMenuEvent(QContextMenuEvent* event) {
export_subkey_act_->setDisabled(!subkey.IsSecretKey());
edit_subkey_act_->setDisabled(!subkey.IsSecretKey());
+ delete_subkey_act_->setDisabled(!subkey.IsSecretKey());
subkey_opera_menu_->exec(event->globalPos());
}
@@ -391,11 +403,7 @@ void KeyPairSubkeyTab::slot_export_subkey() {
tr("Do you want to proceed with exporting this subkey?"),
QMessageBox::Cancel | QMessageBox::Yes, QMessageBox::Cancel);
- if (ret != QMessageBox::Yes) {
- QMessageBox::information(this, tr("Export Cancelled"),
- tr("The subkey export has been cancelled."));
- return;
- }
+ if (ret != QMessageBox::Yes) return;
const auto& subkey = get_selected_subkey();
@@ -431,4 +439,51 @@ void KeyPairSubkeyTab::slot_export_subkey() {
}
}
+void KeyPairSubkeyTab::slot_delete_subkey() {
+ const auto& subkey = get_selected_subkey();
+ const auto subkeys = key_.GetSubKeys();
+
+ QString message = tr("<h3>You are about to delete the subkey:</h3><br />"
+ "<b>KeyID:</b> %1<br /><br />"
+ "This action is irreversible. Please confirm.")
+ .arg(subkey.GetID());
+
+ int ret = QMessageBox::warning(
+ this, tr("Delete Subkey Confirmation"), message,
+ QMessageBox::Cancel | QMessageBox::Yes, QMessageBox::Cancel);
+
+ if (ret != QMessageBox::Yes) return;
+
+ int index = 0;
+ for (const auto& sk : *subkeys) {
+ if (sk.GetFingerprint() == subkey.GetFingerprint()) {
+ break;
+ }
+ index++;
+ }
+
+ if (index == 0) {
+ QMessageBox::critical(
+ this, tr("Illegal Operation"),
+ tr("Cannot delete the primary key or an invalid subkey."));
+ return;
+ }
+
+ auto res = GpgKeyManager::GetInstance(current_gpg_context_channel_)
+ .DeleteSubkey(key_, index);
+
+ if (!res) {
+ QMessageBox::critical(this, tr("Operation Failed"),
+ tr("The selected subkey could not be deleted. "
+ "Please check your permissions or try again."));
+ return;
+ }
+
+ QMessageBox::information(
+ this, tr("Operation Successful"),
+ tr("The subkey with KeyID %1 has been successfully deleted.")
+ .arg(subkey.GetID()));
+
+ emit SignalKeyDatabaseRefresh();
+}
} // namespace GpgFrontend::UI
diff --git a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.h b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.h
index 4e626ae5..1222b77d 100644
--- a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.h
+++ b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.h
@@ -93,6 +93,7 @@ class KeyPairSubkeyTab : public QWidget {
QAction* export_subkey_act_;
QAction* edit_subkey_act_;
+ QAction* delete_subkey_act_;
private slots:
@@ -138,6 +139,20 @@ class KeyPairSubkeyTab : public QWidget {
*/
void slot_export_subkey();
+ /**
+ * @brief
+ *
+ */
+ void slot_delete_subkey();
+
+ signals:
+
+ /**
+ * @brief
+ *
+ */
+ void SignalKeyDatabaseRefresh();
+
protected:
/**
* @brief
diff --git a/src/ui/function/SetOwnerTrustLevel.cpp b/src/ui/function/SetOwnerTrustLevel.cpp
index d3ca33b3..b0fa8b0f 100644
--- a/src/ui/function/SetOwnerTrustLevel.cpp
+++ b/src/ui/function/SetOwnerTrustLevel.cpp
@@ -47,8 +47,8 @@ auto SetOwnerTrustLevel::Exec(int channel, const QString& key_id) -> bool {
QStringList items;
- items << tr("Unknown") << tr("Undefined") << tr("Never") << tr("Marginal")
- << tr("Full") << tr("Ultimate");
+ items << tr("Undefined") << tr("Never") << tr("Marginal") << tr("Full")
+ << tr("Ultimate");
bool ok;
QString item = QInputDialog::getItem(this, tr("Modify Owner Trust Level"),
tr("Trust for the Key Pair:"), items,
@@ -69,11 +69,6 @@ auto SetOwnerTrustLevel::Exec(int channel, const QString& key_id) -> bool {
}
if (trust_level == 0) {
- QMessageBox::warning(
- this, tr("Warning"),
- QString(
- tr("Owner Trust Level cannot set to Unknown level, automately "
- "changing it into Undefined level.")));
trust_level = 1;
}
diff --git a/src/ui/main_window/MainWindow.cpp b/src/ui/main_window/MainWindow.cpp
index fc320391..0c88412a 100644
--- a/src/ui/main_window/MainWindow.cpp
+++ b/src/ui/main_window/MainWindow.cpp
@@ -202,6 +202,8 @@ void MainWindow::recover_editor_unsaved_pages_from_cache() {
auto title = unsaved_page_json["title"].toString();
auto content = unsaved_page_json["content"].toString();
+ LOG_D() << "restoring tab, title: " << title;
+
if (first) {
edit_->SlotCloseTab();
first = false;