aboutsummaryrefslogtreecommitdiffstats
path: root/src/ui/dialog/import_export/KeyServerImportDialog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/dialog/import_export/KeyServerImportDialog.cpp')
-rw-r--r--src/ui/dialog/import_export/KeyServerImportDialog.cpp550
1 files changed, 550 insertions, 0 deletions
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