fix: fix multiple errors in scripts implementation (#44)

Following changes were made:
- fix: add sections to scripts
- fix: fix scripts display
- fix: fix logic of scripts loading
- fix: fix no section crash
- fix: scripts plugin: change ini files path and create default dirs
- fix: create folder for scripts
- fix: improve debug of genericwriter
- fix: fix add script widget navigation order
- fix: disable edit triggers in script widget
- fix: fix scripts category in scriptssnapinprivate.cpp
- feat: add power shell script order entries to combobox
- feat: set format to be utf16le in ini files
- feat: disable order widget
- fix: fix conversion to wide characters in iniformat
- chore: refactor io generic writer
This commit is contained in:
august-alt 2023-07-19 16:54:40 +04:00 committed by GitHub
parent 69aaf409b5
commit 59944735a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 231 additions and 56 deletions

View File

@ -5,6 +5,8 @@ set(HEADERS
genericfile.inl
genericreader.h
genericreader.inl
genericwriter.h
genericwriter.inl
commentresourcesfile.h
inifile.h
io.h

View File

@ -26,9 +26,9 @@ bool GenericWriter::save(const std::string &filename, TData *fileData)
std::ofstream file;
file.open(filename, std::ofstream::out);
file.open(filename, std::ios::out);
if(file.good())
if (file.good())
{
if(!format->write(file, fileData))
{
@ -37,6 +37,10 @@ bool GenericWriter::save(const std::string &filename, TData *fileData)
return false;
}
}
else
{
qWarning() << "Unable to open a file: " << filename.c_str();
}
file.close();

View File

@ -20,6 +20,8 @@
#include "iniformat.h"
#include <codecvt>
#include <QDebug>
#include <boost/property_tree/ptree.hpp>
@ -38,8 +40,33 @@ IniFormat::IniFormat()
bool IniFormat::read(std::istream &input, IniFile *file)
{
try {
// TODO: Check validity of conversion to wide characters.
// Research Boost lexical_cast and Boost Iostreams Filters.
// Maybe they are better way of doing conversion.
auto wide_input = reinterpret_cast<std::basic_streambuf<wchar_t>*>(input.rdbuf());
std::istreambuf_iterator<wchar_t> eos;
std::wstring string_input(std::istreambuf_iterator<wchar_t>(wide_input), eos);
std::wstring_convert<std::codecvt_utf8_utf16<char16_t, 0x10ffff,
std::codecvt_mode::little_endian>, char16_t> convertor;
auto utf8 = convertor.to_bytes(reinterpret_cast<const char16_t*>(string_input.data()));
std::string utf_string{};
// Strip BOM
if (utf8[0] == '\xef'
&& utf8[1] == '\xbb'
&& utf8[2] == '\xbf')
{
utf_string = utf8.substr(3, utf8.size() - 3);
}
std::istringstream utf8stream(utf_string);
boost::property_tree::ptree pt;
boost::property_tree::ini_parser::read_ini(input, pt);
boost::property_tree::ini_parser::read_ini(utf8stream, pt);
for (auto& section : pt)
{
@ -85,7 +112,18 @@ bool IniFormat::write(std::ostream &output, IniFile *file)
++section_iterator;
}
boost::property_tree::ini_parser::write_ini(output, pt);
std::stringstream string_output;
boost::property_tree::ini_parser::write_ini(string_output, pt);
std::wstring_convert<std::codecvt_utf8_utf16<char16_t, 0x10ffff,
std::codecvt_mode::little_endian>, char16_t> convert;
auto utf16le = convert.from_bytes(string_output.str());
// Add BOM
output.write("\xff\xfe", 2);
output.write(reinterpret_cast<const char*>(utf16le.data()), utf16le.size() * 2);
}
catch (std::exception& e)
{

View File

@ -75,6 +75,13 @@
</item>
</layout>
</widget>
<tabstops>
<tabstop>okPushButton</tabstop>
<tabstop>nameLineEdit</tabstop>
<tabstop>paramLineEdit</tabstop>
<tabstop>browsePushButton</tabstop>
<tabstop>cancelPushButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -53,9 +53,9 @@ void BaseScriptTabWidget::onDownClicked()
}
}
void BaseScriptTabWidget::onAddClicked(bool isScripts)
void BaseScriptTabWidget::onAddClicked()
{
auto root = findRootItem(isScripts);
auto root = findRootItem();
if (root == nullptr)
{
@ -113,32 +113,35 @@ void BaseScriptTabWidget::onDeleteClicked()
void BaseScriptTabWidget::onBrowseClicked()
{
if (!scriptsItemContainer)
{
return;
}
auto path = scriptsItemContainer->property<std::string>(ScriptItemContainer::INI_FILE_PATH);
QDesktopServices::openUrl(QUrl(QString::fromStdString(path), QUrl::TolerantMode));
QString dirName = QFileInfo(QString::fromStdString(path)).absolutePath();
qWarning() << dirName;
QDesktopServices::openUrl(QUrl(dirName, QUrl::TolerantMode));
}
ScriptItemContainer *BaseScriptTabWidget::findRootItem(bool isScripts)
ScriptItemContainer *BaseScriptTabWidget::findRootItem()
{
std::string sectionName = "Shutdown";
std::string machineSectionName = "Shutdown";
std::string userSectionName = "Logoff";
if (isScripts)
if (this->isStartUpScripts)
{
if (this->isStartUpScripts)
{
sectionName = "Logon";
}
else
{
sectionName = "Logoff";
}
machineSectionName = "Startup";
userSectionName = "Logon";
}
else
if (!this->sessionModel)
{
if (this->isStartUpScripts)
{
sectionName = "Startup";
}
qCritical() << "Section model is NULL!";
return nullptr;
}
auto containers = this->sessionModel->topItems();
@ -151,14 +154,19 @@ ScriptItemContainer *BaseScriptTabWidget::findRootItem(bool isScripts)
if (section)
{
if (sectionName.compare(section->property<std::string>(ScriptItemContainer::SECTION_NAME)) == 0)
if (machineSectionName.compare(section->property<std::string>(ScriptItemContainer::SECTION_NAME)) == 0)
{
return section;
}
if (userSectionName.compare(section->property<std::string>(ScriptItemContainer::SECTION_NAME)) == 0)
{
return section;
}
}
}
qWarning() << "Section:" << sectionName.c_str() << " not found!!";
qWarning() << "Section:" << userSectionName.c_str() << " or " << machineSectionName.c_str() << " not found!!";
return nullptr;
}

View File

@ -47,7 +47,7 @@ public:
public:
void onUpClicked();
void onDownClicked();
void onAddClicked(bool isScripts);
void onAddClicked();
void onEditClicked();
void onDeleteClicked();
void onBrowseClicked();
@ -99,7 +99,7 @@ public:
}
private:
ScriptItemContainer *findRootItem(bool isScripts);
ScriptItemContainer *findRootItem();
private:
QWidget *parent;

View File

@ -185,6 +185,14 @@
<source>For this GPO, run scripts in the following order:</source>
<translation>For this GPO, run scripts in the following order:</translation>
</message>
<message>
<source>Run PowerShell scripts first</source>
<translation>Run PowerShell scripts first</translation>
</message>
<message>
<source>Run PowerShell scripts last</source>
<translation>Run PowerShell scripts last</translation>
</message>
</context>
<context>
<name>ScriptsWidget</name>

View File

@ -68,7 +68,7 @@
</message>
<message>
<source>User level policies.</source>
<translation>Политика для пользователя</translation>
<translation>Политика для пользователя.</translation>
</message>
<message>
<source>Logon</source>
@ -181,7 +181,15 @@
</message>
<message>
<source>For this GPO, run scripts in the following order:</source>
<translation>Запуск скриптов для этого объекта в следующем порядке</translation>
<translation>Запуск скриптов для этого объекта в следующем порядке:</translation>
</message>
<message>
<source>Run PowerShell scripts first</source>
<translation>Запускать скрипты PowerShell первыми</translation>
</message>
<message>
<source>Run PowerShell scripts last</source>
<translation>Запускать скрипты PowerShell последними</translation>
</message>
</context>
<context>

View File

@ -31,6 +31,20 @@ namespace scripts_plugin
{
ScriptModelBuilder::ScriptModelBuilder() {}
void scripts_plugin::ScriptModelBuilder::makeSectionIfNotFound(const std::string &sectionName,
const std::string &file_path,
std::shared_ptr<io::IniFile::sections> sections,
ScriptsModel *model)
{
auto found = sections->find(sectionName);
if (found == sections->end())
{
auto container = model->insertItem<ScriptItemContainer>();
container->setProperty(ScriptItemContainer::SECTION_NAME, sectionName);
container->setProperty(ScriptItemContainer::INI_FILE_PATH, file_path);
}
}
void ScriptModelBuilder::iniToModel(ScriptsModel *model, io::IniFile *iniFile, std::string &file_path)
{
auto sections = iniFile->getAllSections();
@ -73,6 +87,18 @@ void ScriptModelBuilder::iniToModel(ScriptsModel *model, io::IniFile *iniFile, s
}
}
}
QString filePath = QString::fromStdString(file_path);
if (filePath.toLower().contains("user"))
{
makeSectionIfNotFound("Logon", file_path, sections, model);
makeSectionIfNotFound("Logoff", file_path, sections, model);
}
else
{
makeSectionIfNotFound("Startup", file_path, sections, model);
makeSectionIfNotFound("Shutdown", file_path, sections, model);
}
}
std::unique_ptr<io::IniFile> ScriptModelBuilder::modelToIni(ScriptsModel *model)

View File

@ -36,6 +36,12 @@ public:
void iniToModel(ScriptsModel *model, io::IniFile *iniFile, std::string &file_path);
std::unique_ptr<io::IniFile> modelToIni(ScriptsModel *model);
private:
void makeSectionIfNotFound(const std::string &sectionName,
const std::string &file_path,
std::shared_ptr<io::IniFile::sections> sections,
ScriptsModel *model);
};
} // namespace scripts_plugin

View File

@ -104,11 +104,15 @@ void ScriptsContentWidget::startDialog(const QModelIndex &index)
{
dialog->setModels(snapIn->d->machineScriptsModel.get(),
snapIn->d->machinePowerScriptsModel.get(),
isStartupScripts);
isStartupScripts,
!isMachineNamespace);
}
else
{
dialog->setModels(snapIn->d->userScriptsModel.get(), snapIn->d->userPowerScriptsModel.get(), isStartupScripts);
dialog->setModels(snapIn->d->userScriptsModel.get(),
snapIn->d->userPowerScriptsModel.get(),
isStartupScripts,
!isMachineNamespace);
}
QObject::connect(dialog, &ScriptsDialog::saveDataSignal, snapIn->d, &ScriptsSnapInPrivate::saveData);

View File

@ -48,9 +48,7 @@ ScriptsDialog::~ScriptsDialog()
delete ui;
}
void ScriptsDialog::setModels(ScriptsModel *scriptsModel,
ScriptsModel *powerScriptsModel,
bool isOnStartUp)
void ScriptsDialog::setModels(ScriptsModel *scriptsModel, ScriptsModel *powerScriptsModel, bool isOnStartUp, bool isUser)
{
ScriptItemContainer *scriptsItem = nullptr;
ScriptItemContainer *powerScriptsItem = nullptr;
@ -59,13 +57,29 @@ void ScriptsDialog::setModels(ScriptsModel *scriptsModel,
if (isOnStartUp)
{
scriptsItem = findItemContainer(scriptsModel, "Logon");
powerScriptsItem = findItemContainer(powerScriptsModel, "Startup");
if (!isUser)
{
scriptsItem = findItemContainer(scriptsModel, "Startup");
powerScriptsItem = findItemContainer(powerScriptsModel, "Startup");
}
else
{
scriptsItem = findItemContainer(scriptsModel, "Logon");
powerScriptsItem = findItemContainer(powerScriptsModel, "Logon");
}
}
else
{
scriptsItem = findItemContainer(scriptsModel, "Logoff");
powerScriptsItem = findItemContainer(powerScriptsModel, "Shutdown");
if (!isUser)
{
scriptsItem = findItemContainer(scriptsModel, "Shutdown");
powerScriptsItem = findItemContainer(powerScriptsModel, "Shutdown");
}
else
{
scriptsItem = findItemContainer(scriptsModel, "Logoff");
powerScriptsItem = findItemContainer(powerScriptsModel, "Logoff");
}
}
if (scriptsItem != nullptr)
@ -87,8 +101,7 @@ ScriptItemContainer *ScriptsDialog::findItemContainer(ScriptsModel *model, std::
if (item)
{
auto containerSectionName = item->property<std::string>(
ScriptItemContainer::SECTION_NAME);
auto containerSectionName = item->property<std::string>(ScriptItemContainer::SECTION_NAME);
if (containerSectionName.compare(section) == 0)
{

View File

@ -55,7 +55,7 @@ public:
ScriptsDialog(QWidget *parent = nullptr);
~ScriptsDialog();
void setModels(ScriptsModel *scriptsModel, ScriptsModel *powerScriptsModel, bool isOnStartUp);
void setModels(ScriptsModel *scriptsModel, ScriptsModel *powerScriptsModel, bool isOnStartUp, bool isUser);
private:
template<typename TWidget>

View File

@ -4,6 +4,7 @@
#include "../../io/inifile.h"
#include "../../plugins/storage/smb/smbclient.h"
#include "../../plugins/storage/smb/smbfile.h"
#include "../../plugins/storage/smb/smbdirectory.h"
#include "groupscriptcontaineritem.h"
#include "scriptitem.h"
#include "scriptitemcontainer.h"
@ -20,7 +21,7 @@ std::string ScriptsModelIo::correctPath(const std::string &path)
{
std::string newPath = path;
if (*newPath.end() != '/' && *newPath.end() != '\\')
if (*newPath.rbegin() != '/' && *newPath.rbegin() != '\\')
{
newPath.append("/");
}
@ -38,10 +39,10 @@ void ScriptsModelIo::loadPolicies(const std::string &path,
{
std::string newPath = correctPath(path);
auto machinePathScripts = newPath + "Machine/scripts.ini";
auto machinePathPowerScripts = newPath + "Machine/psscripts.ini";
auto userPathScripts = newPath + "User/scripts.ini";
auto userPathPowerScripts = newPath + "User/psscripts.ini";
auto machinePathScripts = newPath + "Machine/Scripts/scripts.ini";
auto machinePathPowerScripts = newPath + "Machine/Scripts/psscripts.ini";
auto userPathScripts = newPath + "User/Scripts/scripts.ini";
auto userPathPowerScripts = newPath + "User/Scripts/psscripts.ini";
loadIniFile(machinePathScripts, machineScripts);
loadIniFile(machinePathPowerScripts, machinePowerScripts);
@ -60,10 +61,18 @@ void ScriptsModelIo::savePolicies(const std::string &path,
{
std::string newPath = correctPath(path);
auto machinePathScripts = newPath + "Machine/scripts.ini";
auto machinePathPowerScripts = newPath + "Machine/psscripts.ini";
auto userPathScripts = newPath + "User/scripts.ini";
auto userPathPowerScripts = newPath + "User/psscripts.ini";
createDirectory(newPath + "Machine/Scripts/");
createDirectory(newPath + "User/Scripts/");
createDirectory(newPath + "Machine/Scripts/Startup");
createDirectory(newPath + "Machine/Scripts/Shutdown");
createDirectory(newPath + "User/Scripts/Logon");
createDirectory(newPath + "User/Scripts/Logoff");
auto machinePathScripts = newPath + "Machine/Scripts/scripts.ini";
auto machinePathPowerScripts = newPath + "Machine/Scripts/psscripts.ini";
auto userPathScripts = newPath + "User/Scripts/scripts.ini";
auto userPathPowerScripts = newPath + "User/Scripts/psscripts.ini";
saveIniFile(machinePathScripts, machineScripts);
saveIniFile(machinePathPowerScripts, machinePowerScripts);
@ -168,4 +177,28 @@ void ScriptsModelIo::saveIniFile(std::string &path, ScriptsModel *model)
}
}
void ScriptsModelIo::createDirectory(const std::string &directoryName)
{
const QString path = QString::fromStdString(directoryName);
if (path.startsWith("smb://"))
{
gpui::smb::SmbDirectory dir(path);
if (!dir.exists())
{
dir.mkdir(path);
}
}
else
{
QDir dir(path);
if (!dir.exists())
{
dir.mkdir(path);
}
}
}
} // namespace scripts_plugin

View File

@ -29,6 +29,8 @@ private:
void saveIniFile(std::string &path, ScriptsModel *model);
std::string correctPath(const std::string &path);
void createDirectory(const std::string &directoryName);
};
} // namespace scripts_plugin

View File

@ -57,7 +57,7 @@ void ScriptsPowerShellWidget::on_downPushButton_clicked()
void ScriptsPowerShellWidget::on_addPushButton_clicked()
{
BaseScriptTabWidget::onAddClicked(false);
BaseScriptTabWidget::onAddClicked();
}
void ScriptsPowerShellWidget::on_editPushButton_clicked()

View File

@ -16,11 +16,24 @@
<layout class="QGridLayout" name="gridLayout">
<item row="11" column="0">
<widget class="QComboBox" name="runComboBox">
<property name="enabled">
<bool>false</bool>
</property>
<item>
<property name="text">
<string>Not configured</string>
</property>
</item>
<item>
<property name="text">
<string>Run PowerShell scripts first</string>
</property>
</item>
<item>
<property name="text">
<string>Run PowerShell scripts last</string>
</property>
</item>
</widget>
</item>
<item row="9" column="0" colspan="2">

View File

@ -16,18 +16,18 @@ ScriptsSnapInPrivate::ScriptsSnapInPrivate(ScriptsSnapIn *scriptsSnapIn)
userLogoffItemContainer->setProperty(ScriptItemContainer::SECTION_NAME, "Logoff");
auto userStartUpItemContainer = userPowerScriptsModel.get()->insertItem<ScriptItemContainer>();
userStartUpItemContainer->setProperty(ScriptItemContainer::SECTION_NAME, "Startup");
userStartUpItemContainer->setProperty(ScriptItemContainer::SECTION_NAME, "Logon");
auto userShutdownItemContainer = userPowerScriptsModel.get()->insertItem<ScriptItemContainer>();
userShutdownItemContainer->setProperty(ScriptItemContainer::SECTION_NAME, "Shutdown");
userShutdownItemContainer->setProperty(ScriptItemContainer::SECTION_NAME, "Logoff");
//--------------------------------------------------------------------------------------
//----machine scripts models
auto machineLogonItemContainer = machineScriptsModel.get()->insertItem<ScriptItemContainer>();
machineLogonItemContainer->setProperty(ScriptItemContainer::SECTION_NAME, "Logon");
machineLogonItemContainer->setProperty(ScriptItemContainer::SECTION_NAME, "Startup");
auto machineLogoffItemContainer = machineScriptsModel.get()->insertItem<ScriptItemContainer>();
machineLogoffItemContainer->setProperty(ScriptItemContainer::SECTION_NAME, "Logoff");
machineLogoffItemContainer->setProperty(ScriptItemContainer::SECTION_NAME, "Shutdown");
auto machineStartUpItemContainer = machinePowerScriptsModel.get()
->insertItem<ScriptItemContainer>();

View File

@ -57,7 +57,7 @@ void ScriptsWidget::on_downPushButton_clicked()
void ScriptsWidget::on_addPushButton_clicked()
{
BaseScriptTabWidget::onAddClicked(true);
BaseScriptTabWidget::onAddClicked();
}
void ScriptsWidget::on_editPushButton_clicked()

View File

@ -130,6 +130,9 @@
</item>
<item row="2" column="0" rowspan="6">
<widget class="QTreeView" name="treeView">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>