/** * 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 . * * 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 starting on May 12, 2021. * * SPDX-License-Identifier: GPL-3.0-or-later * */ #include "UserInterfaceUtils.h" #include #include #include "core/common/CoreCommonUtil.h" #include "core/function/FileOperator.h" #include "core/function/GlobalSettingStation.h" #include "core/function/gpg/GpgKeyGetter.h" #include "easylogging++.h" #include "ui/SignalStation.h" #include "ui/dialog/WaitingDialog.h" #include "ui/widgets/TextEdit.h" namespace GpgFrontend::UI { std::unique_ptr GpgFrontend::UI::CommonUtils::instance_ = nullptr; void show_verify_details(QWidget *parent, InfoBoardWidget *info_board, GpgError error, const GpgVerifyResult &verify_result) { // take out result info_board->ResetOptionActionsMenu(); info_board->AddOptionalAction("Show Verify Details", [=]() { VerifyDetailsDialog(parent, error, verify_result); }); } void import_unknown_key_from_keyserver( QWidget *parent, const GpgVerifyResultAnalyse &verify_res) { QMessageBox::StandardButton reply; reply = QMessageBox::question( parent, _("Public key not found locally"), _("There is no target public key content in local for GpgFrontend to " "gather enough information about this Signature. Do you want to " "import the public key from Keyserver now?"), QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { auto dialog = KeyServerImportDialog(true, parent); auto key_ids = std::make_unique(); auto *signature = verify_res.GetSignatures(); while (signature != nullptr) { LOG(INFO) << "signature fpr" << signature->fpr; key_ids->push_back(signature->fpr); signature = signature->next; } dialog.show(); dialog.SlotImport(key_ids); } } void refresh_info_board(InfoBoardWidget *info_board, int status, const std::string &report_text) { if (status < 0) info_board->SlotRefresh(QString::fromStdString(report_text), INFO_ERROR_CRITICAL); else if (status > 0) info_board->SlotRefresh(QString::fromStdString(report_text), INFO_ERROR_OK); else info_board->SlotRefresh(QString::fromStdString(report_text), INFO_ERROR_WARN); } void process_result_analyse(TextEdit *edit, InfoBoardWidget *info_board, const GpgResultAnalyse &result_analyse) { info_board->AssociateTabWidget(edit->tab_widget_); refresh_info_board(info_board, result_analyse.GetStatus(), result_analyse.GetResultReport()); } void process_result_analyse(TextEdit *edit, InfoBoardWidget *info_board, const GpgResultAnalyse &result_analyse_a, const GpgResultAnalyse &result_analyse_b) { LOG(INFO) << "process_result_analyse Started"; info_board->AssociateTabWidget(edit->tab_widget_); refresh_info_board( info_board, std::min(result_analyse_a.GetStatus(), result_analyse_b.GetStatus()), result_analyse_a.GetResultReport() + result_analyse_b.GetResultReport()); } void process_operation(QWidget *parent, const std::string &waiting_title, const std::function &func) { auto thread = QThread::create(func); QApplication::connect(thread, &QThread::finished, thread, &QThread::deleteLater); thread->start(); auto *dialog = new WaitingDialog(QString::fromStdString(waiting_title), parent); while (thread->isRunning()) { QApplication::processEvents(); } dialog->close(); } CommonUtils *CommonUtils::GetInstance() { if (instance_ == nullptr) { instance_ = std::make_unique(); } return instance_.get(); } CommonUtils::CommonUtils() : QWidget(nullptr) { LOG(INFO) << "common utils created"; connect(CoreCommonUtil::GetInstance(), &CoreCommonUtil::SignalGnupgNotInstall, this, &CommonUtils::SignalGnupgNotInstall); connect(this, &CommonUtils::SignalKeyStatusUpdated, SignalStation::GetInstance(), &SignalStation::SignalKeyDatabaseRefresh); connect(this, &CommonUtils::SignalKeyDatabaseRefreshDone, SignalStation::GetInstance(), &SignalStation::SignalKeyDatabaseRefreshDone); // directly connect to SignalKeyStatusUpdated // to avoid the delay of signal emitting // when the key database is refreshed connect(SignalStation::GetInstance(), &SignalStation::SignalKeyDatabaseRefresh, this, &CommonUtils::slot_update_key_status); connect(this, &CommonUtils::SignalGnupgNotInstall, this, []() { QMessageBox::critical( nullptr, _("ENV Loading Failed"), _("Gnupg(gpg) is not installed correctly, please follow the " "ReadME " "instructions in Github to install Gnupg and then open " "GpgFrontend.")); QCoreApplication::quit(); }); } void CommonUtils::SlotImportKeys(QWidget *parent, const std::string &in_buffer) { GpgImportInformation result = GpgKeyImportExporter::GetInstance().ImportKey( std::make_unique(in_buffer)); emit SignalKeyStatusUpdated(); new KeyImportDetailDialog(result, false, parent); } void CommonUtils::SlotImportKeyFromFile(QWidget *parent) { QString file_name = QFileDialog::getOpenFileName( this, _("Open Key"), QString(), QString(_("Key Files")) + " (*.asc *.txt);;" + _("Keyring files") + " (*.gpg);;All Files (*)"); if (!file_name.isNull()) { QByteArray key_buffer; if (!FileOperator::ReadFile(file_name, key_buffer)) { QMessageBox::critical(nullptr, _("File Open Failed"), _("Failed to open file: ") + file_name); return; } SlotImportKeys(parent, key_buffer.toStdString()); } } void CommonUtils::SlotImportKeyFromKeyServer(QWidget *parent) { auto dialog = new KeyServerImportDialog(false, parent); dialog->show(); } void CommonUtils::SlotImportKeyFromClipboard(QWidget *parent) { QClipboard *cb = QApplication::clipboard(); SlotImportKeys(parent, cb->text(QClipboard::Clipboard).toUtf8().toStdString()); } void CommonUtils::SlotExecuteGpgCommand( const QStringList &arguments, const std::function &interact_func) { QEventLoop looper; auto dialog = new WaitingDialog(_("Processing"), nullptr); dialog->show(); auto *gpg_process = new QProcess(&looper); gpg_process->setProcessChannelMode(QProcess::MergedChannels); connect(gpg_process, qOverload(&QProcess::finished), &looper, &QEventLoop::quit); connect(gpg_process, qOverload(&QProcess::finished), dialog, &WaitingDialog::deleteLater); connect(gpg_process, &QProcess::errorOccurred, &looper, &QEventLoop::quit); connect(gpg_process, &QProcess::started, []() -> void { LOG(ERROR) << "Gpg Process Started Success"; }); connect(gpg_process, &QProcess::readyReadStandardOutput, [interact_func, gpg_process]() { interact_func(gpg_process); }); connect(gpg_process, &QProcess::errorOccurred, this, [=]() -> void { LOG(ERROR) << "Error in Process"; dialog->close(); QMessageBox::critical(nullptr, _("Failure"), _("Failed to execute command.")); }); connect(gpg_process, qOverload(&QProcess::finished), this, [=](int, QProcess::ExitStatus status) { dialog->close(); if (status == QProcess::NormalExit) QMessageBox::information(nullptr, _("Success"), _("Succeed in executing command.")); else QMessageBox::information(nullptr, _("Warning"), _("Finished executing command.")); }); gpg_process->setProgram(GpgContext::GetInstance().GetInfo().AppPath.c_str()); gpg_process->setArguments(arguments); gpg_process->start(); looper.exec(); dialog->close(); dialog->deleteLater(); } void CommonUtils::SlotImportKeyFromKeyServer( const KeyIdArgsList &key_ids, const ImportCallbackFunctiopn &callback) { 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; } } auto thread = QThread::create([target_keyserver, key_ids, callback]() { QUrl target_keyserver_url(target_keyserver.c_str()); auto network_manager = std::make_unique(); // LOOP decltype(key_ids.size()) current_index = 1, all_index = key_ids.size(); for (const auto &key_id : key_ids) { // New Req Url QUrl req_url( target_keyserver_url.scheme() + "://" + target_keyserver_url.host() + "/pks/lookup?op=get&search=0x" + key_id.c_str() + "&options=mr"); LOG(INFO) << "request url" << req_url.toString().toStdString(); // Waiting for reply QNetworkReply *reply = network_manager->get(QNetworkRequest(req_url)); QEventLoop loop; connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); loop.exec(); // Get Data auto key_data = reply->readAll(); auto key_data_ptr = std::make_unique(key_data.data(), key_data.size()); // Detect status std::string status; auto error = reply->error(); if (error != QNetworkReply::NoError) { switch (error) { case QNetworkReply::ContentNotFoundError: status = _("Key Not Found"); break; case QNetworkReply::TimeoutError: status = _("Timeout"); break; case QNetworkReply::HostNotFoundError: status = _("Key Server Not Found"); break; default: status = _("Connection Error"); } } reply->deleteLater(); // Try importing GpgImportInformation result = GpgKeyImportExporter::GetInstance().ImportKey( std::move(key_data_ptr)); if (result.imported == 1) { status = _("The key has been updated"); } else { status = _("No need to update the key"); } callback(key_id, status, current_index, all_index); current_index++; } }); connect(thread, &QThread::finished, thread, &QThread::deleteLater); thread->start(); } void CommonUtils::slot_update_key_status() { LOG(INFO) << "called"; std::vector threads; // flush key cache for all GpgKeyGetter Intances. for (const auto &channel_id : GpgKeyGetter::GetAllChannelId()) { // multi threading auto *thread = QThread::create([channel_id]() { LOG(INFO) << "thread start" << "channel:" << channel_id; GpgKeyGetter::GetInstance(channel_id).FlushKeyCache(); }); thread->start(); threads.push_back(thread); } for (auto *thread : threads) { thread->wait(); thread->deleteLater(); } emit SignalKeyDatabaseRefreshDone(); LOG(INFO) << "finished"; } } // namespace GpgFrontend::UI