aboutsummaryrefslogtreecommitdiffstats
path: root/src/ui/dialog/keypair_details
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/dialog/keypair_details')
-rw-r--r--src/ui/dialog/keypair_details/KeyDetailsDialog.cpp62
-rw-r--r--src/ui/dialog/keypair_details/KeyDetailsDialog.h49
-rw-r--r--src/ui/dialog/keypair_details/KeyNewUIDDialog.cpp114
-rw-r--r--src/ui/dialog/keypair_details/KeyNewUIDDialog.h88
-rw-r--r--src/ui/dialog/keypair_details/KeyPairDetailTab.cpp276
-rw-r--r--src/ui/dialog/keypair_details/KeyPairDetailTab.h99
-rw-r--r--src/ui/dialog/keypair_details/KeyPairOperaTab.cpp360
-rw-r--r--src/ui/dialog/keypair_details/KeyPairOperaTab.h113
-rw-r--r--src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp338
-rw-r--r--src/ui/dialog/keypair_details/KeyPairSubkeyTab.h139
-rw-r--r--src/ui/dialog/keypair_details/KeyPairUIDTab.cpp583
-rw-r--r--src/ui/dialog/keypair_details/KeyPairUIDTab.h207
-rw-r--r--src/ui/dialog/keypair_details/KeySetExpireDateDialog.cpp145
-rw-r--r--src/ui/dialog/keypair_details/KeySetExpireDateDialog.h99
-rw-r--r--src/ui/dialog/keypair_details/KeyUIDSignDialog.cpp134
-rw-r--r--src/ui/dialog/keypair_details/KeyUIDSignDialog.h78
16 files changed, 2884 insertions, 0 deletions
diff --git a/src/ui/dialog/keypair_details/KeyDetailsDialog.cpp b/src/ui/dialog/keypair_details/KeyDetailsDialog.cpp
new file mode 100644
index 00000000..9c2f8003
--- /dev/null
+++ b/src/ui/dialog/keypair_details/KeyDetailsDialog.cpp
@@ -0,0 +1,62 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * 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 "KeyDetailsDialog.h"
+
+#include "KeyPairDetailTab.h"
+#include "KeyPairOperaTab.h"
+#include "KeyPairSubkeyTab.h"
+#include "KeyPairUIDTab.h"
+
+namespace GpgFrontend::UI {
+KeyDetailsDialog::KeyDetailsDialog(const GpgKey& key, QWidget* parent)
+ : GeneralDialog(typeid(KeyDetailsDialog).name(), parent) {
+ tab_widget_ = new QTabWidget();
+ tab_widget_->addTab(new KeyPairDetailTab(key.GetId(), tab_widget_),
+ _("KeyPair"));
+ tab_widget_->addTab(new KeyPairUIDTab(key.GetId(), tab_widget_), _("UIDs"));
+ tab_widget_->addTab(new KeyPairSubkeyTab(key.GetId(), tab_widget_),
+ _("Subkeys"));
+ tab_widget_->addTab(new KeyPairOperaTab(key.GetId(), tab_widget_),
+ _("Operations"));
+
+ auto* mainLayout = new QVBoxLayout;
+ mainLayout->addWidget(tab_widget_);
+
+#ifdef MACOS
+ setAttribute(Qt::WA_LayoutUsesWidgetRect);
+#endif
+ this->setAttribute(Qt::WA_DeleteOnClose, true);
+ this->setLayout(mainLayout);
+ this->setWindowTitle(_("Key Details"));
+ this->setModal(true);
+ this->setMinimumSize({520, 600});
+ this->resize(this->minimumSize());
+ this->show();
+}
+} // namespace GpgFrontend::UI
diff --git a/src/ui/dialog/keypair_details/KeyDetailsDialog.h b/src/ui/dialog/keypair_details/KeyDetailsDialog.h
new file mode 100644
index 00000000..1ddcda00
--- /dev/null
+++ b/src/ui/dialog/keypair_details/KeyDetailsDialog.h
@@ -0,0 +1,49 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * 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
+ *
+ */
+
+#ifndef __KEYDETAILSDIALOG_H__
+#define __KEYDETAILSDIALOG_H__
+
+#include "core/GpgContext.h"
+#include "ui/GpgFrontendUI.h"
+#include "ui/dialog/GeneralDialog.h"
+
+namespace GpgFrontend::UI {
+
+class KeyDetailsDialog : public GeneralDialog {
+ Q_OBJECT
+
+ public:
+ explicit KeyDetailsDialog(const GpgKey& key, QWidget* parent = nullptr);
+
+ private:
+ QTabWidget* tab_widget_{};
+};
+} // namespace GpgFrontend::UI
+
+#endif // __KEYDETAILSDIALOG_H__
diff --git a/src/ui/dialog/keypair_details/KeyNewUIDDialog.cpp b/src/ui/dialog/keypair_details/KeyNewUIDDialog.cpp
new file mode 100644
index 00000000..18dd1967
--- /dev/null
+++ b/src/ui/dialog/keypair_details/KeyNewUIDDialog.cpp
@@ -0,0 +1,114 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * 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.
+ *
+ * The source code version of this software was modified and released
+ * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021.
+ *
+ */
+
+#include "KeyNewUIDDialog.h"
+
+#include "core/function/gpg/GpgKeyGetter.h"
+#include "core/function/gpg/GpgUIDOperator.h"
+#include "ui/SignalStation.h"
+
+namespace GpgFrontend::UI {
+KeyNewUIDDialog::KeyNewUIDDialog(const KeyId& key_id, QWidget* parent)
+ : GeneralDialog(typeid(KeyNewUIDDialog).name(), parent),
+ m_key_(GpgKeyGetter::GetInstance().GetKey(key_id)) {
+ name_ = new QLineEdit();
+ name_->setMinimumWidth(240);
+ email_ = new QLineEdit();
+ email_->setMinimumWidth(240);
+ comment_ = new QLineEdit();
+ comment_->setMinimumWidth(240);
+ create_button_ = new QPushButton("Create");
+ error_label_ = new QLabel();
+
+ auto gridLayout = new QGridLayout();
+ gridLayout->addWidget(new QLabel(_("Name")), 0, 0);
+ gridLayout->addWidget(new QLabel(_("Email")), 1, 0);
+ gridLayout->addWidget(new QLabel(_("Comment")), 2, 0);
+
+ gridLayout->addWidget(name_, 0, 1);
+ gridLayout->addWidget(email_, 1, 1);
+ gridLayout->addWidget(comment_, 2, 1);
+
+ gridLayout->addWidget(create_button_, 3, 0, 1, 2);
+ gridLayout->addWidget(
+ new QLabel(_("Notice: The New UID Created will be set as Primary.")), 4,
+ 0, 1, 2);
+ gridLayout->addWidget(error_label_, 5, 0, 1, 2);
+
+ connect(create_button_, &QPushButton::clicked, this,
+ &KeyNewUIDDialog::slot_create_new_uid);
+
+ this->setLayout(gridLayout);
+ this->setWindowTitle(_("Create New UID"));
+ this->setAttribute(Qt::WA_DeleteOnClose, true);
+ this->setModal(true);
+
+ connect(this, &KeyNewUIDDialog::SignalUIDCreated,
+ SignalStation::GetInstance(),
+ &SignalStation::SignalKeyDatabaseRefresh);
+}
+
+void KeyNewUIDDialog::slot_create_new_uid() {
+ std::stringstream error_stream;
+
+ /**
+ * check for errors in keygen dialog input
+ */
+ if ((name_->text()).size() < 5) {
+ error_stream << " " << _("Name must contain at least five characters.")
+ << std::endl;
+ }
+ if (email_->text().isEmpty() || !check_email_address(email_->text())) {
+ error_stream << " " << _("Please give a email address.") << std::endl;
+ }
+ auto error_string = error_stream.str();
+ if (error_string.empty()) {
+ if (GpgUIDOperator::GetInstance().AddUID(
+ m_key_, name_->text().toStdString(), comment_->text().toStdString(),
+ email_->text().toStdString())) {
+ emit finished(1);
+ emit SignalUIDCreated();
+ } else
+ emit finished(-1);
+
+ } else {
+ /**
+ * create error message
+ */
+ error_label_->setAutoFillBackground(true);
+ QPalette error = error_label_->palette();
+ error.setColor(QPalette::Window, "#ff8080");
+ error_label_->setPalette(error);
+ error_label_->setText(error_string.c_str());
+
+ this->show();
+ }
+}
+
+bool KeyNewUIDDialog::check_email_address(const QString& str) {
+ return re_email_.match(str).hasMatch();
+}
+} // namespace GpgFrontend::UI
diff --git a/src/ui/dialog/keypair_details/KeyNewUIDDialog.h b/src/ui/dialog/keypair_details/KeyNewUIDDialog.h
new file mode 100644
index 00000000..291b59c4
--- /dev/null
+++ b/src/ui/dialog/keypair_details/KeyNewUIDDialog.h
@@ -0,0 +1,88 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * 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.
+ *
+ * The source code version of this software was modified and released
+ * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021.
+ *
+ */
+
+#ifndef GPGFRONTEND_KEYNEWUIDDIALOG_H
+#define GPGFRONTEND_KEYNEWUIDDIALOG_H
+
+#include "core/GpgContext.h"
+#include "ui/GpgFrontendUI.h"
+#include "ui/dialog/GeneralDialog.h"
+
+namespace GpgFrontend::UI {
+class KeyNewUIDDialog : public GeneralDialog {
+ Q_OBJECT
+
+ public:
+ /**
+ * @brief Construct a new Key New U I D Dialog object
+ *
+ * @param key
+ * @param parent
+ */
+ KeyNewUIDDialog(const KeyId& key, QWidget* parent = nullptr);
+
+ signals:
+ /**
+ * @brief
+ *
+ */
+ void SignalUIDCreated();
+
+ private slots:
+
+ /**
+ * @brief
+ *
+ */
+ void slot_create_new_uid();
+
+ private:
+ GpgKey m_key_; ///<
+
+ QLineEdit* name_{}; ///<
+ QLineEdit* email_{}; ///<
+ QLineEdit* comment_{}; ///<
+
+ QPushButton* create_button_{}; ///<
+
+ QStringList error_messages_; ///<
+ QLabel* error_label_{}; ///<
+
+ QRegularExpression re_email_{
+ R"((?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\]))"};
+
+ /**
+ * @brief
+ *
+ * @param str
+ * @return true
+ * @return false
+ */
+ bool check_email_address(const QString& str);
+};
+} // namespace GpgFrontend::UI
+
+#endif // GPGFRONTEND_KEYNEWUIDDIALOG_H
diff --git a/src/ui/dialog/keypair_details/KeyPairDetailTab.cpp b/src/ui/dialog/keypair_details/KeyPairDetailTab.cpp
new file mode 100644
index 00000000..4a6e4b52
--- /dev/null
+++ b/src/ui/dialog/keypair_details/KeyPairDetailTab.cpp
@@ -0,0 +1,276 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * 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.
+ *
+ * The source code version of this software was modified and released
+ * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021.
+ *
+ */
+
+#include "KeyPairDetailTab.h"
+
+#include "core/function/gpg/GpgKeyGetter.h"
+#include "core/function/gpg/GpgKeyImportExporter.h"
+#include "dialog/WaitingDialog.h"
+#include "ui/SignalStation.h"
+
+namespace GpgFrontend::UI {
+KeyPairDetailTab::KeyPairDetailTab(const std::string& key_id, QWidget* parent)
+ : QWidget(parent), key_(GpgKeyGetter::GetInstance().GetKey(key_id)) {
+ LOG(INFO) << key_.GetEmail() << key_.IsPrivateKey() << key_.IsHasMasterKey()
+ << key_.GetSubKeys()->front().IsPrivateKey();
+
+ owner_box_ = new QGroupBox(_("Owner"));
+ key_box_ = new QGroupBox(_("Primary Key"));
+ fingerprint_box_ = new QGroupBox(_("Fingerprint"));
+ additional_uid_box_ = new QGroupBox(_("Additional UIDs"));
+
+ name_var_label_ = new QLabel();
+ name_var_label_->setTextInteractionFlags(Qt::TextSelectableByMouse);
+ email_var_label_ = new QLabel();
+ email_var_label_->setTextInteractionFlags(Qt::TextSelectableByMouse);
+
+ comment_var_label_ = new QLabel();
+ comment_var_label_->setTextInteractionFlags(Qt::TextSelectableByMouse);
+ key_id_var_label = new QLabel();
+ key_id_var_label->setTextInteractionFlags(Qt::TextSelectableByMouse);
+
+ usage_var_label_ = new QLabel();
+ actual_usage_var_label_ = new QLabel();
+
+ key_size_var_label_ = new QLabel();
+ expire_var_label_ = new QLabel();
+ created_var_label_ = new QLabel();
+ last_update_var_label_ = new QLabel();
+ algorithm_var_label_ = new QLabel();
+ primary_key_exist_var_label_ = new QLabel();
+
+ auto* mvbox = new QVBoxLayout();
+ auto* vboxKD = new QGridLayout();
+ auto* vboxOD = new QGridLayout();
+
+ vboxOD->addWidget(new QLabel(QString(_("Name")) + ": "), 0, 0);
+ vboxOD->addWidget(new QLabel(QString(_("Email Address")) + ": "), 1, 0);
+ vboxOD->addWidget(new QLabel(QString(_("Comment")) + ": "), 2, 0);
+ vboxOD->addWidget(name_var_label_, 0, 1);
+ vboxOD->addWidget(email_var_label_, 1, 1);
+ vboxOD->addWidget(comment_var_label_, 2, 1);
+
+ vboxKD->addWidget(new QLabel(QString(_("Key ID")) + ": "), 0, 0);
+ vboxKD->addWidget(new QLabel(QString(_("Algorithm")) + ": "), 1, 0);
+ vboxKD->addWidget(new QLabel(QString(_("Key Size")) + ": "), 2, 0);
+ vboxKD->addWidget(new QLabel(QString(_("Nominal Usage")) + ": "), 3, 0);
+ vboxKD->addWidget(new QLabel(QString(_("Actual Usage")) + ": "), 4, 0);
+ vboxKD->addWidget(new QLabel(QString(_("Create Date (Local Time)")) + ": "),
+ 5, 0);
+ vboxKD->addWidget(new QLabel(QString(_("Expires on (Local Time)")) + ": "), 6,
+ 0);
+ vboxKD->addWidget(new QLabel(QString(_("Last Update (Local Time)")) + ": "),
+ 7, 0);
+ vboxKD->addWidget(new QLabel(QString(_("Primary Key Existence")) + ": "), 8,
+ 0);
+
+ key_id_var_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ vboxKD->addWidget(key_id_var_label, 0, 1, 1, 1);
+ vboxKD->addWidget(algorithm_var_label_, 1, 1, 1, 2);
+ vboxKD->addWidget(key_size_var_label_, 2, 1, 1, 2);
+ vboxKD->addWidget(usage_var_label_, 3, 1, 1, 2);
+ vboxKD->addWidget(actual_usage_var_label_, 4, 1, 1, 2);
+ vboxKD->addWidget(created_var_label_, 5, 1, 1, 2);
+ vboxKD->addWidget(expire_var_label_, 6, 1, 1, 2);
+ vboxKD->addWidget(last_update_var_label_, 7, 1, 1, 2);
+ vboxKD->addWidget(primary_key_exist_var_label_, 8, 1, 1, 2);
+
+ auto* copyKeyIdButton = new QPushButton(_("Copy"));
+ copyKeyIdButton->setFlat(true);
+ copyKeyIdButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ vboxKD->addWidget(copyKeyIdButton, 0, 2);
+ connect(copyKeyIdButton, &QPushButton::clicked, this, [=]() {
+ QString fpr = key_id_var_label->text().trimmed();
+ QClipboard* cb = QApplication::clipboard();
+ cb->setText(fpr);
+ });
+
+ owner_box_->setLayout(vboxOD);
+ mvbox->addWidget(owner_box_);
+ key_box_->setLayout(vboxKD);
+ mvbox->addWidget(key_box_);
+
+ fingerprint_var_label_ = new QLabel();
+ fingerprint_var_label_->setWordWrap(false);
+ fingerprint_var_label_->setTextInteractionFlags(Qt::TextSelectableByMouse);
+ fingerprint_var_label_->setStyleSheet("margin-left: 0; margin-right: 5;");
+ fingerprint_var_label_->setAlignment(Qt::AlignCenter);
+ auto* hboxFP = new QHBoxLayout();
+
+ hboxFP->addStretch();
+ hboxFP->addWidget(fingerprint_var_label_);
+
+ auto* copyFingerprintButton = new QPushButton(_("Copy"));
+ copyFingerprintButton->setFlat(true);
+ copyFingerprintButton->setToolTip(_("copy fingerprint to clipboard"));
+ connect(copyFingerprintButton, &QPushButton::clicked, this,
+ &KeyPairDetailTab::slot_copy_fingerprint);
+
+ hboxFP->addWidget(copyFingerprintButton);
+ hboxFP->addStretch();
+
+ fingerprint_box_->setLayout(hboxFP);
+ mvbox->addWidget(fingerprint_box_);
+ mvbox->addStretch();
+
+ auto* expBox = new QHBoxLayout();
+ QPixmap pixmap(":warning.png");
+
+ exp_label_ = new QLabel();
+ icon_label_ = new QLabel();
+
+ icon_label_->setPixmap(pixmap.scaled(24, 24, Qt::KeepAspectRatio));
+ exp_label_->setAlignment(Qt::AlignCenter);
+ expBox->addStretch();
+ expBox->addWidget(icon_label_);
+ expBox->addWidget(exp_label_);
+ expBox->addStretch();
+ mvbox->addLayout(expBox);
+ mvbox->setContentsMargins(0, 0, 0, 0);
+
+ // when key database updated
+ connect(SignalStation::GetInstance(),
+ &SignalStation::SignalKeyDatabaseRefreshDone, this,
+ &KeyPairDetailTab::slot_refresh_key);
+
+ slot_refresh_key_info();
+ setAttribute(Qt::WA_DeleteOnClose, true);
+ setLayout(mvbox);
+}
+
+void KeyPairDetailTab::slot_copy_fingerprint() {
+ QString fpr =
+ fingerprint_var_label_->text().trimmed().replace(" ", QString());
+ QClipboard* cb = QApplication::clipboard();
+ cb->setText(fpr);
+}
+
+void KeyPairDetailTab::slot_refresh_key_info() {
+ // Show the situation that primary key not exists.
+ primary_key_exist_var_label_->setText(
+ key_.IsHasMasterKey() ? _("Exists") : _("Not Exists"));
+ if (!key_.IsHasMasterKey()) {
+ auto palette_expired = primary_key_exist_var_label_->palette();
+ palette_expired.setColor(primary_key_exist_var_label_->foregroundRole(),
+ Qt::red);
+ primary_key_exist_var_label_->setPalette(palette_expired);
+ } else {
+ auto palette_valid = primary_key_exist_var_label_->palette();
+ palette_valid.setColor(primary_key_exist_var_label_->foregroundRole(),
+ Qt::darkGreen);
+ primary_key_exist_var_label_->setPalette(palette_valid);
+ }
+
+ if (key_.IsExpired()) {
+ auto paletteExpired = expire_var_label_->palette();
+ paletteExpired.setColor(expire_var_label_->foregroundRole(), Qt::red);
+ expire_var_label_->setPalette(paletteExpired);
+ } else {
+ auto paletteValid = expire_var_label_->palette();
+ paletteValid.setColor(expire_var_label_->foregroundRole(), Qt::darkGreen);
+ expire_var_label_->setPalette(paletteValid);
+ }
+
+ name_var_label_->setText(QString::fromStdString(key_.GetName()));
+ email_var_label_->setText(QString::fromStdString(key_.GetEmail()));
+
+ comment_var_label_->setText(QString::fromStdString(key_.GetComment()));
+ key_id_var_label->setText(QString::fromStdString(key_.GetId()));
+
+ std::stringstream usage_steam;
+
+ if (key_.IsHasCertificationCapability())
+ usage_steam << _("Certificate") << " ";
+ if (key_.IsHasEncryptionCapability()) usage_steam << _("Encrypt") << " ";
+ if (key_.IsHasSigningCapability()) usage_steam << _("Sign") << " ";
+ if (key_.IsHasAuthenticationCapability()) usage_steam << _("Auth") << " ";
+
+ usage_var_label_->setText(usage_steam.str().c_str());
+
+ std::stringstream actual_usage_steam;
+
+ if (key_.IsHasActualCertificationCapability())
+ actual_usage_steam << _("Certificate") << " ";
+ if (key_.IsHasActualEncryptionCapability())
+ actual_usage_steam << _("Encrypt") << " ";
+ if (key_.IsHasActualSigningCapability())
+ actual_usage_steam << _("Sign") << " ";
+ if (key_.IsHasActualAuthenticationCapability())
+ actual_usage_steam << _("Auth") << " ";
+
+ actual_usage_var_label_->setText(actual_usage_steam.str().c_str());
+
+ std::string key_size_val, key_expire_val, key_create_time_val, key_algo_val,
+ key_last_update_val;
+
+ key_size_val = std::to_string(key_.GetPrimaryKeyLength());
+
+ if (to_time_t(boost::posix_time::ptime(key_.GetExpireTime())) == 0) {
+ expire_var_label_->setText(_("Never Expire"));
+ } else {
+ expire_var_label_->setText(QLocale::system().toString(
+ QDateTime::fromTime_t(to_time_t(key_.GetExpireTime()))));
+ }
+
+ key_algo_val = key_.GetPublicKeyAlgo();
+
+ created_var_label_->setText(QLocale::system().toString(
+ QDateTime::fromTime_t(to_time_t(key_.GetCreateTime()))));
+
+ if (to_time_t(boost::posix_time::ptime(key_.GetLastUpdateTime())) == 0) {
+ last_update_var_label_->setText(_("No Data"));
+ } else {
+ last_update_var_label_->setText(QLocale::system().toString(
+ QDateTime::fromTime_t(to_time_t(key_.GetLastUpdateTime()))));
+ }
+
+ key_size_var_label_->setText(key_size_val.c_str());
+ algorithm_var_label_->setText(key_algo_val.c_str());
+ fingerprint_var_label_->setText(
+ beautify_fingerprint(key_.GetFingerprint()).c_str());
+
+ icon_label_->hide();
+ exp_label_->hide();
+
+ if (key_.IsExpired()) {
+ icon_label_->show();
+ exp_label_->show();
+ exp_label_->setText(_("Warning: The primary key has expired."));
+ }
+ if (key_.IsRevoked()) {
+ icon_label_->show();
+ exp_label_->show();
+ exp_label_->setText(_("Warning: The primary key has been revoked."));
+ }
+}
+
+void KeyPairDetailTab::slot_refresh_key() {
+ LOG(INFO) << _("Called");
+ this->key_ = GpgKeyGetter::GetInstance().GetKey(key_.GetId());
+ this->slot_refresh_key_info();
+}
+
+} // namespace GpgFrontend::UI
diff --git a/src/ui/dialog/keypair_details/KeyPairDetailTab.h b/src/ui/dialog/keypair_details/KeyPairDetailTab.h
new file mode 100644
index 00000000..91ccdab8
--- /dev/null
+++ b/src/ui/dialog/keypair_details/KeyPairDetailTab.h
@@ -0,0 +1,99 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * 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.
+ *
+ * The source code version of this software was modified and released
+ * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021.
+ *
+ */
+
+#ifndef GPGFRONTEND_KEYPAIRDETAILTAB_H
+#define GPGFRONTEND_KEYPAIRDETAILTAB_H
+
+#include "KeySetExpireDateDialog.h"
+#include "core/GpgContext.h"
+#include "ui/GpgFrontendUI.h"
+#include "ui/dialog/import_export/KeyServerImportDialog.h"
+#include "ui/dialog/import_export/KeyUploadDialog.h"
+
+namespace GpgFrontend::UI {
+
+class KeyPairDetailTab : public QWidget {
+ Q_OBJECT
+
+ private slots:
+
+ /**
+ * @details Copy the fingerprint to clipboard
+ */
+ void slot_copy_fingerprint();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_refresh_key_info();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_refresh_key();
+
+ private:
+ GpgKey key_; ///<
+
+ QGroupBox* owner_box_; ///< Groupbox containing owner information
+ QGroupBox* key_box_; ///< Groupbox containing key information
+ QGroupBox* fingerprint_box_; ///< Groupbox containing fingerprint information
+ QGroupBox* additional_uid_box_; ///< Groupbox containing information about
+ ///< additional uids
+
+ QLabel* name_var_label_; ///< Label containing the keys name
+ QLabel* email_var_label_; ///< Label containing the keys email
+ QLabel* comment_var_label_; ///< Label containing the keys comment
+ QLabel* key_size_var_label_; ///< Label containing the keys key size
+ QLabel* expire_var_label_; ///< Label containing the keys expiration date
+ QLabel* created_var_label_; ///< Label containing the keys creation date
+ QLabel* last_update_var_label_; ///<
+ QLabel* algorithm_var_label_; ///< Label containing the keys algorithm
+ QLabel* key_id_var_label; ///< Label containing the keys keyid
+ QLabel* fingerprint_var_label_; ///< Label containing the keys fingerprint
+ QLabel* usage_var_label_;
+ QLabel* actual_usage_var_label_;
+ QLabel* primary_key_exist_var_label_;
+
+ QLabel* icon_label_; ///<
+ QLabel* exp_label_; ///<
+
+ public:
+ /**
+ * @brief Construct a new Key Pair Detail Tab object
+ *
+ * @param key_id
+ * @param parent
+ */
+ explicit KeyPairDetailTab(const std::string& key_id,
+ QWidget* parent = nullptr);
+};
+
+} // namespace GpgFrontend::UI
+
+#endif // GPGFRONTEND_KEYPAIRDETAILTAB_H
diff --git a/src/ui/dialog/keypair_details/KeyPairOperaTab.cpp b/src/ui/dialog/keypair_details/KeyPairOperaTab.cpp
new file mode 100644
index 00000000..77eba0bc
--- /dev/null
+++ b/src/ui/dialog/keypair_details/KeyPairOperaTab.cpp
@@ -0,0 +1,360 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * 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.
+ *
+ * The source code version of this software was modified and released
+ * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021.
+ *
+ */
+
+#include "KeyPairOperaTab.h"
+
+#include "KeySetExpireDateDialog.h"
+#include "core/function/gpg/GpgKeyImportExporter.h"
+#include "core/function/gpg/GpgKeyOpera.h"
+#include "ui/SignalStation.h"
+#include "ui/UserInterfaceUtils.h"
+#include "ui/dialog/import_export/KeyUploadDialog.h"
+
+namespace GpgFrontend::UI {
+
+KeyPairOperaTab::KeyPairOperaTab(const std::string& key_id, QWidget* parent)
+ : QWidget(parent), m_key_(GpgKeyGetter::GetInstance().GetKey(key_id)) {
+ // Set Menu
+ CreateOperaMenu();
+ auto m_vbox = new QVBoxLayout(this);
+
+ auto* opera_key_box = new QGroupBox(_("General Operations"));
+ auto* vbox_p_k = new QVBoxLayout();
+
+ auto export_h_box_layout = new QHBoxLayout();
+ vbox_p_k->addLayout(export_h_box_layout);
+
+ auto* export_public_button = new QPushButton(_("Export Public Key"));
+ export_h_box_layout->addWidget(export_public_button);
+ connect(export_public_button, &QPushButton::clicked, this,
+ &KeyPairOperaTab::slot_export_public_key);
+
+ if (m_key_.IsPrivateKey()) {
+ auto* export_private_button = new QPushButton(_("Export Private Key"));
+ export_private_button->setStyleSheet("text-align:center;");
+ export_private_button->setMenu(secret_key_export_opera_menu_);
+ export_h_box_layout->addWidget(export_private_button);
+
+ if (m_key_.IsHasMasterKey()) {
+ auto* edit_expires_button =
+ new QPushButton(_("Modify Expiration Datetime (Primary Key)"));
+ connect(edit_expires_button, &QPushButton::clicked, this,
+ &KeyPairOperaTab::slot_modify_edit_datetime);
+ auto* edit_password_button = new QPushButton(_("Modify Password"));
+ connect(edit_password_button, &QPushButton::clicked, this,
+ &KeyPairOperaTab::slot_modify_password);
+
+ vbox_p_k->addWidget(edit_expires_button);
+ vbox_p_k->addWidget(edit_password_button);
+ }
+ }
+
+ auto advance_h_box_layout = new QHBoxLayout();
+ auto* key_server_opera_button =
+ new QPushButton(_("Key Server Operation (Pubkey)"));
+ key_server_opera_button->setStyleSheet("text-align:center;");
+ key_server_opera_button->setMenu(key_server_opera_menu_);
+ advance_h_box_layout->addWidget(key_server_opera_button);
+
+ if (m_key_.IsPrivateKey() && m_key_.IsHasMasterKey()) {
+ auto* revoke_cert_gen_button =
+ new QPushButton(_("Generate Revoke Certificate"));
+ connect(revoke_cert_gen_button, &QPushButton::clicked, this,
+ &KeyPairOperaTab::slot_gen_revoke_cert);
+ advance_h_box_layout->addWidget(revoke_cert_gen_button);
+ }
+
+ auto* modify_tofu_button = new QPushButton(_("Modify TOFU Policy"));
+ connect(modify_tofu_button, &QPushButton::clicked, this,
+ &KeyPairOperaTab::slot_modify_tofu_policy);
+
+ vbox_p_k->addLayout(advance_h_box_layout);
+ opera_key_box->setLayout(vbox_p_k);
+ m_vbox->addWidget(opera_key_box);
+ vbox_p_k->addWidget(modify_tofu_button);
+ m_vbox->addStretch(0);
+
+ setLayout(m_vbox);
+}
+
+void KeyPairOperaTab::CreateOperaMenu() {
+ key_server_opera_menu_ = new QMenu(this);
+
+ auto* uploadKeyPair = new QAction(_("Upload Key Pair to Key Server"), this);
+ connect(uploadKeyPair, &QAction::triggered, this,
+ &KeyPairOperaTab::slot_upload_key_to_server);
+ if (!(m_key_.IsPrivateKey() && m_key_.IsHasMasterKey()))
+ uploadKeyPair->setDisabled(true);
+
+ auto* updateKeyPair = new QAction(_("Sync Key Pair From Key Server"), this);
+ connect(updateKeyPair, &QAction::triggered, this,
+ &KeyPairOperaTab::slot_update_key_from_server);
+
+ // when a key has primary key, it should always upload to keyserver.
+ if (m_key_.IsHasMasterKey()) {
+ updateKeyPair->setDisabled(true);
+ }
+
+ key_server_opera_menu_->addAction(uploadKeyPair);
+ key_server_opera_menu_->addAction(updateKeyPair);
+
+ secret_key_export_opera_menu_ = new QMenu(this);
+
+ auto* exportFullSecretKey = new QAction(_("Export Full Secret Key"), this);
+ connect(exportFullSecretKey, &QAction::triggered, this,
+ &KeyPairOperaTab::slot_export_private_key);
+ if (!m_key_.IsPrivateKey()) exportFullSecretKey->setDisabled(true);
+
+ auto* exportShortestSecretKey =
+ new QAction(_("Export Shortest Secret Key"), this);
+ connect(exportShortestSecretKey, &QAction::triggered, this,
+ &KeyPairOperaTab::slot_export_short_private_key);
+
+ secret_key_export_opera_menu_->addAction(exportFullSecretKey);
+ secret_key_export_opera_menu_->addAction(exportShortestSecretKey);
+}
+
+void KeyPairOperaTab::slot_export_public_key() {
+ ByteArrayPtr keyArray = nullptr;
+
+ if (!GpgKeyImportExporter::GetInstance().ExportKey(m_key_, keyArray)) {
+ QMessageBox::critical(this, _("Error"),
+ _("An error occurred during the export operation."));
+ return;
+ }
+
+ // generate a file name
+ auto file_string = m_key_.GetName() + "<" + m_key_.GetEmail() + ">(" +
+ m_key_.GetId() + ")_pub.asc";
+ std::replace(file_string.begin(), file_string.end(), ' ', '_');
+
+ auto file_name =
+ QFileDialog::getSaveFileName(
+ this, _("Export Key To File"), QString::fromStdString(file_string),
+ QString(_("Key Files")) + " (*.asc *.txt);;All Files (*)")
+ .toStdString();
+
+ if (file_name.empty()) return;
+
+ if (!write_buffer_to_file(file_name, *keyArray)) {
+ QMessageBox::critical(
+ this, _("Export Error"),
+ QString(_("Couldn't open %1 for writing")).arg(file_name.c_str()));
+ return;
+ }
+}
+
+void KeyPairOperaTab::slot_export_short_private_key() {
+ // Show a information box with explanation about private key
+ int ret = QMessageBox::information(
+ this, _("Exporting short private Key"),
+ "<h3>" + QString(_("You are about to export your")) +
+ "<font color=\"red\">" + _(" PRIVATE KEY ") + "</font>!</h3>\n" +
+ _("This is NOT your Public Key, so DON'T give it away.") + "<br />" +
+ _("Do you REALLY want to export your PRIVATE KEY in a Minimum "
+ "Size?") +
+ "<br />" +
+ _("For OpenPGP keys it removes all signatures except for the latest "
+ "self-signatures."),
+ QMessageBox::Cancel | QMessageBox::Ok);
+
+ // export key, if ok was clicked
+ if (ret == QMessageBox::Ok) {
+ ByteArrayPtr keyArray = nullptr;
+
+ if (!GpgKeyImportExporter::GetInstance().ExportSecretKeyShortest(
+ m_key_, keyArray)) {
+ QMessageBox::critical(
+ this, _("Error"),
+ _("An error occurred during the export operation."));
+ return;
+ }
+
+ auto file_string = m_key_.GetName() + "<" + m_key_.GetEmail() + ">(" +
+ m_key_.GetId() + ")_short_secret.asc";
+ std::replace(file_string.begin(), file_string.end(), ' ', '_');
+
+ auto file_name =
+ QFileDialog::getSaveFileName(
+ this, _("Export Key To File"), QString::fromStdString(file_string),
+ QString(_("Key Files")) + " (*.asc *.txt);;All Files (*)")
+ .toStdString();
+
+ if (file_name.empty()) return;
+
+ if (!write_buffer_to_file(file_name, *keyArray)) {
+ QMessageBox::critical(
+ this, _("Export Error"),
+ QString(_("Couldn't open %1 for writing")).arg(file_name.c_str()));
+ return;
+ }
+ }
+}
+
+void KeyPairOperaTab::slot_export_private_key() {
+ // Show a information box with explanation about private key
+ int ret = QMessageBox::information(
+ this, _("Exporting private Key"),
+ "<h3>" + QString(_("You are about to export your")) +
+ "<font color=\"red\">" + _(" PRIVATE KEY ") + "</font>!</h3>\n" +
+ _("This is NOT your Public Key, so DON'T give it away.") + "<br />" +
+ _("Do you REALLY want to export your PRIVATE KEY?"),
+ QMessageBox::Cancel | QMessageBox::Ok);
+
+ // export key, if ok was clicked
+ if (ret == QMessageBox::Ok) {
+ ByteArrayPtr keyArray = nullptr;
+
+ if (!GpgKeyImportExporter::GetInstance().ExportSecretKey(m_key_,
+ keyArray)) {
+ QMessageBox::critical(
+ this, _("Error"),
+ _("An error occurred during the export operation."));
+ return;
+ }
+ auto file_string = m_key_.GetName() + "<" + m_key_.GetEmail() + ">(" +
+ m_key_.GetId() + ")_full_secret.asc";
+ std::replace(file_string.begin(), file_string.end(), ' ', '_');
+
+ auto file_name =
+ QFileDialog::getSaveFileName(
+ this, _("Export Key To File"), QString::fromStdString(file_string),
+ QString(_("Key Files")) + " (*.asc *.txt);;All Files (*)")
+ .toStdString();
+
+ if (file_name.empty()) return;
+
+ if (!write_buffer_to_file(file_name, *keyArray)) {
+ QMessageBox::critical(
+ this, _("Export Error"),
+ QString(_("Couldn't open %1 for writing")).arg(file_name.c_str()));
+ return;
+ }
+ }
+}
+
+void KeyPairOperaTab::slot_modify_edit_datetime() {
+ auto dialog = new KeySetExpireDateDialog(m_key_.GetId(), this);
+ dialog->show();
+}
+
+void KeyPairOperaTab::slot_upload_key_to_server() {
+ auto keys = std::make_unique<KeyIdArgsList>();
+ keys->push_back(m_key_.GetId());
+ auto* dialog = new KeyUploadDialog(keys, this);
+ dialog->show();
+ dialog->SlotUpload();
+}
+
+void KeyPairOperaTab::slot_update_key_from_server() {
+ auto keys = std::make_unique<KeyIdArgsList>();
+ keys->push_back(m_key_.GetId());
+ auto* dialog = new KeyServerImportDialog(this);
+ dialog->show();
+ dialog->SlotImport(keys);
+}
+
+void KeyPairOperaTab::slot_gen_revoke_cert() {
+ auto literal = QString("%1 (*.rev)").arg(_("Revocation Certificates"));
+ QString m_output_file_name;
+
+ QFileDialog dialog(this, "Generate revocation certificate", QString(),
+ literal);
+ dialog.setDefaultSuffix(".rev");
+ dialog.setAcceptMode(QFileDialog::AcceptSave);
+
+ if (dialog.exec()) m_output_file_name = dialog.selectedFiles().front();
+
+ if (!m_output_file_name.isEmpty())
+ CommonUtils::GetInstance()->SlotExecuteGpgCommand(
+ {"--command-fd", "0", "--status-fd", "1", "--no-tty", "-o",
+ m_output_file_name, "--gen-revoke", m_key_.GetFingerprint().c_str()},
+ [](QProcess* proc) -> void {
+ // Code From Gpg4Win
+ while (proc->canReadLine()) {
+ const QString line = QString::fromUtf8(proc->readLine()).trimmed();
+ LOG(INFO) << "line" << line.toStdString();
+ if (line == QLatin1String("[GNUPG:] GET_BOOL gen_revoke.okay")) {
+ proc->write("y\n");
+ } else if (line == QLatin1String("[GNUPG:] GET_LINE "
+ "ask_revocation_reason.code")) {
+ proc->write("0\n");
+ } else if (line == QLatin1String("[GNUPG:] GET_LINE "
+ "ask_revocation_reason.text")) {
+ proc->write("\n");
+ } else if (line ==
+ QLatin1String(
+ "[GNUPG:] GET_BOOL openfile.overwrite.okay")) {
+ // We asked before
+ proc->write("y\n");
+ } else if (line == QLatin1String("[GNUPG:] GET_BOOL "
+ "ask_revocation_reason.okay")) {
+ proc->write("y\n");
+ }
+ }
+ });
+}
+
+void KeyPairOperaTab::slot_modify_password() {
+ auto err = GpgKeyOpera::GetInstance().ModifyPassword(m_key_);
+ if (check_gpg_error_2_err_code(err) != GPG_ERR_NO_ERROR) {
+ QMessageBox::critical(this, _("Not Successful"),
+ QString(_("Modify password not successfully.")));
+ }
+}
+
+void KeyPairOperaTab::slot_modify_tofu_policy() {
+ QStringList items;
+ items << _("Policy Auto") << _("Policy Good") << _("Policy Bad")
+ << _("Policy Ask") << _("Policy Unknown");
+
+ bool ok;
+ QString item = QInputDialog::getItem(
+ this, _("Modify TOFU Policy(Default is Auto)"),
+ _("Policy for the Key Pair:"), items, 0, false, &ok);
+ if (ok && !item.isEmpty()) {
+ LOG(INFO) << "selected policy" << item.toStdString();
+ gpgme_tofu_policy_t tofu_policy = GPGME_TOFU_POLICY_AUTO;
+ if (item == _("Policy Auto")) {
+ tofu_policy = GPGME_TOFU_POLICY_AUTO;
+ } else if (item == _("Policy Good")) {
+ tofu_policy = GPGME_TOFU_POLICY_GOOD;
+ } else if (item == _("Policy Bad")) {
+ tofu_policy = GPGME_TOFU_POLICY_BAD;
+ } else if (item == _("Policy Ask")) {
+ tofu_policy = GPGME_TOFU_POLICY_ASK;
+ } else if (item == _("Policy Unknown")) {
+ tofu_policy = GPGME_TOFU_POLICY_UNKNOWN;
+ }
+ auto err = GpgKeyOpera::GetInstance().ModifyTOFUPolicy(m_key_, tofu_policy);
+ if (check_gpg_error_2_err_code(err) != GPG_ERR_NO_ERROR) {
+ QMessageBox::critical(this, _("Not Successful"),
+ QString(_("Modify TOFU policy not successfully.")));
+ }
+ }
+}
+
+} // namespace GpgFrontend::UI
diff --git a/src/ui/dialog/keypair_details/KeyPairOperaTab.h b/src/ui/dialog/keypair_details/KeyPairOperaTab.h
new file mode 100644
index 00000000..af6b1eee
--- /dev/null
+++ b/src/ui/dialog/keypair_details/KeyPairOperaTab.h
@@ -0,0 +1,113 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * 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.
+ *
+ * The source code version of this software was modified and released
+ * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021.
+ *
+ */
+
+#ifndef GPGFRONTEND_KEYPAIROPERATAB_H
+#define GPGFRONTEND_KEYPAIROPERATAB_H
+
+#include "core/function/gpg/GpgKeyGetter.h"
+#include "ui/GpgFrontendUI.h"
+
+namespace GpgFrontend::UI {
+class KeyPairOperaTab : public QWidget {
+ Q_OBJECT
+ public:
+ /**
+ * @brief Construct a new Key Pair Opera Tab object
+ *
+ * @param key_id
+ * @param parent
+ */
+ KeyPairOperaTab(const std::string& key_id, QWidget* parent);
+
+ /**
+ * @brief Create a Opera Menu object
+ *
+ */
+ void CreateOperaMenu();
+
+ private slots:
+
+ /**
+ * @details Export the key to a file, which is chosen in a file dialog
+ */
+ void slot_export_private_key();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_export_short_private_key();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_export_public_key();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_modify_edit_datetime();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_modify_password();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_upload_key_to_server();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_update_key_from_server();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_gen_revoke_cert();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_modify_tofu_policy();
+
+ private:
+ GpgKey m_key_; ///<
+ QMenu* key_server_opera_menu_{}; ///<
+ QMenu* secret_key_export_opera_menu_{}; ///<
+};
+} // namespace GpgFrontend::UI
+
+#endif // GPGFRONTEND_KEYPAIROPERATAB_H
diff --git a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp
new file mode 100644
index 00000000..fe1d0798
--- /dev/null
+++ b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp
@@ -0,0 +1,338 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * 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 "KeyPairSubkeyTab.h"
+
+#include "core/function/gpg/GpgKeyGetter.h"
+#include "ui/SignalStation.h"
+
+namespace GpgFrontend::UI {
+
+KeyPairSubkeyTab::KeyPairSubkeyTab(const std::string& key_id, QWidget* parent)
+ : QWidget(parent), key_(GpgKeyGetter::GetInstance().GetKey(key_id)) {
+ LOG(INFO) << key_.GetEmail() << key_.IsPrivateKey() << key_.IsHasMasterKey()
+ << key_.GetSubKeys()->front().IsPrivateKey();
+
+ create_subkey_list();
+ create_subkey_opera_menu();
+
+ list_box_ = new QGroupBox(_("Subkey List"));
+ detail_box_ = new QGroupBox(_("Detail of Selected Subkey"));
+
+ auto uidButtonsLayout = new QGridLayout();
+
+ auto addSubkeyButton = new QPushButton(_("Generate A New Subkey"));
+ if (!key_.IsPrivateKey() || !key_.IsHasMasterKey()) {
+ addSubkeyButton->setDisabled(true);
+ setHidden(addSubkeyButton);
+ }
+
+ uidButtonsLayout->addWidget(addSubkeyButton, 0, 1);
+
+ auto* baseLayout = new QVBoxLayout();
+
+ auto subkeyListLayout = new QGridLayout();
+ subkeyListLayout->addWidget(subkey_list_, 0, 0);
+ subkeyListLayout->addLayout(uidButtonsLayout, 1, 0);
+ subkeyListLayout->setContentsMargins(0, 10, 0, 0);
+
+ auto* subkeyDetailLayout = new QGridLayout();
+
+ subkeyDetailLayout->addWidget(new QLabel(QString(_("Key ID")) + ": "), 0, 0);
+ subkeyDetailLayout->addWidget(new QLabel(QString(_("Algorithm")) + ": "), 1,
+ 0);
+ subkeyDetailLayout->addWidget(new QLabel(QString(_("Key Size")) + ": "), 2,
+ 0);
+ subkeyDetailLayout->addWidget(new QLabel(QString(_("Usage")) + ": "), 3, 0);
+ subkeyDetailLayout->addWidget(
+ new QLabel(QString(_("Expires On (Local Time)")) + ": "), 4, 0);
+ subkeyDetailLayout->addWidget(
+ new QLabel(QString(_("Create Date (Local Time)")) + ": "), 5, 0);
+ subkeyDetailLayout->addWidget(new QLabel(QString(_("Existence")) + ": "), 6,
+ 0);
+ subkeyDetailLayout->addWidget(
+ new QLabel(QString(_("Key in Smart Card")) + ": "), 7, 0);
+ subkeyDetailLayout->addWidget(new QLabel(QString(_("Fingerprint")) + ": "), 8,
+ 0);
+
+ key_id_var_label_ = new QLabel(this);
+ key_size_var_label_ = new QLabel(this);
+ expire_var_label_ = new QLabel(this);
+ algorithm_var_label_ = new QLabel(this);
+ created_var_label_ = new QLabel(this);
+ usage_var_label_ = new QLabel(this);
+ master_key_exist_var_label_ = new QLabel(this);
+ fingerprint_var_label_ = new QLabel(this);
+ card_key_label_ = new QLabel(this);
+
+ subkeyDetailLayout->addWidget(key_id_var_label_, 0, 1, 1, 1);
+ subkeyDetailLayout->addWidget(key_size_var_label_, 2, 1, 1, 2);
+ subkeyDetailLayout->addWidget(expire_var_label_, 4, 1, 1, 2);
+ subkeyDetailLayout->addWidget(algorithm_var_label_, 1, 1, 1, 2);
+ subkeyDetailLayout->addWidget(created_var_label_, 5, 1, 1, 2);
+ subkeyDetailLayout->addWidget(usage_var_label_, 3, 1, 1, 2);
+ subkeyDetailLayout->addWidget(master_key_exist_var_label_, 6, 1, 1, 2);
+ subkeyDetailLayout->addWidget(card_key_label_, 7, 1, 1, 2);
+ subkeyDetailLayout->addWidget(fingerprint_var_label_, 8, 1, 1, 2);
+
+ auto* copyKeyIdButton = new QPushButton(_("Copy"));
+ copyKeyIdButton->setFlat(true);
+ subkeyDetailLayout->addWidget(copyKeyIdButton, 0, 2);
+ connect(copyKeyIdButton, &QPushButton::clicked, this, [=]() {
+ QString fpr = key_id_var_label_->text().trimmed();
+ QClipboard* cb = QApplication::clipboard();
+ cb->setText(fpr);
+ });
+
+ list_box_->setLayout(subkeyListLayout);
+ list_box_->setContentsMargins(0, 12, 0, 0);
+ detail_box_->setLayout(subkeyDetailLayout);
+
+ baseLayout->addWidget(list_box_);
+ baseLayout->addWidget(detail_box_);
+ baseLayout->addStretch();
+
+ connect(addSubkeyButton, &QPushButton::clicked, this,
+ &KeyPairSubkeyTab::slot_add_subkey);
+ connect(subkey_list_, &QTableWidget::itemSelectionChanged, this,
+ &KeyPairSubkeyTab::slot_refresh_subkey_detail);
+
+ // key database refresh signal
+ connect(SignalStation::GetInstance(),
+ &SignalStation::SignalKeyDatabaseRefreshDone, this,
+ &KeyPairSubkeyTab::slot_refresh_key_info);
+ connect(SignalStation::GetInstance(),
+ &SignalStation::SignalKeyDatabaseRefreshDone, this,
+ &KeyPairSubkeyTab::slot_refresh_subkey_list);
+
+ baseLayout->setContentsMargins(0, 0, 0, 0);
+
+ setLayout(baseLayout);
+ setAttribute(Qt::WA_DeleteOnClose, true);
+
+ slot_refresh_subkey_list();
+}
+
+void KeyPairSubkeyTab::create_subkey_list() {
+ subkey_list_ = new QTableWidget(this);
+
+ subkey_list_->setColumnCount(5);
+ subkey_list_->horizontalHeader()->setSectionResizeMode(
+ QHeaderView::ResizeToContents);
+ subkey_list_->verticalHeader()->hide();
+ subkey_list_->setShowGrid(false);
+ subkey_list_->setSelectionBehavior(QAbstractItemView::SelectRows);
+
+ // tableitems not editable
+ subkey_list_->setEditTriggers(QAbstractItemView::NoEditTriggers);
+
+ // no focus (rectangle around tableitems)
+ // may be it should focus on whole row
+ subkey_list_->setFocusPolicy(Qt::NoFocus);
+ subkey_list_->setAlternatingRowColors(true);
+
+ QStringList labels;
+ labels << _("Subkey ID") << _("Key Size") << _("Algo")
+ << _("Create Date (UTC)") << _("Expire Date (UTC)");
+
+ subkey_list_->setHorizontalHeaderLabels(labels);
+ subkey_list_->horizontalHeader()->setStretchLastSection(false);
+}
+
+void KeyPairSubkeyTab::slot_refresh_subkey_list() {
+ LOG(INFO) << "Called";
+ int row = 0;
+
+ subkey_list_->setSelectionMode(QAbstractItemView::SingleSelection);
+
+ this->buffered_subkeys_.clear();
+ auto sub_keys = key_.GetSubKeys();
+ for (auto& sub_key : *sub_keys) {
+ if (sub_key.IsDisabled() || sub_key.IsRevoked()) continue;
+ this->buffered_subkeys_.push_back(std::move(sub_key));
+ }
+
+ subkey_list_->setRowCount(buffered_subkeys_.size());
+
+ for (const auto& subkeys : buffered_subkeys_) {
+ auto* tmp0 = new QTableWidgetItem(QString::fromStdString(subkeys.GetID()));
+ tmp0->setTextAlignment(Qt::AlignCenter);
+ subkey_list_->setItem(row, 0, tmp0);
+
+ auto* tmp1 = new QTableWidgetItem(QString::number(subkeys.GetKeyLength()));
+ tmp1->setTextAlignment(Qt::AlignCenter);
+ subkey_list_->setItem(row, 1, tmp1);
+
+ auto* tmp2 =
+ new QTableWidgetItem(QString::fromStdString(subkeys.GetPubkeyAlgo()));
+ tmp2->setTextAlignment(Qt::AlignCenter);
+ subkey_list_->setItem(row, 2, tmp2);
+
+ auto* tmp3 = new QTableWidgetItem(
+ QString::fromStdString(to_iso_string(subkeys.GetCreateTime())));
+ tmp3->setTextAlignment(Qt::AlignCenter);
+ subkey_list_->setItem(row, 3, tmp3);
+
+ auto* tmp4 = new QTableWidgetItem(
+ boost::posix_time::to_time_t(
+ boost::posix_time::ptime(subkeys.GetExpireTime())) == 0
+ ? _("Never Expire")
+ : QString::fromStdString(to_iso_string(subkeys.GetExpireTime())));
+ tmp4->setTextAlignment(Qt::AlignCenter);
+ subkey_list_->setItem(row, 4, tmp4);
+
+ if (!row) {
+ for (auto i = 0; i < subkey_list_->columnCount(); i++) {
+ subkey_list_->item(row, i)->setForeground(QColor(65, 105, 255));
+ }
+ }
+
+ row++;
+ }
+
+ if (subkey_list_->rowCount() > 0) {
+ subkey_list_->selectRow(0);
+ }
+}
+
+void KeyPairSubkeyTab::slot_add_subkey() {
+ auto dialog = new SubkeyGenerateDialog(key_.GetId(), this);
+ dialog->show();
+}
+
+void KeyPairSubkeyTab::slot_refresh_subkey_detail() {
+ auto& subkey = get_selected_subkey();
+
+ key_id_var_label_->setText(QString::fromStdString(subkey.GetID()));
+ key_size_var_label_->setText(QString::number(subkey.GetKeyLength()));
+
+ time_t subkey_time_t = boost::posix_time::to_time_t(
+ boost::posix_time::ptime(subkey.GetExpireTime()));
+
+ expire_var_label_->setText(
+ subkey_time_t == 0 ? _("Never Expires")
+ : QLocale::system().toString(QDateTime::fromTime_t(
+ to_time_t(subkey.GetExpireTime()))));
+ if (subkey_time_t != 0 &&
+ subkey.GetExpireTime() < boost::posix_time::second_clock::local_time()) {
+ auto paletteExpired = expire_var_label_->palette();
+ paletteExpired.setColor(expire_var_label_->foregroundRole(), Qt::red);
+ expire_var_label_->setPalette(paletteExpired);
+ } else {
+ auto paletteValid = expire_var_label_->palette();
+ paletteValid.setColor(expire_var_label_->foregroundRole(), Qt::darkGreen);
+ expire_var_label_->setPalette(paletteValid);
+ }
+
+ algorithm_var_label_->setText(QString::fromStdString(subkey.GetPubkeyAlgo()));
+ created_var_label_->setText(QLocale::system().toString(
+ QDateTime::fromTime_t(to_time_t(subkey.GetCreateTime()))));
+
+ std::stringstream usage_steam;
+
+ if (subkey.IsHasCertificationCapability())
+ usage_steam << _("Certificate") << " ";
+ if (subkey.IsHasEncryptionCapability()) usage_steam << _("Encrypt") << " ";
+ if (subkey.IsHasSigningCapability()) usage_steam << _("Sign") << " ";
+ if (subkey.IsHasAuthenticationCapability()) usage_steam << _("Auth") << " ";
+
+ usage_var_label_->setText(usage_steam.str().c_str());
+
+ // Show the situation that secret key not exists.
+ master_key_exist_var_label_->setText(subkey.IsSecretKey() ? _("Exists")
+ : _("Not Exists"));
+
+ // Show the situation if key in a smart card.
+ card_key_label_->setText(subkey.IsCardKey() ? _("Yes") : _("No"));
+
+ if (!subkey.IsSecretKey()) {
+ auto palette_expired = master_key_exist_var_label_->palette();
+ palette_expired.setColor(master_key_exist_var_label_->foregroundRole(),
+ Qt::red);
+ master_key_exist_var_label_->setPalette(palette_expired);
+ } else {
+ auto palette_valid = master_key_exist_var_label_->palette();
+ palette_valid.setColor(master_key_exist_var_label_->foregroundRole(),
+ Qt::darkGreen);
+ master_key_exist_var_label_->setPalette(palette_valid);
+ }
+
+ if (!subkey.IsCardKey()) {
+ auto palette_expired = card_key_label_->palette();
+ palette_expired.setColor(card_key_label_->foregroundRole(), Qt::red);
+ card_key_label_->setPalette(palette_expired);
+ } else {
+ auto palette_valid = card_key_label_->palette();
+ palette_valid.setColor(card_key_label_->foregroundRole(), Qt::darkGreen);
+ card_key_label_->setPalette(palette_valid);
+ }
+
+ fingerprint_var_label_->setText(
+ QString::fromStdString(subkey.GetFingerprint()));
+}
+
+void KeyPairSubkeyTab::create_subkey_opera_menu() {
+ subkey_opera_menu_ = new QMenu(this);
+ auto* editSubkeyAct = new QAction(_("Edit Expire Date"));
+ connect(editSubkeyAct, &QAction::triggered, this,
+ &KeyPairSubkeyTab::slot_edit_subkey);
+
+ subkey_opera_menu_->addAction(editSubkeyAct);
+}
+
+void KeyPairSubkeyTab::slot_edit_subkey() {
+ LOG(INFO) << "Fpr" << get_selected_subkey().GetFingerprint();
+
+ auto dialog = new KeySetExpireDateDialog(
+ key_.GetId(), get_selected_subkey().GetFingerprint(), this);
+ dialog->show();
+}
+
+void KeyPairSubkeyTab::slot_revoke_subkey() {}
+
+void KeyPairSubkeyTab::contextMenuEvent(QContextMenuEvent* event) {
+ if (!subkey_list_->selectedItems().isEmpty()) {
+ subkey_opera_menu_->exec(event->globalPos());
+ }
+}
+
+const GpgSubKey& KeyPairSubkeyTab::get_selected_subkey() {
+ int row = 0;
+
+ for (int i = 0; i < subkey_list_->rowCount(); i++) {
+ if (subkey_list_->item(row, 0)->isSelected()) break;
+ row++;
+ }
+
+ return buffered_subkeys_[row];
+}
+void KeyPairSubkeyTab::slot_refresh_key_info() {
+ key_ = GpgKeyGetter::GetInstance().GetKey(key_.GetId());
+}
+
+} // namespace GpgFrontend::UI
diff --git a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.h b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.h
new file mode 100644
index 00000000..a93ebca5
--- /dev/null
+++ b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.h
@@ -0,0 +1,139 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * 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
+ *
+ */
+
+#ifndef GPGFRONTEND_KEYPAIRSUBKEYTAB_H
+#define GPGFRONTEND_KEYPAIRSUBKEYTAB_H
+
+#include "KeySetExpireDateDialog.h"
+#include "core/GpgContext.h"
+#include "ui/GpgFrontendUI.h"
+#include "ui/dialog/key_generate/SubkeyGenerateDialog.h"
+
+namespace GpgFrontend::UI {
+
+class KeyPairSubkeyTab : public QWidget {
+ Q_OBJECT
+
+ public:
+ /**
+ * @brief Construct a new Key Pair Subkey Tab object
+ *
+ * @param key
+ * @param parent
+ */
+ KeyPairSubkeyTab(const std::string& key, QWidget* parent);
+
+ private:
+ /**
+ * @brief Create a subkey list object
+ *
+ */
+ void create_subkey_list();
+
+ /**
+ * @brief Create a subkey opera menu object
+ *
+ */
+ void create_subkey_opera_menu();
+
+ /**
+ * @brief Get the selected subkey object
+ *
+ * @return const GpgSubKey&
+ */
+ const GpgSubKey& get_selected_subkey();
+
+ GpgKey key_; ///<
+ QTableWidget* subkey_list_{}; ///<
+ std::vector<GpgSubKey> buffered_subkeys_; ///<
+
+ QGroupBox* list_box_; ///<
+ QGroupBox* detail_box_; ///<
+
+ QMenu* subkey_opera_menu_{}; ///<
+
+ QLabel* key_size_var_label_; ///< Label containing the keys key size
+ QLabel* expire_var_label_; ///< Label containing the keys expiration date
+ QLabel* created_var_label_; ///< Label containing the keys creation date
+ QLabel* algorithm_var_label_; ///< Label containing the keys algorithm
+ QLabel* key_id_var_label_; ///< Label containing the keys keyid
+ QLabel* fingerprint_var_label_; ///< Label containing the keys fingerprint
+ QLabel* usage_var_label_; ///<
+ QLabel* master_key_exist_var_label_; ///<
+ QLabel* card_key_label_; ///<
+
+ private slots:
+
+ /**
+ * @brief
+ *
+ */
+ void slot_add_subkey();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_refresh_subkey_list();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_refresh_subkey_detail();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_edit_subkey();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_revoke_subkey();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_refresh_key_info();
+
+ protected:
+ /**
+ * @brief
+ *
+ * @param event
+ */
+ void contextMenuEvent(QContextMenuEvent* event) override;
+};
+
+} // namespace GpgFrontend::UI
+
+#endif // GPGFRONTEND_KEYPAIRSUBKEYTAB_H
diff --git a/src/ui/dialog/keypair_details/KeyPairUIDTab.cpp b/src/ui/dialog/keypair_details/KeyPairUIDTab.cpp
new file mode 100644
index 00000000..b923dbec
--- /dev/null
+++ b/src/ui/dialog/keypair_details/KeyPairUIDTab.cpp
@@ -0,0 +1,583 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * 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.
+ *
+ * The source code version of this software was modified and released
+ * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021.
+ *
+ */
+
+#include "KeyPairUIDTab.h"
+
+#include "core/function/gpg/GpgKeyGetter.h"
+#include "core/function/gpg/GpgKeyManager.h"
+#include "core/function/gpg/GpgUIDOperator.h"
+#include "ui/SignalStation.h"
+#include "ui/widgets/TOFUInfoPage.h"
+
+namespace GpgFrontend::UI {
+
+KeyPairUIDTab::KeyPairUIDTab(const std::string& key_id, QWidget* parent)
+ : QWidget(parent), m_key_(GpgKeyGetter::GetInstance().GetKey(key_id)) {
+ create_uid_list();
+ create_sign_list();
+ create_manage_uid_menu();
+ create_uid_popup_menu();
+ create_sign_popup_menu();
+
+ auto uidButtonsLayout = new QGridLayout();
+
+ auto addUIDButton = new QPushButton(_("New UID"));
+ auto manageUIDButton = new QPushButton(_("UID Management"));
+
+ if (m_key_.IsHasMasterKey()) {
+ manageUIDButton->setMenu(manage_selected_uid_menu_);
+ } else {
+ manageUIDButton->setDisabled(true);
+ }
+
+ uidButtonsLayout->addWidget(addUIDButton, 0, 1);
+ uidButtonsLayout->addWidget(manageUIDButton, 0, 2);
+
+ auto grid_layout = new QGridLayout();
+
+ grid_layout->addWidget(uid_list_, 0, 0);
+ grid_layout->addLayout(uidButtonsLayout, 1, 0);
+ grid_layout->setContentsMargins(0, 10, 0, 0);
+
+ auto uid_group_box = new QGroupBox();
+ uid_group_box->setLayout(grid_layout);
+ uid_group_box->setTitle(_("UIDs"));
+
+ auto tofu_group_box = new QGroupBox();
+ auto tofu_vbox_layout = new QVBoxLayout();
+ tofu_group_box->setLayout(tofu_vbox_layout);
+ tofu_group_box->setTitle(_("TOFU"));
+#if !defined(RELEASE)
+ tofu_tabs_ = new QTabWidget(this);
+ tofu_vbox_layout->addWidget(tofu_tabs_);
+#endif
+
+ auto sign_grid_layout = new QGridLayout();
+ sign_grid_layout->addWidget(sig_list_, 0, 0);
+ sign_grid_layout->setContentsMargins(0, 10, 0, 0);
+
+ auto sign_group_box = new QGroupBox();
+ sign_group_box->setLayout(sign_grid_layout);
+ sign_group_box->setTitle(_("Signature of Selected UID"));
+
+ auto vboxLayout = new QVBoxLayout();
+ vboxLayout->addWidget(uid_group_box);
+#if !defined(RELEASE)
+ // Function needed testing
+ vboxLayout->addWidget(tofu_group_box);
+#endif
+ vboxLayout->addWidget(sign_group_box);
+
+ vboxLayout->setContentsMargins(0, 0, 0, 0);
+
+ connect(addUIDButton, &QPushButton::clicked, this,
+ &KeyPairUIDTab::slot_add_uid);
+ connect(uid_list_, &QTableWidget::itemSelectionChanged, this,
+ &KeyPairUIDTab::slot_refresh_tofu_info);
+ connect(uid_list_, &QTableWidget::itemSelectionChanged, this,
+ &KeyPairUIDTab::slot_refresh_sig_list);
+
+ // Key Database Refresh
+ connect(SignalStation::GetInstance(),
+ &SignalStation::SignalKeyDatabaseRefreshDone, this,
+ &KeyPairUIDTab::slot_refresh_key);
+
+ connect(this, &KeyPairUIDTab::SignalUpdateUIDInfo,
+ SignalStation::GetInstance(),
+ &SignalStation::SignalKeyDatabaseRefresh);
+
+ setLayout(vboxLayout);
+ setAttribute(Qt::WA_DeleteOnClose, true);
+
+ slot_refresh_uid_list();
+}
+
+void KeyPairUIDTab::create_uid_list() {
+ uid_list_ = new QTableWidget(this);
+ uid_list_->setColumnCount(4);
+ uid_list_->horizontalHeader()->setSectionResizeMode(
+ QHeaderView::ResizeToContents);
+ uid_list_->verticalHeader()->hide();
+ uid_list_->setShowGrid(false);
+ uid_list_->setSelectionBehavior(QAbstractItemView::SelectRows);
+ uid_list_->setSelectionMode(QAbstractItemView::SingleSelection);
+
+ // tableitems not editable
+ uid_list_->setEditTriggers(QAbstractItemView::NoEditTriggers);
+
+ // no focus (rectangle around tableitems)
+ // may be it should focus on whole row
+ uid_list_->setFocusPolicy(Qt::NoFocus);
+ uid_list_->setAlternatingRowColors(true);
+
+ QStringList labels;
+ labels << _("Select") << _("Name") << _("Email") << _("Comment");
+ uid_list_->setHorizontalHeaderLabels(labels);
+ uid_list_->horizontalHeader()->setStretchLastSection(true);
+}
+
+void KeyPairUIDTab::create_sign_list() {
+ sig_list_ = new QTableWidget(this);
+ sig_list_->setColumnCount(5);
+ sig_list_->horizontalHeader()->setSectionResizeMode(
+ QHeaderView::ResizeToContents);
+ sig_list_->verticalHeader()->hide();
+ sig_list_->setShowGrid(false);
+ sig_list_->setSelectionBehavior(QAbstractItemView::SelectRows);
+
+ // table items not editable
+ sig_list_->setEditTriggers(QAbstractItemView::NoEditTriggers);
+
+ // no focus (rectangle around table items)
+ // may be it should focus on whole row
+ sig_list_->setFocusPolicy(Qt::NoFocus);
+ sig_list_->setAlternatingRowColors(true);
+
+ QStringList labels;
+ labels << _("Key ID") << _("Name") << _("Email") << _("Create Date (UTC)")
+ << _("Expired Date (UTC)");
+ sig_list_->setHorizontalHeaderLabels(labels);
+ sig_list_->horizontalHeader()->setStretchLastSection(false);
+}
+
+void KeyPairUIDTab::slot_refresh_uid_list() {
+ int row = 0;
+
+ uid_list_->setSelectionMode(QAbstractItemView::SingleSelection);
+
+ this->buffered_uids_.clear();
+
+ auto uids = m_key_.GetUIDs();
+ for (auto& uid : *uids) {
+ if (uid.GetInvalid() || uid.GetRevoked()) {
+ continue;
+ }
+ this->buffered_uids_.push_back(std::move(uid));
+ }
+
+ uid_list_->setRowCount(buffered_uids_.size());
+
+ for (const auto& uid : buffered_uids_) {
+ auto* tmp0 = new QTableWidgetItem(QString::fromStdString(uid.GetUID()));
+ uid_list_->setItem(row, 1, tmp0);
+
+ auto* tmp1 = new QTableWidgetItem(QString::fromStdString(uid.GetUID()));
+ uid_list_->setItem(row, 2, tmp1);
+
+ auto* tmp2 = new QTableWidgetItem(QString::fromStdString(uid.GetUID()));
+ uid_list_->setItem(row, 3, tmp2);
+
+ auto* tmp3 = new QTableWidgetItem(QString::number(row));
+ tmp3->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled |
+ Qt::ItemIsSelectable);
+ tmp3->setTextAlignment(Qt::AlignCenter);
+ tmp3->setCheckState(Qt::Unchecked);
+ uid_list_->setItem(row, 0, tmp3);
+
+ if (!row) {
+ for (auto i = 0; i < uid_list_->columnCount(); i++) {
+ uid_list_->item(row, i)->setForeground(QColor(65, 105, 255));
+ }
+ }
+
+ row++;
+ }
+
+ if (uid_list_->rowCount() > 0) {
+ uid_list_->selectRow(0);
+ }
+
+ slot_refresh_sig_list();
+ slot_refresh_tofu_info();
+}
+
+void KeyPairUIDTab::slot_refresh_tofu_info() {
+ if (this->tofu_tabs_ == nullptr) return;
+
+ int uidRow = 0;
+ tofu_tabs_->clear();
+ for (const auto& uid : buffered_uids_) {
+ // Only Show Selected UID Signatures
+ if (!uid_list_->item(uidRow++, 0)->isSelected()) {
+ continue;
+ }
+ auto tofu_infos = uid.GetTofuInfos();
+ LOG(INFO) << "tofu info size" << tofu_infos->size();
+ if (tofu_infos->empty()) {
+ tofu_tabs_->hide();
+ } else {
+ tofu_tabs_->show();
+ }
+ int index = 1;
+ for (const auto& tofu_info : *tofu_infos) {
+ tofu_tabs_->addTab(new TOFUInfoPage(tofu_info, this),
+ QString(_("TOFU %1")).arg(index++));
+ }
+ }
+}
+
+void KeyPairUIDTab::slot_refresh_sig_list() {
+ int uidRow = 0, sigRow = 0;
+ for (const auto& uid : buffered_uids_) {
+ // Only Show Selected UID Signatures
+ if (!uid_list_->item(uidRow++, 0)->isSelected()) {
+ continue;
+ }
+
+ buffered_signatures_.clear();
+ auto signatures = uid.GetSignatures();
+ for (auto& sig : *signatures) {
+ if (sig.IsInvalid() || sig.IsRevoked()) {
+ continue;
+ }
+ buffered_signatures_.push_back(std::move(sig));
+ }
+
+ sig_list_->setRowCount(buffered_signatures_.size());
+
+ for (const auto& sig : buffered_signatures_) {
+ auto* tmp0 = new QTableWidgetItem(QString::fromStdString(sig.GetKeyID()));
+ sig_list_->setItem(sigRow, 0, tmp0);
+
+ if (gpgme_err_code(sig.GetStatus()) == GPG_ERR_NO_PUBKEY) {
+ auto* tmp2 = new QTableWidgetItem("<Unknown>");
+ sig_list_->setItem(sigRow, 1, tmp2);
+
+ auto* tmp3 = new QTableWidgetItem("<Unknown>");
+ sig_list_->setItem(sigRow, 2, tmp3);
+ } else {
+ auto* tmp2 =
+ new QTableWidgetItem(QString::fromStdString(sig.GetName()));
+ sig_list_->setItem(sigRow, 1, tmp2);
+
+ auto* tmp3 =
+ new QTableWidgetItem(QString::fromStdString(sig.GetEmail()));
+ sig_list_->setItem(sigRow, 2, tmp3);
+ }
+
+ auto* tmp4 = new QTableWidgetItem(QLocale::system().toString(
+ QDateTime::fromTime_t(to_time_t(sig.GetCreateTime()))));
+ sig_list_->setItem(sigRow, 3, tmp4);
+
+ auto* tmp5 = new QTableWidgetItem(
+ boost::posix_time::to_time_t(
+ boost::posix_time::ptime(sig.GetExpireTime())) == 0
+ ? _("Never Expires")
+ : QLocale::system().toString(
+ QDateTime::fromTime_t(to_time_t(sig.GetExpireTime()))));
+ tmp5->setTextAlignment(Qt::AlignCenter);
+ sig_list_->setItem(sigRow, 4, tmp5);
+
+ sigRow++;
+ }
+
+ break;
+ }
+}
+
+void KeyPairUIDTab::slot_add_sign() {
+ auto selected_uids = get_uid_checked();
+
+ if (selected_uids->empty()) {
+ QMessageBox::information(
+ nullptr, _("Invalid Operation"),
+ _("Please select one or more UIDs before doing this operation."));
+ return;
+ }
+
+ auto keySignDialog =
+ new KeyUIDSignDialog(m_key_, std::move(selected_uids), this);
+ keySignDialog->show();
+}
+
+UIDArgsListPtr KeyPairUIDTab::get_uid_checked() {
+ 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* signUIDAct = new QAction(_("Sign Selected UID(s)"), this);
+ connect(signUIDAct, &QAction::triggered, this, &KeyPairUIDTab::slot_add_sign);
+ auto* delUIDAct = new QAction(_("Delete Selected UID(s)"), this);
+ connect(delUIDAct, &QAction::triggered, this, &KeyPairUIDTab::slot_del_uid);
+
+ if (m_key_.IsHasMasterKey()) {
+ manage_selected_uid_menu_->addAction(signUIDAct);
+ manage_selected_uid_menu_->addAction(delUIDAct);
+ }
+}
+
+void KeyPairUIDTab::slot_add_uid() {
+ auto keyNewUIDDialog = new KeyNewUIDDialog(m_key_.GetId(), this);
+ connect(keyNewUIDDialog, &KeyNewUIDDialog::finished, this,
+ &KeyPairUIDTab::slot_add_uid_result);
+ connect(keyNewUIDDialog, &KeyNewUIDDialog::finished, keyNewUIDDialog,
+ &KeyPairUIDTab::deleteLater);
+ keyNewUIDDialog->show();
+}
+
+void KeyPairUIDTab::slot_add_uid_result(int result) {
+ if (result == 1) {
+ QMessageBox::information(nullptr, _("Successful Operation"),
+ _("Successfully added a new UID."));
+ } else if (result == -1) {
+ QMessageBox::critical(nullptr, _("Operation Failed"),
+ _("An error occurred during the operation."));
+ }
+}
+
+void KeyPairUIDTab::slot_del_uid() {
+ auto selected_uids = get_uid_checked();
+
+ if (selected_uids->empty()) {
+ QMessageBox::information(
+ nullptr, _("Invalid Operation"),
+ _("Please select one or more UIDs before doing this operation."));
+ return;
+ }
+
+ QString keynames;
+ for (auto& uid : *selected_uids) {
+ keynames.append(QString::fromStdString(uid));
+ keynames.append("<br/>");
+ }
+
+ int ret = QMessageBox::warning(
+ this, _("Deleting UIDs"),
+ "<b>" +
+ QString(
+ _("Are you sure that you want to delete the following UIDs?")) +
+ "</b><br/><br/>" + keynames + +"<br/>" +
+ _("The action can not be undone."),
+ QMessageBox::No | QMessageBox::Yes);
+
+ if (ret == QMessageBox::Yes) {
+ for (const auto& uid : *selected_uids) {
+ LOG(INFO) << "KeyPairUIDTab::slot_del_uid UID" << uid;
+ if (!GpgUIDOperator::GetInstance().RevUID(m_key_, uid)) {
+ QMessageBox::critical(
+ nullptr, _("Operation Failed"),
+ QString(_("An error occurred during the delete %1 operation."))
+ .arg(uid.c_str()));
+ }
+ }
+ emit SignalUpdateUIDInfo();
+ }
+}
+
+void KeyPairUIDTab::slot_set_primary_uid() {
+ auto selected_uids = get_uid_selected();
+
+ if (selected_uids->empty()) {
+ auto emptyUIDMsg = new QMessageBox();
+ emptyUIDMsg->setText("Please select one UID before doing this operation.");
+ emptyUIDMsg->exec();
+ return;
+ }
+
+ QString keynames;
+
+ keynames.append(QString::fromStdString(selected_uids->front()));
+ keynames.append("<br/>");
+
+ int ret = QMessageBox::warning(
+ this, _("Set Primary UID"),
+ "<b>" +
+ QString(_("Are you sure that you want to set the Primary UID to?")) +
+ "</b><br/><br/>" + keynames + +"<br/>" +
+ _("The action can not be undone."),
+ QMessageBox::No | QMessageBox::Yes);
+
+ if (ret == QMessageBox::Yes) {
+ if (!GpgUIDOperator::GetInstance().SetPrimaryUID(m_key_,
+ selected_uids->front())) {
+ QMessageBox::critical(nullptr, _("Operation Failed"),
+ _("An error occurred during the operation."));
+ } else {
+ emit SignalUpdateUIDInfo();
+ }
+ }
+}
+
+UIDArgsListPtr KeyPairUIDTab::get_uid_selected() {
+ 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());
+ }
+ }
+ return uids;
+}
+
+SignIdArgsListPtr KeyPairUIDTab::get_sign_selected() {
+ auto signatures = std::make_unique<SignIdArgsList>();
+ for (int i = 0; i < sig_list_->rowCount(); i++) {
+ if (sig_list_->item(i, 0)->isSelected()) {
+ auto& sign = buffered_signatures_[i];
+ signatures->push_back({sign.GetKeyID(), sign.GetUID()});
+ }
+ }
+ return signatures;
+}
+
+void KeyPairUIDTab::create_uid_popup_menu() {
+ uid_popup_menu_ = new QMenu(this);
+
+ auto* serPrimaryUIDAct = new QAction(_("Set As Primary"), this);
+ connect(serPrimaryUIDAct, &QAction::triggered, this,
+ &KeyPairUIDTab::slot_set_primary_uid);
+ auto* signUIDAct = new QAction(_("Sign UID"), this);
+ connect(signUIDAct, &QAction::triggered, this,
+ &KeyPairUIDTab::slot_add_sign_single);
+ auto* delUIDAct = new QAction(_("Delete UID"), this);
+ connect(delUIDAct, &QAction::triggered, this,
+ &KeyPairUIDTab::slot_del_uid_single);
+
+ if (m_key_.IsHasMasterKey()) {
+ uid_popup_menu_->addAction(serPrimaryUIDAct);
+ uid_popup_menu_->addAction(signUIDAct);
+ uid_popup_menu_->addAction(delUIDAct);
+ }
+}
+
+void KeyPairUIDTab::contextMenuEvent(QContextMenuEvent* event) {
+ if (uid_list_->selectedItems().length() > 0 &&
+ sig_list_->selectedItems().isEmpty()) {
+ 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, _("Invalid Operation"),
+ _("Please select one UID before doing this operation."));
+ return;
+ }
+
+ auto keySignDialog =
+ new KeyUIDSignDialog(m_key_, std::move(selected_uids), this);
+ keySignDialog->show();
+}
+
+void KeyPairUIDTab::slot_del_uid_single() {
+ auto selected_uids = get_uid_selected();
+ if (selected_uids->empty()) {
+ QMessageBox::information(
+ nullptr, _("Invalid Operation"),
+ _("Please select one UID before doing this operation."));
+ return;
+ }
+
+ QString keynames;
+
+ keynames.append(QString::fromStdString(selected_uids->front()));
+ keynames.append("<br/>");
+
+ int ret = QMessageBox::warning(
+ this, _("Deleting UID"),
+ "<b>" +
+ QString(
+ _("Are you sure that you want to delete the following uid?")) +
+ "</b><br/><br/>" + keynames + +"<br/>" +
+ _("The action can not be undone."),
+ QMessageBox::No | QMessageBox::Yes);
+
+ if (ret == QMessageBox::Yes) {
+ if (!GpgUIDOperator::GetInstance().RevUID(m_key_, selected_uids->front())) {
+ QMessageBox::critical(nullptr, _("Operation Failed"),
+ _("An error occurred during the operation."));
+ } else {
+ emit SignalUpdateUIDInfo();
+ }
+ }
+}
+
+void KeyPairUIDTab::create_sign_popup_menu() {
+ sign_popup_menu_ = new QMenu(this);
+
+ auto* delSignAct = new QAction(_("Delete(Revoke) Key Signature"), this);
+ connect(delSignAct, &QAction::triggered, this, &KeyPairUIDTab::slot_del_sign);
+
+ sign_popup_menu_->addAction(delSignAct);
+}
+
+void KeyPairUIDTab::slot_del_sign() {
+ auto selected_signs = get_sign_selected();
+ if (selected_signs->empty()) {
+ QMessageBox::information(
+ nullptr, _("Invalid Operation"),
+ _("Please select one Key Signature before doing this operation."));
+ return;
+ }
+
+ if (!GpgKeyGetter::GetInstance()
+ .GetKey(selected_signs->front().first)
+ .IsGood()) {
+ QMessageBox::critical(
+ nullptr, _("Invalid Operation"),
+ _("To delete the signature, you need to have its corresponding public "
+ "key in the local database."));
+ return;
+ }
+
+ QString keynames;
+
+ keynames.append(QString::fromStdString(selected_signs->front().second));
+ keynames.append("<br/>");
+
+ int ret =
+ QMessageBox::warning(this, _("Deleting Key Signature"),
+ "<b>" +
+ QString(_("Are you sure that you want to delete "
+ "the following signature?")) +
+ "</b><br/><br/>" + keynames + +"<br/>" +
+ _("The action can not be undone."),
+ QMessageBox::No | QMessageBox::Yes);
+
+ if (ret == QMessageBox::Yes) {
+ if (!GpgKeyManager::GetInstance().RevSign(m_key_, selected_signs)) {
+ QMessageBox::critical(nullptr, _("Operation Failed"),
+ _("An error occurred during the operation."));
+ }
+ }
+}
+void KeyPairUIDTab::slot_refresh_key() {
+ this->m_key_ = GpgKeyGetter::GetInstance().GetKey(this->m_key_.GetId());
+ this->slot_refresh_uid_list();
+ this->slot_refresh_tofu_info();
+ this->slot_refresh_sig_list();
+}
+
+} // namespace GpgFrontend::UI
diff --git a/src/ui/dialog/keypair_details/KeyPairUIDTab.h b/src/ui/dialog/keypair_details/KeyPairUIDTab.h
new file mode 100644
index 00000000..fae8f9f2
--- /dev/null
+++ b/src/ui/dialog/keypair_details/KeyPairUIDTab.h
@@ -0,0 +1,207 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * 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
+ *
+ */
+
+#ifndef GPGFRONTEND_KEYPAIRUIDTAB_H
+#define GPGFRONTEND_KEYPAIRUIDTAB_H
+
+#include "KeyNewUIDDialog.h"
+#include "KeyUIDSignDialog.h"
+#include "core/GpgContext.h"
+#include "ui/GpgFrontendUI.h"
+
+namespace GpgFrontend::UI {
+
+class KeyPairUIDTab : public QWidget {
+ Q_OBJECT
+
+ public:
+ /**
+ * @brief Construct a new Key Pair U I D Tab object
+ *
+ * @param key_id
+ * @param parent
+ */
+ KeyPairUIDTab(const std::string& key_id, QWidget* parent);
+
+ signals:
+
+ /**
+ * @brief
+ *
+ */
+ void SignalUpdateUIDInfo();
+
+ private:
+ 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();
+
+ /**
+ * @brief Create a sign list object
+ *
+ */
+ void create_sign_list();
+
+ /**
+ * @brief Create a manage uid menu object
+ *
+ */
+ void create_manage_uid_menu();
+
+ /**
+ * @brief Create a uid popup menu object
+ *
+ */
+ void create_uid_popup_menu();
+
+ /**
+ * @brief Create a sign popup menu object
+ *
+ */
+ void create_sign_popup_menu();
+
+ /**
+ * @brief Get the uid checked object
+ *
+ * @return UIDArgsListPtr
+ */
+ UIDArgsListPtr get_uid_checked();
+
+ /**
+ * @brief Get the uid selected object
+ *
+ * @return UIDArgsListPtr
+ */
+ UIDArgsListPtr get_uid_selected();
+
+ /**
+ * @brief Get the sign selected object
+ *
+ * @return SignIdArgsListPtr
+ */
+ SignIdArgsListPtr get_sign_selected();
+
+ private slots:
+
+ /**
+ * @brief
+ *
+ */
+ void slot_refresh_uid_list();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_refresh_tofu_info();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_refresh_sig_list();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_add_sign();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_add_sign_single();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_add_uid();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_del_uid();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_del_uid_single();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_set_primary_uid();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_del_sign();
+
+ /**
+ * @brief
+ *
+ */
+ void slot_refresh_key();
+
+ /**
+ * @brief
+ *
+ * @param result
+ */
+ static void slot_add_uid_result(int result);
+
+ protected:
+ /**
+ * @brief
+ *
+ * @param event
+ */
+ void contextMenuEvent(QContextMenuEvent* event) override;
+};
+
+} // namespace GpgFrontend::UI
+
+#endif // GPGFRONTEND_KEYPAIRUIDTAB_H
diff --git a/src/ui/dialog/keypair_details/KeySetExpireDateDialog.cpp b/src/ui/dialog/keypair_details/KeySetExpireDateDialog.cpp
new file mode 100644
index 00000000..2c10b895
--- /dev/null
+++ b/src/ui/dialog/keypair_details/KeySetExpireDateDialog.cpp
@@ -0,0 +1,145 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * 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 "KeySetExpireDateDialog.h"
+
+#include <utility>
+
+#include "core/function/GlobalSettingStation.h"
+#include "core/function/gpg/GpgKeyGetter.h"
+#include "core/function/gpg/GpgKeyOpera.h"
+#include "ui/SignalStation.h"
+#include "ui_ModifiedExpirationDateTime.h"
+
+namespace GpgFrontend::UI {
+
+KeySetExpireDateDialog::KeySetExpireDateDialog(const KeyId& key_id,
+ QWidget* parent)
+ : GeneralDialog(typeid(KeySetExpireDateDialog).name(), parent),
+ ui_(std::make_shared<Ui_ModifiedExpirationDateTime>()),
+ m_key_(GpgKeyGetter::GetInstance().GetKey(key_id)) {
+ init();
+}
+
+KeySetExpireDateDialog::KeySetExpireDateDialog(const KeyId& key_id,
+ std::string subkey_fpr,
+ QWidget* parent)
+ : GeneralDialog(typeid(KeySetExpireDateDialog).name(), parent),
+ ui_(std::make_shared<Ui_ModifiedExpirationDateTime>()),
+ m_key_(GpgKeyGetter::GetInstance().GetKey(key_id)),
+ m_subkey_(std::move(subkey_fpr)) {
+ init();
+}
+
+void KeySetExpireDateDialog::slot_confirm() {
+ LOG(INFO) << "Called" << ui_->dateEdit->date().toString().toStdString()
+ << ui_->timeEdit->time().toString().toStdString();
+ auto datetime = QDateTime(ui_->dateEdit->date(), ui_->timeEdit->time());
+ std::unique_ptr<boost::posix_time::ptime> expires = nullptr;
+ if (ui_->noExpirationCheckBox->checkState() == Qt::Unchecked) {
+ expires = std::make_unique<boost::posix_time::ptime>(
+ boost::posix_time::from_time_t(datetime.toLocalTime().toTime_t()));
+ LOG(INFO) << "keyid" << m_key_.GetId() << m_subkey_ << *expires;
+ } else {
+ LOG(INFO) << "keyid" << m_key_.GetId() << m_subkey_ << "Non Expired";
+ }
+
+ auto err = GpgKeyOpera::GetInstance().SetExpire(m_key_, m_subkey_, expires);
+
+ if (check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR) {
+ auto* msg_box = new QMessageBox((QWidget*)this->parent());
+ msg_box->setAttribute(Qt::WA_DeleteOnClose);
+ msg_box->setStandardButtons(QMessageBox::Ok);
+ msg_box->setWindowTitle(_("Success"));
+ msg_box->setText(_("The expire date of the key pair has been updated."));
+ msg_box->setModal(true);
+ msg_box->open();
+
+ emit SignalKeyExpireDateUpdated();
+
+ this->close();
+ } else {
+ QMessageBox::critical(
+ this, _("Failure"),
+ _("Failed to update the expire date of the key pair."));
+ }
+}
+
+void KeySetExpireDateDialog::init() {
+ ui_->setupUi(this);
+
+ auto& settings = GlobalSettingStation::GetInstance().GetUISettings();
+
+ bool longer_expiration_date = false;
+ try {
+ longer_expiration_date = settings.lookup("general.longer_expiration_date");
+ LOG(INFO) << "longer_expiration_date" << longer_expiration_date;
+
+ } catch (...) {
+ LOG(ERROR) << _("Setting Operation Error") << _("longer_expiration_date");
+ }
+
+ auto max_date_time =
+ longer_expiration_date
+ ? QDateTime::currentDateTime().toLocalTime().addYears(30)
+ : QDateTime::currentDateTime().toLocalTime().addYears(2);
+
+ auto min_date_time = QDateTime::currentDateTime().addDays(7);
+
+ ui_->dateEdit->setMaximumDateTime(max_date_time);
+ ui_->dateEdit->setMinimumDateTime(min_date_time);
+
+ // set default date time to expire date time
+ auto current_expire_time =
+ QDateTime::fromTime_t(to_time_t(m_key_.GetExpireTime()));
+ ui_->dateEdit->setDateTime(current_expire_time);
+ ui_->timeEdit->setDateTime(current_expire_time);
+
+ connect(ui_->noExpirationCheckBox, &QCheckBox::stateChanged, this,
+ &KeySetExpireDateDialog::slot_non_expired_checked);
+ connect(ui_->button_box_, &QDialogButtonBox::accepted, this,
+ &KeySetExpireDateDialog::slot_confirm);
+ connect(this, &KeySetExpireDateDialog::SignalKeyExpireDateUpdated,
+ SignalStation::GetInstance(),
+ &SignalStation::SignalKeyDatabaseRefresh);
+
+ ui_->titleLabel->setText(_("Modified Expiration Date (Local Time)"));
+ ui_->label->setText(
+ _("Tips: For the sake of security, the key is valid for up to two years. "
+ "If you are an expert user, please unlock it for a longer time in the "
+ "settings."));
+ ui_->noExpirationCheckBox->setText(_("No Expiration"));
+ this->setWindowTitle(_("Modified Expiration Date"));
+}
+
+void KeySetExpireDateDialog::slot_non_expired_checked(int state) {
+ ui_->dateEdit->setDisabled(state == Qt::Checked);
+ ui_->timeEdit->setDisabled(state == Qt::Checked);
+}
+
+} // namespace GpgFrontend::UI
diff --git a/src/ui/dialog/keypair_details/KeySetExpireDateDialog.h b/src/ui/dialog/keypair_details/KeySetExpireDateDialog.h
new file mode 100644
index 00000000..3cd6cd01
--- /dev/null
+++ b/src/ui/dialog/keypair_details/KeySetExpireDateDialog.h
@@ -0,0 +1,99 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * 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
+ *
+ */
+
+#ifndef GPGFRONTEND_KEYSETEXPIREDATEDIALOG_H
+#define GPGFRONTEND_KEYSETEXPIREDATEDIALOG_H
+
+#include "core/GpgContext.h"
+#include "core/model/GpgKey.h"
+#include "core/model/GpgSubKey.h"
+#include "ui/GpgFrontendUI.h"
+#include "ui/dialog/GeneralDialog.h"
+
+class Ui_ModifiedExpirationDateTime;
+
+namespace GpgFrontend::UI {
+
+class KeySetExpireDateDialog : public GeneralDialog {
+ Q_OBJECT
+ public:
+ /**
+ * @brief Construct a new Key Set Expire Date Dialog object
+ *
+ * @param key_id
+ * @param parent
+ */
+ explicit KeySetExpireDateDialog(const KeyId& key_id,
+ QWidget* parent = nullptr);
+
+ /**
+ * @brief Construct a new Key Set Expire Date Dialog object
+ *
+ * @param key_id
+ * @param subkey_fpr
+ * @param parent
+ */
+ explicit KeySetExpireDateDialog(const KeyId& key_id, std::string subkey_fpr,
+ QWidget* parent = nullptr);
+
+ signals:
+ /**
+ * @brief
+ *
+ */
+ void SignalKeyExpireDateUpdated();
+
+ private:
+ /**
+ * @brief
+ *
+ */
+ void init();
+
+ std::shared_ptr<Ui_ModifiedExpirationDateTime> ui_; ///<
+ const GpgKey m_key_; ///<
+ const SubkeyId m_subkey_; ///<
+
+ private slots:
+ /**
+ * @brief
+ *
+ */
+ void slot_confirm();
+
+ /**
+ * @brief
+ *
+ * @param state
+ */
+ void slot_non_expired_checked(int state);
+};
+
+} // namespace GpgFrontend::UI
+
+#endif // GPGFRONTEND_KEYSETEXPIREDATEDIALOG_H
diff --git a/src/ui/dialog/keypair_details/KeyUIDSignDialog.cpp b/src/ui/dialog/keypair_details/KeyUIDSignDialog.cpp
new file mode 100644
index 00000000..0f7de587
--- /dev/null
+++ b/src/ui/dialog/keypair_details/KeyUIDSignDialog.cpp
@@ -0,0 +1,134 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * 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.
+ *
+ * The source code version of this software was modified and released
+ * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021.
+ *
+ */
+
+#include "KeyUIDSignDialog.h"
+
+#include "core/function/gpg/GpgKeyGetter.h"
+#include "core/function/gpg/GpgKeyManager.h"
+#include "ui/SignalStation.h"
+
+namespace GpgFrontend::UI {
+
+KeyUIDSignDialog::KeyUIDSignDialog(const GpgKey& key, UIDArgsListPtr uid,
+ QWidget* parent)
+ : GeneralDialog(typeid(KeyUIDSignDialog).name(), parent),
+ m_uids_(std::move(uid)),
+ m_key_(key) {
+ const auto key_id = m_key_.GetId();
+ m_key_list_ = new KeyList(KeyMenuAbility::NONE, this);
+ m_key_list_->AddListGroupTab(
+ _("Signers"), KeyListRow::ONLY_SECRET_KEY,
+ KeyListColumn::NAME | KeyListColumn::EmailAddress,
+ [key_id](const GpgKey& key) -> bool {
+ if (key.IsDisabled() || !key.IsHasCertificationCapability() ||
+ !key.IsHasMasterKey() || key.IsExpired() || key.IsRevoked() ||
+ key_id == key.GetId())
+ return false;
+ else
+ return true;
+ });
+ m_key_list_->SlotRefresh();
+
+ sign_key_button_ = new QPushButton("Sign");
+
+ /**
+ * A DateTime after 5 Years is recommend.
+ */
+ expires_edit_ = new QDateTimeEdit(QDateTime::currentDateTime().addYears(5));
+ expires_edit_->setMinimumDateTime(QDateTime::currentDateTime());
+
+ /**
+ * Note further that the OpenPGP protocol uses 32 bit values for timestamps
+ * and thus can only encode dates up to the year 2106.
+ */
+ expires_edit_->setMaximumDate(QDate(2106, 1, 1));
+
+ non_expire_check_ = new QCheckBox("Non Expired");
+ non_expire_check_->setTristate(false);
+
+ connect(non_expire_check_, &QCheckBox::stateChanged, this,
+ [this](int state) -> void {
+ if (state == 0)
+ expires_edit_->setDisabled(false);
+ else
+ expires_edit_->setDisabled(true);
+ });
+
+ auto layout = new QGridLayout();
+
+ auto timeLayout = new QGridLayout();
+
+ layout->addWidget(m_key_list_, 0, 0);
+ layout->addWidget(sign_key_button_, 2, 0, Qt::AlignRight);
+ timeLayout->addWidget(new QLabel(_("Expire Date")), 0, 0);
+ timeLayout->addWidget(expires_edit_, 0, 1);
+ timeLayout->addWidget(non_expire_check_, 0, 2);
+ layout->addLayout(timeLayout, 1, 0);
+
+ connect(sign_key_button_, &QPushButton::clicked, this,
+ &KeyUIDSignDialog::slot_sign_key);
+
+ this->setLayout(layout);
+ this->setModal(true);
+ this->setWindowTitle(_("Sign For Key's UID(s)"));
+ this->adjustSize();
+
+ setAttribute(Qt::WA_DeleteOnClose, true);
+
+ connect(this, &KeyUIDSignDialog::SignalKeyUIDSignUpdate,
+ SignalStation::GetInstance(),
+ &SignalStation::SignalKeyDatabaseRefresh);
+}
+
+void KeyUIDSignDialog::slot_sign_key(bool clicked) {
+ LOG(INFO) << "Called";
+
+ // Set Signers
+ auto key_ids = m_key_list_->GetChecked();
+ auto keys = GpgKeyGetter::GetInstance().GetKeys(key_ids);
+
+ LOG(INFO) << "Key Info Got";
+ auto expires = std::make_unique<boost::posix_time::ptime>(
+ boost::posix_time::from_time_t(expires_edit_->dateTime().toTime_t()));
+
+ LOG(INFO) << "Sign Start";
+ for (const auto& uid : *m_uids_) {
+ LOG(INFO) << "Sign UID" << uid;
+ // Sign For mKey
+ if (!GpgKeyManager::GetInstance().SignKey(m_key_, *keys, uid, expires)) {
+ QMessageBox::critical(
+ nullptr, _("Unsuccessful Operation"),
+ QString(_("Signature operation failed for UID %1")).arg(uid.c_str()));
+ }
+ }
+
+ QMessageBox::information(nullptr, _("Operation Complete"),
+ _("The signature operation of the UID is complete"));
+ this->close();
+ emit SignalKeyUIDSignUpdate();
+}
+
+} // namespace GpgFrontend::UI
diff --git a/src/ui/dialog/keypair_details/KeyUIDSignDialog.h b/src/ui/dialog/keypair_details/KeyUIDSignDialog.h
new file mode 100644
index 00000000..bfaff6d2
--- /dev/null
+++ b/src/ui/dialog/keypair_details/KeyUIDSignDialog.h
@@ -0,0 +1,78 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * 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.
+ *
+ * The source code version of this software was modified and released
+ * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021.
+ *
+ */
+
+#ifndef GPGFRONTEND_KEYUIDSIGNDIALOG_H
+#define GPGFRONTEND_KEYUIDSIGNDIALOG_H
+
+#include "core/GpgContext.h"
+#include "ui/GpgFrontendUI.h"
+#include "ui/dialog/GeneralDialog.h"
+#include "ui/widgets/KeyList.h"
+
+namespace GpgFrontend::UI {
+
+class KeyUIDSignDialog : public GeneralDialog {
+ Q_OBJECT
+
+ public:
+ /**
+ * @brief Construct a new Key U I D Sign Dialog object
+ *
+ * @param key
+ * @param uid
+ * @param parent
+ */
+ explicit KeyUIDSignDialog(const GpgKey& key, UIDArgsListPtr uid,
+ QWidget* parent = nullptr);
+
+ signals:
+ /**
+ * @brief
+ *
+ */
+ void SignalKeyUIDSignUpdate();
+
+ private:
+ KeyList* m_key_list_; ///<
+ QPushButton* sign_key_button_; ///<
+ QDateTimeEdit* expires_edit_; ///<
+ QCheckBox* non_expire_check_; ///<
+ UIDArgsListPtr m_uids_; ///<
+
+ const GpgKey& m_key_; ///<
+
+ private slots:
+ /**
+ * @brief
+ *
+ * @param clicked
+ */
+ void slot_sign_key(bool clicked);
+};
+
+} // namespace GpgFrontend::UI
+
+#endif // GPGFRONTEND_KEYUIDSIGNDIALOG_H