diff --git a/deploy/FortFirewall.iss b/deploy/FortFirewall.iss index 24c0d466..8964c92f 100644 --- a/deploy/FortFirewall.iss +++ b/deploy/FortFirewall.iss @@ -49,7 +49,6 @@ Name: pt; MessagesFile: "compiler:Languages\Portuguese.isl" [Tasks] Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; Flags: unchecked -Name: "explorer"; Description: "File Explorer integration"; Flags: unchecked Name: "portable"; Description: "Portable"; Flags: unchecked [Files] @@ -57,16 +56,6 @@ Source: "build\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createa Source: "{#APP_EXE_NAME}.example.ini"; DestDir: "{app}" Source: "README.portable"; DestDir: "{app}"; Tasks: portable -[Registry] -; Explorer's Context Menu -#define REG_SHELL "SystemFileAssociations\.exe\Shell" -#define REG_SHELL_MENU REG_SHELL + "\Fort Firewall" -Root: HKCR; Subkey: "{#REG_SHELL}"; Flags: uninsdeletekeyifempty; Tasks: explorer -Root: HKCR; Subkey: "{#REG_SHELL_MENU}"; Flags: deletekey uninsdeletekey -Root: HKCR; Subkey: "{#REG_SHELL_MENU}"; ValueType: string; ValueName: "icon"; ValueData: "{#APP_EXE}"; Tasks: explorer -Root: HKCR; Subkey: "{#REG_SHELL_MENU}"; ValueType: string; ValueName: "MUIVerb"; ValueData: "Fort Firewall ..."; Tasks: explorer -Root: HKCR; Subkey: "{#REG_SHELL_MENU}\command"; ValueType: string; ValueData: """{#APP_EXE}"" -w -c prog add ""%1"""; Tasks: explorer - [Icons] ; Start menu shortcut Name: "{group}\{#APP_NAME}"; Filename: "{#APP_EXE}"; WorkingDir: "{app}"; Parameters: "--lang {code:LanguageName}" diff --git a/src/ui/FortFirewallUI.pro b/src/ui/FortFirewallUI.pro index cd02e006..d235bf97 100644 --- a/src/ui/FortFirewallUI.pro +++ b/src/ui/FortFirewallUI.pro @@ -129,6 +129,7 @@ SOURCES += \ util/net/netutil.cpp \ util/osutil.cpp \ util/processinfo.cpp \ + util/regkey.cpp \ util/serviceworker.cpp \ util/startuputil.cpp \ util/stringutil.cpp \ @@ -265,6 +266,7 @@ HEADERS += \ util/net/netutil.h \ util/osutil.h \ util/processinfo.h \ + util/regkey.h \ util/serviceworker.h \ util/startuputil.h \ util/stringutil.h \ diff --git a/src/ui/form/opt/pages/optionspage.cpp b/src/ui/form/opt/pages/optionspage.cpp index dc3caa4f..64070e4d 100644 --- a/src/ui/form/opt/pages/optionspage.cpp +++ b/src/ui/form/opt/pages/optionspage.cpp @@ -26,7 +26,7 @@ #include "../optionscontroller.h" OptionsPage::OptionsPage(OptionsController *ctrl, QWidget *parent) : - BasePage(ctrl, parent), m_iniEdited(false), m_currentStartMode(0) + BasePage(ctrl, parent), m_iniEdited(false), m_currentStartMode(0), m_explorerIntegrated(false) { setupUi(); } @@ -50,6 +50,7 @@ void OptionsPage::onEditResetted() void OptionsPage::onSaved() { saveStartMode(); + saveExplorerIntegration(); if (iniEdited()) { saveIni(); @@ -72,6 +73,14 @@ void OptionsPage::saveStartMode() } } +void OptionsPage::saveExplorerIntegration() +{ + if (m_explorerIntegrated != m_cbExplorerMenu->isChecked()) { + m_explorerIntegrated = m_cbExplorerMenu->isChecked(); + StartupUtil::integrateExplorer(m_explorerIntegrated); + } +} + void OptionsPage::saveIni() { settings()->setHotKeyEnabled(m_cbHotKeys->isChecked()); @@ -110,6 +119,8 @@ void OptionsPage::onRetranslateUi() m_cbStopTraffic->setText(tr("Stop Traffic")); m_cbStopInetTraffic->setText(tr("Stop Internet Traffic")); m_cbAllowAllNew->setText(tr("Auto-Allow New Programs")); + + m_cbExplorerMenu->setText(tr("Windows Explorer integration")); m_cbHotKeys->setText(tr("Hot Keys")); m_cbPassword->setText(tr("Password:")); @@ -290,6 +301,11 @@ void OptionsPage::setupTrafficBox() void OptionsPage::setupGlobalBox() { + m_explorerIntegrated = StartupUtil::isExplorerIntegrated(); + m_cbExplorerMenu = ControlUtil::createCheckBox( + m_explorerIntegrated, [&](int) { ctrl()->setOthersEdited(true); }); + m_cbExplorerMenu->setEnabled(OsUtil::isUserAdmin()); + m_cbHotKeys = ControlUtil::createCheckBox( settings()->hotKeyEnabled(), [&](bool) { setIniEdited(true); }); @@ -300,6 +316,7 @@ void OptionsPage::setupGlobalBox() auto langLayout = setupLangLayout(); auto layout = new QVBoxLayout(); + layout->addWidget(m_cbExplorerMenu); layout->addWidget(m_cbHotKeys); layout->addLayout(passwordLayout); layout->addWidget(m_btPasswordLock, 0, Qt::AlignCenter); diff --git a/src/ui/form/opt/pages/optionspage.h b/src/ui/form/opt/pages/optionspage.h index e2221fc9..ea92bf50 100644 --- a/src/ui/form/opt/pages/optionspage.h +++ b/src/ui/form/opt/pages/optionspage.h @@ -21,6 +21,7 @@ protected slots: private: void saveStartMode(); + void saveExplorerIntegration(); void saveIni(); void retranslateComboStartMode(); @@ -47,6 +48,7 @@ private: private: uint m_iniEdited : 1; uint m_currentStartMode : 4; + uint m_explorerIntegrated : 1; QGroupBox *m_gbStartup = nullptr; QGroupBox *m_gbTraffic = nullptr; @@ -62,6 +64,7 @@ private: QCheckBox *m_cbStopTraffic = nullptr; QCheckBox *m_cbStopInetTraffic = nullptr; QCheckBox *m_cbAllowAllNew = nullptr; + QCheckBox *m_cbExplorerMenu = nullptr; QCheckBox *m_cbHotKeys = nullptr; QCheckBox *m_cbPassword = nullptr; QLineEdit *m_editPassword = nullptr; diff --git a/src/ui/main.cpp b/src/ui/main.cpp index 23149792..f3b8cf64 100644 --- a/src/ui/main.cpp +++ b/src/ui/main.cpp @@ -30,10 +30,11 @@ static void setupAppStyle() int main(int argc, char *argv[]) { - // Uninstall: Unregister booted provider, startup entries and exit + // Uninstall if (argc > 1 && !strcmp(argv[1], "-u")) { - StartupUtil::setStartupMode(StartupUtil::StartupDisabled); - DriverCommon::provUnregister(); + StartupUtil::setStartupMode(StartupUtil::StartupDisabled); // Remove auto-run and service + StartupUtil::integrateExplorer(false); // Remove Windows Explorer integration + DriverCommon::provUnregister(); // Unregister booted provider return 0; } diff --git a/src/ui/util/regkey.cpp b/src/ui/util/regkey.cpp new file mode 100644 index 00000000..e30f8e5c --- /dev/null +++ b/src/ui/util/regkey.cpp @@ -0,0 +1,103 @@ +#include "regkey.h" + +#define WIN32_LEAN_AND_MEAN +#include + +#define PREDEFINED_BASE_HANDLE quint32(HKEY_CLASSES_ROOT) + +RegKey::RegKey(quint32 parentHandle, const QString &subKey, quint32 flags) +{ + LPCWSTR subKeyStr = (LPCWSTR) subKey.utf16(); + const REGSAM samDesired = ((flags & ReadOnly) != 0 ? KEY_READ : KEY_ALL_ACCESS) + | ((flags & Native64Key) != 0 ? KEY_WOW64_64KEY : 0) + | ((flags & Native32Key) != 0 ? KEY_WOW64_32KEY : 0); + + if ((flags & Create) != 0) { + DWORD disposition; + RegCreateKeyEx((HKEY) parentHandle, subKeyStr, 0, nullptr, 0, samDesired, nullptr, + (PHKEY) &m_handle, &disposition); + } else { + RegOpenKeyEx((HKEY) parentHandle, subKeyStr, 0, samDesired, (PHKEY) &m_handle); + } +} + +RegKey::RegKey(Root root, const QString &subKey, quint32 flags) : + RegKey(PREDEFINED_BASE_HANDLE + root, subKey, flags) +{ +} + +RegKey::RegKey(const RegKey &parent, const QString &subKey, quint32 flags) : + RegKey(parent.handle(), subKey, flags) +{ +} + +RegKey::~RegKey() +{ + if (!isNull() && handle() < PREDEFINED_BASE_HANDLE) { + RegCloseKey((HKEY) handle()); + } +} + +bool RegKey::removeKey(const QString &subKey) +{ + return !RegDeleteKey((HKEY) handle(), (LPCWSTR) subKey.utf16()); +} + +bool RegKey::clearTree(const QString &subKey) +{ + return !RegDeleteTree((HKEY) handle(), subKey.isEmpty() ? nullptr : (LPCWSTR) subKey.utf16()); +} + +bool RegKey::removeRecursively(const QString &subKey) +{ + return clearTree(subKey) && removeKey(subKey); +} + +bool RegKey::removeValue(const QString &name) +{ + return !RegDeleteValue((HKEY) handle(), (LPCWSTR) name.utf16()); +} + +bool RegKey::setValue(const QString &name, const QVariant &value) +{ + QString text; + union { + qint64 i64; + qint32 i32; + } num; + const unsigned char *data = nullptr; + DWORD size = 0, type = REG_SZ; + + switch (value.userType()) { + case QMetaType::UnknownType: + case QMetaType::Void: + case QMetaType::Nullptr: + break; + case QMetaType::Bool: + case QMetaType::Int: + case QMetaType::UInt: + num.i32 = value.toInt(); + data = (const unsigned char *) &num.i32; + size = sizeof(qint32); + type = REG_DWORD; + break; + case QMetaType::LongLong: + case QMetaType::ULongLong: + num.i64 = value.toLongLong(); + data = (const unsigned char *) &num.i64; + size = sizeof(qint64); + type = REG_QWORD; + break; + default: + text = value.toString(); + data = (const unsigned char *) text.utf16(); + size = sizeof(wchar_t) * (text.size() + 1); /* + terminating null character */ + } + + return !RegSetValueEx((HKEY) handle(), (LPCWSTR) name.utf16(), 0, type, data, size); +} + +bool RegKey::contains(const QString &name) const +{ + return !RegQueryValueEx((HKEY) handle(), (LPCWSTR) name.utf16(), 0, nullptr, nullptr, nullptr); +} diff --git a/src/ui/util/regkey.h b/src/ui/util/regkey.h new file mode 100644 index 00000000..933f09ec --- /dev/null +++ b/src/ui/util/regkey.h @@ -0,0 +1,52 @@ +#ifndef REGKEY_H +#define REGKEY_H + +#include +#include + +#include "../util/classhelpers.h" + +class RegKey +{ +public: + enum Root : qint8 { HKCR = 0, HKCU, HKLM, HKU }; + + enum OpenFlag : qint8 { + ReadOnly = 0x01, + ReadWrite = 0x02, + Create = 0x04, + Native64Key = 0x10, + Native32Key = 0x20, + DefaultReadOnly = (ReadOnly | Native64Key), + DefaultReadWrite = (ReadWrite | Native64Key), + DefaultCreate = (ReadWrite | Create | Native64Key) + }; + +protected: + explicit RegKey(quint32 parentHandle, const QString &subKey, quint32 flags); + +public: + explicit RegKey(Root root, const QString &subKey = QString(), quint32 flags = DefaultReadOnly); + explicit RegKey(const RegKey &parent, const QString &subKey = QString(), + quint32 flags = DefaultReadOnly); + ~RegKey(); + CLASS_DELETE_COPY_MOVE(RegKey) + + bool isNull() const { return m_handle == 0; } + + bool removeKey(const QString &subKey); + bool clearTree(const QString &subKey = QString()); + bool removeRecursively(const QString &subKey); + bool removeValue(const QString &name); + bool setValue(const QString &name, const QVariant &value); + bool setDefaultValue(const QVariant &value) { return setValue(QString(), value); } + bool contains(const QString &name) const; + +protected: + quint32 handle() const { return m_handle; } + +private: + quint32 m_handle = 0; +}; + +#endif // REGKEY_H diff --git a/src/ui/util/startuputil.cpp b/src/ui/util/startuputil.cpp index a1b47798..8568a4a4 100644 --- a/src/ui/util/startuputil.cpp +++ b/src/ui/util/startuputil.cpp @@ -10,13 +10,18 @@ #include #include "fileutil.h" +#include "regkey.h" namespace { -const char *const regCurUserRun = - R"(HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run)"; -const char *const regAllUsersRun = - R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run)"; +constexpr RegKey::Root regCurUserRoot = RegKey::HKCU; +const char *const regCurUserRun = R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Run)"; + +constexpr RegKey::Root regAllUsersRoot = RegKey::HKLM; +const char *const regAllUsersRun = R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Run)"; + +constexpr RegKey::Root regShellRoot = RegKey::HKLM; +const char *const regShellMenu = R"(SOFTWARE\Classes\SystemFileAssociations\.exe\Shell)"; QString startupShortcutPath() { @@ -26,56 +31,56 @@ QString startupShortcutPath() QString wrappedAppFilePath() { - const auto filePath = QCoreApplication::applicationFilePath().replace('/', '\\'); + const auto filePath = FileUtil::toNativeSeparators(QCoreApplication::applicationFilePath()); return QString("\"%1\"").arg(filePath); } -bool isAutorunForUser(const char *key) +bool isAutorunForUser(RegKey::Root root, const char *key) { - const QSettings reg(key, QSettings::Registry64Format); - return !reg.value(APP_NAME).isNull(); + const RegKey reg(root, key, RegKey::DefaultReadOnly); + return reg.contains(APP_NAME); } bool isAutorunForCurrentUser() { - return isAutorunForUser(regCurUserRun); + return isAutorunForUser(regCurUserRoot, regCurUserRun); } bool isAutorunForAllUsers() { - return isAutorunForUser(regAllUsersRun); + return isAutorunForUser(regAllUsersRoot, regAllUsersRun); } -void setAutorunForUser(const char *key, const QString &command) +void setAutorunForUser(RegKey::Root root, const char *key, const QString &command) { - QSettings reg(key, QSettings::Registry64Format); + RegKey reg(root, key, RegKey::DefaultReadWrite); reg.setValue(APP_NAME, command); } void setAutorunForCurrentUser(const QString &command) { - setAutorunForUser(regCurUserRun, command); + setAutorunForUser(regCurUserRoot, regCurUserRun, command); } void setAutorunForAllUsers(const QString &command) { - setAutorunForUser(regAllUsersRun, command); + setAutorunForUser(regAllUsersRoot, regAllUsersRun, command); } -void removeAutorunForUser(const char *key) +void removeAutorunForUser(RegKey::Root root, const char *key) { - QSettings reg(key, QSettings::Registry64Format); - reg.remove(APP_NAME); + RegKey reg(root, key, RegKey::DefaultReadWrite); + reg.removeValue(APP_NAME); } void removeAutorunForCurrentUser() { - removeAutorunForUser(regCurUserRun); + removeAutorunForUser(regCurUserRoot, regCurUserRun); } void removeAutorunForAllUsers() { - removeAutorunForUser(regAllUsersRun); + removeAutorunForUser(regAllUsersRoot, regAllUsersRun); } bool installService( @@ -194,3 +199,27 @@ bool StartupUtil::isServiceMode(int mode) { return mode == StartupAllUsers || mode == StartupAllUsersBackground; } + +bool StartupUtil::isExplorerIntegrated() +{ + const RegKey regShell(regShellRoot, regShellMenu, RegKey::DefaultReadOnly); + const RegKey reg(regShell, APP_NAME, RegKey::DefaultReadOnly); + return !reg.isNull(); +} + +void StartupUtil::integrateExplorer(bool integrate) +{ + RegKey regShell(regShellRoot, regShellMenu, RegKey::DefaultReadWrite); + if (integrate) { + const QString wrappedPath = wrappedAppFilePath(); + + RegKey reg(regShell, APP_NAME, RegKey::DefaultCreate); + reg.setValue("icon", wrappedPath); + reg.setValue("MUIVerb", APP_NAME + QLatin1String(" ...")); + + RegKey regCommand(reg, "command", RegKey::DefaultCreate); + regCommand.setDefaultValue(wrappedPath + " -w -c prog add \"%1\""); + } else { + regShell.removeRecursively(APP_NAME); + } +} diff --git a/src/ui/util/startuputil.h b/src/ui/util/startuputil.h index 89759dcc..25392f13 100644 --- a/src/ui/util/startuputil.h +++ b/src/ui/util/startuputil.h @@ -23,6 +23,9 @@ public: static void setStartupMode(int mode, const QString &defaultLanguage = QString()); static bool isServiceMode(int mode); + + static bool isExplorerIntegrated(); + static void integrateExplorer(bool integrate); }; #endif // STARTUPUTIL_H