diff options
author | Saturn&Eric <[email protected]> | 2023-02-25 11:49:54 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2023-02-25 11:49:54 +0000 |
commit | af1cd680f2496629026ba27707cef2afd860f5f9 (patch) | |
tree | 78e78450893e98b8828cc41010e377c1561e5f34 /src/core/GpgContext.cpp | |
parent | fix: improve manual (diff) | |
parent | feat: use aqt to install qt in ci build (diff) | |
download | GpgFrontend-af1cd680f2496629026ba27707cef2afd860f5f9.tar.gz GpgFrontend-af1cd680f2496629026ba27707cef2afd860f5f9.zip |
Merge pull request #91 from saturneric/dev/2.0.10/main
Develop 2.1.0.1
Diffstat (limited to '')
-rw-r--r-- | src/core/GpgContext.cpp | 381 |
1 files changed, 359 insertions, 22 deletions
diff --git a/src/core/GpgContext.cpp b/src/core/GpgContext.cpp index 7ebd9fa9..a5213134 100644 --- a/src/core/GpgContext.cpp +++ b/src/core/GpgContext.cpp @@ -30,12 +30,19 @@ #include <gpg-error.h> #include <gpgme.h> +#include <unistd.h> -#include <functional> +#include <mutex> +#include <shared_mutex> #include <string> -#include <utility> -#include "GpgConstants.h" +#include "core/GpgConstants.h" +#include "core/GpgModel.h" +#include "core/common/CoreCommonUtil.h" +#include "core/function/CoreSignalStation.h" +#include "core/function/gpg/GpgCommandExecutor.h" +#include "core/thread/TaskRunnerGetter.h" +#include "thread/Task.h" #ifdef _WIN32 #include <windows.h> @@ -55,7 +62,7 @@ GpgContext::GpgContext(const GpgContextInitArgs &args) : args_(args) { if (_first) { /* Initialize the locale environment. */ - LOG(INFO) << "locale" << setlocale(LC_CTYPE, nullptr); + SPDLOG_DEBUG("locale: {}", setlocale(LC_CTYPE, nullptr)); info_.GpgMEVersion = gpgme_check_version(nullptr); gpgme_set_locale(nullptr, LC_CTYPE, setlocale(LC_CTYPE, nullptr)); #ifdef LC_MESSAGES @@ -87,15 +94,14 @@ GpgContext::GpgContext(const GpgContextInitArgs &args) : args_(args) { continue; } - LOG(INFO) << gpgme_get_protocol_name(engine_info->protocol) - << std::string(engine_info->file_name == nullptr - ? "null" - : engine_info->file_name) - << std::string(engine_info->home_dir == nullptr - ? "null" - : engine_info->home_dir) - << std::string(engine_info->version ? "null" - : engine_info->version); + SPDLOG_DEBUG( + "gpg context engine info: {} {} {} {}", + gpgme_get_protocol_name(engine_info->protocol), + std::string(engine_info->file_name == nullptr ? "null" + : engine_info->file_name), + std::string(engine_info->home_dir == nullptr ? "null" + : engine_info->home_dir), + std::string(engine_info->version ? "null" : engine_info->version)); switch (engine_info->protocol) { case GPGME_PROTOCOL_OpenPGP: @@ -137,7 +143,7 @@ GpgContext::GpgContext(const GpgContextInitArgs &args) : args_(args) { auto err = gpgme_ctx_set_engine_info(_ctx_ref.get(), GPGME_PROTOCOL_OpenPGP, info_.AppPath.c_str(), info_.DatabasePath.c_str()); - LOG(INFO) << "ctx set custom key db path:" << info_.DatabasePath; + SPDLOG_DEBUG("ctx set custom key db path: {}", info_.DatabasePath); assert(check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR); } @@ -149,20 +155,28 @@ GpgContext::GpgContext(const GpgContextInitArgs &args) : args_(args) { if (!check_passed) { this->good_ = false; - LOG(ERROR) << "Env check failed"; + SPDLOG_ERROR("env check failed"); return; } else { - LOG(INFO) << "gnupg version" << info_.GnupgVersion; - init_ctx(); + // async, init context + Thread::TaskRunnerGetter::GetInstance() + .GetTaskRunner(Thread::TaskRunnerGetter::kTaskRunnerType_GPG) + ->PostTask(new Thread::Task( + [=](Thread::Task::DataObjectPtr) -> int { + post_init_ctx(); + return 0; + }, + "post_init_ctx")); + good_ = true; } } -void GpgContext::init_ctx() { +void GpgContext::post_init_ctx() { // Set Independent Database if (info_.GnupgVersion <= "2.0.0" && args_.independent_database) { info_.DatabasePath = args_.db_path; - LOG(INFO) << "custom key db path" << info_.DatabasePath; + SPDLOG_DEBUG("custom key db path {}", info_.DatabasePath); auto err = gpgme_ctx_set_engine_info(_ctx_ref.get(), GPGME_PROTOCOL_OpenPGP, info_.AppPath.c_str(), info_.DatabasePath.c_str()); @@ -198,10 +212,18 @@ void GpgContext::init_ctx() { // for unit test if (args_.test_mode) { - LOG(INFO) << "test mode"; if (info_.GnupgVersion >= "2.1.0") SetPassphraseCb(test_passphrase_cb); gpgme_set_status_cb(*this, test_status_cb, nullptr); } + + // preload info + auto &info = GetInfo(); + + // listen passphrase input event + SetPassphraseCb(custom_passphrase_cb); + connect(this, &GpgContext::SignalNeedUserInputPassphrase, + CoreSignalStation::GetInstance(), + &CoreSignalStation::SignalNeedUserInputPassphrase); } bool GpgContext::good() const { return good_; } @@ -213,7 +235,7 @@ void GpgContext::SetPassphraseCb(gpgme_passphrase_cb_t cb) const { } gpgme_set_passphrase_cb(*this, cb, nullptr); } else { - LOG(ERROR) << "Not supported for gnupg version" << info_.GnupgVersion; + SPDLOG_ERROR("not supported for gnupg version: {}", info_.GnupgVersion); } } @@ -234,12 +256,327 @@ gpgme_error_t GpgContext::test_passphrase_cb(void *opaque, const char *uid_hint, return off == pass_len ? 0 : gpgme_error_from_errno(errno); } +gpgme_error_t GpgContext::custom_passphrase_cb(void *opaque, + const char *uid_hint, + const char *passphrase_info, + int last_was_bad, int fd) { + SPDLOG_DEBUG("custom passphrase cb called, bad times: {}", last_was_bad); + + if (last_was_bad > 3) { + SPDLOG_WARN("failure_counts is over three times"); + return gpgme_error_from_errno(GPG_ERR_CANCELED); + } + + std::string passphrase = + CoreCommonUtil::GetInstance()->GetTempCacheValue("__key_passphrase"); + // no pawword is an error situation + if (passphrase.empty()) { + // user input passphrase + SPDLOG_DEBUG("might need user to input passparase"); + passphrase = GpgContext::GetInstance().need_user_input_passphrase(); + if (passphrase.empty()) { + gpgme_io_write(fd, "\n", 1); + return gpgme_error_from_errno(GPG_ERR_CANCELED); + } + } + + // the user must at least write a newline character before returning from the + // callback. + passphrase = passphrase.append("\n"); + auto passpahrase_size = passphrase.size(); + + size_t off = 0, res = 0; + do { + res = gpgme_io_write(fd, &passphrase[off], passpahrase_size - off); + if (res > 0) off += res; + } while (res > 0 && off != passpahrase_size); + + return off == passpahrase_size ? 0 : gpgme_error_from_errno(GPG_ERR_CANCELED); +} + gpgme_error_t GpgContext::test_status_cb(void *hook, const char *keyword, const char *args) { - LOG(INFO) << "keyword" << keyword; + SPDLOG_DEBUG("keyword {}", keyword); return GPG_ERR_NO_ERROR; } +std::string GpgContext::need_user_input_passphrase() { + emit SignalNeedUserInputPassphrase(); + + std::string final_passphrase; + bool input_done = false; + SPDLOG_DEBUG("loop start to wait from user"); + auto connection = + connect(CoreSignalStation::GetInstance(), + &CoreSignalStation::SignalUserInputPassphraseDone, this, + [&](QString passphrase) { + SPDLOG_DEBUG("SignalUserInputPassphraseDone emitted"); + final_passphrase = passphrase.toStdString(); + input_done = true; + }); + while (!input_done) { + QCoreApplication::processEvents(QEventLoop::AllEvents, 800); + } + disconnect(connection); + + SPDLOG_DEBUG("lopper end"); + return final_passphrase; +} + +const GpgInfo &GpgContext::GetInfo(bool refresh) { + if (!extend_info_loaded_ || refresh) { + // try lock + std::unique_lock lock(preload_lock_); + + // check twice + if (extend_info_loaded_ && !refresh) return info_; + + SPDLOG_DEBUG("start to load extra info"); + + // get all components + GpgCommandExecutor::GetInstance().Execute( + info_.GpgConfPath, {"--list-components"}, + [=](int exit_code, const std::string &p_out, const std::string &p_err) { + SPDLOG_DEBUG( + "gpgconf components exit_code: {} process stdout size: {}", + exit_code, p_out.size()); + + if (exit_code != 0) { + SPDLOG_ERROR( + "gpgconf execute error, process stderr: {} ,process stdout: " + "{}", + p_err, p_out); + return; + } + + auto &components_info = info_.ComponentsInfo; + components_info["gpgme"] = {"GPG Made Easy", info_.GpgMEVersion, + _("Embedded In"), "/"}; + + auto gpgconf_binary_checksum = + check_binary_chacksum(info_.GpgConfPath); + components_info["gpgconf"] = {"GPG Configure", "/", info_.GpgConfPath, + gpgconf_binary_checksum.has_value() + ? gpgconf_binary_checksum.value() + : "/"}; + + 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(":")); + + if (info_split_list.size() != 3) continue; + + auto component_name = info_split_list[0]; + auto component_desc = info_split_list[1]; + auto component_path = info_split_list[2]; + auto binary_checksum = check_binary_chacksum(component_path); + + SPDLOG_DEBUG( + "gnupg component name: {} desc: {} checksum: {} path: {} ", + component_name, component_desc, + binary_checksum.has_value() ? binary_checksum.value() : "/", + component_path); + + std::string version = "/"; + + if (component_name == "gpg") { + version = info_.GnupgVersion; + } + if (component_name == "gpg-agent") { + info_.GpgAgentPath = info_split_list[2]; + } + if (component_name == "dirmngr") { + info_.DirmngrPath = info_split_list[2]; + } + if (component_name == "keyboxd") { + info_.KeyboxdPath = info_split_list[2]; + } + + { + // try lock + std::unique_lock lock(info_.Lock); + // add component info to list + components_info[component_name] = { + component_desc, version, component_path, + binary_checksum.has_value() ? binary_checksum.value() : "/"}; + } + } + }); + + SPDLOG_DEBUG("start to get dirs info"); + + GpgCommandExecutor::GetInstance().ExecuteConcurrently( + info_.GpgConfPath, {"--list-dirs"}, + [=](int exit_code, const std::string &p_out, const std::string &p_err) { + SPDLOG_DEBUG( + "gpgconf configurations exit_code: {} process stdout size: {}", + exit_code, p_out.size()); + + if (exit_code != 0) { + SPDLOG_ERROR( + "gpgconf execute error, process stderr: {} process stdout: " + "{}", + p_err, 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(":")); + SPDLOG_DEBUG("gpgconf info line: {} info size: {}", line, + info_split_list.size()); + + if (info_split_list.size() != 2) continue; + + // record gnupg home path + if (info_split_list[0] == "homedir") { + info_.GnuPGHomePath = info_split_list[1]; + } + + auto configuration_name = info_split_list[0]; + { + // try lock + std::unique_lock lock(info_.Lock); + configurations_info[configuration_name] = {info_split_list[1]}; + } + } + }); + + SPDLOG_DEBUG("start to get components info"); + + for (const auto &component : info_.ComponentsInfo) { + SPDLOG_DEBUG("gpgconf check options ready", "component", component.first); + + if (component.first == "gpgme" || component.first == "gpgconf") continue; + + GpgCommandExecutor::GetInstance().ExecuteConcurrently( + info_.GpgConfPath, {"--check-options", component.first}, + [=](int exit_code, const std::string &p_out, + const std::string &p_err) { + SPDLOG_DEBUG( + "gpgconf {} options exit_code: {} process stdout " + "size: {} ", + component.first, exit_code, p_out.size()); + + if (exit_code != 0) { + SPDLOG_ERROR( + "gpgconf {} options execute error, process " + "stderr: {} , process stdout:", + component.first, p_err, 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(":")); + + SPDLOG_DEBUG("component {} options line: {} info size: {}", + component.first, line, info_split_list.size()); + + if (info_split_list.size() != 6) continue; + + auto configuration_name = info_split_list[0]; + { + // try lock + std::unique_lock lock(info_.Lock); + options_info[configuration_name] = { + info_split_list[1], info_split_list[2], info_split_list[3], + info_split_list[4], info_split_list[5]}; + } + } + }); + } + + SPDLOG_DEBUG("start to get avaliable component options info"); + + for (const auto &component : info_.ComponentsInfo) { + SPDLOG_DEBUG("gpgconf list options ready", "component", component.first); + + if (component.first == "gpgme" || component.first == "gpgconf") continue; + + GpgCommandExecutor::GetInstance().ExecuteConcurrently( + info_.GpgConfPath, {"--list-options", component.first}, + [=](int exit_code, const std::string &p_out, + const std::string &p_err) { + SPDLOG_DEBUG( + "gpgconf {} avaliable options exit_code: {} process stdout " + "size: {} ", + component.first, exit_code, p_out.size()); + + if (exit_code != 0) { + SPDLOG_ERROR( + "gpgconf {} avaliable options execute error, process stderr: " + "{} , process stdout:", + component.first, p_err, 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(":")); + + SPDLOG_DEBUG( + "component {} avaliable options line: {} info size: {}", + component.first, line, info_split_list.size()); + + if (info_split_list.size() != 10) continue; + + auto configuration_name = info_split_list[0]; + { + // try lock + std::unique_lock lock(info_.Lock); + 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; + } + + // ensure nothing is changing now + std::shared_lock lock(preload_lock_); + return info_; +} + +std::optional<std::string> GpgContext::check_binary_chacksum( + std::filesystem::path path) { + QFile f(QString::fromStdString(path.u8string())); + if (!f.open(QFile::ReadOnly)) return {}; + + // read all data from file + auto buffer = f.readAll(); + f.close(); + + auto hash_md5 = QCryptographicHash(QCryptographicHash::Md5); + // md5 + hash_md5.addData(buffer); + auto md5 = hash_md5.result().toHex().toStdString(); + SPDLOG_DEBUG("md5 {}", md5); + + return md5.substr(0, 6); +} + void GpgContext::_ctx_ref_deleter::operator()(gpgme_ctx_t _ctx) { if (_ctx != nullptr) gpgme_release(_ctx); } |