diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5783c19a..7e92285e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [1.7.1 / 5.62.1] - 2022-12-
+### Added
+- added option to create a new sandbox to run in from the box picker dialog
+- added sandbox creation wizard (not available in vintage view mode)
+
+
### Fixed
- fixed BlockNetworkFiles=y not workign tigether with RestrictDevices=y [#2629](https://github.com/sandboxie-plus/Sandboxie/issues/2629)
- fixed sandman crash issue introduced in 1.7.0
@@ -18,6 +23,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
+
+
## [1.7.0 / 5.62.0] - 2022-12-27
diff --git a/Sandboxie/core/dll/file.c b/Sandboxie/core/dll/file.c
index 25d0ef68..dcb2f0c1 100644
--- a/Sandboxie/core/dll/file.c
+++ b/Sandboxie/core/dll/file.c
@@ -1,6 +1,6 @@
/*
* Copyright 2004-2020 Sandboxie Holdings, LLC
- * Copyright 2020-2022 David Xanatos, xanasoft.com
+ * Copyright 2020-2023 David Xanatos, xanasoft.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/Sandboxie/core/dll/file_del.c b/Sandboxie/core/dll/file_del.c
index a6fe61fb..f03c2f98 100644
--- a/Sandboxie/core/dll/file_del.c
+++ b/Sandboxie/core/dll/file_del.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 David Xanatos, xanasoft.com
+ * Copyright 2022-2023 David Xanatos, xanasoft.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/Sandboxie/core/dll/file_dir.c b/Sandboxie/core/dll/file_dir.c
index f4f0f649..6591fad3 100644
--- a/Sandboxie/core/dll/file_dir.c
+++ b/Sandboxie/core/dll/file_dir.c
@@ -1,6 +1,6 @@
/*
* Copyright 2004-2020 Sandboxie Holdings, LLC
- * Copyright 2020-2022 David Xanatos, xanasoft.com
+ * Copyright 2020-2023 David Xanatos, xanasoft.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/Sandboxie/core/dll/file_init.c b/Sandboxie/core/dll/file_init.c
index f9bff1f8..5db611fc 100644
--- a/Sandboxie/core/dll/file_init.c
+++ b/Sandboxie/core/dll/file_init.c
@@ -1,6 +1,6 @@
/*
* Copyright 2004-2020 Sandboxie Holdings, LLC
- * Copyright 2020-2022 David Xanatos, xanasoft.com
+ * Copyright 2020-2023 David Xanatos, xanasoft.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/Sandboxie/core/dll/key.c b/Sandboxie/core/dll/key.c
index cb1e6d37..6b9ae423 100644
--- a/Sandboxie/core/dll/key.c
+++ b/Sandboxie/core/dll/key.c
@@ -1,6 +1,6 @@
/*
* Copyright 2004-2020 Sandboxie Holdings, LLC
- * Copyright 2021-2022 David Xanatos, xanasoft.com
+ * Copyright 2021-2023 David Xanatos, xanasoft.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/Sandboxie/core/dll/key_del.c b/Sandboxie/core/dll/key_del.c
index 392352b8..0b64a4f4 100644
--- a/Sandboxie/core/dll/key_del.c
+++ b/Sandboxie/core/dll/key_del.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 David Xanatos, xanasoft.com
+ * Copyright 2022-2023 David Xanatos, xanasoft.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/SandboxiePlus/SandMan/Forms/NewBoxWindow.ui b/SandboxiePlus/SandMan/Forms/NewBoxWindow.ui
index 4a9eeb91..ac2506d7 100644
--- a/SandboxiePlus/SandMan/Forms/NewBoxWindow.ui
+++ b/SandboxiePlus/SandMan/Forms/NewBoxWindow.ui
@@ -7,7 +7,7 @@
0
0
500
- 300
+ 328
@@ -34,30 +34,13 @@
-
-
-
-
-
- QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
-
+
+
+ 32
- -
-
-
- Sandbox Name:
-
-
-
- -
-
-
- Box Type Preset:
-
-
-
- -
-
-
-
@@ -71,6 +54,37 @@
+ -
+
+
+ Box Type Preset:
+
+
+
+ -
+
+
+ Sandbox Name:
+
+
+
+ -
+
+
+ -
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+
+
+
+
-
@@ -81,13 +95,6 @@
- -
-
-
- 32
-
-
-
-
@@ -101,13 +108,6 @@
- -
-
-
-
-
-
-
diff --git a/SandboxiePlus/SandMan/Forms/SelectBoxWindow.ui b/SandboxiePlus/SandMan/Forms/SelectBoxWindow.ui
index 000120ab..a1571a40 100644
--- a/SandboxiePlus/SandMan/Forms/SelectBoxWindow.ui
+++ b/SandboxiePlus/SandMan/Forms/SelectBoxWindow.ui
@@ -6,8 +6,8 @@
0
0
- 280
- 400
+ 263
+ 430
@@ -34,36 +34,10 @@
-
-
-
-
-
- Select the sandbox in which to start the program, installer or document.
-
-
- true
-
-
-
- -
-
-
-
- Sandbox
-
-
-
-
- -
-
-
- QDialogButtonBox::Cancel|QDialogButtonBox::Ok
-
-
-
-
-
-
- Run As UAC Administrator
+
+
+ Qt::Horizontal
@@ -77,24 +51,53 @@
- -
+
-
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+
+ Sandbox
+
+
+
+
+ -
Run Outside the Sandbox
- -
-
-
- Qt::Horizontal
+
-
+
+
+ Run As UAC Administrator
- -
-
-
- Qt::Horizontal
+
-
+
+
+ Select the sandbox in which to start the program, installer or document.
+
+
+ true
+
+
+
+ -
+
+
+ Run in a new Sandboxed
+
+
+ false
diff --git a/SandboxiePlus/SandMan/SandMan.pri b/SandboxiePlus/SandMan/SandMan.pri
index e63129d4..977cfe54 100644
--- a/SandboxiePlus/SandMan/SandMan.pri
+++ b/SandboxiePlus/SandMan/SandMan.pri
@@ -31,6 +31,7 @@ HEADERS += ./stdafx.h \
./Windows/SelectBoxWindow.h \
./Windows/SupportDialog.h\
./OnlineUpdater.h \
+ ./Wizards/NewBoxWizard.h \
./Wizards/SetupWizard.h
SOURCES += ./main.cpp \
@@ -63,6 +64,7 @@ SOURCES += ./main.cpp \
./Windows/SelectBoxWindow.cpp \
./Windows/SupportDialog.cpp\
./OnlineUpdater.cpp \
+ ./Wizards/NewBoxWizard.cpp \
./Wizards/SetupWizard.cpp
FORMS += ./Forms/NewBoxWindow.ui \
diff --git a/SandboxiePlus/SandMan/SandMan.vcxproj b/SandboxiePlus/SandMan/SandMan.vcxproj
index 81103e6c..7a7aa49f 100644
--- a/SandboxiePlus/SandMan/SandMan.vcxproj
+++ b/SandboxiePlus/SandMan/SandMan.vcxproj
@@ -316,9 +316,11 @@
+
+
diff --git a/SandboxiePlus/SandMan/SandMan.vcxproj.filters b/SandboxiePlus/SandMan/SandMan.vcxproj.filters
index 22d5d4b0..e67d8bc4 100644
--- a/SandboxiePlus/SandMan/SandMan.vcxproj.filters
+++ b/SandboxiePlus/SandMan/SandMan.vcxproj.filters
@@ -183,6 +183,9 @@
Helpers
+
+ Helpers
+
@@ -283,6 +286,9 @@
SandMan
+
+ Wizards
+
diff --git a/SandboxiePlus/SandMan/Views/SbieView.cpp b/SandboxiePlus/SandMan/Views/SbieView.cpp
index 6127db44..22a0bf54 100644
--- a/SandboxiePlus/SandMan/Views/SbieView.cpp
+++ b/SandboxiePlus/SandMan/Views/SbieView.cpp
@@ -12,6 +12,7 @@
#include "../Windows/RecoveryWindow.h"
#include "../Windows/NewBoxWindow.h"
#include "../Views/FileView.h"
+#include "../Wizards/NewBoxWizard.h"
#include "qt_windows.h"
#include "qwindowdefs_win.h"
@@ -960,17 +961,24 @@ bool CSbieView::MoveItem(const QString& Name, const QString& To, int pos)
QString CSbieView::AddNewBox()
{
- CNewBoxWindow NewBoxWindow(this);
- bool bAlwaysOnTop = theConf->GetBool("Options/AlwaysOnTop", false);
- NewBoxWindow.setWindowFlag(Qt::WindowStaysOnTopHint, bAlwaysOnTop);
- if (NewBoxWindow.exec() == 1)
- {
+ QString BoxName;
+
+ bool bVintage = theConf->GetInt("Options/ViewMode", 1) == 2;
+
+ if (bVintage) {
+ CNewBoxWindow NewBoxWindow(this);
+ if (NewBoxWindow.exec() == 1)
+ BoxName = NewBoxWindow.m_Name;
+ }
+ else
+ BoxName = CNewBoxWizard::CreateNewBox(this);
+
+ if (!BoxName.isEmpty()) {
theAPI->ReloadBoxes();
Refresh();
- SelectBox(NewBoxWindow.m_Name);
- return NewBoxWindow.m_Name;
+ SelectBox(BoxName);
}
- return QString();
+ return BoxName;
}
QString CSbieView::AddNewGroup()
diff --git a/SandboxiePlus/SandMan/Windows/SelectBoxWindow.cpp b/SandboxiePlus/SandMan/Windows/SelectBoxWindow.cpp
index ee404c8c..2cb39d7a 100644
--- a/SandboxiePlus/SandMan/Windows/SelectBoxWindow.cpp
+++ b/SandboxiePlus/SandMan/Windows/SelectBoxWindow.cpp
@@ -90,6 +90,7 @@ CSelectBoxWindow::CSelectBoxWindow(const QStringList& Commands, const QString& B
ui.treeBoxes->setAlternatingRowColors(theConf->GetBool("Options/AltRowColors", false));
connect(ui.radBoxed, SIGNAL(clicked(bool)), this, SLOT(OnBoxType()));
+ connect(ui.radBoxedNew, SIGNAL(clicked(bool)), this, SLOT(OnBoxType()));
connect(ui.radUnBoxed, SIGNAL(clicked(bool)), this, SLOT(OnBoxType()));
connect(ui.buttonBox, SIGNAL(accepted()), SLOT(OnRun()));
@@ -162,7 +163,7 @@ void CSelectBoxWindow::closeEvent(QCloseEvent *e)
void CSelectBoxWindow::OnBoxType()
{
- ui.treeBoxes->setEnabled(!ui.radUnBoxed->isChecked());
+ ui.treeBoxes->setEnabled(ui.radBoxed->isChecked());
}
void CSelectBoxWindow::OnBoxDblClick(QTreeWidgetItem*)
@@ -181,6 +182,14 @@ void CSelectBoxWindow::OnRun()
return;
pItem = NULL;
}
+ else if (ui.radBoxedNew->isChecked())
+ {
+ BoxName = theGUI->GetBoxView()->AddNewBox();
+ if (BoxName.isEmpty()) {
+ close();
+ return;
+ }
+ }
else if (pItem == NULL) {
QMessageBox("Sandboxie-Plus", tr("Please select a sandbox."), QMessageBox::Information, QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton, this).exec();
return;
diff --git a/SandboxiePlus/SandMan/Wizards/NewBoxWizard.cpp b/SandboxiePlus/SandMan/Wizards/NewBoxWizard.cpp
new file mode 100644
index 00000000..503a07c6
--- /dev/null
+++ b/SandboxiePlus/SandMan/Wizards/NewBoxWizard.cpp
@@ -0,0 +1,556 @@
+#include "stdafx.h"
+
+#include "NewBoxWizard.h"
+#include "../MiscHelpers/Common/Common.h"
+#include "../Windows/SettingsWindow.h"
+#include "../SandMan.h"
+#include "Helpers/WinAdmin.h"
+#include
+#include "../QSbieAPI/SbieUtils.h"
+#include "../Views/SbieView.h"
+
+
+CNewBoxWizard::CNewBoxWizard(QWidget *parent)
+ : QWizard(parent)
+{
+ setPage(Page_Type, new CBoxTypePage);
+ setPage(Page_Files, new CFilesPage);
+ setPage(Page_Advanced, new CAdvancedPage);
+ setPage(Page_Summary, new CSummaryPage);
+
+ m_bAdvanced = false;
+
+ setWizardStyle(ModernStyle);
+ //setOption(HaveHelpButton, true);
+ setPixmap(QWizard::LogoPixmap, QPixmap(":/SandMan.png").scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
+
+ connect(this, &QWizard::helpRequested, this, &CNewBoxWizard::showHelp);
+
+ setWindowTitle(tr("New Box Wizard"));
+}
+
+void CNewBoxWizard::showHelp()
+{
+
+}
+
+QString CNewBoxWizard::CreateNewBox(QWidget* pParent)
+{
+ CNewBoxWizard wizard(pParent);
+ if (!wizard.exec())
+ return QString();
+
+ QString BoxName = wizard.field("boxName").toString();
+ BoxName.replace(" ", "_");
+ return BoxName;
+}
+
+SB_STATUS CNewBoxWizard::TryToCreateBox()
+{
+ QString BoxName = field("boxName").toString();
+ BoxName.replace(" ", "_");
+ int BoxType = field("boxType").toInt();
+
+ SB_STATUS Status = theAPI->CreateBox(BoxName, true);
+
+ if (!Status.IsError())
+ {
+ CSandBoxPtr pBox = theAPI->GetBoxByName(BoxName);
+
+ switch (BoxType)
+ {
+ case CSandBoxPlus::eHardenedPlus:
+ case CSandBoxPlus::eHardened:
+ pBox->SetBool("UseSecurityMode", true);
+ if(BoxType == CSandBoxPlus::eHardenedPlus)
+ pBox->SetBool("UsePrivacyMode", true);
+ break;
+ case CSandBoxPlus::eDefaultPlus:
+ case CSandBoxPlus::eDefault:
+ pBox->SetBool("UseSecurityMode", false);
+ if(BoxType == CSandBoxPlus::eDefaultPlus)
+ pBox->SetBool("UsePrivacyMode", true);
+ break;
+ case CSandBoxPlus::eAppBoxPlus:
+ case CSandBoxPlus::eAppBox:
+ pBox->SetBool("NoSecurityIsolation", true);
+ if(BoxType == CSandBoxPlus::eAppBoxPlus)
+ pBox->SetBool("UsePrivacyMode", true);
+ //pBox->InsertText("Template", "NoUACProxy"); // proxy is always needed for exes in the box
+ pBox->InsertText("Template", "RpcPortBindingsExt");
+ break;
+ }
+
+ QRgb rgb = theGUI->GetBoxColor(BoxType);
+ pBox->SetText("BorderColor", QString("#%1%2%3").arg(qBlue(rgb), 2, 16, QChar('0')).arg(qGreen(rgb), 2, 16, QChar('0')).arg(qRed(rgb), 2, 16, QChar('0')) + ",ttl");
+
+
+ QString Location = field("boxLocation").toString();
+ if (!Location.isEmpty())
+ pBox->SetText("FileRootPath", Location);
+
+ if (field("boxVersion").toInt() == 1) {
+ pBox->SetBool("UseFileDeleteV2", true);
+ pBox->SetBool("UseRegDeleteV2", true);
+ }
+ if(!field("separateUser").toBool())
+ pBox->SetBool("SeparateUserFolders", false);
+ if(field("useVolumeSN").toBool())
+ pBox->SetBool("UseVolumeSerialNumbers", true);
+
+ if(field("autoDelete").toBool())
+ pBox->SetBool("AutoDelete", true);
+ if(field("autoRecover").toBool())
+ pBox->SetBool("AutoRecover", true);
+
+ if (field("blockNetwork").toInt() == 1) // old style
+ pBox->InsertText("AllowNetworkAccess", "!,n");
+ else if (field("blockNetwork").toInt() == 2) // WFP
+ pBox->InsertText("ClosedFilePath", "!,InternetAccessDevices");
+ pBox->SetBool("BlockNetworkFiles", !field("shareAccess").toBool());
+
+ if(field("fakeAdmin").toBool())
+ pBox->SetBool("FakeAdminRights", true);
+ if(field("msiServer").toBool())
+ pBox->SetBool("MsiInstallerExemptions", true);
+ }
+
+ return Status;
+}
+
+QString CNewBoxWizard::GetDefaultLocation()
+{
+ QString DefaultPath = theAPI->GetGlobalSettings()->GetText("FileRootPath", "\\??\\%SystemDrive%\\Sandbox\\%USER%\\%SANDBOX%", false, false);
+ // HACK HACK: globally %SANDBOX% evaluates to GlobalSettings
+ DefaultPath.replace("\\GlobalSettings", "\\" + field("boxName").toString());
+ return theAPI->Nt2DosPath(DefaultPath);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// CBoxTypePage
+//
+
+CBoxTypePage::CBoxTypePage(QWidget *parent)
+ : QWizardPage(parent)
+{
+ setTitle(tr("Create new Sandbox"));
+ setPixmap(QWizard::WatermarkPixmap, QPixmap(":/SideLogo.png"));
+
+ m_bInstant = theConf->GetBool("Options/InstantBoxWizard", false);
+
+ int row = 0;
+ QGridLayout *layout = new QGridLayout;
+ QLabel* pTopLabel = new QLabel(tr("A sandbox isolates your host system from processes running within the box, "
+ "it prevents them from making permanent changes to other programs and data in your computer. "
+ "The level of isolation impacts your security as well as the compatibility with applications, "
+ "hence there will be a different level of isolation depending on the selected Box Type. "
+ "Sandboxie can also protect your personal data from being accessed by processes running under its supervision."));
+ pTopLabel->setWordWrap(true);
+ layout->addWidget(pTopLabel, row++ , 0, 1, 3);
+
+
+ layout->addWidget(new QLabel(tr("Enter box name:")), row++, 0);
+
+ m_pBoxName = new QLineEdit();
+ m_pBoxName->setMaxLength(32);
+ QMap Boxes = theAPI->GetAllBoxes();
+ for (int i=0;; i++) {
+ QString NewName = tr("New Box");
+ if (i > 0) NewName.append(" " + QString::number(i));
+ if (Boxes.contains(NewName.toLower().replace(" ", "_")))
+ continue;
+ m_pBoxName->setText(NewName);
+ break;
+ }
+ m_pBoxName->setFocus();
+ layout->addWidget(m_pBoxName, row++, 1, 1, 2);
+ registerField("boxName", m_pBoxName);
+
+
+ layout->addWidget(new QLabel(tr("Sellect box type:")), row++, 0);
+
+ m_pBoxType = new QComboBox();
+ m_pBoxType->addItem(theGUI->GetBoxIcon(CSandBoxPlus::eHardenedPlus), tr("Hardened Sandbox with Data Protection"), (int)CSandBoxPlus::eHardenedPlus);
+ m_pBoxType->addItem(theGUI->GetBoxIcon(CSandBoxPlus::eHardened), tr("Security Hardened Sandbox"), (int)CSandBoxPlus::eHardened);
+ m_pBoxType->addItem(theGUI->GetBoxIcon(CSandBoxPlus::eDefaultPlus), tr("Sandbox with Data Protection"), (int)CSandBoxPlus::eDefaultPlus);
+ m_pBoxType->addItem(theGUI->GetBoxIcon(CSandBoxPlus::eDefault), tr("Standard Isolation Sandbox (Default)"), (int)CSandBoxPlus::eDefault);
+ //m_pBoxType->addItem(theGUI->GetBoxIcon(CSandBoxPlus::eInsecure), tr("INSECURE Configuration (please change)"), (int)CSandBoxPlus::eInsecure);
+ m_pBoxType->addItem(theGUI->GetBoxIcon(CSandBoxPlus::eAppBoxPlus), tr("Application Compartment with Data Protection"), (int)CSandBoxPlus::eAppBoxPlus);
+ m_pBoxType->addItem(theGUI->GetBoxIcon(CSandBoxPlus::eAppBox), tr("Application Compartment (NO Isolation)"), (int)CSandBoxPlus::eAppBox);
+ connect(m_pBoxType, SIGNAL(currentIndexChanged(int)), this, SLOT(OnBoxTypChanged()));
+ layout->addWidget(m_pBoxType, row++, 1, 1, 2);
+ registerField("boxType", m_pBoxType);
+
+ m_pInfoLabel = new QLabel();
+ m_pInfoLabel->setWordWrap(true);
+ layout->addWidget(m_pInfoLabel, row++, 0, 1, 3);
+
+ m_pBoxType->setCurrentIndex(3); // default
+
+
+
+ QWidget* pSpacer = new QWidget();
+ pSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ layout->addWidget(pSpacer, row++, 1);
+
+ m_pAdvanced = new QCheckBox(tr("Configure advanced options"));
+ layout->addWidget(m_pAdvanced, row++, 2);
+ connect(m_pAdvanced, SIGNAL(toggled(bool)), this, SLOT(OnAdvanced()));
+
+ setLayout(layout);
+}
+
+void CBoxTypePage::OnBoxTypChanged()
+{
+ int BoxType = m_pBoxType->currentData().toInt();
+
+ m_pInfoLabel->setText(theGUI->GetBoxDescription(BoxType));
+
+ if(BoxType != CSandBoxPlus::eDefault)
+ theGUI->CheckCertificate(this);
+}
+
+void CBoxTypePage::OnAdvanced()
+{
+ ((CNewBoxWizard*)wizard())->m_bAdvanced = m_pAdvanced->isChecked();
+ if (m_bInstant)
+ {
+ QString BoxName = m_pBoxName->text();
+ int BoxType = m_pBoxType->currentIndex();
+
+ wizard()->restart();
+
+ m_pBoxName->setText(BoxName);
+ m_pBoxType->setCurrentIndex(BoxType);
+ }
+}
+
+int CBoxTypePage::nextId() const
+{
+ if (!m_pAdvanced->isChecked()) {
+ if(m_bInstant)
+ return -1;
+ return CNewBoxWizard::Page_Summary;
+ }
+ return CNewBoxWizard::Page_Files;
+}
+
+bool CBoxTypePage::isComplete() const
+{
+ return true;
+}
+
+bool CBoxTypePage::validatePage()
+{
+ QString BoxName = field("boxName").toString();
+ if (!theGUI->GetBoxView()->TestNameAndWarn(BoxName))
+ return false;
+
+ if (m_bInstant && !m_pAdvanced->isChecked())
+ return !((CNewBoxWizard*)wizard())->TryToCreateBox().IsError();
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// CFilesPage
+//
+
+CFilesPage::CFilesPage(QWidget *parent)
+ : QWizardPage(parent)
+{
+ setTitle(tr("Sandbox location and behavioure"));
+ setSubTitle(tr("On this page the sandbox location and its behaviorue can be customized.\nYou can use %USER% to save each users sandbox to an own fodler."));
+
+ int row = 0;
+ QGridLayout *layout = new QGridLayout;
+
+ QLabel* pFileLabel = new QLabel(tr("Sandboxed Files"), this);
+ QFont fnt = pFileLabel->font();
+ fnt.setBold(true);
+ //fnt.setWeight(QFont::DemiBold);
+ pFileLabel->setFont(fnt);
+ layout->addWidget(pFileLabel, row++, 0);
+ layout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 2, 1, 1);
+
+
+ // Location
+ QLineEdit* pDummy = new QLineEdit();
+ pDummy->setVisible(false);
+ layout->addWidget(pDummy, row, 0);
+ registerField("boxLocation", pDummy);
+
+ QHBoxLayout* pLayout = new QHBoxLayout();
+ pLayout->setContentsMargins(0,0,0,0);
+ m_pBoxLocation = new QComboBox();
+ m_pBoxLocation->setEditable(true);
+ pLayout->addWidget(m_pBoxLocation);
+ QPushButton* pButton = new QPushButton("...");
+ pButton->setMaximumWidth(25);
+ connect(pButton, &QPushButton::clicked, [&]() {
+ QString FilePath = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
+ if (!FilePath.isEmpty())
+ this->m_pBoxLocation->setCurrentText(FilePath.replace("/", "\\"));
+ });
+ pLayout->addWidget(pButton);
+ layout->addLayout(pLayout, row++, 1, 1, 3);
+ //
+
+ QLabel* pVersionLbl = new QLabel(tr("Virtualization scheme"), this);
+ layout->addWidget(pVersionLbl, row, 1);
+
+ QComboBox* pVersion = new QComboBox();
+ pVersion->addItem(tr("Version 1"));
+ pVersion->addItem(tr("Version 2"));
+ layout->addWidget(pVersion, row++, 2);
+ pVersion->setCurrentIndex(theConf->GetInt("BoxDefaults/BoxScheme", 2) - 1); // V2 default
+ layout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 3, 1, 1);
+ registerField("boxVersion", pVersion);
+
+ QCheckBox* pUserFolders = new QCheckBox(tr("Separate user folders"));
+ pUserFolders->setChecked(theConf->GetBool("BoxDefaults/SeparateUser", true));
+ layout->addWidget(pUserFolders, row++, 2, 1, 2);
+ registerField("separateUser", pUserFolders);
+
+ QCheckBox* pUseVolumeSN = new QCheckBox(tr("Use volume serial numbers for drives"));
+ pUseVolumeSN->setChecked(theConf->GetBool("BoxDefaults/UseVolumeSN", false));
+ layout->addWidget(pUseVolumeSN, row++, 2, 1, 2);
+ registerField("useVolumeSN", pUseVolumeSN);
+
+ QCheckBox* pAutoDelete = new QCheckBox(tr("Auto delete content when last process terminates"));
+ pAutoDelete->setChecked(theConf->GetBool("BoxDefaults/AutoDelete", false));
+ layout->addWidget(pAutoDelete, row++, 1, 1, 3);
+ registerField("autoDelete", pAutoDelete);
+
+ QCheckBox* pAutoRecover = new QCheckBox(tr("Enable Immediate Recovery of files from recovery locations"));
+ pAutoRecover->setChecked(theConf->GetBool("BoxDefaults/AutoRecover", true));
+ layout->addWidget(pAutoRecover, row++, 1, 1, 3);
+ registerField("autoRecover", pAutoRecover);
+
+
+ setLayout(layout);
+
+
+ int size = 16.0;
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ size *= (QApplication::desktop()->logicalDpiX() / 96.0); // todo Qt6
+#endif
+ AddIconToLabel(pFileLabel, CSandMan::GetIcon("Folder").pixmap(size,size));
+}
+
+int CFilesPage::nextId() const
+{
+ return CNewBoxWizard::Page_Advanced;
+}
+
+void CFilesPage::initializePage()
+{
+ m_pBoxLocation->clear();
+ QString Location = theAPI->GetGlobalSettings()->GetText("FileRootPath", "\\??\\%SystemDrive%\\Sandbox\\%USER%\\%SANDBOX%");
+ m_pBoxLocation->addItem(Location/*.replace("%SANDBOX%", field("boxName").toString())*/);
+}
+
+bool CFilesPage::validatePage()
+{
+ QString Location = m_pBoxLocation->currentText();
+ if (Location == m_pBoxLocation->itemText(0))
+ wizard()->setField("boxLocation", "");
+ else {
+ if (Location.mid(2).contains(QRegularExpression("[ <>:\"/\\|?*\\[\\]]"))){
+ QMessageBox::critical(this, "Sandboxie-Plus", tr("The sellected box location is not a valid path."));
+ return false;
+ }
+ QDir Dir(Location);
+ if (Dir.exists() && !Dir.entryList(QDir::NoDotAndDotDot | QDir::AllEntries).isEmpty()) {
+ if(QMessageBox::warning(this, "Sandboxie-Plus", tr("The sellected box location exists and is not empty, it is recomended to pick a new or empty folder. "
+ "Are you sure you want to use an existing folder?"), QDialogButtonBox::Yes, QDialogButtonBox::No) != QDialogButtonBox::Yes)
+ return false;
+ }
+ if (!QDir().exists(Location.left(3))) {
+ QMessageBox::critical(this, "Sandboxie-Plus", tr("The sellected box location not placed on a currently available drive."));
+ return false;
+ }
+ wizard()->setField("boxLocation", Location);
+ }
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// CAdvancedPage
+//
+
+CAdvancedPage::CAdvancedPage(QWidget *parent)
+ : QWizardPage(parent)
+{
+ setTitle(tr("Advanced Sandbox options"));
+ setSubTitle(tr("On this page advanced sandbox options can be configured."));
+
+ int row = 0;
+ QGridLayout *layout = new QGridLayout;
+
+ QLabel* pNetLabel = new QLabel(tr("Network Access"), this);
+ QFont fnt = pNetLabel->font();
+ fnt.setBold(true);
+ //fnt.setWeight(QFont::DemiBold);
+ pNetLabel->setFont(fnt);
+ layout->addWidget(pNetLabel, row++, 0);
+
+ QComboBox* pNetAccess = new QComboBox();
+ pNetAccess->addItem(tr("Allow network/internet access"));
+ pNetAccess->addItem(tr("Block network/internet by denying access to Network devices"));
+ if (theAPI->GetGlobalSettings()->GetBool("NetworkEnableWFP", false))
+ pNetAccess->addItem(tr("Block network/internet using Windows Filtering Platform"));
+ pNetAccess->setCurrentIndex(theConf->GetInt("BoxDefaults/BlockNetwork", 0));
+ layout->addWidget(pNetAccess, row++, 1, 1, 3);
+ registerField("blockNetwork", pNetAccess);
+
+ m_pShareAccess = new QCheckBox(tr("Allow access to network files and fodlers"));
+ m_pShareAccess->setToolTip(tr("This option is not recomended for Hardened boxes"));
+ m_pShareAccess->setChecked(theConf->GetBool("BoxDefaults/ShareAccess", false));
+ layout->addWidget(m_pShareAccess, row++, 1, 1, 3);
+ registerField("shareAccess", m_pShareAccess);
+
+
+ QLabel* pAdminLabel = new QLabel(tr("Admin Options"), this);
+ pAdminLabel->setFont(fnt);
+ layout->addWidget(pAdminLabel, row++, 0);
+
+ QCheckBox* pFakeAdmin = new QCheckBox(tr("Make applications think they are running elevated"));
+ pFakeAdmin->setChecked(theConf->GetBool("BoxDefaults/FakeAdmin", false));
+ layout->addWidget(pFakeAdmin, row++, 1, 1, 3);
+ registerField("fakeAdmin", pFakeAdmin);
+
+ m_pMSIServer = new QCheckBox(tr("Allow MSIServer to run with a sandboxed system token"));
+ m_pMSIServer->setToolTip(tr("This option is not recomended for Hardened boxes"));
+ m_pMSIServer->setChecked(theConf->GetBool("BoxDefaults/MsiExemptions", false));
+ layout->addWidget(m_pMSIServer, row++, 1, 1, 3);
+ registerField("msiServer", m_pMSIServer);
+
+ setLayout(layout);
+
+
+ int size = 16.0;
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ size *= (QApplication::desktop()->logicalDpiX() / 96.0); // todo Qt6
+#endif
+ AddIconToLabel(pNetLabel, CSandMan::GetIcon("Network").pixmap(size,size));
+ AddIconToLabel(pAdminLabel, CSandMan::GetIcon("Shield9").pixmap(size,size));
+}
+
+int CAdvancedPage::nextId() const
+{
+ return CNewBoxWizard::Page_Summary;
+}
+
+void CAdvancedPage::initializePage()
+{
+ int BoxType = wizard()->field("boxType").toInt();
+ bool bHardened = (BoxType == CSandBoxPlus::eHardenedPlus || BoxType == CSandBoxPlus::eHardened);
+ m_pMSIServer->setEnabled(!bHardened);
+ m_pShareAccess->setEnabled(!bHardened);
+}
+
+bool CAdvancedPage::validatePage()
+{
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// CSummaryPage
+//
+
+CSummaryPage::CSummaryPage(QWidget *parent)
+ : QWizardPage(parent)
+{
+ setTitle(tr("Create the new Sandbox"));
+ setPixmap(QWizard::WatermarkPixmap, QPixmap(":/SideLogo.png"));
+
+ int row = 0;
+ QGridLayout *layout = new QGridLayout;
+
+ QLabel* pLabel = new QLabel;
+ pLabel->setWordWrap(true);
+ pLabel->setText(tr("Almost complete, click Finish to create a new sandbox and conclude the wizard."));
+ layout->addWidget(pLabel, row++ , 0, 1, 3);
+
+
+ m_pSummary = new QTextEdit();
+ m_pSummary->setReadOnly(true);
+ m_pSummary->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ layout->addWidget(m_pSummary, row++ , 0, 1, 3);
+
+ m_pSetDefault = new QCheckBox(tr("Save options as new defaults"));
+ layout->addWidget(m_pSetDefault, row++, 2);
+
+ //QWidget* pSpacer = new QWidget();
+ //pSpacer->setMinimumHeight(16);
+ //layout->addWidget(pSpacer);
+
+ QWidget* pSpacer = new QWidget();
+ pSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ layout->addWidget(pSpacer, row++, 1);
+
+ m_pSetInstant = new QCheckBox(tr("Don't show the summary page in future (unless advanced options were set)"));
+ m_pSetInstant->setChecked(theConf->GetBool("Options/InstantBoxWizard", false));
+ layout->addWidget(m_pSetInstant, row++, 1, 1, 2);
+
+ setLayout(layout);
+}
+
+int CSummaryPage::nextId() const
+{
+ return -1;
+}
+
+void CSummaryPage::initializePage()
+{
+ m_pSummary->setText(theGUI->GetBoxDescription(wizard()->field("boxType").toInt()));
+
+ QString Location = field("boxLocation").toString();
+ if (Location.isEmpty())
+ Location = ((CNewBoxWizard*)wizard())->GetDefaultLocation();
+ m_pSummary->append(tr("\nThis Sandbox will be saved to: %1").arg(Location));
+
+ if (field("autoDelete").toBool())
+ m_pSummary->append(tr("\nThis box will DISCARD its content when its closed, its suitable only for temporary data."));
+ if (field("blockNetwork").toInt())
+ m_pSummary->append(tr("\nProcesses in this box will not be able to access the internet or the local network, this ensures all accessed data to stay confidential."));
+ if (field("msiServer").toBool())
+ m_pSummary->append(tr("\nThis box will run the MSIServer (*.msi installer service) with a system token, this improves the compatybility but reduces the security isolation."));
+ else if(field("fakeAdmin").toBool())
+ m_pSummary->append(tr("\nProcesses in this box will think they are run with administrative privileges, without actually having them, hence installers can be used even in a security hardened box."));
+
+ m_pSetDefault->setVisible(((CNewBoxWizard*)wizard())->m_bAdvanced);
+}
+
+bool CSummaryPage::validatePage()
+{
+ if (m_pSetDefault->isChecked())
+ {
+ theConf->SetValue("BoxDefaults/BoxScheme", field("boxVersion").toInt() + 1);
+ theConf->SetValue("BoxDefaults/SeparateUser", field("separateUser").toBool());
+ theConf->SetValue("BoxDefaults/UseVolumeSN", field("useVolumeSN").toBool());
+
+
+ theConf->SetValue("BoxDefaults/AutoDelete", field("autoDelete").toBool());
+ theConf->SetValue("BoxDefaults/AutoRecover", field("autoRecover").toBool());
+
+ theConf->SetValue("BoxDefaults/BlockNetwork", field("blockNetwork").toInt());
+ theConf->SetValue("BoxDefaults/ShareAccess", field("shareAccess").toBool());
+
+ theConf->SetValue("BoxDefaults/FakeAdmin", field("fakeAdmin").toBool());
+ theConf->SetValue("BoxDefaults/MsiExemptions", field("msiServer").toBool());
+ }
+
+ theConf->SetValue("Options/InstantBoxWizard", m_pSetInstant->isChecked());
+
+ SB_STATUS Status = ((CNewBoxWizard*)wizard())->TryToCreateBox();
+ if (Status.IsError()) {
+ QMessageBox::critical(this, "Sandboxie-Plus", tr("Failed to create new box: %1").arg(theGUI->FormatError(Status)));
+ return false;
+ }
+ return true;
+}
\ No newline at end of file
diff --git a/SandboxiePlus/SandMan/Wizards/NewBoxWizard.h b/SandboxiePlus/SandMan/Wizards/NewBoxWizard.h
new file mode 100644
index 00000000..d266c41b
--- /dev/null
+++ b/SandboxiePlus/SandMan/Wizards/NewBoxWizard.h
@@ -0,0 +1,130 @@
+#pragma once
+
+#include
+#include "../../QSbieAPI/SbieStatus.h"
+
+QT_BEGIN_NAMESPACE
+class QCheckBox;
+class QLabel;
+class QLineEdit;
+class QRadioButton;
+QT_END_NAMESPACE
+
+class CNewBoxWizard : public QWizard
+{
+ Q_OBJECT
+
+public:
+ enum { Page_Type, Page_Files, Page_Advanced, Page_Summary };
+
+ CNewBoxWizard(QWidget *parent = nullptr);
+
+ static QString CreateNewBox(QWidget* pParent = NULL);
+
+ QString GetDefaultLocation();
+
+private slots:
+ void showHelp();
+
+protected:
+ friend class CBoxTypePage;
+ friend class CSummaryPage;
+
+ SB_STATUS TryToCreateBox();
+
+ bool m_bAdvanced;
+};
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// CBoxTypePage
+//
+
+class CBoxTypePage : public QWizardPage
+{
+ Q_OBJECT
+
+public:
+ CBoxTypePage(QWidget *parent = nullptr);
+
+ int nextId() const override;
+ bool isComplete() const override;
+ bool validatePage() override;
+
+private slots:
+ void OnBoxTypChanged();
+ void OnAdvanced();
+
+private:
+ QComboBox* m_pBoxType;
+ QLabel* m_pInfoLabel;
+ QLineEdit* m_pBoxName;
+ QCheckBox* m_pAdvanced;
+
+ bool m_bInstant;
+};
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// CFilesPage
+//
+
+class CFilesPage : public QWizardPage
+{
+ Q_OBJECT
+
+public:
+ CFilesPage(QWidget *parent = nullptr);
+
+ int nextId() const override;
+ void initializePage() override;
+ bool validatePage() override;
+
+private:
+ QComboBox* m_pBoxLocation;
+};
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// CAdvancedPage
+//
+
+class CAdvancedPage : public QWizardPage
+{
+ Q_OBJECT
+
+public:
+ CAdvancedPage(QWidget *parent = nullptr);
+
+ int nextId() const override;
+ void initializePage() override;
+ bool validatePage() override;
+
+private:
+ QCheckBox* m_pShareAccess;
+ QCheckBox* m_pMSIServer;
+};
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// CSummaryPage
+//
+
+class CSummaryPage : public QWizardPage
+{
+ Q_OBJECT
+
+public:
+ CSummaryPage(QWidget *parent = nullptr);
+
+ int nextId() const override;
+ void initializePage() override;
+ bool validatePage() override;
+
+private:
+ QTextEdit* m_pSummary;
+ QCheckBox* m_pSetDefault;
+ QCheckBox* m_pSetInstant;
+};
+
+