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
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> |