diff options
Diffstat (limited to 'src/ui/dialog/import_export')
-rw-r--r-- | src/ui/dialog/import_export/ExportKeyPackageDialog.cpp | 145 | ||||
-rw-r--r-- | src/ui/dialog/import_export/ExportKeyPackageDialog.h | 62 | ||||
-rw-r--r-- | src/ui/dialog/import_export/KeyImportDetailDialog.cpp | 204 | ||||
-rw-r--r-- | src/ui/dialog/import_export/KeyImportDetailDialog.h | 92 | ||||
-rw-r--r-- | src/ui/dialog/import_export/KeyServerImportDialog.cpp | 550 | ||||
-rw-r--r-- | src/ui/dialog/import_export/KeyServerImportDialog.h | 182 | ||||
-rw-r--r-- | src/ui/dialog/import_export/KeyUploadDialog.cpp | 159 | ||||
-rw-r--r-- | src/ui/dialog/import_export/KeyUploadDialog.h | 83 |
8 files changed, 1477 insertions, 0 deletions
diff --git a/src/ui/dialog/import_export/ExportKeyPackageDialog.cpp b/src/ui/dialog/import_export/ExportKeyPackageDialog.cpp new file mode 100644 index 00000000..312cd946 --- /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(_("Export As Key Package")); +} 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 |