Merge branch 'august-alt:master' into scripts-title-fix

This commit is contained in:
Ravenwolve 2024-08-20 13:18:42 +04:00 committed by GitHub
commit 5224892a8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 229 additions and 226 deletions

View File

@ -438,10 +438,10 @@ void AdministrativeTemplatesSnapIn::onShutdown()
void AdministrativeTemplatesSnapIn::onDataLoad(const std::string &policyPath, const std::string &locale)
{
Q_UNUSED(locale);
if (!policyPath.empty())
{
QString localeName = QString::fromStdString(locale);
d->policyPath = policyPath;
d->userRegistryPath = QString::fromStdString(policyPath) + "/User/Registry.pol";
@ -464,8 +464,8 @@ void AdministrativeTemplatesSnapIn::onDataLoad(const std::string &policyPath, co
d->proxyModel->setMachineRegistrySource(d->machineRegistrySource.get());
d->filterModel->setMachineRegistrySource(d->machineRegistrySource.get());
d->machineCommentsModel->load(QString::fromStdString(policyPath) + "/Machine/comment.cmtx");
d->userCommentsModel->load(QString::fromStdString(policyPath) + "/User/comment.cmtx");
d->machineCommentsModel->load(QString::fromStdString(policyPath) + "/Machine/comment.cmtx", localeName);
d->userCommentsModel->load(QString::fromStdString(policyPath) + "/User/comment.cmtx", localeName);
d->proxyModel->setMachineCommentModel(d->machineCommentsModel.get());
d->proxyModel->setUserCommentModel(d->userCommentsModel.get());
@ -486,11 +486,14 @@ void AdministrativeTemplatesSnapIn::setMenuItemNames()
void AdministrativeTemplatesSnapIn::onRetranslateUI(const std::string &locale)
{
QString localeName = QString::fromStdString(locale);
d->localeName = locale;
d->policyBundleLoad();
setMenuItemNames();
d->filterDialog->onLanguageChanged();
d->updateFilter();
d->machineCommentsModel->load(d->machineCommentsPath + "/comment.cmtx", localeName);
d->userCommentsModel->load(d->userCommentsPath + "/comment.cmtx", localeName);
setRootNode(static_cast<QAbstractItemModel *>(d->filterModel.get()));
}

View File

@ -160,11 +160,9 @@ void savePolicies(const QString &pluginName, const QString &fileName, std::share
delete format;
}
QString constructCMTLFileName(const QFileInfo &fileName)
QString constructCMTLFileName(const QFileInfo &fileName, const QString &localeName)
{
QString admlFileName = fileName.filePath();
admlFileName.replace(admlFileName.length() - 4, 4, "cmtl");
QString admlFileName = fileName.absoluteDir().path() + "/" + localeName + "/" + fileName.baseName() + ".cmtl";
return admlFileName;
}
@ -192,8 +190,9 @@ std::string constructPolicyRef(const std::string& id)
return id.substr(4);
}
void CommentsModel::load(const QString &cmtxFileName)
void CommentsModel::load(const QString &cmtxFileName, const QString &localeName)
{
this->clear();
auto commentDefinitions
= loadPolicies<io::PolicyCommentsFile, io::PolicyFileFormat<io::PolicyCommentsFile>>("cmtx", cmtxFileName);
if (!commentDefinitions.get())
@ -203,9 +202,10 @@ void CommentsModel::load(const QString &cmtxFileName)
}
bool noCMTL = false;
QString cmtlFileName = constructCMTLFileName(cmtxFileName);
QString cmtlFileName = constructCMTLFileName(cmtxFileName, localeName);
auto commentTranslations
= loadPolicies<io::CommentResourcesFile, io::PolicyFileFormat<io::CommentResourcesFile>>("cmtl", cmtlFileName);
if (!commentTranslations.get())
{
qWarning() << "File not found: " << cmtlFileName;
@ -298,7 +298,7 @@ void CommentsModel::save(const QString &path, const QString& localeName)
commentDefinitions->resources = std::make_unique<LocalizationResourceReference>();
bool enableLocalizedComments = localeName != "en-US";
bool englishLocalization = localeName != "en-US";
for (const auto& comment : comments)
{
@ -322,40 +322,7 @@ void CommentsModel::save(const QString &path, const QString& localeName)
commentDefinitions->comments.push_back(currentComment);
commentDefinitions->resources->stringTable.emplace_back((enableLocalizedComments
? ""
: comment.second.toStdString()),
resourceName);
}
if (enableLocalizedComments)
{
auto cmtlFileName = path + "comment.cmtl";
std::shared_ptr<comments::CommentDefinitionResources> commentResources
= std::make_shared<comments::CommentDefinitionResources>();
for (const auto& comment : comments)
{
namespaceIndex = 0;
for (const auto& currentNamespace : commentDefinitions->policyNamespaces.using_)
{
if (comment.namespace_.compare(currentNamespace.namespace_.c_str()) == 0)
{
break;
}
namespaceIndex++;
}
commentResources->stringTable.emplace_back(comment.second.toStdString(),
"ns" + std::to_string(namespaceIndex)
+ "_" + comment.first.toStdString());
}
savePolicies<io::CommentResourcesFile, comments::CommentDefinitionResources>("cmtl", cmtlFileName,
commentResources);
commentDefinitions->resources->stringTable.emplace_back((comment.second.toStdString()), resourceName);
}
auto cmtxFileName = path + "comment.cmtx";

View File

@ -41,7 +41,7 @@ public:
public:
CommentsModel(QObject* parent = nullptr);
void load(const QString& path);
void load(const QString& path, const QString &localeName);
void save(const QString &path, const QString &localeName);
QModelIndex indexFromItemReference(const QString &itemRef);

View File

@ -48,8 +48,12 @@ public:
virtual void markValueForDeletion(const std::string &key, const std::string &valueName) = 0;
virtual bool undeleteValue(const std::string &key, const std::string &valueName) = 0;
virtual bool isValueMarkedForDeletion(const std::string &key, const std::string &valueName) const = 0;
virtual std::vector<std::string> getNonSpecialValueNames(const std::string &key) const = 0;
virtual std::vector<std::string> getValueNames(const std::string &key) const = 0;
virtual void markKeyForDeletion(const std::string &key) = 0;
virtual std::vector<std::string> getValueNames(const std::string &key) const = 0;
virtual void clearKey(const std::string &key) = 0;
virtual void clearValue(const std::string &key, const std::string &valueName) = 0;

View File

@ -207,7 +207,30 @@ std::vector<std::string> PolRegistrySource::getValueNames(const std::string &key
return result;
}
void PolRegistrySource::clearKey(const std::string &key)
static bool isSpecialValueName(const std::string &str)
{
// TODO: make case-insensitive
// TODO: check for others
return (str == "**deletevalues") ||
(str == "**delvals.") ||
(str == "**deletekeys") ||
(str == "**securekey") ||
(str.length() >= 6 && strncmp(str.c_str(), "**del.", 6)) ||
(str.length() >= 7 && strncmp(str.c_str(), "**soft.", 7));
}
std::vector<std::string> PolRegistrySource::getNonSpecialValueNames(const std::string &key) const
{
std::vector<std::string> result = getValueNames(key);
result.erase(std::remove_if(result.begin(),
result.end(),
&isSpecialValueName),
result.end());
return result;
}
void PolRegistrySource::markKeyForDeletion(const std::string &key)
{
std::vector<std::string> values = getValueNames(key);
for (const auto &value : values)
@ -216,6 +239,15 @@ void PolRegistrySource::clearKey(const std::string &key)
}
}
void PolRegistrySource::clearKey(const std::string &key)
{
std::vector<std::string> values = getValueNames(key);
for (const auto &value : values)
{
clearValue(key, value);
}
}
void PolRegistrySource::clearValue(const std::string &key, const std::string &valueName)
{
auto &entries = d->registry->registryEntries;

View File

@ -55,7 +55,10 @@ public:
bool undeleteValue(const std::string &key, const std::string &valueName) override final;
bool isValueMarkedForDeletion(const std::string &key, const std::string &valueName) const override final;
std::vector<std::string> getNonSpecialValueNames(const std::string &key) const override final;
std::vector<std::string> getValueNames(const std::string &key) const override final;
void markKeyForDeletion(const std::string &key) override final;
void clearKey(const std::string &key) override final;
void clearValue(const std::string &key, const std::string &valueName) override final;

View File

@ -262,7 +262,10 @@ void AdministrativeTemplatesWidget::setModelIndex(const QModelIndex &index)
{
ui->okPushButton->disconnect();
ui->cancelPushButton->disconnect();
}
if (commentsModel && policy)
{
auto gui = this->ui;
connect(ui->okPushButton, &QPushButton::clicked, [commentsModel, gui, policy]() -> void
@ -275,7 +278,10 @@ void AdministrativeTemplatesWidget::setModelIndex(const QModelIndex &index)
commentsModel->setComment(commentText, QString::fromStdString(policy->name),
QString::fromStdString(policy->namespace_));
});
}
if (presentation && policy)
{
auto layout = PresentationBuilder::build(
{*presentation, *policy, *source, *ui->okPushButton, d->dataChanged, d->stateEnabled});
connectDialogBoxSignals();

View File

@ -107,6 +107,93 @@ QHBoxLayout *createCaptions()
return horizontalLayout;
}
QMap<std::string, QString> loadListFromRegistry(AbstractRegistrySource &source, const std::string &key, const std::string &prefix)
{
QMap<std::string, QString> items;
std::vector<std::string> valueNames = source.getNonSpecialValueNames(key);
if(!prefix.empty())
{
// remove all valueNames(from return result), that doesn't have `prefix` prefix
valueNames.erase(std::remove_if(valueNames.begin(), valueNames.end(),
[&prefix](const std::string& str)
{
return !(str.length() > prefix.length() &&
strncmp(str.c_str(), prefix.c_str(), prefix.length()));
}), valueNames.end());
}
for (auto &valueName : valueNames) {
items[valueName] =
source.getValue(key, valueName).value<QString>();
}
return items;
}
void cleanUpListInRegistry(AbstractRegistrySource &source, const std::string &key, const std::string &prefix = "")
{
// small optimization
if (prefix.empty())
{
source.clearKey(key);
}
std::vector<std::string> valueNames = source.getNonSpecialValueNames(key);
// TODO: make case-insensitive.
// clean-up all values that contain `prefix` prefix (case-sensitive)
for (auto &value : valueNames) {
if (value.size() > prefix.size() &&
strncmp(value.c_str(), prefix.c_str(), prefix.size()) == 0)
{
source.clearValue(key, value);
}
}
}
void writeListIntoRegistry(AbstractRegistrySource &source, QMap<std::string, QString> valueList, const std::string &key, bool explicitValue, bool expandable, std::string &prefix)
{
// https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-r2-and-2008/cc731025(v=ws.10)
// explicitValue cannot be used with the valuePrefix attribute.
if (explicitValue && !prefix.empty())
{
qWarning() << "Presentation builder::save: attempt to use explicitValue with the valuePrefix attribute";
}
if (valueList.empty())
{
return;
}
// https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-r2-and-2008/cc770327(v=ws.10)
// true represents expandable string type (REG_EXPAND_SZ) and false represents string type (REG_SZ)
auto type = expandable ? REG_EXPAND_SZ : REG_SZ;
if (explicitValue)
{
for (auto begin = valueList.begin(), end = valueList.end(); begin != end; ++begin)
{
if (!begin.value().trimmed().isEmpty())
{
source.setValue(key, begin.key(), type, begin.value());
}
}
return;
}
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gpreg/57226664-ce00-4487-994e-a6b3820f3e49
// for non explicit values. Non-explicit value will be ignored.
source.setValue(key, "**delvals.", REG_SZ, " ");
size_t index = 1;
for (auto begin = valueList.begin(), end = valueList.end(); begin != end; ++begin, ++index)
{
// https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-r2-and-2008/cc772195(v=ws.10)
// valuePrefix represents the text string to be prepended to the incremented integer for registry subkey creation.
source.setValue(key, prefix + std::to_string(index), type, begin.value());
}
}
bool *m_dataChanged = nullptr;
bool *m_stateEnabled = nullptr;
@ -445,145 +532,49 @@ public:
return;
}
// https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-r2-and-2008/cc731025(v=ws.10)
// explicitValue cannot be used with the valuePrefix attribute.
if (listElement->valuePrefix.size() > 0 && listElement->explicitValue)
{
qWarning() << "Unable to get valid policy listElement (explicitValue cannot be used with the valuePrefix attribute).";
return;
}
RegistryEntryType registryEntryType = listElement->expandable ? RegistryEntryType::REG_EXPAND_SZ
: RegistryEntryType::REG_SZ;
if (listElement->explicitValue)
{
{
QMap<std::string, QString> items;
// Create two column widget.
auto valueNames = m_source->getValueNames(listElement->key);
for (const auto &valueName : valueNames)
{
items[valueName] = m_source->getValue(listElement->key, valueName).toString();
}
listBox->setItems(items);
}
listBox->connect(listBox,
&gpui::ListBoxDialog::itemsEditingFinished,
[=](QMap<std::string, QString> currentItems) {
if (!(*m_stateEnabled))
{
return;
}
qWarning() << "Items debug: " << currentItems.values();
// clean-up registry values.
auto valueNames = m_source->getValueNames(listElement->key);
for (const auto &valueName : valueNames)
{
m_source->clearValue(listElement->key, valueName);
}
// set-up current values.
for (const auto &valueName : currentItems.keys())
{
auto value = currentItems.value(valueName);
if (!value.trimmed().isEmpty())
{
m_source->setValue(elementInfo.key,
valueName,
registryEntryType,
value);
}
}
*m_dataChanged = true;
});
// two collumn ListBox
listBox->setItems(loadListFromRegistry(*m_source, listElement->key, listElement->valuePrefix));
}
else
else
{
// Create one column widget.
QStringList items;
if (listElement->valuePrefix.size() > 0)
{
// If there is a prefix then use prefix to load values.
{
auto valueNames = m_source->getValueNames(listElement->key);
size_t index = 1;
auto valueName = listElement->valuePrefix + std::to_string(index);
while (m_source->isValuePresent(listElement->key, valueName))
{
items.append(m_source->getValue(listElement->key, valueName).toString());
valueName = listElement->valuePrefix + std::to_string(++index);
}
}
listBox->connect(listBox,
&gpui::ListBoxDialog::itemsEditingFinished,
[=](QMap<std::string, QString> currentItems) {
if (!(*m_stateEnabled))
{
return;
}
qWarning() << "Items debug: " << currentItems.values();
size_t index = 1;
// clean-up registry values.
auto registryValueName = listElement->valuePrefix
+ std::to_string(index);
while (m_source->isValuePresent(listElement->key, registryValueName))
{
m_source->clearValue(listElement->key, registryValueName);
registryValueName = listElement->valuePrefix
+ std::to_string(++index);
}
// set-up current values.
for (const auto &item : currentItems.values())
{
if (!item.trimmed().isEmpty())
{
auto valueName = listElement->valueName
+ std::to_string(index);
m_source->setValue(elementInfo.key,
valueName,
registryEntryType,
item);
}
}
*m_dataChanged = true;
});
}
else
{
auto valueNames = m_source->getValueNames(listElement->key);
for (const auto &valueName : valueNames)
{
items.append(m_source->getValue(listElement->key, valueName).toString());
}
listBox->connect(listBox,
&gpui::ListBoxDialog::itemsEditingFinished,
[=](QMap<std::string, QString> currentItems) {
if (!(*m_stateEnabled))
{
return;
}
qWarning() << "Items debug: " << currentItems.values();
// clean-up registry values.
auto registryValueNames = m_source->getValueNames(listElement->key);
for (const auto &valueName : registryValueNames)
{
m_source->clearValue(listElement->key, valueName);
}
// set-up current values.
for (const auto &item : currentItems.values())
{
if (!item.trimmed().isEmpty())
{
m_source->setValue(elementInfo.key,
item.toStdString(),
registryEntryType,
item);
}
}
*m_dataChanged = true;
});
}
qWarning() << "Items debug: " << items;
listBox->setItems(items);
// one collumn ListBox
listBox->setItems(loadListFromRegistry(*m_source, listElement->key, listElement->valuePrefix).values());
}
listBox->connect(listBox,
&gpui::ListBoxDialog::itemsEditingFinished,
[=](QMap<std::string, QString> currentItems) {
if (!(*m_stateEnabled))
{
return;
}
qWarning() << "Items debug: " << currentItems.values();
cleanUpListInRegistry(*m_source, listElement->key, listElement->valuePrefix);
writeListIntoRegistry(*m_source,
currentItems,
listElement->key,
listElement->explicitValue,
listElement->expandable,
listElement->valuePrefix);
*m_dataChanged = true;
});
listBox->show();
};

View File

@ -28,11 +28,11 @@ namespace pol {
/*!
* \brief Valid POL Registery file header. Binary equal valid header.
* leToNative is used because the entry 0x5052656701000000 is
* leToNative is used because the entry 0x0167655250 is
* equivalent to the header in case uint64_t stores a number in LittleEndian.
* BigEndian - 0x00 0x00 0x00 0x01 0x67 0x65 0x52 0x50
* BigEndian - 0x00 0x00 0x00 0x01 0x67 0x65 0x52 0x50 (bytes must be swaped)
*/
static const uint64_t valid_header = leToNative<uint64_t>(0x5052656701000000);
static const uint64_t valid_header = leToNative<uint64_t>(0x0167655250);
/*!
* \brief Match regex `[\x20-\x7E]`
@ -66,12 +66,8 @@ GPUI_SYMBOL_EXPORT PolicyFile PRegParser::parse(std::istream &stream)
GPUI_SYMBOL_EXPORT bool PRegParser::write(std::ostream &stream, const PolicyFile &file)
{
writeHeader(stream);
for (const auto &[key, records] : file.instructions) {
for (const auto &[value, array] : records) {
for (const auto &instruction : array) {
writeInstruction(stream, instruction, key, value);
}
}
for (const auto &instruction : file.instructions) {
writeInstruction(stream, instruction, instruction.key, instruction.value);
}
return true;
@ -249,11 +245,11 @@ void PRegParser::insertInstruction(std::istream &stream, PolicyTree &tree)
check_sym(stream, '[');
std::string keyPath = getKeypath(stream);
instruction.key = getKeypath(stream);
check_sym(stream, ';');
std::string value = getValue(stream);
instruction.value = getValue(stream);
try {
check_sym(stream, ';');
@ -270,19 +266,13 @@ void PRegParser::insertInstruction(std::istream &stream, PolicyTree &tree)
check_sym(stream, ']');
if (tree.find(keyPath) == tree.end()) {
tree[keyPath] = {};
}
if (tree[keyPath].find(value) == tree[keyPath].end()) {
tree[keyPath][value] = {};
}
tree[keyPath][value].emplace_back(std::move(instruction));
tree.emplace_back(std::move(instruction));
} catch (const std::exception &e) {
throw std::runtime_error(std::string(e.what()) + "\nLINE: " + std::to_string(__LINE__)
+ ", FILE: " + __FILE__
+ ", Error was encountered wile parsing instruction with key: "
+ keyPath + ", value: " + value);
+ instruction.key + ", value: " + instruction.value);
}
}

View File

@ -70,20 +70,20 @@ typedef struct PolicyInstruction
{
inline bool operator==(const PolicyInstruction &other) const
{
return type == other.type && data == other.data;
return key == other.key && value == other.value && type == other.type && data == other.data;
}
inline bool operator!=(const PolicyInstruction &other) const
{
return type != other.type && data != other.data;
return !this->operator==(other);
}
PolicyRegType type{};
PolicyData data{};
std::string key{};
std::string value{};
} PolicyInstruction;
typedef std::unordered_map<std::string,
std::unordered_map<std::string, std::vector<PolicyInstruction>>>
PolicyTree;
typedef std::vector<PolicyInstruction> PolicyTree;
typedef struct PolicyFile
{

View File

@ -43,15 +43,8 @@ public:
{
pol::PolicyInstruction instruction;
auto key = entry->key.toStdString();
auto value = entry->value.toStdString();
if (tree.find(key) == tree.end()) {
tree[key] = {};
}
if (tree[key].find(value) == tree[key].end()) {
tree[key][value] = {};
}
instruction.key = entry->key.toStdString();
instruction.value = entry->value.toStdString();
switch (entry->type) {
case model::registry::RegistryEntryType::REG_SZ: {
@ -117,7 +110,7 @@ public:
}
}
tree[key][value].emplace_back(instruction);
tree.emplace_back(instruction);
}
static std::unique_ptr<model::registry::AbstractRegistryEntry>
@ -240,14 +233,10 @@ bool PolFormat::read(std::istream &input, io::RegistryFile *file)
return false;
}
for (const auto &[key, record] : result.instructions) {
for (const auto &[value, array] : record) {
for(const auto &entry : array) {
auto registryEntry = RegistryEntryAdapter::create(entry, key, value);
if (registryEntry.get()) {
registry->registryEntries.push_back(std::move(registryEntry));
}
}
for(const auto &entry : result.instructions) {
auto registryEntry = RegistryEntryAdapter::create(entry, entry.key, entry.value);
if (registryEntry) {
registry->registryEntries.push_back(std::move(registryEntry));
}
}

View File

@ -172,7 +172,9 @@ pol::PolicyFile generateCase(size_t seed = -1)
pol::PolicyInstruction instruction;
instruction.type = generateRandomType(gen);
instruction.data = generateRandomData(instruction.type, gen);
data.instructions[generateRandomKeypath(gen)][generateRandomValue(gen)] = { instruction };
instruction.key = generateRandomKeypath(gen);
instruction.value = generateRandomValue(gen);
data.instructions.emplace_back(instruction);
}
return data;

View File

@ -53,8 +53,8 @@ void PolTest::endianness()
void PolTest::bufferToIntegralLe()
{
std::stringstream buffer;
char tmp[4] = { 0x12, 0x34, 0x56, 0x78 };
const uint32_t &num = *reinterpret_cast<uint32_t *>(&tmp[0]);
char tmp[4] = { 0x78, 0x56, 0x34, 0x12 };
const uint32_t num = (pol::getEndianess() == pol::Endian::LittleEndian) ? 0x12345678 : 0x78563412;
buffer.write(tmp, 4);
buffer.seekg(0);
@ -68,15 +68,14 @@ void PolTest::bufferToIntegralBe()
{
std::stringstream buffer;
char tmp[4] = { 0x12, 0x34, 0x56, 0x78 };
const uint32_t &num = *reinterpret_cast<uint32_t *>(&tmp[0]);
const uint32_t num = (pol::getEndianess() == pol::Endian::LittleEndian) ? 0x12345678 : 0x78563412;
uint32_t result;
buffer.write(reinterpret_cast<const char *>(&num), 4);
buffer.write(tmp, 4);
buffer.seekg(0);
result = pol::readIntegralFromBuffer<uint32_t, false>(buffer);
std::reverse(tmp, tmp + 4);
QCOMPARE(result, num);
}
@ -133,6 +132,21 @@ void PolTest::autogenerateCases(size_t seed)
auto test = parser->parse(file);
QCOMPARE(policyFile, test);
}
void PolTest::testPRegHeader()
{
std::stringstream stream;
pol::PolicyFile file;
auto parser = pol::createPregParser();
parser->write(stream, file);
std::vector<uint8_t> result(8), expected = { 0x50, 0x52, 0x65, 0x67, 0x01, 0x00, 0x00, 0x00};
stream.read(reinterpret_cast<char*>(result.data()), 8);
QCOMPARE(result, expected);
}
} // namespace tests
QTEST_MAIN(tests::PolTest)

View File

@ -39,6 +39,8 @@ private slots:
void testCase(QString filename);
void autogenerateCases(size_t seed);
void testPRegHeader();
};
} // namespace tests