diff options
Diffstat (limited to 'src/ui/dialog')
47 files changed, 7936 insertions, 2 deletions
diff --git a/src/ui/dialog/Wizard.h b/src/ui/dialog/Wizard.h index 1ab0f782..879dc5d9 100644 --- a/src/ui/dialog/Wizard.h +++ b/src/ui/dialog/Wizard.h @@ -32,8 +32,8 @@ #include "core/GpgConstants.h" #include "main_window/KeyMgmt.h" #include "ui/GpgFrontendUI.h" -#include "ui/key_generate/KeygenDialog.h" -#include "ui/settings/SettingsDialog.h" +#include "ui/dialog/key_generate/KeygenDialog.h" +#include "ui/dialog/settings/SettingsDialog.h" namespace GpgFrontend::UI { diff --git a/src/ui/dialog/details/SignatureDetailsDialog.cpp b/src/ui/dialog/details/SignatureDetailsDialog.cpp new file mode 100644 index 00000000..a3ad03b3 --- /dev/null +++ b/src/ui/dialog/details/SignatureDetailsDialog.cpp @@ -0,0 +1,29 @@ +/** + * 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 "SignatureDetailsDialog.h" diff --git a/src/ui/dialog/details/SignatureDetailsDialog.h b/src/ui/dialog/details/SignatureDetailsDialog.h new file mode 100644 index 00000000..847b320b --- /dev/null +++ b/src/ui/dialog/details/SignatureDetailsDialog.h @@ -0,0 +1,38 @@ +/** + * 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_SIGNATUREDETAILSDIALOG_H +#define GPGFRONTEND_SIGNATUREDETAILSDIALOG_H +#include "ui/GpgFrontendUI.h" + +class SignatureDetailsDialog : public QDialog { + Q_OBJECT + public: +}; + +#endif // GPGFRONTEND_SIGNATUREDETAILSDIALOG_H diff --git a/src/ui/dialog/details/VerifyDetailsDialog.cpp b/src/ui/dialog/details/VerifyDetailsDialog.cpp new file mode 100644 index 00000000..d2af4ee1 --- /dev/null +++ b/src/ui/dialog/details/VerifyDetailsDialog.cpp @@ -0,0 +1,97 @@ +/** + * 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 "VerifyDetailsDialog.h" + +#include <boost/format.hpp> + +namespace GpgFrontend::UI { + +VerifyDetailsDialog::VerifyDetailsDialog(QWidget* parent, GpgError error, + GpgVerifyResult result) + : QDialog(parent), m_result_(std::move(result)), error_(error) { + this->setWindowTitle(_("Signatures Details")); + + main_layout_ = new QHBoxLayout(); + this->setLayout(main_layout_); + + slot_refresh(); + + this->exec(); +} + +void VerifyDetailsDialog::slot_refresh() { + m_vbox_ = new QWidget(); + auto* mVboxLayout = new QVBoxLayout(m_vbox_); + main_layout_->addWidget(m_vbox_); + + // Button Box for close button + button_box_ = new QDialogButtonBox(QDialogButtonBox::Close); + connect(button_box_, &QDialogButtonBox::rejected, this, + &VerifyDetailsDialog::close); + + auto sign = m_result_->signatures; + + if (sign == nullptr) { + mVboxLayout->addWidget(new QLabel(_("No valid input found"))); + mVboxLayout->addWidget(button_box_); + return; + } + + // Get timestamp of signature of current text + QDateTime timestamp; + timestamp.setTime_t(sign->timestamp); + + // Set the title widget depending on sign status + if (gpg_err_code(sign->status) == GPG_ERR_BAD_SIGNATURE) { + mVboxLayout->addWidget(new QLabel(_("Error Validating signature"))); + } else if (input_signature_ != nullptr) { + const auto info = (boost::format(_("File was signed on %1%")) % + QLocale::system().toString(timestamp).toStdString()) + .str() + + "<br/>" + _("It Contains") + ": " + "<br/><br/>"; + mVboxLayout->addWidget(new QLabel(info.c_str())); + } else { + const auto info = (boost::format(_("Signed on %1%")) % + QLocale::system().toString(timestamp).toStdString()) + .str() + + "<br/>" + _("It Contains") + ": " + "<br/><br/>"; + mVboxLayout->addWidget(new QLabel(info.c_str())); + } + // Add information box for every single key + while (sign) { + GpgSignature signature(sign); + auto* sign_box = new VerifyKeyDetailBox(signature, this); + sign = sign->next; + mVboxLayout->addWidget(sign_box); + } + + mVboxLayout->addWidget(button_box_); +} + +} // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/dialog/details/VerifyDetailsDialog.h b/src/ui/dialog/details/VerifyDetailsDialog.h new file mode 100644 index 00000000..97e2cc2d --- /dev/null +++ b/src/ui/dialog/details/VerifyDetailsDialog.h @@ -0,0 +1,75 @@ +/** + * 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 __VERIFYDETAILSDIALOG_H__ +#define __VERIFYDETAILSDIALOG_H__ + +#include "ui/GpgFrontendUI.h" +#include "ui/widgets/PlainTextEditorPage.h" +#include "ui/widgets/VerifyKeyDetailBox.h" + +namespace GpgFrontend::UI { +/** + * @brief + * + */ +class VerifyDetailsDialog : public QDialog { + Q_OBJECT + public: + /** + * @brief Construct a new Verify Details Dialog object + * + * @param parent + * @param error + * @param result + */ + explicit VerifyDetailsDialog(QWidget* parent, GpgError error, + GpgVerifyResult result); + + private slots: + + /** + * @brief + * + */ + void slot_refresh(); + + private: + KeyList* key_list_; ///< + QHBoxLayout* main_layout_; ///< + QWidget* m_vbox_{}; ///< + QByteArray* input_data_{}; ///< + QByteArray* input_signature_{}; ///< + QDialogButtonBox* button_box_{}; ///< + GpgVerifyResult m_result_; ///< + gpgme_error_t error_; ///< +}; + +} // namespace GpgFrontend::UI + +#endif // __VERIFYDETAILSDIALOG_H__ diff --git a/src/ui/dialog/help/AboutDialog.cpp b/src/ui/dialog/help/AboutDialog.cpp new file mode 100644 index 00000000..7612d9d9 --- /dev/null +++ b/src/ui/dialog/help/AboutDialog.cpp @@ -0,0 +1,256 @@ +/** + * 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 "AboutDialog.h" + +#include "GpgFrontendBuildInfo.h" +#include "core/function/GlobalSettingStation.h" +#include "core/thread/TaskRunnerGetter.h" +#include "ui/thread/VersionCheckTask.h" + +namespace GpgFrontend::UI { + +AboutDialog::AboutDialog(int defaultIndex, QWidget* parent) + : GeneralDialog(typeid(AboutDialog).name(), parent) { + this->setWindowTitle(QString(_("About")) + " " + qApp->applicationName()); + + auto* tabWidget = new QTabWidget; + auto* infoTab = new InfoTab(); + auto* translatorsTab = new TranslatorsTab(); + update_tab_ = new UpdateTab(); + + tabWidget->addTab(infoTab, _("About Software")); + tabWidget->addTab(translatorsTab, _("Translators")); + tabWidget->addTab(update_tab_, _("Update")); + + connect(tabWidget, &QTabWidget::currentChanged, this, + [&](int index) { LOG(INFO) << "Current Index" << index; }); + + if (defaultIndex < tabWidget->count() && defaultIndex >= 0) { + tabWidget->setCurrentIndex(defaultIndex); + } + + auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); + connect(buttonBox, &QDialogButtonBox::accepted, this, &AboutDialog::close); + + auto* mainLayout = new QVBoxLayout; + mainLayout->addWidget(tabWidget); + mainLayout->addWidget(buttonBox); + setLayout(mainLayout); + + this->resize(450, 580); + this->setMinimumWidth(450); + this->show(); +} + +void AboutDialog::showEvent(QShowEvent* ev) { + QDialog::showEvent(ev); + update_tab_->getLatestVersion(); +} + +InfoTab::InfoTab(QWidget* parent) : QWidget(parent) { + auto* pixmap = new QPixmap(":gpgfrontend-logo.png"); + auto* text = new QString( + "<center><h2>" + qApp->applicationName() + "</h2></center>" + + "<center><b>" + qApp->applicationVersion() + "</b></center>" + + "<center>" + GIT_VERSION + "</center>" + "<br><center>" + + _("GpgFrontend is an easy-to-use, compact, cross-platform, " + "and installation-free gpg front-end tool." + "It visualizes most of the common operations of gpg commands." + "It's licensed under the GPL v3") + + "<br><br>" + "<b>" + + _("Developer:") + "</b><br>" + "Saturneric" + "<br><br>" + + _("If you have any questions or suggestions, raise an issue at") + + "<br/>" + " <a href=\"https://github.com/saturneric/GpgFrontend\">GitHub</a> " + + _("or send a mail to my mailing list at") + " <a " + + "href=\"mailto:[email protected]\">[email protected]</a>." + "<br><br> " + + _("Built with Qt") + " " + qVersion() + " " + _("and GPGME") + " " + + GpgFrontend::GpgContext::GetInstance().GetInfo().GpgMEVersion.c_str() + + "<br>" + _("Built at") + " " + BUILD_TIMESTAMP + "</center>"); + + auto* layout = new QGridLayout(); + auto* pixmapLabel = new QLabel(); + pixmapLabel->setPixmap(*pixmap); + layout->addWidget(pixmapLabel, 0, 0, 1, -1, Qt::AlignCenter); + auto* aboutLabel = new QLabel(); + aboutLabel->setText(*text); + aboutLabel->setWordWrap(true); + aboutLabel->setOpenExternalLinks(true); + layout->addWidget(aboutLabel, 1, 0, 1, -1); + layout->addItem( + new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Fixed), 2, 1, + 1, 1); + + setLayout(layout); +} + +TranslatorsTab::TranslatorsTab(QWidget* parent) : QWidget(parent) { + QFile translators_qfile; + auto translators_file = + GlobalSettingStation::GetInstance().GetResourceDir() / "TRANSLATORS"; + translators_qfile.setFileName(translators_file.u8string().c_str()); +#ifdef LINUX + if (!translators_qfile.exists()) { + translators_qfile.setFileName("/usr/local/share/GpgFrontend/TRANSLATORS"); + } +#endif + + translators_qfile.open(QIODevice::ReadOnly); + QByteArray in_buffer = translators_qfile.readAll(); + + auto* label = new QLabel(in_buffer); + + auto* main_layout = new QVBoxLayout(this); + main_layout->addWidget(label); + main_layout->addStretch(); + + auto notice_label = new QLabel( + _("If you think there are any problems with the translation, why not " + "participate in the translation work? If you want to participate, " + "please " + "read the document or contact me via email."), + this); + notice_label->setWordWrap(true); + main_layout->addWidget(notice_label); + + setLayout(main_layout); +} + +UpdateTab::UpdateTab(QWidget* parent) : QWidget(parent) { + auto* pixmap = new QPixmap(":gpgfrontend-logo.png"); + auto* layout = new QGridLayout(); + auto* pixmap_label = new QLabel(); + pixmap_label->setPixmap(*pixmap); + layout->addWidget(pixmap_label, 0, 0, 1, -1, Qt::AlignCenter); + + current_version_ = "v" + QString::number(VERSION_MAJOR) + "." + + QString::number(VERSION_MINOR) + "." + + QString::number(VERSION_PATCH); + + auto tips_label = new QLabel(); + tips_label->setText( + "<center>" + + QString(_("It is recommended that you always check the version " + "of GpgFrontend and upgrade to the latest version.")) + + "</center><center>" + + _("New versions not only represent new features, but " + "also often represent functional and security fixes.") + + "</center>"); + tips_label->setWordWrap(true); + + current_version_label_ = new QLabel(); + current_version_label_->setText("<center>" + QString(_("Current Version")) + + _(": ") + "<b>" + current_version_ + + "</b></center>"); + current_version_label_->setWordWrap(true); + + latest_version_label_ = new QLabel(); + latest_version_label_->setWordWrap(true); + + upgrade_label_ = new QLabel(); + upgrade_label_->setWordWrap(true); + upgrade_label_->setOpenExternalLinks(true); + upgrade_label_->setHidden(true); + + pb_ = new QProgressBar(); + pb_->setRange(0, 0); + pb_->setTextVisible(false); + + layout->addWidget(tips_label, 1, 0, 1, -1); + layout->addWidget(current_version_label_, 2, 0, 1, -1); + layout->addWidget(latest_version_label_, 3, 0, 1, -1); + layout->addWidget(upgrade_label_, 4, 0, 1, -1); + layout->addWidget(pb_, 5, 0, 1, -1); + layout->addItem( + new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Fixed), 2, 1, + 1, 1); + + setLayout(layout); +} + +void UpdateTab::getLatestVersion() { + this->pb_->setHidden(false); + + LOG(INFO) << _("try to get latest version"); + + auto* version_task = new VersionCheckTask(); + + connect(version_task, &VersionCheckTask::SignalUpgradeVersion, this, + &UpdateTab::slot_show_version_status); + + Thread::TaskRunnerGetter::GetInstance() + .GetTaskRunner(Thread::TaskRunnerGetter::kTaskRunnerType_Network) + ->PostTask(version_task); +} + +void UpdateTab::slot_show_version_status(const SoftwareVersion& version) { + this->pb_->setHidden(true); + latest_version_label_->setText( + "<center><b>" + QString(_("Latest Version From Github")) + ": " + + version.latest_version.c_str() + "</b></center>"); + + if (version.NeedUpgrade()) { + upgrade_label_->setText( + "<center>" + + QString(_("The current version is less than the latest version on " + "github.")) + + "</center><center>" + _("Please click") + + " <a " + "href=\"https://www.gpgfrontend.pub/#/downloads\">" + + _("Here") + "</a> " + _("to download the latest stable version.") + + "</center>"); + upgrade_label_->show(); + } else if (version.VersionWithDrawn()) { + upgrade_label_->setText( + "<center>" + + QString(_("This version has serious problems and has been withdrawn. " + "Please stop using it immediately.")) + + "</center><center>" + _("Please click") + + " <a " + "href=\"https://github.com/saturneric/GpgFrontend/releases\">" + + _("Here") + "</a> " + _("to download the latest stable version.") + + "</center>"); + upgrade_label_->show(); + } else if (!version.CurrentVersionReleased()) { + upgrade_label_->setText( + "<center>" + + QString(_("This version has not been released yet, it may be a beta " + "version. If you are not a tester and care about version " + "stability, please do not use this version.")) + + "</center><center>" + _("Please click") + + " <a " + "href=\"https://www.gpgfrontend.pub/#/downloads\">" + + _("Here") + "</a> " + _("to download the latest stable version.") + + "</center>"); + upgrade_label_->show(); + } +} + +} // namespace GpgFrontend::UI diff --git a/src/ui/dialog/help/AboutDialog.h b/src/ui/dialog/help/AboutDialog.h new file mode 100644 index 00000000..09a63734 --- /dev/null +++ b/src/ui/dialog/help/AboutDialog.h @@ -0,0 +1,146 @@ +/** + * 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 __ABOUTDIALOG_H__ +#define __ABOUTDIALOG_H__ + +#include "core/GpgContext.h" +#include "ui/GpgFrontendUI.h" +#include "ui/dialog/GeneralDialog.h" +#include "ui/struct/SoftwareVersion.h" + +namespace GpgFrontend::UI { + +/** + * @brief Class containing the main tab of about dialog + * + */ +class InfoTab : public QWidget { + Q_OBJECT + + public: + /** + * @brief Construct a new Info Tab object + * + * @param parent + */ + explicit InfoTab(QWidget* parent = nullptr); +}; + +/** + * @brief Class containing the translator tab of about dialog + * + */ +class TranslatorsTab : public QWidget { + Q_OBJECT + + public: + /** + * @brief Construct a new Translators Tab object + * + * @param parent + */ + explicit TranslatorsTab(QWidget* parent = nullptr); +}; + +/** + * @brief Class containing the main tab of about dialog + * + */ +class UpdateTab : public QWidget { + Q_OBJECT + + QLabel* current_version_label_; ///< + QLabel* latest_version_label_; ///< + QLabel* upgrade_label_; ///< + QProgressBar* pb_; ///< + QString current_version_; ///< + QPushButton* download_button_; ///< + + public: + /** + * @brief Construct a new Update Tab object + * + * @param parent + */ + explicit UpdateTab(QWidget* parent = nullptr); + + /** + * @brief Get the Latest Version object + * + */ + void getLatestVersion(); + + private slots: + /** + * @brief + * + * @param version + */ + void slot_show_version_status(const SoftwareVersion& version); + + signals: + /** + * @brief + * + * @param data + */ + void SignalReplyFromUpdateServer(QByteArray data); +}; + +/** + * @brief Class for handling the about dialog + * + */ +class AboutDialog : public GeneralDialog { + Q_OBJECT + + public: + /** + * @brief Construct a new About Dialog object + * + * @param defaultIndex + * @param parent + */ + explicit AboutDialog(int defaultIndex, QWidget* parent); + + protected: + /** + * @brief + * + * @param ev + */ + void showEvent(QShowEvent* ev) override; + + private: + UpdateTab* update_tab_; ///< +}; + +} // namespace GpgFrontend::UI + +#endif // __ABOUTDIALOG_H__ diff --git a/src/ui/dialog/import_export/ExportKeyPackageDialog.cpp b/src/ui/dialog/import_export/ExportKeyPackageDialog.cpp new file mode 100644 index 00000000..074f07ba --- /dev/null +++ b/src/ui/dialog/import_export/ExportKeyPackageDialog.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 "ExportKeyPackageDialog.h" + +#include <boost/format.hpp> + +#include "core/function/KeyPackageOperator.h" +#include "core/function/gpg/GpgKeyGetter.h" +#include "ui_ExportKeyPackageDialog.h" + +GpgFrontend::UI::ExportKeyPackageDialog::ExportKeyPackageDialog( + KeyIdArgsListPtr key_ids, QWidget* parent) + : GeneralDialog(typeid(ExportKeyPackageDialog).name(), parent), + ui_(std::make_shared<Ui_exportKeyPackageDialog>()), + key_ids_(std::move(key_ids)) { + ui_->setupUi(this); + + ui_->nameValueLabel->setText( + KeyPackageOperator::GenerateKeyPackageName().c_str()); + + connect(ui_->gnerateNameButton, &QPushButton::clicked, this, [=]() { + ui_->nameValueLabel->setText( + KeyPackageOperator::GenerateKeyPackageName().c_str()); + }); + + connect(ui_->setOutputPathButton, &QPushButton::clicked, this, [=]() { + auto file_name = QFileDialog::getSaveFileName( + this, _("Export Key Package"), ui_->nameValueLabel->text() + ".gfepack", + QString(_("Key Package")) + " (*.gfepack);;All Files (*)"); + ui_->outputPathLabel->setText(file_name); + }); + + connect(ui_->generatePassphraseButton, &QPushButton::clicked, this, [=]() { + auto file_name = QFileDialog::getSaveFileName( + this, _("Export Key Package Passphrase"), + ui_->nameValueLabel->text() + ".key", + QString(_("Key File")) + " (*.key);;All Files (*)"); + + if (!KeyPackageOperator::GeneratePassphrase(file_name.toStdString(), + passphrase_)) { + QMessageBox::critical( + this, _("Error"), + _("An error occurred while generating the passphrase file.")); + return; + } + ui_->passphraseValueLabel->setText(file_name); + }); + + connect(ui_->button_box_, &QDialogButtonBox::accepted, this, [=]() { + if (ui_->outputPathLabel->text().isEmpty()) { + QMessageBox::critical( + this, _("Forbidden"), + _("Please select an output path before exporting.")); + return; + } + + if (ui_->passphraseValueLabel->text().isEmpty()) { + QMessageBox::critical( + this, _("Forbidden"), + _("Please generate a password to protect your key before exporting, " + "it is very important. Don't forget to back up your password in a " + "safe place.")); + return; + } + + // get suitable key ids + auto key_id_exported = std::make_unique<KeyIdArgsList>(); + auto keys = GpgKeyGetter::GetInstance().GetKeys(key_ids_); + for (const auto& key : *keys) { + if (ui_->noPublicKeyCheckBox->isChecked() && !key.IsPrivateKey()) + continue; + key_id_exported->push_back(key.GetId()); + } + + if (KeyPackageOperator::GenerateKeyPackage( + ui_->outputPathLabel->text().toStdString(), + ui_->nameValueLabel->text().toStdString(), key_id_exported, + passphrase_, ui_->includeSecretKeyCheckBox->isChecked())) { + QMessageBox::information( + this, _("Success"), + QString( + _("The Key Package has been successfully generated and has been " + "protected by encryption algorithms(AES-256-ECB). You can " + "safely transfer your Key Package.")) + + "<br /><br />" + "<b>" + + _("But the key file cannot be leaked under any " + "circumstances. Please delete the Key Package and key file as " + "soon " + "as possible after completing the transfer operation.") + + "</b>"); + accept(); + } else { + QMessageBox::critical( + this, _("Error"), + _("An error occurred while exporting the key package.")); + } + }); + + connect(ui_->button_box_, &QDialogButtonBox::rejected, this, + [=]() { this->close(); }); + + ui_->nameLabel->setText(_("Key Package Name")); + ui_->selectOutputPathLabel->setText(_("Output Path")); + ui_->passphraseLabel->setText(_("Passphrase")); + ui_->tipsLabel->setText( + _("Tips: You can use Key Package to safely and conveniently transfer " + "your public and private keys between devices.")); + ui_->generatePassphraseButton->setText(_("Generate and Save Passphrase")); + ui_->gnerateNameButton->setText(_("Generate Key Package Name")); + ui_->setOutputPathButton->setText(_("Select Output Path")); + + ui_->includeSecretKeyCheckBox->setText( + _("Include secret key (Think twice before acting)")); + ui_->noPublicKeyCheckBox->setText( + _("Exclude keys that do not have a private key")); + + setAttribute(Qt::WA_DeleteOnClose); + setWindowTitle(_("exportKeyPackageDialog")); +} diff --git a/src/ui/dialog/import_export/ExportKeyPackageDialog.h b/src/ui/dialog/import_export/ExportKeyPackageDialog.h new file mode 100644 index 00000000..c5f9a2b1 --- /dev/null +++ b/src/ui/dialog/import_export/ExportKeyPackageDialog.h @@ -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 + * + */ + +#ifndef GPGFRONTEND_EXPORTKEYPACKAGEDIALOG_H +#define GPGFRONTEND_EXPORTKEYPACKAGEDIALOG_H + +#include "GpgFrontendUI.h" +#include "ui/dialog/GeneralDialog.h" + +class Ui_exportKeyPackageDialog; + +namespace GpgFrontend::UI { + +/** + * @brief + * + */ +class ExportKeyPackageDialog : public GeneralDialog { + Q_OBJECT + + public: + /** + * @brief Construct a new Export Key Package Dialog object + * + * @param key_ids + * @param parent + */ + explicit ExportKeyPackageDialog(KeyIdArgsListPtr key_ids, QWidget* parent); + + private: + std::shared_ptr<Ui_exportKeyPackageDialog> ui_; ///< + KeyIdArgsListPtr key_ids_; ///< + std::string passphrase_; ///< +}; +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_EXPORTKEYPACKAGEDIALOG_H diff --git a/src/ui/dialog/import_export/KeyImportDetailDialog.cpp b/src/ui/dialog/import_export/KeyImportDetailDialog.cpp new file mode 100644 index 00000000..31183a34 --- /dev/null +++ b/src/ui/dialog/import_export/KeyImportDetailDialog.cpp @@ -0,0 +1,204 @@ +/** + * 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 "KeyImportDetailDialog.h" + +#include "core/function/gpg/GpgKeyGetter.h" + +namespace GpgFrontend::UI { +KeyImportDetailDialog::KeyImportDetailDialog(GpgImportInformation result, + bool automatic, QWidget* parent) + : GeneralDialog(typeid(KeyImportDetailDialog).name(), parent), + m_result_(std::move(result)) { + // If no key for import found, just show a message + if (m_result_.considered == 0) { + if (automatic) + QMessageBox::information(parent, _("Key Update Details"), + _("No keys found")); + else + QMessageBox::information(parent, _("Key Import Details"), + _("No keys found to import")); + emit finished(0); + this->close(); + this->deleteLater(); + } else { + auto* mv_box = new QVBoxLayout(); + + this->create_general_info_box(); + mv_box->addWidget(general_info_box_); + this->create_keys_table(); + mv_box->addWidget(keys_table_); + this->create_button_box(); + mv_box->addWidget(button_box_); + + this->setLayout(mv_box); + if (automatic) + this->setWindowTitle(_("Key Update Details")); + else + this->setWindowTitle(_("Key Import Details")); + + auto pos = QPoint(100, 100); + if (parent) pos += parent->pos(); + this->move(pos); + + this->setMinimumSize(QSize(600, 300)); + this->adjustSize(); + + this->setModal(true); + this->show(); + } +} + +void KeyImportDetailDialog::create_general_info_box() { + // GridBox for general import information + general_info_box_ = new QGroupBox(_("General key info")); + auto* generalInfoBoxLayout = new QGridLayout(general_info_box_); + + generalInfoBoxLayout->addWidget(new QLabel(QString(_("Considered")) + ": "), + 1, 0); + generalInfoBoxLayout->addWidget( + new QLabel(QString::number(m_result_.considered)), 1, 1); + int row = 2; + if (m_result_.unchanged != 0) { + generalInfoBoxLayout->addWidget( + new QLabel(QString(_("Public unchanged")) + ": "), row, 0); + generalInfoBoxLayout->addWidget( + new QLabel(QString::number(m_result_.unchanged)), row, 1); + row++; + } + if (m_result_.imported != 0) { + generalInfoBoxLayout->addWidget(new QLabel(QString(_("Imported")) + ": "), + row, 0); + generalInfoBoxLayout->addWidget( + new QLabel(QString::number(m_result_.imported)), row, 1); + row++; + } + if (m_result_.not_imported != 0) { + generalInfoBoxLayout->addWidget( + new QLabel(QString(_("Not Imported")) + ": "), row, 0); + generalInfoBoxLayout->addWidget( + new QLabel(QString::number(m_result_.not_imported)), row, 1); + row++; + } + if (m_result_.secret_read != 0) { + generalInfoBoxLayout->addWidget( + new QLabel(QString(_("Private Read")) + ": "), row, 0); + generalInfoBoxLayout->addWidget( + new QLabel(QString::number(m_result_.secret_read)), row, 1); + row++; + } + if (m_result_.secret_imported != 0) { + generalInfoBoxLayout->addWidget( + new QLabel(QString(_("Private Imported")) + ": "), row, 0); + generalInfoBoxLayout->addWidget( + new QLabel(QString::number(m_result_.secret_imported)), row, 1); + row++; + } + if (m_result_.secret_unchanged != 0) { + generalInfoBoxLayout->addWidget( + new QLabel(QString(_("Private Unchanged")) + ": "), row, 0); + generalInfoBoxLayout->addWidget( + new QLabel(QString::number(m_result_.secret_unchanged)), row, 1); + row++; + } +} + +void KeyImportDetailDialog::create_keys_table() { + LOG(INFO) << "KeyImportDetailDialog::create_keys_table() Called"; + + keys_table_ = new QTableWidget(this); + keys_table_->setRowCount(0); + keys_table_->setColumnCount(4); + keys_table_->setEditTriggers(QAbstractItemView::NoEditTriggers); + // Nothing is selectable + keys_table_->setSelectionMode(QAbstractItemView::NoSelection); + + QStringList headerLabels; + headerLabels << _("Name") << _("Email") << _("Status") << _("Fingerprint"); + keys_table_->verticalHeader()->hide(); + + keys_table_->setHorizontalHeaderLabels(headerLabels); + int row = 0; + for (const auto& imp_key : m_result_.importedKeys) { + keys_table_->setRowCount(row + 1); + GpgKey key = GpgKeyGetter::GetInstance().GetKey(imp_key.fpr); + if (!key.IsGood()) continue; + keys_table_->setItem( + row, 0, new QTableWidgetItem(QString::fromStdString(key.GetName()))); + keys_table_->setItem( + row, 1, new QTableWidgetItem(QString::fromStdString(key.GetEmail()))); + keys_table_->setItem( + row, 2, new QTableWidgetItem(get_status_string(imp_key.import_status))); + keys_table_->setItem( + row, 3, new QTableWidgetItem(QString::fromStdString(imp_key.fpr))); + row++; + } + keys_table_->horizontalHeader()->setSectionResizeMode( + 0, QHeaderView::ResizeToContents); + keys_table_->horizontalHeader()->setStretchLastSection(true); + keys_table_->resizeColumnsToContents(); +} + +QString KeyImportDetailDialog::get_status_string(int keyStatus) { + QString statusString; + // keystatus is greater than 15, if key is private + if (keyStatus > 15) { + statusString.append(_("Private")); + keyStatus = keyStatus - 16; + } else { + statusString.append(_("Public")); + } + if (keyStatus == 0) { + statusString.append(", " + QString(_("Unchanged"))); + } else { + if (keyStatus == 1) { + statusString.append(", " + QString(_("New Key"))); + } else { + if (keyStatus > 7) { + statusString.append(", " + QString(_("New Subkey"))); + keyStatus = keyStatus - 8; + } + if (keyStatus > 3) { + statusString.append(", " + QString(_("New Signature"))); + keyStatus = keyStatus - 4; + } + if (keyStatus > 1) { + statusString.append(", " + QString(_("New UID"))); + keyStatus = keyStatus - 2; + } + } + } + return statusString; +} + +void KeyImportDetailDialog::create_button_box() { + button_box_ = new QDialogButtonBox(QDialogButtonBox::Ok); + connect(button_box_, &QDialogButtonBox::accepted, this, + &KeyImportDetailDialog::close); +} +} // namespace GpgFrontend::UI diff --git a/src/ui/dialog/import_export/KeyImportDetailDialog.h b/src/ui/dialog/import_export/KeyImportDetailDialog.h new file mode 100644 index 00000000..06f44e94 --- /dev/null +++ b/src/ui/dialog/import_export/KeyImportDetailDialog.h @@ -0,0 +1,92 @@ +/** + * 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 __KEYIMPORTDETAILSDIALOG_H__ +#define __KEYIMPORTDETAILSDIALOG_H__ + +#include "core/GpgContext.h" +#include "core/function/gpg/GpgKeyImportExporter.h" +#include "ui/GpgFrontendUI.h" +#include "ui/dialog/GeneralDialog.h" + +namespace GpgFrontend::UI { + +/** + * @brief + * + */ +class KeyImportDetailDialog : public GeneralDialog { + Q_OBJECT + + public: + /** + * @brief Construct a new Key Import Detail Dialog object + * + * @param result + * @param automatic + * @param parent + */ + KeyImportDetailDialog(GpgImportInformation result, bool automatic, + QWidget* parent = nullptr); + + private: + /** + * @brief Create a general info box object + * + */ + void create_general_info_box(); + + /** + * @brief Create a keys table object + * + */ + void create_keys_table(); + + /** + * @brief Create a button box object + * + */ + void create_button_box(); + + /** + * @brief Get the status string object + * + * @param keyStatus + * @return QString + */ + static QString get_status_string(int keyStatus); + + QTableWidget* keys_table_{}; ///< + QGroupBox* general_info_box_{}; ///< + QGroupBox* key_info_box_{}; ///< + QDialogButtonBox* button_box_{}; ///< + GpgImportInformation m_result_; ///< +}; +} // namespace GpgFrontend::UI + +#endif // __KEYIMPORTDETAILSDIALOG_H__ diff --git a/src/ui/dialog/import_export/KeyServerImportDialog.cpp b/src/ui/dialog/import_export/KeyServerImportDialog.cpp new file mode 100644 index 00000000..6430a22e --- /dev/null +++ b/src/ui/dialog/import_export/KeyServerImportDialog.cpp @@ -0,0 +1,550 @@ +/** + * 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 "KeyServerImportDialog.h" + +#include <string> +#include <utility> + +#include "core/function/GlobalSettingStation.h" +#include "core/function/gpg/GpgKeyImportExporter.h" +#include "thread/KeyServerImportTask.h" +#include "ui/SignalStation.h" +#include "ui/struct/SettingsObject.h" +#include "ui/thread/KeyServerSearchTask.h" + +namespace GpgFrontend::UI { + +KeyServerImportDialog::KeyServerImportDialog(bool automatic, QWidget* parent) + : GeneralDialog("key_server_import_dialog", parent), + m_automatic_(automatic) { + // Layout for messagebox + auto* message_layout = new QHBoxLayout(); + + if (automatic) { + setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint); + } else { + // Buttons + + close_button_ = new QPushButton(_("Close")); + connect(close_button_, &QPushButton::clicked, this, + &KeyServerImportDialog::close); + import_button_ = new QPushButton(_("Import ALL")); + connect(import_button_, &QPushButton::clicked, this, + &KeyServerImportDialog::slot_import); + import_button_->setDisabled(true); + search_button_ = new QPushButton(_("Search")); + connect(search_button_, &QPushButton::clicked, this, + &KeyServerImportDialog::slot_search); + + // Line edits for search string + search_label_ = new QLabel(QString(_("Search String")) + _(": ")); + search_line_edit_ = new QLineEdit(); + + // combobox for keyserver list + key_server_label_ = new QLabel(QString(_("Key Server")) + _(": ")); + key_server_combo_box_ = create_comboBox(); + + // table containing the keys found + create_keys_table(); + message_ = new QLabel(); + message_->setFixedHeight(24); + icon_ = new QLabel(); + icon_->setFixedHeight(24); + + message_layout->addWidget(icon_); + message_layout->addWidget(message_); + message_layout->addStretch(); + } + + // Network Waiting + waiting_bar_ = new QProgressBar(); + waiting_bar_->setVisible(false); + waiting_bar_->setRange(0, 0); + waiting_bar_->setFixedWidth(200); + message_layout->addWidget(waiting_bar_); + + auto* mainLayout = new QGridLayout; + + // 自动化调用界面布局 + if (automatic) { + mainLayout->addLayout(message_layout, 0, 0, 1, 3); + } else { + mainLayout->addWidget(search_label_, 1, 0); + mainLayout->addWidget(search_line_edit_, 1, 1); + mainLayout->addWidget(search_button_, 1, 2); + mainLayout->addWidget(key_server_label_, 2, 0); + mainLayout->addWidget(key_server_combo_box_, 2, 1); + mainLayout->addWidget(keys_table_, 3, 0, 1, 3); + mainLayout->addLayout(message_layout, 4, 0, 1, 3); + + // Layout for import and close button + auto* buttonsLayout = new QHBoxLayout; + buttonsLayout->addStretch(); + buttonsLayout->addWidget(import_button_); + buttonsLayout->addWidget(close_button_); + mainLayout->addLayout(buttonsLayout, 6, 0, 1, 3); + } + + this->setLayout(mainLayout); + if (automatic) + this->setWindowTitle(_("Update Keys from Keyserver")); + else + this->setWindowTitle(_("Import Keys from Keyserver")); + + if (automatic) { + this->setFixedSize(240, 42); + } + + this->setModal(true); + + connect(this, &KeyServerImportDialog::SignalKeyImported, + SignalStation::GetInstance(), + &SignalStation::SignalKeyDatabaseRefresh); +} + +KeyServerImportDialog::KeyServerImportDialog(QWidget* parent) + : GeneralDialog("key_server_import_dialog", parent), m_automatic_(true) { + setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint); + + // Network Waiting + waiting_bar_ = new QProgressBar(); + waiting_bar_->setVisible(false); + waiting_bar_->setRange(0, 0); + waiting_bar_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + waiting_bar_->setTextVisible(false); + + // Layout for messagebox + auto* layout = new QHBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + layout->addWidget(waiting_bar_); + + key_server_combo_box_ = create_comboBox(); + + this->setLayout(layout); + this->setWindowTitle(_("Update Keys from Keyserver")); + this->setFixedSize(240, 42); + this->setModal(true); +} + +QComboBox* KeyServerImportDialog::create_comboBox() { + auto* comboBox = new QComboBox; + comboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + try { + SettingsObject key_server_json("key_server"); + + const auto key_server_list = + key_server_json.Check("server_list", nlohmann::json::array()); + + for (const auto& key_server : key_server_list) { + const auto key_server_str = key_server.get<std::string>(); + comboBox->addItem(key_server_str.c_str()); + } + + int default_key_server_index = key_server_json.Check("default_server", 0); + if (default_key_server_index >= key_server_list.size()) { + throw std::runtime_error("default_server index out of range"); + } + std::string default_key_server = + key_server_list[default_key_server_index].get<std::string>(); + + comboBox->setCurrentText(default_key_server.c_str()); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << "server_list" + << "default_server"; + } + + return comboBox; +} + +void KeyServerImportDialog::create_keys_table() { + keys_table_ = new QTableWidget(); + keys_table_->setColumnCount(4); + + // always a whole row is marked + keys_table_->setSelectionBehavior(QAbstractItemView::SelectRows); + keys_table_->setEditTriggers(QAbstractItemView::NoEditTriggers); + + // Make just one row selectable + keys_table_->setSelectionMode(QAbstractItemView::SingleSelection); + + QStringList labels; + labels << _("UID") << _("Creation date") << _("KeyID") << _("Tag"); + keys_table_->horizontalHeader()->setSectionResizeMode( + 0, QHeaderView::ResizeToContents); + keys_table_->setHorizontalHeaderLabels(labels); + keys_table_->verticalHeader()->hide(); + + connect(keys_table_, &QTableWidget::cellActivated, this, + &KeyServerImportDialog::slot_import); +} + +void KeyServerImportDialog::set_message(const QString& text, bool error) { + if (m_automatic_) return; + + message_->setText(text); + if (error) { + icon_->setPixmap( + QPixmap(":error.png").scaled(QSize(24, 24), Qt::KeepAspectRatio)); + } else { + icon_->setPixmap( + QPixmap(":info.png").scaled(QSize(24, 24), Qt::KeepAspectRatio)); + } +} + +void KeyServerImportDialog::slot_search() { + if (search_line_edit_->text().isEmpty()) { + set_message("<h4>" + QString(_("Text is empty.")) + "</h4>", false); + return; + } + + auto* task = new KeyServerSearchTask( + key_server_combo_box_->currentText().toStdString(), + search_line_edit_->text().toStdString()); + + connect(task, &KeyServerSearchTask::SignalKeyServerSearchResult, this, + &KeyServerImportDialog::slot_search_finished); + + connect(task, &KeyServerSearchTask::SignalKeyServerSearchResult, this, [=]() { + this->search_button_->setDisabled(false); + this->key_server_combo_box_->setDisabled(false); + this->search_line_edit_->setReadOnly(false); + this->import_button_->setDisabled(false); + set_loading(false); + }); + + set_loading(true); + this->search_button_->setDisabled(true); + this->key_server_combo_box_->setDisabled(true); + this->search_line_edit_->setReadOnly(true); + this->import_button_->setDisabled(true); + + Thread::TaskRunnerGetter::GetInstance() + .GetTaskRunner(Thread::TaskRunnerGetter::kTaskRunnerType_Network) + ->PostTask(task); +} + +void KeyServerImportDialog::slot_search_finished( + QNetworkReply::NetworkError error, QByteArray buffer) { + LOG(INFO) << "Called" << error << buffer.size(); + LOG(INFO) << buffer.toStdString(); + + keys_table_->clearContents(); + keys_table_->setRowCount(0); + + auto stream = QTextStream(buffer); + + if (error != QNetworkReply::NoError) { + LOG(INFO) << "Error From Reply" << error; + + switch (error) { + case QNetworkReply::ContentNotFoundError: + set_message(_("Not Key Found"), true); + break; + case QNetworkReply::TimeoutError: + set_message(_("Timeout"), true); + break; + case QNetworkReply::HostNotFoundError: + set_message(_("Key Server Not Found"), true); + break; + default: + set_message(_("Connection Error"), true); + } + return; + } + + if (stream.readLine().contains("Error")) { + auto text = stream.readLine(1024); + + if (text.contains("Too many responses")) { + set_message( + "<h4>" + QString(_("Too many responses from keyserver!")) + "</h4>", + true); + return; + } else if (text.contains("No keys found")) { + // if string looks like hex string, search again with 0x prepended + QRegExp rx("[0-9A-Fa-f]*"); + QString query = search_line_edit_->text(); + if (rx.exactMatch(query)) { + set_message( + "<h4>" + + QString(_("No keys found, input may be kexId, retrying search " + "with 0x.")) + + "</h4>", + true); + search_line_edit_->setText(query.prepend("0x")); + this->slot_search(); + return; + } else { + set_message( + "<h4>" + QString(_("No keys found containing the search string!")) + + "</h4>", + true); + return; + } + } else if (text.contains("Insufficiently specific words")) { + set_message("<h4>" + + QString(_("Insufficiently specific search string!")) + + "</h4>", + true); + return; + } else { + set_message(text, true); + return; + } + } else { + int row = 0; + bool strikeout = false; + + // read lines until end of steam + while (!stream.atEnd()) { + QStringList line = + QString::fromUtf8(QByteArray::fromPercentEncoding( + stream.readLine().trimmed().toUtf8())) + .split(":"); + + // TODO: have a look at two following pub lines + if (line[0] == "pub") { + strikeout = false; + + QString flags = line[line.size() - 1]; + keys_table_->setRowCount(row + 1); + + // flags can be "d" for disabled, "r" for revoked + // or "e" for expired + if (flags.contains("r") or flags.contains("d") or flags.contains("e")) { + strikeout = true; + if (flags.contains("e")) { + keys_table_->setItem(row, 3, + new QTableWidgetItem(QString("expired"))); + } + if (flags.contains("r")) { + keys_table_->setItem(row, 3, + new QTableWidgetItem(QString(_("revoked")))); + } + if (flags.contains("d")) { + keys_table_->setItem(row, 3, + new QTableWidgetItem(QString(_("disabled")))); + } + } + + QStringList line2 = QString(QByteArray::fromPercentEncoding( + stream.readLine().trimmed().toUtf8())) + .split(":"); + + auto* uid = new QTableWidgetItem(); + if (line2.size() > 1) { + uid->setText(line2[1]); + keys_table_->setItem(row, 0, uid); + } + auto* creation_date = new QTableWidgetItem( + QDateTime::fromTime_t(line[4].toInt()).toString("dd. MMM. yyyy")); + keys_table_->setItem(row, 1, creation_date); + auto* keyid = new QTableWidgetItem(line[1]); + keys_table_->setItem(row, 2, keyid); + if (strikeout) { + QFont strike = uid->font(); + strike.setStrikeOut(true); + uid->setFont(strike); + creation_date->setFont(strike); + keyid->setFont(strike); + } + row++; + } else { + if (line[0] == "uid") { + QStringList l; + int height = keys_table_->rowHeight(row - 1); + keys_table_->setRowHeight(row - 1, height + 16); + QString tmp = keys_table_->item(row - 1, 0)->text(); + tmp.append(QString("\n") + line[1]); + auto* tmp1 = new QTableWidgetItem(tmp); + keys_table_->setItem(row - 1, 0, tmp1); + if (strikeout) { + QFont strike = tmp1->font(); + strike.setStrikeOut(true); + tmp1->setFont(strike); + } + } + } + set_message( + QString("<h4>") + + QString(_("%1 keys found. Double click a key to import it.")) + .arg(row) + + "</h4>", + false); + } + keys_table_->resizeColumnsToContents(); + import_button_->setDisabled(keys_table_->size().isEmpty()); + } +} + +void KeyServerImportDialog::slot_import() { + std::vector<std::string> key_ids; + const int row_count = keys_table_->rowCount(); + for (int i = 0; i < row_count; ++i) { + if (keys_table_->item(i, 2)->isSelected()) { + QString keyid = keys_table_->item(i, 2)->text(); + key_ids.push_back(keyid.toStdString()); + } + } + if (!key_ids.empty()) + SlotImport(key_ids, key_server_combo_box_->currentText().toStdString()); +} + +void KeyServerImportDialog::SlotImport(const KeyIdArgsListPtr& keys) { + // keyserver host url + std::string target_keyserver; + + if (key_server_combo_box_ != nullptr) { + target_keyserver = key_server_combo_box_->currentText().toStdString(); + } + if (target_keyserver.empty()) { + try { + SettingsObject key_server_json("key_server"); + const auto key_server_list = + key_server_json.Check("server_list", nlohmann::json::array()); + + int default_key_server_index = key_server_json.Check("default_server", 0); + if (default_key_server_index >= key_server_list.size()) { + throw std::runtime_error("default_server index out of range"); + } + std::string default_key_server = + key_server_list[default_key_server_index].get<std::string>(); + + target_keyserver = default_key_server; + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << "server_list" + << "default_server"; + QMessageBox::critical( + nullptr, _("Default Keyserver Not Found"), + _("Cannot read default keyserver from your settings, " + "please set a default keyserver first")); + return; + } + } + std::vector<std::string> key_ids; + for (const auto& key_id : *keys) { + key_ids.push_back(key_id); + } + SlotImport(key_ids, target_keyserver); +} + +void KeyServerImportDialog::SlotImport(std::vector<std::string> key_ids, + std::string keyserver_url) { + auto* task = new KeyServerImportTask(keyserver_url, key_ids); + + connect(task, &KeyServerImportTask::SignalKeyServerImportResult, this, + &KeyServerImportDialog::slot_import_finished); + + Thread::TaskRunnerGetter::GetInstance() + .GetTaskRunner(Thread::TaskRunnerGetter::kTaskRunnerType_Network) + ->PostTask(task); +} + +void KeyServerImportDialog::slot_import_finished( + QNetworkReply::NetworkError error, QByteArray buffer) { + LOG(INFO) << _("Called"); + + if (error != QNetworkReply::NoError) { + LOG(ERROR) << "Error From Reply" << buffer.toStdString(); + if (!m_automatic_) { + switch (error) { + case QNetworkReply::ContentNotFoundError: + set_message(_("Key Not Found"), true); + break; + case QNetworkReply::TimeoutError: + set_message(_("Timeout"), true); + break; + case QNetworkReply::HostNotFoundError: + set_message(_("Key Server Not Found"), true); + break; + default: + set_message(_("Connection Error"), true); + } + } else { + switch (error) { + case QNetworkReply::ContentNotFoundError: + QMessageBox::critical(nullptr, _("Key Not Found"), + QString(_("key not found in the Keyserver"))); + break; + case QNetworkReply::TimeoutError: + QMessageBox::critical(nullptr, _("Timeout"), "Connection timeout"); + break; + case QNetworkReply::HostNotFoundError: + QMessageBox::critical(nullptr, _("Host Not Found"), + "cannot resolve the default Keyserver"); + break; + default: + QMessageBox::critical(nullptr, _("Connection Error"), + _("General Connection Error")); + } + } + if (m_automatic_) { + setWindowFlags(Qt::Window | Qt::WindowTitleHint | + Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint); + } + return; + } + + this->import_keys( + std::make_unique<ByteArray>(buffer.constData(), buffer.length())); + + if (!m_automatic_) { + set_message(QString("<h4>") + _("Key Imported") + "</h4>", false); + } +} + +void KeyServerImportDialog::import_keys(ByteArrayPtr in_data) { + GpgImportInformation result = + GpgKeyImportExporter::GetInstance().ImportKey(std::move(in_data)); + + // refresh the key database + emit SignalKeyImported(); + + QWidget* _parent = qobject_cast<QWidget*>(parent()); + if (m_automatic_) { + auto dialog = new KeyImportDetailDialog(result, true, _parent); + dialog->show(); + this->accept(); + } else { + auto dialog = new KeyImportDetailDialog(result, false, this); + dialog->exec(); + } +} + +void KeyServerImportDialog::set_loading(bool status) { + waiting_bar_->setVisible(status); + if (!m_automatic_) { + icon_->setVisible(!status); + message_->setVisible(!status); + } +} + +} // namespace GpgFrontend::UI diff --git a/src/ui/dialog/import_export/KeyServerImportDialog.h b/src/ui/dialog/import_export/KeyServerImportDialog.h new file mode 100644 index 00000000..fd912bdd --- /dev/null +++ b/src/ui/dialog/import_export/KeyServerImportDialog.h @@ -0,0 +1,182 @@ +/** + * 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 __KEY_SERVER_IMPORT_DIALOG_H__ +#define __KEY_SERVER_IMPORT_DIALOG_H__ + +#include <string> + +#include "KeyImportDetailDialog.h" +#include "core/GpgContext.h" +#include "ui/GpgFrontendUI.h" +#include "ui/dialog/GeneralDialog.h" +#include "ui/widgets/KeyList.h" + +namespace GpgFrontend::UI { + +/** + * @brief + * + */ +class KeyServerImportDialog : public GeneralDialog { + Q_OBJECT + + public: + /** + * @brief Construct a new Key Server Import Dialog object + * + * @param automatic + * @param parent + */ + KeyServerImportDialog(bool automatic, QWidget* parent); + + /** + * @brief Construct a new Key Server Import Dialog object + * + * @param parent + */ + explicit KeyServerImportDialog(QWidget* parent); + + public slots: + + /** + * @brief + * + * @param keys + */ + void SlotImport(const KeyIdArgsListPtr& keys); + + /** + * @brief + * + * @param keyIds + * @param keyserverUrl + */ + void SlotImport(std::vector<std::string> key_ids_list, + std::string keyserver_url); + + signals: + + /** + * @brief + * + */ + void SignalKeyImported(); + + private slots: + + /** + * @brief import key(s) for the key table selection + * + */ + void slot_import(); + + /** + * @brief + * + */ + void slot_search_finished(QNetworkReply::NetworkError reply, + QByteArray buffer); + + /** + * @brief + * + * @param keyid + */ + void slot_import_finished(QNetworkReply::NetworkError error, + QByteArray buffer); + + /** + * @brief + * + */ + void slot_search(); + + private: + /** + * @brief Create a keys table object + * + */ + void create_keys_table(); + + /** + * @brief Set the message object + * + * @param text + * @param error + */ + void set_message(const QString& text, bool error); + + /** + * @brief + * + * @param in_data + */ + void import_keys(ByteArrayPtr in_data); + + /** + * @brief Set the loading object + * + * @param status + */ + void set_loading(bool status); + + /** + * @brief Create a button object + * + * @param text + * @param member + * @return QPushButton* + */ + QPushButton* create_button(const QString& text, const char* member); + + /** + * @brief Create a comboBox object + * + * @return QComboBox* + */ + QComboBox* create_comboBox(); + + bool m_automatic_ = false; ///< + + QLineEdit* search_line_edit_{}; ///< + QComboBox* key_server_combo_box_{}; ///< + QProgressBar* waiting_bar_; ///< + QLabel* search_label_{}; ///< + QLabel* key_server_label_{}; ///< + QLabel* message_{}; ///< + QLabel* icon_{}; ///< + QPushButton* close_button_{}; ///< + QPushButton* import_button_{}; ///< + QPushButton* search_button_{}; ///< + QTableWidget* keys_table_{}; ///< +}; + +} // namespace GpgFrontend::UI + +#endif // __KEY_SERVER_IMPORT_DIALOG_H__ diff --git a/src/ui/dialog/import_export/KeyUploadDialog.cpp b/src/ui/dialog/import_export/KeyUploadDialog.cpp new file mode 100644 index 00000000..055f2e1f --- /dev/null +++ b/src/ui/dialog/import_export/KeyUploadDialog.cpp @@ -0,0 +1,159 @@ +/** + * 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 "KeyUploadDialog.h" + +#include <algorithm> + +#include "core/function/GlobalSettingStation.h" +#include "core/function/gpg/GpgKeyGetter.h" +#include "core/function/gpg/GpgKeyImportExporter.h" + +namespace GpgFrontend::UI { + +KeyUploadDialog::KeyUploadDialog(const KeyIdArgsListPtr& keys_ids, + QWidget* parent) + : GeneralDialog(typeid(KeyUploadDialog).name(), parent), + m_keys_(GpgKeyGetter::GetInstance().GetKeys(keys_ids)) { + auto* pb = new QProgressBar(); + pb->setRange(0, 0); + pb->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + pb->setTextVisible(false); + + auto* layout = new QVBoxLayout(); + layout->addWidget(pb); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + this->setLayout(layout); + + this->setModal(true); + this->setWindowTitle(_("Uploading Public Key")); + this->setFixedSize(240, 42); +} + +void KeyUploadDialog::SlotUpload() { + auto out_data = std::make_unique<ByteArray>(); + GpgKeyImportExporter::GetInstance().ExportKeys(*m_keys_, out_data); + slot_upload_key_to_server(*out_data); +} + +void KeyUploadDialog::slot_upload_key_to_server( + const GpgFrontend::ByteArray& keys_data) { + std::string target_keyserver; + if (target_keyserver.empty()) { + try { + auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); + + target_keyserver = settings.lookup("keyserver.default_server").c_str(); + + LOG(INFO) << _("Set target Key Server to default Key Server") + << target_keyserver; + } catch (...) { + LOG(ERROR) << _("Cannot read default_keyserver From Settings"); + QMessageBox::critical( + nullptr, _("Default Keyserver Not Found"), + _("Cannot read default keyserver from your settings, " + "please set a default keyserver first")); + return; + } + } + + QUrl req_url(QString::fromStdString(target_keyserver + "/pks/add")); + auto qnam = new QNetworkAccessManager(this); + + // Building Post Data + QByteArray postData; + + auto data = std::string(keys_data); + + boost::algorithm::replace_all(data, "\n", "%0A"); + boost::algorithm::replace_all(data, "\r", "%0D"); + boost::algorithm::replace_all(data, "(", "%28"); + boost::algorithm::replace_all(data, ")", "%29"); + boost::algorithm::replace_all(data, "/", "%2F"); + boost::algorithm::replace_all(data, ":", "%3A"); + boost::algorithm::replace_all(data, "+", "%2B"); + boost::algorithm::replace_all(data, "=", "%3D"); + boost::algorithm::replace_all(data, " ", "+"); + + QNetworkRequest request(req_url); + request.setHeader(QNetworkRequest::ContentTypeHeader, + "application/x-www-form-urlencoded"); + + postData.append("keytext").append("=").append( + QString::fromStdString(data).toUtf8()); + + // Send Post Data + QNetworkReply* reply = qnam->post(request, postData); + connect(reply, &QNetworkReply::finished, this, + &KeyUploadDialog::slot_upload_finished); + + // Keep Waiting + while (reply->isRunning()) { + QApplication::processEvents(); + } + + // Done + this->hide(); + this->close(); +} + +void KeyUploadDialog::slot_upload_finished() { + auto* reply = qobject_cast<QNetworkReply*>(sender()); + + QByteArray response = reply->readAll(); + LOG(INFO) << "Response: " << response.toStdString(); + + auto error = reply->error(); + if (error != QNetworkReply::NoError) { + LOG(INFO) << "Error From Reply" << reply->errorString().toStdString(); + QString message; + switch (error) { + case QNetworkReply::ContentNotFoundError: + message = _("Key Not Found"); + break; + case QNetworkReply::TimeoutError: + message = _("Timeout"); + break; + case QNetworkReply::HostNotFoundError: + message = _("Key Server Not Found"); + break; + default: + message = _("Connection Error"); + } + QMessageBox::critical(this, "Upload Failed", message); + return; + } else { + QMessageBox::information(this, _("Upload Success"), + _("Upload Public Key Successfully")); + LOG(INFO) << "Success while contacting keyserver!"; + } + reply->deleteLater(); +} + +} // namespace GpgFrontend::UI diff --git a/src/ui/dialog/import_export/KeyUploadDialog.h b/src/ui/dialog/import_export/KeyUploadDialog.h new file mode 100644 index 00000000..d621f33a --- /dev/null +++ b/src/ui/dialog/import_export/KeyUploadDialog.h @@ -0,0 +1,83 @@ +/** + * 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_KEYUPLOADWIDGET_H +#define GPGFRONTEND_KEYUPLOADWIDGET_H + +#include "core/GpgContext.h" +#include "ui/GpgFrontendUI.h" +#include "ui/dialog/GeneralDialog.h" + +namespace GpgFrontend::UI { + +/** + * @brief + * + */ +class KeyUploadDialog : public GeneralDialog { + Q_OBJECT + public: + /** + * @brief Construct a new Key Upload Dialog object + * + * @param keys_ids + * @param parent + */ + explicit KeyUploadDialog(const KeyIdArgsListPtr& keys_ids, QWidget* parent); + + public slots: + + /** + * @brief + * + */ + void SlotUpload(); + + private slots: + + /** + * @brief + * + * @param keys_data + */ + void slot_upload_key_to_server(const GpgFrontend::ByteArray& keys_data); + + /** + * @brief + * + */ + void slot_upload_finished(); + + private: + KeyListPtr m_keys_; ///< + QByteArray m_key_data_; ///< +}; + +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_KEYUPLOADWIDGET_H diff --git a/src/ui/dialog/key_generate/KeygenDialog.cpp b/src/ui/dialog/key_generate/KeygenDialog.cpp new file mode 100644 index 00000000..42160ec9 --- /dev/null +++ b/src/ui/dialog/key_generate/KeygenDialog.cpp @@ -0,0 +1,407 @@ +/** + * 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 "KeygenDialog.h" + +#include "core/function/GlobalSettingStation.h" +#include "core/function/gpg/GpgKeyOpera.h" +#include "dialog/WaitingDialog.h" +#include "ui/SignalStation.h" + +namespace GpgFrontend::UI { + +KeyGenDialog::KeyGenDialog(QWidget* parent) + : GeneralDialog(typeid(KeyGenDialog).name(), parent) { + button_box_ = + new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); + + // max expire date time + 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"); + } + + max_date_time_ = longer_expiration_date + ? QDateTime::currentDateTime().toLocalTime().addYears(30) + : QDateTime::currentDateTime().toLocalTime().addYears(2); + + this->setWindowTitle(_("Generate Key")); + this->setModal(true); + + connect(this, &KeyGenDialog::SignalKeyGenerated, SignalStation::GetInstance(), + &SignalStation::SignalKeyDatabaseRefresh); + + generate_key_dialog(); +} + +void KeyGenDialog::generate_key_dialog() { + key_usage_group_box_ = create_key_usage_group_box(); + + auto* groupGrid = new QGridLayout(this); + groupGrid->addWidget(create_basic_info_group_box(), 0, 0); + groupGrid->addWidget(key_usage_group_box_, 1, 0); + + auto* nameList = new QWidget(this); + nameList->setLayout(groupGrid); + + auto* vbox2 = new QVBoxLayout(); + vbox2->addWidget(nameList); + vbox2->addWidget(error_label_); + vbox2->addWidget(button_box_); + + this->setLayout(vbox2); + + set_signal_slot(); + + refresh_widgets_state(); +} + +void KeyGenDialog::slot_key_gen_accept() { + std::stringstream error_stream; + + /** + * check for errors in keygen dialog input + */ + if ((name_edit_->text()).size() < 5) { + error_stream << " " << _("Name must contain at least five characters.") + << std::endl; + } + if (email_edit_->text().isEmpty() || + !check_email_address(email_edit_->text())) { + error_stream << " " << _("Please give a email address.") << std::endl; + } + + /** + * primary keys should have a reasonable expiration date (no more than 2 years + * in the future) + */ + if (date_edit_->dateTime() > max_date_time_) { + error_stream << " " << _("Expiration time too long.") << std::endl; + } + + auto err_string = error_stream.str(); + + if (err_string.empty()) { + /** + * create the string for key generation + */ + gen_key_info_->SetName(name_edit_->text().toStdString()); + gen_key_info_->SetEmail(email_edit_->text().toStdString()); + gen_key_info_->SetComment(comment_edit_->text().toStdString()); + + gen_key_info_->SetKeyLength(key_size_spin_box_->value()); + + if (expire_check_box_->checkState()) { + gen_key_info_->SetNonExpired(true); + } else { + gen_key_info_->SetExpireTime( + boost::posix_time::from_time_t(date_edit_->dateTime().toTime_t())); + } + + GpgGenKeyResult result; + gpgme_error_t error = false; + auto thread = QThread::create([&]() { + error = GpgKeyOpera::GetInstance().GenerateKey(gen_key_info_, result); + }); + thread->start(); + + auto* dialog = new WaitingDialog(_("Generating"), this); + dialog->show(); + + while (thread->isRunning()) { + QCoreApplication::processEvents(); + } + + dialog->close(); + + LOG(INFO) << "generate done"; + + if (gpgme_err_code(error) == 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 new key pair has been generated.")); + msg_box->setModal(true); + msg_box->open(); + + LOG(INFO) << "generate success"; + + emit SignalKeyGenerated(); + this->close(); + } else { + QMessageBox::critical(this, _("Failure"), _("Key generation failed.")); + } + + } 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(err_string.c_str()); + + this->show(); + } +} + +void KeyGenDialog::slot_expire_box_changed() { + if (expire_check_box_->checkState()) { + date_edit_->setEnabled(false); + } else { + date_edit_->setEnabled(true); + } +} + +QGroupBox* KeyGenDialog::create_key_usage_group_box() { + auto* groupBox = new QGroupBox(this); + auto* grid = new QGridLayout(this); + + groupBox->setTitle(_("Key Usage")); + + auto* encrypt = new QCheckBox(_("Encryption"), groupBox); + encrypt->setTristate(false); + + auto* sign = new QCheckBox(_("Signing"), groupBox); + sign->setTristate(false); + + auto* cert = new QCheckBox(_("Certification"), groupBox); + cert->setTristate(false); + + auto* auth = new QCheckBox(_("Authentication"), groupBox); + auth->setTristate(false); + + key_usage_check_boxes_.push_back(encrypt); + key_usage_check_boxes_.push_back(sign); + key_usage_check_boxes_.push_back(cert); + key_usage_check_boxes_.push_back(auth); + + grid->addWidget(encrypt, 0, 0); + grid->addWidget(sign, 0, 1); + grid->addWidget(cert, 1, 0); + grid->addWidget(auth, 1, 1); + + groupBox->setLayout(grid); + + return groupBox; +} + +void KeyGenDialog::slot_encryption_box_changed(int state) { + if (state == 0) { + gen_key_info_->SetAllowEncryption(false); + } else { + gen_key_info_->SetAllowEncryption(true); + } +} + +void KeyGenDialog::slot_signing_box_changed(int state) { + if (state == 0) { + gen_key_info_->SetAllowSigning(false); + } else { + gen_key_info_->SetAllowSigning(true); + } +} + +void KeyGenDialog::slot_certification_box_changed(int state) { + if (state == 0) { + gen_key_info_->SetAllowCertification(false); + } else { + gen_key_info_->SetAllowCertification(true); + } +} + +void KeyGenDialog::slot_authentication_box_changed(int state) { + if (state == 0) { + gen_key_info_->SetAllowAuthentication(false); + } else { + gen_key_info_->SetAllowAuthentication(true); + } +} + +void KeyGenDialog::slot_activated_key_type(int index) { + qDebug() << "key type index changed " << index; + + gen_key_info_->SetAlgo( + this->key_type_combo_box_->itemText(index).toStdString()); + refresh_widgets_state(); +} + +void KeyGenDialog::refresh_widgets_state() { + qDebug() << "refresh_widgets_state called"; + + if (gen_key_info_->IsAllowEncryption()) + key_usage_check_boxes_[0]->setCheckState(Qt::CheckState::Checked); + else + key_usage_check_boxes_[0]->setCheckState(Qt::CheckState::Unchecked); + + if (gen_key_info_->IsAllowChangeEncryption()) + key_usage_check_boxes_[0]->setDisabled(false); + else + key_usage_check_boxes_[0]->setDisabled(true); + + if (gen_key_info_->IsAllowSigning()) + key_usage_check_boxes_[1]->setCheckState(Qt::CheckState::Checked); + else + key_usage_check_boxes_[1]->setCheckState(Qt::CheckState::Unchecked); + + if (gen_key_info_->IsAllowChangeSigning()) + key_usage_check_boxes_[1]->setDisabled(false); + else + key_usage_check_boxes_[1]->setDisabled(true); + + if (gen_key_info_->IsAllowCertification()) + key_usage_check_boxes_[2]->setCheckState(Qt::CheckState::Checked); + else + key_usage_check_boxes_[2]->setCheckState(Qt::CheckState::Unchecked); + + if (gen_key_info_->IsAllowChangeCertification()) + key_usage_check_boxes_[2]->setDisabled(false); + else + key_usage_check_boxes_[2]->setDisabled(true); + + if (gen_key_info_->IsAllowAuthentication()) + key_usage_check_boxes_[3]->setCheckState(Qt::CheckState::Checked); + else + key_usage_check_boxes_[3]->setCheckState(Qt::CheckState::Unchecked); + + if (gen_key_info_->IsAllowChangeAuthentication()) + key_usage_check_boxes_[3]->setDisabled(false); + else + key_usage_check_boxes_[3]->setDisabled(true); + + if (gen_key_info_->IsAllowNoPassPhrase()) + no_pass_phrase_check_box_->setDisabled(false); + else + no_pass_phrase_check_box_->setDisabled(true); + + key_size_spin_box_->setRange(gen_key_info_->GetSuggestMinKeySize(), + gen_key_info_->GetSuggestMaxKeySize()); + key_size_spin_box_->setValue(gen_key_info_->GetKeyLength()); + key_size_spin_box_->setSingleStep(gen_key_info_->GetSizeChangeStep()); +} + +void KeyGenDialog::set_signal_slot() { + connect(button_box_, &QDialogButtonBox::accepted, this, + &KeyGenDialog::slot_key_gen_accept); + connect(button_box_, &QDialogButtonBox::rejected, this, + &KeyGenDialog::reject); + + connect(expire_check_box_, &QCheckBox::stateChanged, this, + &KeyGenDialog::slot_expire_box_changed); + + connect(key_usage_check_boxes_[0], &QCheckBox::stateChanged, this, + &KeyGenDialog::slot_encryption_box_changed); + connect(key_usage_check_boxes_[1], &QCheckBox::stateChanged, this, + &KeyGenDialog::slot_signing_box_changed); + connect(key_usage_check_boxes_[2], &QCheckBox::stateChanged, this, + &KeyGenDialog::slot_certification_box_changed); + connect(key_usage_check_boxes_[3], &QCheckBox::stateChanged, this, + &KeyGenDialog::slot_authentication_box_changed); + + connect(key_type_combo_box_, qOverload<int>(&QComboBox::currentIndexChanged), + this, &KeyGenDialog::slot_activated_key_type); + + connect(no_pass_phrase_check_box_, &QCheckBox::stateChanged, this, + [this](int state) -> void { + if (state == 0) { + gen_key_info_->SetNonPassPhrase(false); + } else { + gen_key_info_->SetNonPassPhrase(true); + } + }); +} + +bool KeyGenDialog::check_email_address(const QString& str) { + return re_email_.match(str).hasMatch(); +} + +QGroupBox* KeyGenDialog::create_basic_info_group_box() { + error_label_ = new QLabel(); + name_edit_ = new QLineEdit(this); + email_edit_ = new QLineEdit(this); + comment_edit_ = new QLineEdit(this); + key_size_spin_box_ = new QSpinBox(this); + key_type_combo_box_ = new QComboBox(this); + + for (auto& algo : GenKeyInfo::GetSupportedKeyAlgo()) { + key_type_combo_box_->addItem(QString::fromStdString(algo)); + } + if (!GenKeyInfo::GetSupportedKeyAlgo().empty()) { + key_type_combo_box_->setCurrentIndex(0); + } + + date_edit_ = + new QDateTimeEdit(QDateTime::currentDateTime().addYears(2), this); + date_edit_->setMinimumDateTime(QDateTime::currentDateTime()); + date_edit_->setMaximumDateTime(max_date_time_); + date_edit_->setDisplayFormat("dd/MM/yyyy hh:mm:ss"); + date_edit_->setCalendarPopup(true); + date_edit_->setEnabled(true); + + expire_check_box_ = new QCheckBox(this); + expire_check_box_->setCheckState(Qt::Unchecked); + + no_pass_phrase_check_box_ = new QCheckBox(this); + no_pass_phrase_check_box_->setCheckState(Qt::Unchecked); + + auto* vbox1 = new QGridLayout; + + vbox1->addWidget(new QLabel(QString(_("Name")) + ": "), 0, 0); + vbox1->addWidget(new QLabel(QString(_("Email Address")) + ": "), 1, 0); + vbox1->addWidget(new QLabel(QString(_("Comment")) + ": "), 2, 0); + vbox1->addWidget(new QLabel(QString(_("Expiration Date")) + ": "), 3, 0); + vbox1->addWidget(new QLabel(QString(_("Never Expire")) + ": "), 3, 3); + vbox1->addWidget(new QLabel(QString(_("KeySize (in Bit)")) + ": "), 4, 0); + vbox1->addWidget(new QLabel(QString(_("Key Type")) + ": "), 5, 0); + vbox1->addWidget(new QLabel(QString(_("Non Pass Phrase")) + ": "), 6, 0); + + vbox1->addWidget(name_edit_, 0, 1, 1, 3); + vbox1->addWidget(email_edit_, 1, 1, 1, 3); + vbox1->addWidget(comment_edit_, 2, 1, 1, 3); + vbox1->addWidget(date_edit_, 3, 1); + vbox1->addWidget(expire_check_box_, 3, 2); + vbox1->addWidget(key_size_spin_box_, 4, 1); + vbox1->addWidget(key_type_combo_box_, 5, 1); + vbox1->addWidget(no_pass_phrase_check_box_, 6, 1); + + auto basicInfoGroupBox = new QGroupBox(); + basicInfoGroupBox->setLayout(vbox1); + basicInfoGroupBox->setTitle(_("Basic Information")); + + return basicInfoGroupBox; +} + +} // namespace GpgFrontend::UI diff --git a/src/ui/dialog/key_generate/KeygenDialog.h b/src/ui/dialog/key_generate/KeygenDialog.h new file mode 100644 index 00000000..baf10dbf --- /dev/null +++ b/src/ui/dialog/key_generate/KeygenDialog.h @@ -0,0 +1,184 @@ +/** + * 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 __KEYGENDIALOG_H__ +#define __KEYGENDIALOG_H__ + +#include "core/GpgContext.h" +#include "core/GpgGenKeyInfo.h" +#include "ui/GpgFrontendUI.h" +#include "ui/dialog/GeneralDialog.h" + +namespace GpgFrontend::UI { + +/** + * @brief + * + */ +class KeyGenDialog : public GeneralDialog { + Q_OBJECT + + public: + /** + * @details Constructor of this class + * + * @param ctx The current GpgME context + * @param key The key to show details of + * @param parent The parent of this widget + */ + explicit KeyGenDialog(QWidget* parent = nullptr); + + signals: + /** + * @brief + * + */ + void SignalKeyGenerated(); + + private: + /** + * @brief Create a key usage group box object + * + * @return QGroupBox* + */ + QGroupBox* create_key_usage_group_box(); + + /** + * @brief Create a basic info group box object + * + * @return QGroupBox* + */ + QGroupBox* create_basic_info_group_box(); + + /** + * @brief + * + */ + 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 + * + */ + QStringList error_messages_; ///< List of errors occurring when checking + ///< entries of line edits + std::unique_ptr<GenKeyInfo> gen_key_info_ = + std::make_unique<GenKeyInfo>(); ///< + QDialogButtonBox* button_box_; ///< Box for standard buttons + QLabel* error_label_{}; ///< Label containing error message + QLineEdit* name_edit_{}; ///< Line edit for the keys name + QLineEdit* email_edit_{}; ///< Line edit for the keys email + QLineEdit* comment_edit_{}; ///< Line edit for the keys comment + QSpinBox* key_size_spin_box_{}; ///< Spinbox for the keys size (in bit) + QComboBox* key_type_combo_box_{}; ///< Combobox for Key type + QDateTimeEdit* date_edit_{}; ///< Date edit for expiration date + QCheckBox* expire_check_box_{}; ///< Checkbox, if key should expire + QCheckBox* no_pass_phrase_check_box_{}; + QGroupBox* key_usage_group_box_{}; ///< Group of Widgets detecting the usage + ///< of the Key + QDateTime max_date_time_; ///< + std::vector<QCheckBox*> key_usage_check_boxes_; ///< ENCR, SIGN, CERT, AUTH + + /** + * @brief + * + */ + void generate_key_dialog(); + + /** + * @details Refresh widgets state by GenKeyInfo + */ + void refresh_widgets_state(); + + /** + * @brief Set the signal slot object + * + */ + void set_signal_slot(); + + /** + * @brief + * + * @param str + * @return true + * @return false + */ + bool check_email_address(const QString& str); + + private slots: + + /** + * @details when expirebox was checked/unchecked, enable/disable the + * expiration date box + */ + void slot_expire_box_changed(); + + /** + * @details check all lineedits for false entries. Show error, when there is + * one, otherwise generate the key + */ + void slot_key_gen_accept(); + + /** + * @brief + * + * @param state + */ + void slot_encryption_box_changed(int state); + + /** + * @brief + * + * @param state + */ + void slot_signing_box_changed(int state); + + /** + * @brief + * + * @param state + */ + void slot_certification_box_changed(int state); + + /** + * @brief + * + * @param state + */ + void slot_authentication_box_changed(int state); + + /** + * @brief + * + * @param index + */ + void slot_activated_key_type(int index); +}; + +} // namespace GpgFrontend::UI + +#endif // __KEYGENDIALOG_H__ diff --git a/src/ui/dialog/key_generate/SubkeyGenerateDialog.cpp b/src/ui/dialog/key_generate/SubkeyGenerateDialog.cpp new file mode 100644 index 00000000..806c0e50 --- /dev/null +++ b/src/ui/dialog/key_generate/SubkeyGenerateDialog.cpp @@ -0,0 +1,344 @@ +/** + * 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 "SubkeyGenerateDialog.h" + +#include "core/function/GlobalSettingStation.h" +#include "core/function/gpg/GpgKeyGetter.h" +#include "core/function/gpg/GpgKeyOpera.h" +#include "dialog/WaitingDialog.h" +#include "ui/SignalStation.h" + +namespace GpgFrontend::UI { + +SubkeyGenerateDialog::SubkeyGenerateDialog(const KeyId& key_id, QWidget* parent) + : GeneralDialog(typeid(SubkeyGenerateDialog).name(), parent), + key_(GpgKeyGetter::GetInstance().GetKey(key_id)) { + auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); + + // max expire date time + 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"); + } + + max_date_time_ = longer_expiration_date + ? QDateTime::currentDateTime().toLocalTime().addYears(30) + : QDateTime::currentDateTime().toLocalTime().addYears(2); + + button_box_ = + new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + key_usage_group_box_ = create_key_usage_group_box(); + + auto* groupGrid = new QGridLayout(this); + groupGrid->addWidget(create_basic_info_group_box(), 0, 0); + groupGrid->addWidget(key_usage_group_box_, 1, 0); + + auto* nameList = new QWidget(this); + nameList->setLayout(groupGrid); + + auto* vbox2 = new QVBoxLayout(); + vbox2->addWidget(nameList); + vbox2->addWidget(error_label_); + vbox2->addWidget(button_box_); + + this->setWindowTitle(_("Generate New Subkey")); + this->setLayout(vbox2); + this->setModal(true); + + connect(this, &SubkeyGenerateDialog::SignalSubKeyGenerated, + SignalStation::GetInstance(), + &SignalStation::SignalKeyDatabaseRefresh); + + set_signal_slot(); + refresh_widgets_state(); +} + +QGroupBox* SubkeyGenerateDialog::create_key_usage_group_box() { + auto* groupBox = new QGroupBox(this); + auto* grid = new QGridLayout(this); + + groupBox->setTitle(_("Key Usage")); + + auto* encrypt = new QCheckBox(_("Encryption"), groupBox); + encrypt->setTristate(false); + + auto* sign = new QCheckBox(_("Signing"), groupBox); + sign->setTristate(false); + + auto* cert = new QCheckBox(_("Certification"), groupBox); + cert->setTristate(false); + + auto* auth = new QCheckBox(_("Authentication"), groupBox); + auth->setTristate(false); + + key_usage_check_boxes_.push_back(encrypt); + key_usage_check_boxes_.push_back(sign); + key_usage_check_boxes_.push_back(cert); + key_usage_check_boxes_.push_back(auth); + + grid->addWidget(encrypt, 0, 0); + grid->addWidget(sign, 0, 1); + grid->addWidget(cert, 1, 0); + grid->addWidget(auth, 1, 1); + + groupBox->setLayout(grid); + + return groupBox; +} + +QGroupBox* SubkeyGenerateDialog::create_basic_info_group_box() { + error_label_ = new QLabel(); + key_size_spin_box_ = new QSpinBox(this); + key_type_combo_box_ = new QComboBox(this); + + for (auto& algo : GenKeyInfo::GetSupportedKeyAlgo()) { + key_type_combo_box_->addItem(QString::fromStdString(algo)); + } + if (!GenKeyInfo::GetSupportedKeyAlgo().empty()) { + key_type_combo_box_->setCurrentIndex(0); + } + + date_edit_ = + new QDateTimeEdit(QDateTime::currentDateTime().addYears(2), this); + date_edit_->setMinimumDateTime(QDateTime::currentDateTime()); + date_edit_->setMaximumDateTime(max_date_time_); + date_edit_->setDisplayFormat("dd/MM/yyyy hh:mm:ss"); + date_edit_->setCalendarPopup(true); + date_edit_->setEnabled(true); + + expire_check_box_ = new QCheckBox(this); + expire_check_box_->setCheckState(Qt::Unchecked); + + auto* vbox1 = new QGridLayout; + + vbox1->addWidget(new QLabel(QString(_("Expiration Date")) + ": "), 2, 0); + vbox1->addWidget(new QLabel(QString(_("Never Expire")) + ": "), 2, 3); + vbox1->addWidget(new QLabel(QString(_("KeySize (in Bit)")) + ": "), 1, 0); + vbox1->addWidget(new QLabel(QString(_("Key Type")) + ": "), 0, 0); + + vbox1->addWidget(date_edit_, 2, 1); + vbox1->addWidget(expire_check_box_, 2, 2); + vbox1->addWidget(key_size_spin_box_, 1, 1); + vbox1->addWidget(key_type_combo_box_, 0, 1); + + auto basicInfoGroupBox = new QGroupBox(); + basicInfoGroupBox->setLayout(vbox1); + basicInfoGroupBox->setTitle(_("Basic Information")); + + return basicInfoGroupBox; +} + +void SubkeyGenerateDialog::set_signal_slot() { + connect(button_box_, &QDialogButtonBox::accepted, this, + &SubkeyGenerateDialog::slot_key_gen_accept); + connect(button_box_, &QDialogButtonBox::rejected, this, + &SubkeyGenerateDialog::reject); + + connect(expire_check_box_, &QCheckBox::stateChanged, this, + &SubkeyGenerateDialog::slot_expire_box_changed); + + connect(key_usage_check_boxes_[0], &QCheckBox::stateChanged, this, + &SubkeyGenerateDialog::slot_encryption_box_changed); + connect(key_usage_check_boxes_[1], &QCheckBox::stateChanged, this, + &SubkeyGenerateDialog::slot_signing_box_changed); + connect(key_usage_check_boxes_[2], &QCheckBox::stateChanged, this, + &SubkeyGenerateDialog::slot_certification_box_changed); + connect(key_usage_check_boxes_[3], &QCheckBox::stateChanged, this, + &SubkeyGenerateDialog::slot_authentication_box_changed); + + connect(key_type_combo_box_, qOverload<int>(&QComboBox::currentIndexChanged), + this, &SubkeyGenerateDialog::slot_activated_key_type); +} + +void SubkeyGenerateDialog::slot_expire_box_changed() { + if (expire_check_box_->checkState()) { + date_edit_->setEnabled(false); + } else { + date_edit_->setEnabled(true); + } +} + +void SubkeyGenerateDialog::refresh_widgets_state() { + qDebug() << "refresh_widgets_state called"; + + if (gen_key_info_->IsAllowEncryption()) + key_usage_check_boxes_[0]->setCheckState(Qt::CheckState::Checked); + else + key_usage_check_boxes_[0]->setCheckState(Qt::CheckState::Unchecked); + + if (gen_key_info_->IsAllowChangeEncryption()) + key_usage_check_boxes_[0]->setDisabled(false); + else + key_usage_check_boxes_[0]->setDisabled(true); + + if (gen_key_info_->IsAllowSigning()) + key_usage_check_boxes_[1]->setCheckState(Qt::CheckState::Checked); + else + key_usage_check_boxes_[1]->setCheckState(Qt::CheckState::Unchecked); + + if (gen_key_info_->IsAllowChangeSigning()) + key_usage_check_boxes_[1]->setDisabled(false); + else + key_usage_check_boxes_[1]->setDisabled(true); + + if (gen_key_info_->IsAllowCertification()) + key_usage_check_boxes_[2]->setCheckState(Qt::CheckState::Checked); + else + key_usage_check_boxes_[2]->setCheckState(Qt::CheckState::Unchecked); + + if (gen_key_info_->IsAllowChangeCertification()) + key_usage_check_boxes_[2]->setDisabled(false); + else + key_usage_check_boxes_[2]->setDisabled(true); + + if (gen_key_info_->IsAllowAuthentication()) + key_usage_check_boxes_[3]->setCheckState(Qt::CheckState::Checked); + else + key_usage_check_boxes_[3]->setCheckState(Qt::CheckState::Unchecked); + + if (gen_key_info_->IsAllowChangeAuthentication()) + key_usage_check_boxes_[3]->setDisabled(false); + else + key_usage_check_boxes_[3]->setDisabled(true); + + key_size_spin_box_->setRange(gen_key_info_->GetSuggestMinKeySize(), + gen_key_info_->GetSuggestMaxKeySize()); + key_size_spin_box_->setValue(gen_key_info_->GetKeyLength()); + key_size_spin_box_->setSingleStep(gen_key_info_->GetSizeChangeStep()); +} + +void SubkeyGenerateDialog::slot_key_gen_accept() { + std::stringstream err_stream; + + /** + * primary keys should have a reasonable expiration date (no more than 2 years + * in the future) + */ + if (date_edit_->dateTime() > QDateTime::currentDateTime().addYears(2)) { + err_stream << " " << _("Expiration time no more than 2 years.") << " "; + } + + auto err_string = err_stream.str(); + + if (err_string.empty()) { + gen_key_info_->SetKeyLength(key_size_spin_box_->value()); + + if (expire_check_box_->checkState()) { + gen_key_info_->SetNonExpired(true); + } else { + gen_key_info_->SetExpireTime( + boost::posix_time::from_time_t(date_edit_->dateTime().toTime_t())); + } + + GpgError error; + auto thread = QThread::create([&]() { + LOG(INFO) << "Thread Started"; + error = GpgKeyOpera::GetInstance().GenerateSubkey(key_, gen_key_info_); + }); + thread->start(); + + auto* dialog = new WaitingDialog(_("Generating"), this); + dialog->show(); + + while (thread->isRunning()) { + QCoreApplication::processEvents(); + } + dialog->close(); + + if (check_gpg_error_2_err_code(error) == 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 new subkey has been generated.")); + msg_box->setModal(true); + msg_box->open(); + + emit SignalSubKeyGenerated(); + this->close(); + } else + QMessageBox::critical(this, _("Failure"), _("Failed to generate key.")); + + } 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(err_string.c_str()); + + this->show(); + } +} + +void SubkeyGenerateDialog::slot_encryption_box_changed(int state) { + if (state == 0) { + gen_key_info_->SetAllowEncryption(false); + } else { + gen_key_info_->SetAllowEncryption(true); + } +} + +void SubkeyGenerateDialog::slot_signing_box_changed(int state) { + if (state == 0) { + gen_key_info_->SetAllowSigning(false); + } else { + gen_key_info_->SetAllowSigning(true); + } +} + +void SubkeyGenerateDialog::slot_certification_box_changed(int state) { + if (state == 0) { + gen_key_info_->SetAllowCertification(false); + } else { + gen_key_info_->SetAllowCertification(true); + } +} + +void SubkeyGenerateDialog::slot_authentication_box_changed(int state) { + if (state == 0) { + gen_key_info_->SetAllowAuthentication(false); + } else { + gen_key_info_->SetAllowAuthentication(true); + } +} + +void SubkeyGenerateDialog::slot_activated_key_type(int index) { + qDebug() << "key type index changed " << index; + gen_key_info_->SetAlgo( + this->key_type_combo_box_->itemText(index).toStdString()); + refresh_widgets_state(); +} + +} // namespace GpgFrontend::UI diff --git a/src/ui/dialog/key_generate/SubkeyGenerateDialog.h b/src/ui/dialog/key_generate/SubkeyGenerateDialog.h new file mode 100644 index 00000000..1e6608b2 --- /dev/null +++ b/src/ui/dialog/key_generate/SubkeyGenerateDialog.h @@ -0,0 +1,152 @@ +/** + * 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_SUBKEYGENERATEDIALOG_H +#define GPGFRONTEND_SUBKEYGENERATEDIALOG_H + +#include "core/GpgContext.h" +#include "core/GpgGenKeyInfo.h" +#include "ui/GpgFrontendUI.h" +#include "ui/dialog/GeneralDialog.h" + +namespace GpgFrontend::UI { +/** + * @brief + * + */ +class SubkeyGenerateDialog : public GeneralDialog { + Q_OBJECT + + public: + /** + * @brief Construct a new Subkey Generate Dialog object + * + * @param key_id + * @param parent + */ + explicit SubkeyGenerateDialog(const KeyId& key_id, QWidget* parent); + + signals: + /** + * @brief + * + */ + void SignalSubKeyGenerated(); + + private: + GpgKey key_; ///< + + std::unique_ptr<GenKeyInfo> gen_key_info_ = + std::make_unique<GenKeyInfo>(true); ///< + + QGroupBox* key_usage_group_box_{}; + QDialogButtonBox* button_box_; ///< Box for standard buttons + QLabel* error_label_{}; ///< Label containing error message + QSpinBox* key_size_spin_box_{}; ///< Spinbox for the keys size (in bit) + QComboBox* key_type_combo_box_{}; ///< Combobox for Key tpe + QDateTimeEdit* date_edit_{}; ///< Date edit for expiration date + QCheckBox* expire_check_box_{}; ///< Checkbox, if key should expire + + std::vector<QCheckBox*> key_usage_check_boxes_; ///< ENCR, SIGN, CERT, AUTH + QDateTime max_date_time_; ///< + + /** + * @brief Create a key usage group box object + * + * @return QGroupBox* + */ + QGroupBox* create_key_usage_group_box(); + + /** + * @brief Create a basic info group box object + * + * @return QGroupBox* + */ + QGroupBox* create_basic_info_group_box(); + /** + * @brief Set the signal slot object + * + */ + void set_signal_slot(); + + /** + * @details Refresh widgets state by GenKeyInfo + */ + void refresh_widgets_state(); + + private slots: + + /** + * @details when expire box was checked/unchecked, enable/disable the + * expiration date box + */ + void slot_expire_box_changed(); + + /** + * @details check all line edits for false entries. Show error, when there is + * one, otherwise generate the key + */ + void slot_key_gen_accept(); + + /** + * @brief + * + * @param state + */ + void slot_encryption_box_changed(int state); + + /** + * @brief + * + * @param state + */ + void slot_signing_box_changed(int state); + + /** + * @brief + * + * @param state + */ + void slot_certification_box_changed(int state); + + /** + * @brief + * + * @param state + */ + void slot_authentication_box_changed(int state); + + /** + * @brief + * + * @param index + */ + void slot_activated_key_type(int index); +}; + +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_SUBKEYGENERATEDIALOG_H 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 diff --git a/src/ui/dialog/settings/SettingsAdvanced.cpp b/src/ui/dialog/settings/SettingsAdvanced.cpp new file mode 100644 index 00000000..516d4d02 --- /dev/null +++ b/src/ui/dialog/settings/SettingsAdvanced.cpp @@ -0,0 +1,104 @@ +/** + * 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 "SettingsAdvanced.h" + +#include "core/function/GlobalSettingStation.h" + +namespace GpgFrontend::UI { + +AdvancedTab::AdvancedTab(QWidget* parent) : QWidget(parent) { + auto* stegano_box = new QGroupBox(_("Show Steganography Options")); + auto* stegano_box_layout = new QHBoxLayout(); + stegano_check_box_ = new QCheckBox(_("Show Steganography Options."), this); + stegano_box_layout->addWidget(stegano_check_box_); + stegano_box->setLayout(stegano_box_layout); + + auto* pubkey_exchange_box = new QGroupBox(_("Pubkey Exchange")); + auto* pubkey_exchange_box_layout = new QHBoxLayout(); + auto_pubkey_exchange_check_box_ = + new QCheckBox(_("Auto Pubkey Exchange"), this); + pubkey_exchange_box_layout->addWidget(auto_pubkey_exchange_check_box_); + pubkey_exchange_box->setLayout(pubkey_exchange_box_layout); + + auto* main_layout = new QVBoxLayout; + main_layout->addWidget(stegano_box); + main_layout->addWidget(pubkey_exchange_box); + SetSettings(); + main_layout->addStretch(1); + setLayout(main_layout); +} + +void AdvancedTab::SetSettings() { + auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); + try { + bool stegano_checked = settings.lookup("advanced.stegano_checked"); + if (stegano_checked) stegano_check_box_->setCheckState(Qt::Checked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("stegano_checked"); + } + + try { + bool auto_pubkey_exchange_checked = + settings.lookup("advanced.auto_pubkey_exchange_checked"); + if (auto_pubkey_exchange_checked) + auto_pubkey_exchange_check_box_->setCheckState(Qt::Checked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") + << _("auto_pubkey_exchange_checked"); + } +} + +void AdvancedTab::ApplySettings() { + auto& settings = + GpgFrontend::GlobalSettingStation::GetInstance().GetUISettings(); + + if (!settings.exists("advanced") || + settings.lookup("advanced").getType() != libconfig::Setting::TypeGroup) + settings.add("advanced", libconfig::Setting::TypeGroup); + + auto& advanced = settings["advanced"]; + + if (!advanced.exists("stegano_checked")) + advanced.add("stegano_checked", libconfig::Setting::TypeBoolean) = + stegano_check_box_->isChecked(); + else { + advanced["stegano_checked"] = stegano_check_box_->isChecked(); + } + + if (!advanced.exists("auto_pubkey_exchange_checked")) + advanced.add("auto_pubkey_exchange_checked", + libconfig::Setting::TypeBoolean) = + auto_pubkey_exchange_check_box_->isChecked(); + else { + advanced["auto_pubkey_exchange_checked"] = + auto_pubkey_exchange_check_box_->isChecked(); + } +} + +} // namespace GpgFrontend::UI diff --git a/src/ui/dialog/settings/SettingsAdvanced.h b/src/ui/dialog/settings/SettingsAdvanced.h new file mode 100644 index 00000000..c1a3d5a6 --- /dev/null +++ b/src/ui/dialog/settings/SettingsAdvanced.h @@ -0,0 +1,55 @@ +/** + * 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_SETTINGSADVANCED_H +#define GPGFRONTEND_SETTINGSADVANCED_H + +#include "ui/GpgFrontendUI.h" + +namespace GpgFrontend::UI { +class AdvancedTab : public QWidget { + Q_OBJECT + + public: + explicit AdvancedTab(QWidget* parent = nullptr); + + void SetSettings(); + + void ApplySettings(); + + private: + QCheckBox* stegano_check_box_; + QCheckBox* auto_pubkey_exchange_check_box_; + + signals: + + void SignalRestartNeeded(bool needed); +}; +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_SETTINGSADVANCED_H diff --git a/src/ui/dialog/settings/SettingsAppearance.cpp b/src/ui/dialog/settings/SettingsAppearance.cpp new file mode 100644 index 00000000..11a59985 --- /dev/null +++ b/src/ui/dialog/settings/SettingsAppearance.cpp @@ -0,0 +1,206 @@ +/** + * 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 "SettingsAppearance.h" + +#include "core/function/GlobalSettingStation.h" +#include "ui/struct/SettingsObject.h" + +namespace GpgFrontend::UI { + +AppearanceTab::AppearanceTab(QWidget* parent) : QWidget(parent) { + /***************************************** + * Icon-Size-Box + *****************************************/ + auto* iconSizeBox = new QGroupBox(_("Icon Size")); + icon_size_group_ = new QButtonGroup(); + icon_size_small_ = new QRadioButton(_("small")); + icon_size_medium_ = new QRadioButton(_("medium")); + icon_size_large_ = new QRadioButton(_("large")); + + icon_size_group_->addButton(icon_size_small_, 1); + icon_size_group_->addButton(icon_size_medium_, 2); + icon_size_group_->addButton(icon_size_large_, 3); + + auto* iconSizeBoxLayout = new QHBoxLayout(); + iconSizeBoxLayout->addWidget(icon_size_small_); + iconSizeBoxLayout->addWidget(icon_size_medium_); + iconSizeBoxLayout->addWidget(icon_size_large_); + + iconSizeBox->setLayout(iconSizeBoxLayout); + + /***************************************** + * Icon-Style-Box + *****************************************/ + auto* iconStyleBox = new QGroupBox(_("Icon Style")); + icon_style_group_ = new QButtonGroup(); + icon_text_button_ = new QRadioButton(_("just text")); + icon_icons_button_ = new QRadioButton(_("just icons")); + icon_all_button_ = new QRadioButton(_("text and icons")); + + icon_style_group_->addButton(icon_text_button_, 1); + icon_style_group_->addButton(icon_icons_button_, 2); + icon_style_group_->addButton(icon_all_button_, 3); + + auto* iconStyleBoxLayout = new QHBoxLayout(); + iconStyleBoxLayout->addWidget(icon_text_button_); + iconStyleBoxLayout->addWidget(icon_icons_button_); + iconStyleBoxLayout->addWidget(icon_all_button_); + + iconStyleBox->setLayout(iconStyleBoxLayout); + + /***************************************** + * Window-Size-Box + *****************************************/ + auto* windowSizeBox = new QGroupBox(_("Window State")); + auto* windowSizeBoxLayout = new QHBoxLayout(); + window_size_check_box_ = + new QCheckBox(_("Save window size and position on exit."), this); + windowSizeBoxLayout->addWidget(window_size_check_box_); + windowSizeBox->setLayout(windowSizeBoxLayout); + + /***************************************** + * Info-Board-Font-Size-Box + *****************************************/ + + auto* infoBoardBox = new QGroupBox(_("Information Board")); + auto* infoBoardLayout = new QHBoxLayout(); + info_board_font_size_spin_ = new QSpinBox(); + info_board_font_size_spin_->setRange(9, 18); + info_board_font_size_spin_->setValue(10); + info_board_font_size_spin_->setSingleStep(1); + infoBoardLayout->addWidget(new QLabel(_("Font Size in Information Board"))); + infoBoardLayout->addWidget(info_board_font_size_spin_); + infoBoardBox->setLayout(infoBoardLayout); + + auto* mainLayout = new QVBoxLayout; + mainLayout->addWidget(iconSizeBox); + mainLayout->addWidget(iconStyleBox); + mainLayout->addWidget(windowSizeBox); + mainLayout->addWidget(infoBoardBox); + mainLayout->addStretch(1); + SetSettings(); + setLayout(mainLayout); +} + +/********************************** + * Read the settings from config + * and set the buttons and checkboxes + * appropriately + **********************************/ +void AppearanceTab::SetSettings() { + SettingsObject main_windows_state("main_windows_state"); + + int width = main_windows_state.Check("icon_size").Check("width", 24), + height = main_windows_state.Check("icon_size").Check("height", 24); + + auto icon_size = QSize(width, height); + + switch (icon_size.width()) { + case 12: + icon_size_small_->setChecked(true); + break; + case 24: + icon_size_medium_->setChecked(true); + break; + case 32: + icon_size_large_->setChecked(true); + break; + } + + // icon_style + int s_icon_style = + main_windows_state.Check("icon_style", Qt::ToolButtonTextUnderIcon); + auto icon_style = static_cast<Qt::ToolButtonStyle>(s_icon_style); + + switch (icon_style) { + case Qt::ToolButtonTextOnly: + icon_text_button_->setChecked(true); + break; + case Qt::ToolButtonIconOnly: + icon_icons_button_->setChecked(true); + break; + case Qt::ToolButtonTextUnderIcon: + icon_all_button_->setChecked(true); + break; + default: + break; + } + + bool window_save = main_windows_state.Check("window_save", true); + if (window_save) window_size_check_box_->setCheckState(Qt::Checked); + + auto info_font_size = main_windows_state.Check("info_font_size", 10); + if (info_font_size < 9 || info_font_size > 18) info_font_size = 10; + info_board_font_size_spin_->setValue(info_font_size); +} + +/*********************************** + * get the values of the buttons and + * write them to settings-file + *************************************/ +void AppearanceTab::ApplySettings() { + SettingsObject main_windows_state("main_windows_state"); + + int icon_size = 24; + switch (icon_size_group_->checkedId()) { + case 1: + icon_size = 12; + break; + case 2: + icon_size = 24; + break; + case 3: + icon_size = 32; + break; + } + + main_windows_state["icon_size"]["width"] = icon_size; + main_windows_state["icon_size"]["height"] = icon_size; + + auto icon_style = Qt::ToolButtonTextUnderIcon; + switch (icon_style_group_->checkedId()) { + case 1: + icon_style = Qt::ToolButtonTextOnly; + break; + case 2: + icon_style = Qt::ToolButtonIconOnly; + break; + case 3: + icon_style = Qt::ToolButtonTextUnderIcon; + break; + } + + main_windows_state["icon_style"] = icon_style; + + main_windows_state["window_save"] = window_size_check_box_->isChecked(); + + main_windows_state["info_font_size"] = info_board_font_size_spin_->value(); +} + +} // namespace GpgFrontend::UI diff --git a/src/ui/dialog/settings/SettingsAppearance.h b/src/ui/dialog/settings/SettingsAppearance.h new file mode 100644 index 00000000..7110d992 --- /dev/null +++ b/src/ui/dialog/settings/SettingsAppearance.h @@ -0,0 +1,83 @@ +/** + * 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_SETTINGSAPPEARANCE_H +#define GPGFRONTEND_SETTINGSAPPEARANCE_H + +#include "ui/GpgFrontendUI.h" + +namespace GpgFrontend::UI { + +class AppearanceTab : public QWidget { + Q_OBJECT + + public: + /** + * @brief Construct a new Appearance Tab object + * + * @param parent + */ + explicit AppearanceTab(QWidget* parent = nullptr); + + /** + * @brief Set the Settings object + * + */ + void SetSettings(); + + /** + * @brief + * + */ + void ApplySettings(); + + private: + QButtonGroup* icon_style_group_; ///< + QRadioButton* icon_size_small_; ///< + QRadioButton* icon_size_medium_; ///< + QRadioButton* icon_size_large_; ///< + QButtonGroup* icon_size_group_; ///< + QRadioButton* icon_text_button_; ///< + QRadioButton* icon_icons_button_; ///< + QRadioButton* icon_all_button_; ///< + QSpinBox* info_board_font_size_spin_; ///< + QCheckBox* window_size_check_box_; ///< + + signals: + + /** + * @brief + * + * @param needed + */ + void signalRestartNeeded(bool needed); +}; + +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_SETTINGSAPPEARANCE_H diff --git a/src/ui/dialog/settings/SettingsDialog.cpp b/src/ui/dialog/settings/SettingsDialog.cpp new file mode 100644 index 00000000..e2677a0f --- /dev/null +++ b/src/ui/dialog/settings/SettingsDialog.cpp @@ -0,0 +1,146 @@ +/** + * 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 "SettingsDialog.h" + +#include "SettingsAdvanced.h" +#include "SettingsAppearance.h" +#include "SettingsGeneral.h" +#include "SettingsKeyServer.h" +#include "SettingsNetwork.h" +#include "core/function/GlobalSettingStation.h" +#include "ui/main_window/MainWindow.h" + +namespace GpgFrontend::UI { + +SettingsDialog::SettingsDialog(QWidget* parent) + : GeneralDialog(typeid(SettingsDialog).name(), parent) { + tab_widget_ = new QTabWidget(); + general_tab_ = new GeneralTab(); + appearance_tab_ = new AppearanceTab(); + key_server_tab_ = new KeyserverTab(); + network_tab_ = new NetworkTab(); + + auto* mainLayout = new QVBoxLayout; + mainLayout->addWidget(tab_widget_); + mainLayout->stretch(0); + + tab_widget_->addTab(general_tab_, _("General")); + tab_widget_->addTab(appearance_tab_, _("Appearance")); + tab_widget_->addTab(key_server_tab_, _("Key Server")); + tab_widget_->addTab(network_tab_, _("Network")); + +#ifndef MACOS + button_box_ = + new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(button_box_, &QDialogButtonBox::accepted, this, + &SettingsDialog::SlotAccept); + connect(button_box_, &QDialogButtonBox::rejected, this, + &SettingsDialog::reject); + mainLayout->addWidget(button_box_); + mainLayout->stretch(0); + setWindowTitle(_("Settings")); +#else + connect(this, &QDialog::finished, this, &SettingsDialog::SlotAccept); + connect(this, &QDialog::finished, this, &SettingsDialog::deleteLater); + setWindowTitle(_("Preference")); +#endif + + setLayout(mainLayout); + + // slots for handling the restart needed member + this->slot_set_restart_needed(false); + connect(general_tab_, &GeneralTab::SignalRestartNeeded, this, + &SettingsDialog::slot_set_restart_needed); + connect(this, &SettingsDialog::SignalRestartNeeded, + qobject_cast<MainWindow*>(parent), &MainWindow::SlotSetRestartNeeded); + + this->setMinimumSize(480, 680); + this->adjustSize(); + this->show(); +} + +bool SettingsDialog::get_restart_needed() const { + return this->restart_needed_; +} + +void SettingsDialog::slot_set_restart_needed(bool needed) { + this->restart_needed_ = needed; +} + +void SettingsDialog::SlotAccept() { + LOG(INFO) << "Called"; + + general_tab_->ApplySettings(); + appearance_tab_->ApplySettings(); + key_server_tab_->ApplySettings(); + network_tab_->ApplySettings(); + + LOG(INFO) << "apply done"; + + // write settings to filesystem + GlobalSettingStation::GetInstance().SyncSettings(); + + LOG(INFO) << "restart needed" << get_restart_needed(); + if (get_restart_needed()) { + emit SignalRestartNeeded(true); + } + close(); +} + +QHash<QString, QString> SettingsDialog::ListLanguages() { + QHash<QString, QString> languages; + + languages.insert(QString(), _("System Default")); + + auto locale_path = GlobalSettingStation::GetInstance().GetLocaleDir(); + + auto locale_dir = QDir(QString::fromStdString(locale_path.string())); + QStringList file_names = locale_dir.entryList(QStringList("*")); + + for (int i = 0; i < file_names.size(); ++i) { + QString locale = file_names[i]; + LOG(INFO) << "locale" << locale.toStdString(); + if (locale == "." || locale == "..") continue; + + // this works in qt 4.8 + QLocale q_locale(locale); + if (q_locale.nativeCountryName().isEmpty()) continue; +#if QT_VERSION < 0x040800 + QString language = + QLocale::languageToString(q_locale.language()) + " (" + locale + + ")"; //+ " (" + QLocale::languageToString(q_locale.language()) + ")"; +#else + auto language = q_locale.nativeLanguageName() + " (" + locale + ")"; +#endif + languages.insert(locale, language); + } + return languages; +} + +} // namespace GpgFrontend::UI diff --git a/src/ui/dialog/settings/SettingsDialog.h b/src/ui/dialog/settings/SettingsDialog.h new file mode 100644 index 00000000..172370d0 --- /dev/null +++ b/src/ui/dialog/settings/SettingsDialog.h @@ -0,0 +1,112 @@ +/** + * 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 __SETTINGSDIALOG_H__ +#define __SETTINGSDIALOG_H__ + +#include "ui/GpgFrontendUI.h" +#include "ui/dialog/GeneralDialog.h" +#include "ui/widgets/KeyList.h" + +namespace GpgFrontend::UI { + +class GeneralTab; +class AppearanceTab; +class KeyserverTab; +class NetworkTab; + +/** + * @brief + * + */ +class SettingsDialog : public GeneralDialog { + Q_OBJECT + + public: + /** + * @brief Construct a new Settings Dialog object + * + * @param parent + */ + explicit SettingsDialog(QWidget* parent = nullptr); + + GeneralTab* general_tab_; ///< + AppearanceTab* appearance_tab_; ///< + KeyserverTab* key_server_tab_; ///< + NetworkTab* network_tab_; ///< + + /** + * @brief + * + * @return QHash<QString, QString> + */ + static QHash<QString, QString> ListLanguages(); + + public slots: + + /** + * @brief + * + */ + void SlotAccept(); + + signals: + + /** + * @brief + * + * @param needed + */ + void SignalRestartNeeded(bool needed); + + private: + QTabWidget* tab_widget_; ///< + QDialogButtonBox* button_box_; ///< + bool restart_needed_{}; ///< + + /** + * @brief Get the Restart Needed object + * + * @return true + * @return false + */ + bool get_restart_needed() const; + + private slots: + + /** + * @brief + * + * @param needed + */ + void slot_set_restart_needed(bool needed); +}; + +} // namespace GpgFrontend::UI + +#endif // __SETTINGSDIALOG_H__ diff --git a/src/ui/dialog/settings/SettingsGeneral.cpp b/src/ui/dialog/settings/SettingsGeneral.cpp new file mode 100644 index 00000000..3c7bca32 --- /dev/null +++ b/src/ui/dialog/settings/SettingsGeneral.cpp @@ -0,0 +1,196 @@ +/** + * 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 "SettingsGeneral.h" + +#ifdef MULTI_LANG_SUPPORT +#include "SettingsDialog.h" +#endif + +#include "core/function/GlobalSettingStation.h" +#include "ui_GeneralSettings.h" + +namespace GpgFrontend::UI { + +GeneralTab::GeneralTab(QWidget* parent) + : QWidget(parent), ui_(std::make_shared<Ui_GeneralSettings>()) { + ui_->setupUi(this); + + ui_->saveCheckedKeysBox->setTitle(_("Save Checked Keys")); + ui_->saveCheckedKeysCheckBox->setText( + _("Save checked private keys on exit and restore them on next start.")); + ui_->longerKeyExpirationDateBox->setTitle(_("Longer Key Expiration Date")); + ui_->longerKeyExpirationDateCheckBox->setText( + _("Unlock key expiration date setting up to 30 years.")); + ui_->importConfirmationBox->setTitle(_("Confirm drag'n'drop key import")); + ui_->importConfirmationCheckBox->setText( + _("Import files dropped on the Key List without confirmation.")); + + ui_->asciiModeBox->setTitle(_("ASCII Mode")); + ui_->asciiModeCheckBox->setText( + _("ASCII encoding is not used when file encrypting and " + "signing.")); + + ui_->langBox->setTitle(_("Language")); + ui_->langNoteLabel->setText( + "<b>" + QString(_("NOTE")) + _(": ") + "</b>" + + _("GpgFrontend will restart automatically if you change the language!")); + +#ifdef MULTI_LANG_SUPPORT + lang_ = SettingsDialog::ListLanguages(); + for (const auto& l : lang_) { + ui_->langSelectBox->addItem(l); + } + connect(ui_->langSelectBox, qOverload<int>(&QComboBox::currentIndexChanged), + this, &GeneralTab::slot_language_changed); +#endif + + SetSettings(); +} + +/********************************** + * Read the settings from config + * and set the buttons and checkboxes + * appropriately + **********************************/ +void GeneralTab::SetSettings() { + auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); + try { + bool save_key_checked = settings.lookup("general.save_key_checked"); + if (save_key_checked) + ui_->saveCheckedKeysCheckBox->setCheckState(Qt::Checked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("save_key_checked"); + } + + try { + bool longer_expiration_date = + settings.lookup("general.longer_expiration_date"); + LOG(INFO) << "longer_expiration_date" << longer_expiration_date; + if (longer_expiration_date) + ui_->longerKeyExpirationDateCheckBox->setCheckState(Qt::Checked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("longer_expiration_date"); + } + +#ifdef MULTI_LANG_SUPPORT + try { + std::string lang_key = settings.lookup("general.lang"); + QString lang_value = lang_.value(lang_key.c_str()); + LOG(INFO) << "lang settings current" << lang_value.toStdString(); + if (!lang_.empty()) { + ui_->langSelectBox->setCurrentIndex( + ui_->langSelectBox->findText(lang_value)); + } else { + ui_->langSelectBox->setCurrentIndex(0); + } + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("lang"); + } +#endif + + try { + bool confirm_import_keys = settings.lookup("general.confirm_import_keys"); + LOG(INFO) << "confirm_import_keys" << confirm_import_keys; + if (confirm_import_keys) + ui_->importConfirmationCheckBox->setCheckState(Qt::Checked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("confirm_import_keys"); + } + + try { + bool non_ascii_when_export = + settings.lookup("general.non_ascii_when_export"); + LOG(INFO) << "non_ascii_when_export" << non_ascii_when_export; + if (non_ascii_when_export) + ui_->asciiModeCheckBox->setCheckState(Qt::Checked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("non_ascii_when_export"); + } +} + +/*********************************** + * get the values of the buttons and + * write them to settings-file + *************************************/ +void GeneralTab::ApplySettings() { + auto& settings = + GpgFrontend::GlobalSettingStation::GetInstance().GetUISettings(); + + if (!settings.exists("general") || + settings.lookup("general").getType() != libconfig::Setting::TypeGroup) + settings.add("general", libconfig::Setting::TypeGroup); + + auto& general = settings["general"]; + + if (!general.exists("longer_expiration_date")) + general.add("longer_expiration_date", libconfig::Setting::TypeBoolean) = + ui_->longerKeyExpirationDateCheckBox->isChecked(); + else { + general["longer_expiration_date"] = + ui_->longerKeyExpirationDateCheckBox->isChecked(); + } + + if (!general.exists("save_key_checked")) + general.add("save_key_checked", libconfig::Setting::TypeBoolean) = + ui_->saveCheckedKeysCheckBox->isChecked(); + else { + general["save_key_checked"] = ui_->saveCheckedKeysCheckBox->isChecked(); + } + + if (!general.exists("non_ascii_when_export")) + general.add("non_ascii_when_export", libconfig::Setting::TypeBoolean) = + ui_->asciiModeCheckBox->isChecked(); + else { + general["non_ascii_when_export"] = ui_->asciiModeCheckBox->isChecked(); + } + +#ifdef MULTI_LANG_SUPPORT + if (!general.exists("lang")) + general.add("lang", libconfig::Setting::TypeBoolean) = + lang_.key(ui_->langSelectBox->currentText()).toStdString(); + else { + general["lang"] = + lang_.key(ui_->langSelectBox->currentText()).toStdString(); + } +#endif + + if (!general.exists("confirm_import_keys")) + general.add("confirm_import_keys", libconfig::Setting::TypeBoolean) = + ui_->importConfirmationCheckBox->isChecked(); + else { + general["confirm_import_keys"] = + ui_->importConfirmationCheckBox->isChecked(); + } +} + +#ifdef MULTI_LANG_SUPPORT +void GeneralTab::slot_language_changed() { emit SignalRestartNeeded(true); } +#endif + +} // namespace GpgFrontend::UI diff --git a/src/ui/dialog/settings/SettingsGeneral.h b/src/ui/dialog/settings/SettingsGeneral.h new file mode 100644 index 00000000..b3e7d904 --- /dev/null +++ b/src/ui/dialog/settings/SettingsGeneral.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_SETTINGSGENERAL_H +#define GPGFRONTEND_SETTINGSGENERAL_H + +#include "ui/GpgFrontendUI.h" + +class Ui_GeneralSettings; + +namespace GpgFrontend::UI { +class KeyList; + +/** + * @brief + * + */ +class GeneralTab : public QWidget { + Q_OBJECT + + public: + /** + * @brief Construct a new General Tab object + * + * @param parent + */ + explicit GeneralTab(QWidget* parent = nullptr); + + /** + * @brief Set the Settings object + * + */ + void SetSettings(); + + /** + * @brief + * + */ + void ApplySettings(); + + signals: + + /** + * @brief + * + * @param needed + */ + void SignalRestartNeeded(bool needed); + + private: + std::shared_ptr<Ui_GeneralSettings> ui_; ///< + +#ifdef MULTI_LANG_SUPPORT + QHash<QString, QString> lang_; ///< +#endif + + std::vector<std::string> key_ids_list_; ///< + + KeyList* m_key_list_{}; ///< + + private slots: + +#ifdef MULTI_LANG_SUPPORT + /** + * @brief + * + */ + void slot_language_changed(); + +#endif +}; +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_SETTINGSGENERAL_H diff --git a/src/ui/dialog/settings/SettingsKeyServer.cpp b/src/ui/dialog/settings/SettingsKeyServer.cpp new file mode 100644 index 00000000..2c09b66c --- /dev/null +++ b/src/ui/dialog/settings/SettingsKeyServer.cpp @@ -0,0 +1,301 @@ +/** + * 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 "SettingsKeyServer.h" + +#include "core/function/GlobalSettingStation.h" +#include "core/thread/Task.h" +#include "core/thread/TaskRunnerGetter.h" +#include "ui/struct/SettingsObject.h" +#include "ui/thread/ListedKeyServerTestTask.h" +#include "ui_KeyServerSettings.h" + +namespace GpgFrontend::UI { + +KeyserverTab::KeyserverTab(QWidget* parent) + : QWidget(parent), ui_(std::make_shared<Ui_KeyServerSettings>()) { + ui_->setupUi(this); + ui_->keyServerListTable->setSizeAdjustPolicy( + QAbstractScrollArea::AdjustToContents); + + connect(ui_->addKeyServerPushButton, &QPushButton::clicked, this, + &KeyserverTab::slot_add_key_server); + connect(ui_->testKeyServerButton, &QPushButton::clicked, this, + &KeyserverTab::slot_test_listed_key_server); + + ui_->keyServerListGroupBox->setTitle(_("Keyserver List")); + ui_->operationsGroupBox->setTitle(_("Operations")); + + ui_->keyServerListTable->horizontalHeaderItem(0)->setText(_("Default")); + ui_->keyServerListTable->horizontalHeaderItem(1)->setText( + _("Keyserver Address")); + ui_->keyServerListTable->horizontalHeaderItem(2)->setText(_("Security")); + ui_->keyServerListTable->horizontalHeaderItem(3)->setText(_("Available")); + + ui_->addKeyServerPushButton->setText(_("Add")); + ui_->testKeyServerButton->setText(_("Test Listed Keyserver")); + + ui_->tipsLabel->setText( + _("Tips: Please Double-click table item to edit it.")); + ui_->actionDelete_Selected_Key_Server->setText(_("Delete Selected")); + ui_->actionDelete_Selected_Key_Server->setToolTip( + _("Delete Selected Key Server")); + ui_->actionSet_As_Default->setText(_("Set As Default")); + ui_->actionSet_As_Default->setToolTip(_("Set As Default")); + + popup_menu_ = new QMenu(this); + popup_menu_->addAction(ui_->actionSet_As_Default); + popup_menu_->addAction(ui_->actionDelete_Selected_Key_Server); + + connect(ui_->keyServerListTable, &QTableWidget::itemChanged, + [=](QTableWidgetItem* item) { + LOG(INFO) << "item edited" << item->column(); + if (item->column() != 1) return; + const auto row_size = ui_->keyServerListTable->rowCount(); + // Update Actions + if (row_size > 0) { + key_server_str_list_.clear(); + for (int i = 0; i < row_size; i++) { + const auto key_server = + ui_->keyServerListTable->item(i, 1)->text(); + key_server_str_list_.append(key_server); + } + } + }); + + connect(ui_->actionSet_As_Default, &QAction::triggered, [=]() { + const auto row_size = ui_->keyServerListTable->rowCount(); + for (int i = 0; i < row_size; i++) { + const auto item = ui_->keyServerListTable->item(i, 1); + if (!item->isSelected()) continue; + this->default_key_server_ = item->text(); + } + this->slot_refresh_table(); + }); + + connect(ui_->actionDelete_Selected_Key_Server, &QAction::triggered, [=]() { + const auto row_size = ui_->keyServerListTable->rowCount(); + for (int i = 0; i < row_size; i++) { + const auto item = ui_->keyServerListTable->item(i, 1); + if (!item->isSelected()) continue; + this->key_server_str_list_.removeAt(i); + break; + } + this->slot_refresh_table(); + }); + + // Read key-list from ini-file and fill it into combobox + SetSettings(); + slot_refresh_table(); +} + +/********************************** + * Read the settings from config + * and set the buttons and checkboxes + * appropriately + **********************************/ +void KeyserverTab::SetSettings() { + try { + SettingsObject key_server_json("key_server"); + + const auto key_server_list = + key_server_json.Check("server_list", nlohmann::json::array()); + + for (const auto& key_server : key_server_list) { + const auto key_server_str = key_server.get<std::string>(); + this->key_server_str_list_.append(key_server_str.c_str()); + } + + int default_key_server_index = key_server_json.Check("default_server", 0); + if (default_key_server_index >= key_server_list.size()) { + throw std::runtime_error("default_server index out of range"); + } + std::string default_key_server = + key_server_list[default_key_server_index].get<std::string>(); + + if (!key_server_str_list_.contains(default_key_server.c_str())) + key_server_str_list_.append(default_key_server.c_str()); + default_key_server_ = QString::fromStdString(default_key_server); + } catch (const std::exception& e) { + LOG(ERROR) << "Error reading key-server settings: " << e.what(); + } +} + +void KeyserverTab::slot_add_key_server() { + auto target_url = ui_->addKeyServerEdit->text(); + if (url_reg_.match(target_url).hasMatch()) { + if (target_url.startsWith("https://")) { + ; + } else if (target_url.startsWith("http://")) { + QMessageBox::warning( + this, _("Insecure keyserver address"), + _("For security reasons, using HTTP as the communication protocol " + "with " + "the key server is not recommended. It is recommended to use " + "HTTPS.")); + } + key_server_str_list_.append(ui_->addKeyServerEdit->text()); + } else { + auto ret = QMessageBox::warning( + this, _("Warning"), + _("You may not use HTTPS or HTTP as the protocol for communicating " + "with the key server, which may not be wrong. But please check the " + "address you entered again to make sure it is correct. Are you " + "sure " + "that want to add it into the keyserver list?"), + QMessageBox::Ok | QMessageBox::Cancel); + + if (ret == QMessageBox::Cancel) + return; + else + key_server_str_list_.append(ui_->addKeyServerEdit->text()); + } + slot_refresh_table(); +} + +void KeyserverTab::ApplySettings() { + SettingsObject key_server_json("key_server"); + key_server_json["server_list"] = nlohmann::json::array(); + auto& key_server_list = key_server_json["server_list"]; + + const auto list_size = key_server_str_list_.size(); + for (int i = 0; i < list_size; i++) { + const auto key_server = key_server_str_list_[i]; + if (default_key_server_ == key_server) + key_server_json["default_server"] = i; + key_server_list.insert(key_server_list.end(), key_server.toStdString()); + } +} + +void KeyserverTab::slot_refresh_table() { + LOG(INFO) << "Start Refreshing Key Server Table"; + + ui_->keyServerListTable->blockSignals(true); + ui_->keyServerListTable->setRowCount(key_server_str_list_.size()); + + int index = 0; + for (const auto& server : key_server_str_list_) { + auto* tmp1 = + new QTableWidgetItem(server == default_key_server_ ? "*" : QString{}); + tmp1->setTextAlignment(Qt::AlignCenter); + ui_->keyServerListTable->setItem(index, 0, tmp1); + tmp1->setFlags(tmp1->flags() ^ Qt::ItemIsEditable); + + auto* tmp2 = new QTableWidgetItem(server); + tmp2->setTextAlignment(Qt::AlignCenter); + ui_->keyServerListTable->setItem(index, 1, tmp2); + + auto* tmp3 = new QTableWidgetItem(server.startsWith("https") ? _("true") + : _("false")); + tmp3->setTextAlignment(Qt::AlignCenter); + ui_->keyServerListTable->setItem(index, 2, tmp3); + tmp3->setFlags(tmp3->flags() ^ Qt::ItemIsEditable); + + auto* tmp4 = new QTableWidgetItem(_("unknown")); + tmp4->setTextAlignment(Qt::AlignCenter); + ui_->keyServerListTable->setItem(index, 3, tmp4); + tmp4->setFlags(tmp3->flags() ^ Qt::ItemIsEditable); + index++; + } + const auto column_count = ui_->keyServerListTable->columnCount(); + for (int i = 0; i < column_count; i++) { + ui_->keyServerListTable->resizeColumnToContents(i); + } + ui_->keyServerListTable->blockSignals(false); +} + +void KeyserverTab::slot_test_listed_key_server() { + auto timeout = QInputDialog::getInt(this, _("Set TCP Timeout"), + tr("timeout(ms): "), 2500, 200, 16000); + + QStringList urls; + const auto row_size = ui_->keyServerListTable->rowCount(); + for (int i = 0; i < row_size; i++) { + const auto keyserver_url = ui_->keyServerListTable->item(i, 1)->text(); + urls.push_back(keyserver_url); + } + + auto* task = new ListedKeyServerTestTask(urls, timeout, this); + + connect( + task, + &GpgFrontend::UI::ListedKeyServerTestTask::SignalKeyServerListTestResult, + this, + [=](std::vector<ListedKeyServerTestTask::KeyServerTestResultType> + result) { + const auto row_size = ui_->keyServerListTable->rowCount(); + if (result.size() != row_size) return; + ui_->keyServerListTable->blockSignals(true); + for (int i = 0; i < row_size; i++) { + const auto status = result[i]; + auto status_iem = ui_->keyServerListTable->item(i, 3); + if (status == ListedKeyServerTestTask::kTestResultType_Success) { + status_iem->setText(_("Reachable")); + status_iem->setForeground(QBrush(QColor::fromRgb(0, 255, 0))); + } else { + status_iem->setText(_("Not Reachable")); + status_iem->setForeground(QBrush(QColor::fromRgb(255, 0, 0))); + } + } + ui_->keyServerListTable->blockSignals(false); + }); + + // Waiting Dialog + auto* waiting_dialog = new QProgressDialog(this); + waiting_dialog->setMaximum(0); + waiting_dialog->setMinimum(0); + auto waiting_dialog_label = + new QLabel(QString(_("Test Key Server Connection...")) + "<br /><br />" + + _("This test only tests the network connectivity of the key " + "server. Passing the test does not mean that the key server " + "is functionally available.")); + waiting_dialog_label->setWordWrap(true); + waiting_dialog->setLabel(waiting_dialog_label); + waiting_dialog->resize(420, 120); + waiting_dialog->setModal(true); + connect(task, &Thread::Task::SignalTaskFinished, [=]() { + waiting_dialog->close(); + waiting_dialog->deleteLater(); + }); + // Show Waiting Dialog + waiting_dialog->show(); + waiting_dialog->setFocus(); + + Thread::TaskRunnerGetter::GetInstance() + .GetTaskRunner(Thread::TaskRunnerGetter::kTaskRunnerType_Network) + ->PostTask(task); +} + +void KeyserverTab::contextMenuEvent(QContextMenuEvent* event) { + QWidget::contextMenuEvent(event); + if (ui_->keyServerListTable->selectedItems().length() > 0) { + popup_menu_->exec(event->globalPos()); + } +} + +} // namespace GpgFrontend::UI diff --git a/src/ui/dialog/settings/SettingsKeyServer.h b/src/ui/dialog/settings/SettingsKeyServer.h new file mode 100644 index 00000000..f983e69b --- /dev/null +++ b/src/ui/dialog/settings/SettingsKeyServer.h @@ -0,0 +1,111 @@ +/** + * 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_SETTINGSKEYSERVER_H +#define GPGFRONTEND_SETTINGSKEYSERVER_H + +#include "ui/GpgFrontendUI.h" + +class Ui_KeyServerSettings; + +namespace GpgFrontend::UI { +/** + * @brief + * + */ +class KeyserverTab : public QWidget { + Q_OBJECT + + public: + /** + * @brief Construct a new Keyserver Tab object + * + * @param parent + */ + explicit KeyserverTab(QWidget* parent = nullptr); + + /** + * @brief Set the Settings object + * + */ + void SetSettings(); + + /** + * @brief + * + */ + void ApplySettings(); + + private: + std::shared_ptr<Ui_KeyServerSettings> ui_; + QString default_key_server_; + QStringList key_server_str_list_; + QMenu* popup_menu_{}; + + QRegularExpression url_reg_{ + R"(^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$)"}; + + private slots: + + /** + * @brief + * + */ + void slot_add_key_server(); + + /** + * @brief + * + */ + void slot_refresh_table(); + + /** + * @brief + * + */ + void slot_test_listed_key_server(); + + signals: + /** + * @brief + * + * @param needed + */ + void SignalRestartNeeded(bool needed); + + protected: + /** + * @brief + * + * @param event + */ + void contextMenuEvent(QContextMenuEvent* event) override; +}; +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_SETTINGSKEYSERVER_H diff --git a/src/ui/dialog/settings/SettingsNetwork.cpp b/src/ui/dialog/settings/SettingsNetwork.cpp new file mode 100644 index 00000000..d4edae42 --- /dev/null +++ b/src/ui/dialog/settings/SettingsNetwork.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 "SettingsNetwork.h" + +#include "core/function/GlobalSettingStation.h" +#include "ui/thread/ProxyConnectionTestThread.h" +#include "ui_NetworkSettings.h" + +GpgFrontend::UI::NetworkTab::NetworkTab(QWidget *parent) + : QWidget(parent), ui_(std::make_shared<Ui_NetworkSettings>()) { + ui_->setupUi(this); + + connect(ui_->enableProxyCheckBox, &QCheckBox::stateChanged, this, + [=](int state) { switch_ui_enabled(state == Qt::Checked); }); + + connect( + ui_->proxyTypeComboBox, &QComboBox::currentTextChanged, this, + [=](const QString ¤t_text) { switch_ui_proxy_type(current_text); }); + + connect(ui_->checkProxyConnectionButton, &QPushButton::clicked, this, + &NetworkTab::slot_test_proxy_connection_result); + + ui_->proxyGroupBox->setTitle(_("Proxy")); + ui_->capabilityGroupBox->setTitle(_("Network Capability")); + ui_->operationsGroupBox->setTitle(_("Operations")); + + ui_->enableProxyCheckBox->setText(_("Enable Proxy")); + ui_->proxyServerPortLabel->setText(_("Port")); + + ui_->proxyServerAddressLabel->setText(_("Host Address")); + ui_->proxyServerPortLabel->setText(_("Port")); + ui_->proxyTypeLabel->setText(_("Proxy Type")); + ui_->usernameLabel->setText(_("Username")); + ui_->passwordLabel->setText(_("Password")); + + ui_->forbidALLCheckBox->setText(_("Forbid all network connection.")); + ui_->forbidALLCheckBox->setDisabled(true); + + ui_->prohibitUpdateCheck->setText( + _("Prohibit checking for version updates when the program starts.")); + ui_->checkProxyConnectionButton->setText(_("Check Proxy Connection")); + + SetSettings(); +} + +void GpgFrontend::UI::NetworkTab::SetSettings() { + auto &settings = GlobalSettingStation::GetInstance().GetUISettings(); + + try { + std::string proxy_host = settings.lookup("proxy.proxy_host"); + ui_->proxyServerAddressEdit->setText(proxy_host.c_str()); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("proxy_host"); + } + + try { + std::string std_username = settings.lookup("proxy.username"); + ui_->usernameEdit->setText(std_username.c_str()); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("username"); + } + + try { + std::string std_password = settings.lookup("proxy.password"); + ui_->passwordEdit->setText(std_password.c_str()); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("password"); + } + + try { + int port = settings.lookup("proxy.port"); + ui_->portSpin->setValue(port); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("port"); + } + + ui_->proxyTypeComboBox->setCurrentText("HTTP"); + try { + std::string proxy_type = settings.lookup("proxy.proxy_type"); + ui_->proxyTypeComboBox->setCurrentText(proxy_type.c_str()); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("proxy_type"); + } + switch_ui_proxy_type(ui_->proxyTypeComboBox->currentText()); + + ui_->enableProxyCheckBox->setCheckState(Qt::Unchecked); + try { + bool proxy_enable = settings.lookup("proxy.enable"); + if (proxy_enable) + ui_->enableProxyCheckBox->setCheckState(Qt::Checked); + else + ui_->enableProxyCheckBox->setCheckState(Qt::Unchecked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("proxy_enable"); + } + + { + auto state = ui_->enableProxyCheckBox->checkState(); + switch_ui_enabled(state == Qt::Checked); + } + + ui_->forbidALLCheckBox->setCheckState(Qt::Unchecked); + try { + bool forbid_all_connection = + settings.lookup("network.forbid_all_connection"); + if (forbid_all_connection) + ui_->forbidALLCheckBox->setCheckState(Qt::Checked); + else + ui_->forbidALLCheckBox->setCheckState(Qt::Unchecked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("forbid_all_connection"); + } + + ui_->prohibitUpdateCheck->setCheckState(Qt::Unchecked); + try { + bool prohibit_update_checking = + settings.lookup("network.prohibit_update_checking"); + if (prohibit_update_checking) + ui_->prohibitUpdateCheck->setCheckState(Qt::Checked); + else + ui_->prohibitUpdateCheck->setCheckState(Qt::Unchecked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("prohibit_update_checking"); + } +} + +void GpgFrontend::UI::NetworkTab::ApplySettings() { + LOG(INFO) << "called"; + + auto &settings = + GpgFrontend::GlobalSettingStation::GetInstance().GetUISettings(); + + if (!settings.exists("proxy") || + settings.lookup("proxy").getType() != libconfig::Setting::TypeGroup) + settings.add("proxy", libconfig::Setting::TypeGroup); + + auto &proxy = settings["proxy"]; + + if (!proxy.exists("proxy_host")) + proxy.add("proxy_host", libconfig::Setting::TypeString) = + ui_->proxyServerAddressEdit->text().toStdString(); + else { + proxy["proxy_host"] = ui_->proxyServerAddressEdit->text().toStdString(); + } + + if (!proxy.exists("username")) + proxy.add("username", libconfig::Setting::TypeString) = + ui_->usernameEdit->text().toStdString(); + else { + proxy["username"] = ui_->usernameEdit->text().toStdString(); + } + + if (!proxy.exists("password")) + proxy.add("password", libconfig::Setting::TypeString) = + ui_->passwordEdit->text().toStdString(); + else { + proxy["password"] = ui_->passwordEdit->text().toStdString(); + } + + if (!proxy.exists("port")) + proxy.add("port", libconfig::Setting::TypeInt) = ui_->portSpin->value(); + else { + proxy["port"] = ui_->portSpin->value(); + } + + if (!proxy.exists("proxy_type")) + proxy.add("proxy_type", libconfig::Setting::TypeString) = + ui_->proxyTypeComboBox->currentText().toStdString(); + else { + proxy["proxy_type"] = ui_->proxyTypeComboBox->currentText().toStdString(); + } + + if (!proxy.exists("enable")) + proxy.add("enable", libconfig::Setting::TypeBoolean) = + ui_->enableProxyCheckBox->isChecked(); + else { + proxy["enable"] = ui_->enableProxyCheckBox->isChecked(); + } + + if (!settings.exists("network") || + settings.lookup("network").getType() != libconfig::Setting::TypeGroup) + settings.add("network", libconfig::Setting::TypeGroup); + + auto &network = settings["network"]; + + if (!network.exists("forbid_all_connection")) + network.add("forbid_all_connection", libconfig::Setting::TypeBoolean) = + ui_->forbidALLCheckBox->isChecked(); + else { + network["forbid_all_connection"] = ui_->forbidALLCheckBox->isChecked(); + } + + if (!network.exists("prohibit_update_checking")) + network.add("prohibit_update_checking", libconfig::Setting::TypeBoolean) = + ui_->prohibitUpdateCheck->isChecked(); + else { + network["prohibit_update_checking"] = ui_->prohibitUpdateCheck->isChecked(); + } + + apply_proxy_settings(); + + LOG(INFO) << "done"; +} + +void GpgFrontend::UI::NetworkTab::slot_test_proxy_connection_result() { + apply_proxy_settings(); + + bool ok; + auto url = QInputDialog::getText(this, _("Test Server Url Accessibility"), + tr("Server Url"), QLineEdit::Normal, + "https://", &ok); + if (ok && !url.isEmpty()) { + auto thread = new ProxyConnectionTestThread(url, 800, this); + connect(thread, + &GpgFrontend::UI::ProxyConnectionTestThread:: + SignalProxyConnectionTestResult, + this, [=](const QString &result) { + if (result == "Reachable") { + QMessageBox::information(this, _("Success"), + _("Successfully connect to the target " + "server through the proxy server.")); + } else { + QMessageBox::critical( + this, _("Failed"), + _("Unable to connect to the target server through the " + "proxy server. Proxy settings may be invalid.")); + } + }); + connect(thread, &QThread::finished, thread, &QThread::deleteLater); + + // Waiting Dialog + auto *waiting_dialog = new QProgressDialog(this); + waiting_dialog->setMaximum(0); + waiting_dialog->setMinimum(0); + auto waiting_dialog_label = new QLabel( + QString(_("Test Proxy Server Connection...")) + "<br /><br />" + + _("Is using your proxy settings to access the url. Note that this test " + "operation will apply your proxy settings to the entire software.")); + waiting_dialog_label->setWordWrap(true); + waiting_dialog->setLabel(waiting_dialog_label); + waiting_dialog->resize(420, 120); + connect(thread, &QThread::finished, [=]() { + waiting_dialog->finished(0); + waiting_dialog->deleteLater(); + }); + connect(waiting_dialog, &QProgressDialog::canceled, [=]() { + LOG(INFO) << "cancel clicked"; + if (thread->isRunning()) thread->terminate(); + }); + + // Show Waiting Dialog + waiting_dialog->show(); + waiting_dialog->setFocus(); + + thread->start(); + QEventLoop loop; + connect(thread, &QThread::finished, &loop, &QEventLoop::quit); + loop.exec(); + } +} + +void GpgFrontend::UI::NetworkTab::apply_proxy_settings() { + // apply settings + QNetworkProxy _proxy; + if (ui_->enableProxyCheckBox->isChecked() && + proxy_type_ != QNetworkProxy::DefaultProxy) { + _proxy.setType(proxy_type_); + _proxy.setHostName(ui_->proxyServerAddressEdit->text()); + _proxy.setPort(ui_->portSpin->value()); + if (!ui_->usernameEdit->text().isEmpty()) { + _proxy.setUser(ui_->usernameEdit->text()); + _proxy.setPassword(ui_->passwordEdit->text()); + } + } else { + _proxy.setType(proxy_type_); + } + + QNetworkProxy::setApplicationProxy(_proxy); +} + +void GpgFrontend::UI::NetworkTab::switch_ui_enabled(bool enabled) { + ui_->proxyServerAddressEdit->setDisabled(!enabled); + ui_->portSpin->setDisabled(!enabled); + ui_->proxyTypeComboBox->setDisabled(!enabled); + ui_->usernameEdit->setDisabled(!enabled); + ui_->passwordEdit->setDisabled(!enabled); + ui_->checkProxyConnectionButton->setDisabled(!enabled); + if (!enabled) proxy_type_ = QNetworkProxy::NoProxy; +} + +void GpgFrontend::UI::NetworkTab::switch_ui_proxy_type( + const QString &type_text) { + if (type_text == "HTTP") { + ui_->proxyServerAddressEdit->setDisabled(false); + ui_->portSpin->setDisabled(false); + ui_->usernameEdit->setDisabled(false); + ui_->passwordEdit->setDisabled(false); + proxy_type_ = QNetworkProxy::HttpProxy; + } else if (type_text == "Socks5") { + ui_->proxyServerAddressEdit->setDisabled(false); + ui_->portSpin->setDisabled(false); + ui_->usernameEdit->setDisabled(false); + ui_->passwordEdit->setDisabled(false); + proxy_type_ = QNetworkProxy::Socks5Proxy; + } else { + ui_->proxyServerAddressEdit->setDisabled(true); + ui_->portSpin->setDisabled(true); + ui_->usernameEdit->setDisabled(true); + ui_->passwordEdit->setDisabled(true); + proxy_type_ = QNetworkProxy::DefaultProxy; + } +} diff --git a/src/ui/dialog/settings/SettingsNetwork.h b/src/ui/dialog/settings/SettingsNetwork.h new file mode 100644 index 00000000..d4c0d00d --- /dev/null +++ b/src/ui/dialog/settings/SettingsNetwork.h @@ -0,0 +1,94 @@ +/** + * 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_SETTINGSNETWORK_H +#define GPGFRONTEND_SETTINGSNETWORK_H + +#include "ui/GpgFrontendUI.h" + +class Ui_NetworkSettings; + +namespace GpgFrontend::UI { +class NetworkTab : public QWidget { + Q_OBJECT + + public: + /** + * @brief Construct a new Network Tab object + * + * @param parent + */ + explicit NetworkTab(QWidget* parent = nullptr); + + /** + * @brief Set the Settings object + * + */ + void SetSettings(); + + /** + * @brief + * + */ + void ApplySettings(); + + private slots: + + /** + * @brief + * + */ + void slot_test_proxy_connection_result(); + + private: + std::shared_ptr<Ui_NetworkSettings> ui_; ///< + QNetworkProxy::ProxyType proxy_type_ = QNetworkProxy::HttpProxy; ///< + + /** + * @brief + * + */ + void apply_proxy_settings(); + + /** + * @brief + * + * @param enabled + */ + void switch_ui_enabled(bool enabled); + + /** + * @brief + * + * @param type_text + */ + void switch_ui_proxy_type(const QString& type_text); +}; +} // namespace GpgFrontend::UI + +#endif // GPGFRONTEND_SETTINGSNETWORK_H |