diff options
Diffstat (limited to 'src/ui/dialog/keypair_details')
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 |