aboutsummaryrefslogtreecommitdiffstats
path: root/src/ui/import_export/KeyServerImportDialog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/import_export/KeyServerImportDialog.cpp')
-rw-r--r--src/ui/import_export/KeyServerImportDialog.cpp607
1 files changed, 607 insertions, 0 deletions
diff --git a/src/ui/import_export/KeyServerImportDialog.cpp b/src/ui/import_export/KeyServerImportDialog.cpp
new file mode 100644
index 00000000..62622d14
--- /dev/null
+++ b/src/ui/import_export/KeyServerImportDialog.cpp
@@ -0,0 +1,607 @@
+/**
+ * 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 <utility>
+
+#include "gpg/function/GpgKeyImportExporter.h"
+#include "ui/SignalStation.h"
+#include "ui/settings/GlobalSettingStation.h"
+
+namespace GpgFrontend::UI {
+
+KeyServerImportDialog::KeyServerImportDialog(bool automatic, QWidget* parent)
+ : QDialog(parent), m_automatic_(automatic) {
+ // Layout for messagebox
+ auto* messageLayout = new QHBoxLayout;
+
+ if (automatic) {
+ setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
+ } else {
+ // Buttons
+ close_button_ = create_button(_("Close"), SLOT(close()));
+ import_button_ = create_button(_("Import ALL"), SLOT(slot_import()));
+ import_button_->setDisabled(true);
+ search_button_ = create_button(_("Search"), SLOT(slot_search()));
+
+ // Line edit for search string
+ search_label_ = new QLabel(QString(_("Search String")) + _(": "));
+ search_line_edit_ = new QLineEdit();
+
+ // combobox for keyserverlist
+ 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);
+
+ messageLayout->addWidget(icon_);
+ messageLayout->addWidget(message_);
+ messageLayout->addStretch();
+ }
+
+ // Network Waiting
+ waiting_bar_ = new QProgressBar();
+ waiting_bar_->setVisible(false);
+ waiting_bar_->setRange(0, 0);
+ waiting_bar_->setFixedWidth(200);
+ messageLayout->addWidget(waiting_bar_);
+
+ auto* mainLayout = new QGridLayout;
+
+ // 自动化调用界面布局
+ if (automatic) {
+ mainLayout->addLayout(messageLayout, 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(messageLayout, 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);
+ } else {
+ auto pos = QPoint(150, 150);
+ LOG(INFO) << "parent" << parent;
+ if (parent) pos += parent->pos();
+ LOG(INFO) << "pos default" << pos.x() << pos.y();
+ auto size = QSize(800, 500);
+
+ try {
+ auto& settings = GlobalSettingStation::GetInstance().GetUISettings();
+
+ int x, y, width, height;
+ x = settings.lookup("window.import_from_keyserver.position.x");
+ y = settings.lookup("window.import_from_keyserver.position.y");
+ width = settings.lookup("window.import_from_keyserver.size.width");
+ height = settings.lookup("window.import_from_keyserver.size.height");
+ pos = QPoint(x, y);
+ size = QSize(width, height);
+
+ } catch (...) {
+ LOG(WARNING) << "cannot read pos or size from settings";
+ }
+
+ this->resize(size);
+ this->move(pos);
+ }
+
+ this->setModal(true);
+
+ connect(this, SIGNAL(SignalKeyImported()), SignalStation::GetInstance(),
+ SIGNAL(KeyDatabaseRefresh()));
+
+ // save window pos and size to configure file
+ connect(this, SIGNAL(finished(int)), this, SLOT(slot_save_window_state()));
+}
+
+QPushButton* KeyServerImportDialog::create_button(const QString& text,
+ const char* member) {
+ auto* button = new QPushButton(text);
+ connect(button, SIGNAL(clicked()), this, member);
+ return button;
+}
+
+QComboBox* KeyServerImportDialog::create_comboBox() {
+ auto* comboBox = new QComboBox;
+ comboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+
+ auto& settings = GlobalSettingStation::GetInstance().GetUISettings();
+
+ try {
+ auto& server_list = settings.lookup("keyserver.server_list");
+ const auto server_list_size = server_list.getLength();
+ for (int i = 0; i < server_list_size; i++) {
+ std::string server_url = server_list[i];
+ comboBox->addItem(server_url.c_str());
+ }
+ } catch (...) {
+ LOG(ERROR) << _("Setting Operation Error") << _("server_list");
+ }
+
+ // set default keyserver in combobox
+ try {
+ std::string default_server = settings.lookup("keyserver.default_server");
+ comboBox->setCurrentText(default_server.c_str());
+ } catch (...) {
+ LOG(ERROR) << _("Setting Operation Error") << _("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_, SIGNAL(cellActivated(int, int)), this,
+ SLOT(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;
+ }
+
+ QUrl url_from_remote = key_server_combo_box_->currentText() +
+ "/pks/lookup?search=" + search_line_edit_->text() +
+ "&op=index&options=mr";
+ network_access_manager_ = new QNetworkAccessManager(this);
+ QNetworkReply* reply =
+ network_access_manager_->get(QNetworkRequest(url_from_remote));
+
+ connect(reply, SIGNAL(finished()), this, SLOT(slot_search_finished()));
+
+ 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);
+
+ while (reply->isRunning()) {
+ QApplication::processEvents();
+ }
+
+ 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);
+}
+
+void KeyServerImportDialog::slot_search_finished() {
+ LOG(INFO) << "KeyServerImportDialog::slot_search_finished Called";
+
+ auto* reply = qobject_cast<QNetworkReply*>(sender());
+
+ keys_table_->clearContents();
+ keys_table_->setRowCount(0);
+ QString first_line = QString(reply->readLine(1024));
+
+ auto error = reply->error();
+ if (error != QNetworkReply::NoError) {
+ qDebug() << "Error From Reply" << reply->errorString();
+ 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 (first_line.contains("Error")) {
+ QString text = QString(reply->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;
+ while (reply->canReadLine()) {
+ auto line_buff = reply->readLine().trimmed();
+ QString decoded =
+ QString::fromUtf8(line_buff.constData(), line_buff.size());
+ QStringList line = decoded.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(reply->readLine()).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());
+ }
+ reply->deleteLater();
+}
+
+void KeyServerImportDialog::slot_import() {
+ LOG(INFO) << _("Current Row") << keys_table_->currentRow();
+ if (keys_table_->currentRow() > -1) {
+ QString keyid = keys_table_->item(keys_table_->currentRow(), 2)->text();
+ SlotImport(QStringList(keyid), key_server_combo_box_->currentText());
+ }
+}
+
+void KeyServerImportDialog::SlotImport(const KeyIdArgsListPtr& keys) {
+ std::string target_keyserver;
+ if (key_server_combo_box_ != nullptr) {
+ target_keyserver = key_server_combo_box_->currentText().toStdString();
+ }
+ 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;
+ }
+ }
+ auto key_ids = QStringList();
+ for (const auto& key_id : *keys)
+ key_ids.append(QString::fromStdString(key_id));
+ SlotImport(key_ids, QUrl(target_keyserver.c_str()));
+}
+
+void KeyServerImportDialog::SlotImport(const QStringList& keyIds,
+ const QUrl& keyserverUrl) {
+ for (const auto& keyId : keyIds) {
+ QUrl req_url(keyserverUrl.scheme() + "://" + keyserverUrl.host() +
+ "/pks/lookup?op=get&search=0x" + keyId + "&options=mr");
+
+ LOG(INFO) << "request url" << req_url.toString().toStdString();
+ auto manager = new QNetworkAccessManager(this);
+
+ QNetworkReply* reply = manager->get(QNetworkRequest(req_url));
+ connect(reply, &QNetworkReply::finished, this,
+ [&, keyId]() { this->slot_import_finished(keyId); });
+ LOG(INFO) << "loading start";
+ set_loading(true);
+ while (reply->isRunning()) QApplication::processEvents();
+ set_loading(false);
+ LOG(INFO) << "loading done";
+ }
+}
+
+void KeyServerImportDialog::slot_import_finished(const QString& keyid) {
+ LOG(INFO) << _("Called");
+
+ auto* reply = qobject_cast<QNetworkReply*>(sender());
+
+ QByteArray key = reply->readAll();
+
+ auto error = reply->error();
+ if (error != QNetworkReply::NoError) {
+ LOG(ERROR) << "Error From Reply" << reply->errorString().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, _("Public key Not Found"),
+ QString(_("Public key fingerprint %1 not found in the Keyserver"))
+ .arg(keyid));
+ 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;
+ }
+
+ reply->deleteLater();
+
+ this->import_keys(std::make_unique<ByteArray>(key.constData(), key.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));
+ 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, _parent);
+ dialog->exec();
+ }
+}
+
+void KeyServerImportDialog::set_loading(bool status) {
+ waiting_bar_->setVisible(status);
+ if (!m_automatic_) {
+ icon_->setVisible(!status);
+ message_->setVisible(!status);
+ }
+}
+
+KeyServerImportDialog::KeyServerImportDialog(QWidget* parent)
+ : QDialog(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);
+}
+
+void KeyServerImportDialog::slot_save_window_state() {
+ LOG(INFO) << _("Called");
+
+ if (m_automatic_) return;
+
+ auto& settings =
+ GpgFrontend::UI::GlobalSettingStation::GetInstance().GetUISettings();
+
+ if (!settings.exists("window") ||
+ settings.lookup("window").getType() != libconfig::Setting::TypeGroup)
+ settings.add("window", libconfig::Setting::TypeGroup);
+
+ auto& window = settings["window"];
+
+ if (!window.exists("import_from_keyserver") ||
+ window.lookup("import_from_keyserver").getType() !=
+ libconfig::Setting::TypeGroup)
+ window.add("import_from_keyserver", libconfig::Setting::TypeGroup);
+
+ auto& import_from_keyserver = window["import_from_keyserver"];
+
+ if (!import_from_keyserver.exists("position") ||
+ import_from_keyserver.lookup("position").getType() !=
+ libconfig::Setting::TypeGroup) {
+ auto& position =
+ import_from_keyserver.add("position", libconfig::Setting::TypeGroup);
+ position.add("x", libconfig::Setting::TypeInt) = pos().x();
+ position.add("y", libconfig::Setting::TypeInt) = pos().y();
+ } else {
+ import_from_keyserver["position"]["x"] = pos().x();
+ import_from_keyserver["position"]["y"] = pos().y();
+ }
+
+ if (!import_from_keyserver.exists("size") ||
+ import_from_keyserver.lookup("size").getType() !=
+ libconfig::Setting::TypeGroup) {
+ auto& size =
+ import_from_keyserver.add("size", libconfig::Setting::TypeGroup);
+ size.add("width", libconfig::Setting::TypeInt) = QWidget::width();
+ size.add("height", libconfig::Setting::TypeInt) = QWidget::height();
+ } else {
+ import_from_keyserver["size"]["width"] = QWidget::width();
+ import_from_keyserver["size"]["height"] = QWidget::height();
+ }
+
+ GlobalSettingStation::GetInstance().SyncSettings();
+}
+
+} // namespace GpgFrontend::UI