diff --git a/CHANGELOG.md b/CHANGELOG.md index f504f77ccc..1bbdc92739 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,28 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [0.3.5 / 5.42.1] - 2020-07-19 + +### Added +- Added settings window +- added translationsupport +- added dark theme +- added auto start option +- added sandbox options +- added debug option "NoAddProcessToJob=y" + +### Changed +- improved empty sandbox tray icon +- improved message parsing +- updated homepage links + +### Fixed +- fixed ini issue with sandman.exe when renaming sandboxes +- fixed ini auto reload bug introduced in the last build +- fixed issue when hooking delayd loaded libraries + + + ## [0.3 / 5.42] - 2020-07-04 ### Added diff --git a/Sandboxie/apps/common/RunBrowser.cpp b/Sandboxie/apps/common/RunBrowser.cpp index 9d45f98be3..a4b29798d5 100644 --- a/Sandboxie/apps/common/RunBrowser.cpp +++ b/Sandboxie/apps/common/RunBrowser.cpp @@ -134,7 +134,7 @@ void CRunBrowser::OnNo() CString CRunBrowser::GetTopicUrl(const CString &topic) { - return L"https://www.sandboxie.com/index.php?" + topic; + return L"https://xanasoft.com/Sandboxie/" + topic; } @@ -155,5 +155,5 @@ void CRunBrowser::OpenHelp(CWnd *pParentWnd, const CString &topic) void CRunBrowser::OpenForum(CWnd *pParentWnd) { - CRunBrowser x(pParentWnd, L"http://forums.sandboxie.com/phpBB3/"); + CRunBrowser x(pParentWnd, L"https://forum.xanasoft.com/"); } diff --git a/Sandboxie/apps/control/LockConfigDialog.cpp b/Sandboxie/apps/control/LockConfigDialog.cpp index cbb90a494a..82d617929f 100644 --- a/Sandboxie/apps/control/LockConfigDialog.cpp +++ b/Sandboxie/apps/control/LockConfigDialog.cpp @@ -246,11 +246,11 @@ void CLockConfigDialog::OnOK() ini.SetRestrictions( isEditAdminOnly, isForceDisableAdminOnly, isForgetPassword); - if ((*m_NewPassword) || isEditAdminOnly) { - int rv = CMyApp::MsgBox(this, MSG_4269, MB_YESNO); - if (rv == IDYES) - CRunBrowser::OpenHelp(this, L"ConfigurationProtection"); - } + //if ((*m_NewPassword) || isEditAdminOnly) { + // int rv = CMyApp::MsgBox(this, MSG_4269, MB_YESNO); + // if (rv == IDYES) + // CRunBrowser::OpenHelp(this, L"ConfigurationProtection"); + //} EndDialog(0); } diff --git a/Sandboxie/apps/control/MyFrame.cpp b/Sandboxie/apps/control/MyFrame.cpp index 5a9bc5eb25..9e68376a69 100644 --- a/Sandboxie/apps/control/MyFrame.cpp +++ b/Sandboxie/apps/control/MyFrame.cpp @@ -223,7 +223,8 @@ CMyFrame::CMyFrame(BOOL ForceVisible, BOOL ForceSync) AdjustSizePosition(left, top, width, height); ULONG exStyle = (CMyApp::m_LayoutRTL) ? WS_EX_LAYOUTRTL : 0; - CreateEx( exStyle, (LPCTSTR)CMyApp::m_atom, CMyApp::m_appTitle, + CString strTitle = CMyApp::m_appTitle + " - xanasoft.com"; + CreateEx( exStyle, (LPCTSTR)CMyApp::m_atom, strTitle, WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_SYSMENU, left, top, width, height, NULL, NULL, NULL); diff --git a/Sandboxie/common/my_version.h b/Sandboxie/common/my_version.h index 73140ed22c..37a84097ac 100644 --- a/Sandboxie/common/my_version.h +++ b/Sandboxie/common/my_version.h @@ -20,8 +20,8 @@ #ifndef _MY_VERSION_H #define _MY_VERSION_H -#define MY_VERSION_BINARY 5,42,0 -#define MY_VERSION_STRING "5.42.0" +#define MY_VERSION_BINARY 5,42,1 +#define MY_VERSION_STRING "5.42.1" #define MY_VERSION_COMPAT "5.42" // These #defines are used by either Resource Compiler, or by NSIC installer diff --git a/Sandboxie/core/dll/SboxDll32.def b/Sandboxie/core/dll/SboxDll32.def index c8b219e23d..3cdd716fb7 100644 --- a/Sandboxie/core/dll/SboxDll32.def +++ b/Sandboxie/core/dll/SboxDll32.def @@ -27,7 +27,7 @@ SbieApi_GetHomePath=_SbieApi_GetHomePath@16 SbieApi_GetUnmountHive=_SbieApi_GetUnmountHive@4 SbieApi_GetVersion=_SbieApi_GetVersion@4 ;;; SbieApi_GetWork=_SbieApi_GetWork@12 -SbieApi_GetMessage=_SbieApi_GetMessage@20 +SbieApi_GetMessage=_SbieApi_GetMessage@24 SbieApi_HookTramp=_SbieApi_HookTramp@8 diff --git a/Sandboxie/core/dll/dllhook.c b/Sandboxie/core/dll/dllhook.c index e9cd2abd61..82a2c78445 100644 --- a/Sandboxie/core/dll/dllhook.c +++ b/Sandboxie/core/dll/dllhook.c @@ -178,6 +178,12 @@ skip_e9_rewrite: ; SourceFunc = (void *)target; } + // + // this simplification fails for delay loaded libraries, see coments about SetSecurityInfo, + // resulting in an endless loop, so just dont do that + // + +#if 0 // // 64-bit only: if the function begins with 'jmp qword ptr [x]' // (6 bytes) then replace the value at x, rather than overwrite @@ -216,6 +222,7 @@ skip_e9_rewrite: ; return orig_addr; } +#endif #endif _WIN64 diff --git a/Sandboxie/core/drv/process.c b/Sandboxie/core/drv/process.c index 0c9e85dda5..edda776694 100644 --- a/Sandboxie/core/drv/process.c +++ b/Sandboxie/core/drv/process.c @@ -1024,7 +1024,7 @@ _FX void Process_NotifyProcess_Create( // don't put the process into a job if OpenWinClass=* // - if (new_proc->open_all_win_classes) { + if (new_proc->open_all_win_classes || Conf_Get_Boolean(box->name, L"NoAddProcessToJob", 0, FALSE)) { add_process_to_job = FALSE; } diff --git a/Sandboxie/install/ParseVersion.bat b/Sandboxie/install/ParseVersion.bat index 25221c7aa9..11017a031a 100644 --- a/Sandboxie/install/ParseVersion.bat +++ b/Sandboxie/install/ParseVersion.bat @@ -12,7 +12,7 @@ echo. > %OUTPUT% for /F "tokens=3*" %%A in ('findstr /R "^#define.SBIE_INSTALLER_PATH\>" %INPUT%') do ( echo ^^!define SBIE_INSTALLER_PATH %%A) >> %OUTPUT% -for /F "tokens=3*" %%A in ('findstr /R "^#define.MY_VERSION_STRING_EX\>" %INPUT%') do ( echo ^^!define VERSION %%A) >> %OUTPUT% +for /F "tokens=3*" %%A in ('findstr /R "^#define.MY_VERSION_STRING\>" %INPUT%') do ( echo ^^!define VERSION %%A) >> %OUTPUT% for /F "tokens=3*" %%A in ('findstr /R "^#define.MY_PRODUCT_NAME_STRING\>" %INPUT%') do ( set C=%%A %%B& echo ^^!define PRODUCT_FULL_NAME !C! & set C=!C: =!& echo ^^!define PRODUCT_NAME !C!) >> %OUTPUT% diff --git a/Sandboxie/install/SandboxieVS.nsi b/Sandboxie/install/SandboxieVS.nsi index ff83f5ea37..7d178c8674 100644 --- a/Sandboxie/install/SandboxieVS.nsi +++ b/Sandboxie/install/SandboxieVS.nsi @@ -28,7 +28,9 @@ SetCompressor /SOLID /FINAL lzma ; these are the build-time config settings. Need to be cmd line args or something better. ; pick either 32 or 64 bit ;!define _BUILDARCH Win32 -!define _BUILDARCH x64 +;!define _BUILDARCH x64 +!define _BUILDARCH "$%SBIE_BUILDARCH%" + ; uncomment this line if you want to make the special versions that download VC Redist ;!define INCLUDE_VCREDIST_DNLD diff --git a/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.cpp b/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.cpp index d90f3572bc..dc77e2448c 100644 --- a/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.cpp +++ b/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.cpp @@ -132,6 +132,11 @@ void CComboInputDialog::setValue(const QString &t) d->combo->setCurrentIndex(idx); } +int CComboInputDialog::findValue(const QString &t) const +{ + return d->combo->findText(t); +} + QVariant CComboInputDialog::data() const { return d->combo->currentData(); @@ -142,6 +147,11 @@ void CComboInputDialog::setData(const QVariant & v) d->combo->setCurrentIndex(d->combo->findData(v)); } +int CComboInputDialog::findData(const QVariant & v) const +{ + return d->combo->findData(v); +} + QPixmap CComboInputDialog::iconPixmap() const { if (const QPixmap *p = d->pixmapLabel->pixmap()) diff --git a/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.h b/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.h index bee2a39407..b2667c208c 100644 --- a/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.h +++ b/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.h @@ -28,10 +28,11 @@ class MISCHELPERS_EXPORT CComboInputDialog: public QDialog QString value() const; void setValue(const QString &); + int findValue(const QString &) const; QVariant data() const; void setData(const QVariant &); - + int findData(const QVariant &) const; QDialogButtonBox::StandardButtons standardButtons() const; void setStandardButtons(QDialogButtonBox::StandardButtons s); diff --git a/SandboxiePlus/QSbieAPI/QSbieAPI.vcxproj b/SandboxiePlus/QSbieAPI/QSbieAPI.vcxproj index bce3bbe30e..73d4ea12fb 100644 --- a/SandboxiePlus/QSbieAPI/QSbieAPI.vcxproj +++ b/SandboxiePlus/QSbieAPI/QSbieAPI.vcxproj @@ -185,9 +185,10 @@ + - + @@ -203,7 +204,8 @@ - + + diff --git a/SandboxiePlus/QSbieAPI/QSbieAPI.vcxproj.filters b/SandboxiePlus/QSbieAPI/QSbieAPI.vcxproj.filters index 418e655f6f..10d33791c2 100644 --- a/SandboxiePlus/QSbieAPI/QSbieAPI.vcxproj.filters +++ b/SandboxiePlus/QSbieAPI/QSbieAPI.vcxproj.filters @@ -39,12 +39,15 @@ Sandboxie - - Sandboxie - SbieAPI + + Sandboxie + + + Sandboxie + @@ -78,7 +81,10 @@ SbieAPI - + + Sandboxie + + Sandboxie diff --git a/SandboxiePlus/QSbieAPI/Sandboxie/SandBox.cpp b/SandboxiePlus/QSbieAPI/Sandboxie/SandBox.cpp index d2d0c8f71f..e8ffadf09d 100644 --- a/SandboxiePlus/QSbieAPI/Sandboxie/SandBox.cpp +++ b/SandboxiePlus/QSbieAPI/Sandboxie/SandBox.cpp @@ -23,7 +23,7 @@ //{ //}; -CSandBox::CSandBox(const QString& BoxName, class CSbieAPI* pAPI) : CIniSection(BoxName, pAPI) +CSandBox::CSandBox(const QString& BoxName, class CSbieAPI* pAPI) : CSbieIni(BoxName, pAPI) { //m = new SSandBox; @@ -41,7 +41,7 @@ CSandBox::CSandBox(const QString& BoxName, class CSbieAPI* pAPI) : CIniSection(B } else { - SetBool("AutoRecover", true); + SetBool("AutoRecover", false); SetBool("BlockNetworkFiles", true); //SetDefaultTemplates6(*this); // why 6? diff --git a/SandboxiePlus/QSbieAPI/Sandboxie/SandBox.h b/SandboxiePlus/QSbieAPI/Sandboxie/SandBox.h index e32a1989b3..c6fa475a2f 100644 --- a/SandboxiePlus/QSbieAPI/Sandboxie/SandBox.h +++ b/SandboxiePlus/QSbieAPI/Sandboxie/SandBox.h @@ -21,9 +21,9 @@ #include "../qsbieapi_global.h" #include "BoxedProcess.h" -#include "IniSection.h" +#include "SbieIni.h" -class QSBIEAPI_EXPORT CSandBox : public CIniSection +class QSBIEAPI_EXPORT CSandBox : public CSbieIni { Q_OBJECT public: @@ -32,8 +32,6 @@ class QSBIEAPI_EXPORT CSandBox : public CIniSection virtual void UpdateDetails(); - virtual QString GetName() const { return m_Name; } - virtual QString GetFileRoot() const { return m_FilePath; } virtual QString GetRegRoot() const { return m_RegPath; } virtual QString GetIpcRoot() const { return m_IpcPath; } diff --git a/SandboxiePlus/QSbieAPI/Sandboxie/IniSection.cpp b/SandboxiePlus/QSbieAPI/Sandboxie/SbieIni.cpp similarity index 57% rename from SandboxiePlus/QSbieAPI/Sandboxie/IniSection.cpp rename to SandboxiePlus/QSbieAPI/Sandboxie/SbieIni.cpp index 4403455032..3c88b64213 100644 --- a/SandboxiePlus/QSbieAPI/Sandboxie/IniSection.cpp +++ b/SandboxiePlus/QSbieAPI/Sandboxie/SbieIni.cpp @@ -16,7 +16,7 @@ * along with this program. If not, see . */ #include "stdafx.h" -#include "IniSection.h" +#include "SbieIni.h" #include "../SbieAPI.h" #include @@ -25,39 +25,39 @@ typedef long NTSTATUS; #include "..\..\Sandboxie\core\drv\api_flags.h" -CIniSection::CIniSection(const QString& Section, class CSbieAPI* pAPI, QObject* parent) : QObject(parent) +CSbieIni::CSbieIni(const QString& Section, class CSbieAPI* pAPI, QObject* parent) : QObject(parent) { m_Name = Section; m_pAPI = pAPI; } -CIniSection::~CIniSection() +CSbieIni::~CSbieIni() { } -SB_STATUS CIniSection::SetText(const QString& Setting, const QString& Value) +SB_STATUS CSbieIni::SetText(const QString& Setting, const QString& Value) { if (GetText(Setting) == Value) return SB_OK; return m_pAPI->SbieIniSet(m_Name, Setting, Value); } -SB_STATUS CIniSection::SetNum(const QString& Setting, int Value) +SB_STATUS CSbieIni::SetNum(const QString& Setting, int Value) { return SetText(Setting, QString::number(Value)); } -SB_STATUS CIniSection::SetNum64(const QString& Setting, __int64 Value) +SB_STATUS CSbieIni::SetNum64(const QString& Setting, __int64 Value) { return SetText(Setting, QString::number(Value)); } -SB_STATUS CIniSection::SetBool(const QString& Setting, bool Value) +SB_STATUS CSbieIni::SetBool(const QString& Setting, bool Value) { return SetText(Setting, Value ? "y" : "n"); } -QString CIniSection::GetText(const QString& Setting, const QString& Default) const +QString CSbieIni::GetText(const QString& Setting, const QString& Default) const { int flags = (m_Name.isEmpty() ? 0 : CONF_GET_NO_GLOBAL) | CONF_GET_NO_EXPAND; QString Value = m_pAPI->SbieIniGet(m_Name, Setting, flags); @@ -65,7 +65,7 @@ QString CIniSection::GetText(const QString& Setting, const QString& Default) con return Value; } -int CIniSection::GetNum(const QString& Setting, int Default) const +int CSbieIni::GetNum(const QString& Setting, int Default) const { QString StrValue = GetText(Setting); bool ok; @@ -74,7 +74,7 @@ int CIniSection::GetNum(const QString& Setting, int Default) const return Value; } -__int64 CIniSection::GetNum64(const QString& Setting, __int64 Default) const +__int64 CSbieIni::GetNum64(const QString& Setting, __int64 Default) const { QString StrValue = GetText(Setting); bool ok; @@ -83,7 +83,7 @@ __int64 CIniSection::GetNum64(const QString& Setting, __int64 Default) const return Value; } -bool CIniSection::GetBool(const QString& Setting, bool Default) const +bool CSbieIni::GetBool(const QString& Setting, bool Default) const { QString StrValue = GetText(Setting); if (StrValue.compare("y", Qt::CaseInsensitive) == 0) @@ -93,15 +93,15 @@ bool CIniSection::GetBool(const QString& Setting, bool Default) const return Default; } -QStringList CIniSection::GetTextList(const QString &Setting, bool withBrackets) +QStringList CSbieIni::GetTextList(const QString &Setting, bool withTemplates) const { QStringList TextList; int flags = (m_Name.isEmpty() ? 0 : CONF_GET_NO_GLOBAL) | CONF_GET_NO_EXPAND; - if (withBrackets) + if (!withTemplates) flags |= CONF_GET_NO_TEMPLS; - for(int index = 0; ; index++) + for (int index = 0; ; index++) { QString Value = m_pAPI->SbieIniGet(m_Name, Setting, index | flags); if (Value.isNull()) @@ -112,35 +112,80 @@ QStringList CIniSection::GetTextList(const QString &Setting, bool withBrackets) return TextList; } -SB_STATUS CIniSection::InsertText(const QString& Setting, const QString& Value) +SB_STATUS CSbieIni::UpdateTextList(const QString &Setting, const QStringList& List) +{ + QStringList OldSettings = GetTextList(Setting); + QStringList NewSettings; + foreach(const QString& Value, List) { + if (!OldSettings.removeOne(Value)) + NewSettings.append(Value); + } + // delete removed or changed settings + foreach(const QString& Value, OldSettings) + DelValue(Setting, Value); + // add new or changed settings + foreach(const QString& Value, NewSettings) + InsertText(Setting, Value); + return SB_OK; +} + +QStringList CSbieIni::GetTemplates() const +{ + QStringList Templates; + + for (int tmpl_index = 0; ; tmpl_index++) + { + QString TmplName = m_pAPI->SbieIniGet(m_Name, "Template", tmpl_index | CONF_GET_NO_TEMPLS); + if (TmplName.isNull()) + break; + Templates.append(TmplName); + } + + return Templates; +} + +QStringList CSbieIni::GetTextListTmpl(const QString &Setting, const QString& Template) const +{ + QStringList TextList; + + for (int index = 0; ; index++) + { + QString Value = m_pAPI->SbieIniGet("Template_" + Template, Setting, index | CONF_GET_NO_GLOBAL); + if (Value.isNull()) + break; + TextList.append(Value); + } + + return TextList; +} + +SB_STATUS CSbieIni::InsertText(const QString& Setting, const QString& Value) { return m_pAPI->SbieIniSet(m_Name, Setting, Value, CSbieAPI::eIniInsert); } -SB_STATUS CIniSection::AppendText(const QString& Setting, const QString& Value) +SB_STATUS CSbieIni::AppendText(const QString& Setting, const QString& Value) { return m_pAPI->SbieIniSet(m_Name, Setting, Value, CSbieAPI::eIniAppend); } -SB_STATUS CIniSection::DelValue(const QString& Setting, const QString& Value) +SB_STATUS CSbieIni::DelValue(const QString& Setting, const QString& Value) { return m_pAPI->SbieIniSet(m_Name, Setting, Value, CSbieAPI::eIniDelete); } - -SB_STATUS CIniSection::RenameSection( const QString& NewName, bool deleteOld) // Note: deleteOld is used when duplicating a box +QList> CSbieIni::GetIniSection(qint32* pStatus, bool withTemplates) const { - if (m_Name.isEmpty() || NewName.isEmpty()) - return SB_ERR(); - bool SameName = (bool)(NewName.compare(m_Name, Qt::CaseInsensitive) == 0); - qint32 status = STATUS_SUCCESS; - // Get all Settigns + int flags = CONF_GET_NO_EXPAND; + if (!withTemplates) + flags |= CONF_GET_NO_TEMPLS; + QList> Settings; for (int setting_index = 0; ; setting_index++) { - QString setting_name = m_pAPI->SbieIniGet(m_Name, NULL, setting_index | CONF_GET_NO_TEMPLS | CONF_GET_NO_EXPAND, &status); + QString setting_name = m_pAPI->SbieIniGet(m_Name, "", setting_index | flags, &status); if (status == STATUS_RESOURCE_NAME_NOT_FOUND) { status = STATUS_SUCCESS; break; @@ -150,7 +195,7 @@ SB_STATUS CIniSection::RenameSection( const QString& NewName, bool deleteOld) // for (int value_index = 0; ; value_index++) { - QString setting_value = m_pAPI->SbieIniGet(m_Name, setting_name, value_index | CONF_GET_NO_GLOBAL | CONF_GET_NO_TEMPLS | CONF_GET_NO_EXPAND, &status); + QString setting_value = m_pAPI->SbieIniGet(m_Name, setting_name, value_index | CONF_GET_NO_GLOBAL | flags, &status); if (status == STATUS_RESOURCE_NAME_NOT_FOUND) { status = STATUS_SUCCESS; break; @@ -165,13 +210,27 @@ SB_STATUS CIniSection::RenameSection( const QString& NewName, bool deleteOld) // break; } + if (pStatus) *pStatus = status; + return Settings; +} + +SB_STATUS CSbieIni::RenameSection( const QString& NewName, bool deleteOld) // Note: deleteOld is used when duplicating a box +{ + qint32 status = STATUS_SUCCESS; + + if (m_Name.isEmpty() || NewName.isEmpty()) + return SB_ERR(); + bool SameName = (bool)(NewName.compare(m_Name, Qt::CaseInsensitive) == 0); + + // Get all Settigns + QList> Settings = GetIniSection(&status); if (status != STATUS_SUCCESS) return SB_ERR(CSbieAPI::tr("Failed to copy configuration from sandbox %1: %2").arg(m_Name).arg(status, 8, 16), status); // check if such a box already exists if (!SameName) { - m_pAPI->SbieIniGet(NewName, NULL, CONF_GET_NO_EXPAND, &status); + m_pAPI->SbieIniGet(NewName, "", CONF_GET_NO_EXPAND, &status); if (status != STATUS_RESOURCE_NAME_NOT_FOUND) return SB_ERR(CSbieAPI::tr("A sandbox of the name %1 already exists").arg(NewName)); } @@ -185,7 +244,7 @@ SB_STATUS CIniSection::RenameSection( const QString& NewName, bool deleteOld) // // Apply all Settigns for (QList>::iterator I = Settings.begin(); I != Settings.end(); ++I) { - SB_STATUS Status = m_pAPI->SbieIniSet(NewName, I->first, I->second); + SB_STATUS Status = m_pAPI->SbieIniSet(NewName, I->first, I->second, CSbieAPI::eIniInsert); if (Status.IsError()) return Status; } @@ -206,7 +265,7 @@ SB_STATUS CIniSection::RenameSection( const QString& NewName, bool deleteOld) // return SB_OK; } -SB_STATUS CIniSection::RemoveSection() +SB_STATUS CSbieIni::RemoveSection() { return m_pAPI->SbieIniSet(m_Name, "*", ""); } \ No newline at end of file diff --git a/SandboxiePlus/QSbieAPI/Sandboxie/IniSection.h b/SandboxiePlus/QSbieAPI/Sandboxie/SbieIni.h similarity index 64% rename from SandboxiePlus/QSbieAPI/Sandboxie/IniSection.h rename to SandboxiePlus/QSbieAPI/Sandboxie/SbieIni.h index bcd80c534a..a3e5b27acc 100644 --- a/SandboxiePlus/QSbieAPI/Sandboxie/IniSection.h +++ b/SandboxiePlus/QSbieAPI/Sandboxie/SbieIni.h @@ -5,12 +5,14 @@ #include "../SbieError.h" -class QSBIEAPI_EXPORT CIniSection: public QObject +class QSBIEAPI_EXPORT CSbieIni: public QObject { Q_OBJECT public: - CIniSection(const QString& Section, class CSbieAPI* pAPI, QObject* parent = 0); - virtual ~CIniSection(); + CSbieIni(const QString& Section, class CSbieAPI* pAPI, QObject* parent = 0); + virtual ~CSbieIni(); + + virtual QString GetName() const { return m_Name; } virtual SB_STATUS SetText(const QString& Setting, const QString& Value); virtual SB_STATUS SetNum(const QString& Setting, int Value); @@ -22,16 +24,23 @@ class QSBIEAPI_EXPORT CIniSection: public QObject virtual __int64 GetNum64(const QString& Setting, __int64 Default = 0) const; virtual bool GetBool(const QString& Setting, bool Default = false) const; - virtual QStringList GetTextList(const QString &Setting, bool withBrackets = false); + virtual QStringList GetTextList(const QString &Setting, bool withTemplates = true) const; + virtual SB_STATUS UpdateTextList(const QString &Setting, const QStringList& List); + virtual QStringList GetTemplates() const; + virtual QStringList GetTextListTmpl(const QString &Setting, const QString& Template) const; virtual SB_STATUS InsertText(const QString& Setting, const QString& Value); virtual SB_STATUS AppendText(const QString& Setting, const QString& Value); virtual SB_STATUS DelValue(const QString& Setting, const QString& Value); + virtual QList> GetIniSection(qint32* pStatus = NULL, bool withTemplates = false) const; + virtual SB_STATUS RenameSection(const QString& NewName, bool deleteOld = true); virtual SB_STATUS RemoveSection(); + CSbieAPI* GetAPI() { return m_pAPI; } + protected: QString m_Name; diff --git a/SandboxiePlus/QSbieAPI/SbieAPI.cpp b/SandboxiePlus/QSbieAPI/SbieAPI.cpp index 6ab7ccc5d8..0c8398a346 100644 --- a/SandboxiePlus/QSbieAPI/SbieAPI.cpp +++ b/SandboxiePlus/QSbieAPI/SbieAPI.cpp @@ -72,7 +72,7 @@ struct SSbieAPI ULONG SizeofPortMsg; ULONG CallSeqNumber; - QString Password; // todo: suppor lcoked configurations + QString Password; ULONG sessionId; @@ -111,6 +111,8 @@ CSbieAPI::CSbieAPI(QObject* parent) : QThread(parent) { m = new SSbieAPI(); + m_pGlobalSection = new CSbieIni("GlobalSettings", this, this); + m_bReloadPending = false; connect(&m_IniWatcher, SIGNAL(fileChanged(const QString&)), this, SLOT(OnIniChanged(const QString&))); @@ -398,6 +400,11 @@ SB_STATUS CSbieAPI__CallServer(SSbieAPI* m, MSG_HEADER* req, MSG_HEADER** prpl) SB_STATUS CSbieAPI::CallServer(void* req, void* rpl) const { + // + // Note: Once we open a port to the server from a threat the service will remember it we can't reconnect after disconnection + // So for every new connection we need a new threat, we achive this by letting our monitor threat issue all requests + // + while (InterlockedCompareExchange(&m->SvcLock, SVC_OP_STATE_PREP, SVC_OP_STATE_IDLE) != SVC_OP_STATE_IDLE) QThread::msleep(1); @@ -535,6 +542,7 @@ void CSbieAPI::OnIniChanged(const QString &path) void CSbieAPI::OnReloadConfig() { + m_bReloadPending = false; ReloadConfig(); } @@ -668,7 +676,9 @@ SB_STATUS CSbieAPI::ReloadBoxes() SB_STATUS CSbieAPI::SbieIniSet(void *RequestBuf, void *pPasswordWithinRequestBuf, const QString& SectionName, const QString& SettingName) { +retry: m->Password.toWCharArray((WCHAR*)pPasswordWithinRequestBuf); // fix-me: potential overflow + ((WCHAR*)pPasswordWithinRequestBuf)[m->Password.length()] = L'\0'; MSG_HEADER *rpl = NULL; SB_STATUS Status = CSbieAPI::CallServer((MSG_HEADER *)RequestBuf, &rpl); @@ -679,8 +689,17 @@ SB_STATUS CSbieAPI::SbieIniSet(void *RequestBuf, void *pPasswordWithinRequestBuf free(rpl); if (status == 0) return SB_OK; - if (status == STATUS_LOGON_NOT_GRANTED || status == STATUS_WRONG_PASSWORD) + if (status == STATUS_LOGON_NOT_GRANTED || status == STATUS_WRONG_PASSWORD) + { + if (((MSG_HEADER *)RequestBuf)->msgid != MSGID_SBIE_INI_TEST_PASSWORD) + { + bool bRetry = false; + emit NotAuthorized(status == STATUS_WRONG_PASSWORD, bRetry); + if (bRetry) + goto retry; + } return SB_ERR(CSbieAPI::tr("You are not authorized to update configuration in section '%1'").arg(SectionName), status); + } return SB_ERR(CSbieAPI::tr("Failed to set configuration setting %1 in section %2: %3").arg(SettingName).arg(SectionName).arg(status, 8, 16), status); } @@ -1179,6 +1198,43 @@ bool CSbieAPI::IsBoxEnabled(const QString& BoxName) return NT_SUCCESS(m->IoControl(parms)); } +bool CSbieAPI::IsConfigLocked() +{ + return m->Password.isEmpty() && !SbieIniGet("GlobalSettings", "EditPassword", 0).isEmpty(); +} + +SB_STATUS CSbieAPI::UnlockConfig(const QString& Password) +{ + SBIE_INI_PASSWORD_REQ *req = (SBIE_INI_PASSWORD_REQ *)malloc(REQUEST_LEN); + req->h.msgid = MSGID_SBIE_INI_TEST_PASSWORD; + req->h.length = sizeof(SBIE_INI_PASSWORD_REQ); + m->Password = Password; + SB_STATUS Status = SbieIniSet(req, req->old_password, "GlobalSettings", "*"); + if (Status.IsError()) + m->Password.clear(); + free(req); + return Status; +} + +SB_STATUS CSbieAPI::LockConfig(const QString& NewPassword) +{ + SBIE_INI_PASSWORD_REQ *req = (SBIE_INI_PASSWORD_REQ *)malloc(REQUEST_LEN); + req->h.msgid = MSGID_SBIE_INI_SET_PASSWORD; + req->h.length = sizeof(SBIE_INI_PASSWORD_REQ); + m->Password.toWCharArray(req->new_password); // fix-me: potential overflow + req->new_password[m->Password.length()] = L'\0'; + SB_STATUS Status = SbieIniSet(req, req->old_password, "GlobalSettings", "*"); + if (!Status.IsError()) + m->Password = NewPassword; + free(req); + return Status; +} + +void CSbieAPI::ClearPassword() +{ + m->Password.clear(); +} + /////////////////////////////////////////////////////////////////////////////// // Log // @@ -1213,7 +1269,7 @@ bool CSbieAPI::GetLog() wchar_t* Buffer[4*1024]; ULONG Length = ARRAYSIZE(Buffer); - ULONG MessageId = 0; + ULONG MsgCode = 0; ULONG ProcessId = 0; ULONG MessageNum = m->lastMessageNum; @@ -1225,7 +1281,7 @@ bool CSbieAPI::GetLog() args->func_code = API_GET_MESSAGE; args->msg_num.val = &MessageNum; args->session_id.val = m->sessionId; - args->msgid.val = &MessageId; + args->msgid.val = &MsgCode; args->msgtext.val = &msgtext; args->process_id.val = &ProcessId; @@ -1237,7 +1293,7 @@ bool CSbieAPI::GetLog() // we missed something m->lastMessageNum = MessageNum; - if (MessageId == 0) + if (MsgCode == 0) return true; // empty dummy message for maintaining sequence consistency WCHAR *str1 = (WCHAR*)msgtext.Buffer; @@ -1245,7 +1301,36 @@ bool CSbieAPI::GetLog() WCHAR *str2 = str1 + str1_len + 1; ULONG str2_len = wcslen(str2); - QString Message = CSbieAPI__FormatSbieMsg(m, MessageId, str1, str2); + // + // 0xTFFFMMMM + // + // T = ttcr + // tt = 00 - Ok + // tt = 01 - Info + // tt = 10 - Warning + // tt = 11 - Error + // c = unused + // r = reserved + // + // FFF = 0x000 UIstr + // FFF = 0x101 POPUP + // FFF = 0x102 EVENT + // + // MMMM = Message Code + // + quint8 Severity = MsgCode >> 30; + quint16 Facility = (MsgCode >> 16) & 0x0FFF; + quint16 MessageId= MsgCode & 0xFFFF; + + if (MessageId == 2199) // Auto Recovery notification + { + QString TempStr = QString::fromWCharArray(str1); + int TempPos = TempStr.indexOf(" "); + FileToRecover(TempStr.left(TempPos), Nt2DosPath(TempStr.mid(TempPos + 1))); + return true; + } + + QString Message = CSbieAPI__FormatSbieMsg(m, MsgCode, str1, str2); if(ProcessId != 4) // if its not from the driver add the pid Message += tr(" by process: %1").arg(ProcessId); emit LogMessage(Message); @@ -1253,6 +1338,45 @@ bool CSbieAPI::GetLog() return true; } +/////////////////////////////////////////////////////////////////////////////// +// Forced Processes +// + +SB_STATUS CSbieAPI::DisableForceProcess(bool Set) +{ + //m_pGlobalSection->SetNum("ForceDisableSeconds", Seconds); + + ULONG uEnable = Set ? TRUE : FALSE; + + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + API_DISABLE_FORCE_PROCESS_ARGS* args = (API_DISABLE_FORCE_PROCESS_ARGS*)parms; + + memset(parms, 0, sizeof(parms)); + args->func_code = API_DISABLE_FORCE_PROCESS; + args->set_flag.val = &uEnable; // NewState + args->get_flag.val = NULL; // OldState + + NTSTATUS status = m->IoControl(parms); + if (!NT_SUCCESS(status)) + return SB_ERR(status); + return SB_OK; +} + +bool CSbieAPI::AreForceProcessDisabled() +{ + ULONG uEnabled = FALSE; + + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + API_DISABLE_FORCE_PROCESS_ARGS* args = (API_DISABLE_FORCE_PROCESS_ARGS*)parms; + + memset(parms, 0, sizeof(parms)); + args->func_code = API_DISABLE_FORCE_PROCESS; + args->set_flag.val = NULL; // NewState + args->get_flag.val = &uEnabled; // OldState + + return NT_SUCCESS(m->IoControl(parms)) && uEnabled; +} + /////////////////////////////////////////////////////////////////////////////// // Monitor // @@ -1326,6 +1450,15 @@ bool CSbieAPI::GetMonitor() return true; } +/////////////////////////////////////////////////////////////////////////////// +// Other +// + +QString CSbieAPI::GetSbieMessage(int MessageId, const QString& arg1, const QString& arg2) const +{ + return CSbieAPI__FormatSbieMsg(m, MessageId, arg1.toStdWString().c_str(), arg2.toStdWString().c_str()); +} + /////////////////////////////////////////////////////////////////////////////// // // diff --git a/SandboxiePlus/QSbieAPI/SbieAPI.h b/SandboxiePlus/QSbieAPI/SbieAPI.h index 83c1bcda0c..1ec3587005 100644 --- a/SandboxiePlus/QSbieAPI/SbieAPI.h +++ b/SandboxiePlus/QSbieAPI/SbieAPI.h @@ -108,6 +108,15 @@ class QSBIEAPI_EXPORT CSbieAPI : public QThread virtual QString SbieIniGet(const QString& Section, const QString& Setting, quint32 Index = 0, qint32* ErrCode = NULL); virtual SB_STATUS SbieIniSet(const QString& Section, const QString& Setting, const QString& Value, ESetMode Mode = eIniUpdate); virtual bool IsBoxEnabled(const QString& BoxName); + virtual CSbieIni* GetGlobalSettings() const { return m_pGlobalSection; } + virtual bool IsConfigLocked(); + virtual SB_STATUS UnlockConfig(const QString& Password); + virtual SB_STATUS LockConfig(const QString& NewPassword); + virtual void ClearPassword(); + + // Forced Processes + virtual SB_STATUS DisableForceProcess(bool Set); + virtual bool AreForceProcessDisabled(); // Monitor virtual SB_STATUS EnableMonitor(bool Enable); @@ -116,9 +125,14 @@ class QSBIEAPI_EXPORT CSbieAPI : public QThread virtual QList GetResLog() const { QReadLocker Lock(&m_ResLogMutex); return m_ResLogList; } virtual void ClearResLog() { QWriteLocker Lock(&m_ResLogMutex); m_ResLogList.clear(); } + // Other + virtual QString GetSbieMessage(int MessageId, const QString& arg1 = QString(), const QString& arg2 = QString()) const; + signals: void StatusChanged(); void LogMessage(const QString& Message, bool bNotify = true); + void FileToRecover(const QString& BoxName, const QString& FilePath); + void NotAuthorized(bool bLoginRequired, bool &bRetry); private slots: //virtual void OnMonitorEntry(quint64 ProcessId, quint32 Type, const QString& Value); @@ -169,6 +183,8 @@ private slots: bool m_bTerminate; + CSbieIni* m_pGlobalSection; + private: mutable QMutex m_ThreadMutex; mutable QWaitCondition m_ThreadWait; diff --git a/SandboxiePlus/SandMan/Forms/OptionsWindow.ui b/SandboxiePlus/SandMan/Forms/OptionsWindow.ui new file mode 100644 index 0000000000..5961a002a7 --- /dev/null +++ b/SandboxiePlus/SandMan/Forms/OptionsWindow.ui @@ -0,0 +1,1126 @@ + + + OptionsWindow + + + + 0 + 0 + 614 + 397 + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + SandboxiePlus Options + + + + + + + + + true + + + + QTabWidget::West + + + 0 + + + + General Options + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 75 + true + + + + Appearance + + + + + + + Copy file size limit: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 75 + 16777215 + + + + + + + + + 75 + true + + + + File Mrigration + + + + + + + Issue message 2102 when a file is too large + + + + + + + kilobytes + + + + + + + + + + + + + Sandbox Indicator in title: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Sandboxed window border: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + + + + Program Groups + + + + + + + + You can group programs together and give them a group name. Program groups can be used with some of the settings instead of program names. + + + true + + + + + + + Add Group + + + + + + + Add Program + + + + + + + Remove + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + true + + + + Name + + + + + + + + + + + Forced Programs + + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + + Remove + + + + + + + Force Folder + + + + + + + true + + + + Type + + + + + Path + + + + + + + + Programs enteres here, or programs started from entered locations, will be put in this sandbox automatically, unless thay are explicitly started in an otehr sandbox. + + + true + + + + + + + Force Program + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Show Templates + + + + + + + + + + Stop Behavioure + + + + + + + + Remove Progam + + + + + + + Lingering programs will be automatically terminated is thay are still running after all other processes have terminated. + +If leader processes are defined all others are threated as lingering processes. + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Add Leader Program + + + + + + + Add Lingering Program + + + + + + + true + + + + Type + + + + + Path + + + + + + + + Show Templates + + + + + + + + + + Start Restrictions + + + + + + + + Add Program + + + + + + + true + + + + Name + + + + + + + + Issue message 1308 when a program fails to start + + + + + + + Note: Programs installed to this sandbox wont be able to start at all. + + + true + + + + + + + Remove Program + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Allow only sellected programs to start in this sandbox. + + + + + + + + + + Other Restrictions + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 75 + true + + + + Protect the system from sandboxed processes + + + General restrictions + + + + + + + + 75 + true + + + + Protect the sandbox integrity itself + + + Sandbox protection + + + + + + + Drop elevated privileges from processes + + + + + + + Block network files and folders, unless pecifically opened. + + + + + + + Don't open default COM objects + + + + + + + Limit access to the emulated service controll manager to privileged processes + + + + + + + Start the sandboxed RpcSs as a SYSTEM process + + + + + + + Protect sandboxed SYSTEM processes from unprivileged unsandboxed processes + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Internet Access + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Remove Program + + + + + + + Issue message 1307 when a program are denied internet + + + + + + + Add Program + + + + + + + true + + + + Name + + + + + + + + Block internet access for all programs except those added to the list. + + + + + + + Note: Programs installed to this sandbox wont be able access the internet at all. + + + + + + + + + + Resource Access + + + + + + + + true + + + + Type + + + + + Program + + + + + Access + + + + + Path + + + + + + + + Add Reg Key + + + + + + + Add File/Folder + + + + + + + Remove + + + + + + + Add Wnd Class + + + + + + + Add COM Object + + + + + + + Add IPC Path + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Move Up + + + + + + + Move Down + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Show Templates + + + + + + + Configure which processec can access what resoutrces. +Double click on an entry to edit it. +Note: Not all access modes are available to processes instaleld into a sandbox. + + + + + + + + + + Advanced Options + + + + + + + + + 75 + true + + + + Allow only sellected users to use this sandbox + + + User restrictions + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Delete command: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Add User + + + + + + + + + + Auto delete content when last sandboxed process terminates + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Protect this sandbox from deletion or emptying + + + + + + + + 75 + true + + + + Delete options + + + + + + + Remove User + + + + + + + + + + App Templates + + + + + + + + This list contains a large amount of sandbox comatybility enchancing templates + + + true + + + + + + + + + + true + + + + Category + + + + + Name + + + + + + + + Filter Categories + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Edit ini Section + + + + + + Edit ini + + + false + + + + + + + false + + + Cancel + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + Save + + + + + + + QPlainTextEdit::NoWrap + + + + + + + + + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + diff --git a/SandboxiePlus/SandMan/Forms/SettingsWindow.ui b/SandboxiePlus/SandMan/Forms/SettingsWindow.ui new file mode 100644 index 0000000000..ab68058095 --- /dev/null +++ b/SandboxiePlus/SandMan/Forms/SettingsWindow.ui @@ -0,0 +1,362 @@ + + + SettingsWindow + + + + 0 + 0 + 573 + 451 + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + SandboxiePlus Settings + + + + + + + + + true + + + + QTabWidget::North + + + 1 + + + + General Options + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Show Sys-Tray + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 75 + true + + + + Tray options + + + + + + + Restart required (!) + + + Qt::AlignCenter + + + + + + + Start with Windows + + + + + + + + + + + + + Use Dark Theme + + + + + + + Show Notifications for relevant log Messages + + + false + + + + + + + On main window close: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Watch Sandboxie.ini for changes + + + + + + + + + + Advanced Options + + + + + + + + Only Administrator user accounts can use Disable Forced Programs command + + + + + + + Only Administrator user accounts can make changes + + + + + + + + 75 + true + + + + Config protection + + + + + + + Password must be entered in order to make changes + + + + + + + Change Password + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 75 + true + + + + Sandbox default + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Sandbox file system root: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Sandbox ipc root: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Sandbox registry root: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Separate user folders + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Clear password when main window becomes hidden + + + + + + + + + + + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + diff --git a/SandboxiePlus/SandMan/Helpers/WinAdmin.cpp b/SandboxiePlus/SandMan/Helpers/WinAdmin.cpp new file mode 100644 index 0000000000..7843f5668c --- /dev/null +++ b/SandboxiePlus/SandMan/Helpers/WinAdmin.cpp @@ -0,0 +1,129 @@ +#include "stdafx.h" +#include "WinAdmin.h" + +#include +#include +#include + +bool IsElevated() +{ + bool fRet = false; + HANDLE hToken = NULL; + if(OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,&hToken)) + { + TOKEN_ELEVATION Elevation; + DWORD cbSize = sizeof(TOKEN_ELEVATION); + if(GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof( Elevation ), &cbSize)) + fRet = Elevation.TokenIsElevated; + } + if(hToken) + CloseHandle(hToken); + return fRet; +} + +int RunElevated(const wstring& Params, bool bGetCode) +{ + wchar_t szPath[MAX_PATH]; + if (!GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath))) + return -3; + return RunElevated(wstring(szPath), Params, bGetCode); +} + +int RunElevated(const wstring& binaryPath, const wstring& Params, bool bGetCode) +{ + // Launch itself as admin + SHELLEXECUTEINFO sei = { sizeof(sei) }; + sei.fMask = SEE_MASK_NOCLOSEPROCESS; + sei.lpVerb = L"runas"; + sei.lpFile = binaryPath.c_str(); + sei.lpParameters = Params.c_str(); + sei.hwnd = NULL; + sei.nShow = SW_NORMAL; + if (!ShellExecuteEx(&sei)) + { + DWORD dwError = GetLastError(); + if (dwError == ERROR_CANCELLED) + return -2; // The user refused to allow privileges elevation. + } + else + { + if (bGetCode) + { + WaitForSingleObject(sei.hProcess, 10000); + DWORD ExitCode = -4; + BOOL success = GetExitCodeProcess(sei.hProcess, &ExitCode); + CloseHandle(sei.hProcess); + return success ? ExitCode : -4; + } + return 0; + } + return -1; +} + +int RestartElevated(int &argc, char **argv) +{ + wstring Params; + for (int i = 1; i < argc; i++) + { + if (i > 1) + Params.append(L" "); + Params.append(L"\"" + wstring_convert>().from_bytes(argv[i]) + L"\""); + } + return RunElevated(Params); +} + +////////////////////////////////////////////////////////////////////////////////// +// AutoRun + +#define APP_NAME L"SandboxiePlus" + +#define AUTO_RUN_KEY_NAME APP_NAME L"_AutoRun" + +bool IsAutorunEnabled() +{ + bool result = false; + + HKEY hkey = nullptr; + if (RegOpenKeyEx (HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS) + { + WCHAR buffer[MAX_PATH] = {0}; + DWORD size = _countof (buffer); + + if (RegQueryValueEx (hkey, AUTO_RUN_KEY_NAME, nullptr, nullptr, (LPBYTE)buffer, &size) == ERROR_SUCCESS) + { + result = true; // todo: check path + } + + RegCloseKey (hkey); + } + + return result; +} + +bool AutorunEnable (bool is_enable) +{ + bool result = false; + + HKEY hkey = nullptr; + if (RegOpenKeyEx (HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS) + { + if (is_enable) + { + wchar_t szPath[MAX_PATH]; + if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath))) + { + wstring path = L"\"" + wstring(szPath) + L"\" -autorun"; + + result = (RegSetValueEx(hkey, AUTO_RUN_KEY_NAME, 0, REG_SZ, (LPBYTE)path.c_str(), DWORD((path.length() + 1) * sizeof(WCHAR))) == ERROR_SUCCESS); + } + } + else + { + result = (RegDeleteValue (hkey, AUTO_RUN_KEY_NAME) == ERROR_SUCCESS); + } + + RegCloseKey (hkey); + } + + return false; +} \ No newline at end of file diff --git a/SandboxiePlus/SandMan/Helpers/WinAdmin.h b/SandboxiePlus/SandMan/Helpers/WinAdmin.h new file mode 100644 index 0000000000..1195504a12 --- /dev/null +++ b/SandboxiePlus/SandMan/Helpers/WinAdmin.h @@ -0,0 +1,9 @@ +#pragma once + +bool IsElevated(); +int RunElevated(const wstring& Params, bool bGetCode = false); +int RunElevated(const wstring& binaryPath, const wstring& Params, bool bGetCode = false); +int RestartElevated(int &argc, char **argv); + +bool IsAutorunEnabled(); +bool AutorunEnable(bool is_enable); diff --git a/SandboxiePlus/SandMan/Resources/Actions/SetLogging.png b/SandboxiePlus/SandMan/Resources/Actions/SetLogging.png index f769c08377..1ffcc1ee38 100644 Binary files a/SandboxiePlus/SandMan/Resources/Actions/SetLogging.png and b/SandboxiePlus/SandMan/Resources/Actions/SetLogging.png differ diff --git a/SandboxiePlus/SandMan/Resources/Actions/config.png b/SandboxiePlus/SandMan/Resources/Actions/config.png new file mode 100644 index 0000000000..72f9f8cde2 Binary files /dev/null and b/SandboxiePlus/SandMan/Resources/Actions/config.png differ diff --git a/SandboxiePlus/SandMan/Resources/Actions/empty_all.png b/SandboxiePlus/SandMan/Resources/Actions/empty_all.png index 7637be6f3e..4da90db371 100644 Binary files a/SandboxiePlus/SandMan/Resources/Actions/empty_all.png and b/SandboxiePlus/SandMan/Resources/Actions/empty_all.png differ diff --git a/SandboxiePlus/SandMan/Resources/SandMan.png b/SandboxiePlus/SandMan/Resources/SandMan.png index 973fbab3b9..4e5bdc1bec 100644 Binary files a/SandboxiePlus/SandMan/Resources/SandMan.png and b/SandboxiePlus/SandMan/Resources/SandMan.png differ diff --git a/SandboxiePlus/SandMan/Resources/SandMan.qrc b/SandboxiePlus/SandMan/Resources/SandMan.qrc index f56f6ff768..f22b1aff4a 100644 --- a/SandboxiePlus/SandMan/Resources/SandMan.qrc +++ b/SandboxiePlus/SandMan/Resources/SandMan.qrc @@ -25,6 +25,7 @@ Actions/Stop.png Actions/Advanced.png Actions/Service.png + Actions/config.png Boxes/sandbox-b-empty.png diff --git a/SandboxiePlus/SandMan/Resources/SandMan2.png b/SandboxiePlus/SandMan/Resources/SandMan2.png index baf2b6f347..75a9e15c84 100644 Binary files a/SandboxiePlus/SandMan/Resources/SandMan2.png and b/SandboxiePlus/SandMan/Resources/SandMan2.png differ diff --git a/SandboxiePlus/SandMan/SandMan.cpp b/SandboxiePlus/SandMan/SandMan.cpp index b825a9ab34..c2056ec668 100644 --- a/SandboxiePlus/SandMan/SandMan.cpp +++ b/SandboxiePlus/SandMan/SandMan.cpp @@ -10,6 +10,7 @@ #include "./Dialogs/MultiErrorDialog.h" #include "../QSbieAPI/SbieUtils.h" #include "../QSbieAPI/Sandboxie/BoxBorder.h" +#include "Windows/SettingsWindow.h" CSbiePlusAPI* theAPI = NULL; @@ -81,6 +82,13 @@ CSandMan::CSandMan(QWidget *parent) theGUI = this; + m_DefaultStyle = QApplication::style()->objectName(); + m_DefaultPalett = QApplication::palette(); + + LoadLanguage(); + if (theConf->GetBool("Options/DarkTheme", false)) + SetDarkTheme(true); + m_bExit = false; theAPI = new CSbiePlusAPI(this); @@ -114,47 +122,6 @@ CSandMan::CSandMan(QWidget *parent) m_pPanelSplitter->setOrientation(Qt::Horizontal); m_pLogSplitter->addWidget(m_pPanelSplitter); - /* - // Box Tree - m_pBoxModel = new CSbieModel(); - m_pBoxModel->SetTree(true); - m_pBoxModel->SetUseIcons(true); - - m_pSortProxy = new CSortFilterProxyModel(false, this); - m_pSortProxy->setSortRole(Qt::EditRole); - m_pSortProxy->setSourceModel(m_pBoxModel); - m_pSortProxy->setDynamicSortFilter(true); - - m_pBoxTree = new QTreeViewEx(); - //m_pBoxTree->setItemDelegate(theGUI->GetItemDelegate()); - - m_pBoxTree->setModel(m_pSortProxy); - - m_pBoxTree->setSelectionMode(QAbstractItemView::ExtendedSelection); -#ifdef WIN32 - QStyle* pStyle = QStyleFactory::create("windows"); - m_pBoxTree->setStyle(pStyle); -#endif - m_pBoxTree->setSortingEnabled(true); - - m_pBoxTree->setContextMenuPolicy(Qt::CustomContextMenu); - connect(m_pBoxTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(OnMenu(const QPoint &))); - - //connect(theGUI, SIGNAL(ReloadPanels()), m_pWindowModel, SLOT(Clear())); - - m_pBoxTree->setColumnReset(2); - connect(m_pBoxTree, SIGNAL(ResetColumns()), this, SLOT(OnResetColumns())); - connect(m_pBoxTree, SIGNAL(ColumnChanged(int, bool)), this, SLOT(OnColumnsChanged())); - - //m_pSplitter->addWidget(CFinder::AddFinder(m_pBoxTree, m_pSortProxy)); - //m_pSplitter->setCollapsible(0, false); - // - - //connect(m_pBoxTree, SIGNAL(clicked(const QModelIndex&)), this, SLOT(OnItemSelected(const QModelIndex&))); - //connect(m_pBoxTree->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(OnItemSelected(QModelIndex))); - - m_pPanelSplitter->addWidget(m_pBoxTree); - */ m_pBoxView = new CSbieView(); m_pPanelSplitter->addWidget(m_pBoxView); @@ -246,6 +213,8 @@ CSandMan::CSandMan(QWidget *parent) m_pKeepTerminated->setCheckable(true); m_pMenuOptions = menuBar()->addMenu(tr("&Options")); + m_pMenuSettings = m_pMenuOptions->addAction(MakeActionIcon(":/Actions/Settings"), tr("Global Settings"), this, SLOT(OnSettings())); + m_pMenuOptions->addSeparator(); m_pEditIni = m_pMenuOptions->addAction(QIcon(":/Actions/EditIni"), tr("Edit ini file"), this, SLOT(OnEditIni())); m_pReloadIni = m_pMenuOptions->addAction(QIcon(":/Actions/ReloadIni"), tr("Reload ini file"), this, SLOT(OnReloadIni())); m_pMenuOptions->addSeparator(); @@ -264,6 +233,8 @@ CSandMan::CSandMan(QWidget *parent) m_pAbout = m_pMenuHelp->addAction(QIcon(":/SandMan.png"), tr("About Sandboxie-Plus"), this, SLOT(OnAbout())); + m_pToolBar->addAction(m_pMenuSettings); + m_pToolBar->addSeparator(); //m_pToolBar->addAction(m_pMenuNew); //m_pToolBar->addAction(m_pMenuEmptyAll); @@ -341,18 +312,22 @@ CSandMan::CSandMan(QWidget *parent) if (theConf->GetBool("Options/NoStatusBar", false)) statusBar()->hide(); - else if (theConf->GetBool("Options/NoSizeGrip", false)) - statusBar()->setSizeGripEnabled(false); + //else if (theConf->GetBool("Options/NoSizeGrip", false)) + // statusBar()->setSizeGripEnabled(false); m_pKeepTerminated->setChecked(theConf->GetBool("Options/KeepTerminated")); m_pProgressDialog = new CProgressDialog("Maintenance operation progress...", this); m_pProgressDialog->setWindowModality(Qt::ApplicationModal); + if (!bAutoRun) + show(); + if (CSbieUtils::IsRunning(CSbieUtils::eAll) || theConf->GetBool("Options/StartIfStopped", true)) ConnectSbie(); connect(theAPI, SIGNAL(LogMessage(const QString&, bool)), this, SLOT(OnLogMessage(const QString&, bool))); + connect(theAPI, SIGNAL(NotAuthorized(bool, bool&)), this, SLOT(OnNotAuthorized(bool, bool&)), Qt::DirectConnection); m_uTimerID = startTimer(250); } @@ -395,6 +370,9 @@ void CSandMan::closeEvent(QCloseEvent *e) { hide(); + if (theAPI->GetGlobalSettings()->GetBool("ForgetPassword", false)) + theAPI->ClearPassword(); + e->ignore(); return; } @@ -486,8 +464,6 @@ void CSandMan::timerEvent(QTimerEvent* pEvent) theAPI->UpdateProcesses(m_pKeepTerminated->isChecked()); } - m_pBoxView->Refresh(); - if (m_bIconEmpty != (theAPI->TotalProcesses() == 0)) { m_bIconEmpty = (theAPI->TotalProcesses() == 0); @@ -496,16 +472,10 @@ void CSandMan::timerEvent(QTimerEvent* pEvent) m_pTrayIcon->setIcon(Icon); } - /*QList Added = m_pBoxModel->Sync(theAPI->GetAllBoxes()); + if (!isVisible() || windowState().testFlag(Qt::WindowMinimized)) + return; - if (m_pBoxModel->IsTree()) - { - QTimer::singleShot(100, this, [this, Added]() { - foreach(const QVariant ID, Added) { - m_pBoxTree->expand(m_pSortProxy->mapFromSource(m_pBoxModel->FindIndex(ID))); - } - }); - }*/ + m_pBoxView->Refresh(); OnSelectionChanged(); } @@ -605,7 +575,7 @@ void CSandMan::OnLogMessage(const QString& Message, bool bNotify) if (bNotify) { - int iNotify = theConf->GetInt("Options/Notifications", 1); + int iNotify = theConf->GetInt("Options/ShowNotifications", 1); if (iNotify & 1) m_pTrayIcon->showMessage("Sandboxie-Plus", Message); if (iNotify & 2) @@ -613,25 +583,36 @@ void CSandMan::OnLogMessage(const QString& Message, bool bNotify) } } -/* -void CSandMan::OnResetColumns() +void CSandMan::OnNotAuthorized(bool bLoginRequired, bool& bRetry) { - for (int i = 0; i < m_pBoxModel->columnCount(); i++) - m_pBoxTree->SetColumnHidden(i, false); -} + if (!bLoginRequired) + { + QMessageBox::warning(this, "Sandboxie-Plus", tr("Only Administrators can change the config.")); + return; + } -void CSandMan::OnColumnsChanged() -{ - m_pBoxModel->Sync(theAPI->GetAllBoxes()); + static bool LoginOpen = false; + if (LoginOpen) + return; + LoginOpen = true; + for (;;) + { + QString Value = QInputDialog::getText(this, "Sandboxie-Plus", tr("Please enter the configuration password."), QLineEdit::Password); + if (Value.isEmpty()) + break; + SB_STATUS Status = theAPI->UnlockConfig(Value); + if (!Status.IsError()) { + bRetry = true; + break; + } + QMessageBox::warning(this, "Sandboxie-Plus", tr("Login Failed: %1").arg(Status.GetText())); + } + LoginOpen = false; } -void CSandMan::OnMenu(const QPoint& Point) -{ -}*/ - void CSandMan::OnNewBox() { - QString Value = QInputDialog::getText(this, "Sandboxie-Plus", "Please enter a name for the new Sandbox.", QLineEdit::Normal, "NewBox"); + QString Value = QInputDialog::getText(this, "Sandboxie-Plus", tr("Please enter a name for the new Sandbox."), QLineEdit::Normal, "NewBox"); if (Value.isEmpty()) return; theAPI->CreateBox(Value); @@ -784,6 +765,23 @@ void CSandMan::OnSetKeep() theAPI->UpdateProcesses(false); } +void CSandMan::OnSettings() +{ + CSettingsWindow* pSettingsWindow = new CSettingsWindow(this); + connect(pSettingsWindow, SIGNAL(OptionsChanged()), this, SLOT(UpdateSettings())); + pSettingsWindow->show(); +} + +void CSandMan::UpdateSettings() +{ + SetDarkTheme(theConf->GetBool("Options/DarkTheme", false)); + + if (theConf->GetBool("Options/ShowSysTray", true)) + m_pTrayIcon->show(); + else + m_pTrayIcon->hide(); +} + void CSandMan::OnEditIni() { if (theConf->GetBool("Options/NoEditInfo", true)) @@ -901,7 +899,12 @@ void CSandMan::OnSysTray(QSystemTrayIcon::ActivationReason Reason) { if(TriggerSet) NullifyTrigger = true; + hide(); + + if (theAPI->GetGlobalSettings()->GetBool("ForgetPassword", false)) + theAPI->ClearPassword(); + break; } show(); @@ -976,6 +979,62 @@ void CSandMan::OnAbout() QDesktopServices::openUrl(QUrl("https://www.patreon.com/DavidXanatos")); } +void CSandMan::SetDarkTheme(bool bDark) +{ + if (bDark) + { + QApplication::setStyle(QStyleFactory::create("Fusion")); + QPalette palette; + palette.setColor(QPalette::Window, QColor(53, 53, 53)); + palette.setColor(QPalette::WindowText, Qt::white); + palette.setColor(QPalette::Base, QColor(25, 25, 25)); + palette.setColor(QPalette::AlternateBase, QColor(53, 53, 53)); + palette.setColor(QPalette::ToolTipBase, Qt::white); + palette.setColor(QPalette::ToolTipText, Qt::white); + palette.setColor(QPalette::Text, Qt::white); + palette.setColor(QPalette::Button, QColor(53, 53, 53)); + palette.setColor(QPalette::ButtonText, Qt::white); + palette.setColor(QPalette::BrightText, Qt::red); + palette.setColor(QPalette::Link, QColor(218, 130, 42)); + palette.setColor(QPalette::Highlight, QColor(42, 130, 218)); + palette.setColor(QPalette::HighlightedText, Qt::black); + QApplication::setPalette(palette); + } + else + { + QApplication::setStyle(QStyleFactory::create(m_DefaultStyle)); + QApplication::setPalette(m_DefaultPalett); + } + + CTreeItemModel::SetDarkMode(bDark); + CListItemModel::SetDarkMode(bDark); +} + +void CSandMan::LoadLanguage() +{ + qApp->removeTranslator(&m_Translator); + m_Translation.clear(); + + QString Lang = theConf->GetString("Options/Language"); + if (!Lang.isEmpty()) + { + QString LangAux = Lang; // Short version as fallback + LangAux.truncate(LangAux.lastIndexOf('_')); + + QString LangPath = QApplication::applicationDirPath() + "/translations/taskexplorer_"; + bool bAux = false; + if (QFile::exists(LangPath + Lang + ".qm") || (bAux = QFile::exists(LangPath + LangAux + ".qm"))) + { + QFile File(LangPath + (bAux ? LangAux : Lang) + ".qm"); + File.open(QFile::ReadOnly); + m_Translation = File.readAll(); + } + + if (!m_Translation.isEmpty() && m_Translator.load((const uchar*)m_Translation.data(), m_Translation.size())) + qApp->installTranslator(&m_Translator); + } +} + ////////////////////////////////////////////////////////////////////////////////////////// // /* diff --git a/SandboxiePlus/SandMan/SandMan.h b/SandboxiePlus/SandMan/SandMan.h index f220ede9c3..619d113f3c 100644 --- a/SandboxiePlus/SandMan/SandMan.h +++ b/SandboxiePlus/SandMan/SandMan.h @@ -8,10 +8,11 @@ #include "../MiscHelpers/Common/ProgressDialog.h" #include "Models/ResMonModel.h" #include "Models/ApiMonModel.h" +#include #define VERSION_MJR 0 #define VERSION_MIN 3 -#define VERSION_REV 0 +#define VERSION_REV 5 #define VERSION_UPD 0 @@ -60,13 +61,13 @@ public slots: void OnStatusChanged(); void OnLogMessage(const QString& Message, bool bNotify = false); + void OnNotAuthorized(bool bLoginRequired, bool& bRetry); + + void UpdateSettings(); + private slots: void OnSelectionChanged(); - //void OnResetColumns(); - //void OnColumnsChanged(); - //void OnMenu(const QPoint& Point); - void OnMenuHover(QAction* action); void OnNewBox(); @@ -76,6 +77,7 @@ private slots: void OnCleanUp(); void OnSetKeep(); + void OnSettings(); void OnEditIni(); void OnReloadIni(); void OnSetMonitoring(); @@ -96,9 +98,6 @@ private slots: QSplitter* m_pLogSplitter; - //QTreeViewEx* m_pBoxTree; - //CSbieModel* m_pBoxModel; - //QSortFilterProxyModel* m_pSortProxy; CSbieView* m_pBoxView; @@ -139,6 +138,7 @@ private slots: QAction* m_pKeepTerminated; QMenu* m_pMenuOptions; + QAction* m_pMenuSettings; QAction* m_pEditIni; QAction* m_pReloadIni; QAction* m_pEnableMonitoring; @@ -156,6 +156,14 @@ private slots: bool m_bExit; CProgressDialog* m_pProgressDialog; + + void SetDarkTheme(bool bDark); + QString m_DefaultStyle; + QPalette m_DefaultPalett; + + void LoadLanguage(); + QTranslator m_Translator; + QByteArray m_Translation; }; extern CSandMan* theGUI; \ No newline at end of file diff --git a/SandboxiePlus/SandMan/SandMan.vcxproj b/SandboxiePlus/SandMan/SandMan.vcxproj index 232e5294ee..5fc75e951c 100644 --- a/SandboxiePlus/SandMan/SandMan.vcxproj +++ b/SandboxiePlus/SandMan/SandMan.vcxproj @@ -193,7 +193,9 @@ + + @@ -205,14 +207,20 @@ Create + + + + + + @@ -226,6 +234,10 @@ + + + + diff --git a/SandboxiePlus/SandMan/SandMan.vcxproj.filters b/SandboxiePlus/SandMan/SandMan.vcxproj.filters index a237d55736..7335e4f919 100644 --- a/SandboxiePlus/SandMan/SandMan.vcxproj.filters +++ b/SandboxiePlus/SandMan/SandMan.vcxproj.filters @@ -35,6 +35,12 @@ {6accf3ae-da17-4c0f-ba83-214e3874b029} + + {20d5954b-be86-4a34-948d-00954dcfd07b} + + + {d0068730-c9fb-49ef-90ec-8ecd2131ae47} + @@ -64,6 +70,18 @@ SandMan + + Models + + + Helpers + + + Windows + + + Windows + @@ -72,6 +90,9 @@ Resource Files + + Helpers + @@ -95,6 +116,15 @@ SandMan + + Models + + + Windows + + + Windows + @@ -111,4 +141,12 @@ Resource Files + + + Form Files + + + Form Files + + \ No newline at end of file diff --git a/SandboxiePlus/SandMan/SbiePlusAPI.cpp b/SandboxiePlus/SandMan/SbiePlusAPI.cpp index f49c016b3d..c2100fd31a 100644 --- a/SandboxiePlus/SandMan/SbiePlusAPI.cpp +++ b/SandboxiePlus/SandMan/SbiePlusAPI.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "SbiePlusAPI.h" +#include "..\MiscHelpers\Common\Common.h" CSbiePlusAPI::CSbiePlusAPI(QObject* parent) : CSbieAPI(parent) @@ -120,14 +121,14 @@ void CSandBoxPlus::SetLogApi(bool bEnable) void CSandBoxPlus::SetINetBlock(bool bEnable) { if (bEnable) - DelValue("ClosedFilePath", "InternetAccessDevices"); + DelValue("ClosedFilePath", "!,InternetAccessDevices"); else InsertText("ClosedFilePath", "InternetAccessDevices"); } void CSandBoxPlus::SetAllowShares(bool bEnable) { - SetBool("BlockNetworkFiles", bEnable); + SetBool("BlockNetworkFiles", !bEnable); } void CSandBoxPlus::SetDropRights(bool bEnable) diff --git a/SandboxiePlus/SandMan/Views/SbieView.cpp b/SandboxiePlus/SandMan/Views/SbieView.cpp index 65e93c20cd..0be4a0f883 100644 --- a/SandboxiePlus/SandMan/Views/SbieView.cpp +++ b/SandboxiePlus/SandMan/Views/SbieView.cpp @@ -4,6 +4,7 @@ #include "../QSbieAPI/SbieAPI.h" #include "../../MiscHelpers/Common/SortFilterProxyModel.h" #include "../../MiscHelpers/Common/Settings.h" +#include "../Windows/OptionsWindow.h" CSbieView::CSbieView(QWidget* parent) : CPanelView(parent) { @@ -32,6 +33,7 @@ CSbieView::CSbieView(QWidget* parent) : CPanelView(parent) m_pSbieTree->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_pSbieTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(OnMenu(const QPoint &))); + connect(m_pSbieTree, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(OnDoubleClicked(const QModelIndex&))); connect(m_pSbieTree->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), SLOT(ProcessSelection(QItemSelection, QItemSelection))); //connect(theGUI, SIGNAL(ReloadPanels()), m_pSbieModel, SLOT(Clear())); @@ -62,7 +64,7 @@ CSbieView::CSbieView(QWidget* parent) : CPanelView(parent) m_pMenuPresetsShares->setCheckable(true); m_pMenuPresetsNoAdmin = m_pMenuPresets->addAction(tr("Drop Admin Rights"), this, SLOT(OnSandBoxAction())); m_pMenuPresetsNoAdmin->setCheckable(true); - + m_pMenuOptions = m_pMenu->addAction(tr("Sandbox Options"), this, SLOT(OnSandBoxAction())); m_pMenuRename = m_pMenu->addAction(tr("Rename Sandbox"), this, SLOT(OnSandBoxAction())); m_pMenuRemove = m_pMenu->addAction(tr("Remove Sandbox"), this, SLOT(OnSandBoxAction())); m_iMenuBox = m_pMenu->actions().count(); @@ -168,6 +170,7 @@ void CSbieView::OnMenu(const QPoint& Point) m_pMenuPresetsShares->setChecked(pBox && pBox.objectCast()->HasSharesAccess()); m_pMenuPresetsNoAdmin->setChecked(pBox && pBox.objectCast()->IsDropRights()); + m_pMenuOptions->setEnabled(iSandBoxeCount == 1); for (int i = m_iMenuBox; i < m_iMenuProc; i++) MenuActions[i]->setVisible(iProcessCount > 0 && iSandBoxeCount == 0); @@ -207,11 +210,15 @@ void CSbieView::OnSandBoxAction() SandBoxes.first().objectCast()->SetAllowShares(m_pMenuPresetsShares->isChecked()); else if (Action == m_pMenuPresetsNoAdmin) SandBoxes.first().objectCast()->SetDropRights(m_pMenuPresetsNoAdmin->isChecked()); - + else if (Action == m_pMenuOptions) + { + COptionsWindow* pOptionsWindow = new COptionsWindow(SandBoxes.first(), SandBoxes.first()->GetName(), this); + pOptionsWindow->show(); + } else if (Action == m_pMenuRename) { QString OldValue = SandBoxes.first()->GetName().replace("_", " "); - QString Value = QInputDialog::getText(this, "Sandboxie-Plus", "Please enter a new name for the Sandbox.", QLineEdit::Normal, OldValue); + QString Value = QInputDialog::getText(this, "Sandboxie-Plus", tr("Please enter a new name for the Sandbox."), QLineEdit::Normal, OldValue); if (Value.isEmpty() || Value == OldValue) return; Results.append((SandBoxes.first()->RenameBox(Value))); @@ -281,6 +288,17 @@ void CSbieView::OnProcessAction() CSandMan::CheckResults(Results); } +void CSbieView::OnDoubleClicked(const QModelIndex& index) +{ + QModelIndex ModelIndex = m_pSortProxy->mapToSource(index); + CSandBoxPtr pBox = m_pSbieModel->GetSandBox(ModelIndex); + if (pBox.isNull()) + return; + + COptionsWindow* pOptionsWindow = new COptionsWindow(pBox, pBox->GetName(), this); + pOptionsWindow->show(); +} + void CSbieView::ProcessSelection(const QItemSelection& selected, const QItemSelection& deselected) { if (selected.empty()) diff --git a/SandboxiePlus/SandMan/Views/SbieView.h b/SandboxiePlus/SandMan/Views/SbieView.h index 8c3e41a3c5..9e8c7a480d 100644 --- a/SandboxiePlus/SandMan/Views/SbieView.h +++ b/SandboxiePlus/SandMan/Views/SbieView.h @@ -21,6 +21,7 @@ public slots: private slots: void OnToolTipCallback(const QVariant& ID, QString& ToolTip); + void OnDoubleClicked(const QModelIndex& index); void ProcessSelection(const QItemSelection& selected, const QItemSelection& deselected); void OnSandBoxAction(); @@ -52,6 +53,7 @@ private slots: QAction* m_pMenuPresetsINet; QAction* m_pMenuPresetsShares; QAction* m_pMenuPresetsNoAdmin; + QAction* m_pMenuOptions; QAction* m_pMenuEmptyBox; QAction* m_pMenuCleanUp; QAction* m_pMenuRemove; diff --git a/SandboxiePlus/SandMan/Windows/OptionsWindow.cpp b/SandboxiePlus/SandMan/Windows/OptionsWindow.cpp new file mode 100644 index 0000000000..3f5753fe60 --- /dev/null +++ b/SandboxiePlus/SandMan/Windows/OptionsWindow.cpp @@ -0,0 +1,1502 @@ +#include "stdafx.h" +#include "OptionsWindow.h" +#include "SandMan.h" +#include "../MiscHelpers/Common/Settings.h" +#include "../MiscHelpers/Common/Common.h" +#include "../MiscHelpers/Common/ComboInputDialog.h" +#include "Helpers/WinAdmin.h" +#include + +class CustomTabStyle : public QProxyStyle { +public: + QSize sizeFromContents(ContentsType type, const QStyleOption* option, + const QSize& size, const QWidget* widget) const { + QSize s = QProxyStyle::sizeFromContents(type, option, size, widget); + if (type == QStyle::CT_TabBarTab) { + s.transpose(); + s.setHeight(s.height() * 15 / 10); + } + return s; + } + + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const { + if (element == CE_TabBarTabLabel) { + if (const QStyleOptionTab* tab = qstyleoption_cast(option)) { + QStyleOptionTab opt(*tab); + opt.shape = QTabBar::RoundedNorth; + QProxyStyle::drawControl(element, &opt, painter, widget); + return; + } + } + QProxyStyle::drawControl(element, option, painter, widget); + } +}; + + +COptionsWindow::COptionsWindow(const QSharedPointer& pBox, const QString& Name, QWidget *parent) + : QMainWindow(parent) +{ + m_pBox = pBox; + + m_Template = pBox->GetName().left(9).compare("Template_", Qt::CaseInsensitive) == 0; + bool ReadOnly = /*pBox->GetAPI()->IsConfigLocked() ||*/ (m_Template && pBox->GetName().mid(9, 6).compare("Local_", Qt::CaseInsensitive) != 0); + + + QWidget* centralWidget = new QWidget(); + ui.setupUi(centralWidget); + this->setCentralWidget(centralWidget); + this->setWindowTitle(tr("Sandboxie Plus - '%1' Options").arg(Name)); + + ui.tabs->setTabPosition(QTabWidget::West); + ui.tabs->tabBar()->setStyle(new CustomTabStyle()); + + if (m_Template) + { + ui.tabGeneral->setEnabled(false); + ui.tabStart->setEnabled(false); + ui.tabRestrictions->setEnabled(false); + ui.tabInternet->setEnabled(false); + ui.tabAdvanced->setEnabled(false); + ui.tabTemplates->setEnabled(false); + + for (int i = 0; i < ui.tabs->count(); i++) + ui.tabs->setTabEnabled(i, ui.tabs->widget(i)->isEnabled()); + + ui.tabs->setCurrentIndex(ui.tabs->indexOf(ui.tabAccess)); + + ui.chkShowForceTmpl->setEnabled(false); + ui.chkShowStopTmpl->setEnabled(false); + ui.chkShowAccessTmpl->setEnabled(false); + } + + m_ConfigDirty = true; + + // General + ui.cmbBoxIndicator->addItem(tr("Don't alter the window title"), "-"); + ui.cmbBoxIndicator->addItem(tr("Display [#] indicator only"), "n"); + ui.cmbBoxIndicator->addItem(tr("Display box name in title"), "y"); + + ui.cmbBoxBorder->addItem(tr("Border disabled"), "off"); + ui.cmbBoxBorder->addItem(tr("Show only when title is in focus"), "ttl"); + ui.cmbBoxBorder->addItem(tr("Always show"), "on"); + + connect(ui.cmbBoxIndicator, SIGNAL(currentIndexChanged(int)), this, SLOT(OnGeneralChanged())); + connect(ui.cmbBoxBorder, SIGNAL(currentIndexChanged(int)), this, SLOT(OnGeneralChanged())); + connect(ui.btnBorderColor, SIGNAL(pressed()), this, SLOT(OnPickColor())); + connect(ui.txtCopyLimit, SIGNAL(textChanged(const QString&)), this, SLOT(OnGeneralChanged())); + connect(ui.chkNoCopyWarn, SIGNAL(clicked(bool)), this, SLOT(OnGeneralChanged())); + // + + // Groupes + connect(ui.btnAddGroup, SIGNAL(pressed()), this, SLOT(OnAddGroup())); + connect(ui.btnAddProg, SIGNAL(pressed()), this, SLOT(OnAddProg())); + connect(ui.btnDelProg, SIGNAL(pressed()), this, SLOT(OnDelProg())); + // + + // Force + connect(ui.btnForceProg, SIGNAL(pressed()), this, SLOT(OnForceProg())); + connect(ui.btnForceDir, SIGNAL(pressed()), this, SLOT(OnForceDir())); + connect(ui.btnDelForce, SIGNAL(pressed()), this, SLOT(OnDelForce())); + connect(ui.chkShowForceTmpl, SIGNAL(clicked(bool)), this, SLOT(OnShowForceTmpl())); + // + + // Stop + connect(ui.btnAddLingering, SIGNAL(pressed()), this, SLOT(OnAddLingering())); + connect(ui.btnAddLeader, SIGNAL(pressed()), this, SLOT(OnAddLeader())); + connect(ui.btnDelStopProg, SIGNAL(pressed()), this, SLOT(OnDelStopProg())); + connect(ui.chkShowStopTmpl, SIGNAL(clicked(bool)), this, SLOT(OnShowStopTmpl())); + // + + // Start + connect(ui.chkRestrictStart, SIGNAL(clicked(bool)), this, SLOT(OnRestrictStart())); + connect(ui.btnAddStartProg, SIGNAL(pressed()), this, SLOT(OnAddStartProg())); + connect(ui.btnDelStartProg, SIGNAL(pressed()), this, SLOT(OnDelStartProg())); + connect(ui.chkStartBlockMsg, SIGNAL(clicked(bool)), this, SLOT(OnStartChanged())); + // + + // Restrictions + connect(ui.chkBlockShare, SIGNAL(clicked(bool)), this, SLOT(OnRestrictionChanged())); + connect(ui.chkDropRights, SIGNAL(clicked(bool)), this, SLOT(OnRestrictionChanged())); + connect(ui.chkNoDefaultCOM, SIGNAL(clicked(bool)), this, SLOT(OnRestrictionChanged())); + connect(ui.chkProtectSCM, SIGNAL(clicked(bool)), this, SLOT(OnRestrictionChanged())); + connect(ui.chkProtectRpcSs, SIGNAL(clicked(bool)), this, SLOT(OnRestrictionChanged())); + connect(ui.chkProtectSystem, SIGNAL(clicked(bool)), this, SLOT(OnRestrictionChanged())); + // + + // INet + connect(ui.chkBlockINet, SIGNAL(clicked(bool)), this, SLOT(OnBlockINet())); + connect(ui.btnAddINetProg, SIGNAL(pressed()), this, SLOT(OnAddINetProg())); + connect(ui.btnDelINetProg, SIGNAL(pressed()), this, SLOT(OnDelINetProg())); + connect(ui.chkINetBlockMsg, SIGNAL(clicked(bool)), this, SLOT(OnINetBlockChanged())); + // + + // Access + connect(ui.btnAddFile, SIGNAL(pressed()), this, SLOT(OnAddFile())); + connect(ui.btnAddKey, SIGNAL(pressed()), this, SLOT(OnAddKey())); + connect(ui.btnAddIPC, SIGNAL(pressed()), this, SLOT(OnAddIPC())); + connect(ui.btnAddClsId, SIGNAL(pressed()), this, SLOT(OnAddClsId())); + connect(ui.btnAddCOM, SIGNAL(pressed()), this, SLOT(OnAddCOM())); + // todo: add priority by order + ui.btnMoveUp->setVisible(false); + ui.btnMoveDown->setVisible(false); + connect(ui.chkShowAccessTmpl, SIGNAL(clicked(bool)), this, SLOT(OnShowAccessTmpl())); + connect(ui.btnDelAccess, SIGNAL(pressed()), this, SLOT(OnDelAccess())); + + connect(ui.treeAccess, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(OnAccessItemClicked(QTreeWidgetItem*, int))); + connect(ui.treeAccess, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(OnAccessItemDoubleClicked(QTreeWidgetItem*, int))); + // + + // Advanced + ui.cmbEmptyCmd->addItem("%SystemRoot%\\System32\\cmd.exe /c RMDIR /s /q \"%SANDBOX%\""); + + connect(ui.chkProtectBox, SIGNAL(clicked(bool)), this, SLOT(OnAdvancedChanged())); + connect(ui.chkAutoEmpty, SIGNAL(clicked(bool)), this, SLOT(OnAdvancedChanged())); + connect(ui.cmbEmptyCmd, SIGNAL(currentTextChanged(const QString&)), this, SLOT(OnAdvancedChanged())); + connect(ui.btnAddUser, SIGNAL(pressed()), this, SLOT(OnAddUser())); + connect(ui.btnDelUser, SIGNAL(pressed()), this, SLOT(OnDelUser())); + // + + // Templates + connect(ui.cmbCategories, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFilterTemplates())); + connect(ui.treeTemplates, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(OnTemplateClicked(QTreeWidgetItem*, int))); + connect(ui.treeTemplates, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(OnTemplateDoubleClicked(QTreeWidgetItem*, int))); + // + + connect(ui.tabs, SIGNAL(currentChanged(int)), this, SLOT(OnTab())); + + // edit + connect(ui.btnEditIni, SIGNAL(pressed()), this, SLOT(OnEditIni())); + connect(ui.btnSaveIni, SIGNAL(pressed()), this, SLOT(OnSaveIni())); + connect(ui.btnCancelEdit, SIGNAL(pressed()), this, SLOT(OnCancelEdit())); + // + + connect(ui.buttonBox->button(QDialogButtonBox::Ok), SIGNAL(pressed()), this, SLOT(accept())); + connect(ui.buttonBox->button(QDialogButtonBox::Apply), SIGNAL(pressed()), this, SLOT(apply())); + connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + if (ReadOnly) { + ui.btnEditIni->setEnabled(false); + ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); + } + + OnTab(); // -> LoadConfig(); + + restoreGeometry(theConf->GetBlob("OptionsWindow/Window_Geometry")); +} + +COptionsWindow::~COptionsWindow() +{ + theConf->SetBlob("OptionsWindow/Window_Geometry",saveGeometry()); +} + +void COptionsWindow::closeEvent(QCloseEvent *e) +{ + this->deleteLater(); +} + +void COptionsWindow::LoadConfig() +{ + m_ConfigDirty = false; + + { + QString BoxNameTitle = m_pBox->GetText("BoxNameTitle", "n"); + ui.cmbBoxIndicator->setCurrentIndex(ui.cmbBoxIndicator->findData(BoxNameTitle.toLower())); + + QStringList BorderCfg = m_pBox->GetText("BorderColor").split(","); + ui.cmbBoxBorder->setCurrentIndex(ui.cmbBoxBorder->findData(BorderCfg.size() >= 2 ? BorderCfg[1].toLower() : "on")); + m_BorderColor = QColor("#" + BorderCfg[0].mid(5, 2) + BorderCfg[0].mid(3, 2) + BorderCfg[0].mid(1, 2)); + ui.btnBorderColor->setStyleSheet("background-color: " + m_BorderColor.name()); + + ui.txtCopyLimit->setText(QString::number(m_pBox->GetNum("CopyLimitKb", 80 * 1024))); + ui.chkNoCopyWarn->setChecked(!m_pBox->GetBool("CopyLimitSilent", false)); + + m_GeneralChanged = false; + } + + LoadGroups(); + + LoadForced(); + + LoadStop(); + + { + ui.chkStartBlockMsg->setChecked(m_pBox->GetBool("NotifyStartRunAccessDenied", true)); + + m_StartChanged = false; + } + + { + ui.chkBlockShare->setChecked(m_pBox->GetBool("BlockNetworkFiles", true)); + ui.chkDropRights->setChecked(m_pBox->GetBool("DropAdminRights", false)); + ui.chkNoDefaultCOM->setChecked(!m_pBox->GetBool("OpenDefaultClsid", true)); + ui.chkProtectSCM->setChecked(!m_pBox->GetBool("UnrestrictedSCM", false)); + ui.chkProtectRpcSs->setChecked(m_pBox->GetBool("ProtectRpcSs", false)); + ui.chkProtectSystem->setChecked(!m_pBox->GetBool("ExposeBoxedSystem", false)); + + m_RestrictionChanged = false; + } + + { + ui.chkINetBlockMsg->setChecked(m_pBox->GetBool("NotifyInternetAccessDenied", true)); + + m_INetBlockChanged = false; + } + + LoadAccessList(); + + { + ui.chkProtectBox->setChecked(m_pBox->GetBool("NeverDelete", false)); + ui.chkAutoEmpty->setChecked(m_pBox->GetBool("AutoDelete", false)); + ui.cmbEmptyCmd->setCurrentText(m_pBox->GetText("DeleteCommand", "")); + + QStringList Users = m_pBox->GetText("Enabled").split(","); + ui.lstUsers->clear(); + if (Users.count() > 1) + ui.lstUsers->addItems(Users.mid(1)); + + m_AdvancedChanged = false; + } + + { + LoadTemplates(); + m_TemplatesChanged = false; + } +} + +void COptionsWindow::SaveConfig() +{ + if (m_GeneralChanged) + { + m_pBox->SetText("BoxNameTitle", ui.cmbBoxIndicator->currentData().toString()); + + QStringList BorderCfg; + BorderCfg.append(QString("#%1,%2,%3").arg(m_BorderColor.blue(), 2, 16, QChar('0')).arg(m_BorderColor.green(), 2, 16, QChar('0')).arg(m_BorderColor.red(), 2, 16, QChar('0'))); + BorderCfg.append(ui.cmbBoxBorder->currentData().toString()); + //BorderCfg.append(5) // width + m_pBox->SetText("BorderColor", BorderCfg.join(",")); + + m_pBox->SetNum("CopyLimitKb", ui.txtCopyLimit->text().toInt()); + m_pBox->SetBool("CopyLimitSilent", ui.chkNoCopyWarn->isChecked()); + + m_GeneralChanged = false; + } + + if (m_GroupsChanged) + SaveGroups(); + + if (m_ForcedChanged) + SaveForced(); + + if (m_StopChanged) + SaveStop(); + + if (m_StartChanged) + { + m_pBox->SetBool("NotifyStartRunAccessDenied", ui.chkStartBlockMsg->isChecked()); + + m_StartChanged = false; + } + + if (m_RestrictionChanged) + { + m_pBox->SetBool("BlockNetworkFiles", ui.chkBlockShare->isChecked()); + m_pBox->SetBool("DropAdminRights", ui.chkDropRights->isChecked()); + m_pBox->SetBool("OpenDefaultClsid", ui.chkNoDefaultCOM->isChecked()); + m_pBox->SetBool("UnrestrictedSCM", ui.chkProtectSCM->isChecked()); + m_pBox->SetBool("ProtectRpcSs", ui.chkProtectRpcSs->isChecked()); + m_pBox->SetBool("ExposeBoxedSystem", ui.chkProtectSystem->isChecked()); + + m_RestrictionChanged = false; + } + + if (m_INetBlockChanged) + { + m_pBox->SetBool("NotifyInternetAccessDenied", ui.chkINetBlockMsg->isChecked()); + + m_INetBlockChanged = false; + } + + if (m_AccessChanged) + SaveAccessList(); + + if (m_AdvancedChanged) + { + m_pBox->SetBool("NeverDelete", ui.chkProtectBox->isChecked()); + m_pBox->SetBool("AutoDelete", ui.chkAutoEmpty->isChecked()); + m_pBox->SetText("DeleteCommand", ui.cmbEmptyCmd->currentText()); + + QStringList Users; + for (int i = 0; i < ui.lstUsers->count(); i++) + Users.append(ui.lstUsers->item(i)->text()); + m_pBox->SetText("Enabled", Users.count() > 0 ? "y," + Users.join(",") : "y"); + + m_AdvancedChanged = false; + } + + if (m_TemplatesChanged) + SaveTemplates(); +} + +void COptionsWindow::apply() +{ + if (!ui.btnEditIni->isEnabled()) + SaveIniSection(); + else + SaveConfig(); + + LoadConfig(); + + emit OptionsChanged(); +} + +void COptionsWindow::accept() +{ + apply(); + + this->close(); +} + +void COptionsWindow::reject() +{ + this->close(); +} + +void COptionsWindow::OnGeneralChanged() +{ + m_GeneralChanged = true; + + ui.lblCopyLimit->setText(tr("kilobytes (%1)").arg(FormatSize(ui.txtCopyLimit->text().toInt() * 1024))); +} + +void COptionsWindow::OnPickColor() +{ + QColor color = QColorDialog::getColor(m_BorderColor, this, "Select color"); + if (!color.isValid()) + return; + m_GeneralChanged = true; + m_BorderColor = color; + ui.btnBorderColor->setStyleSheet("background-color: " + m_BorderColor.name()); +} + +void COptionsWindow::SetProgramItem(QString Program, QTreeWidgetItem* pItem, int Column) +{ + pItem->setData(Column, Qt::UserRole, Program); + if (Program.left(1) == "<") + Program = tr("Group: %1").arg(Program.mid(1, Program.length() - 2)); + pItem->setText(Column, Program); +} + +void COptionsWindow::LoadGroups() +{ + m_TemplateGroups.clear(); + ui.treeGroups->clear(); + + QMultiMap GroupMap; // if we have a duplicate we want to know it + QSet LocalGroups; + + QStringList ProcessGroups = m_pBox->GetTextList("ProcessGroup", m_Template); + foreach(const QString& Group, ProcessGroups) + { + QStringList Entries = Group.split(","); + QString GroupName = Entries.takeFirst(); + GroupMap.insertMulti(GroupName, Entries); + LocalGroups.insert(GroupName); + } + + foreach(const QString& Template, m_pBox->GetTemplates()) + { + foreach(const QString& Group, m_pBox->GetTextListTmpl("ProcessGroup", Template)) + { + m_TemplateGroups.insert(Group); + QStringList Entries = Group.split(","); + QString GroupName = Entries.takeFirst(); + if (LocalGroups.contains(GroupName)) + continue; // local group definitions overwrite template once + GroupMap.insertMulti(GroupName, Entries); + } + } + + for(QMultiMap::iterator I = GroupMap.begin(); I != GroupMap.end(); ++I) + { + QString GroupName = I.key(); + QStringList Entries = I.value(); + QTreeWidgetItem* pItem = new QTreeWidgetItem(); + pItem->setData(0, Qt::UserRole, GroupName); + if (GroupName.length() > 2) + GroupName = GroupName.mid(1, GroupName.length() - 2); + pItem->setText(0, GroupName); + for (int i = 1; i < Entries.count(); i++) + { + QTreeWidgetItem* pSubItem = new QTreeWidgetItem(); + SetProgramItem(Entries[i], pSubItem, 0); + pItem->addChild(pSubItem); + } + ui.treeGroups->addTopLevelItem(pItem); + } + ui.treeGroups->expandAll(); + + m_GroupsChanged = false; +} + +void COptionsWindow::SaveGroups() +{ + QStringList ProcessGroups; + for (int i = 0; i < ui.treeGroups->topLevelItemCount(); i++) + { + QTreeWidgetItem* pItem = ui.treeGroups->topLevelItem(i); + QString GroupName = pItem->data(0, Qt::UserRole).toString(); + QStringList Programs; + for (int j = 0; j < pItem->childCount(); j++) + Programs.append(pItem->child(j)->data(0, Qt::UserRole).toString()); + QString Group = GroupName + "," + Programs.join(","); + if (m_TemplateGroups.contains(Group)) + continue; // don't save unchanged groups to local config + ProcessGroups.append(Group); + } + + m_pBox->UpdateTextList("ProcessGroup", ProcessGroups); + + m_GroupsChanged = false; +} + +void COptionsWindow::OnAddGroup() +{ + QString Value = QInputDialog::getText(this, "Sandboxie-Plus", tr("Please enter a name for the new group"), QLineEdit::Normal, "NewGroup"); + if (Value.isEmpty()) + return; + + for (int i = 0; i < ui.treeGroups->topLevelItemCount(); i++) { + QTreeWidgetItem* pItem = ui.treeGroups->topLevelItem(i); + if (pItem->text(0).compare(Value, Qt::CaseInsensitive) == 0) + return; + } + + QTreeWidgetItem* pItem = new QTreeWidgetItem(); + pItem->setText(0, Value); + pItem->setData(0, Qt::UserRole, "<" + Value + ">"); + ui.treeGroups->addTopLevelItem(pItem); + + m_GroupsChanged = true; +} + +QString COptionsWindow::SelectProgram(bool bOrGroup) +{ + CComboInputDialog progDialog(this); + progDialog.setText(tr("Enter program:")); + progDialog.setEditable(true); + + if (bOrGroup) + { + for (int i = 0; i < ui.treeGroups->topLevelItemCount(); i++) { + QTreeWidgetItem* pItem = ui.treeGroups->topLevelItem(i); + progDialog.addItem(tr("Group: %1").arg(pItem->text(0)), pItem->data(0, Qt::UserRole).toString()); + } + } + + progDialog.setValue(""); + + if (!progDialog.exec()) + return QString(); + + QString Program = progDialog.value(); + int Index = progDialog.findValue(Program); + if (Index != -1) + Program = progDialog.data().toString(); + + return Program; +} + +void COptionsWindow::OnAddProg() +{ + QTreeWidgetItem* pItem = ui.treeGroups->currentItem(); + while (pItem && pItem->parent()) + pItem = pItem->parent(); + + if (!pItem) + { + QMessageBox::warning(this, "SandboxiePlus", tr("Please sellect group first.")); + return; + } + + QString Value = SelectProgram(); + if (Value.isEmpty()) + return; + + QTreeWidgetItem* pSubItem = new QTreeWidgetItem(); + SetProgramItem(Value, pSubItem, 0); + pItem->addChild(pSubItem); + + m_GroupsChanged = true; +} + +void COptionsWindow::OnDelProg() +{ + QTreeWidgetItem* pItem = ui.treeGroups->currentItem(); + if (!pItem) + return; + + delete pItem; + + m_GroupsChanged = true; +} + +void COptionsWindow::CopyGroupToList(const QString& Groupe, QTreeWidget* pTree) +{ + pTree->clear(); + + for (int i = 0; i < ui.treeGroups->topLevelItemCount(); i++) + { + QTreeWidgetItem* pItem = ui.treeGroups->topLevelItem(i); + if (pItem->data(0, Qt::UserRole).toString().compare(Groupe, Qt::CaseInsensitive) == 0) + { + for (int j = 0; j < pItem->childCount(); j++) + { + QString Value = pItem->child(j)->data(0, Qt::UserRole).toString(); + + QTreeWidgetItem* pSubItem = new QTreeWidgetItem(); + SetProgramItem(Value, pSubItem, 0); + pTree->addTopLevelItem(pSubItem); + } + break; + } + } +} + +void COptionsWindow::LoadForced() +{ + ui.treeForced->clear(); + + foreach(const QString& Value, m_pBox->GetTextList("ForceProcess", m_Template)) + AddForcedEntry(Value, 1); + + foreach(const QString& Value, m_pBox->GetTextList("ForceFolder", m_Template)) + AddForcedEntry(Value, 2); + + if (ui.chkShowForceTmpl->isChecked()) + { + foreach(const QString& Template, m_pBox->GetTemplates()) + { + foreach(const QString& Value, m_pBox->GetTextListTmpl("ForceProcess", Template)) + AddForcedEntry(Value, 1, Template); + + foreach(const QString& Value, m_pBox->GetTextListTmpl("ForceFolder", Template)) + AddForcedEntry(Value, 2, Template); + } + } + + m_ForcedChanged = false; +} + +void COptionsWindow::AddForcedEntry(const QString& Name, int type, const QString& Template) +{ + QTreeWidgetItem* pItem = new QTreeWidgetItem(); + pItem->setText(0, (type == 1 ? tr("Process") : tr("Folder")) + (Template.isEmpty() ? "" : (" (" + Template + ")"))); + pItem->setData(0, Qt::UserRole, Template.isEmpty() ? type : -1); + SetProgramItem(Name, pItem, 1); + ui.treeForced->addTopLevelItem(pItem); +} + +void COptionsWindow::SaveForced() +{ + QStringList ForceProcess; + QStringList ForceFolder; + for (int i = 0; i < ui.treeForced->topLevelItemCount(); i++) + { + QTreeWidgetItem* pItem = ui.treeForced->topLevelItem(i); + int Type = pItem->data(0, Qt::UserRole).toInt(); + if (Type == -1) + continue; // entry from template + switch (Type) + { + case 1: ForceProcess.append(pItem->data(1, Qt::UserRole).toString()); break; + case 2: ForceFolder.append(pItem->data(1, Qt::UserRole).toString()); break; + } + } + + m_pBox->UpdateTextList("ForceProcess", ForceProcess); + m_pBox->UpdateTextList("ForceFolder", ForceFolder); + + m_ForcedChanged = false; +} + +void COptionsWindow::OnForceProg() +{ + QString Value = SelectProgram(); + if (Value.isEmpty()) + return; + AddForcedEntry(Value, 1); + m_ForcedChanged = true; +} + +void COptionsWindow::OnForceDir() +{ + QString Value = QFileDialog::getExistingDirectory(this, tr("Select Directory")); + if (Value.isEmpty()) + return; + AddForcedEntry(Value, 2); + m_ForcedChanged = true; +} + +void COptionsWindow::OnDelForce() +{ + DeleteAccessEntry(ui.treeForced->currentItem()); + m_ForcedChanged = true; +} + +void COptionsWindow::LoadStop() +{ + ui.treeStop->clear(); + + foreach(const QString& Value, m_pBox->GetTextList("LingerProcess", m_Template)) + AddStopEntry(Value, 1); + + foreach(const QString& Value, m_pBox->GetTextList("LeaderProcess", m_Template)) + AddStopEntry(Value, 2); + + if (ui.chkShowStopTmpl->isChecked()) + { + foreach(const QString& Template, m_pBox->GetTemplates()) + { + foreach(const QString& Value, m_pBox->GetTextListTmpl("LingerProcess", Template)) + AddStopEntry(Value, 1, Template); + + foreach(const QString& Value, m_pBox->GetTextListTmpl("LeaderProcess", Template)) + AddStopEntry(Value, 2, Template); + } + } + + m_StopChanged = false; +} + +void COptionsWindow::AddStopEntry(const QString& Name, int type, const QString& Template) +{ + QTreeWidgetItem* pItem = new QTreeWidgetItem(); + pItem->setText(0, (type == 1 ? tr("Lingerer") : tr("Leader")) + (Template.isEmpty() ? "" : (" (" + Template + ")"))); + pItem->setData(0, Qt::UserRole, Template.isEmpty() ? type : -1); + SetProgramItem(Name, pItem, 1); + ui.treeStop->addTopLevelItem(pItem); +} + +void COptionsWindow::SaveStop() +{ + QStringList LingerProcess; + QStringList LeaderProcess; + for (int i = 0; i < ui.treeForced->topLevelItemCount(); i++) + { + QTreeWidgetItem* pItem = ui.treeForced->topLevelItem(i); + int Type = pItem->data(0, Qt::UserRole).toInt(); + if (Type == -1) + continue; // entry from template + switch (Type) + { + case 1: LingerProcess.append(pItem->data(1, Qt::UserRole).toString()); break; + case 2: LeaderProcess.append(pItem->data(1, Qt::UserRole).toString()); break; + } + } + + m_pBox->UpdateTextList("LingerProcess", LingerProcess); + m_pBox->UpdateTextList("LeaderProcess", LeaderProcess); + + m_StopChanged = false; +} + +void COptionsWindow::OnAddLingering() +{ + QString Value = SelectProgram(); + if (Value.isEmpty()) + return; + AddStopEntry(Value, 1); + m_StopChanged = true; +} + +void COptionsWindow::OnAddLeader() +{ + QString Value = SelectProgram(); + if (Value.isEmpty()) + return; + AddStopEntry(Value, 2); + m_StopChanged = true; +} + +void COptionsWindow::OnDelStopProg() +{ + DeleteAccessEntry(ui.treeStop->currentItem()); + m_StopChanged = true; +} + +void COptionsWindow::OnRestrictStart() +{ + bool Enable = ui.chkRestrictStart->isChecked(); + if (Enable) + SetAccessEntry(eIPC, "!", eClosed, "*"); + else + DelAccessEntry(eIPC, "!", eClosed, "*"); + //m_StartChanged = true; +} + +void COptionsWindow::OnAddStartProg() +{ + AddProgToGroup(ui.treeStart, ""); + //m_StartChanged = true; +} + +void COptionsWindow::OnDelStartProg() +{ + DelProgFromGroup(ui.treeStart, ""); + //m_StartChanged = true; +} + +void COptionsWindow::OnBlockINet() +{ + bool Enable = ui.chkBlockINet->isChecked(); + if (Enable) + SetAccessEntry(eFile, "!", eClosed, "InternetAccessDevices"); + else + DelAccessEntry(eFile, "!", eClosed, "InternetAccessDevices"); + //m_INetBlockChanged = true; +} + +void COptionsWindow::OnAddINetProg() +{ + AddProgToGroup(ui.treeINet, ""); + //m_INetBlockChanged = true; +} + +void COptionsWindow::OnDelINetProg() +{ + DelProgFromGroup(ui.treeINet, ""); + //m_INetBlockChanged = true; +} + +void COptionsWindow::AddProgToGroup(QTreeWidget* pTree, const QString& Groupe) +{ + QString Value = SelectProgram(); + if (Value.isEmpty()) + return; + + QTreeWidgetItem* pItem = new QTreeWidgetItem(); + SetProgramItem(Value, pItem, 0); + pTree->addTopLevelItem(pItem); + + AddProgToGroup(Value, Groupe); +} + +void COptionsWindow::AddProgToGroup(const QString& Value, const QString& Groupe) +{ + QTreeWidgetItem* pGroupItem = NULL; + for (int i = 0; i < ui.treeGroups->topLevelItemCount(); i++) + { + QTreeWidgetItem* pCurItem = ui.treeGroups->topLevelItem(i); + if (pCurItem->data(0, Qt::UserRole).toString().compare(Groupe, Qt::CaseInsensitive) == 0) + { + pGroupItem = pCurItem; + break; + } + } + + if (!pGroupItem) + { + pGroupItem = new QTreeWidgetItem(); + pGroupItem->setText(0, Groupe.mid(1, Groupe.length()-2)); + pGroupItem->setData(0, Qt::UserRole, Groupe); + ui.treeGroups->addTopLevelItem(pGroupItem); + } + + QTreeWidgetItem* pProgItem = new QTreeWidgetItem(); + SetProgramItem(Value, pProgItem, 0); + pGroupItem->addChild(pProgItem); + + m_GroupsChanged = true; +} + +void COptionsWindow::DelProgFromGroup(QTreeWidget* pTree, const QString& Groupe) +{ + QTreeWidgetItem* pItem = pTree->currentItem(); + if (!pItem) + return; + + QString Value = pItem->data(0, Qt::UserRole).toString(); + + delete pItem; + + for (int i = 0; i < ui.treeGroups->topLevelItemCount(); i++) + { + QTreeWidgetItem* pGroupItem = ui.treeGroups->topLevelItem(i); + if (pGroupItem->data(0, Qt::UserRole).toString().compare(Groupe, Qt::CaseInsensitive) == 0) + { + for (int j = 0; j < pGroupItem->childCount(); j++) + { + QTreeWidgetItem* pProgItem = pGroupItem->child(j); + if (pProgItem->data(0, Qt::UserRole).toString().compare(Value, Qt::CaseInsensitive) == 0) + { + delete pProgItem; + m_GroupsChanged = true; + break; + } + } + break; + } + } +} + +QTreeWidgetItem* COptionsWindow::GetAccessEntry(EAccessType Type, const QString& Program, EAccessMode Mode, const QString& Path) +{ + for (int i = 0; i < ui.treeAccess->topLevelItemCount(); i++) + { + QTreeWidgetItem* pItem = ui.treeAccess->topLevelItem(i); + if (pItem->data(0, Qt::UserRole).toInt() == Type + && pItem->data(1, Qt::UserRole).toString().compare(Program, Qt::CaseInsensitive) == 0 + && pItem->data(2, Qt::UserRole).toInt() == Mode + && pItem->data(3, Qt::UserRole).toString().compare(Path, Qt::CaseInsensitive) == 0) + return pItem; + } + return NULL; +} + +void COptionsWindow::SetAccessEntry(EAccessType Type, const QString& Program, EAccessMode Mode, const QString& Path) +{ + if (GetAccessEntry(Type, Program, Mode, Path) != NULL) + return; // already set + AddAccessEntry(Type, Mode, Program, Path); +} + +void COptionsWindow::DelAccessEntry(EAccessType Type, const QString& Program, EAccessMode Mode, const QString& Path) +{ + if(QTreeWidgetItem* pItem = GetAccessEntry(Type, Program, Mode, Path)) + { + delete pItem; + m_AccessChanged = true; + } +} + +QString COptionsWindow::AccessTypeToName(EAccessEntry Type) +{ + switch (Type) + { + case eOpenFilePath: return "OpenFilePath"; + case eOpenPipePath: return "OpenPipePath"; + case eClosedFilePath: return "ClosedFilePath"; + case eReadFilePath: return "ReadFilePath"; + case eWriteFilePath: return "WriteFilePath"; + + case eOpenKeyPath: return "OpenKeyPath"; + case eClosedKeyPath: return "ClosedKeyPath"; + case eReadKeyPath: return "ReadKeyPath"; + case eWriteKeyPath: return "WriteKeyPath"; + + case eOpenIpcPath: return "OpenIpcPath"; + case eClosedIpcPath: return "ClosedIpcPath"; + + case eOpenWinClass: return "OpenWinClass"; + + case eOpenClsid: return "OpenClsid"; + } + return "Unknown"; +} + +void COptionsWindow::LoadAccessList() +{ + ui.treeAccess->clear(); + + for (int i = 0; i < eMaxAccessType; i++) + { + foreach(const QString& Value, m_pBox->GetTextList(AccessTypeToName((EAccessEntry)i), m_Template)) + ParseAndAddAccessEntry((EAccessEntry)i, Value); + } + + if (ui.chkShowAccessTmpl->isChecked()) + { + foreach(const QString& Template, m_pBox->GetTemplates()) + { + for (int i = 0; i < eMaxAccessType; i++) + { + foreach(const QString& Value, m_pBox->GetTextListTmpl(AccessTypeToName((EAccessEntry)i), Template)) + ParseAndAddAccessEntry((EAccessEntry)i, Value, Template); + } + } + } + + m_AccessChanged = false; +} + +void COptionsWindow::ParseAndAddAccessEntry(EAccessEntry EntryType, const QString& Value, const QString& Template) +{ + EAccessType Type; + EAccessMode Mode; + switch (EntryType) + { + case eOpenFilePath: Type = eFile; Mode = eDirect; break; + case eOpenPipePath: Type = eFile; Mode = eFull; break; + case eClosedFilePath: Type = eFile; Mode = eClosed; break; + case eReadFilePath: Type = eFile; Mode = eReadOnly; break; + case eWriteFilePath: Type = eFile; Mode = eWriteOnly; break; + + case eOpenKeyPath: Type = eKey; Mode = eDirect; break; + case eClosedKeyPath: Type = eKey; Mode = eClosed; break; + case eReadKeyPath: Type = eKey; Mode = eReadOnly; break; + case eWriteKeyPath: Type = eKey; Mode = eWriteOnly; break; + + case eOpenIpcPath: Type = eIPC; Mode = eDirect; break; + case eClosedIpcPath: Type = eIPC; Mode = eClosed; break; + + case eOpenWinClass: Type = eWndCls; Mode = eDirect; break; + + case eOpenClsid: Type = eClsId; Mode = eDirect; break; + + default: return; + } + + QStringList Values = Value.split(","); + if (Values.count() >= 2) + AddAccessEntry(Type, Mode, Values[0], Values[1], Template); + else // all programs + AddAccessEntry(Type, Mode, "", Values[0], Template); +} + +QString COptionsWindow::GetAccessModeStr(EAccessMode Mode) +{ + switch (Mode) + { + case eDirect: return "Direct"; + case eFull: return "Full"; + case eClosed: return "Closed"; + case eReadOnly: return "Read Only"; + case eWriteOnly: return "Write Only"; + } + return "Unknown"; +} + +QString COptionsWindow::GetAccessTypeStr(EAccessType Type) +{ + switch (Type) + { + case eFile: return "File/Folder"; + case eKey: return "Registry"; + case eIPC: return "IPC Path"; + case eWndCls: return "Wnd Class"; + case eClsId: return "COM Object"; + } + return "Unknown"; +} + +void COptionsWindow::AddAccessEntry(EAccessType Type, EAccessMode Mode, QString Program, const QString& Path, const QString& Template) +{ + QTreeWidgetItem* pItem = new QTreeWidgetItem(); + + pItem->setText(0, GetAccessTypeStr(Type) + (Template.isEmpty() ? "" : " (" + Template + ")")); + pItem->setData(0, Qt::UserRole, !Template.isEmpty() ? -1 : (int)Type); + + pItem->setData(1, Qt::UserRole, Program); + if (Program.isEmpty()) + Program = tr("All Programs"); + bool Not = Program.left(1) == "!"; + if (Not) + Program.remove(0, 1); + if (Program.left(1) == "<") + Program = tr("Group: %1").arg(Program.mid(1, Program.length() - 2)); + pItem->setText(1, (Not ? "NOT " : "") + Program); + + pItem->setText(2, GetAccessModeStr(Mode)); + pItem->setData(2, Qt::UserRole, (int)Mode); + + pItem->setText(3, Path); + pItem->setData(3, Qt::UserRole, Path); + + ui.treeAccess->addTopLevelItem(pItem); +} + +QString COptionsWindow::MakeAccessStr(EAccessType Type, EAccessMode Mode) +{ + switch (Type) + { + case eFile: + switch (Mode) + { + case eDirect: return "OpenFilePath"; + case eFull: return "OpenPipePath"; + case eClosed: return "ClosedFilePath"; + case eReadOnly: return "ReadFilePath"; + case eWriteOnly: return "WriteFilePath"; + } + break; + case eKey: + switch (Mode) + { + case eDirect: return "OpenKeyPath"; + case eClosed: return "ClosedKeyPath"; + case eReadOnly: return "ReadKeyPath"; + case eWriteOnly: return "WriteKeyPath"; + } + break; + case eIPC: + switch (Mode) + { + case eDirect: return "OpenIpcPath"; + case eClosed: return "ClosedIpcPath"; + } + break; + case eWndCls: + switch (Mode) + { + case eDirect: return "OpenWinClass"; + } + break; + case eClsId: + switch (Mode) + { + case eDirect: return "OpenClsid"; + } + break; + } + return "Unknown"; +} + +void COptionsWindow::SaveAccessList() +{ + QMultiMap AccessMap; + for (int i = 0; i < ui.treeAccess->topLevelItemCount(); i++) + { + QTreeWidgetItem* pItem = ui.treeAccess->topLevelItem(i); + int Type = pItem->data(0, Qt::UserRole).toInt(); + if (Type == -1) + continue; // entry from template + int Mode = pItem->data(2, Qt::UserRole).toInt(); + QString Program = pItem->data(1, Qt::UserRole).toString(); + QString Value = pItem->data(3, Qt::UserRole).toString(); + if (Program.isEmpty()) + Value.prepend(Program + ","); + AccessMap.insertMulti(MakeAccessStr((EAccessType)Type, (EAccessMode)Mode), Value); + } + + foreach(const QString& Key, AccessMap.uniqueKeys()) + m_pBox->UpdateTextList(Key, AccessMap.values(Key)); + + m_AccessChanged = false; +} + +void COptionsWindow::OnAccessItemClicked(QTreeWidgetItem* pItem, int Column) +{ + if (Column != 0) + return; + + QWidget* pProgram = ui.treeAccess->itemWidget(pItem, 1); + if (!pProgram) + return; + + QHBoxLayout* pLayout = (QHBoxLayout*)pProgram->layout(); + QToolButton* pNot = (QToolButton*)pLayout->itemAt(0)->widget(); + QComboBox* pCombo = (QComboBox*)pLayout->itemAt(1)->widget(); + + QComboBox* pMode = (QComboBox*)ui.treeAccess->itemWidget(pItem, 2); + QLineEdit* pPath = (QLineEdit*)ui.treeAccess->itemWidget(pItem, 3); + + QString Program = pCombo->currentText(); + int Index = pCombo->findText(Program); + if(Index != -1) + Program = pCombo->itemData(Index, Qt::UserRole).toString(); + + pItem->setText(1, (pNot->isChecked() ? "NOT " : "") + pCombo->currentText()); + pItem->setData(1, Qt::UserRole, (pNot->isChecked() ? "!" : "") + Program); + pItem->setText(2, GetAccessModeStr((EAccessMode)pMode->currentData().toInt())); + pItem->setData(2, Qt::UserRole, pMode->currentData()); + pItem->setText(3, pPath->text()); + pItem->setData(3, Qt::UserRole, pPath->text()); + + ui.treeAccess->setItemWidget(pItem, 1, NULL); + ui.treeAccess->setItemWidget(pItem, 2, NULL); + ui.treeAccess->setItemWidget(pItem, 3, NULL); + + m_AccessChanged = true; +} + +QList COptionsWindow::GetAccessModes(EAccessType Type) +{ + switch (Type) + { + case eFile: return QList() << eDirect << eFull << eClosed << eReadOnly << eWriteOnly; + case eKey: return QList() << eDirect << eClosed << eReadOnly << eWriteOnly; + case eIPC: return QList() << eDirect << eClosed; + } + return QList() << eDirect; +} + +void COptionsWindow::OnAccessItemDoubleClicked(QTreeWidgetItem* pItem, int Column) +{ + if (Column == 0) + return; + + int Type = pItem->data(0, Qt::UserRole).toInt(); + if (Type == -1) { + QMessageBox::warning(this, "SandboxiePlus", tr("Template values can not be edited.")); + return; + } + + QString Program = pItem->data(1, Qt::UserRole).toString(); + + QWidget* pProgram = new QWidget(); + pProgram->setAutoFillBackground(true); + QHBoxLayout* pLayout = new QHBoxLayout(); + pLayout->setMargin(0); + pLayout->setSpacing(0); + pProgram->setLayout(pLayout); + QToolButton* pNot = new QToolButton(pProgram); + pNot->setText("!"); + pNot->setCheckable(true); + if (Program.left(1) == "!"){ + pNot->setChecked(true); + Program.remove(0, 1); + } + pLayout->addWidget(pNot); + QComboBox* pCombo = new QComboBox(pProgram); + pCombo->addItem(tr("All Programs"), ""); + + // todo: add recently ran programs or programs from other configs + + for (int i = 0; i < ui.treeGroups->topLevelItemCount(); i++) { + QTreeWidgetItem* pItem = ui.treeGroups->topLevelItem(i); + pCombo->addItem(tr("Group: %1").arg(pItem->text(0)), "<" + pItem->text(0) + ">"); + } + + pCombo->setEditable(true); + int Index = pCombo->findData(Program); + pCombo->setCurrentIndex(Index); + if(Index == -1) + pCombo->setCurrentText(Program); + pLayout->addWidget(pCombo); + + ui.treeAccess->setItemWidget(pItem, 1, pProgram); + + QComboBox* pMode = new QComboBox(); + foreach(EAccessMode Mode, GetAccessModes((EAccessType)Type)) + pMode->addItem(GetAccessModeStr(Mode), (int)Mode); + pMode->setCurrentIndex(pMode->findData(pItem->data(2, Qt::UserRole))); + ui.treeAccess->setItemWidget(pItem, 2, pMode); + + QLineEdit* pPath = new QLineEdit(); + pPath->setText(pItem->data(3, Qt::UserRole).toString()); + ui.treeAccess->setItemWidget(pItem, 3, pPath); +} + +void COptionsWindow::DeleteAccessEntry(QTreeWidgetItem* pItem) +{ + if (!pItem) + return; + + if (pItem->data(0, Qt::UserRole).toInt() == -1) { + QMessageBox::warning(this, "SandboxiePlus", tr("Template values can not be removed.")); + return; + } + + delete pItem; +} + +void COptionsWindow::OnDelAccess() +{ + DeleteAccessEntry(ui.treeAccess->currentItem()); + m_AccessChanged = true; +} + +void COptionsWindow::OnAdvancedChanged() +{ + m_AdvancedChanged = true; + + ui.chkAutoEmpty->setEnabled(!ui.chkProtectBox->isChecked()); + ui.cmbEmptyCmd->setEnabled(!ui.chkProtectBox->isChecked()); +} + +#include +#include + +void COptionsWindow::OnAddUser() +{ + QStringList Users; + + IDsObjectPicker *pObjectPicker = NULL; + HRESULT hr = CoCreateInstance(CLSID_DsObjectPicker, NULL, CLSCTX_INPROC_SERVER, IID_IDsObjectPicker, (void **)&pObjectPicker); + if (FAILED(hr)) + return; + + DSOP_SCOPE_INIT_INFO ScopeInit; + memset(&ScopeInit, 0, sizeof(DSOP_SCOPE_INIT_INFO)); + ScopeInit.cbSize = sizeof(DSOP_SCOPE_INIT_INFO); + ScopeInit.flType = DSOP_SCOPE_TYPE_TARGET_COMPUTER | DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN | DSOP_SCOPE_TYPE_DOWNLEVEL_JOINED_DOMAIN; + ScopeInit.flScope = DSOP_SCOPE_FLAG_STARTING_SCOPE | DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS | DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS; + ScopeInit.FilterFlags.Uplevel.flBothModes = DSOP_FILTER_USERS | DSOP_FILTER_WELL_KNOWN_PRINCIPALS | DSOP_FILTER_BUILTIN_GROUPS + | DSOP_FILTER_UNIVERSAL_GROUPS_SE | DSOP_FILTER_GLOBAL_GROUPS_SE | DSOP_FILTER_DOMAIN_LOCAL_GROUPS_SE; + ScopeInit.FilterFlags.flDownlevel = DSOP_DOWNLEVEL_FILTER_USERS | DSOP_DOWNLEVEL_FILTER_LOCAL_GROUPS | DSOP_DOWNLEVEL_FILTER_GLOBAL_GROUPS; + + DSOP_INIT_INFO InitInfo; + memset(&InitInfo, 0, sizeof(InitInfo)); + InitInfo.cbSize = sizeof(InitInfo); + InitInfo.pwzTargetComputer = NULL; + InitInfo.cDsScopeInfos = 1; + InitInfo.aDsScopeInfos = &ScopeInit; + InitInfo.flOptions = DSOP_FLAG_MULTISELECT; + + hr = pObjectPicker->Initialize(&InitInfo); + + if (SUCCEEDED(hr)) + { + IDataObject *pDataObject = NULL; + hr = pObjectPicker->InvokeDialog((HWND)this->winId(), &pDataObject); + if (SUCCEEDED(hr) && pDataObject) + { + FORMATETC formatEtc; + formatEtc.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOP_DS_SELECTION_LIST); + formatEtc.ptd = NULL; + formatEtc.dwAspect = DVASPECT_CONTENT; + formatEtc.lindex = -1; + formatEtc.tymed = TYMED_HGLOBAL; + + STGMEDIUM stgMedium; + hr = pDataObject->GetData(&formatEtc, &stgMedium); + if (SUCCEEDED(hr)) + { + PDS_SELECTION_LIST pResults = (PDS_SELECTION_LIST)GlobalLock(stgMedium.hGlobal); + if (pResults) + { + for (ULONG i = 0; i < pResults->cItems; i++) + Users.append(QString::fromWCharArray(pResults->aDsSelection[i].pwzName)); + GlobalUnlock(stgMedium.hGlobal); + } + } + pDataObject->Release(); + } + } + pObjectPicker->Release(); + + + if (Users.isEmpty()) + return; + + ui.lstUsers->addItems(Users); +} + +void COptionsWindow::OnDelUser() +{ + foreach(QListWidgetItem* pItem, ui.lstUsers->selectedItems()) + delete pItem; +} + +void COptionsWindow::LoadTemplates() +{ + m_AllTemplates.clear(); + ui.cmbCategories->clear(); + + QStringList Templates; + for (int index = 0; ; index++) + { + QString Value = m_pBox->GetAPI()->SbieIniGet("", "", index); + if (Value.isNull()) + break; + Templates.append(Value); + } + + for (QStringList::iterator I = Templates.begin(); I != Templates.end();) + { + if (I->left(9).compare("Template_", Qt::CaseInsensitive) != 0 || *I == "Template_KnownConflicts") { + I = Templates.erase(I); + continue; + } + + QString Name = *I++; + QString Category = m_pBox->GetAPI()->SbieIniGet(Name, "Tmpl.Class", 0x40000000L); // CONF_GET_NO_GLOBAL); + QString Title = m_pBox->GetAPI()->SbieIniGet(Name, "Tmpl.Title", 0x40000000L); // CONF_GET_NO_GLOBAL); + + if (Title.left(1) == "#") + { + int End = Title.mid(1).indexOf(","); + if (End == -1) End = Title.length() - 1; + int MsgNum = Title.mid(1, End).toInt(); + Title = m_pBox->GetAPI()->GetSbieMessage(MsgNum, Title.mid(End+2)); + } + if (Title.isEmpty()) Title = Name; + //else Title += " (" + Name + ")"; + if (Title == "-") + continue; // skip separators + + m_AllTemplates.insertMulti(Category, qMakePair(Name, Title)); + } + + ui.cmbCategories->addItem(tr("All Categories"), ""); + ui.cmbCategories->setCurrentIndex(0); + foreach(const QString& Category, m_AllTemplates.uniqueKeys()) + { + if (Category.isEmpty()) + continue; + ui.cmbCategories->addItem(Category, Category); + } + + m_GlobalTemplates = m_pBox->GetAPI()->GetGlobalSettings()->GetTextList("Template", false); + m_BoxTemplates = m_pBox->GetTextList("Template", false); + + ShowTemplates(); +} + +void COptionsWindow::ShowTemplates() +{ + ui.treeTemplates->clear(); + + QString Category = ui.cmbCategories->currentData().toString(); + + for (QMultiMap>::iterator I = m_AllTemplates.begin(); I != m_AllTemplates.end(); ++I) + { + if (!Category.isEmpty() && I.key().compare(Category, Qt::CaseInsensitive) != 0) + continue; + + QString Name = I.value().first.mid(9); + + QTreeWidgetItem* pItem = new QTreeWidgetItem(); + pItem->setText(0, I.key()); + pItem->setData(1, Qt::UserRole, I.value().first); + pItem->setText(1, I.value().second); + //pItem->setFlags(pItem->flags() | Qt::ItemIsUserCheckable); + if(m_GlobalTemplates.contains(Name)) + pItem->setCheckState(1, Qt::PartiallyChecked); + else if (m_BoxTemplates.contains(Name)) + pItem->setCheckState(1, Qt::Checked); + else + pItem->setCheckState(1, Qt::Unchecked); + ui.treeTemplates->addTopLevelItem(pItem); + } +} + +void COptionsWindow::OnTemplateClicked(QTreeWidgetItem* pItem, int Column) +{ + QString Name = pItem->data(1, Qt::UserRole).toString().mid(9); + if (m_GlobalTemplates.contains(Name)) { + QMessageBox::warning(this, "SandboxiePlus", tr("This template is enabled globally to configure it use the global options.")); + pItem->setCheckState(1, Qt::PartiallyChecked); + return; + } + + if (pItem->checkState(1) == Qt::Checked) { + if (!m_BoxTemplates.contains(Name)) { + m_BoxTemplates.append(Name); + m_TemplatesChanged = true; + } + } + else if (pItem->checkState(1) == Qt::Unchecked) { + if (m_BoxTemplates.contains(Name)) { + m_BoxTemplates.removeAll(Name); + m_TemplatesChanged = true; + } + } +} + +void COptionsWindow::OnTemplateDoubleClicked(QTreeWidgetItem* pItem, int Column) +{ + QSharedPointer pTemplate = QSharedPointer(new CSbieIni(pItem->data(1, Qt::UserRole).toString(), m_pBox->GetAPI())); + + COptionsWindow* pOptionsWindow = new COptionsWindow(pTemplate, pItem->text(1), this); + pOptionsWindow->show(); +} + +void COptionsWindow::SaveTemplates() +{ + m_pBox->UpdateTextList("Template", m_BoxTemplates); + + m_TemplatesChanged = false; +} + +void COptionsWindow::OnTab() +{ + if (ui.tabs->currentWidget() == ui.tabEdit) + { + LoadIniSection(); + ui.txtIniSection->setReadOnly(true); + } + else + { + if (m_ConfigDirty) + LoadConfig(); + + if (ui.tabs->currentWidget() == ui.tabStart) + { + ui.chkRestrictStart->setChecked(GetAccessEntry(eIPC, "!", eClosed, "*") != NULL); + CopyGroupToList("", ui.treeStart); + } + else if (ui.tabs->currentWidget() == ui.tabInternet) + { + ui.chkBlockINet->setChecked(GetAccessEntry(eFile, "!", eClosed, "InternetAccessDevices") != NULL); + CopyGroupToList("", ui.treeINet); + } + } +} + +void COptionsWindow::SetIniEdit(bool bEnable) +{ + for (int i = 0; i < ui.tabs->count() - 1; i++) { + bool Enabled = ui.tabs->widget(i)->isEnabled(); + ui.tabs->setTabEnabled(i, !bEnable && Enabled); + ui.tabs->widget(i)->setEnabled(Enabled); + } + ui.btnSaveIni->setEnabled(bEnable); + ui.btnCancelEdit->setEnabled(bEnable); + ui.txtIniSection->setReadOnly(!bEnable); + ui.btnEditIni->setEnabled(!bEnable); +} + +void COptionsWindow::OnEditIni() +{ + SetIniEdit(true); +} + +void COptionsWindow::OnSaveIni() +{ + SaveIniSection(); + SetIniEdit(false); +} + +void COptionsWindow::OnCancelEdit() +{ + SetIniEdit(false); +} + +void COptionsWindow::LoadIniSection() +{ + QString Section; + + m_Settings = m_pBox->GetIniSection(NULL, m_Template); + + for (QList>::const_iterator I = m_Settings.begin(); I != m_Settings.end(); ++I) + Section += I->first + "=" + I->second + "\n"; + + ui.txtIniSection->setPlainText(Section); +} + +void COptionsWindow::SaveIniSection() +{ + m_ConfigDirty = true; + + // Note: an incremental update would be more elegat but it would change the entry order in the ini, + // hence its better for the user to fully rebuild the section each time. + // + for (QList>::const_iterator I = m_Settings.begin(); I != m_Settings.end(); ++I) + m_pBox->DelValue(I->first, I->second); + + //QList> NewSettings; + //QList> OldSettings = m_Settings; + + QStringList Section = SplitStr(ui.txtIniSection->toPlainText(), "\n"); + foreach(const QString& Line, Section) + { + if (Line.isEmpty()) + return; + StrPair Settings = Split2(Line, "="); + + //if (!OldSettings.removeOne(Settings)) + // NewSettings.append(Settings); + + m_pBox->InsertText(Settings.first, Settings.second); + } + + //for (QList>::const_iterator I = OldSettings.begin(); I != OldSettings.end(); ++I) + // m_pBox->DelValue(I->first, I->second); + // + //for (QList>::const_iterator I = NewSettings.begin(); I != NewSettings.end(); ++I) + // m_pBox->InsertText(I->first, I->second); + + LoadIniSection(); +} \ No newline at end of file diff --git a/SandboxiePlus/SandMan/Windows/OptionsWindow.h b/SandboxiePlus/SandMan/Windows/OptionsWindow.h new file mode 100644 index 0000000000..32bd81996d --- /dev/null +++ b/SandboxiePlus/SandMan/Windows/OptionsWindow.h @@ -0,0 +1,196 @@ +#pragma once + +#include +#include "ui_OptionsWindow.h" +#include "SbiePlusAPI.h" + +class COptionsWindow : public QMainWindow +{ + Q_OBJECT + +public: + COptionsWindow(const QSharedPointer& pBox, const QString& Name, QWidget *parent = Q_NULLPTR); + ~COptionsWindow(); + +signals: + void OptionsChanged(); + +public slots: + void apply(); + void accept(); + void reject(); + +private slots: + + void OnPickColor(); + + void OnAddGroup(); + void OnAddProg(); + void OnDelProg(); + + void OnForceProg(); + void OnForceDir(); + void OnDelForce(); + void OnShowForceTmpl() { LoadForced(); } + + void OnAddLingering(); + void OnAddLeader(); + void OnDelStopProg(); + void OnShowStopTmpl() { LoadStop(); } + + void OnRestrictStart(); + void OnAddStartProg(); + void OnDelStartProg(); + + void OnBlockINet(); + void OnAddINetProg(); + void OnDelINetProg(); + + void OnAccessItemClicked(QTreeWidgetItem* pItem, int Column); + void OnAccessItemDoubleClicked(QTreeWidgetItem* pItem, int Column); + + void OnAddFile() { AddAccessEntry(eFile, eDirect, "", ""); } + void OnAddKey() { AddAccessEntry(eKey, eDirect, "", ""); } + void OnAddIPC() { AddAccessEntry(eIPC, eDirect, "", ""); } + void OnAddClsId() { AddAccessEntry(eWndCls, eDirect, "", ""); } + void OnAddCOM() { AddAccessEntry(eClsId, eDirect, "", ""); } + void OnDelAccess(); + void OnShowAccessTmpl() { LoadAccessList(); } + + void OnAddUser(); + void OnDelUser(); + + void OnFilterTemplates() { ShowTemplates(); } + void OnTemplateClicked(QTreeWidgetItem* pItem, int Column); + void OnTemplateDoubleClicked(QTreeWidgetItem* pItem, int Column); + + void OnTab(); + + void OnGeneralChanged(); + void OnStartChanged() { m_StartChanged = true; } + void OnRestrictionChanged() { m_RestrictionChanged = true; } + void OnINetBlockChanged() { m_INetBlockChanged = true; } + void OnAdvancedChanged(); + + void SetIniEdit(bool bEnable); + void OnEditIni(); + void OnSaveIni(); + void OnCancelEdit(); + +protected: + void closeEvent(QCloseEvent *e); + + enum EAccessEntry + { + eOpenFilePath, + eOpenPipePath, + eClosedFilePath, + eReadFilePath, + eWriteFilePath, + + eOpenKeyPath, + eClosedKeyPath, + eReadKeyPath, + eWriteKeyPath, + + eOpenIpcPath, + eClosedIpcPath, + + eOpenWinClass, + + eOpenClsid, + + eMaxAccessType + }; + + enum EAccessType + { + eFile, + eKey, + eIPC, + eWndCls, + eClsId + }; + + enum EAccessMode + { + eDirect, + eFull, + eClosed, + eReadOnly, + eWriteOnly + }; + + void SetProgramItem(QString Program, QTreeWidgetItem* pItem, int Column); + + QString SelectProgram(bool bOrGroup = true); + + void CopyGroupToList(const QString& Groupe, QTreeWidget* pTree); + QTreeWidgetItem* GetAccessEntry(EAccessType Type, const QString& Program, EAccessMode Mode, const QString& Path); + void SetAccessEntry(EAccessType Type, const QString& Program, EAccessMode Mode, const QString& Path); + void DelAccessEntry(EAccessType Type, const QString& Program, EAccessMode Mode, const QString& Path); + void AddProgToGroup(QTreeWidget* pTree, const QString& Groupe); + void AddProgToGroup(const QString& Value, const QString& Groupe); + void DelProgFromGroup(QTreeWidget* pTree, const QString& Groupe); + + void LoadConfig(); + void SaveConfig(); + + void LoadGroups(); + void SaveGroups(); + + void LoadForced(); + void AddForcedEntry(const QString& Name, int type, const QString& Template = QString()); + void SaveForced(); + + void LoadStop(); + void AddStopEntry(const QString& Name, int type, const QString& Template = QString()); + void SaveStop(); + + QString AccessTypeToName(EAccessEntry Type); + void LoadAccessList(); + QString GetAccessTypeStr(EAccessType Type); + QString GetAccessModeStr(EAccessMode Mode); + void ParseAndAddAccessEntry(EAccessEntry EntryType, const QString& Value, const QString& Template = QString()); + void AddAccessEntry(EAccessType Type, EAccessMode Mode, QString Program, const QString& Path, const QString& Template = QString()); + QString MakeAccessStr(EAccessType Type, EAccessMode Mode); + void SaveAccessList(); + QList GetAccessModes(EAccessType Type); + void DeleteAccessEntry(QTreeWidgetItem* pItem); + + void LoadTemplates(); + void ShowTemplates(); + void SaveTemplates(); + + void LoadIniSection(); + void SaveIniSection(); + + bool m_ConfigDirty; + QColor m_BorderColor; + + bool m_GeneralChanged; + bool m_GroupsChanged; + bool m_ForcedChanged; + bool m_StopChanged; + bool m_StartChanged; + bool m_RestrictionChanged; + bool m_INetBlockChanged; + bool m_AccessChanged; + bool m_TemplatesChanged; + bool m_AdvancedChanged; + + bool m_Template; + + QSet m_TemplateGroups; + + QMultiMap> m_AllTemplates; + QStringList m_GlobalTemplates; + QStringList m_BoxTemplates; + + QList> m_Settings; + + QSharedPointer m_pBox; + +private: + Ui::OptionsWindow ui; +}; diff --git a/SandboxiePlus/SandMan/Windows/SettingsWindow.cpp b/SandboxiePlus/SandMan/Windows/SettingsWindow.cpp new file mode 100644 index 0000000000..0ab181268d --- /dev/null +++ b/SandboxiePlus/SandMan/Windows/SettingsWindow.cpp @@ -0,0 +1,168 @@ +#include "stdafx.h" +#include "SettingsWindow.h" +#include "SandMan.h" +#include "../MiscHelpers/Common/Settings.h" +#include "Helpers/WinAdmin.h" + + +CSettingsWindow::CSettingsWindow(QWidget *parent) + : QMainWindow(parent) +{ + QWidget* centralWidget = new QWidget(); + ui.setupUi(centralWidget); + this->setCentralWidget(centralWidget); + this->setWindowTitle(tr("Sandboxie Plus - Settings")); + + ui.uiLang->addItem("International Englisch", ""); + QDir langDir(QApplication::applicationDirPath() + "/translations/"); + foreach(const QString& langFile, langDir.entryList(QStringList("taskexplorer_*.qm"), QDir::Files)) + { + QString Code = langFile.mid(13, langFile.length() - 13 - 3); + QLocale Locale(Code); + QString Lang = Locale.nativeLanguageName(); + ui.uiLang->addItem(Lang, Code); + } + ui.uiLang->setCurrentIndex(ui.uiLang->findData(theConf->GetString("Options/Language"))); + + ui.chkAutoStart->setChecked(IsAutorunEnabled()); + + ui.chkDarkTheme->setChecked(theConf->GetBool("Options/DarkTheme", false)); + + ui.chkNotifications->setChecked(theConf->GetBool("Options/ShowNotifications", true)); + + ui.chkWatchConfig->setChecked(theConf->GetBool("Options/WatchIni", true)); + + ui.onClose->addItem(tr("Close to Tray"), "ToTray"); + ui.onClose->addItem(tr("Prompt before Close"), "Prompt"); + ui.onClose->addItem(tr("Close"), "Close"); + ui.onClose->setCurrentIndex(ui.onClose->findData(theConf->GetString("Options/OnClose", "ToTray"))); + + ui.chkShowTray->setChecked(theConf->GetBool("Options/ShowSysTray", true)); + + connect(ui.chkShowTray, SIGNAL(stateChanged(int)), this, SLOT(OnChange())); + //connect(ui.chkUseCycles, SIGNAL(stateChanged(int)), this, SLOT(OnChange())); + + + ui.fileRoot->setText(theAPI->GetGlobalSettings()->GetText("FileRootPath")); + ui.chkSeparateUserFolders->setChecked(theAPI->GetGlobalSettings()->GetBool("SeparateUserFolders", true)); + ui.regRoot->setText(theAPI->GetGlobalSettings()->GetText("KeyRootPath")); + ui.ipcRoot->setText(theAPI->GetGlobalSettings()->GetText("IpcRootPath")); + + ui.chkAdminOnly->setChecked(theAPI->GetGlobalSettings()->GetBool("EditAdminOnly", false)); + ui.chkPassRequired->setChecked(!theAPI->GetGlobalSettings()->GetText("EditPassword", "").isEmpty()); + connect(ui.chkPassRequired, SIGNAL(stateChanged(int)), this, SLOT(OnChange())); + connect(ui.btnSetPassword, SIGNAL(pressed()), this, SLOT(OnSetPassword())); + ui.chkAdminOnlyFP->setChecked(theAPI->GetGlobalSettings()->GetBool("ForceDisableAdminOnly", false)); + ui.chkClearPass->setChecked(theAPI->GetGlobalSettings()->GetBool("ForgetPassword", false)); + + connect(ui.buttonBox->button(QDialogButtonBox::Ok), SIGNAL(pressed()), this, SLOT(accept())); + connect(ui.buttonBox->button(QDialogButtonBox::Apply), SIGNAL(pressed()), this, SLOT(apply())); + connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + restoreGeometry(theConf->GetBlob("SettingsWindow/Window_Geometry")); + + OnChange(); +} + +CSettingsWindow::~CSettingsWindow() +{ + theConf->SetBlob("SettingsWindow/Window_Geometry",saveGeometry()); +} + +void CSettingsWindow::closeEvent(QCloseEvent *e) +{ + this->deleteLater(); +} + +void CSettingsWindow::apply() +{ + theConf->SetValue("Options/Language", ui.uiLang->currentData()); + + theConf->SetValue("Options/DarkTheme", ui.chkDarkTheme->isChecked()); + + AutorunEnable(ui.chkAutoStart->isChecked()); + + theConf->SetValue("Options/ShowNotifications", ui.chkNotifications->isChecked()); + + theConf->SetValue("Options/WatchIni", ui.chkWatchConfig->isChecked()); + + theConf->SetValue("Options/OnClose", ui.onClose->currentData()); + + theConf->SetValue("Options/ShowSysTray", ui.chkShowTray->isChecked()); + + if (ui.fileRoot->text().isEmpty()) + ui.fileRoot->setText("\\??\\%SystemDrive%\\Sandbox\\%USER%\\%SANDBOX%"); + theAPI->GetGlobalSettings()->SetText("FileRootPath", ui.fileRoot->text()); + theAPI->GetGlobalSettings()->SetBool("SeparateUserFolders", ui.chkSeparateUserFolders->isChecked()); + + if (ui.regRoot->text().isEmpty()) + ui.regRoot->setText("\\REGISTRY\\USER\\Sandbox_%USER%_%SANDBOX%"); + theAPI->GetGlobalSettings()->SetText("KeyRootPath", ui.regRoot->text()); + + if (ui.ipcRoot->text().isEmpty()) + ui.ipcRoot->setText("\\Sandbox\\%USER%\\%SANDBOX%\\Session_%SESSION%"); + theAPI->GetGlobalSettings()->SetText("IpcRootPath", ui.ipcRoot->text()); + + + theAPI->GetGlobalSettings()->SetBool("EditAdminOnly", ui.chkAdminOnly->isChecked()); + + bool isPassSet = !theAPI->GetGlobalSettings()->GetText("EditPassword", "").isEmpty(); + if (ui.chkPassRequired->isChecked()) + { + if (!isPassSet && m_NewPassword.isEmpty()) + OnSetPassword(); // request password entry if it wasn't already + if (!m_NewPassword.isEmpty()) { + theAPI->LockConfig(m_NewPassword); // set new/changed password + m_NewPassword.clear(); + } + } + else if (isPassSet) + theAPI->LockConfig(QString()); // clear password + + theAPI->GetGlobalSettings()->SetBool("ForceDisableAdminOnly", ui.chkAdminOnlyFP->isChecked()); + theAPI->GetGlobalSettings()->SetBool("ForgetPassword", ui.chkClearPass->isChecked()); + + emit OptionsChanged(); +} + +void CSettingsWindow::accept() +{ + apply(); + + this->close(); +} + +void CSettingsWindow::reject() +{ + this->close(); +} + +void CSettingsWindow::OnChange() +{ + //ui.chkLinuxStyle->setEnabled(!ui.chkUseCycles->isChecked()); + + QStandardItemModel *model = qobject_cast(ui.onClose->model()); + QStandardItem *item = model->item(0); + item->setFlags((!ui.chkShowTray->isChecked()) ? item->flags() & ~Qt::ItemIsEnabled : item->flags() | Qt::ItemIsEnabled); + + ui.btnSetPassword->setEnabled(ui.chkPassRequired->isChecked()); +} + +void CSettingsWindow::OnSetPassword() +{ +retry: + QString Value1 = QInputDialog::getText(this, "Sandboxie-Plus", tr("Please enter the new configuration password."), QLineEdit::Password); + if (Value1.isEmpty()) + return; + + QString Value2 = QInputDialog::getText(this, "Sandboxie-Plus", tr("Please re enter the new configuration password."), QLineEdit::Password); + if (Value2.isEmpty()) + return; + + if (Value1 != Value2) { + QMessageBox::warning(this, "Sandboxie-Plus", tr("Passwords did not match, please retry.")); + goto retry; + } + + m_NewPassword = Value1; +} \ No newline at end of file diff --git a/SandboxiePlus/SandMan/Windows/SettingsWindow.h b/SandboxiePlus/SandMan/Windows/SettingsWindow.h new file mode 100644 index 0000000000..c9c8ddedc5 --- /dev/null +++ b/SandboxiePlus/SandMan/Windows/SettingsWindow.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include "ui_SettingsWindow.h" + +class CSettingsWindow : public QMainWindow +{ + Q_OBJECT + +public: + CSettingsWindow(QWidget *parent = Q_NULLPTR); + ~CSettingsWindow(); + +signals: + void OptionsChanged(); + +public slots: + void apply(); + void accept(); + void reject(); + +private slots: + void OnChange(); + void OnSetPassword(); + +protected: + void closeEvent(QCloseEvent *e); + + QString m_NewPassword; + +private: + Ui::SettingsWindow ui; +}; diff --git a/SandboxiePlus/SandMan/main.cpp b/SandboxiePlus/SandMan/main.cpp index 1127d2eeee..2a5cbf92fd 100644 --- a/SandboxiePlus/SandMan/main.cpp +++ b/SandboxiePlus/SandMan/main.cpp @@ -49,8 +49,6 @@ int main(int argc, char *argv[]) CSandMan* pWnd = new CSandMan(); QObject::connect(&app, SIGNAL(messageReceived(const QString&)), pWnd, SLOT(OnMessage(const QString&))); - pWnd->show(); - int ret = app.exec(); delete pWnd;