/** * 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 . * * 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 starting on May 12, 2021. * */ #include "ui/keypair_details/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::SignalKeyDatabaseRefresh, 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(""); sig_list_->setItem(sigRow, 1, tmp2); auto* tmp3 = new QTableWidgetItem(""); 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(); 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("
"); } int ret = QMessageBox::warning( this, _("Deleting UIDs"), "" + QString( _("Are you sure that you want to delete the following UIDs?")) + "

" + keynames + +"
" + _("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("
"); int ret = QMessageBox::warning( this, _("Set Primary UID"), "" + QString(_("Are you sure that you want to set the Primary UID to?")) + "

" + keynames + +"
" + _("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(); 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(); 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("
"); int ret = QMessageBox::warning( this, _("Deleting UID"), "" + QString( _("Are you sure that you want to delete the following uid?")) + "

" + keynames + +"
" + _("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("
"); int ret = QMessageBox::warning(this, _("Deleting Key Signature"), "" + QString(_("Are you sure that you want to delete " "the following signature?")) + "

" + keynames + +"
" + _("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