diff options
author | Saturneric <[email protected]> | 2023-01-07 14:20:07 +0000 |
---|---|---|
committer | Saturneric <[email protected]> | 2023-01-07 14:20:07 +0000 |
commit | 225d435423888c38e0c75f63c1d7a41283839bd5 (patch) | |
tree | 4c575375dde858e9391e585f9bf47d65090c70cc | |
parent | fix: continue to solve ubuntu 18.04 build issues (diff) | |
download | GpgFrontend-225d435423888c38e0c75f63c1d7a41283839bd5.tar.gz GpgFrontend-225d435423888c38e0c75f63c1d7a41283839bd5.zip |
feat: add advanced gnupg operations
1.fix some issues
2. add advanced gnupg operations
3. add configurations information in Gnupg Help Tab
Diffstat (limited to '')
27 files changed, 836 insertions, 201 deletions
diff --git a/src/core/GpgContext.cpp b/src/core/GpgContext.cpp index 7ebd9fa9..59e21b84 100644 --- a/src/core/GpgContext.cpp +++ b/src/core/GpgContext.cpp @@ -31,11 +31,8 @@ #include <gpg-error.h> #include <gpgme.h> -#include <functional> -#include <string> -#include <utility> - #include "GpgConstants.h" +#include "core/function/gpg/GpgCommandExecutor.h" #ifdef _WIN32 #include <windows.h> @@ -240,6 +237,168 @@ gpgme_error_t GpgContext::test_status_cb(void *hook, const char *keyword, return GPG_ERR_NO_ERROR; } +const GpgInfo &GpgContext::GetInfo(bool refresh) { + if (!extend_info_loaded_ || refresh) { + GpgCommandExecutor::GetInstance().Execute( + info_.GpgConfPath, {"--list-components"}, + [=](int exit_code, const std::string &p_out, const std::string &p_err) { + LOG(INFO) << "gpgconf components exit_code" << exit_code + << "process stdout size" << p_out.size(); + + if (exit_code != 0) { + LOG(ERROR) << "gpgconf execute error, process stderr:" << p_err + << ", process stdout:" << p_out; + return; + } + + auto &components_info = info_.ComponentsInfo; + components_info["gpgme"] = {"GPG Made Easy", info_.GpgMEVersion, + _("Embedded In")}; + components_info["gpgconf"] = {"GPG Configure", "/", + info_.GpgConfPath}; + + std::vector<std::string> line_split_list; + boost::split(line_split_list, p_out, boost::is_any_of("\n")); + + for (const auto &line : line_split_list) { + std::vector<std::string> info_split_list; + boost::split(info_split_list, line, boost::is_any_of(":")); + LOG(INFO) << "gpgconf info line" << line << "info size" + << info_split_list.size(); + + if (info_split_list.size() != 3) continue; + + auto component_name = info_split_list[0]; + if (component_name == "gpg") { + components_info[component_name] = { + info_split_list[1], info_.GnupgVersion, info_split_list[2]}; + } else { + components_info[component_name] = {info_split_list[1], "/", + info_split_list[2]}; + } + } + }); + + GpgCommandExecutor::GetInstance().Execute( + info_.GpgConfPath, {"--list-dirs"}, + [=](int exit_code, const std::string &p_out, const std::string &p_err) { + LOG(INFO) << "gpgconf configurations exit_code" << exit_code + << "process stdout size" << p_out.size(); + + if (exit_code != 0) { + LOG(ERROR) << "gpgconf execute error, process stderr:" << p_err + << ", process stdout:" << p_out; + return; + } + + auto &configurations_info = info_.ConfigurationsInfo; + + std::vector<std::string> line_split_list; + boost::split(line_split_list, p_out, boost::is_any_of("\n")); + + for (const auto &line : line_split_list) { + std::vector<std::string> info_split_list; + boost::split(info_split_list, line, boost::is_any_of(":")); + LOG(INFO) << "gpgconf info line" << line << "info size" + << info_split_list.size(); + + if (info_split_list.size() != 2) continue; + + auto configuration_name = info_split_list[0]; + configurations_info[configuration_name] = {info_split_list[1]}; + } + }); + + for (const auto &component : info_.ComponentsInfo) { + LOG(INFO) << "gpgconf check options ready" + << "component" << component.first; + + if (component.first == "gpgme" || component.first == "gpgconf") continue; + + GpgCommandExecutor::GetInstance().Execute( + info_.GpgConfPath, {"--check-options", component.first}, + [=](int exit_code, const std::string &p_out, + const std::string &p_err) { + LOG(INFO) << "gpgconf options exit_code" << exit_code + << "process stdout size" << p_out.size() << "component" + << component.first; + + if (exit_code != 0) { + LOG(ERROR) << "gpgconf execute error, process stderr:" << p_err + << ", process stdout:" << p_out; + return; + } + + auto &options_info = info_.OptionsInfo; + + std::vector<std::string> line_split_list; + boost::split(line_split_list, p_out, boost::is_any_of("\n")); + + for (const auto &line : line_split_list) { + std::vector<std::string> info_split_list; + boost::split(info_split_list, line, boost::is_any_of(":")); + + LOG(INFO) << "gpgconf info line" << line << "info size" + << info_split_list.size(); + + if (info_split_list.size() != 6) continue; + + auto configuration_name = info_split_list[0]; + options_info[configuration_name] = { + info_split_list[1], info_split_list[2], info_split_list[3], + info_split_list[4], info_split_list[5]}; + } + }); + } + + for (const auto &component : info_.ComponentsInfo) { + LOG(INFO) << "gpgconf list options ready" + << "component" << component.first; + + if (component.first == "gpgme" || component.first == "gpgconf") continue; + + GpgCommandExecutor::GetInstance().Execute( + info_.GpgConfPath, {"--list-options", component.first}, + [=](int exit_code, const std::string &p_out, + const std::string &p_err) { + LOG(INFO) << "gpgconf options exit_code" << exit_code + << "process stdout size" << p_out.size() << "component" + << component.first; + + if (exit_code != 0) { + LOG(ERROR) << "gpgconf execute error, process stderr:" << p_err + << ", process stdout:" << p_out; + return; + } + + auto &available_options_info = info_.AvailableOptionsInfo; + + std::vector<std::string> line_split_list; + boost::split(line_split_list, p_out, boost::is_any_of("\n")); + + for (const auto &line : line_split_list) { + std::vector<std::string> info_split_list; + boost::split(info_split_list, line, boost::is_any_of(":")); + + LOG(INFO) << "gpgconf info line" << line << "info size" + << info_split_list.size(); + + if (info_split_list.size() != 10) continue; + + auto configuration_name = info_split_list[0]; + available_options_info[configuration_name] = { + info_split_list[1], info_split_list[2], info_split_list[3], + info_split_list[4], info_split_list[5], info_split_list[6], + info_split_list[7], info_split_list[8], info_split_list[9]}; + } + }); + } + + extend_info_loaded_ = true; + } + return info_; +} + void GpgContext::_ctx_ref_deleter::operator()(gpgme_ctx_t _ctx) { if (_ctx != nullptr) gpgme_release(_ctx); } diff --git a/src/core/GpgContext.h b/src/core/GpgContext.h index e1f1bda4..12a0fe1c 100644 --- a/src/core/GpgContext.h +++ b/src/core/GpgContext.h @@ -92,7 +92,7 @@ class GPGFRONTEND_CORE_EXPORT GpgContext * * @return const GpgInfo& */ - [[nodiscard]] const GpgInfo& GetInfo() const { return info_; } + [[nodiscard]] const GpgInfo& GetInfo(bool refresh = false); /** * @brief @@ -104,6 +104,7 @@ class GPGFRONTEND_CORE_EXPORT GpgContext private: GpgInfo info_; ///< GpgContextInitArgs args_; ///< + bool extend_info_loaded_ = false; /** * @brief diff --git a/src/core/GpgInfo.h b/src/core/GpgInfo.h index d0453b9f..53c5c3f5 100644 --- a/src/core/GpgInfo.h +++ b/src/core/GpgInfo.h @@ -42,9 +42,14 @@ class GpgInfo { std::string DatabasePath; ///< std::string GnupgVersion; ///< std::string GpgConfPath; ///< - std::string AssuanPath; ///< + std::string AssuanPath; ///< std::string CMSPath; ///< std::string GpgMEVersion; ///< + + std::map<std::string, std::vector<std::string>> ComponentsInfo; ///< + std::map<std::string, std::vector<std::string>> ConfigurationsInfo; ///< + std::map<std::string, std::vector<std::string>> OptionsInfo; ///< + std::map<std::string, std::vector<std::string>> AvailableOptionsInfo; ///< }; } // namespace GpgFrontend diff --git a/src/core/function/gpg/GpgAdvancedOperator.cpp b/src/core/function/gpg/GpgAdvancedOperator.cpp new file mode 100644 index 00000000..0ec447fe --- /dev/null +++ b/src/core/function/gpg/GpgAdvancedOperator.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2023. 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 + */ + +// +// Created by eric on 07.01.2023. +// + +#include "GpgAdvancedOperator.h" + +#include "core/function/gpg/GpgCommandExecutor.h" + +GpgFrontend::GpgAdvancedOperator::GpgAdvancedOperator(int channel) + : SingletonFunctionObject(channel) {} + +bool GpgFrontend::GpgAdvancedOperator::ClearGpgPasswordCache() { + bool success = false; + GpgFrontend::GpgCommandExecutor::GetInstance().Execute( + ctx_.GetInfo().GpgConfPath, {"--reload", "gpg-agent"}, + [&](int exit_code, const std::string &p_out, const std::string &p_err) { + if (exit_code == 0) { + success = true; + } + }); + + return success; +} + +bool GpgFrontend::GpgAdvancedOperator::ReloadGpgComponents() { + bool success = false; + GpgFrontend::GpgCommandExecutor::GetInstance().Execute( + ctx_.GetInfo().GpgConfPath, {"--reload"}, + [&](int exit_code, const std::string &p_out, const std::string &p_err) { + if (exit_code == 0) { + success = true; + } else { + LOG(ERROR) << "gpgconf execute error, process stderr:" << p_err + << ", process stdout:" << p_out; + return; + } + }); + + return success; +} + +bool GpgFrontend::GpgAdvancedOperator::RestartGpgComponents() { + bool success = false; + GpgFrontend::GpgCommandExecutor::GetInstance().Execute( + ctx_.GetInfo().GpgConfPath, {"--kill", "all"}, + [&](int exit_code, const std::string &p_out, const std::string &p_err) { + if (exit_code == 0) { + success = true; + } else { + LOG(ERROR) << "gpgconf execute error, process stderr:" << p_err + << ", process stdout:" << p_out; + return; + } + }); + + GpgFrontend::GpgCommandExecutor::GetInstance().Execute( + ctx_.GetInfo().GpgConfPath, {"--launch", "all"}, + [&](int exit_code, const std::string &p_out, const std::string &p_err) { + if (exit_code == 0) { + success = true; + } else { + success = false; + LOG(ERROR) << "gpgconf execute error, process stderr:" << p_err + << ", process stdout:" << p_out; + return; + } + }); + + return success; +} + +bool GpgFrontend::GpgAdvancedOperator::ResetConfigures() { + bool success = false; + GpgFrontend::GpgCommandExecutor::GetInstance().Execute( + ctx_.GetInfo().GpgConfPath, {"--apply-defaults"}, + [&](int exit_code, const std::string &p_out, const std::string &p_err) { + if (exit_code == 0) { + success = true; + } else { + LOG(ERROR) << "gpgconf execute error, process stderr:" << p_err + << ", process stdout:" << p_out; + return; + } + }); + + return success; +} diff --git a/src/core/function/gpg/GpgAdvancedOperator.h b/src/core/function/gpg/GpgAdvancedOperator.h new file mode 100644 index 00000000..267f1614 --- /dev/null +++ b/src/core/function/gpg/GpgAdvancedOperator.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023. 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 + */ + +// +// Created by eric on 07.01.2023. +// + +#ifndef GPGFRONTEND_GPGADVANCEDOPERATOR_H +#define GPGFRONTEND_GPGADVANCEDOPERATOR_H + +#include "core/GpgConstants.h" +#include "core/GpgContext.h" +#include "core/GpgFunctionObject.h" + +namespace GpgFrontend { + +class GPGFRONTEND_CORE_EXPORT GpgAdvancedOperator + : public SingletonFunctionObject<GpgAdvancedOperator> { + public: + /** + * @brief Construct a new Basic Operator object + * + * @param channel Channel corresponding to the context + */ + explicit GpgAdvancedOperator( + int channel = SingletonFunctionObject::GetDefaultChannel()); + + bool ClearGpgPasswordCache(); + + bool ReloadGpgComponents(); + + bool RestartGpgComponents(); + + bool ResetConfigures(); + + private: + GpgContext& ctx_ = GpgContext::GetInstance( + SingletonFunctionObject::GetChannel()); ///< Corresponding context +}; + +} // namespace GpgFrontend + +#endif // GPGFRONTEND_GPGADVANCEDOPERATOR_H diff --git a/src/core/function/gpg/GpgCommandExecutor.cpp b/src/core/function/gpg/GpgCommandExecutor.cpp index 2292ed0e..0fe9b5c3 100644 --- a/src/core/function/gpg/GpgCommandExecutor.cpp +++ b/src/core/function/gpg/GpgCommandExecutor.cpp @@ -27,41 +27,119 @@ */ #include "GpgCommandExecutor.h" +#include "GpgFunctionObject.h" +#include "core/thread/TaskRunnerGetter.h" + GpgFrontend::GpgCommandExecutor::GpgCommandExecutor(int channel) : SingletonFunctionObject<GpgCommandExecutor>(channel) {} -#ifndef WINDOWS -#include <boost/asio.hpp> -#endif +void GpgFrontend::GpgCommandExecutor::Execute( + std::string cmd, std::vector<std::string> arguments, + std::function<void(int, std::string, std::string)> callback, + std::function<void(QProcess *)> interact_func) { + LOG(INFO) << "called" + << "cmd" << cmd << "arguments size" << arguments.size(); -#ifndef WINDOWS + Thread::Task::TaskCallback result_callback = + [](int rtn, Thread::Task::DataObjectPtr data_object) { + LOG(INFO) << "called"; -using boost::process::async_pipe; + if (data_object->GetObjectSize() != 4) + throw std::runtime_error("invalid data object size"); -void GpgFrontend::GpgCommandExecutor::Execute( - StringArgsRef arguments, - const std::function<void(async_pipe& in, async_pipe& out)>& interact_func) { - using namespace boost::process; + auto exit_code = data_object->PopObject<int>(); + auto process_stdout = data_object->PopObject<std::string>(); + auto process_stderr = data_object->PopObject<std::string>(); + auto callback = data_object->PopObject< + std::function<void(int, std::string, std::string)>>(); - boost::asio::io_service ios; + // call callback + callback(exit_code, process_stdout, process_stderr); + }; - std::vector<char> buf; + Thread::Task::TaskRunnable runner = + [](GpgFrontend::Thread::Task::DataObjectPtr data_object) -> int { + LOG(INFO) << "process runner called, data object size" + << data_object->GetObjectSize(); - async_pipe in_pipe_stream(ios); - async_pipe out_pipe_stream(ios); + if (data_object->GetObjectSize() != 4) + throw std::runtime_error("invalid data object size"); - child child_process(ctx_.GetInfo().AppPath.c_str(), arguments, - std_out > in_pipe_stream, std_in < out_pipe_stream); + // get arguments + auto cmd = data_object->PopObject<std::string>(); + LOG(INFO) << "get cmd" << cmd; + auto arguments = data_object->PopObject<std::vector<std::string>>(); + auto interact_func = + data_object->PopObject<std::function<void(QProcess *)>>(); - boost::asio::async_read( - in_pipe_stream, boost::asio::buffer(buf), - [&](const boost::system::error_code& ec, std::size_t size) { - interact_func(in_pipe_stream, out_pipe_stream); - }); + auto *cmd_process = new QProcess(); + cmd_process->setProcessChannelMode(QProcess::MergedChannels); - ios.run(); - child_process.wait(); - child_process.exit_code(); -} + QObject::connect(cmd_process, &QProcess::started, + []() -> void { LOG(INFO) << "process started"; }); + QObject::connect( + cmd_process, &QProcess::readyReadStandardOutput, + [interact_func, cmd_process]() { interact_func(cmd_process); }); + QObject::connect(cmd_process, &QProcess::errorOccurred, [=]() { + LOG(ERROR) << "error in executing command:" << cmd; + }); + QObject::connect(cmd_process, + qOverload<int, QProcess::ExitStatus>(&QProcess::finished), + [=](int, QProcess::ExitStatus status) { + if (status == QProcess::NormalExit) + LOG(INFO) << "succeed in executing command:" << cmd; + else + LOG(WARNING) << "error in executing command:" << cmd; + }); + + cmd_process->setProgram(QString::fromStdString(cmd)); + + QStringList q_arguments; + for (const auto &argument : arguments) + q_arguments.append(QString::fromStdString(argument)); + cmd_process->setArguments(q_arguments); + + LOG(INFO) << "process execute ready"; + + cmd_process->start(); + cmd_process->waitForFinished(30); + + std::string process_stdout = + cmd_process->readAllStandardOutput().toStdString(), + process_stderr = + cmd_process->readAllStandardError().toStdString(); + int exit_code = cmd_process->exitCode(); -#endif + cmd_process->close(); + cmd_process->deleteLater(); + + // transfer result + data_object->AppendObject(std::move(process_stderr)); + data_object->AppendObject(std::move(process_stdout)); + data_object->AppendObject(std::move(exit_code)); + + return 0; + }; + + // data transfer into task + auto data_object = std::make_shared<Thread::Task::DataObject>(); + data_object->AppendObject(std::move(callback)); + data_object->AppendObject(std::move(interact_func)); + data_object->AppendObject(std::move(arguments)); + data_object->AppendObject(std::move(cmd)); + + auto *process_task = new GpgFrontend::Thread::Task( + std::move(runner), std::move(result_callback), data_object); + + QEventLoop looper; + QObject::connect(process_task, &Thread::Task::SignalTaskFinished, &looper, + &QEventLoop::quit); + + GpgFrontend::Thread::TaskRunnerGetter::GetInstance() + .GetTaskRunner(Thread::TaskRunnerGetter::kTaskRunnerType_External_Process) + ->PostTask(process_task); + + // block until task finished + // this is to keep reference vaild until task finished + looper.exec(); +} diff --git a/src/core/function/gpg/GpgCommandExecutor.h b/src/core/function/gpg/GpgCommandExecutor.h index 40ee22df..c597feac 100644 --- a/src/core/function/gpg/GpgCommandExecutor.h +++ b/src/core/function/gpg/GpgCommandExecutor.h @@ -35,6 +35,7 @@ #include "core/GpgContext.h" #include "core/GpgFunctionObject.h" +#include "core/thread/Task.h" namespace GpgFrontend { @@ -53,19 +54,17 @@ class GPGFRONTEND_CORE_EXPORT GpgCommandExecutor explicit GpgCommandExecutor( int channel = SingletonFunctionObject::GetDefaultChannel()); -#ifndef WINDOWS - /** * @brief Excuting an order * * @param arguments Command parameters * @param interact_func Command answering function */ - void Execute(StringArgsRef arguments, - const std::function<void(boost::process::async_pipe &in, - boost::process::async_pipe &out)> - &interact_func); -#endif + void Execute( + std::string cmd, std::vector<std::string> arguments, + std::function<void(int, std::string, std::string)> callback = + [](int, std::string, std::string) {}, + std::function<void(QProcess *)> interact_func = [](QProcess *) {}); private: GpgContext &ctx_ = GpgContext::GetInstance( diff --git a/src/core/function/gpg/GpgKeyOpera.cpp b/src/core/function/gpg/GpgKeyOpera.cpp index 0839c132..08103bbb 100644 --- a/src/core/function/gpg/GpgKeyOpera.cpp +++ b/src/core/function/gpg/GpgKeyOpera.cpp @@ -103,59 +103,7 @@ GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::SetExpire( * @return the process doing this job */ void GpgFrontend::GpgKeyOpera::GenerateRevokeCert( - const GpgKey& key, const std::string& output_file_name) { - auto args = std::vector<std::string>{"--no-tty", - "--command-fd", - "0", - "--status-fd", - "1", - "-o", - output_file_name, - "--gen-revoke", - key.GetFingerprint()}; - - using boost::asio::async_write; - using boost::process::async_pipe; -#ifndef WINDOWS - GpgCommandExecutor::GetInstance().Execute( - args, [](async_pipe& in, async_pipe& out) -> void { - // boost::asio::streambuf buff; - // boost::asio::read_until(in, buff, '\n'); - // - // std::istream is(&buff); - // - // while (!is.eof()) { - // std::string line; - // is >> line; - // LOG(INFO) << "line" << line; - // boost::algorithm::trim(line); - // if (line == std::string("[GNUPG:] GET_BOOL - // gen_revoke.okay")) { - // - // } else if (line == - // std::string( - // "[GNUPG:] GET_LINE - // ask_revocation_reason.code")) { - // - // } else if (line == - // std::string( - // "[GNUPG:] GET_LINE - // ask_revocation_reason.text")) { - // - // } else if (line == - // std::string("[GNUPG:] GET_BOOL - // openfile.overwrite.okay")) { - // - // } else if (line == - // std::string( - // "[GNUPG:] GET_BOOL - // ask_revocation_reason.okay")) { - // - // } - // } - }); -#endif -} + const GpgKey& key, const std::string& output_file_name) {} /** * Generate a new key pair @@ -181,9 +129,9 @@ GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::GenerateKey( GpgError err; - LOG(INFO) << "ctx version" << ctx_.GetInfo().GnupgVersion; + LOG(INFO) << "ctx version" << ctx_.GetInfo(false).GnupgVersion; - if (ctx_.GetInfo().GnupgVersion >= "2.1.0") { + if (ctx_.GetInfo(false).GnupgVersion >= "2.1.0") { unsigned int flags = 0; if (!params->IsSubKey()) flags |= GPGME_CREATE_CERT; @@ -278,7 +226,7 @@ GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::GenerateSubkey( GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::ModifyPassword( const GpgFrontend::GpgKey& key) { - if (ctx_.GetInfo().GnupgVersion < "2.0.15") { + if (ctx_.GetInfo(false).GnupgVersion < "2.0.15") { LOG(ERROR) << _("operator not support"); return GPG_ERR_NOT_SUPPORTED; } @@ -287,7 +235,7 @@ GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::ModifyPassword( } GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::ModifyTOFUPolicy( const GpgFrontend::GpgKey& key, gpgme_tofu_policy_t tofu_policy) { - if (ctx_.GetInfo().GnupgVersion < "2.1.10") { + if (ctx_.GetInfo(false).GnupgVersion < "2.1.10") { LOG(ERROR) << _("operator not support"); return GPG_ERR_NOT_SUPPORTED; } diff --git a/src/core/thread/Task.cpp b/src/core/thread/Task.cpp index 6b1a27a1..669334d5 100644 --- a/src/core/thread/Task.cpp +++ b/src/core/thread/Task.cpp @@ -139,8 +139,8 @@ size_t GpgFrontend::Thread::Task::DataObject::GetObjectSize() { } void GpgFrontend::Thread::Task::DataObject::free_heap_ptr(Destructor *ptr) { - DLOG(TRACE) << "p_obj: " << ptr->p_obj << "destructor: " << ptr->destroy - << "DataObject:" << this; + LOG(TRACE) << "p_obj: " << ptr->p_obj << "destructor: " << ptr->destroy + << "DataObject:" << this; if (ptr->destroy != nullptr) { ptr->destroy(ptr->p_obj); } diff --git a/src/core/thread/Task.h b/src/core/thread/Task.h index c94baea6..163e1507 100644 --- a/src/core/thread/Task.h +++ b/src/core/thread/Task.h @@ -80,7 +80,8 @@ class GPGFRONTEND_CORE_EXPORT Task : public QObject, public QRunnable { void AppendObject(T &&obj) { DLOG(TRACE) << "called:" << this; auto *obj_dstr = this->get_heap_ptr(sizeof(T)); - auto *ptr_heap = new ((void *)obj_dstr->p_obj) T(std::move(obj)); + new ((void *)obj_dstr->p_obj) T(std::forward<T>(obj)); + if (std::is_class_v<T>) { auto destructor = [](const void *x) { static_cast<const T *>(x)->~T(); @@ -89,7 +90,8 @@ class GPGFRONTEND_CORE_EXPORT Task : public QObject, public QRunnable { } else { obj_dstr->destroy = nullptr; } - data_objects_.push(std::move(obj_dstr)); + + data_objects_.push(obj_dstr); } /** @@ -169,16 +171,16 @@ class GPGFRONTEND_CORE_EXPORT Task : public QObject, public QRunnable { * * @param callback The callback function to be executed. */ - Task(TaskCallback callback, DataObjectPtr data_object = nullptr); + explicit Task(TaskCallback callback, DataObjectPtr data_object = nullptr); /** * @brief Construct a new Task object * * @param runnable */ - Task( + explicit Task( TaskRunnable runnable, - TaskCallback callback = [](int, std::shared_ptr<DataObject>) {}, + TaskCallback callback = [](int, const std::shared_ptr<DataObject> &) {}, DataObjectPtr data = nullptr); /** diff --git a/src/core/thread/TaskRunner.cpp b/src/core/thread/TaskRunner.cpp index f70b2d4c..ffbeb495 100644 --- a/src/core/thread/TaskRunner.cpp +++ b/src/core/thread/TaskRunner.cpp @@ -36,10 +36,11 @@ GpgFrontend::Thread::TaskRunner::TaskRunner() = default; GpgFrontend::Thread::TaskRunner::~TaskRunner() = default; void GpgFrontend::Thread::TaskRunner::PostTask(Task* task) { + if (task == nullptr) return; + std::string uuid = task->GetUUID(); LOG(TRACE) << "Post Task" << uuid; - if (task == nullptr) return; task->setParent(nullptr); task->moveToThread(this); @@ -59,7 +60,7 @@ void GpgFrontend::Thread::TaskRunner::PostTask(Task* task) { quit(); } -void GpgFrontend::Thread::TaskRunner::run() { +[[noreturn]] void GpgFrontend::Thread::TaskRunner::run() { LOG(TRACE) << "called" << "thread id:" << QThread::currentThreadId(); while (true) { @@ -91,7 +92,7 @@ void GpgFrontend::Thread::TaskRunner::run() { task->deleteLater(); pending_tasks_.erase(task->GetUUID()); } catch (...) { - LOG(ERROR) << "TaskRunner: Unknwon Exception in Task" + LOG(ERROR) << "TaskRunner: Unknown Exception in Task" << task->GetUUID(); // destroy the task, remove the task from the pending tasks diff --git a/src/core/thread/TaskRunner.h b/src/core/thread/TaskRunner.h index f18efca6..565c123c 100644 --- a/src/core/thread/TaskRunner.h +++ b/src/core/thread/TaskRunner.h @@ -55,7 +55,7 @@ class GPGFRONTEND_CORE_EXPORT TaskRunner : public QThread { * @brief * */ - void run() override; + [[noreturn]] void run() override; public slots: diff --git a/src/core/thread/TaskRunnerGetter.h b/src/core/thread/TaskRunnerGetter.h index afbf63af..80b25c3e 100644 --- a/src/core/thread/TaskRunnerGetter.h +++ b/src/core/thread/TaskRunnerGetter.h @@ -41,6 +41,7 @@ class GPGFRONTEND_CORE_EXPORT TaskRunnerGetter kTaskRunnerType_GPG, kTaskRunnerType_IO, kTaskRunnerType_Network, + kTaskRunnerType_External_Process, }; TaskRunnerGetter(int channel = SingletonFunctionObject::GetDefaultChannel()); diff --git a/src/ui/UserInterfaceUtils.cpp b/src/ui/UserInterfaceUtils.cpp index f2659e9d..db28b312 100644 --- a/src/ui/UserInterfaceUtils.cpp +++ b/src/ui/UserInterfaceUtils.cpp @@ -213,6 +213,41 @@ void CommonUtils::SlotImportKeyFromClipboard(QWidget *parent) { cb->text(QClipboard::Clipboard).toUtf8().toStdString()); } +void CommonUtils::SlotExecuteCommand( + const std::string& cmd, + const QStringList &arguments, + const std::function<void(QProcess *)> &interact_func) { + QEventLoop looper; + auto *cmd_process = new QProcess(&looper); + cmd_process->setProcessChannelMode(QProcess::MergedChannels); + + connect(cmd_process, + qOverload<int, QProcess::ExitStatus>(&QProcess::finished), &looper, + &QEventLoop::quit); + connect(cmd_process, &QProcess::errorOccurred, &looper, &QEventLoop::quit); + connect(cmd_process, &QProcess::started, + []() -> void { LOG(INFO) << "process started"; }); + connect(cmd_process, &QProcess::readyReadStandardOutput, + [interact_func, cmd_process]() { interact_func(cmd_process); }); + connect(cmd_process, &QProcess::errorOccurred, this, [=]() -> void { + LOG(ERROR) << "Error in Process"; + }); + connect(cmd_process, + qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this, + [=](int, QProcess::ExitStatus status) { + if (status == QProcess::NormalExit) + LOG(INFO) << "succeed in executing command:" << cmd; + else + LOG(WARNING) << "error in executing command:" << cmd; + }); + + cmd_process->setProgram(QString::fromStdString(cmd)); + cmd_process->setArguments(arguments); + cmd_process->start(); + looper.exec(); +} + + void CommonUtils::SlotExecuteGpgCommand( const QStringList &arguments, const std::function<void(QProcess *)> &interact_func) { @@ -230,7 +265,7 @@ void CommonUtils::SlotExecuteGpgCommand( &WaitingDialog::deleteLater); connect(gpg_process, &QProcess::errorOccurred, &looper, &QEventLoop::quit); connect(gpg_process, &QProcess::started, - []() -> void { LOG(ERROR) << "Gpg Process Started Success"; }); + []() -> void { LOG(INFO) << "gpg process started"; }); connect(gpg_process, &QProcess::readyReadStandardOutput, [interact_func, gpg_process]() { interact_func(gpg_process); }); connect(gpg_process, &QProcess::errorOccurred, this, [=]() -> void { @@ -251,7 +286,8 @@ void CommonUtils::SlotExecuteGpgCommand( _("Finished executing command.")); }); - gpg_process->setProgram(GpgContext::GetInstance().GetInfo().AppPath.c_str()); + gpg_process->setProgram( + GpgContext::GetInstance().GetInfo(false).AppPath.c_str()); gpg_process->setArguments(arguments); gpg_process->start(); looper.exec(); diff --git a/src/ui/UserInterfaceUtils.h b/src/ui/UserInterfaceUtils.h index bcfa28d2..d11ae9a2 100644 --- a/src/ui/UserInterfaceUtils.h +++ b/src/ui/UserInterfaceUtils.h @@ -214,6 +214,17 @@ class CommonUtils : public QWidget { const QStringList& arguments, const std::function<void(QProcess*)>& interact_func); + /** + * @brief + * + * @param arguments + * @param interact_func + */ + void SlotExecuteCommand( + const std::string& cmd, + const QStringList& arguments, + const std::function<void(QProcess*)>& interact_func); + private slots: /** diff --git a/src/ui/dialog/help/AboutDialog.cpp b/src/ui/dialog/help/AboutDialog.cpp index 6b6e4356..25ace162 100644 --- a/src/ui/dialog/help/AboutDialog.cpp +++ b/src/ui/dialog/help/AboutDialog.cpp @@ -95,7 +95,9 @@ InfoTab::InfoTab(QWidget* parent) : QWidget(parent) { _("or send a mail to my mailing list at") + " <a " + "href=\"mailto:[email protected]\">[email protected]</a>." + "<br><br> " + _("Built with Qt") + " " + qVersion() + " " + _("and GPGME") + " " + - GpgFrontend::GpgContext::GetInstance().GetInfo().GpgMEVersion.c_str() + + GpgFrontend::GpgContext::GetInstance() + .GetInfo(false) + .GpgMEVersion.c_str() + "<br>" + _("Built at") + " " + BUILD_TIMESTAMP + "</center>"); auto* layout = new QGridLayout(); diff --git a/src/ui/dialog/help/GnupgTab.cpp b/src/ui/dialog/help/GnupgTab.cpp index 9d783367..8be35416 100644 --- a/src/ui/dialog/help/GnupgTab.cpp +++ b/src/ui/dialog/help/GnupgTab.cpp @@ -31,103 +31,102 @@ #include "GnupgTab.h" -#include "easylogging++.h" +#include "ui/UserInterfaceUtils.h" #include "ui_GnuPGInfo.h" GpgFrontend::UI::GnupgTab::GnupgTab(QWidget* parent) - : QWidget(parent), - ui_(std::make_shared<Ui_GnuPGInfo>()), - gpgconf_process_(new QProcess(this)) { + : QWidget(parent), ui_(std::make_shared<Ui_GnuPGInfo>()) { GpgContext& ctx = GpgContext::GetInstance(); - auto info = ctx.GetInfo(); + auto info = ctx.GetInfo(false); ui_->setupUi(this); - QStringList column_titles; - column_titles << _("Name") << _("Description") << _("Version") << _("Path"); + QStringList components_column_titles; + components_column_titles << _("Name") << _("Description") << _("Version") + << _("Path"); - ui_->conponentDetailsTable->setColumnCount(column_titles.length()); - ui_->conponentDetailsTable->setHorizontalHeaderLabels(column_titles); - ui_->conponentDetailsTable->horizontalHeader()->setStretchLastSection(false); - ui_->conponentDetailsTable->setSelectionBehavior( + ui_->componentDetailsTable->setColumnCount(components_column_titles.length()); + ui_->componentDetailsTable->setHorizontalHeaderLabels( + components_column_titles); + ui_->componentDetailsTable->horizontalHeader()->setStretchLastSection(false); + ui_->componentDetailsTable->setSelectionBehavior( QAbstractItemView::SelectRows); + QStringList configurations_column_titles; + configurations_column_titles << _("Name") << _("Path"); + + ui_->configurationDetailsTable->setColumnCount( + configurations_column_titles.length()); + ui_->configurationDetailsTable->setHorizontalHeaderLabels( + configurations_column_titles); + ui_->configurationDetailsTable->horizontalHeader()->setStretchLastSection( + false); + ui_->configurationDetailsTable->setSelectionBehavior( + QAbstractItemView::SelectRows); + + ui_->softwareDetailsTableTitle->setText(_("Software Information")); + // tableitems not editable - ui_->conponentDetailsTable->setEditTriggers( + ui_->componentDetailsTable->setEditTriggers( QAbstractItemView::NoEditTriggers); // no focus (rectangle around tableitems) // may be it should focus on whole row - ui_->conponentDetailsTable->setFocusPolicy(Qt::NoFocus); - ui_->conponentDetailsTable->setAlternatingRowColors(true); - - gpgconf_process_->start(QString::fromStdString(info.GpgConfPath), - QStringList() << "--list-components"); + ui_->componentDetailsTable->setFocusPolicy(Qt::NoFocus); + ui_->componentDetailsTable->setAlternatingRowColors(true); - connect(gpgconf_process_, - qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this, - &GnupgTab::process_components_info); + process_software_info(); } -void GpgFrontend::UI::GnupgTab::process_components_info( - int exit_code, QProcess::ExitStatus exit_status) { +void GpgFrontend::UI::GnupgTab::process_software_info() { LOG(INFO) << "called"; - GpgContext& ctx = GpgContext::GetInstance(); - auto info = ctx.GetInfo(); + auto ctx_info = GpgContext::GetInstance().GetInfo(true); - std::vector<std::vector<std::string>> components_info = { - {"gpgme", "GPG Made Easy", info.GpgMEVersion, "/"}, - {"gpgconf", "GPG Configure", "/", info.GpgConfPath}, + ui_->componentDetailsTable->setRowCount(ctx_info.ComponentsInfo.size()); - }; + int row = 0; + for (const auto& info : ctx_info.ComponentsInfo) { + if (info.second.size() != 3) continue; - if (gpgconf_process_ != nullptr) { - QString data = gpgconf_process_->readAllStandardOutput(); + auto* tmp0 = new QTableWidgetItem(QString::fromStdString(info.first)); + tmp0->setTextAlignment(Qt::AlignCenter); + ui_->componentDetailsTable->setItem(row, 0, tmp0); - std::vector<std::string> line_split_list; - boost::split(line_split_list, data.toStdString(), boost::is_any_of("\n")); + auto* tmp1 = new QTableWidgetItem(QString::fromStdString(info.second[0])); + tmp1->setTextAlignment(Qt::AlignCenter); + ui_->componentDetailsTable->setItem(row, 1, tmp1); - for (const auto& line : line_split_list) { - std::vector<std::string> info_split_list; - boost::split(info_split_list, line, boost::is_any_of(":")); - LOG(INFO) << "gpgconf info line" << line << "info size" - << info_split_list.size(); + auto* tmp2 = new QTableWidgetItem(QString::fromStdString(info.second[1])); + tmp2->setTextAlignment(Qt::AlignCenter); + ui_->componentDetailsTable->setItem(row, 2, tmp2); - if (info_split_list.size() != 3) continue; + auto* tmp3 = new QTableWidgetItem(QString::fromStdString(info.second[2])); + tmp3->setTextAlignment(Qt::AlignLeft); + ui_->componentDetailsTable->setItem(row, 3, tmp3); - if (info_split_list[0] == "gpg") { - components_info.push_back({info_split_list[0], info_split_list[1], - info.GnupgVersion, info_split_list[2]}); - } else { - components_info.push_back( - {info_split_list[0], info_split_list[1], "/", info_split_list[2]}); - } - } + row++; } - ui_->conponentDetailsTable->setRowCount(components_info.size()); + ui_->componentDetailsTable->resizeColumnsToContents(); - int row = 0; - for (const auto& info : components_info) { - if (info.size() != 4) continue; + ui_->configurationDetailsTable->setRowCount( + ctx_info.ConfigurationsInfo.size()); - auto* tmp0 = new QTableWidgetItem(QString::fromStdString(info[0])); + row = 0; + for (const auto& info : ctx_info.ConfigurationsInfo) { + if (info.second.size() != 1) continue; + + auto* tmp0 = new QTableWidgetItem(QString::fromStdString(info.first)); tmp0->setTextAlignment(Qt::AlignCenter); - ui_->conponentDetailsTable->setItem(row, 0, tmp0); + ui_->configurationDetailsTable->setItem(row, 0, tmp0); - auto* tmp1 = new QTableWidgetItem(QString::fromStdString(info[1])); + auto* tmp1 = new QTableWidgetItem(QString::fromStdString(info.second[0])); tmp1->setTextAlignment(Qt::AlignCenter); - ui_->conponentDetailsTable->setItem(row, 1, tmp1); - - auto* tmp2 = new QTableWidgetItem(QString::fromStdString(info[2])); - tmp2->setTextAlignment(Qt::AlignCenter); - ui_->conponentDetailsTable->setItem(row, 2, tmp2); - - auto* tmp3 = new QTableWidgetItem(QString::fromStdString(info[3])); - tmp3->setTextAlignment(Qt::AlignLeft); - ui_->conponentDetailsTable->setItem(row, 3, tmp3); + ui_->configurationDetailsTable->setItem(row, 1, tmp1); row++; } + + ui_->configurationDetailsTable->resizeColumnsToContents(); } diff --git a/src/ui/dialog/help/GnupgTab.h b/src/ui/dialog/help/GnupgTab.h index c143bae3..9195dee0 100644 --- a/src/ui/dialog/help/GnupgTab.h +++ b/src/ui/dialog/help/GnupgTab.h @@ -48,11 +48,9 @@ class GnupgTab : public QWidget { explicit GnupgTab(QWidget* parent = nullptr); private: - std::shared_ptr<Ui_GnuPGInfo> ui_; - QProcess* gpgconf_process_; + std::shared_ptr<Ui_GnuPGInfo> ui_; ///< - private slots: - void process_components_info(int, QProcess::ExitStatus); + void process_software_info(); }; } // namespace GpgFrontend::UI diff --git a/src/ui/dialog/settings/SettingsGeneral.cpp b/src/ui/dialog/settings/SettingsGeneral.cpp index 680ed014..7c7f254d 100644 --- a/src/ui/dialog/settings/SettingsGeneral.cpp +++ b/src/ui/dialog/settings/SettingsGeneral.cpp @@ -43,9 +43,12 @@ GeneralTab::GeneralTab(QWidget* parent) : QWidget(parent), ui_(std::make_shared<Ui_GeneralSettings>()) { ui_->setupUi(this); - ui_->saveCheckedKeysBox->setTitle(_("Save Checked Keys")); + ui_->cacheBox->setTitle(_("Cache")); ui_->saveCheckedKeysCheckBox->setText( _("Save checked private keys on exit and restore them on next start.")); + ui_->clearGpgPasswordCacheCheckBox->setText( + "Clear gpg password cache when closing GpgFrontend."); + ui_->longerKeyExpirationDateBox->setTitle(_("Longer Key Expiration Date")); ui_->longerKeyExpirationDateCheckBox->setText( _("Unlock key expiration date setting up to 30 years.")); @@ -63,6 +66,10 @@ GeneralTab::GeneralTab(QWidget* parent) "<b>" + QString(_("NOTE")) + _(": ") + "</b>" + _("GpgFrontend will restart automatically if you change the language!")); + ui_->gnupgDatabaseBox->setTitle(_("GnuPG Key Database Path")); + ui_->keyDatabseUseCustomCheckBox->setText(_("Use Custom Path")); + ui_->customKeyDatabasePathSelectButton->setText(_("Select Custom Path")); + #ifdef MULTI_LANG_SUPPORT lang_ = SettingsDialog::ListLanguages(); for (const auto& l : lang_) { @@ -136,6 +143,15 @@ void GeneralTab::SetSettings() { } try { + bool clear_gpg_password_cache = + settings.lookup("general.clear_gpg_password_cache"); + if (clear_gpg_password_cache) + ui_->clearGpgPasswordCacheCheckBox->setCheckState(Qt::Checked); + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") << _("clear_gpg_password_cache"); + } + + try { bool longer_expiration_date = settings.lookup("general.longer_expiration_date"); LOG(INFO) << "longer_expiration_date" << longer_expiration_date; @@ -223,6 +239,14 @@ void GeneralTab::ApplySettings() { general["save_key_checked"] = ui_->saveCheckedKeysCheckBox->isChecked(); } + if (!general.exists("clear_gpg_password_cache")) + general.add("clear_gpg_password_cache", libconfig::Setting::TypeBoolean) = + ui_->clearGpgPasswordCacheCheckBox->isChecked(); + else { + general["clear_gpg_password_cache"] = + ui_->saveCheckedKeysCheckBox->isChecked(); + } + if (!general.exists("non_ascii_when_export")) general.add("non_ascii_when_export", libconfig::Setting::TypeBoolean) = ui_->asciiModeCheckBox->isChecked(); @@ -265,7 +289,7 @@ void GeneralTab::slot_language_changed() { emit SignalRestartNeeded(true); } void GeneralTab::slot_update_custom_key_database_path_label(int state) { if (state != Qt::CheckState::Checked) { ui_->currentKeyDatabasePathLabel->setText(QString::fromStdString( - GpgContext::GetInstance().GetInfo().DatabasePath)); + GpgContext::GetInstance().GetInfo(false).DatabasePath)); // hide label (not necessary to show the default path) this->ui_->currentKeyDatabasePathLabel->setHidden(true); diff --git a/src/ui/main_window/MainWindow.cpp b/src/ui/main_window/MainWindow.cpp index b0273d86..a5df66f0 100644 --- a/src/ui/main_window/MainWindow.cpp +++ b/src/ui/main_window/MainWindow.cpp @@ -29,6 +29,7 @@ #include "MainWindow.h" #include "core/function/GlobalSettingStation.h" +#include "core/function/gpg/GpgAdvancedOperator.h" #include "ui/SignalStation.h" #include "ui/UserInterfaceUtils.h" #include "ui/struct/SettingsObject.h" @@ -128,6 +129,30 @@ void MainWindow::Init() noexcept { ->PostTask(version_task); } + // before application exit + connect(qApp, &QCoreApplication::aboutToQuit, this, []() { + LOG(INFO) << "about to quit process started"; + + auto &settings = GlobalSettingStation::GetInstance().GetUISettings(); + try { + bool clear_gpg_password_cache = + settings.lookup("general.clear_gpg_password_cache"); + + if (clear_gpg_password_cache) { + if (GpgFrontend::GpgAdvancedOperator::GetInstance() + .ClearGpgPasswordCache()) { + LOG(INFO) << "clear gpg password cache done"; + } else { + LOG(ERROR) << "clear gpg password cache error"; + } + } + + } catch (...) { + LOG(ERROR) << _("Setting Operation Error") + << _("clear_gpg_password_cache"); + } + }); + } catch (...) { LOG(FATAL) << _("Critical error occur while loading GpgFrontend."); QMessageBox::critical(nullptr, _("Loading Failed"), diff --git a/src/ui/main_window/MainWindow.h b/src/ui/main_window/MainWindow.h index 2e24cecd..e32a02ff 100644 --- a/src/ui/main_window/MainWindow.h +++ b/src/ui/main_window/MainWindow.h @@ -328,6 +328,7 @@ class MainWindow : public GeneralMainWindow { QMenu* file_menu_{}; ///< Submenu for file-operations QMenu* edit_menu_{}; ///< Submenu for text-operations QMenu* crypt_menu_{}; ///< Submenu for crypt-operations + QMenu* gpg_menu_{}; ///< Submenu for help-operations QMenu* help_menu_{}; ///< Submenu for help-operations QMenu* key_menu_{}; ///< Submenu for key-operations QMenu* view_menu_{}; ///< Submenu for view operations @@ -369,6 +370,10 @@ class MainWindow : public GeneralMainWindow { QAction* clean_double_line_breaks_act_{}; ///< Action to remove double line ///< breaks + QAction* clean_gpg_password_cache_act_{}; ///< + QAction* reload_components_act_{}; ///< + QAction* restart_components_act_{}; ///< + QAction* append_selected_keys_act_{}; ///< Action to append selected keys to edit QAction* copy_mail_address_to_clipboard_act_{}; ///< Action to copy mail to diff --git a/src/ui/main_window/MainWindowSlotFunction.cpp b/src/ui/main_window/MainWindowSlotFunction.cpp index f715046c..4a7f721a 100644 --- a/src/ui/main_window/MainWindowSlotFunction.cpp +++ b/src/ui/main_window/MainWindowSlotFunction.cpp @@ -36,9 +36,9 @@ #include "core/function/gpg/GpgBasicOperator.h" #include "core/function/gpg/GpgKeyGetter.h" #include "core/function/gpg/GpgKeyImportExporter.h" +#include "dialog/SignersPicker.h" #include "ui/UserInterfaceUtils.h" #include "ui/dialog/help/AboutDialog.h" -#include "dialog/SignersPicker.h" namespace GpgFrontend::UI { /** @@ -662,7 +662,7 @@ void MainWindow::SlotOpenFile(QString& path) { edit_->SlotOpenFile(path); } void MainWindow::slot_version_upgrade(const SoftwareVersion& version) { LOG(INFO) << _("Called"); - if (!version.InfoVaild()) { + if (!version.InfoValid()) { LOG(INFO) << "Invalid version info"; return; } diff --git a/src/ui/main_window/MainWindowUI.cpp b/src/ui/main_window/MainWindowUI.cpp index 1470b731..77bf2e14 100644 --- a/src/ui/main_window/MainWindowUI.cpp +++ b/src/ui/main_window/MainWindowUI.cpp @@ -27,6 +27,7 @@ */ #include "MainWindow.h" +#include "core/function/gpg/GpgAdvancedOperator.h" #include "ui/UserInterfaceUtils.h" namespace GpgFrontend::UI { @@ -241,6 +242,48 @@ void MainWindow::create_actions() { connect(open_key_management_act_, &QAction::triggered, this, &MainWindow::slot_open_key_management); + clean_gpg_password_cache_act_ = new QAction(_("Clear Password Cache"), this); + clean_gpg_password_cache_act_->setIcon(QIcon(":configure.png")); + clean_gpg_password_cache_act_->setToolTip(_("Clear Password Cache of GnuPG")); + connect(clean_gpg_password_cache_act_, &QAction::triggered, this, [=]() { + if (GpgFrontend::GpgAdvancedOperator::GetInstance() + .ClearGpgPasswordCache()) { + QMessageBox::information(this, _("Successful Operation"), + _("Clear password cache successfully")); + } else { + QMessageBox::critical(this, _("Failed Operation"), + _("Failed to clear password cache of GnuPG")); + } + }); + + reload_components_act_ = new QAction(_("Reload All Components"), this); + reload_components_act_->setIcon(QIcon(":configure.png")); + reload_components_act_->setToolTip(_("Reload All GnuPG's Components")); + connect(reload_components_act_, &QAction::triggered, this, [=]() { + if (GpgFrontend::GpgAdvancedOperator::GetInstance() + .ReloadGpgComponents()) { + QMessageBox::information(this, _("Successful Operation"), + _("Reload all the GnuPG's components successfully")); + } else { + QMessageBox::critical(this, _("Failed Operation"), + _("Failed to reload all or one of the GnuPG's component(s)")); + } + }); + + restart_components_act_ = new QAction(_("Restart All Components"), this); + restart_components_act_->setIcon(QIcon(":configure.png")); + restart_components_act_->setToolTip(_("Restart All GnuPG's Components")); + connect(restart_components_act_, &QAction::triggered, this, [=]() { + if (GpgFrontend::GpgAdvancedOperator::GetInstance() + .RestartGpgComponents()) { + QMessageBox::information(this, _("Successful Operation"), + _("Restart all the GnuPG's components successfully")); + } else { + QMessageBox::critical(this, _("Failed Operation"), + _("Failed to restart all or one of the GnuPG's component(s)")); + } + }); + /* * About Menu */ @@ -270,7 +313,7 @@ void MainWindow::create_actions() { check_update_act_->setIcon(QIcon(":help.png")); check_update_act_->setToolTip(_("Check for updates")); connect(check_update_act_, &QAction::triggered, this, - [=]() { new AboutDialog(2, this); }); + [=]() { new AboutDialog(3, this); }); start_wizard_act_ = new QAction(_("Open Wizard"), this); start_wizard_act_->setToolTip(_("Open the wizard")); @@ -372,6 +415,12 @@ void MainWindow::create_menus() { import_key_menu_->addAction(import_key_from_key_server_act_); key_menu_->addAction(open_key_management_act_); + gpg_menu_ = menuBar()->addMenu(_("GnuPG")); + gpg_menu_->addAction(clean_gpg_password_cache_act_); + gpg_menu_->addSeparator(); + gpg_menu_->addAction(reload_components_act_); + gpg_menu_->addAction(restart_components_act_); + steganography_menu_ = menuBar()->addMenu(_("Steganography")); steganography_menu_->addAction(cut_pgp_header_act_); steganography_menu_->addAction(add_pgp_header_act_); diff --git a/src/ui/struct/SoftwareVersion.cpp b/src/ui/struct/SoftwareVersion.cpp index ecccf7c0..0059715a 100644 --- a/src/ui/struct/SoftwareVersion.cpp +++ b/src/ui/struct/SoftwareVersion.cpp @@ -27,3 +27,70 @@ */ #include "SoftwareVersion.h" + +int GpgFrontend::UI::SoftwareVersion::version_compare(const std::string& a, + const std::string& b) { + + auto temp_a = a, temp_b = b; + + if (!temp_a.empty() && temp_a.front() == 'v') { + temp_a = temp_a.erase(0, 1); + LOG(INFO) << "real version a" << temp_a; + } + + if (!temp_b.empty() && temp_b.front() == 'v') { + temp_b.erase(0, 1); + LOG(INFO) << "real version b" << temp_b; + } + + // First, split the string. + std::vector<std::string> va, vb; + boost::split(va, temp_a, boost::is_any_of(".")); + boost::split(vb, temp_b, boost::is_any_of(".")); + + // Compare the numbers step by step, but only as deep as the version + // with the least elements allows. + const int depth = + std::min(static_cast<int>(va.size()), static_cast<int>(vb.size())); + int ia = 0, ib = 0; + for (int i = 0; i < depth; ++i) { + try { + ia = boost::lexical_cast<int>(va[i]); + ib = boost::lexical_cast<int>(vb[i]); + } catch (boost::bad_lexical_cast& ignored) { + break; + } + if (ia != ib) break; + } + + // Return the required number. + if (ia > ib) + return 1; + else if (ia < ib) + return -1; + else { + // In case of equal versions, assumes that the version + // with the most elements is the highest version. + if (va.size() > vb.size()) + return 1; + else if (va.size() < vb.size()) + return -1; + } + + // Everything is equal, return 0. + return 0; +} + +bool GpgFrontend::UI::SoftwareVersion::NeedUpgrade() const { + return load_info_done && !latest_prerelease && !latest_draft && + version_compare(current_version, latest_version) < 0; +} + +bool GpgFrontend::UI::SoftwareVersion::VersionWithDrawn() const { + return load_info_done && !current_version_found && current_prerelease && + !current_draft; +} + +bool GpgFrontend::UI::SoftwareVersion::CurrentVersionReleased() const { + return load_info_done && current_version_found; +} diff --git a/src/ui/struct/SoftwareVersion.h b/src/ui/struct/SoftwareVersion.h index da93f8c6..942efec9 100644 --- a/src/ui/struct/SoftwareVersion.h +++ b/src/ui/struct/SoftwareVersion.h @@ -54,7 +54,7 @@ struct SoftwareVersion { * @return true * @return false */ - [[nodiscard]] bool InfoVaild() const { return load_info_done; } + [[nodiscard]] bool InfoValid() const { return load_info_done; } /** * @brief @@ -62,10 +62,7 @@ struct SoftwareVersion { * @return true * @return false */ - [[nodiscard]] bool NeedUpgrade() const { - return load_info_done && !latest_prerelease && !latest_draft && - current_version < latest_version; - } + [[nodiscard]] bool NeedUpgrade() const; /** * @brief @@ -73,10 +70,7 @@ struct SoftwareVersion { * @return true * @return false */ - [[nodiscard]] bool VersionWithDrawn() const { - return load_info_done && !current_version_found && current_prerelease && - !current_draft; - } + [[nodiscard]] bool VersionWithDrawn() const; /** * @brief @@ -84,9 +78,11 @@ struct SoftwareVersion { * @return true * @return false */ - [[nodiscard]] bool CurrentVersionReleased() const { - return load_info_done && current_version_found; - } + [[nodiscard]] bool CurrentVersionReleased() const; + + private: + + static int version_compare(const std::string& a, const std::string& b); }; } // namespace GpgFrontend::UI diff --git a/ui/GeneralSettings.ui b/ui/GeneralSettings.ui index 4121e762..678f02a9 100644 --- a/ui/GeneralSettings.ui +++ b/ui/GeneralSettings.ui @@ -37,9 +37,9 @@ </widget> </item> <item> - <widget class="QGroupBox" name="saveCheckedKeysBox"> + <widget class="QGroupBox" name="cacheBox"> <property name="title"> - <string>Save Checked Keys</string> + <string>Cache</string> </property> <layout class="QGridLayout" name="gridLayout_3"> <item row="0" column="0"> @@ -51,6 +51,13 @@ </property> </widget> </item> + <item> + <widget class="QCheckBox" name="clearGpgPasswordCacheCheckBox"> + <property name="text"> + <string>Clear gpg password cache when closing GpgFrontend.</string> + </property> + </widget> + </item> </layout> </item> </layout> diff --git a/ui/GnuPGInfo.ui b/ui/GnuPGInfo.ui index f907874f..3a778dc2 100644 --- a/ui/GnuPGInfo.ui +++ b/ui/GnuPGInfo.ui @@ -16,17 +16,20 @@ <layout class="QGridLayout" name="gridLayout"> <item row="0" column="0"> <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0"> + <property name="spacing"> + <number>0</number> + </property> <property name="leftMargin"> - <number>5</number> + <number>0</number> </property> <property name="topMargin"> - <number>5</number> + <number>0</number> </property> <property name="rightMargin"> - <number>5</number> + <number>0</number> </property> <property name="bottomMargin"> - <number>5</number> + <number>0</number> </property> <item> <widget class="QLabel" name="label"> @@ -64,7 +67,7 @@ </widget> </item> <item> - <widget class="QLabel" name="componentDetailsTableTitle"> + <widget class="QLabel" name="softwareDetailsTableTitle"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -72,7 +75,7 @@ </sizepolicy> </property> <property name="text"> - <string>Components Information</string> + <string>Software Information</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> @@ -83,7 +86,45 @@ </widget> </item> <item> - <widget class="QTableWidget" name="conponentDetailsTable"/> + <widget class="QTabWidget" name="tabWidget"> + <property name="currentIndex"> + <number>1</number> + </property> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>Components</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> + <widget class="QTableWidget" name="componentDetailsTable"> + <property name="sizeAdjustPolicy"> + <enum>QAbstractScrollArea::AdjustToContents</enum> + </property> + <property name="editTriggers"> + <set>QAbstractItemView::NoEditTriggers</set> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_2"> + <attribute name="title"> + <string>Configurations</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> + <widget class="QTableWidget" name="configurationDetailsTable"> + <property name="sizeAdjustPolicy"> + <enum>QAbstractScrollArea::AdjustToContents</enum> + </property> + <property name="editTriggers"> + <set>QAbstractItemView::NoEditTriggers</set> + </property> + </widget> + </item> + </layout> + </widget> + </widget> </item> </layout> </item> |