diff options
author | saturneric <[email protected]> | 2024-11-22 22:06:30 +0000 |
---|---|---|
committer | saturneric <[email protected]> | 2024-11-22 22:06:30 +0000 |
commit | e16e15db09d89a997db73e313b966f95e6c59f56 (patch) | |
tree | 4f6be7375b983eda33b5faa37b9c7a3af0a57ed6 /src | |
parent | fix: correct the path to the executable of app image mode (diff) | |
download | GpgFrontend-e16e15db09d89a997db73e313b966f95e6c59f56.tar.gz GpgFrontend-e16e15db09d89a997db73e313b966f95e6c59f56.zip |
feat: rewrite core init processes and add env option
Diffstat (limited to 'src')
-rw-r--r-- | src/cmd.cpp | 112 | ||||
-rw-r--r-- | src/cmd.h | 2 | ||||
-rw-r--r-- | src/core/GpgCoreInit.cpp | 512 | ||||
-rw-r--r-- | src/core/GpgCoreInit.h | 21 | ||||
-rw-r--r-- | src/core/function/basic/ChannelObject.cpp | 5 | ||||
-rw-r--r-- | src/core/function/gpg/GpgAdvancedOperator.cpp | 15 | ||||
-rw-r--r-- | src/core/function/gpg/GpgContext.cpp | 2 | ||||
-rw-r--r-- | src/core/utils/BuildInfoUtils.cpp | 13 | ||||
-rw-r--r-- | src/core/utils/BuildInfoUtils.h | 21 | ||||
-rw-r--r-- | src/init.cpp | 28 | ||||
-rw-r--r-- | src/main.cpp | 5 | ||||
-rw-r--r-- | src/ui/GpgFrontendUIInit.cpp | 8 | ||||
-rw-r--r-- | src/ui/UserInterfaceUtils.cpp | 76 |
13 files changed, 598 insertions, 222 deletions
diff --git a/src/cmd.cpp b/src/cmd.cpp index b7642b4e..092086fc 100644 --- a/src/cmd.cpp +++ b/src/cmd.cpp @@ -34,6 +34,10 @@ #include <qstring.h> #include <qtextstream.h> +#include "core/GpgCoreInit.h" +#include "core/model/SettingsObject.h" +#include "core/module/ModuleManager.h" +#include "core/struct/settings_object/KeyDatabaseListSO.h" #include "core/utils/BuildInfoUtils.h" // GpgFrontend @@ -43,22 +47,116 @@ namespace GpgFrontend { +inline auto Tr(const char* t) -> QString { return QCoreApplication::tr(t); } + auto PrintVersion() -> int { QTextStream stream(stdout); stream << GetProjectName() << " " << GetProjectVersion() << '\n'; stream << "Copyright (©) 2021 Saturneric <[email protected]>" << '\n' - << QCoreApplication::tr( - "This is free software; see the source for copying conditions.") + << Tr("This is free software; see the source for copying conditions.") << '\n' << '\n'; - stream << QCoreApplication::tr("Build DateTime: ") + stream << Tr("Build DateTime: ") << QLocale().toString(GetProjectBuildTimestamp()) << '\n' - << QCoreApplication::tr("Build Version: ") << GetProjectBuildVersion() - << '\n' - << QCoreApplication::tr("Source Code Version: ") - << GetProjectBuildGitVersion() << '\n'; + << Tr("Build Version: ") << GetProjectBuildVersion() << '\n' + << Tr("Source Code Version: ") << GetProjectBuildGitVersion() << '\n'; + + stream << Qt::endl; + return 0; +} + +auto PrintEnvInfo() -> int { + QTextStream stream(stdout); + stream << GetProjectName() << " " << GetProjectVersion() << " " + << "Environemnt Informations:" << '\n'; + + stream << '\n'; + + stream << Tr("Qt Version: ") << GetProjectQtVersion() << '\n'; + stream << Tr("OpenSSL Version: ") << GetProjectOpenSSLVersion() << '\n'; + stream << Tr("Libarchive Version: ") << GetProjectLibarchiveVersion() << '\n'; + + stream << '\n'; + + stream << Tr("GnuPG: ") << '\n'; + stream << '\n'; + + auto init_result = InitGpgME(); + + auto gnupg_version = Module::RetrieveRTValueTypedOrDefault<>( + "core", "gpgme.ctx.gnupg_version", QString{}); + stream << Tr("GnuPG Version: ") << gnupg_version << '\n'; + + stream << Tr("GpgME Init Status: ") + << (init_result ? Tr("Success") : Tr("Failed")) << '\n'; + + auto gpgconf = Module::RetrieveRTValueTypedOrDefault<>( + "core", "gpgme.engine.gpgconf", 0); + auto openpgp = Module::RetrieveRTValueTypedOrDefault<>( + "core", "gpgme.engine.openpgp", 0); + auto cms = + Module::RetrieveRTValueTypedOrDefault<>("core", "gpgme.engine.cms", 0); + auto assuan = + Module::RetrieveRTValueTypedOrDefault<>("core", "gpgme.engine.assuan", 0); + stream << Tr("Engine 'GPGCONF' Status: ") + << (gpgconf == 1 ? Tr("Exists") : Tr("NOT Exists")) << '\n'; + stream << Tr("Engine 'OPENPGP' Status: ") + << (openpgp == 1 ? Tr("Exists") : Tr("NOT Exists")) << '\n'; + stream << Tr("Engine 'CMS' Status: ") + << (cms == 1 ? Tr("Exists") : Tr("NOT Exists")) << '\n'; + stream << Tr("Engine 'ASSUAN' Status: ") + << (assuan == 1 ? Tr("Exists") : Tr("NOT Exists")) << '\n'; + + stream << '\n'; + + InitBasicPath(); + + auto app_path = Module::RetrieveRTValueTypedOrDefault<>( + "core", "gpgme.ctx.app_path", QString{}); + auto default_database_path = Module::RetrieveRTValueTypedOrDefault<>( + "core", "gpgme.ctx.default_database_path", QString{}); + auto gpgconf_path = Module::RetrieveRTValueTypedOrDefault<>( + "core", "gpgme.ctx.gpgconf_path", QString{}); + auto cms_path = Module::RetrieveRTValueTypedOrDefault<>( + "core", "gpgme.ctx.cms_path", QString{}); + auto assuan_path = Module::RetrieveRTValueTypedOrDefault<>( + "core", "gpgme.ctx.assuan_path", QString{}); + + if (gpgconf == 1) { + stream << Tr("GPGCONF Path: ") << gpgconf_path << '\n'; + } + + if (openpgp == 1) { + stream << Tr("GnuPG Path: ") << app_path << '\n'; + stream << Tr("Default Key Database Path: ") << default_database_path + << '\n'; + } + + if (cms == 1) { + stream << Tr("CMS Path: ") << cms_path << '\n'; + } + + if (assuan == 1) { + stream << Tr("ASSUAN Path: ") << assuan_path << '\n'; + } + + stream << '\n'; + + stream << "Key Database(s): " << '\n'; + stream << '\n'; + + auto key_database_list = + KeyDatabaseListSO(SettingsObject("key_database_list")); + const auto key_databases = key_database_list.key_databases; + + int index = 0; + for (const auto& key_database : key_databases) { + stream << Tr("Key Database [") << index++ << "] " << Tr("Name: ") + << key_database.name << " " << Tr("-> Path: ") << key_database.path + << '\n'; + } stream << Qt::endl; return 0; } @@ -40,4 +40,6 @@ auto ParseLogLevel(const QString& level) -> int; auto RunTest(const GFCxtWPtr&) -> int; +auto PrintEnvInfo() -> int; + } // namespace GpgFrontend
\ No newline at end of file diff --git a/src/core/GpgCoreInit.cpp b/src/core/GpgCoreInit.cpp index e796738e..8a625f9d 100644 --- a/src/core/GpgCoreInit.cpp +++ b/src/core/GpgCoreInit.cpp @@ -89,35 +89,74 @@ auto SearchKeyDatabasePath(const QList<QString>& candidate_paths) -> QString { return {}; } -auto InitGpgME(const QString& gpgconf_path, const QString& gnupg_path) -> bool { - // init gpgme subsystem and get gpgme library version - Module::UpsertRTValue("core", "gpgme.version", - QString(gpgme_check_version(nullptr))); +auto GetDefaultKeyDatabasePath(const QString& gpgconf_path) -> QString { + if (gpgconf_path.isEmpty()) return {}; - gpgme_set_locale(nullptr, LC_CTYPE, setlocale(LC_CTYPE, nullptr)); -#ifdef LC_MESSAGES - gpgme_set_locale(nullptr, LC_MESSAGES, setlocale(LC_MESSAGES, nullptr)); -#endif + QFileInfo info(gpgconf_path); + if (!info.exists() || !info.isFile()) return {}; - if (!gpgconf_path.isEmpty()) { - CheckGpgError(gpgme_set_engine_info(GPGME_PROTOCOL_GPGCONF, - gpgconf_path.toUtf8(), nullptr)); + auto* p = new QProcess(QCoreApplication::instance()); + p->setProgram(info.absoluteFilePath()); + p->setArguments({"--list-dirs", "homedir"}); + p->start(); + + p->waitForFinished(); + auto home_path = p->readAll().trimmed(); + p->deleteLater(); + + QFileInfo home_info(home_path); + return home_info.absoluteFilePath(); +} + +auto InitGpgME() -> bool { + const auto* ver = gpgme_check_version(nullptr); + if (ver == nullptr) { + LOG_E() << "gpgme_check_version() failed, abort..."; + return false; + ; } + Module::UpsertRTValue("core", "gpgme.version", QString(ver)); - if (!gnupg_path.isEmpty()) { - CheckGpgError(gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP, - gnupg_path.toUtf8(), nullptr)); + // require gnupg version > 2.1.0 + if (gpgme_set_global_flag("require-gnupg", "2.1.0") != 0) { + LOG_E() << "gpgme_set_global_flag() with argument 'require-gnupg' failed, " + "abort..."; + return false; + } + + if (CheckGpgError( + gpgme_set_locale(nullptr, LC_CTYPE, setlocale(LC_CTYPE, nullptr))) != + GPG_ERR_NO_ERROR) { + LOG_E() << "gpgme_set_locale() with argument LC_CTYPE failed, abort..."; + return false; + } + +#ifdef LC_MESSAGES + if (CheckGpgError(gpgme_set_locale(nullptr, LC_MESSAGES, + setlocale(LC_MESSAGES, nullptr))) != + GPG_ERR_NO_ERROR) { + LOG_E() << "gpgme_set_locale() with argument LC_MESSAGES failed, abort..."; + return false; } +#endif gpgme_ctx_t p_ctx; - CheckGpgError(gpgme_new(&p_ctx)); + if (CheckGpgError(gpgme_new(&p_ctx)) != GPG_ERR_NO_ERROR) { + LOG_E() << "gpgme_new() failed, abort..."; + return false; + } // get engine info auto* engine_info = gpgme_ctx_get_engine_info(p_ctx); + if (engine_info == nullptr) { + LOG_E() << "gpgme_ctx_get_engine_info() failed, abort..."; + return false; + } + // Check ENV before running - bool find_openpgp = false; - bool find_gpgconf = false; - bool find_cms = false; + bool has_gpgconf = false; + bool has_openpgp = false; + bool has_cms = false; while (engine_info != nullptr) { if (strcmp(engine_info->version, "1.0.0") == 0) { @@ -127,7 +166,7 @@ auto InitGpgME(const QString& gpgconf_path, const QString& gnupg_path) -> bool { switch (engine_info->protocol) { case GPGME_PROTOCOL_OpenPGP: - find_openpgp = true; + has_openpgp = true; Module::UpsertRTValue("core", "gpgme.engine.openpgp", 1); Module::UpsertRTValue("core", "gpgme.ctx.app_path", @@ -140,21 +179,20 @@ auto InitGpgME(const QString& gpgconf_path, const QString& gnupg_path) -> bool { : engine_info->home_dir)); break; case GPGME_PROTOCOL_CMS: - find_cms = true; + has_cms = true; Module::UpsertRTValue("core", "gpgme.engine.cms", 1); Module::UpsertRTValue("core", "gpgme.ctx.cms_path", QString(engine_info->file_name)); break; case GPGME_PROTOCOL_GPGCONF: - find_gpgconf = true; + has_gpgconf = true; Module::UpsertRTValue("core", "gpgme.engine.gpgconf", 1); Module::UpsertRTValue("core", "gpgme.ctx.gpgconf_path", QString(engine_info->file_name)); break; case GPGME_PROTOCOL_ASSUAN: - Module::UpsertRTValue("core", "gpgme.engine.assuan", 1); Module::UpsertRTValue("core", "gpgme.ctx.assuan_path", QString(engine_info->file_name)); @@ -178,21 +216,62 @@ auto InitGpgME(const QString& gpgconf_path, const QString& gnupg_path) -> bool { const auto gnupg_version = Module::RetrieveRTValueTypedOrDefault<>( "core", "gpgme.ctx.gnupg_version", QString{"0.0.0"}); + const auto gpgconf_path = Module::RetrieveRTValueTypedOrDefault<>( + "core", "gpgme.ctx.gnupg_version", QString{}); - // conditional check: only support gpg 2.1.x now - if (!(GFCompareSoftwareVersion(gnupg_version, "2.1.0") >= 0 && find_gpgconf && - find_openpgp && find_cms)) { - FLOG_W("gpgme env check failed, abort"); + if (!has_gpgconf) { + FLOG_E() << "cannot get gpgconf backend engine, abort..."; return false; } - Module::UpsertRTValue("core", "env.state.gpgme", 1); + if (!has_openpgp) { + FLOG_E() << "cannot get openpgp backend engine, abort..."; + return false; + } + + if (!has_cms) { + FLOG_E() << "cannot get cms backend engine, abort..."; + return false; + } + + // ensure, and check twice: only support gpg > 2.1.0 + if (!(GFCompareSoftwareVersion(gnupg_version, "2.1.0") >= 0)) { + FLOG_F("gpgme env check failed, abort"); + return false; + } + + return true; +} + +auto RefreshGpgMEBackendEngine(const QString& gpgconf_path, + const QString& gnupg_path, + const QString& home_path) -> bool { + if (!gpgconf_path.isEmpty()) { + auto err = CheckGpgError(gpgme_set_engine_info( + GPGME_PROTOCOL_GPGCONF, gpgconf_path.toUtf8(), nullptr)); + if (err != GPG_ERR_NO_ERROR) { + LOG_W() << "cannot set gpgconf path of gpgme, fallback using default " + "gpgconf path, target gpgconf path:" + << gpgconf_path; + } + } + + if (!gnupg_path.isEmpty()) { + auto err = CheckGpgError(gpgme_set_engine_info( + GPGME_PROTOCOL_OpenPGP, gnupg_path.toUtf8(), home_path.toUtf8())); + if (err != GPG_ERR_NO_ERROR) { + LOG_W() << "cannot set gnupg path and home path of gpgme, fallback using " + "default gpgconf path, target gnupg path:" + << gnupg_path << "target home path: " << home_path; + } + } + return true; } -auto GetGnuPGPathByGpgConf(const QString& gnupg_install_fs_path) -> QString { - auto* process = new QProcess(); - process->setProgram(gnupg_install_fs_path); +auto GetGnuPGPathByGpgConf(const QString& gpgconf_install_fs_path) -> QString { + auto* process = new QProcess(QCoreApplication::instance()); + process->setProgram(gpgconf_install_fs_path); process->start(); process->waitForFinished(1000); auto output_buffer = process->readAllStandardOutput(); @@ -224,36 +303,44 @@ auto GetGnuPGPathByGpgConf(const QString& gnupg_install_fs_path) -> QString { } return ""; } -auto DetectGpgConfPath() -> QString { + +auto DecideGpgConfPath(const QString& default_gpgconf_path) -> QString { auto settings = GlobalSettingStation::GetInstance().GetSettings(); auto use_custom_gnupg_install_path = settings.value("gnupg/use_custom_gnupg_install_path", false).toBool(); auto custom_gnupg_install_path = settings.value("gnupg/custom_gnupg_install_path", QString{}).toString(); - QString gnupg_install_fs_path; + QString gpgconf_install_fs_path; // user defined if (use_custom_gnupg_install_path && !custom_gnupg_install_path.isEmpty()) { // check gpgconf path - gnupg_install_fs_path = custom_gnupg_install_path; + gpgconf_install_fs_path = custom_gnupg_install_path; #if defined(_WIN32) || defined(WIN32) gnupg_install_fs_path += "/gpgconf.exe"; #else - gnupg_install_fs_path += "/gpgconf"; + gpgconf_install_fs_path += "/gpgconf"; #endif - if (!VerifyGpgconfPath(QFileInfo(gnupg_install_fs_path))) { - LOG_W() << "core loaded custom gpgconf path is illegal: " - << gnupg_install_fs_path; - gnupg_install_fs_path = ""; + if (!VerifyGpgconfPath(QFileInfo(gpgconf_install_fs_path))) { + LOG_W() << "the gpgconf path by settings is illegal, path: " + << gpgconf_install_fs_path; + gpgconf_install_fs_path = ""; } } + if (gpgconf_install_fs_path.isEmpty() && + !default_gpgconf_path.trimmed().isEmpty()) { + LOG_I() << "using default gpgconf path found by gpgme: " + << default_gpgconf_path; + return QFileInfo(default_gpgconf_path).absoluteFilePath(); + } + // custom not found or not defined then fallback to default candidate path - if (gnupg_install_fs_path.isEmpty()) { + if (gpgconf_install_fs_path.isEmpty()) { // platform detection #if defined(__APPLE__) && defined(__MACH__) - gnupg_install_fs_path = SearchGpgconfPath( + gpgconf_install_fs_path = SearchGpgconfPath( {"/usr/local/bin/gpgconf", "/opt/homebrew/bin/gpgconf"}); #elif defined(_WIN32) || defined(WIN32) gnupg_install_fs_path = @@ -265,35 +352,118 @@ auto DetectGpgConfPath() -> QString { #endif } - if (!gnupg_install_fs_path.isEmpty()) { - return QFileInfo(gnupg_install_fs_path).absoluteFilePath(); + if (!gpgconf_install_fs_path.isEmpty()) { + return QFileInfo(gpgconf_install_fs_path).absoluteFilePath(); } return ""; } -auto DetectGnuPGPath(QString gpgconf_path) -> QString { +auto DecideGnuPGPath(const QString& gpgconf_path) -> QString { return GetGnuPGPathByGpgConf(gpgconf_path); } -void InitGpgFrontendCore(CoreInitArgs args) { - // initialize global register table - Module::UpsertRTValue("core", "env.state.gpgme", 0); - Module::UpsertRTValue("core", "env.state.ctx", 0); - Module::UpsertRTValue("core", "env.state.basic", 0); - Module::UpsertRTValue("core", "env.state.all", 0); +auto InitBasicPath() -> bool { + auto default_gpgconf_path = Module::RetrieveRTValueTypedOrDefault<>( + "core", "gpgme.ctx.gpgconf_path", QString{}); + + auto default_gnupg_path = Module::RetrieveRTValueTypedOrDefault<>( + "core", "gpgme.ctx.app_path", QString{}); + + LOG_I() << "default gpgconf path found by gpgme: " << default_gpgconf_path; + LOG_I() << "default gnupg path found by gpgme: " << default_gnupg_path; + + auto target_gpgconf_path = DecideGpgConfPath(default_gpgconf_path); + auto target_gnupg_path = default_gnupg_path; + if (target_gpgconf_path != default_gpgconf_path) { + target_gnupg_path = DecideGnuPGPath(target_gpgconf_path); + } + LOG_I() << "gpgconf path used: " << target_gpgconf_path; + LOG_I() << "gnupg path provided by gpgconf: " << target_gnupg_path; - auto gpgconf_install_fs_path = DetectGpgConfPath(); - auto gnupg_install_fs_path = DetectGnuPGPath(gpgconf_install_fs_path); - LOG_I() << "detected gpgconf path: " << gpgconf_install_fs_path; - LOG_I() << "detected gnupg path: " << gnupg_install_fs_path; + if (target_gpgconf_path.isEmpty()) { + LOG_E() << "Cannot find gpgconf!" + << "GpgFrontend cannot start under this situation!"; + CoreSignalStation::GetInstance()->SignalBadGnupgEnv( + QCoreApplication::tr("Cannot Find GpgConf")); + return false; + } - // initialize library gpgme - if (!InitGpgME(gpgconf_install_fs_path, gnupg_install_fs_path)) { + if (target_gnupg_path.isEmpty()) { + LOG_E() << "Cannot find GnuPG by gpgconf!" + << "GpgFrontend cannot start under this situation!"; CoreSignalStation::GetInstance()->SignalBadGnupgEnv( - QCoreApplication::tr("GpgME initiation failed")); - return; + QCoreApplication::tr("Cannot Find GnuPG")); + return false; } + auto default_home_path = Module::RetrieveRTValueTypedOrDefault<>( + "core", "gpgme.ctx.default_database_path", QString{}); + if (default_home_path.trimmed().isEmpty()) { + default_home_path = GetDefaultKeyDatabasePath(target_gpgconf_path); + } + + LOG_I() << "home path provided by gpgconf: " << default_home_path; + if (default_home_path.isEmpty()) { + LOG_E() << "Cannot find default home path by gpgconf!" + << "GpgFrontend cannot start under this situation!"; + CoreSignalStation::GetInstance()->SignalBadGnupgEnv( + QCoreApplication::tr("Cannot Find Home Path")); + return false; + } + + RefreshGpgMEBackendEngine(target_gpgconf_path, target_gnupg_path, + default_home_path); + + Module::UpsertRTValue("core", "gpgme.ctx.gpgconf_path", + QString(target_gpgconf_path)); + Module::UpsertRTValue("core", "gpgme.ctx.app_path", + QString(target_gnupg_path)); + Module::UpsertRTValue("core", "gpgme.ctx.default_database_path", + QString(default_home_path)); + + return true; +} + +auto GetKeyDatabases(QString& default_home_path) -> QList<KeyDatabaseItemSO> { + auto key_db_list_so = SettingsObject("key_database_list"); + auto key_db_list = KeyDatabaseListSO(key_db_list_so); + auto key_dbs = key_db_list.key_databases; + + key_dbs.removeIf( + [default_home_path](const KeyDatabaseItemSO& key_database) -> bool { + return key_database.path == default_home_path; + }); + key_db_list_so.Store(key_db_list.ToJson()); + return key_dbs; +} + +auto InitGpgFrontendCore(CoreInitArgs args) -> int { + // initialize gpgme + if (!InitGpgME()) { + LOG_E() << "Oops, GpgME init failed!" + << "GpgFrontend cannot start under this situation!"; + Module::UpsertRTValue("core", "env.state.gpgme", -1); + CoreSignalStation::GetInstance()->SignalBadGnupgEnv( + QCoreApplication::tr("GpgME Initiation Failed")); + return -1; + } + + Module::UpsertRTValue("core", "env.state.gpgme", 1); + + // decide gpgconf, gnupg and default home path + if (!InitBasicPath()) { + LOG_E() << "Oops, Basic Path init failed!" + << "GpgFrontend cannot start under this situation!"; + return -1; + } + + auto default_gpgconf_path = Module::RetrieveRTValueTypedOrDefault<>( + "core", "gpgme.ctx.gpgconf_path", QString{}); + auto default_gnupg_path = Module::RetrieveRTValueTypedOrDefault<>( + "core", "gpgme.ctx.app_path", QString{}); + auto default_home_path = Module::RetrieveRTValueTypedOrDefault<>( + "core", "gpgme.ctx.default_database_path", QString{}); + auto settings = GlobalSettingStation::GetInstance().GetSettings(); // read settings from config file @@ -313,18 +483,72 @@ void InitGpgFrontendCore(CoreInitArgs args) { auto restart_all_gnupg_components_on_start = settings.value("gnupg/restart_gpg_agent_on_start", false).toBool(); - auto key_database_list = - KeyDatabaseListSO(SettingsObject("key_database_list")); - const auto key_databases = key_database_list.key_databases; + // unit test mode + if (!args.unit_test_mode) { + Module::UpsertRTValue("core", "env.state.basic", 1); + Module::UpsertRTValue("core", "env.state.all", 1); + CoreSignalStation::GetInstance()->SignalGoodGnupgEnv(); + LOG_I() << "Basic ENV Checking Finished"; + return 0; + } + + // load default context + auto& default_ctx = GpgFrontend::GpgContext::CreateInstance( + kGpgFrontendDefaultChannel, [=]() -> ChannelObjectPtr { + GpgFrontend::GpgContextInitArgs args; + + // set key database path + if (!default_home_path.isEmpty()) { + args.db_name = "DEFAULT"; + args.db_path = default_home_path; + } + + args.offline_mode = forbid_all_gnupg_connection; + args.auto_import_missing_key = auto_import_missing_key; + args.use_pinentry = use_pinentry_as_password_input_dialog; + + LOG_D() << "gpgme default context at channel 0, key db name:" + << args.db_name << "key db path:" << args.db_path; + + return ConvertToChannelObjectPtr<>(SecureCreateUniqueObject<GpgContext>( + args, kGpgFrontendDefaultChannel)); + }); + if (!default_ctx.Good()) { + FLOG_E() << "Init GpgME Default Context failed!" + << "GpgFrontend cannot start under this situation!"; + Module::UpsertRTValue("core", "env.state.ctx", -1); + CoreSignalStation::GetInstance()->SignalBadGnupgEnv( + QCoreApplication::tr("GpgME Default Context Initiation Failed")); + return -1; + } + + Module::UpsertRTValue("core", "env.state.ctx", 1); + + if (!GpgKeyGetter::GetInstance(kGpgFrontendDefaultChannel).FlushKeyCache()) { + FLOG_E() << "Init GpgME Default Key Database failed!" + << "GpgFrontend cannot start under this situation!"; + Module::UpsertRTValue("core", "env.state.ctx", -1); + CoreSignalStation::GetInstance()->SignalBadGnupgEnv( + QCoreApplication::tr("Gpg Default Key Database Initiation Failed")); + return -1; + }; + + Module::UpsertRTValue("core", "env.state.basic", 1); + CoreSignalStation::GetInstance()->SignalGoodGnupgEnv(); + LOG_I() << "Basic ENV Checking Finished"; + + auto key_dbs = GetKeyDatabases(default_home_path); auto* task = new Thread::Task( [=](const DataObjectPtr&) -> int { // key database path QList<KeyDatabaseItemSO> buffered_key_dbs; + LOG_I() << "AAAAAA"; + // try to use user defined key database - if (!key_databases.empty()) { - for (const auto& key_database : key_databases) { + if (!key_dbs.empty()) { + for (const auto& key_database : key_dbs) { if (VerifyKeyDatabasePath(QFileInfo(key_database.path))) { auto key_database_fs_path = QFileInfo(key_database.path).absoluteFilePath(); @@ -335,109 +559,55 @@ void InitGpgFrontendCore(CoreInitArgs args) { << key_database.path; } } - } else { - QString key_database_fs_path; - -#if defined(__linux__) || (defined(__APPLE__) && defined(__MACH__)) - // use user's home path by default - key_database_fs_path = - SearchKeyDatabasePath({QDir::home().path() + "/.gnupg"}); -#endif - if (key_database_fs_path.isEmpty()) { - key_database_fs_path = Module::RetrieveRTValueTypedOrDefault<>( - "core", "gpgme.ctx.default_database_path", QString{}); - } - - // add the default key database path - if (!key_database_fs_path.isEmpty()) { - auto so = SettingsObject("key_database_list"); - auto key_database_list = KeyDatabaseListSO(so); - - auto key_database = KeyDatabaseItemSO(); - key_database.name = "Default"; - key_database.path = key_database_fs_path; - key_database_list.key_databases.append(key_database); - so.Store(key_database_list.ToJson()); - buffered_key_dbs.append(key_database); - } } - if (args.load_default_gpg_context) { - int channel_index = kGpgFrontendDefaultChannel; - for (const auto& key_db : buffered_key_dbs) { - // init ctx, also checking the basic env - auto& ctx = GpgFrontend::GpgContext::CreateInstance( - channel_index, [=]() -> ChannelObjectPtr { - GpgFrontend::GpgContextInitArgs args; - - // set key database path - if (!key_db.path.isEmpty()) { - args.db_name = key_db.name; - args.db_path = key_db.path; - } - - // set custom gnupg path - if (!gnupg_install_fs_path.isEmpty()) { - args.gpgconf_path = gnupg_install_fs_path; - } - - args.offline_mode = forbid_all_gnupg_connection; - args.auto_import_missing_key = auto_import_missing_key; - args.use_pinentry = use_pinentry_as_password_input_dialog; - - LOG_D() << "new gpgme context, channel" << channel_index - << ", key db name" << args.db_name << "key db path" - << args.db_path << ""; - - return ConvertToChannelObjectPtr<>( - SecureCreateUniqueObject<GpgContext>(args, - channel_index)); - }); - - // exit if failed - if (channel_index == kGpgFrontendDefaultChannel && !ctx.Good()) { - FLOG_W() << "gnupg default context init error, key database: " - << key_db.name << "key database path: " << key_db.path; - CoreSignalStation::GetInstance()->SignalBadGnupgEnv( - QCoreApplication::tr("GpgME Context initiation failed")); - return -1; - } - - FLOG_D() << "gnupg context init success, index" << channel_index - << " key database: " << key_db.name - << "key database path: " << key_db.path; - - channel_index++; + int channel_index = kGpgFrontendDefaultChannel + 1; + for (const auto& key_db : buffered_key_dbs) { + // init ctx, also checking the basic env + auto& ctx = GpgFrontend::GpgContext::CreateInstance( + channel_index, [=]() -> ChannelObjectPtr { + GpgFrontend::GpgContextInitArgs args; + + // set key database path + if (!key_db.path.isEmpty()) { + args.db_name = key_db.name; + args.db_path = key_db.path; + } + + args.offline_mode = forbid_all_gnupg_connection; + args.auto_import_missing_key = auto_import_missing_key; + args.use_pinentry = use_pinentry_as_password_input_dialog; + + LOG_D() << "new gpgme context, channel" << channel_index + << ", key db name" << args.db_name << "key db path" + << args.db_path << ""; + + return ConvertToChannelObjectPtr<>( + SecureCreateUniqueObject<GpgContext>(args, channel_index)); + }); + + if (!ctx.Good()) { + FLOG_E() << "gpgme context init failed, index:" << channel_index; + continue; } - Module::UpsertRTValue("core", "env.state.ctx", 1); - } - if (args.load_default_gpg_context) { - // load keys from all key dbs - for (int channel : GpgContext::GetAllChannelId()) { - if (!GpgKeyGetter::GetInstance(channel).FlushKeyCache()) { - CoreSignalStation::GetInstance()->SignalBadGnupgEnv( - QCoreApplication::tr("Gpg Key Database initiation failed")); - }; + if (!GpgKeyGetter::GetInstance(ctx.GetChannel()).FetchKey()) { + FLOG_E() << "gpgme context init key cache failed, index:" + << channel_index; + continue; } - } - - FLOG_D( - "basic env checking finished, including gpgme, ctx, and key infos"); - Module::UpsertRTValue("core", "env.state.basic", 1); - CoreSignalStation::GetInstance()->SignalGoodGnupgEnv(); - - if (restart_all_gnupg_components_on_start) { - GpgAdvancedOperator::RestartGpgComponents(); + channel_index++; } + Module::UpsertRTValue("core", "env.state.all", 1); + return 0; }, - "core_init_task"); + "core_key_dbs_init_task"); QObject::connect(task, &Thread::Task::SignalTaskEnd, []() { - + LOG_I() << "All Key Database(s) Initialize Finished"; }); // start the thread to check ctx and gnupg state @@ -445,25 +615,34 @@ void InitGpgFrontendCore(CoreInitArgs args) { GpgFrontend::Thread::TaskRunnerGetter::GetInstance() .GetTaskRunner(Thread::TaskRunnerGetter::kTaskRunnerType_Default) ->PostTask(task); + + if (restart_all_gnupg_components_on_start) { + GpgAdvancedOperator::RestartGpgComponents(); + } + return 0; } void StartMonitorCoreInitializationStatus() { auto* task = new Thread::Task( [=](const DataObjectPtr&) -> int { + int core_init_state = Module::RetrieveRTValueTypedOrDefault<>( + "core", "env.state.basic", 0); for (;;) { - if (Module::RetrieveRTValueTypedOrDefault<>("core", "env.state.basic", - 0)) { - break; - } + if (core_init_state != 0) break; + core_init_state = Module::RetrieveRTValueTypedOrDefault<>( + "core", "env.state.basic", 0); LOG_D() << "monitor: core env is still initializing, waiting..."; QThread::msleep(15); } + if (core_init_state < 0) return -1; + // waiting for module first + bool module_init_done = + Module::ModuleManager::GetInstance().IsAllModulesRegistered(); for (;;) { - if (Module::ModuleManager::GetInstance().IsAllModulesRegistered()) - break; + if (module_init_done) break; LOG_D() << "monitor: some modules are still going to be registered, " "waiting..."; @@ -471,16 +650,27 @@ void StartMonitorCoreInitializationStatus() { } LOG_D() << "monitor: good, all module are registered."; + int key_db_init_state = + Module::RetrieveRTValueTypedOrDefault<>("core", "env.state.all", 0); + for (;;) { + if (key_db_init_state != 0) break; + key_db_init_state = Module::RetrieveRTValueTypedOrDefault<>( + "core", "env.state.all", 0); + + LOG_D() << "monitor: key dbs are still initializing, waiting..."; + QThread::msleep(15); + } + LOG_D() << "monitor: good, all key db are loaded."; + LOG_D() << "monitor: core is fully initialized, sending signal to ui..."; - Module::UpsertRTValue("core", "env.state.all", 1); CoreSignalStation::GetInstance()->SignalCoreFullyLoaded(); return 0; }, "waiting_core_init_task"); QObject::connect(task, &Thread::Task::SignalTaskEnd, [=]() { - LOG_D() << "monitor task ended, call back to main thead."; + LOG_D() << "monitor: monitor task ended, call back to main thead."; }); // start the thread to check ctx and gnupg state diff --git a/src/core/GpgCoreInit.h b/src/core/GpgCoreInit.h index dd60578a..888ca1a3 100644 --- a/src/core/GpgCoreInit.h +++ b/src/core/GpgCoreInit.h @@ -34,7 +34,7 @@ namespace GpgFrontend { struct CoreInitArgs { bool gather_external_gnupg_info; - bool load_default_gpg_context; + bool unit_test_mode; }; /** @@ -47,11 +47,28 @@ void GPGFRONTEND_CORE_EXPORT DestroyGpgFrontendCore(); * @brief * */ -void GPGFRONTEND_CORE_EXPORT InitGpgFrontendCore(CoreInitArgs); +auto GPGFRONTEND_CORE_EXPORT InitGpgFrontendCore(CoreInitArgs) -> int; /** * @brief * */ void GPGFRONTEND_CORE_EXPORT StartMonitorCoreInitializationStatus(); + +/** + * @brief + * + * @return true + * @return false + */ +auto GPGFRONTEND_CORE_EXPORT InitGpgME() -> bool; + +/** + * @brief + * + * @return true + * @return false + */ +auto GPGFRONTEND_CORE_EXPORT InitBasicPath() -> bool; + } // namespace GpgFrontend diff --git a/src/core/function/basic/ChannelObject.cpp b/src/core/function/basic/ChannelObject.cpp index 15767b93..39a5568b 100644 --- a/src/core/function/basic/ChannelObject.cpp +++ b/src/core/function/basic/ChannelObject.cpp @@ -39,10 +39,7 @@ ChannelObject::ChannelObject(int channel, QString type) #ifdef DEBUG ChannelObject::~ChannelObject() noexcept { - // using iostream instead of log bacause at this time log object may have - // already been destroyed. - QTextStream(stdout) << "releasing channel object: " << this->type_ - << Qt::endl; + LOG_D() << "Releasing Channel Object: " << this->type_; } #else ChannelObject::~ChannelObject() noexcept = default; diff --git a/src/core/function/gpg/GpgAdvancedOperator.cpp b/src/core/function/gpg/GpgAdvancedOperator.cpp index f143cdbb..a742b60a 100644 --- a/src/core/function/gpg/GpgAdvancedOperator.cpp +++ b/src/core/function/gpg/GpgAdvancedOperator.cpp @@ -126,13 +126,14 @@ void GpgFrontend::GpgAdvancedOperator::RestartGpgComponents() { return; } - StartGpgAgent([](int err, DataObjectPtr) { - if (err >= 0) { - Module::UpsertRTValue( - "core", "gpg_advanced_operator.restart_gpg_components", true); - return; - } - }); + // StartGpgAgent([](int err, DataObjectPtr) { + // if (err >= 0) { + // Module::UpsertRTValue( + // "core", "gpg_advanced_operator.restart_gpg_components", + // true); + // return; + // } + // }); }}); } diff --git a/src/core/function/gpg/GpgContext.cpp b/src/core/function/gpg/GpgContext.cpp index 5c5dd813..002c0488 100644 --- a/src/core/function/gpg/GpgContext.cpp +++ b/src/core/function/gpg/GpgContext.cpp @@ -243,7 +243,7 @@ class GpgContext::Impl { assert(ctx != nullptr); if (!args.gpgconf_path.isEmpty()) { - LOG_D() << "set custom gpgconf path: " << args.gpgconf_path; + LOG_D() << "set gpgconf path: " << args.gpgconf_path; auto err = gpgme_ctx_set_engine_info(ctx, GPGME_PROTOCOL_GPGCONF, args.gpgconf_path.toUtf8(), nullptr); diff --git a/src/core/utils/BuildInfoUtils.cpp b/src/core/utils/BuildInfoUtils.cpp index 9e5314b5..ed72cebe 100644 --- a/src/core/utils/BuildInfoUtils.cpp +++ b/src/core/utils/BuildInfoUtils.cpp @@ -28,6 +28,9 @@ #include "BuildInfoUtils.h" +#include <archive.h> +#include <openssl/opensslv.h> + #include "GpgFrontendBuildInfo.h" namespace GpgFrontend { @@ -52,5 +55,15 @@ auto GetProjectBuildGitCommitHash() -> QString { return GIT_COMMIT_HASH; } auto GetProjectBuildGitVersion() -> QString { return GIT_VERSION; } +auto GetProjectQtVersion() -> QString { return {qVersion()}; } + +auto GetProjectOpenSSLVersion() -> QString { + return {OPENSSL_FULL_VERSION_STR}; +} + +auto GetProjectLibarchiveVersion() -> QString { + return {ARCHIVE_VERSION_ONLY_STRING}; +} + auto GetHttpRequestUserAgent() -> QString { return HTTP_REQUEST_USER_AGENT; } }; // namespace GpgFrontend
\ No newline at end of file diff --git a/src/core/utils/BuildInfoUtils.h b/src/core/utils/BuildInfoUtils.h index 823281f7..66954094 100644 --- a/src/core/utils/BuildInfoUtils.h +++ b/src/core/utils/BuildInfoUtils.h @@ -86,6 +86,27 @@ auto GPGFRONTEND_CORE_EXPORT GetProjectBuildGitVersion() -> QString; * * @return QString */ +auto GPGFRONTEND_CORE_EXPORT GetProjectQtVersion() -> QString; + +/** + * @brief + * + * @return QString + */ +auto GPGFRONTEND_CORE_EXPORT GetProjectOpenSSLVersion() -> QString; + +/** + * @brief + * + * @return QString + */ +auto GPGFRONTEND_CORE_EXPORT GetProjectLibarchiveVersion() -> QString; + +/** + * @brief + * + * @return QString + */ auto GPGFRONTEND_CORE_EXPORT GetHttpRequestUserAgent() -> QString; } // namespace GpgFrontend
\ No newline at end of file diff --git a/src/init.cpp b/src/init.cpp index f5476783..59cd8a32 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -32,6 +32,7 @@ #include "core/function/GlobalSettingStation.h" #include "core/function/gpg/GpgAdvancedOperator.h" #include "core/module/ModuleInit.h" +#include "core/module/ModuleManager.h" #include "core/thread/TaskRunnerGetter.h" #include "ui/GpgFrontendUIInit.h" @@ -86,6 +87,28 @@ void InitGlobalPathEnv() { } } +void InitGpgFrontendCoreAsync(CoreInitArgs core_init_args) { + // initialize global register table of init state + Module::UpsertRTValue("core", "env.state.gpgme", 0); + Module::UpsertRTValue("core", "env.state.ctx", 0); + Module::UpsertRTValue("core", "env.state.basic", 0); + Module::UpsertRTValue("core", "env.state.all", 0); + + Thread::TaskRunnerGetter::GetInstance() + .GetTaskRunner(Thread::TaskRunnerGetter::kTaskRunnerType_Default) + ->PostTask(new Thread::Task( + [core_init_args](DataObjectPtr) -> int { + // then load core + if (InitGpgFrontendCore(core_init_args) != 0) { + Module::UpsertRTValue("core", "env.state.basic", -1); + QTextStream(stdout) + << "Fatal: InitGpgFrontendCore() Failed" << Qt::endl; + }; + return 0; + }, + "core_init_task")); +} + void InitGlobalBasicEnv(const GFCxtWPtr &p_ctx, bool gui_mode) { GFCxtSPtr ctx = p_ctx.lock(); if (ctx == nullptr) { @@ -104,10 +127,9 @@ void InitGlobalBasicEnv(const GFCxtWPtr &p_ctx, bool gui_mode) { CoreInitArgs core_init_args; core_init_args.gather_external_gnupg_info = ctx->gather_external_gnupg_info; - core_init_args.load_default_gpg_context = ctx->load_default_gpg_context; + core_init_args.unit_test_mode = ctx->load_default_gpg_context; - // then load core - InitGpgFrontendCore(core_init_args); + InitGpgFrontendCoreAsync(core_init_args); // monitor StartMonitorCoreInitializationStatus(); diff --git a/src/main.cpp b/src/main.cpp index 9c1234e2..fbd53649 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -80,6 +80,7 @@ auto main(int argc, char* argv[]) -> int { parser.addOptions({ {{"v", "version"}, "show version information"}, {{"t", "test"}, "run all unit test cases"}, + {{"e", "environment"}, "show environment information"}, {{"l", "log-level"}, "set log level (trace, debug, info, warn, error)", "debug"}, @@ -95,6 +96,10 @@ auto main(int argc, char* argv[]) -> int { GpgFrontend::ParseLogLevel(parser.value("l")); } + if (parser.isSet("e")) { + return GpgFrontend::PrintEnvInfo(); + } + if (parser.isSet("t")) { ctx->gather_external_gnupg_info = false; ctx->load_default_gpg_context = false; diff --git a/src/ui/GpgFrontendUIInit.cpp b/src/ui/GpgFrontendUIInit.cpp index 9cca46c2..e93a8e3c 100644 --- a/src/ui/GpgFrontendUIInit.cpp +++ b/src/ui/GpgFrontendUIInit.cpp @@ -72,6 +72,14 @@ void WaitEnvCheckingProcess() { waiting_dialog->deleteLater(); }); + QApplication::connect(CoreSignalStation::GetInstance(), + &CoreSignalStation::SignalBadGnupgEnv, waiting_dialog, + [=]() { + LOG_D() << "ui caught signal: core loading failed"; + waiting_dialog->finished(0); + waiting_dialog->deleteLater(); + }); + // new local event looper QEventLoop looper; QApplication::connect(CoreSignalStation::GetInstance(), diff --git a/src/ui/UserInterfaceUtils.cpp b/src/ui/UserInterfaceUtils.cpp index ba3c323b..5c07e009 100644 --- a/src/ui/UserInterfaceUtils.cpp +++ b/src/ui/UserInterfaceUtils.cpp @@ -182,43 +182,45 @@ CommonUtils::CommonUtils() : QWidget(nullptr) { &UISignalStation::SignalRestartApplication, this, &CommonUtils::SlotRestartApplication); - connect(this, &CommonUtils::SignalBadGnupgEnv, this, - [=](const QString &reason) { - QMessageBox msg_box; - msg_box.setText(tr("GnuPG Context Loading Failed")); - msg_box.setInformativeText( - tr("Gnupg(gpg) is not installed correctly, please follow " - "<a href='https://www.gpgfrontend.bktus.com/#/" - "faq?id=how-to-deal-with-39env-loading-failed39'>this " - "notes</a> in FAQ to install Gnupg and then open " - "GpgFrontend. <br />" - "Or, you can open GnuPG Controller to set a " - "custom GnuPG which GpgFrontend should use. Then, " - "GpgFrontend will restart. <br /><br />" - "Breif Reason: %1") - .arg(reason)); - msg_box.setStandardButtons(QMessageBox::Open | QMessageBox::Cancel); - msg_box.setDefaultButton(QMessageBox::Save); - int ret = msg_box.exec(); - - switch (ret) { - case QMessageBox::Open: - (new GnuPGControllerDialog(this))->exec(); - // restart application when loop start - application_need_to_restart_at_once_ = true; - // restart application, core and ui - emit SignalRestartApplication(kDeepRestartCode); - break; - case QMessageBox::Cancel: - // close application - emit SignalRestartApplication(0); - break; - default: - // close application - emit SignalRestartApplication(0); - break; - } - }); + connect( + this, &CommonUtils::SignalBadGnupgEnv, this, [=](const QString &reason) { + QMessageBox msg_box; + msg_box.setText(tr("Failed to Load GnuPG Context")); + msg_box.setInformativeText( + tr("It seems that GnuPG (gpg) is not properly installed. " + "Please refer to the <a " + "href='https://www.gpgfrontend.bktus.com/overview/faq/" + "#troubleshooting-gnupg-installation-issues'>FAQ</a> for " + "instructions on fixing the installation. After resolving the " + "issue, " + "relaunch GpgFrontend.<br /><br />" + "Alternatively, you can open the GnuPG Controller to configure " + "a custom GnuPG installation for GpgFrontend to use. Once set, " + "GpgFrontend will restart automatically.<br /><br />" + "Details: %1") + .arg(reason)); + msg_box.setStandardButtons(QMessageBox::Retry | QMessageBox::Cancel); + msg_box.setDefaultButton(QMessageBox::Retry); + int ret = msg_box.exec(); + + switch (ret) { + case QMessageBox::Retry: + (new GnuPGControllerDialog(this))->exec(); + // Mark application for immediate restart + application_need_to_restart_at_once_ = true; + // Trigger application restart with deep restart code + emit SignalRestartApplication(kDeepRestartCode); + break; + case QMessageBox::Cancel: + // Close application + emit SignalRestartApplication(0); + break; + default: + // Default action: close application + emit SignalRestartApplication(0); + break; + } + }); } void CommonUtils::WaitForOpera(QWidget *parent, |