diff options
author | Saturneric <[email protected]> | 2022-01-23 13:17:48 +0000 |
---|---|---|
committer | Saturneric <[email protected]> | 2022-01-23 13:17:48 +0000 |
commit | b5f212842dc0c2dd6356bf49a28b5d433bc8ad17 (patch) | |
tree | b415a88c7a0d20b18fba40efa65a206b808fc22f /src/ui/import_export/KeyServerImportDialog.cpp | |
parent | <refactor>(ui): tidy up codes and comments. (diff) | |
download | GpgFrontend-b5f212842dc0c2dd6356bf49a28b5d433bc8ad17.tar.gz GpgFrontend-b5f212842dc0c2dd6356bf49a28b5d433bc8ad17.zip |
<refactor, style>(gpg, ui, project): tidy up codes and comments.
1. let GpgInfo get into namespace GpgFrontend.
2. adjust the code structure.
3. add license statement to project configuration file.
Diffstat (limited to 'src/ui/import_export/KeyServerImportDialog.cpp')
-rw-r--r-- | src/ui/import_export/KeyServerImportDialog.cpp | 607 |
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 |