diff --git a/src/plugins/pol/CMakeLists.txt b/src/plugins/pol/CMakeLists.txt index 420f7f7..ed2b571 100644 --- a/src/plugins/pol/CMakeLists.txt +++ b/src/plugins/pol/CMakeLists.txt @@ -3,22 +3,21 @@ include_directories(${GPUI_INCLUDE_DIRS}) find_package(Qt5 COMPONENTS Core REQUIRED) +set(CMAKE_CXX_STANDARD 17) + set(HEADERS polformat.h - - pregdata.h - pregparser.h - iconvwrapper.h + common.h + encoding.h + parser.h ) set(SOURCES + binary.cpp + parser.cpp + polplugin.cpp polformat.cpp - - pregdata.cpp - pregparser.cpp - pregwriter.cpp - iconvwrapper.cpp ) add_gpui_plugin(pol-plugin ${SOURCES}) diff --git a/src/plugins/pol/binary.cpp b/src/plugins/pol/binary.cpp new file mode 100644 index 0000000..b040400 --- /dev/null +++ b/src/plugins/pol/binary.cpp @@ -0,0 +1,171 @@ +/* + * libparsepol - POL Registry file parser + * + * Copyright (C) 2024 BaseALT Ltd. + * Copyright (C) 2020 Korney Yakovlevich + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +#include "binary.h" +#include "common.h" + +namespace pol { + +std::string readStringFromBuffer(std::istream &buffer, size_t size, iconv_t conv) +{ + bool custom_conv = false; + if (conv == nullptr) { + conv = iconv_open("UTF-8", "UTF-16LE"); + custom_conv = true; + } + + if (conv == ICONV_ERROR_DESCRIPTOR) { + throw std::runtime_error( + "LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Encountered with the inability to create a iconv descriptor."); + } + + std::basic_string source((size / 2) - 1, '\0'); + + // std::the string contains '\0' at the end (C style), which means that the actual buffer size + // is `size`. We read `size` bytes to check that the buffer ends with '\0'. + // '\0' is included in `size`, so we read `size` bytes. + buffer.read(reinterpret_cast(source.data()), size); + check_stream(buffer); + + // Check that the buffer ends with the two '\0'. + if (source.data()[(size / 2) - 1] != 0) { + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Encountered with invalid UTF-16LE buffer."); + } + + auto result = convert(source, conv); + if (custom_conv) { + iconv_close(conv); + } + return result; +} + +size_t writeStringToBuffer(std::ostream &buffer, const std::string &source, iconv_t conv) +{ + bool custom_conv = false; + if (conv == nullptr) { + conv = iconv_open("UTF-16LE", "UTF-8"); + custom_conv = true; + } + + if (conv == ICONV_ERROR_DESCRIPTOR) { + throw std::runtime_error( + "LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Encountered with the inability to create a iconv descriptor."); + } + + std::basic_string converted = convert(source, conv); + + buffer.write(reinterpret_cast(converted.data()), + (converted.size() + 1) * sizeof(char16_t)); + check_stream(buffer); + + if (custom_conv) { + iconv_close(conv); + } + return (converted.size() + 1) * sizeof(char16_t); +} + +std::vector readStringsFromBuffer(std::istream &buffer, size_t size, iconv_t conv) +{ + bool custom_conv = false; + + if (size == 0) { + return {}; + } + if (conv == nullptr) { + conv = iconv_open("UTF-8", "UTF-16LE"); + custom_conv = true; + } + + if (conv == ICONV_ERROR_DESCRIPTOR) { + throw std::runtime_error( + "LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Encountered with the inability to create a iconv descriptor."); + } + + std::vector result; + std::basic_string tmp; + size_t current = 0; + size_t found = 0; + + // std::string contains '\0' at the end (C style), which means that the actual buffer size + // is `size`. We read `size` bytes to check that the buffer ends with '\0'. + // '\0' is included in `size`, so we read `size` bytes. + tmp.resize((size / 2) - 1); + buffer.read(reinterpret_cast(tmp.data()), size); + check_stream(buffer); + + while (found <= tmp.size()) { + found = tmp.find(char16_t(0), current); + + if (found == std::string::npos) { + found = tmp.size(); + + if (tmp.data()[found] != 0) { + return {}; + } + } + + result.push_back( + convert(tmp.cbegin() + current, tmp.cbegin() + found, conv)); + + current = found + 1; + found = current; + } + + if (custom_conv) { + iconv_close(conv); + } + return result; +} + +size_t writeStringsFromBuffer(std::ostream &buffer, const std::vector &data, iconv_t conv) +{ + size_t size = 0; + + for (const auto &str : data) { + auto tmp = writeStringToBuffer(buffer, str, conv); + size += tmp; + } + + return size; +} + +std::vector readVectorFromBuffer(std::istream &buffer, size_t size) +{ + std::vector result; + result.resize(size); + + buffer.read(reinterpret_cast(result.data()), size); + check_stream(buffer); + + return result; +} + +void writeVectorToBuffer(std::ostream &buffer, const std::vector &data) +{ + buffer.write(reinterpret_cast(data.data()), data.size()); + check_stream(buffer); +} + +} // namespace pol diff --git a/src/plugins/pol/binary.h b/src/plugins/pol/binary.h new file mode 100644 index 0000000..d496891 --- /dev/null +++ b/src/plugins/pol/binary.h @@ -0,0 +1,135 @@ +/* + * libparsepol - POL Registry file parser + * + * Copyright (C) 2024 BaseALT Ltd. + * Copyright (C) 2020 Korney Yakovlevich + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PREGPARSER_BINARY +#define PREGPARSER_BINARY + +#include +#include +#include +#include + +#include "encoding.h" + +namespace pol { + +/*! + * \brief Get string from istream (binary) + * if conv == nullptr, then conv will be initialized inside by `iconv_open("UTF-8", "UTF-16LE")` + * \return on any error return empty optional + * \warning string in buffer must be ended with '\0' + * \warning `conv` must be initialized by `iconv_open("UTF-8", "UTF-16LE")` + * \warning if `conv` is (size_t)-1, then function will throw std::runtime_error + */ +std::string readStringFromBuffer(std::istream &buffer, size_t size, iconv_t conv = nullptr); +/*! + * \brief Put string from istream (binary) + * if conv == nullptr, then conv will be initialized inside by `iconv_open("UTF-8", "UTF-16LE")` + * \return Size of writed string. On any error return (size_t)-1 + * \warning string in buffer will be ended with '\0' + * \warning `conv` must be initialized by `iconv_open("UTF-16LE", "UTF-8")` + * \warning if `conv` is (size_t)-1, then function will throw std::runtime_error + */ +size_t writeStringToBuffer(std::ostream &buffer, const std::string &data, iconv_t conv = nullptr); + +/*! + * \brief Get strings from istream (binary) + * if conv == nullptr, then conv will be initialized inside by `iconv_open("UTF-8", "UTF-16LE")` + * \return on any error return empty optional + * \warning every strings in buffer must be ended with '\0' (last included) + * \warning `conv` must be initialized by `iconv_open("UTF-8", "UTF-16LE")` + * \warning if `conv` is (size_t)-1, then function will throw std::runtime_error + */ +std::vector readStringsFromBuffer(std::istream &buffer, size_t size, iconv_t conv = nullptr); +/*! + * \brief Put string from istream (binary) + * if conv == nullptr, then conv will be initialized inside by `iconv_open("UTF-8", "UTF-16LE")` + * \return Size of writed strings. On any error return (size_t)-1 + * \warning every string in buffer will be ended with '\0' (last included) + * \warning `conv` must be initialized by `iconv_open("UTF-16LE", "UTF-8")` + * \warning if `conv` is (size_t)-1, then function will throw std::runtime_error + */ +size_t writeStringsFromBuffer(std::ostream &buffer, const std::vector &data, + iconv_t conv = nullptr); +/*! + * \brief Get vector of raw data from istream (binary) + */ +std::vector readVectorFromBuffer(std::istream &buffer, size_t size); + +/*! + * \brief Put vector of raw data to istream (binary) + */ +void writeVectorToBuffer(std::ostream &buffer, const std::vector &data); + +/*! + * \brief Get integral number from istream (binary) + */ +template + && sizeof(T) <= sizeof(unsigned long long)>> +T readIntegralFromBuffer(std::istream &buffer) +{ + T num = 0; + + buffer.read(reinterpret_cast(&num), sizeof(T)); + if (buffer.fail()) { + if (buffer.eof()) { + throw std::runtime_error( + "LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Failed to read integral number from buffer, EOF was encountered."); + } + throw std::runtime_error( + "LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Failed to read integral number from buffer, error was encountered."); + } + if constexpr (LE) { + return leToNative(num); + } else { + return beToNative(num); + } + + return num; +} + +/*! + * \brief Put integral number to ostream (binary) + */ +template + && sizeof(T) <= sizeof(unsigned long long)>> +void writeIntegralToBuffer(std::ostream &buffer, T num) +{ + if constexpr (LE) { + num = nativeToLe(num); + } else { + num = nativeToBe(num); + } + + buffer.write(reinterpret_cast(&num), sizeof(T)); + if (buffer.fail()) { + throw std::runtime_error( + "LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Failed to write integral number to buffer, error was encountered."); + } +} + +} // namespace pol + +#endif // PREGPARSER_BINARY diff --git a/src/plugins/pol/common.h b/src/plugins/pol/common.h new file mode 100644 index 0000000..069c82a --- /dev/null +++ b/src/plugins/pol/common.h @@ -0,0 +1,73 @@ +/* + * libparsepol - POL Registry file parser + * + * Copyright (C) 2024 BaseALT Ltd. + * Copyright (C) 2020 Korney Yakovlevich + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PREGPARSER_COMMON +#define PREGPARSER_COMMON + +#include +#include + +#include "encoding.h" +#include "iconv.h" + +namespace pol { + +inline void check_stream(std::istream &target) +{ + if (target.fail()) { + if (target.eof()) { + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Failed to read buffer, EOF was encountered."); + } + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Failed to read buffer, error was encountered."); + } +} +inline void check_stream(std::ostream &target) +{ + if (target.fail()) { + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Failed to write buffer, error was encountered."); + } +} +inline void check_sym(std::istream &target, char16_t sym) +{ + char16_t buff; + target.read(reinterpret_cast(&buff), 2); + buff = leToNative(buff); + + check_stream(target); + + if (buff != sym) { + throw std::runtime_error( + "LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Failed to read/write buffer, invalid symbol was encountered."); + } +} +inline void write_sym(std::ostream &target, char16_t sym) +{ + sym = nativeToLe(sym); + target.write(reinterpret_cast(&sym), 2); + + check_stream(target); +} +} // namespace pol + +#endif // PREGPARSER_COMMON diff --git a/src/plugins/pol/encoding.h b/src/plugins/pol/encoding.h new file mode 100644 index 0000000..0216f75 --- /dev/null +++ b/src/plugins/pol/encoding.h @@ -0,0 +1,184 @@ +/* + * libparsepol - POL Registry file parser + * + * Copyright (C) 2024 BaseALT Ltd. + * Copyright (C) 2020 Korney Yakovlevich + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PREGPARSER_ENCODING +#define PREGPARSER_ENCODING + +#include +#include +#include +#include +#include +#include +#include + +#include "iconv.h" + +namespace pol { + +// C++ before C++20 is not support endianess. Becouse of that, we need to +// provide our own implementation + +enum class Endian { + BigEndian, + LittleEndian = 1, +}; + +/*! + * \brief Get current native endianness + */ +inline Endian getEndianess() +{ + union { + uint32_t i; + char c[4]; + } bint = { 0x01020304 }; + + return bint.c[0] == 0x01 ? Endian::BigEndian : Endian::LittleEndian; +} + +/*! + * \brief Byte swap a integral number. + */ +template + && sizeof(T) <= sizeof(unsigned long long)>> +inline T byteswap(T value) +{ + if constexpr (sizeof(T) == 1) { + return value; + } + if constexpr (sizeof(T) == 2) { + return (value >> 8) | (value << 8); + } + if constexpr (sizeof(T) == 4) { + return (value >> 24) | ((value >> 8) & 0xFF00) | ((value << 8) & 0xFF0000) | (value << 24); + } + if constexpr (sizeof(T) == 8) { + return (value >> 56) | ((value >> 40) & 0xFF00) | ((value >> 24) & 0xFF0000) + | ((value >> 8) & 0xFF000000) | ((value << 8) & 0xFF00000000) + | ((value << 24) & 0xFF0000000000) | ((value << 40) & 0xFF000000000000) + | (value << 56); + } + return value; +} + +/*! + * \brief Convert big endian to native endianness + */ +template + && sizeof(T) <= sizeof(unsigned long long)>> +inline T beToNative(T value) +{ + auto endianess = getEndianess(); + if (endianess == Endian::BigEndian) { + return value; + } + return byteswap(value); +} + +/*! + * \brief Convert little endian to native endianness + */ +template + && sizeof(T) <= sizeof(unsigned long long)>> +inline T leToNative(T value) +{ + auto endianess = getEndianess(); + if (endianess == Endian::LittleEndian) { + return value; + } + return byteswap(value); +} +/*! + * \brief Convert native endianness to big endian + */ +template + && sizeof(T) <= sizeof(unsigned long long)>> +inline constexpr T nativeToBe(T num) +{ + return beToNative(num); +} + +/*! + * \brief Convert native endianness to little endian + */ +template + && sizeof(T) <= sizeof(unsigned long long)>> +inline constexpr T nativeToLe(T num) +{ + return leToNative(num); +} + +/*! + * \brief Helper alias for string iterator(just minimize code size) + */ +template +using string_const_iterator = typename std::basic_string::const_iterator; + +/*! + * \brief Convert string from one encoding to another using iconv + */ +template +inline std::basic_string convert(string_const_iterator begin, + string_const_iterator end, iconv_t conv) +{ + std::basic_string result = {}; + + char *inbuf = reinterpret_cast(const_cast(&*begin)); + size_t inbytesLeft = std::distance(begin, end) * sizeof(source_char); + + auto temp = std::make_unique>(); + char *outbuf = temp->data(); + size_t outbytesLeft = temp->size(); + + while (inbytesLeft > 0) { + auto ret = iconv(conv, &inbuf, &inbytesLeft, &outbuf, &outbytesLeft); + if (ret == ICONV_ERROR_CODE && errno != E2BIG) { + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Encountered corrupted unicode string."); + } + + result.append(reinterpret_cast(temp->data()), + reinterpret_cast(outbuf)); + outbuf = temp->data(); + outbytesLeft = temp->size(); + } + + return result; +} + +/*! + * \brief Convert string from one encoding to another using iconv + */ +template +inline std::basic_string convert(const std::basic_string &source, + iconv_t conv) +{ + return convert(source.cbegin(), source.cend(), conv); +} + +} // namespace pol + +#endif // PREGPARSER_ENCODING diff --git a/src/plugins/pol/iconv.h b/src/plugins/pol/iconv.h new file mode 100644 index 0000000..f7c4074 --- /dev/null +++ b/src/plugins/pol/iconv.h @@ -0,0 +1,31 @@ +/* + * libparsepol - POL Registry file parser + * + * Copyright (C) 2024 BaseALT Ltd. + * Copyright (C) 2020 Korney Yakovlevich + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PREGPARSER_ICONV +#define PREGPARSER_ICONV + +#include + +namespace pol { +static const size_t ICONV_ERROR_CODE = std::numeric_limits::max(); +static const iconv_t ICONV_ERROR_DESCRIPTOR = reinterpret_cast(ICONV_ERROR_CODE); +} // namespace pol + +#endif // PREGPARSER_ICONV diff --git a/src/plugins/pol/iconvwrapper.cpp b/src/plugins/pol/iconvwrapper.cpp deleted file mode 100644 index 1c27cef..0000000 --- a/src/plugins/pol/iconvwrapper.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/*********************************************************************************************************************** -** -** Copyright (C) 2021 BaseALT Ltd. -** -** 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 the Free Software Foundation; either version 2 -** of the License, or (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -** -***********************************************************************************************************************/ - -#include "iconvwrapper.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace gpui { - -IconvWrapper::IconvWrapper(std::string from_encoding, - std::string to_encoding) { - this->fromEncoding = from_encoding; - this->toEncoding = to_encoding; - this->conv = - iconv_open(this->toEncoding.c_str(), this->fromEncoding.c_str()); - if (this->invalidOpen == this->conv) { - throw std::system_error(errno, std::system_category()); - } -} - -IconvWrapper::~IconvWrapper() { - if (this->invalidOpen != this->conv) { - int result = iconv_close(this->conv); - this->conv = this->invalidOpen; - if (0 != result) { - std::cout << "Error on iconv_close " << errno << std::endl; - } - } -} - -std::string IconvWrapper::convert(std::string from) { - /* - Copyright (c) 2011, Yuya Unno - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Yuya Unno nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - bool ignore_error_ = true; - /* Values like INT_MAX cause awful slowdown */ - size_t buf_size_ = 1024; - // copy the string to a buffer as iconv function requires a non-const char - // pointer. - std::vector in_buf(from.begin(), from.end()); - char *src_ptr = &in_buf[0]; - size_t src_size = from.size(); - - std::vector buf(buf_size_); - std::string dst; - while (0 < src_size) { - char *dst_ptr = &buf[0]; - size_t dst_size = buf.size(); - size_t res = - ::iconv(this->conv, &src_ptr, &src_size, &dst_ptr, &dst_size); - if (res == static_cast(-1)) - { - if (errno == E2BIG) { - // ignore this error - } else if (ignore_error_) { - // skip character - ++src_ptr; - --src_size; - } else { - this->checkConversionError(); - } - } - dst.append(&buf[0], buf.size() - dst_size); - } - std::string output; - dst.swap(output); - return output; -} - -void IconvWrapper::checkConversionError() { - switch (errno) { - case EBADF: { - std::cout << "EBADF" << std::endl; - break; - } - case E2BIG: { - std::cout << "E2BIG" << std::endl; - break; - } - case EILSEQ: { - std::cout << "EILSEQ" << std::endl; - break; - } - case EINVAL: { - std::cout << "EINVAL" << std::endl; - break; - } - default: { - std::cout << "Unknown error " << errno << std::endl; - break; - } - } -} - -} diff --git a/src/plugins/pol/iconvwrapper.h b/src/plugins/pol/iconvwrapper.h deleted file mode 100644 index c77c503..0000000 --- a/src/plugins/pol/iconvwrapper.h +++ /dev/null @@ -1,62 +0,0 @@ -/*********************************************************************************************************************** -** -** Copyright (C) 2021 BaseALT Ltd. -** -** 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 the Free Software Foundation; either version 2 -** of the License, or (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -** -***********************************************************************************************************************/ - -#ifndef GPUI_ICONV_WRAPPER_H -#define GPUI_ICONV_WRAPPER_H - -#include - -#include - -namespace gpui { - -/*! - * \brief The IconWrapper provides wrapper for POSIX iconv functionality to ease the access from C++ - * and a convenient way to operate on std::string buffers. - */ -class IconvWrapper { - const iconv_t invalidOpen = reinterpret_cast(-1); - - IconvWrapper(IconvWrapper const &) = delete; - IconvWrapper &operator=(IconvWrapper const &) = delete; - - iconv_t conv = nullptr; - std::string fromEncoding {}; - std::string toEncoding {}; - -public: - IconvWrapper(std::string from_encoding, std::string to_encoding); - ~IconvWrapper(); - - /*! - * \brief Convert std::string to another format. - */ - std::string convert(std::string from); - -private: - /*! - * \brief Check if there were conversion errors. - */ - void checkConversionError(); -}; - -} - -#endif // GPUI_ICONV_WRAPPER_H diff --git a/src/plugins/pol/parser.cpp b/src/plugins/pol/parser.cpp new file mode 100644 index 0000000..d1cc909 --- /dev/null +++ b/src/plugins/pol/parser.cpp @@ -0,0 +1,463 @@ +/* + * libparsepol - POL Registry file parser + * + * Copyright (C) 2024 BaseALT Ltd. + * Copyright (C) 2020 Korney Yakovlevich + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include + +#include "binary.h" +#include "common.h" +#include "parser.h" + +namespace pol { + +/*! + * \brief Valid POL Registery file header + */ +static const char valid_header[8] = { 0x50, 0x52, 0x65, 0x67, 0x01, 0x00, 0x00, 0x00 }; + +/*! + * \brief Match regex `[\x20-\x7E]` + */ +static inline bool isValueCharacter(uint8_t sym) +{ + return sym >= 0x20 && sym <= 0x7E; +} + +GPUI_SYMBOL_EXPORT PRegParser::PRegParser() +{ + this->m_iconvReadId = ::iconv_open("UTF-8", "UTF-16LE"); + this->m_iconvWriteId = ::iconv_open("UTF-16LE", "UTF-8"); +} + +GPUI_SYMBOL_EXPORT PolicyFile PRegParser::parse(std::istream &stream) +{ + PolicyTree instructions; + + parseHeader(stream); + + stream.peek(); + while (!stream.eof()) { + insertInstruction(stream, instructions); + stream.peek(); + } + + return { instructions }; +} + +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); + } + } + } + + return true; +} + +GPUI_SYMBOL_EXPORT PRegParser::~PRegParser() +{ + ::iconv_close(this->m_iconvReadId); + ::iconv_close(this->m_iconvWriteId); +} + +void PRegParser::parseHeader(std::istream &stream) +{ + char buffer[8]; + stream.read(buffer, 8); + check_stream(stream); + + const uint64_t header = *reinterpret_cast(&buffer[0]); + const uint64_t normal_header = *reinterpret_cast(&valid_header[0]); + + if (header != normal_header) { + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Encountered with invalid header."); + } +} + +uint32_t PRegParser::getSize(std::istream &stream) +{ + return readIntegralFromBuffer(stream); +} + +PolicyRegType PRegParser::getType(std::istream &stream) +{ + PolicyRegType type = static_cast(readIntegralFromBuffer(stream)); + + if (type >= PolicyRegType::REG_SZ && type <= PolicyRegType::REG_QWORD_BIG_ENDIAN) { + return type; + } + + return {}; +} + +std::string PRegParser::getKey(std::istream &stream) +{ + std::string key; + char16_t data; + + stream.read(reinterpret_cast(&data), 2); + check_stream(stream); + + data = leToNative(data); + + while (data >= 0x20 && data <= 0x7E && data != 0x5C) { + key.push_back(static_cast(data)); + + stream.read(reinterpret_cast(&data), 2); + check_stream(stream); + + data = leToNative(data); + } + + // Key from Keypath must contain 1 or more symbols. + if (key.empty() || (data != 0 && data != 0x5C)) { + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Unexpected symbol with code " + std::to_string(data) + "."); + } + + // Remove last symbol + stream.seekg(-2, std::ios::cur); + + return { key }; +} + +std::string PRegParser::getKeypath(std::istream &stream) +{ + std::string keyPath; + char16_t sym = 0; + + while (true) { + auto key = getKey(stream); + + keyPath.append(key); + + stream.read(reinterpret_cast(&sym), 2); + check_stream(stream); + + // End of Keypath + if (sym == 0) { + break; + } + + // This if never be executed, but for safety i use it + if (sym != 0x5C) { + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Unexpected symbol with code " + std::to_string(sym) + + "."); + } + + keyPath.push_back('\\'); + } + + return { keyPath }; +} + +std::string PRegParser::getValue(std::istream &stream) +{ + std::string result; + char16_t data; + + stream.read(reinterpret_cast(&data), 2); + check_stream(stream); + data = leToNative(data); + + // Key in specs [\x20-\x5B\x5D-\x7E](exclude '\'), when keypath include '\' like delimeter + while (data >= 0x20 && data <= 0x7E) { + // Key from Keypath must contain 1 or more symbols. + + // Check maximum value length + if (result.length() == 259) { + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Unexpected symbol with code " + std::to_string(data) + + "."); + } + + result.push_back(data); + + stream.read(reinterpret_cast(&data), 2); + check_stream(stream); + data = leToNative(data); + } + + if (data != 0 || result.empty()) { + return {}; + } + + return { result }; +} + +PolicyData PRegParser::getData(std::istream &stream, PolicyRegType type, uint32_t size) +{ + switch (type) { + case PolicyRegType::REG_NONE: + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Unexpected type REG_NONE."); + case PolicyRegType::REG_SZ: + case PolicyRegType::REG_EXPAND_SZ: + case PolicyRegType::REG_LINK: + return { readStringFromBuffer(stream, size, this->m_iconvReadId) }; + + case PolicyRegType::REG_BINARY: + return { readVectorFromBuffer(stream, size) }; + + case PolicyRegType::REG_DWORD_LITTLE_ENDIAN: + return { readIntegralFromBuffer(stream) }; + case PolicyRegType::REG_DWORD_BIG_ENDIAN: + return { readIntegralFromBuffer(stream) }; + + case PolicyRegType::REG_MULTI_SZ: + case PolicyRegType::REG_RESOURCE_LIST: + case PolicyRegType::REG_FULL_RESOURCE_DESCRIPTOR: // ???? + case PolicyRegType::REG_RESOURCE_REQUIREMENTS_LIST: + return { readStringsFromBuffer(stream, size, this->m_iconvReadId) }; + + case PolicyRegType::REG_QWORD_LITTLE_ENDIAN: + return { readIntegralFromBuffer(stream) }; + case PolicyRegType::REG_QWORD_BIG_ENDIAN: + return { readIntegralFromBuffer(stream) }; + break; + } + return {}; +} + +void PRegParser::insertInstruction(std::istream &stream, PolicyTree &tree) +{ + PolicyInstruction instruction; + uint32_t dataSize; + + check_sym(stream, '['); + + std::string keyPath = getKeypath(stream); + + check_sym(stream, ';'); + + std::string value = getValue(stream); + + try { + check_sym(stream, ';'); + + instruction.type = getType(stream); + + check_sym(stream, ';'); + + dataSize = getSize(stream); + + check_sym(stream, ';'); + + instruction.data = getData(stream, instruction.type, dataSize); + + 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)); + + } 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); + } +} + +std::stringstream PRegParser::getDataStream(const PolicyData &data, PolicyRegType type) +{ + std::stringstream stream; + + switch (type) { + case PolicyRegType::REG_SZ: + case PolicyRegType::REG_EXPAND_SZ: + case PolicyRegType::REG_LINK: + writeStringToBuffer(stream, std::get(data), this->m_iconvWriteId); + break; + + case PolicyRegType::REG_BINARY: + writeVectorToBuffer(stream, std::get>(data)); + break; + + case PolicyRegType::REG_DWORD_LITTLE_ENDIAN: + writeIntegralToBuffer(stream, std::get(data)); + break; + case PolicyRegType::REG_DWORD_BIG_ENDIAN: + writeIntegralToBuffer(stream, std::get(data)); + break; + + case PolicyRegType::REG_MULTI_SZ: + case PolicyRegType::REG_RESOURCE_LIST: + case PolicyRegType::REG_FULL_RESOURCE_DESCRIPTOR: // ???? + case PolicyRegType::REG_RESOURCE_REQUIREMENTS_LIST: + writeStringsFromBuffer(stream, std::get>(data), + this->m_iconvWriteId); + break; + + case PolicyRegType::REG_QWORD_LITTLE_ENDIAN: + writeIntegralToBuffer(stream, std::get(data)); + break; + case PolicyRegType::REG_QWORD_BIG_ENDIAN: + writeIntegralToBuffer(stream, std::get(data)); + break; + + case PolicyRegType::REG_NONE: + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Unexpected type REG_NONE."); + default: + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Unexpected type UNKNOWN(" + + std::to_string(static_cast(type)) + "."); + } + + return stream; +} + +void PRegParser::writeHeader(std::ostream &stream) +{ + stream.write(valid_header, sizeof(valid_header)); +} + +void PRegParser::validateKey(std::string::const_iterator &begin, std::string::const_iterator &end) +{ + auto cursor = begin; + + while (cursor != end && *cursor >= 0x20 && *cursor <= 0x7E && *cursor != 0x5C) { + ++cursor; + } + + if (cursor == begin) { + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Key is empty."); + } + begin = cursor; +} + +void PRegParser::validateKeypath(std::string::const_iterator begin, std::string::const_iterator end) +{ + if (begin == end) { + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Keypath is empty."); + } + while (begin != end) { + validateKey(begin, end); + + if (begin != end && *begin != 0x5C) { + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Invalid character in key was encountered."); + } + + // Skip 0x5C character + ++begin; + } +} +void PRegParser::validateValue(std::string::const_iterator begin, std::string::const_iterator end) +{ + if (begin == end) { + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Value is empty."); + } + + while (begin != end) { + if (*begin < 0x20 || *begin > 0x7E) { + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Invalid character in value was encountered."); + } + ++begin; + } +} + +void PRegParser::validateType(PolicyRegType type) +{ + switch (type) { + default: + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Unexpected type UNKNOWN."); + case PolicyRegType::REG_NONE: + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Unexpected type REG_NONE."); + + case PolicyRegType::REG_SZ: + case PolicyRegType::REG_EXPAND_SZ: + case PolicyRegType::REG_BINARY: + case PolicyRegType::REG_DWORD_LITTLE_ENDIAN: + case PolicyRegType::REG_DWORD_BIG_ENDIAN: + case PolicyRegType::REG_LINK: + case PolicyRegType::REG_MULTI_SZ: + case PolicyRegType::REG_RESOURCE_LIST: + case PolicyRegType::REG_FULL_RESOURCE_DESCRIPTOR: + case PolicyRegType::REG_RESOURCE_REQUIREMENTS_LIST: + case PolicyRegType::REG_QWORD_LITTLE_ENDIAN: + case PolicyRegType::REG_QWORD_BIG_ENDIAN: + break; + } +} + +void PRegParser::writeInstruction(std::ostream &stream, const PolicyInstruction &instruction, + std::string key, std::string value) +{ + + try { + validateKeypath(key.begin(), key.end()); + validateValue(value.begin(), value.end()); + validateType(instruction.type); + + write_sym(stream, '['); + + writeStringToBuffer(stream, key); + + write_sym(stream, ';'); + + writeStringToBuffer(stream, value); + + write_sym(stream, ';'); + + writeIntegralToBuffer(stream, static_cast(instruction.type)); + + write_sym(stream, ';'); + + auto dataStream = getDataStream(instruction.data, instruction.type); + + writeIntegralToBuffer(stream, static_cast(dataStream.tellp())); + + write_sym(stream, ';'); + + stream << dataStream.str(); + check_stream(stream); + + write_sym(stream, ']'); + } catch (const std::exception &e) { + throw std::runtime_error(std::string(e.what()) + "\nLINE: " + std::to_string(__LINE__) + + ", FILE: " + __FILE__ + + ", Error was encountered while writing instruction with key: " + + key + ", value: " + value); + } +} + +GPUI_SYMBOL_EXPORT std::unique_ptr createPregParser() +{ + return std::make_unique(); +} + +} // namespace pol diff --git a/src/plugins/pol/parser.h b/src/plugins/pol/parser.h new file mode 100644 index 0000000..c838772 --- /dev/null +++ b/src/plugins/pol/parser.h @@ -0,0 +1,200 @@ +/* + * libparsepol - POL Registry file parser + * + * Copyright (C) 2024 BaseALT Ltd. + * Copyright (C) 2020 Korney Yakovlevich + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef PREGPARSER_PARSER +#define PREGPARSER_PARSER + +#include "../../../src/core/core.h" +#include +#include +#include +#include +#include +#include +#include + +#include "iconv.h" + +namespace pol { + +enum class PolicyRegType { + REG_NONE, + /* Null-terminated-string */ + REG_SZ = 1, + REG_EXPAND_SZ = 2, + + /* Any kind of binary data */ + REG_BINARY = 3, + /* 32-bit number */ + REG_DWORD_LITTLE_ENDIAN = 4, + + /* 32-bit number in NBO format */ + REG_DWORD_BIG_ENDIAN = 5, + + /* A null-terminated Unicode string that contains the target path of a + * symbolic link. */ + REG_LINK = 6, + + /* Sequence of null-terminated strings terminated by null-terminator */ + REG_MULTI_SZ = 7, + REG_RESOURCE_LIST = 8, + REG_FULL_RESOURCE_DESCRIPTOR = 9, + REG_RESOURCE_REQUIREMENTS_LIST = 10, + + /* 64-bit number */ + REG_QWORD_LITTLE_ENDIAN = 11, + REG_QWORD_BIG_ENDIAN = 12, +}; + +typedef std::variant, std::vector, uint32_t, + uint64_t> + PolicyData; + +typedef struct PolicyInstruction +{ + inline bool operator==(const PolicyInstruction &other) const + { + return type == other.type && data == other.data; + } + inline bool operator!=(const PolicyInstruction &other) const + { + return type != other.type && data != other.data; + } + + PolicyRegType type{}; + PolicyData data{}; +} PolicyInstruction; + +typedef std::unordered_map>> + PolicyTree; + +typedef struct PolicyFile +{ + inline bool operator==(const PolicyFile &other) const + { + return instructions == other.instructions; + } + inline bool operator!=(const PolicyFile &other) const + { + return instructions != other.instructions; + } + + PolicyTree instructions{}; +} PolicyFile; + +class GPUI_CORE_EXPORT PRegParser final +{ +private: + /*! + * \brief Check regex `\x50\x52\x65\x67\x01\x00\x00\x00` + */ + void parseHeader(std::istream &stream); + /*! + * \brief Check regex `(.{4})` and return first group as uint32_t (LE, it will be converted to + * native) + */ + uint32_t getSize(std::istream &stream); + /*! + * \brief Convert binary data from stream to PolicyData + */ + PolicyData getData(std::istream &stream, PolicyRegType type, uint32_t size); + /*! + * \brief Check 32bit LE regex `([\x1\x2\x3\x4\x5\x6\x7\x8\x9\xA\xB\xC])` and return first + * group as Type + */ + PolicyRegType getType(std::istream &stream); + /*! + * \brief Matches regex `([\x20-\x5B\x5D-\x7E]\x00)+` and return + * string as result (UTF-16LE will be converted to UTF-8) + */ + std::string getKey(std::istream &stream); + /*! + * \brief Matches regex + * `((:?([\x20-\x5B\x5D-\x7E]\x00)+)(:?\x5C\x00([\x20-\x5B\x5D-\x7E]\x00)+)+)` and return first + * group as result + */ + std::string getKeypath(std::istream &stream); + /*! + * \brief Matches regex `((:?[\x20-\x7E]\x00){1,259})` and return first group as result + * (UTF-16LE will be converted to UTF-8) + */ + std::string getValue(std::istream &stream); + /*! + * \brief Matches ABNF `LBracket KeyPath SC Value SC Type SC Size SC Data RBracket`, + * where LBracket `\x5B\x00`, RBracket `\x5D\x00`, SC `\x3B\x00`. Return reduced structure + */ + void insertInstruction(std::istream &stream, PolicyTree &tree); + + /*! + * \brief Matches regex `([\x20-\x5B\x5D-\x7E]\x00)+` and throws an + * std::runtime_error if it completely does not match the regex + */ + void validateKey(std::string::const_iterator &begin, std::string::const_iterator &end); + /*! + * \brief Matches regex + * `((:?([\x20-\x5B\x5D-\x7E]\x00)+)(:?\x5C\x00([\x20-\x5B\x5D-\x7E]\x00)+)+)` and throws an + * std::runtime_error if it completely does not match the regex + */ + void validateKeypath(std::string::const_iterator begin, std::string::const_iterator end); + /*! + * \brief Matches regex `((:?[\x20-\x7E]\x00){1,259})` and throws an + * std::runtime_error if it completely does not match the regex + */ + void validateValue(std::string::const_iterator begin, std::string::const_iterator end); + /*! + * \brief Validate type and throw an std::runtime_error if it is invalid + */ + void validateType(PolicyRegType type); + /*! + * \brief Put `\x50\x52\x65\x67\x01\x00\x00\x00` into stream + */ + void writeHeader(std::ostream &stream); + /*! + * \brief Put instruction, with ABNF + * `LBracket KeyPath SC Value SC Type SC Size SC Data RBracket`, + * where LBracket `\x5B\x00`, RBracket `\x5D\x00`, SC `\x3B\x00`, into stream. + */ + void writeInstruction(std::ostream &stream, const PolicyInstruction &instruction, + std::string key, std::string value); + + /*! + * \brief Put PolicyRegData by PolicyRegType into stringstream + */ + std::stringstream getDataStream(const PolicyData &data, PolicyRegType type); + +public: + PRegParser(); + PolicyFile parse(std::istream &stream); + bool write(std::ostream &stream, const PolicyFile &file); + ~PRegParser(); + +private: + PRegParser(const pol::PRegParser &) = delete; + void operator=(const pol::PRegParser &) = delete; + + ::iconv_t m_iconvReadId{}; + ::iconv_t m_iconvWriteId{}; +}; + +std::unique_ptr createPregParser(); + +} // namespace pol + +#endif // PREGPARSER_PARSER diff --git a/src/plugins/pol/polformat.cpp b/src/plugins/pol/polformat.cpp index 8a5be80..9584bf0 100644 --- a/src/plugins/pol/polformat.cpp +++ b/src/plugins/pol/polformat.cpp @@ -20,320 +20,236 @@ #include "polformat.h" -#include "pregdata.h" -#include "pregparser.h" -#include "pregwriter.h" - #include "../../../src/plugins/administrative_templates/registry/registry.h" #include "../../../src/plugins/administrative_templates/registry/registryentry.h" #include "../../../src/plugins/administrative_templates/registry/registryentrytype.h" +#include "parser.h" + #include +#include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" using namespace model::registry; -namespace gpui -{ +namespace gpui { class RegistryEntryAdapter { -private: - static std::unique_ptr adaptCharEntry( - const preg::PregEntry &entry, model::registry::RegistryEntryType type) - { - auto registryEntry = std::make_unique>(); - registryEntry->key = entry.key.c_str(); - registryEntry->type = type; - registryEntry->value = entry.value.c_str(); - if (entry.data) - { - void *pt = entry.data; - size_t requiredSize = entry.size; - registryEntry->data = QString::fromUtf16( - reinterpret_cast(std::align(alignof(char16_t), sizeof(char), pt, requiredSize))); - delete[] entry.data; - } - - return registryEntry; - } - - static std::unique_ptr adaptUInt32Entry( - const preg::PregEntry &entry, model::registry::RegistryEntryType type, bool bigEndian = false) - { - auto registryEntry = std::make_unique>(); - registryEntry->key = entry.key.c_str(); - registryEntry->type = type; - registryEntry->value = entry.value.c_str(); - if (entry.data) - { - uint32_t data = (uint32_t)((uint8_t) entry.data[0]) | (uint32_t)((uint8_t) entry.data[1]) << 8 - | (uint32_t)((uint8_t) entry.data[2]) << 16 | (uint32_t)((uint8_t) entry.data[3]) << 24; - - if (bigEndian) - { - data = bswap_32(data); - } - registryEntry->data = data; - delete[] entry.data; - } - - return registryEntry; - } - - static std::unique_ptr adaptUInt64Entry( - const preg::PregEntry &entry, model::registry::RegistryEntryType type, bool bigEndian = false) - { - auto registryEntry = std::make_unique>(); - registryEntry->key = entry.key.c_str(); - registryEntry->type = type; - registryEntry->value = entry.value.c_str(); - if (entry.data) - { - uint64_t data = (uint64_t)((uint8_t) entry.data[0]) | (uint64_t)((uint8_t) entry.data[1]) << 8 - | (uint64_t)((uint8_t) entry.data[2]) << 16 | (uint64_t)((uint8_t) entry.data[3]) << 24 - | (uint64_t)((uint8_t) entry.data[4]) << 32 | (uint64_t)((uint8_t) entry.data[5]) << 40 - | (uint64_t)((uint8_t) entry.data[6]) << 48 | (uint64_t)((uint8_t) entry.data[7]) << 56; - if (bigEndian) - { - data = bswap_64(data); - } - registryEntry->data = data; - delete[] entry.data; - } - - return registryEntry; - } - - static std::unique_ptr adaptMultiLineEntry( - const preg::PregEntry &entry, model::registry::RegistryEntryType type) - { - auto registryEntry = std::make_unique>(); - registryEntry->key = entry.key.c_str(); - registryEntry->type = type; - registryEntry->value = entry.value.c_str(); - if (entry.data) - { - size_t size = entry.size - 2 > 0 ? entry.size - 2 : entry.size; - void *pt = entry.data; - size_t requiredSize = entry.size; - const char16_t *data = reinterpret_cast( - std::align(alignof(char16_t), sizeof(char), pt, requiredSize)); - std::vector> list; - std::vector current; - qWarning() << "Data: " - << QString::fromUtf16(reinterpret_cast( - std::align(alignof(char16_t), sizeof(char), pt, requiredSize)), - size / 2) - << " size: " << size; - for (size_t i = 0; i < size / 2; ++i) - { - current.push_back(data[i]); - if (data[i] == 0) - { - qWarning() << current << " split at: " << i; - list.emplace_back(current); - current.clear(); - } - } - for (const auto &element : list) - { - registryEntry->data.push_back(QString::fromUtf16(element.data())); - } - delete[] entry.data; - } - - return registryEntry; - } - public: - static std::unique_ptr create(const preg::PregEntry &entry) + static void addInstruction(pol::PolicyTree &tree, + const std::unique_ptr &entry) { - switch (entry.type) - { - case preg::REG_BINARY: { - return adaptCharEntry(entry, model::registry::REG_BINARY); - } - break; + pol::PolicyInstruction instruction; - case preg::REG_DWORD_LITTLE_ENDIAN: { - return adaptUInt32Entry(entry, model::registry::REG_DWORD); - } - break; + auto key = entry->key.toStdString(); + auto value = entry->value.toStdString(); - case preg::REG_DWORD_BIG_ENDIAN: { - return adaptUInt32Entry(entry, model::registry::REG_DWORD_BIG_ENDIAN, true); + if (tree.find(key) == tree.end()) { + tree[key] = {}; + } + if (tree[key].find(value) == tree[key].end()) { + tree[key][value] = {}; } - break; - case preg::REG_EXPAND_SZ: { - return adaptCharEntry(entry, model::registry::REG_EXPAND_SZ); + switch (entry->type) { + case model::registry::RegistryEntryType::REG_SZ: { + auto tmp = static_cast *>(entry.get()); + instruction.type = pol::PolicyRegType::REG_SZ; + instruction.data = tmp->data.toStdString(); + break; } - break; - - case preg::REG_LINK: { - return adaptCharEntry(entry, model::registry::REG_BINARY); - } - break; - - case preg::REG_MULTI_SZ: { - return adaptMultiLineEntry(entry, model::registry::REG_MULTI_SZ); - } - break; - - case preg::REG_NONE: { - return adaptCharEntry(entry, model::registry::REG_BINARY); - } - break; - - case preg::REG_QWORD: { - return adaptUInt64Entry(entry, model::registry::REG_QWORD, true); - } - break; - - case preg::REG_QWORD_LITTLE_ENDIAN: { - return adaptUInt64Entry(entry, model::registry::REG_QWORD); - } - break; - - case preg::REG_SZ: { - return adaptCharEntry(entry, model::registry::REG_SZ); - } - break; - - default: { - qWarning() << "Unrecognized data type detected! " << entry.type; - delete[] entry.data; - } - break; - }; - - return nullptr; - } -}; - -class PregEntryAdapter -{ -public: - static preg::PregEntry create(const std::unique_ptr &entry) - { - auto result = preg::PregEntry(); - result.key = entry->key.toStdString(); - result.value = entry->value.toStdString(); - result.type = entry->type; - - switch (entry->type) - { - case REG_BINARY: - case REG_EXPAND_SZ: - case REG_SZ: { - auto binaryEntry = static_cast *>(entry.get()); - auto sixteenBitString = binaryEntry->data.toStdU16String(); - size_t bufferSize = sixteenBitString.size() * sizeof(char16_t); - char *stringData = new char[bufferSize + 2]; - memcpy(stringData, sixteenBitString.c_str(), bufferSize); - stringData[bufferSize] = '\0'; - stringData[bufferSize + 1] = '\0'; - result.data = stringData; - result.size = bufferSize + 2; - } - break; - case REG_DWORD: - case REG_DWORD_BIG_ENDIAN: { - auto uint32Entry = static_cast *>(entry.get()); - result.size = 4; - size_t bufferSize = sizeof(uint32_t); - char *stringData = new char[bufferSize]; - stringData[0] = (uint8_t)((uint8_t *) (&uint32Entry->data))[0]; - stringData[1] = (uint8_t)((uint8_t *) (&uint32Entry->data))[1]; - stringData[2] = (uint8_t)((uint8_t *) (&uint32Entry->data))[2]; - stringData[3] = (uint8_t)((uint8_t *) (&uint32Entry->data))[3]; - - result.data = stringData; - } - break; - case REG_QWORD: { - auto uint64Entry = static_cast *>(entry.get()); - result.size = 8; - size_t bufferSize = sizeof(uint64_t); - char *stringData = new char[bufferSize]; - stringData[0] = (uint8_t)((uint8_t *) (&uint64Entry->data))[0]; - stringData[1] = (uint8_t)((uint8_t *) (&uint64Entry->data))[1]; - stringData[2] = (uint8_t)((uint8_t *) (&uint64Entry->data))[2]; - stringData[3] = (uint8_t)((uint8_t *) (&uint64Entry->data))[3]; - stringData[4] = (uint8_t)((uint8_t *) (&uint64Entry->data))[4]; - stringData[5] = (uint8_t)((uint8_t *) (&uint64Entry->data))[5]; - stringData[6] = (uint8_t)((uint8_t *) (&uint64Entry->data))[6]; - stringData[7] = (uint8_t)((uint8_t *) (&uint64Entry->data))[7]; - result.data = stringData; - } - break; - case REG_MULTI_SZ: { - auto binaryEntry = static_cast *>(entry.get()); - QByteArray byteArray; - for (const QString &str : binaryEntry->data) - { - auto sixteenBitString = str.toStdU16String(); - size_t size = sixteenBitString.size() * sizeof(char16_t); - byteArray.append(reinterpret_cast(sixteenBitString.c_str()), size); - byteArray.append(2, '\0'); - } - if (byteArray.size() == 0) - { - byteArray.append(4, '\0'); - } - else - { - byteArray.append(2, '\0'); - } - char *stringData = new char[byteArray.size()]; - memcpy(stringData, byteArray, byteArray.size()); - result.data = stringData; - result.size = byteArray.size(); - } - break; - default: + case model::registry::RegistryEntryType::REG_EXPAND_SZ: { + auto tmp = static_cast *>(entry.get()); + instruction.type = pol::PolicyRegType::REG_EXPAND_SZ; + instruction.data = tmp->data.toStdString(); break; } - return result; + case model::registry::RegistryEntryType::REG_BINARY: { + auto tmp = static_cast *>(entry.get()); + instruction.type = pol::PolicyRegType::REG_BINARY; + + auto tmp2 = tmp->data.toStdString(); + + std::vector data; + data.resize(tmp->data.size()); + memcpy(data.data(), tmp2.data(), tmp2.size()); + instruction.data = std::move(data); + break; + } + + case model::registry::RegistryEntryType::REG_MULTI_SZ: { + auto tmp = static_cast *>(entry.get()); + instruction.type = pol::PolicyRegType::REG_MULTI_SZ; + std::vector data; + data.reserve(tmp->data.size()); + for (const auto &str : tmp->data) { + data.push_back(str.toStdString()); + } + instruction.data = std::move(data); + break; + } + + case model::registry::RegistryEntryType::REG_DWORD: { + auto tmp = static_cast *>(entry.get()); + instruction.type = pol::PolicyRegType::REG_DWORD_LITTLE_ENDIAN; + instruction.data = static_cast(tmp->data); + break; + } + + case model::registry::RegistryEntryType::REG_DWORD_BIG_ENDIAN: { + auto tmp = static_cast *>(entry.get()); + instruction.type = pol::PolicyRegType::REG_DWORD_BIG_ENDIAN; + instruction.data = static_cast(tmp->data); + break; + } + case model::registry::RegistryEntryType::REG_QWORD: { + auto tmp = static_cast *>(entry.get()); + instruction.type = pol::PolicyRegType::REG_QWORD_LITTLE_ENDIAN; + instruction.data = static_cast(tmp->data); + break; + } + default: { + qWarning() << "Unrecognized data type `REG_NONE` detected! "; + return; + } + } + + tree[key][value].emplace_back(instruction); + } + + static std::unique_ptr + create(const pol::PolicyInstruction &entry, const std::string &key, const std::string &value) + { + switch (entry.type) { + case pol::PolicyRegType::REG_SZ: { + auto registryEntry = std::make_unique>(); + + registryEntry->key = QString::fromStdString(key); + registryEntry->value = QString::fromStdString(value); + registryEntry->type = model::registry::RegistryEntryType::REG_SZ; + registryEntry->data = QString::fromStdString(std::get(entry.data)); + + return registryEntry; + } + case pol::PolicyRegType::REG_EXPAND_SZ: { + auto registryEntry = std::make_unique>(); + + registryEntry->key = QString::fromStdString(key); + registryEntry->value = QString::fromStdString(value); + registryEntry->type = model::registry::RegistryEntryType::REG_EXPAND_SZ; + registryEntry->data = QString::fromStdString(std::get(entry.data)); + + return registryEntry; + } + case pol::PolicyRegType::REG_BINARY: { + auto registryEntry = std::make_unique>(); + + registryEntry->key = QString::fromStdString(key); + registryEntry->value = QString::fromStdString(value); + registryEntry->type = model::registry::RegistryEntryType::REG_BINARY; + auto &data = std::get>(entry.data); + registryEntry->data = QString::fromStdString( + { reinterpret_cast(data.data()), data.size() }); + + return registryEntry; + } + case pol::PolicyRegType::REG_DWORD_LITTLE_ENDIAN: { + auto registryEntry = std::make_unique>(); + + registryEntry->key = QString::fromStdString(key); + registryEntry->value = QString::fromStdString(value); + registryEntry->type = model::registry::RegistryEntryType::REG_DWORD; + registryEntry->data = std::get(entry.data); + + return registryEntry; + } + case pol::PolicyRegType::REG_DWORD_BIG_ENDIAN: { + auto registryEntry = std::make_unique>(); + + registryEntry->key = QString::fromStdString(key); + registryEntry->value = QString::fromStdString(value); + registryEntry->type = model::registry::RegistryEntryType::REG_DWORD_BIG_ENDIAN; + registryEntry->data = std::get(entry.data); + + return registryEntry; + } + case pol::PolicyRegType::REG_LINK: { + auto registryEntry = std::make_unique>(); + + registryEntry->key = QString::fromStdString(key); + registryEntry->value = QString::fromStdString(value); + registryEntry->type = model::registry::RegistryEntryType::REG_BINARY; + + auto &data = registryEntry->data = + QString::fromStdString(std::get(entry.data)); + + return registryEntry; + } + case pol::PolicyRegType::REG_MULTI_SZ: { + auto registryEntry = std::make_unique>(); + + registryEntry->key = QString::fromStdString(key); + registryEntry->value = QString::fromStdString(value); + registryEntry->type = model::registry::RegistryEntryType::REG_MULTI_SZ; + + auto &data = std::get>(entry.data); + registryEntry->data.reserve(data.size()); + for (const auto &datum : data) { + registryEntry->data.append(QString::fromStdString(datum)); + } + + return registryEntry; + } + case pol::PolicyRegType::REG_QWORD_BIG_ENDIAN: + case pol::PolicyRegType::REG_QWORD_LITTLE_ENDIAN: { + auto registryEntry = std::make_unique>(); + + registryEntry->key = QString::fromStdString(key); + registryEntry->value = QString::fromStdString(value); + registryEntry->type = model::registry::RegistryEntryType::REG_QWORD; + registryEntry->data = std::get(entry.data); + + return registryEntry; + } + case pol::PolicyRegType::REG_RESOURCE_LIST: + case pol::PolicyRegType::REG_FULL_RESOURCE_DESCRIPTOR: + case pol::PolicyRegType::REG_RESOURCE_REQUIREMENTS_LIST: + default: + case pol::PolicyRegType::REG_NONE: + qWarning() << "Unrecognized data type detected! " << static_cast(entry.type); + return nullptr; + } } }; -PolFormat::PolFormat() - : RegistryFileFormat("pol") -{} +PolFormat::PolFormat() : RegistryFileFormat("pol") { } bool PolFormat::read(std::istream &input, io::RegistryFile *file) { auto registry = std::make_shared(); - try - { - auto parser = std::make_unique(input); + auto parser = pol::createPregParser(); + pol::PolicyFile result; + try { + result = parser->parse(input); + } catch (const std::exception &e) { + qWarning() << e.what(); + return false; + } - preg::PregEntry entry; - - while (auto entryPointer = parser->getNextEntry()) - { - auto registryEntry = RegistryEntryAdapter::create(*entryPointer); - if (registryEntry.get()) - { - registry->registryEntries.push_back(std::move(registryEntry)); + 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)); + } } } } - catch (preg::InvalidMagic &e) - { - setErrorString(e.what()); - return false; - } - catch (preg::InvalidVersion &e) - { - setErrorString(e.what()); - return false; - } file->setRegistry(registry); @@ -342,34 +258,17 @@ bool PolFormat::read(std::istream &input, io::RegistryFile *file) bool PolFormat::write(std::ostream &output, io::RegistryFile *file) { - auto writer = std::make_unique(&output); + auto writer = pol::createPregParser(); + auto result = pol::PolicyFile(); - try - { - for (const auto &entry : file->getRegistry()->registryEntries) - { - auto pregEntry = PregEntryAdapter::create(entry); - writer->addEntry(pregEntry); - - switch (pregEntry.type) - { - case REG_DWORD: - case REG_DWORD_BIG_ENDIAN: - case REG_QWORD: - case REG_BINARY: - case REG_EXPAND_SZ: - case REG_MULTI_SZ: - case REG_SZ: { - delete[] pregEntry.data; - } - default: - break; - } - } + for (const auto &entry : file->getRegistry()->registryEntries) { + RegistryEntryAdapter::addInstruction(result.instructions, entry); } - catch (std::exception &e) - { - setErrorString(e.what()); + + try { + writer->write(output, result); + } catch (const std::exception &e) { + qWarning() << e.what(); return false; } diff --git a/src/plugins/pol/pregdata.cpp b/src/plugins/pol/pregdata.cpp deleted file mode 100644 index 3dff0f7..0000000 --- a/src/plugins/pol/pregdata.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/*********************************************************************************************************************** -** -** Copyright (C) 2021 BaseALT Ltd. -** -** 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 the Free Software Foundation; either version 2 -** of the License, or (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -** -***********************************************************************************************************************/ - -#include "pregdata.h" - -namespace preg { - -std::string regtype2str(uint32_t ®type) { - std::string result = "UNKNOWN"; - - switch (regtype) { - case REG_NONE: { - result = "REG_NONE"; - break; - } - case REG_SZ: { - result = "REG_SZ"; - break; - } - case REG_EXPAND_SZ: { - result = "REG_EXPAND_SZ"; - break; - } - case REG_BINARY: { - result = "REG_BINARY"; - break; - } - case REG_DWORD_LITTLE_ENDIAN: { - result = "REG_DWORD_LITTLE_ENDIAN"; - break; - } - case REG_DWORD_BIG_ENDIAN: { - result = "REG_DWORD_BIG_ENDIAN"; - break; - } - case REG_LINK: { - result = "REG_LINK"; - break; - } - case REG_MULTI_SZ: { - result = "REG_MULTI_SZ"; - break; - } - case REG_RESOURCE_LIST: { - result = "REG_RESOURCE_LIST"; - break; - } - case REG_FULL_RESOURCE_DESCRIPTOR: { - result = "REG_FULL_RESOURCE_DESCRIPTOR"; - break; - } - case REG_RESOURCE_REQUIREMENTS_LIST: { - result = "REG_RESOURCE_REQUIREMENTS_LIST"; - break; - } - case REG_QWORD: { - result = "REG_QWORD"; - break; - } - case REG_QWORD_LITTLE_ENDIAN: { - result = "REG_QWORD_LITTLE_ENDIAN"; - break; - } - default: { - result = "UNKNOWN"; - break; - } - } /* switch (regtype) */ - - return result; -} /* std::string regtype2str(uint16_t ®type) */ - -uint32_t str2regtype(std::string ®type) { - uint32_t result = 0; - - if ("REG_NONE" == regtype) { - result = REG_NONE; - } - if ("REG_SZ" == regtype) { - result = REG_SZ; - } - if ("REG_EXPAND_SZ" == regtype) { - result = REG_EXPAND_SZ; - } - if ("REG_BINARY" == regtype) { - result = REG_BINARY; - } - if ("REG_DWORD_LITTLE_ENDIAN" == regtype || "REG_DWORD" == regtype) { - result = REG_DWORD_LITTLE_ENDIAN; - } - if ("REG_DWORD_BIG_ENDIAN" == regtype) { - result = REG_DWORD_BIG_ENDIAN; - } - if ("REG_LINK" == regtype) { - result = REG_LINK; - } - if ("REG_MULTI_SZ" == regtype) { - result = REG_MULTI_SZ; - } - if ("REG_RESOURCE_LIST" == regtype) { - result = REG_RESOURCE_LIST; - } - if ("REG_FULL_RESOURCE_DESCRIPTOR" == regtype) { - result = REG_FULL_RESOURCE_DESCRIPTOR; - } - if ("REG_RESOURCE_REQUIREMENTS_LIST" == regtype) { - result = REG_RESOURCE_REQUIREMENTS_LIST; - } - if ("REG_QWORD" == regtype) { - result = REG_QWORD; - } - if ("REG_QWORD_LITTLE_ENDIAN" == regtype) { - result = REG_QWORD_LITTLE_ENDIAN; - } - - return result; -} /* uint16_t str2regtype(std::string ®type) */ - -const char *InvalidMagic::what() const throw() { - return "Invalid PReg file magic value"; -} - -const char *InvalidVersion::what() const throw() { - return "Invalid PReg file version"; -} - -} diff --git a/src/plugins/pol/pregdata.h b/src/plugins/pol/pregdata.h deleted file mode 100644 index 45c6baf..0000000 --- a/src/plugins/pol/pregdata.h +++ /dev/null @@ -1,97 +0,0 @@ -/*********************************************************************************************************************** -** -** Copyright (C) 2021 BaseALT Ltd. -** -** 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 the Free Software Foundation; either version 2 -** of the License, or (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -** -***********************************************************************************************************************/ - -#ifndef GPUI_PREG_DATA_H -#define GPUI_PREG_DATA_H - -#include -#include -#include -#include - -namespace preg { - -/* Same as REG_BINARY */ -const uint32_t REG_NONE = 0; - -/* Null-terminated-string */ -const uint32_t REG_SZ = 1; - -/* A null-terminated UTF16-LE or ANSI string that contains unexpanded - * references to environment variables. */ -const uint32_t REG_EXPAND_SZ = 2; - -/* Any kind of binary data */ -const uint32_t REG_BINARY = 3; - -/* 32-bit number */ -const uint32_t REG_DWORD_LITTLE_ENDIAN = 4; - -/* 32-bit number in NBO format */ -const uint32_t REG_DWORD_BIG_ENDIAN = 5; - -/* A null-terminated Unicode string that contains the target path of a - * symbolic link. */ -const uint32_t REG_LINK = 6; - -/* Sequence of null-terminated strings terminated by null-terminator */ -const uint32_t REG_MULTI_SZ = 7; -const uint32_t REG_RESOURCE_LIST = 8; -const uint32_t REG_FULL_RESOURCE_DESCRIPTOR = 9; -const uint32_t REG_RESOURCE_REQUIREMENTS_LIST = 10; - -/* 64-bit number */ -const uint32_t REG_QWORD = 11; -const uint32_t REG_QWORD_LITTLE_ENDIAN = 12; - -struct PregEntry -{ - std::string key {}; - std::string value {}; - uint32_t type = REG_NONE; - uint32_t size = 0; - char *data = nullptr; -}; - -class InvalidMagic : public std::exception { -public: - virtual const char *what() const throw(); -}; - -class InvalidVersion : public std::exception { -public: - virtual const char *what() const throw(); -}; - -/*! - * \brief Convert PReg type value from DWORD into string representation. - * May be useful when operating on PReg files from GUI. - */ -std::string regtype2str(uint32_t ®type); - -/*! - * \brief Convert PReg type string representation into DWORD. - * May be useful when serializing data. - */ -uint32_t str2regtype(std::string ®type); - -} - -#endif // GPUI_PREG_DATA_H diff --git a/src/plugins/pol/pregparser.cpp b/src/plugins/pol/pregparser.cpp deleted file mode 100644 index cfda73c..0000000 --- a/src/plugins/pol/pregparser.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/*********************************************************************************************************************** -** -** Copyright (C) 2021 BaseALT Ltd. -** -** 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 the Free Software Foundation; either version 2 -** of the License, or (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -** -***********************************************************************************************************************/ - -#include "pregparser.h" - -#include "iconvwrapper.h" - -#include - -namespace preg { - -namespace { - - bool isRangeStart(char symbol) { - if ('[' == symbol) { - return true; - } - return false; - } - - bool isRangeEnd(char symbol) { - if (']' == symbol) { - return true; - } - return false; - } - - bool isPregEntrySeparator(char symbol) { - if (';' == symbol) { - return true; - } - return false; - } - -} - -uint16_t buffer2uint16(const char *type_buffer) { - uint16_t num = - static_cast(static_cast(type_buffer[1]) << 8 | - static_cast(type_buffer[0])); - return num; -} - -uint32_t buffer2uint32(const char *type_buffer) { - uint32_t num = - static_cast(static_cast(type_buffer[3]) << 24 | - static_cast(type_buffer[2]) << 16 | - static_cast(type_buffer[1]) << 8 | - static_cast(type_buffer[0])); - return num; -} - -uint32_t parse_type(const char *type_buffer) { - return buffer2uint32(type_buffer); -} - -PregParser::PregParser(std::istream &input) - : polfile(&input) -{ - this->loadRegpol(); -} - -void PregParser::loadRegpol() { - if (this->polfile->good()) { - this->polfile->seekg(0, std::ios::end); /* Go to the end of file */ - this->rawFileSize = this->polfile->tellg(); /* Get file length */ - - this->polfile->seekg(0, std::ios::beg); /* Set file position to beginning */ - - this->readHeader(); - this->readVersion(); - } -} - -void PregParser::readHeader() { - if (this->polfile->good() && 4 < this->rawFileSize) { - this->polfile->seekg(0, std::ios::beg); /* Set file position to beginning */ - this->polfile->read(this->header, 4); /* Read first 4 bytes */ - } - this->checkHeader(); -} - -void PregParser::readVersion() { - if (this->polfile->good() && 8 < this->rawFileSize) { - /* Read bytes 4-7 of the file */ - this->polfile->seekg(4, std::ios::beg); - this->polfile->read(this->version, 4); - } - this->checkVersion(); -} - -void PregParser::checkHeader() { - if ('P' == this->header[0] && 'R' == this->header[1] && - 'e' == this->header[2] && 'g' == this->header[3]) { - } else { - throw InvalidMagic(); - } -} - -void PregParser::checkVersion() { - if (1 == this->version[0] && 0 == this->version[1] && - 0 == this->version[2] && 0 == this->version[3]) { - } else { - throw InvalidVersion(); - } -} - -char PregParser::readByte(size_t absFileStartOffset) { - char symbol = 0; - if (absFileStartOffset < this->rawFileSize) { - this->polfile->seekg(absFileStartOffset, std::ios::beg); - this->polfile->read(&symbol, 1); - } - // FIXME: Else throw exception. - return symbol; -} - -size_t PregParser::seekNextSeparator(size_t absFileStartOffset) { - size_t end_offset = absFileStartOffset; - if (absFileStartOffset < this->rawFileSize) { - char sym_buf; - for (size_t abs_file_offset = absFileStartOffset; - abs_file_offset <= this->rawFileSize; abs_file_offset++) { - sym_buf = this->readByte(abs_file_offset); - if (isRangeStart(sym_buf) || isPregEntrySeparator(sym_buf) || - isRangeEnd(sym_buf) || - abs_file_offset == this->rawFileSize) { - - end_offset = abs_file_offset; - break; - } - } - } else { - end_offset = this->rawFileSize; - } - return end_offset; -} - -std::unique_ptr PregParser::getNextKeyEntry() { - auto entry = std::make_unique(); - entry->startOffset = this->nextEntryStartOffset; - entry->endOffset = this->nextEntryStartOffset; - - /* Check if we're not at the end of file */ - if (this->nextEntryStartOffset < this->rawFileSize) { - char range_init = this->readByte(this->nextEntryStartOffset); - - /* Check that we're at the beginning of the entry we - * want to parse */ - if (isRangeStart(range_init)) { - char sym_buf; - - /* Read file byte by byte seeking for the end of entry */ - for (size_t offset = this->nextEntryStartOffset + 1; - offset <= this->rawFileSize; offset++) { - sym_buf = this->readByte(offset); - - /* Build and return the entry if we're found its end */ - if (isRangeEnd(sym_buf)) { - entry->endOffset = offset + 2; - this->nextEntryStartOffset = offset + 2; - return entry; - } - } - } - } else { - return nullptr; - } - - return entry; -} - -std::unique_ptr PregParser::readEntry(KeyEntry kentry) { - std::unique_ptr appentry = std::make_unique(); - std::vector results = this->splitEntry(kentry); - gpui::IconvWrapper iwrapper("UTF-16LE", "UTF-8"); - - std::string vn = iwrapper.convert(results.at(0)); - std::string kn = iwrapper.convert(results.at(1)); - appentry->key = std::string(vn, 0, vn.length() - 1); - appentry->value = std::string(kn, 0, kn.length() - 1); - appentry->type = parse_type(results.at(2).c_str()); - appentry->size = buffer2uint32(results.at(3).c_str()); - - if (results.size() >= 5 && results.at(4).size() > 0) - { - char* dataDouble = new char[appentry->size]; - memcpy(dataDouble, &results.at(4)[0], appentry->size); - appentry->data = dataDouble; - } else { - appentry->data = nullptr; - } - - return appentry; -} - -std::unique_ptr PregParser::getNextEntry() { - auto keyEntry = this->getNextKeyEntry(); - if (keyEntry) - { - return this->readEntry(*keyEntry); - } - - return nullptr; -} - -std::string PregParser::stripSquareBraces(KeyEntry kentry) { - size_t entry_size = (kentry.endOffset - 2) - (kentry.startOffset + 2); - char *entry_buffer = new char[entry_size]; - this->polfile->seekg((kentry.startOffset + 2)); - this->polfile->read(entry_buffer, entry_size); - std::string bufstring(entry_buffer, entry_size); - delete[] entry_buffer; - return bufstring; -} - -std::vector -PregParser::splitEntry(KeyEntry kentry) { - std::string bufstring = this->stripSquareBraces(kentry); - const char *raw_buffer = bufstring.c_str(); - std::vector results; - - size_t offset = 0; - for (size_t i = 0; i <= bufstring.length(); i++) { - if (isPregEntrySeparator(raw_buffer[i]) || i == bufstring.length()) { - size_t split_length = i - offset; - std::string buf = std::string(bufstring, offset, split_length); - results.push_back(buf); - offset = i + 2; // Skip separator - i++; - } - } - - return results; -} - -} diff --git a/src/plugins/pol/pregparser.h b/src/plugins/pol/pregparser.h deleted file mode 100644 index 3b0a849..0000000 --- a/src/plugins/pol/pregparser.h +++ /dev/null @@ -1,96 +0,0 @@ -/*********************************************************************************************************************** -** -** Copyright (C) 2021 BaseALT Ltd. -** -** 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 the Free Software Foundation; either version 2 -** of the License, or (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -** -***********************************************************************************************************************/ - -#ifndef GPUI_PREG_PARSER_H -#define GPUI_PREG_PARSER_H - -#include -#include -#include -#include -#include -#include - -#include "pregdata.h" - -namespace preg { - - /*! - * \brief key_entry This structure contains offsets for PReg file pointing to '[' and ']' - * characters. It is internal to preg_parser. - */ - struct KeyEntry { - size_t startOffset; - size_t endOffset; - }; - - uint16_t buffer2uint16(const char *type_buffer); - uint32_t buffer2uint32(const char *type_buffer); - uint32_t parse_type(const char *type_buffer); - - class PregParser { - private: - std::istream* polfile = nullptr; - - std::string filePath = ""; - - size_t rawFileSize = 0; - - char header[4] { 'P', 'R', 'e', 'g' }; - - char version[4] { '\x01', '\x00', '\x00', '\x00' }; - - size_t nextEntryStartOffset = 8; - - public: - explicit PregParser(std::istream& input); - - std::unique_ptr getNextEntry(); - - protected: - void loadRegpol(); - - void readHeader(); - void readVersion(); - - void checkHeader(); - void checkVersion(); - - char readByte(size_t absFileStartOffset); - - size_t seekNextSeparator(size_t absFileStartOffset); - - std::string stripSquareBraces(KeyEntry kentry); - - std::unique_ptr getNextKeyEntry(); - - std::unique_ptr readEntry(KeyEntry kentry); - std::vector splitEntry(KeyEntry kentry); - - private: - PregParser(const PregParser&) = delete; // copy ctor - PregParser(PregParser&&) = delete; // move ctor - PregParser& operator=(const PregParser&) = delete; // copy assignment - PregParser& operator=(PregParser&&) = delete; // move assignment - }; - -} - -#endif // GPUI_PREG_PARSER_H diff --git a/src/plugins/pol/pregwriter.cpp b/src/plugins/pol/pregwriter.cpp deleted file mode 100644 index c77ebf9..0000000 --- a/src/plugins/pol/pregwriter.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/*********************************************************************************************************************** -** -** Copyright (C) 2021 BaseALT Ltd. -** -** 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 the Free Software Foundation; either version 2 -** of the License, or (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -** -***********************************************************************************************************************/ - -#include "pregwriter.h" -#include "iconvwrapper.h" - -namespace preg { - -PregWriter::PregWriter(std::ostream *initial_preg_file) { - this->preg_file = initial_preg_file; - this->preg_file->write(this->preg_magic, 4); - this->preg_file->write(this->preg_version, 4); -} - -PregWriter::~PregWriter() { -} - -void PregWriter::addEntry(PregEntry &pentry) { - char null_terminator[2]{ '\x00', '\x00' }; - char separator[2]{ ';', '\x00' }; - char range_start[2]{ '[', '\x00' }; - char range_end[2]{ ']', '\x00' }; - - gpui::IconvWrapper iw("UTF-8", "UTF-16LE"); - - std::string keyName = iw.convert(pentry.key); - std::string valueName = iw.convert(pentry.value); - - const char *key = keyName.c_str(); - size_t key_size = keyName.length() * sizeof(char); - - const char *value = valueName.c_str(); - size_t value_size = valueName.length() * sizeof(char); - - char type[2]; - type[0] = pentry.type & 0xFF; - type[1] = pentry.type >> 8; - - char size[4]; - size[0] = pentry.size & 0xFF; - size[1] = pentry.size >> 8; - size[2] = pentry.size >> 16; - size[3] = pentry.size >> 24; - - this->preg_file->write(range_start, 2); - this->preg_file->write(key, key_size); - this->preg_file->write(null_terminator, 2); - this->preg_file->write(separator, 2); - this->preg_file->write(value, value_size); - this->preg_file->write(null_terminator, 2); - this->preg_file->write(separator, 2); - this->preg_file->write(type, 2); - this->preg_file->write(null_terminator, 2); - this->preg_file->write(separator, 2); - this->preg_file->write(size, 4); - this->preg_file->write(separator, 2); - this->preg_file->write(pentry.data, pentry.size); - this->preg_file->write(range_end, 2); -} - -} diff --git a/src/plugins/pol/pregwriter.h b/src/plugins/pol/pregwriter.h deleted file mode 100644 index 7fa8e4d..0000000 --- a/src/plugins/pol/pregwriter.h +++ /dev/null @@ -1,58 +0,0 @@ -/*********************************************************************************************************************** -** -** Copyright (C) 2021 BaseALT Ltd. -** -** 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 the Free Software Foundation; either version 2 -** of the License, or (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -** -***********************************************************************************************************************/ - -#ifndef GPUI_PREG_WRITER_H -#define GPUI_PREG_WRITER_H - -#include -#include -#include -#include - -#include "pregdata.h" - -namespace preg { - -class PregWriter { -private: - std::ostream* preg_file = nullptr; - char preg_magic[4] { 'P', 'R', 'e', 'g' }; - char preg_version[4] { '\x01', '\x00', '\x00', '\x00' }; - -public: - PregWriter(std::ostream *preg_file); - ~PregWriter(); - - void addEntry(preg::PregEntry &pentry); - -private: - void preg_type2buf(uint16_t type); - -private: - PregWriter(const PregWriter&) = delete; // copy ctor - PregWriter(PregWriter&&) = delete; // move ctor - PregWriter& operator=(const PregWriter&) = delete; // copy assignment - PregWriter& operator=(PregWriter&&) = delete; // move assignment -}; - -} - -#endif // GPUI_PREG_WRITER_H - diff --git a/tests/auto/plugins/pol/CMakeLists.txt b/tests/auto/plugins/pol/CMakeLists.txt index 364d62c..6188030 100644 --- a/tests/auto/plugins/pol/CMakeLists.txt +++ b/tests/auto/plugins/pol/CMakeLists.txt @@ -1,4 +1,7 @@ qt5_wrap_cpp(MOC_SOURCES poltest.h) + +set(CMAKE_CXX_STANDARD 17) + add_executable(poltest poltest.cpp ${MOC_SOURCES}) target_link_libraries(poltest ${GPUI_LIBRARIES} pol-plugin Qt5::Core Qt5::Test) add_gpui_test(plugins.poltest poltest) diff --git a/tests/auto/plugins/pol/generatecase.h b/tests/auto/plugins/pol/generatecase.h new file mode 100644 index 0000000..e4f4e0d --- /dev/null +++ b/tests/auto/plugins/pol/generatecase.h @@ -0,0 +1,179 @@ +/* + * libparsepol - POL Registry file parser + * + * Copyright (C) 2024 BaseALT Ltd. + * Copyright (C) 2020 Korney Yakovlevich + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include + +#include "../../../../src/plugins/pol/encoding.h" +#include "../../../../src/plugins/pol/parser.h" + +std::string generateRandomKey(size_t length, std::mt19937 &gen) +{ + std::string key; + key.resize(length); + for (size_t i = 0; i < length; ++i) { + // [0x20-\x5B] | [\x5D-\x7E] + key[i] = (gen() % 0x5E) + 0x20; + key[i] = key[i] >= 0x5c ? key[i] + 1 : key[i]; + } + return key; +} + +std::string generateRandomKeypath(std::mt19937 &gen) +{ + std::string keyPath; + keyPath += generateRandomKey((gen() % 99) + 1, gen); + + while ((rand() % 5) >= 3) { + keyPath += '\\'; + keyPath += generateRandomKey((gen() % 99) + 1, gen); + } + + return keyPath; +} + +std::string generateRandomValue(std::mt19937 &gen) +{ + std::string value; + value.resize((gen() % 99) + 1); + for (size_t i = 0; i < value.size(); ++i) { + value[i] = (gen() % 0x5E) + 0x20; + } + return value; +} + +pol::PolicyRegType generateRandomType(std::mt19937 &gen) +{ + switch (rand() % 7) { + case 0: + return pol::PolicyRegType::REG_BINARY; + case 1: + return pol::PolicyRegType::REG_DWORD_LITTLE_ENDIAN; + case 2: + return pol::PolicyRegType::REG_DWORD_BIG_ENDIAN; + case 3: + return pol::PolicyRegType::REG_QWORD_LITTLE_ENDIAN; + case 4: + return pol::PolicyRegType::REG_QWORD_BIG_ENDIAN; + case 5: + return pol::PolicyRegType::REG_SZ; + case 6: + return pol::PolicyRegType::REG_MULTI_SZ; + default: + break; + } + return pol::PolicyRegType::REG_BINARY; +} + +pol::PolicyData generateRandomData(pol::PolicyRegType type, std::mt19937 &gen) +{ + iconv_t conv = iconv_open("UTF-8", "UTF-32LE"); + if (conv == pol::ICONV_ERROR_DESCRIPTOR) { + throw std::runtime_error( + "LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Encountered with the inability to create an iconv descriptor."); + } + + switch (type) { + case pol::PolicyRegType::REG_NONE: + throw std::runtime_error("LINE: " + std::to_string(__LINE__) + ", FILE: " + __FILE__ + + ", Unexpected type REG_NONE."); + case pol::PolicyRegType::REG_SZ: { + std::basic_string data; + data.resize(rand() % 100); + for (size_t i = 0; i < data.size(); ++i) { + data[i] = (gen() % 0x5E) + 0x20; + } + auto result = pol::convert(data, conv); + iconv_close(conv); + return result; + } + + case pol::PolicyRegType::REG_MULTI_SZ: { + std::vector data1; + size_t count = rand() % 100; + for (size_t i = 0; i < count; ++i) { + std::basic_string data; + data.resize((rand() % 100) + 1); + for (size_t i = 0; i < data.size(); ++i) { + data[i] = (gen() % 0x5E) + 0x20; + } + data1.push_back(pol::convert(data, conv)); + } + iconv_close(conv); + return data1; + } + + case pol::PolicyRegType::REG_BINARY: { + std::vector data; + size_t count = gen() % 100; + for (size_t i = 0; i < count; ++i) { + data.push_back((gen() % 255) + 1); + } + iconv_close(conv); + return data; + } + + case pol::PolicyRegType::REG_DWORD_LITTLE_ENDIAN: + iconv_close(conv); + return uint32_t(rand() % 10'000'000); + case pol::PolicyRegType::REG_DWORD_BIG_ENDIAN: + iconv_close(conv); + return uint32_t(rand() % 10'000'000); + case pol::PolicyRegType::REG_QWORD_LITTLE_ENDIAN: + iconv_close(conv); + return uint64_t(rand() % 10'000'000); + case pol::PolicyRegType::REG_QWORD_BIG_ENDIAN: + iconv_close(conv); + return uint64_t(rand() % 10'000'000); + default: + iconv_close(conv); + return {}; + } +} + +pol::PolicyFile generateCase(size_t seed = -1) +{ + std::mt19937 gen; + std::uniform_int_distribution dist(0, 500); + pol::PolicyFile data; + + if (seed == -1) { + std::random_device dev; + seed = dev(); + gen.seed(seed); + } else { + gen.seed(seed); + } + + // Generate case + size_t el = dist(gen); + for (size_t i = 0; i < el; i++) { + pol::PolicyInstruction instruction; + instruction.type = generateRandomType(gen); + instruction.data = generateRandomData(instruction.type, gen); + data.instructions[generateRandomKeypath(gen)][generateRandomValue(gen)] = { instruction }; + } + + return data; +} diff --git a/tests/auto/plugins/pol/poltest.cpp b/tests/auto/plugins/pol/poltest.cpp index 9f3da4a..11cd5ce 100644 --- a/tests/auto/plugins/pol/poltest.cpp +++ b/tests/auto/plugins/pol/poltest.cpp @@ -19,47 +19,120 @@ ***********************************************************************************************************************/ #include "poltest.h" - -#include "../../../../src/io/registryfile.h" -#include "../../../../src/plugins/administrative_templates/registry/registry.h" -#include "../../../../src/plugins/administrative_templates/registry/registryentry.h" -#include "../../../../src/plugins/pol/polformat.h" +#include "../../../../src/plugins/pol/binary.h" +#include "../../../../src/plugins/pol/encoding.h" +#include "../../../../src/plugins/pol/parser.h" +#include "generatecase.h" #include #include +#include const std::string dataPath = "../../../data/"; -namespace tests +namespace tests { + +void PolTest::endianness() { -void PolTest::read() -{ - gpui::PolFormat format; + uint8_t num1 = 0x12; + QCOMPARE(pol::byteswap(num1), 0x12); - std::ifstream file; + uint16_t num2 = 0x1234; + QCOMPARE(pol::byteswap(num2), 0x3412); + qDebug() << "byteswap: OK"; - file.open(dataPath + "example.pol", std::ifstream::in); + uint32_t num3 = 0x12345678; + QCOMPARE(pol::byteswap(num3), 0x78563412); + qDebug() << "byteswap: OK"; - if (file.good()) - { - std::unique_ptr registry = std::make_unique(); - - format.read(file, registry.get()); - - for (auto &entry : registry->getRegistry()->registryEntries) - { - if (entry) - { - std::cout << "Key name " << entry->key.toStdString() << std::endl; - std::cout << "Value name " << entry->value.toStdString() << std::endl; - std::cout << "Type " << entry->type << std::endl; - } - } - } - - file.close(); + uint64_t num4 = 0x123456789ABCDEF0; + QCOMPARE(pol::byteswap(num4), 0xF0DEBC9A78563412); + qDebug() << "byteswap: OK"; } +void PolTest::bufferToIntegralLe() +{ + std::stringstream buffer; + char tmp[4] = { 0x12, 0x34, 0x56, 0x78 }; + const uint32_t &num = *reinterpret_cast(&tmp[0]); + + buffer.write(tmp, 4); + buffer.seekg(0); + + uint32_t result = pol::readIntegralFromBuffer(buffer); + + QCOMPARE(result, num); +} + +void PolTest::bufferToIntegralBe() +{ + std::stringstream buffer; + char tmp[4] = { 0x12, 0x34, 0x56, 0x78 }; + const uint32_t &num = *reinterpret_cast(&tmp[0]); + uint32_t result; + + buffer.write(reinterpret_cast(&num), 4); + buffer.seekg(0); + + result = pol::readIntegralFromBuffer(buffer); + + std::reverse(tmp, tmp + 4); + QCOMPARE(result, num); +} + +void PolTest::testCase_data() +{ + QTest::addColumn("filename"); + + QTest::newRow("case1") << "case1.pol"; + QTest::newRow("case2") << "case2.pol"; +} + +void PolTest::autogenerateCases_data() +{ + QTest::addColumn("seed"); + + std::mt19937 gen; + std::random_device dev; + size_t seed = dev(); + + gen.seed(seed); + + for (size_t i = 0; i < 50; ++i) { + std::string name = std::to_string(i) + ". seed " + std::to_string(seed); + QTest::newRow(name.c_str()) << seed; + } +} + +void PolTest::testCase(QString filename) +{ + auto stdFilename = filename.toStdString(); + + std::ifstream file(dataPath + stdFilename, std::ios::in | std::ios::binary); + std::stringstream stream; + + auto parser = pol::createPregParser(); + auto pol = parser->parse(file); + + parser->write(stream, pol); + auto pol2 = parser->parse(stream); + + auto failMessage = "`" + stdFilename + "` << is rewrite failed"; + QVERIFY2(pol == pol2, failMessage.c_str()); +} + +void PolTest::autogenerateCases(size_t seed) +{ + pol::PolicyFile policyFile = generateCase(seed); + std::stringstream file; + auto parser = pol::createPregParser(); + + parser->write(file, policyFile); + file.seekg(0, std::ios::beg); + + auto test = parser->parse(file); + QCOMPARE(policyFile, test); +} } // namespace tests QTEST_MAIN(tests::PolTest) diff --git a/tests/auto/plugins/pol/poltest.h b/tests/auto/plugins/pol/poltest.h index ab2c06a..7891a2b 100644 --- a/tests/auto/plugins/pol/poltest.h +++ b/tests/auto/plugins/pol/poltest.h @@ -22,15 +22,24 @@ #define GPUI_POL_TEST_H #include +#include "../../../../src/plugins/pol/parser.h" namespace tests { - class PolTest : public QObject - { - Q_OBJECT +class PolTest : public QObject +{ + Q_OBJECT - private slots: - void read(); - }; -} +private slots: + void endianness(); + void bufferToIntegralLe(); + void bufferToIntegralBe(); + + void testCase_data(); + void autogenerateCases_data(); + + void testCase(QString filename); + void autogenerateCases(size_t seed); +}; +} // namespace tests #endif // GPUI_POL_TEST_H diff --git a/tests/data/case1.pol b/tests/data/case1.pol new file mode 100644 index 0000000..0dad1f6 Binary files /dev/null and b/tests/data/case1.pol differ diff --git a/tests/data/case2.pol b/tests/data/case2.pol new file mode 100755 index 0000000..71f965a Binary files /dev/null and b/tests/data/case2.pol differ