diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index 92ca70a4..09ea6fe3 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -51,9 +51,11 @@ find_package(Qt${QT_VERSION_MAJOR} set(SRC_EXE_MAIN source/scwx/qt/main/main.cpp) set(HDR_MAIN source/scwx/qt/main/application.hpp + source/scwx/qt/main/check_privilege.hpp source/scwx/qt/main/main_window.hpp source/scwx/qt/main/process_validation.hpp) set(SRC_MAIN source/scwx/qt/main/application.cpp + source/scwx/qt/main/check_privilege.cpp source/scwx/qt/main/main_window.cpp source/scwx/qt/main/process_validation.cpp) set(UI_MAIN source/scwx/qt/main/main_window.ui) diff --git a/scwx-qt/source/scwx/qt/main/check_privilege.cpp b/scwx-qt/source/scwx/qt/main/check_privilege.cpp new file mode 100644 index 00000000..e6402af5 --- /dev/null +++ b/scwx-qt/source/scwx/qt/main/check_privilege.cpp @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# include +#else +# include +#endif + +namespace scwx::qt::main +{ + +bool is_high_privilege() +{ +#if defined(_WIN32) + bool isAdmin = false; + HANDLE token = NULL; + TOKEN_ELEVATION elevation; + DWORD elevationSize = sizeof(TOKEN_ELEVATION); + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) + { + return false; + } + if (!GetTokenInformation( + token, TokenElevation, &elevation, elevationSize, &elevationSize)) + { + CloseHandle(token); + return false; + } + isAdmin = elevation.TokenIsElevated; + CloseHandle(token); + return isAdmin; +#elif defined(Q_OS_UNIX) + // On UNIX root is always uid 0. On Linux this is enforced by the kernel. + return geteuid() == 0; +#else + return false; +#endif +} + +#if defined(_WIN32) +static const QString message = QObject::tr( + "Supercell Wx has been run with administrator permissions. It is " + "recommended to run it without administrator permissions Do you wish to " + "continue?"); +#elif defined(Q_OS_UNIX) +static const QString message = QObject::tr( + "Supercell Wx has been run as root. It is recommended to run it as a normal " + "user. Do you wish to continue?"); +#else +static const QString message = QObject::tr(""); +#endif + +static const QString title = QObject::tr("Supercell Wx"); +static const QString checkBoxText = + QObject::tr("Do not show this warning again."); + +class PrivilegeChecker::Impl +{ +public: + explicit Impl() : + highPrivilege_ {is_high_privilege()}, + dialog_ {QMessageBox::Icon::Warning, title, message}, + checkBox_(new QCheckBox(checkBoxText, &dialog_)) + { + const std::string appDataPath { + QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + .toStdString()}; + hasAppData_ = std::filesystem::exists(appDataPath); + + dialog_.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + dialog_.setDefaultButton(QMessageBox::No); + dialog_.setCheckBox(checkBox_); + }; + + bool hasAppData_; + bool firstCheckCheckBoxState_ {true}; + bool highPrivilege_; + + QMessageBox dialog_; + QCheckBox* checkBox_; +}; + +PrivilegeChecker::PrivilegeChecker() : + p(std::make_unique()) +{ +} + +PrivilegeChecker::~PrivilegeChecker() = default; + +bool PrivilegeChecker::pre_settings_check() +{ + if (p->hasAppData_ || !p->highPrivilege_) + { + return false; + } + + const int result = p->dialog_.exec(); + p->firstCheckCheckBoxState_ = p->checkBox_->isChecked(); + + return result != QMessageBox::Yes; +} + +bool PrivilegeChecker::post_settings_check() +{ + auto& highPrivilegeWarningEnabled = + settings::GeneralSettings::Instance().high_privilege_warning_enabled(); + if (!highPrivilegeWarningEnabled.GetValue() || !p->highPrivilege_) + { + return false; + } + else if (!p->hasAppData_) + { + highPrivilegeWarningEnabled.StageValue(!p->firstCheckCheckBoxState_); + return false; + } + + switch (p->dialog_.exec()) + { + case QMessageBox::Yes: + highPrivilegeWarningEnabled.StageValue(!p->checkBox_->isChecked()); + return false; + case QMessageBox::No: + default: + return true; + } +} + +} // namespace scwx::qt::main diff --git a/scwx-qt/source/scwx/qt/main/check_privilege.hpp b/scwx-qt/source/scwx/qt/main/check_privilege.hpp new file mode 100644 index 00000000..8ec3f8e2 --- /dev/null +++ b/scwx-qt/source/scwx/qt/main/check_privilege.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace scwx::qt::main +{ + +bool is_high_privilege(); + +class PrivilegeChecker +{ +public: + explicit PrivilegeChecker(); + ~PrivilegeChecker(); + + PrivilegeChecker(const PrivilegeChecker&) = delete; + PrivilegeChecker& operator=(const PrivilegeChecker&) = delete; + PrivilegeChecker(const PrivilegeChecker&&) = delete; + PrivilegeChecker& operator=(const PrivilegeChecker&&) = delete; + + // returning true means check failed. + bool pre_settings_check(); + bool post_settings_check(); + +private: + class Impl; + std::unique_ptr p; +}; + +} // namespace scwx::qt::main diff --git a/scwx-qt/source/scwx/qt/main/main.cpp b/scwx-qt/source/scwx/qt/main/main.cpp index 7d66c80d..65d7e998 100644 --- a/scwx-qt/source/scwx/qt/main/main.cpp +++ b/scwx-qt/source/scwx/qt/main/main.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -78,6 +79,13 @@ int main(int argc, char* argv[]) QStandardPaths::setTestModeEnabled(true); } + // Test to see if scwx was run with high privilege + scwx::qt::main::PrivilegeChecker privilegeChecker; + if (privilegeChecker.pre_settings_check()) + { + return 0; + } + // Start the io_context main loop boost::asio::io_context& ioContext = scwx::util::io_context(); auto work = boost::asio::make_work_guard(ioContext); @@ -117,20 +125,27 @@ int main(int argc, char* argv[]) // Check process modules for compatibility scwx::qt::main::CheckProcessModules(); - // Run initial setup if required - if (scwx::qt::ui::setup::SetupWizard::IsSetupRequired()) + int result = 0; + if (privilegeChecker.post_settings_check()) { - scwx::qt::ui::setup::SetupWizard w; - w.show(); - a.exec(); + result = 1; } - - // Run Qt main loop - int result; + else { - scwx::qt::main::MainWindow w; - w.show(); - result = a.exec(); + // Run initial setup if required + if (scwx::qt::ui::setup::SetupWizard::IsSetupRequired()) + { + scwx::qt::ui::setup::SetupWizard w; + w.show(); + a.exec(); + } + + // Run Qt main loop + { + scwx::qt::main::MainWindow w; + w.show(); + result = a.exec(); + } } // Deinitialize application diff --git a/scwx-qt/source/scwx/qt/settings/general_settings.cpp b/scwx-qt/source/scwx/qt/settings/general_settings.cpp index 77ca764e..6f254c6b 100644 --- a/scwx-qt/source/scwx/qt/settings/general_settings.cpp +++ b/scwx-qt/source/scwx/qt/settings/general_settings.cpp @@ -80,6 +80,7 @@ public: warningsProvider_.SetDefault(defaultWarningsProviderValue); cursorIconAlwaysOn_.SetDefault(false); radarSiteThreshold_.SetDefault(0.0); + highPrivilegeWarningEnabled_.SetDefault(true); fontSizes_.SetElementMinimum(1); fontSizes_.SetElementMaximum(72); @@ -175,6 +176,8 @@ public: SettingsVariable warningsProvider_ {"warnings_provider"}; SettingsVariable cursorIconAlwaysOn_ {"cursor_icon_always_on"}; SettingsVariable radarSiteThreshold_ {"radar_site_threshold"}; + SettingsVariable highPrivilegeWarningEnabled_ { + "high_privilege_warning_enabled"}; }; GeneralSettings::GeneralSettings() : @@ -210,7 +213,8 @@ GeneralSettings::GeneralSettings() : &p->updateNotificationsEnabled_, &p->warningsProvider_, &p->cursorIconAlwaysOn_, - &p->radarSiteThreshold_}); + &p->radarSiteThreshold_, + &p->highPrivilegeWarningEnabled_}); SetDefaults(); } GeneralSettings::~GeneralSettings() = default; @@ -375,6 +379,11 @@ SettingsVariable& GeneralSettings::radar_site_threshold() const return p->radarSiteThreshold_; } +SettingsVariable& GeneralSettings::high_privilege_warning_enabled() const +{ + return p->highPrivilegeWarningEnabled_; +} + bool GeneralSettings::Shutdown() { bool dataChanged = false; @@ -385,6 +394,7 @@ bool GeneralSettings::Shutdown() dataChanged |= p->loopTime_.Commit(); dataChanged |= p->processModuleWarningsEnabled_.Commit(); dataChanged |= p->trackLocation_.Commit(); + dataChanged |= p->highPrivilegeWarningEnabled_.Commit(); return dataChanged; } @@ -429,7 +439,9 @@ bool operator==(const GeneralSettings& lhs, const GeneralSettings& rhs) rhs.p->updateNotificationsEnabled_ && lhs.p->warningsProvider_ == rhs.p->warningsProvider_ && lhs.p->cursorIconAlwaysOn_ == rhs.p->cursorIconAlwaysOn_ && - lhs.p->radarSiteThreshold_ == rhs.p->radarSiteThreshold_); + lhs.p->radarSiteThreshold_ == rhs.p->radarSiteThreshold_ && + lhs.p->highPrivilegeWarningEnabled_ == + rhs.p->highPrivilegeWarningEnabled_); } } // namespace settings diff --git a/scwx-qt/source/scwx/qt/settings/general_settings.hpp b/scwx-qt/source/scwx/qt/settings/general_settings.hpp index 6a49566b..074b956e 100644 --- a/scwx-qt/source/scwx/qt/settings/general_settings.hpp +++ b/scwx-qt/source/scwx/qt/settings/general_settings.hpp @@ -56,6 +56,7 @@ public: SettingsVariable& warnings_provider() const; SettingsVariable& cursor_icon_always_on() const; SettingsVariable& radar_site_threshold() const; + SettingsVariable& high_privilege_warning_enabled() const; static GeneralSettings& Instance(); diff --git a/test/data b/test/data index 24ececcd..1f3e1259 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 24ececcd183d3b8961e5638da89f0eb36309cd6b +Subproject commit 1f3e1259130a5eb4a6df37d721fe6c8301213e7e