/**
* 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.
*
* Foobar 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 Foobar. If not, see .
*
* The initial version of the source code is inherited from gpg4usb-team.
* Their source code version also complies with GNU General Public License.
*
* The source code version of this software was modified and released
* by Saturneric starting on May 12, 2021.
*
*/
#include "ui/KeyMgmt.h"
#include
#include "gpg/function/GpgKeyGetter.h"
#include "gpg/function/GpgKeyImportExporter.h"
#include "gpg/function/GpgKeyOpera.h"
#include "ui/SignalStation.h"
#include "ui/UserInterfaceUtils.h"
#include "ui/keygen/SubkeyGenerateDialog.h"
#include "ui/settings/GlobalSettingStation.h"
namespace GpgFrontend::UI {
KeyMgmt::KeyMgmt(QWidget* parent) : QMainWindow(parent) {
/* the list of Keys available*/
mKeyList = new KeyList(true, this);
mKeyList->addListGroupTab(_("All"), KeyListRow::SECRET_OR_PUBLIC_KEY);
mKeyList->addListGroupTab(
_("Only Public Key"), KeyListRow::SECRET_OR_PUBLIC_KEY,
KeyListColumn::TYPE | KeyListColumn::NAME | KeyListColumn::EmailAddress |
KeyListColumn::Usage | KeyListColumn::Validity,
[](const GpgKey& key) -> bool {
return !key.is_private_key() &&
!(key.revoked() || key.disabled() || key.expired());
});
mKeyList->addListGroupTab(
_("Has Private Key"), KeyListRow::SECRET_OR_PUBLIC_KEY,
KeyListColumn::TYPE | KeyListColumn::NAME | KeyListColumn::EmailAddress |
KeyListColumn::Usage | KeyListColumn::Validity,
[](const GpgKey& key) -> bool {
return key.is_private_key() &&
!(key.revoked() || key.disabled() || key.expired());
});
mKeyList->addListGroupTab(
_("No Master Key"), KeyListRow::SECRET_OR_PUBLIC_KEY,
KeyListColumn::TYPE | KeyListColumn::NAME | KeyListColumn::EmailAddress |
KeyListColumn::Usage | KeyListColumn::Validity,
[](const GpgKey& key) -> bool {
return !key.has_master_key() &&
!(key.revoked() || key.disabled() || key.expired());
});
mKeyList->addListGroupTab(
_("Revoked"), KeyListRow::SECRET_OR_PUBLIC_KEY,
KeyListColumn::TYPE | KeyListColumn::NAME | KeyListColumn::EmailAddress |
KeyListColumn::Usage | KeyListColumn::Validity,
[](const GpgKey& key) -> bool { return key.revoked(); });
mKeyList->addListGroupTab(
_("Expired"), KeyListRow::SECRET_OR_PUBLIC_KEY,
KeyListColumn::TYPE | KeyListColumn::NAME | KeyListColumn::EmailAddress |
KeyListColumn::Usage | KeyListColumn::Validity,
[](const GpgKey& key) -> bool { return key.expired(); });
setCentralWidget(mKeyList);
mKeyList->setDoubleClickedAction([this](const GpgKey& key, QWidget* parent) {
new KeyDetailsDialog(key, parent);
});
mKeyList->slotRefresh();
createActions();
createMenus();
createToolBars();
connect(this, SIGNAL(signalStatusBarChanged(QString)), this->parent(),
SLOT(slotSetStatusBarText(QString)));
auto& settings = GlobalSettingStation::GetInstance().GetUISettings();
try {
int width = settings.lookup("window.icon_size.width");
int height = settings.lookup("window.icon_size.height");
this->setIconSize(QSize(width, height));
} catch (...) {
LOG(ERROR) << _("Setting Operation Error") << _("icon_size");
}
// icon_style
try {
int s_icon_style = settings.lookup("window.icon_style");
auto icon_style = static_cast(s_icon_style);
this->setToolButtonStyle(icon_style);
} catch (...) {
LOG(ERROR) << _("Setting Operation Error") << _("icon_style");
}
auto pos = QPoint(50, 50);
LOG(INFO) << "parent" << parent;
if (parent) pos += parent->pos();
LOG(INFO) << "pos default" << pos.x() << pos.y();
auto size = QSize(900, 600);
try {
int x, y, width, height;
x = settings.lookup("window.key_management.position.x");
y = settings.lookup("window.key_management.position.y");
width = settings.lookup("window.key_management.size.width");
height = settings.lookup("window.key_management.size.height");
pos = QPoint(x, y);
size = QSize(width, height);
std::string window_state =
settings.lookup("window.key_management.window_state");
// state sets pos & size of dock-widgets
this->restoreState(
QByteArray::fromBase64(QByteArray::fromStdString(window_state)));
} catch (...) {
LOG(WARNING) << "cannot read pos or size from settings";
}
this->resize(size);
this->move(pos);
this->setWindowModality(Qt::ApplicationModal);
this->statusBar()->show();
setWindowTitle(_("KeyPair Management"));
mKeyList->addMenuAction(deleteSelectedKeysAct);
mKeyList->addMenuAction(showKeyDetailsAct);
connect(this, SIGNAL(signalKeyStatusUpdated()), SignalStation::GetInstance(),
SIGNAL(KeyDatabaseRefresh()));
connect(SignalStation::GetInstance(), &SignalStation::signalRefreshStatusBar,
this, [=](const QString& message, int timeout) {
statusBar()->showMessage(message, timeout);
});
}
void KeyMgmt::createActions() {
openKeyFileAct = new QAction(_("Open"), this);
openKeyFileAct->setShortcut(QKeySequence(_("Ctrl+O")));
openKeyFileAct->setToolTip(_("Open Key File"));
connect(importKeyFromFileAct, &QAction::triggered, this,
[&]() { CommonUtils::GetInstance()->slotImportKeyFromFile(this); });
closeAct = new QAction(_("Close"), this);
closeAct->setShortcut(QKeySequence(_("Ctrl+Q")));
closeAct->setIcon(QIcon(":exit.png"));
closeAct->setToolTip(_("Close"));
connect(closeAct, SIGNAL(triggered()), this, SLOT(close()));
generateKeyPairAct = new QAction(_("New Keypair"), this);
generateKeyPairAct->setShortcut(QKeySequence(_("Ctrl+N")));
generateKeyPairAct->setIcon(QIcon(":key_generate.png"));
generateKeyPairAct->setToolTip(_("Generate KeyPair"));
connect(generateKeyPairAct, SIGNAL(triggered()), this,
SLOT(slotGenerateKeyDialog()));
generateSubKeyAct = new QAction(_("New Subkey"), this);
generateSubKeyAct->setShortcut(QKeySequence(_("Ctrl+Shift+N")));
generateSubKeyAct->setIcon(QIcon(":key_generate.png"));
generateSubKeyAct->setToolTip(_("Generate Subkey For Selected KeyPair"));
connect(generateSubKeyAct, SIGNAL(triggered()), this,
SLOT(slotGenerateSubKey()));
importKeyFromFileAct = new QAction(_("File"), this);
importKeyFromFileAct->setIcon(QIcon(":import_key_from_file.png"));
importKeyFromFileAct->setToolTip(_("Import New Key From File"));
connect(importKeyFromFileAct, &QAction::triggered, this,
[&]() { CommonUtils::GetInstance()->slotImportKeyFromFile(this); });
importKeyFromClipboardAct = new QAction(_("Clipboard"), this);
importKeyFromClipboardAct->setIcon(QIcon(":import_key_from_clipboard.png"));
importKeyFromClipboardAct->setToolTip(_("Import New Key From Clipboard"));
connect(importKeyFromClipboardAct, &QAction::triggered, this, [&]() {
CommonUtils::GetInstance()->slotImportKeyFromClipboard(this);
});
importKeyFromKeyServerAct = new QAction(_("Keyserver"), this);
importKeyFromKeyServerAct->setIcon(QIcon(":import_key_from_server.png"));
importKeyFromKeyServerAct->setToolTip(_("Import New Key From Keyserver"));
connect(importKeyFromKeyServerAct, &QAction::triggered, this, [&]() {
CommonUtils::GetInstance()->slotImportKeyFromKeyServer(this);
});
exportKeyToClipboardAct = new QAction(_("Export To Clipboard"), this);
exportKeyToClipboardAct->setIcon(QIcon(":export_key_to_clipboard.png"));
exportKeyToClipboardAct->setToolTip(_("Export Selected Key(s) To Clipboard"));
connect(exportKeyToClipboardAct, SIGNAL(triggered()), this,
SLOT(slotExportKeyToClipboard()));
exportKeyToFileAct = new QAction(_("Export To File"), this);
exportKeyToFileAct->setIcon(QIcon(":export_key_to_file.png"));
exportKeyToFileAct->setToolTip(_("Export Selected Key(s) To File"));
connect(exportKeyToFileAct, SIGNAL(triggered()), this,
SLOT(slotExportKeyToFile()));
exportKeyAsOpenSSHFormat = new QAction(_("Export As OpenSSH"), this);
exportKeyAsOpenSSHFormat->setIcon(QIcon(":ssh-key.png"));
exportKeyAsOpenSSHFormat->setToolTip(
_("Export Selected Key(s) As OpenSSH Format to File"));
connect(exportKeyAsOpenSSHFormat, SIGNAL(triggered()), this,
SLOT(slotExportAsOpenSSHFormat()));
deleteSelectedKeysAct = new QAction(_("Delete Selected Key(s)"), this);
deleteSelectedKeysAct->setToolTip(_("Delete the Selected keys"));
connect(deleteSelectedKeysAct, SIGNAL(triggered()), this,
SLOT(slotDeleteSelectedKeys()));
deleteCheckedKeysAct = new QAction(_("Delete Checked Key(s)"), this);
deleteCheckedKeysAct->setToolTip(_("Delete the Checked keys"));
deleteCheckedKeysAct->setIcon(QIcon(":button_delete.png"));
connect(deleteCheckedKeysAct, SIGNAL(triggered()), this,
SLOT(slotDeleteCheckedKeys()));
showKeyDetailsAct = new QAction(_("Show Key Details"), this);
showKeyDetailsAct->setToolTip(_("Show Details for this Key"));
connect(showKeyDetailsAct, SIGNAL(triggered()), this,
SLOT(slotShowKeyDetails()));
}
void KeyMgmt::createMenus() {
fileMenu = menuBar()->addMenu(_("File"));
fileMenu->addAction(openKeyFileAct);
fileMenu->addAction(closeAct);
keyMenu = menuBar()->addMenu(_("Key"));
generateKeyMenu = keyMenu->addMenu(_("Generate Key"));
generateKeyMenu->addAction(generateKeyPairAct);
generateKeyMenu->addAction(generateSubKeyAct);
importKeyMenu = keyMenu->addMenu(_("Import Key"));
importKeyMenu->addAction(importKeyFromFileAct);
importKeyMenu->addAction(importKeyFromClipboardAct);
importKeyMenu->addAction(importKeyFromKeyServerAct);
keyMenu->addAction(exportKeyToFileAct);
keyMenu->addAction(exportKeyToClipboardAct);
keyMenu->addAction(exportKeyAsOpenSSHFormat);
keyMenu->addSeparator();
keyMenu->addAction(deleteCheckedKeysAct);
}
void KeyMgmt::createToolBars() {
QToolBar* keyToolBar = addToolBar(_("Key"));
keyToolBar->setObjectName("keytoolbar");
// add button with popup menu for import
auto* generateToolButton = new QToolButton(this);
generateToolButton->setMenu(generateKeyMenu);
generateToolButton->setPopupMode(QToolButton::InstantPopup);
generateToolButton->setIcon(QIcon(":key_generate.png"));
generateToolButton->setText(_("Generate"));
generateToolButton->setToolTip(_("Generate A New Keypair or Subkey"));
generateToolButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
keyToolBar->addWidget(generateToolButton);
// add button with popup menu for import
auto* toolButton = new QToolButton(this);
toolButton->setMenu(importKeyMenu);
toolButton->setPopupMode(QToolButton::InstantPopup);
toolButton->setIcon(QIcon(":key_import.png"));
toolButton->setToolTip(_("Import key"));
toolButton->setText(_("Import Key"));
toolButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
keyToolBar->addWidget(toolButton);
keyToolBar->addSeparator();
keyToolBar->addAction(deleteCheckedKeysAct);
keyToolBar->addSeparator();
keyToolBar->addAction(exportKeyToFileAct);
keyToolBar->addAction(exportKeyToClipboardAct);
keyToolBar->addAction(exportKeyAsOpenSSHFormat);
}
void KeyMgmt::slotDeleteSelectedKeys() {
deleteKeysWithWarning(mKeyList->getSelected());
}
void KeyMgmt::slotDeleteCheckedKeys() {
deleteKeysWithWarning(mKeyList->getChecked());
}
void KeyMgmt::deleteKeysWithWarning(KeyIdArgsListPtr key_ids) {
/**
* TODO: Different Messages for private/public key, check if
* more than one selected... compare to seahorse "delete-dialog"
*/
LOG(INFO) << "KeyMgmt::deleteKeysWithWarning Called";
if (key_ids->empty()) return;
QString keynames;
for (const auto& key_id : *key_ids) {
auto key = GpgKeyGetter::GetInstance().GetKey(key_id);
if (!key.good()) continue;
keynames.append(QString::fromStdString(key.name()));
keynames.append(" <");
keynames.append(QString::fromStdString(key.email()));
keynames.append(">
");
}
int ret = QMessageBox::warning(
this, _("Deleting Keys"),
"" +
QString(
_("Are you sure that you want to delete the following keys?")) +
"
" + keynames + +"
" +
_("The action can not be undone."),
QMessageBox::No | QMessageBox::Yes);
if (ret == QMessageBox::Yes) {
GpgKeyOpera::GetInstance().DeleteKeys(std::move(key_ids));
emit signalKeyStatusUpdated();
}
}
void KeyMgmt::slotShowKeyDetails() {
auto keys_selected = mKeyList->getSelected();
if (keys_selected->empty()) return;
auto key = GpgKeyGetter::GetInstance().GetKey(keys_selected->front());
if (!key.good()) {
QMessageBox::critical(nullptr, _("Error"), _("Key Not Found."));
return;
}
new KeyDetailsDialog(key);
}
void KeyMgmt::slotExportKeyToFile() {
ByteArrayPtr key_export_data = nullptr;
auto keys_checked = mKeyList->getChecked();
if (!GpgKeyImportExporter::GetInstance().ExportKeys(keys_checked,
key_export_data)) {
return;
}
auto key =
GpgKeyGetter::GetInstance().GetKey(mKeyList->getSelected()->front());
if (!key.good()) {
QMessageBox::critical(nullptr, _("Error"), _("Key Not Found."));
return;
}
QString fileString = QString::fromStdString(key.name() + " " + key.email() +
"(" + key.id() + ")_pub.asc");
QString file_name = QFileDialog::getSaveFileName(
this, _("Export Key To File"), fileString,
QString(_("Key Files")) + " (*.asc *.txt);;All Files (*)");
write_buffer_to_file(file_name.toStdString(), *key_export_data);
emit signalStatusBarChanged(QString(_("key(s) exported")));
}
void KeyMgmt::slotExportKeyToClipboard() {
ByteArrayPtr key_export_data = nullptr;
auto keys_checked = mKeyList->getChecked();
if (!GpgKeyImportExporter::GetInstance().ExportKeys(keys_checked,
key_export_data)) {
return;
}
QApplication::clipboard()->setText(QString::fromStdString(*key_export_data));
}
void KeyMgmt::slotGenerateKeyDialog() {
auto* keyGenDialog = new KeyGenDialog(this);
keyGenDialog->show();
}
void KeyMgmt::closeEvent(QCloseEvent* event) {
slotSaveWindowState();
QMainWindow::closeEvent(event);
}
void KeyMgmt::slotGenerateSubKey() {
auto keys_selected = mKeyList->getSelected();
if (keys_selected->empty()) {
QMessageBox::information(
nullptr, _("Invalid Operation"),
_("Please select one KeyPair before doing this operation."));
return;
}
const auto key = GpgKeyGetter::GetInstance().GetKey(keys_selected->front());
if (!key.good()) {
QMessageBox::critical(nullptr, _("Error"), _("Key Not Found."));
return;
}
if (!key.is_private_key()) {
QMessageBox::critical(nullptr, _("Invalid Operation"),
_("If a key pair does not have a private key then "
"it will not be able to generate sub-keys."));
return;
}
auto dialog = new SubkeyGenerateDialog(key.id(), this);
dialog->show();
}
void KeyMgmt::slotSaveWindowState() {
auto& settings =
GpgFrontend::UI::GlobalSettingStation::GetInstance().GetUISettings();
if (!settings.exists("window") ||
settings.lookup("window").getType() != libconfig::Setting::TypeGroup)
settings.add("window", libconfig::Setting::TypeGroup);
auto& window = settings["window"];
if (!window.exists("key_management") ||
window.lookup("key_management").getType() !=
libconfig::Setting::TypeGroup)
window.add("key_management", libconfig::Setting::TypeGroup);
auto& key_management = window["key_management"];
if (!key_management.exists("position") ||
key_management.lookup("position").getType() !=
libconfig::Setting::TypeGroup) {
auto& position =
key_management.add("position", libconfig::Setting::TypeGroup);
position.add("x", libconfig::Setting::TypeInt) = pos().x();
position.add("y", libconfig::Setting::TypeInt) = pos().y();
} else {
key_management["position"]["x"] = pos().x();
key_management["position"]["y"] = pos().y();
}
if (!key_management.exists("size") ||
key_management.lookup("size").getType() !=
libconfig::Setting::TypeGroup) {
auto& size = key_management.add("size", libconfig::Setting::TypeGroup);
size.add("width", libconfig::Setting::TypeInt) = QWidget::width();
size.add("height", libconfig::Setting::TypeInt) = QWidget::height();
} else {
key_management["size"]["width"] = QWidget::width();
key_management["size"]["height"] = QWidget::height();
}
if (!key_management.exists("window_state"))
key_management.add("window_state", libconfig::Setting::TypeString) =
saveState().toBase64().toStdString();
GlobalSettingStation::GetInstance().Sync();
}
void KeyMgmt::slotExportAsOpenSSHFormat() {
ByteArrayPtr key_export_data = nullptr;
auto keys_checked = mKeyList->getChecked();
if (keys_checked->empty()) {
QMessageBox::critical(nullptr, _("Error"), _("No Key Checked."));
return;
}
auto key = GpgKeyGetter::GetInstance().GetKey(keys_checked->front());
if (!GpgKeyImportExporter::GetInstance().ExportKeyOpenSSH(key,
key_export_data)) {
QMessageBox::critical(nullptr, _("Error"),
_("An error occur in exporting."));
return;
}
if (key_export_data->empty()) {
QMessageBox::critical(
nullptr, _("Error"),
_("This key may not be able to export as OpenSSH format. Please check "
"the key-size of the subkey(s) used to sign."));
return;
}
key = GpgKeyGetter::GetInstance().GetKey(keys_checked->front());
if (!key.good()) {
QMessageBox::critical(nullptr, _("Error"), _("Key Not Found."));
return;
}
QString fileString = QString::fromStdString(key.name() + " " + key.email() +
"(" + key.id() + ").pub");
QString file_name = QFileDialog::getSaveFileName(
this, _("Export OpenSSH Key To File"), fileString,
QString(_("OpenSSH Public Key Files")) + " (*.pub);;All Files (*)");
if (!file_name.isEmpty()) {
write_buffer_to_file(file_name.toStdString(), *key_export_data);
emit signalStatusBarChanged(QString(_("key(s) exported")));
}
}
} // namespace GpgFrontend::UI