aboutsummaryrefslogtreecommitdiffstats
path: root/src/core/GpgContext.cpp
diff options
context:
space:
mode:
authorSaturn&Eric <[email protected]>2023-02-25 11:49:54 +0000
committerGitHub <[email protected]>2023-02-25 11:49:54 +0000
commitaf1cd680f2496629026ba27707cef2afd860f5f9 (patch)
tree78e78450893e98b8828cc41010e377c1561e5f34 /src/core/GpgContext.cpp
parentfix: improve manual (diff)
parentfeat: use aqt to install qt in ci build (diff)
downloadGpgFrontend-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.cpp381
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);
}