initial changes
This commit is contained in:
BIN
support/3rd_party/cpp-httplib-0.18.1.tar.gz
LFS
vendored
Normal file
BIN
support/3rd_party/cpp-httplib-0.18.1.tar.gz
LFS
vendored
Normal file
Binary file not shown.
1
support/3rd_party/cpp-httplib-0.18.1.tar.gz.sha256
vendored
Normal file
1
support/3rd_party/cpp-httplib-0.18.1.tar.gz.sha256
vendored
Normal file
@@ -0,0 +1 @@
|
||||
405abd8170f2a446fc8612ac635d0db5947c0d2e156e32603403a4496255ff00 *cpp-httplib-0.18.1.tar.gz
|
||||
BIN
support/3rd_party/curl-8.11.0.tar.gz
LFS
vendored
Normal file
BIN
support/3rd_party/curl-8.11.0.tar.gz
LFS
vendored
Normal file
Binary file not shown.
1
support/3rd_party/curl-8.11.0.tar.gz.sha256
vendored
Normal file
1
support/3rd_party/curl-8.11.0.tar.gz.sha256
vendored
Normal file
@@ -0,0 +1 @@
|
||||
5a231145114589491fc52da118f9c7ef8abee885d1cb1ced99c7290e9a352f07 *curl-8.11.0.tar.gz
|
||||
BIN
support/3rd_party/googletest-1.15.2.tar.gz
LFS
vendored
Normal file
BIN
support/3rd_party/googletest-1.15.2.tar.gz
LFS
vendored
Normal file
Binary file not shown.
1
support/3rd_party/googletest-1.15.2.tar.gz.sha256
vendored
Normal file
1
support/3rd_party/googletest-1.15.2.tar.gz.sha256
vendored
Normal file
@@ -0,0 +1 @@
|
||||
7b42b4d6ed48810c5362c265a17faebe90dc2373c885e5216439d37927f02926 googletest-1.15.2.tar.gz
|
||||
BIN
support/3rd_party/json-3.11.3.tar.gz
LFS
vendored
Normal file
BIN
support/3rd_party/json-3.11.3.tar.gz
LFS
vendored
Normal file
Binary file not shown.
1
support/3rd_party/json-3.11.3.tar.gz.sha256
vendored
Normal file
1
support/3rd_party/json-3.11.3.tar.gz.sha256
vendored
Normal file
@@ -0,0 +1 @@
|
||||
0d8ef5af7f9794e3263480193c491549b2ba6cc74bb018906202ada498a79406 json-3.11.3.tar.gz
|
||||
BIN
support/3rd_party/mingw64/binutils-2.43.tar.xz
LFS
vendored
Normal file
BIN
support/3rd_party/mingw64/binutils-2.43.tar.xz
LFS
vendored
Normal file
Binary file not shown.
1
support/3rd_party/mingw64/binutils-2.43.tar.xz.sha256
vendored
Normal file
1
support/3rd_party/mingw64/binutils-2.43.tar.xz.sha256
vendored
Normal file
@@ -0,0 +1 @@
|
||||
b53606f443ac8f01d1d5fc9c39497f2af322d99e14cea5c0b4b124d630379365 binutils-2.43.tar.xz
|
||||
BIN
support/3rd_party/mingw64/expat-2.6.4.tar.gz
LFS
vendored
Normal file
BIN
support/3rd_party/mingw64/expat-2.6.4.tar.gz
LFS
vendored
Normal file
Binary file not shown.
1
support/3rd_party/mingw64/expat-2.6.4.tar.gz.sha256
vendored
Normal file
1
support/3rd_party/mingw64/expat-2.6.4.tar.gz.sha256
vendored
Normal file
@@ -0,0 +1 @@
|
||||
372b18f6527d162fa9658f1c74d22a37429b82d822f5a1e1fc7e00f6045a06a2 *expat-2.6.4.tar.gz
|
||||
BIN
support/3rd_party/mingw64/gcc-14.2.0.tar.gz
LFS
vendored
Normal file
BIN
support/3rd_party/mingw64/gcc-14.2.0.tar.gz
LFS
vendored
Normal file
Binary file not shown.
1
support/3rd_party/mingw64/gcc-14.2.0.tar.gz.sha256
vendored
Normal file
1
support/3rd_party/mingw64/gcc-14.2.0.tar.gz.sha256
vendored
Normal file
@@ -0,0 +1 @@
|
||||
7d376d445f93126dc545e2c0086d0f647c3094aae081cdb78f42ce2bc25e7293 gcc-14.2.0.tar.gz
|
||||
BIN
support/3rd_party/mingw64/icu-release-76-1.tar.gz
LFS
vendored
Normal file
BIN
support/3rd_party/mingw64/icu-release-76-1.tar.gz
LFS
vendored
Normal file
Binary file not shown.
1
support/3rd_party/mingw64/icu-release-76-1.tar.gz.sha256
vendored
Normal file
1
support/3rd_party/mingw64/icu-release-76-1.tar.gz.sha256
vendored
Normal file
@@ -0,0 +1 @@
|
||||
a2c443404f00098e9e90acf29dc318e049d2dc78d9ae5f46efb261934a730ce2 icu-release-76-1.tar.gz
|
||||
BIN
support/3rd_party/mingw64/mingw-w64-v12.0.0.tar.bz2
vendored
Normal file
BIN
support/3rd_party/mingw64/mingw-w64-v12.0.0.tar.bz2
vendored
Normal file
Binary file not shown.
1
support/3rd_party/mingw64/mingw-w64-v12.0.0.tar.bz2.sha256
vendored
Normal file
1
support/3rd_party/mingw64/mingw-w64-v12.0.0.tar.bz2.sha256
vendored
Normal file
@@ -0,0 +1 @@
|
||||
cc41898aac4b6e8dd5cffd7331b9d9515b912df4420a3a612b5ea2955bbeed2f mingw-w64-v12.0.0.tar.bz2
|
||||
BIN
support/3rd_party/mingw64/pkg-config-0.29.2.tar.gz
LFS
vendored
Normal file
BIN
support/3rd_party/mingw64/pkg-config-0.29.2.tar.gz
LFS
vendored
Normal file
Binary file not shown.
1
support/3rd_party/mingw64/pkg-config-0.29.2.tar.gz.sha256
vendored
Normal file
1
support/3rd_party/mingw64/pkg-config-0.29.2.tar.gz.sha256
vendored
Normal file
@@ -0,0 +1 @@
|
||||
6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591 pkg-config-0.29.2.tar.gz
|
||||
BIN
support/3rd_party/mingw64/zlib-1.3.1.tar.gz
LFS
vendored
Normal file
BIN
support/3rd_party/mingw64/zlib-1.3.1.tar.gz
LFS
vendored
Normal file
Binary file not shown.
1
support/3rd_party/mingw64/zlib-1.3.1.tar.gz.sha256
vendored
Normal file
1
support/3rd_party/mingw64/zlib-1.3.1.tar.gz.sha256
vendored
Normal file
@@ -0,0 +1 @@
|
||||
17e88863f3600672ab49182f217281b6fc4d3c762bde361935e436a95214d05c zlib-1.3.1.tar.gz
|
||||
BIN
support/3rd_party/openssl-3.4.0.tar.gz
LFS
vendored
Normal file
BIN
support/3rd_party/openssl-3.4.0.tar.gz
LFS
vendored
Normal file
Binary file not shown.
1
support/3rd_party/openssl-3.4.0.tar.gz.sha256
vendored
Normal file
1
support/3rd_party/openssl-3.4.0.tar.gz.sha256
vendored
Normal file
@@ -0,0 +1 @@
|
||||
e15dda82fe2fe8139dc2ac21a36d4ca01d5313c75f99f46c4e8a27709b7294bf *openssl-3.4.0.tar.gz
|
||||
BIN
support/3rd_party/rocksdb-9.7.4.tar.gz
LFS
vendored
Normal file
BIN
support/3rd_party/rocksdb-9.7.4.tar.gz
LFS
vendored
Normal file
Binary file not shown.
1
support/3rd_party/rocksdb-9.7.4.tar.gz.sha256
vendored
Normal file
1
support/3rd_party/rocksdb-9.7.4.tar.gz.sha256
vendored
Normal file
@@ -0,0 +1 @@
|
||||
9b810c81731835fda0d4bbdb51d3199d901fa4395733ab63752d297da84c5a47 *rocksdb-9.7.4.tar.gz
|
||||
BIN
support/3rd_party/spdlog-1.15.0.tar.gz
LFS
vendored
Normal file
BIN
support/3rd_party/spdlog-1.15.0.tar.gz
LFS
vendored
Normal file
Binary file not shown.
1
support/3rd_party/spdlog-1.15.0.tar.gz.sha256
vendored
Normal file
1
support/3rd_party/spdlog-1.15.0.tar.gz.sha256
vendored
Normal file
@@ -0,0 +1 @@
|
||||
9962648c9b4f1a7bbc76fd8d9172555bad1871fdb14ff4f842ef87949682caa5 *spdlog-1.15.0.tar.gz
|
||||
4509
support/include/backward.hpp
Normal file
4509
support/include/backward.hpp
Normal file
File diff suppressed because it is too large
Load Diff
59
support/include/utils/all.hpp
Normal file
59
support/include/utils/all.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_ALL_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_ALL_HPP_
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
#include "utils/base64.hpp"
|
||||
#include "utils/collection.hpp"
|
||||
#if defined(_WIN32)
|
||||
#include "utils/com_init_wrapper.hpp"
|
||||
#endif // defined(_WIN32)
|
||||
#include "utils/common.hpp"
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
#include "utils/db/sqlite/db_common.hpp"
|
||||
#include "utils/db/sqlite/db_delete.hpp"
|
||||
#include "utils/db/sqlite/db_insert.hpp"
|
||||
#include "utils/db/sqlite/db_select.hpp"
|
||||
#include "utils/db/sqlite/db_update.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
#include "utils/encrypting_reader.hpp"
|
||||
#include "utils/encryption.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
#include "utils/hash.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
#include "utils/path.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "utils/time.hpp"
|
||||
#if !defined(_WIN32)
|
||||
#include "utils/unix.hpp"
|
||||
#endif // !defined(_WIN32)
|
||||
#if defined(_WIN32)
|
||||
#include "utils/windows.hpp"
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
#endif // MONITARR_INCLUDE_UTILS_ALL_HPP_
|
||||
174
support/include/utils/base64.hpp
Normal file
174
support/include/utils/base64.hpp
Normal file
@@ -0,0 +1,174 @@
|
||||
// NOLINTBEGIN
|
||||
#ifndef _MACARON_BASE64_H_
|
||||
#define _MACARON_BASE64_H_
|
||||
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2016 tomykaira
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunknown-warning-option"
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#pragma GCC diagnostic ignored "-Wuseless-cast"
|
||||
#endif
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace macaron::Base64 {
|
||||
static std::string Encode(const unsigned char *data, std::size_t len) {
|
||||
static constexpr std::array<unsigned char, 64U> sEncodingTable{
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
||||
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
|
||||
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
|
||||
};
|
||||
|
||||
auto in_len{len};
|
||||
std::string ret;
|
||||
if (in_len > 0) {
|
||||
std::size_t out_len{4U * ((in_len + 2U) / 3U)};
|
||||
ret = std::string(out_len, '\0');
|
||||
std::size_t i;
|
||||
auto *p = reinterpret_cast<unsigned char *>(ret.data());
|
||||
|
||||
for (i = 0U; i < in_len - 2U; i += 3U) {
|
||||
*p++ = sEncodingTable[(data[i] >> 2U) & 0x3F];
|
||||
*p++ = sEncodingTable[((data[i] & 0x3) << 4U) |
|
||||
((int)(data[i + 1U] & 0xF0) >> 4U)];
|
||||
*p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) |
|
||||
((int)(data[i + 2U] & 0xC0) >> 6U)];
|
||||
*p++ = sEncodingTable[data[i + 2U] & 0x3F];
|
||||
}
|
||||
if (i < in_len) {
|
||||
*p++ = sEncodingTable[(data[i] >> 2U) & 0x3F];
|
||||
if (i == (in_len - 1U)) {
|
||||
*p++ = sEncodingTable[((data[i] & 0x3) << 4U)];
|
||||
*p++ = '=';
|
||||
} else {
|
||||
*p++ = sEncodingTable[((data[i] & 0x3) << 4U) |
|
||||
((int)(data[i + 1U] & 0xF0) >> 4U)];
|
||||
*p++ = sEncodingTable[((data[i + 1U] & 0xF) << 2U)];
|
||||
}
|
||||
*p++ = '=';
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
[[maybe_unused]] static std::string Encode(std::string_view data) {
|
||||
return Encode(reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size());
|
||||
}
|
||||
|
||||
[[maybe_unused]] static std::vector<unsigned char>
|
||||
Decode(std::string_view input) {
|
||||
static constexpr std::array<unsigned char, 256> kDecodingTable{
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 52, 53, 54, 55, 56, 57,
|
||||
58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 64, 0, 1, 2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 64, 64, 64, 64, 64, 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
|
||||
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64,
|
||||
};
|
||||
|
||||
std::vector<unsigned char> out;
|
||||
if (not input.empty()) {
|
||||
auto in_len{input.size()};
|
||||
if (in_len % 4U != 0U) {
|
||||
throw std::runtime_error("Input data size is not a multiple of 4");
|
||||
}
|
||||
|
||||
std::size_t out_len{in_len / 4U * 3U};
|
||||
if (input[in_len - 1U] == '=') {
|
||||
out_len--;
|
||||
}
|
||||
if (input[in_len - 2U] == '=') {
|
||||
out_len--;
|
||||
}
|
||||
|
||||
out.resize(out_len);
|
||||
|
||||
for (std::size_t i = 0U, j = 0U; i < in_len;) {
|
||||
std::uint32_t a =
|
||||
input.at(i) == '='
|
||||
? 0U & i++
|
||||
: kDecodingTable[static_cast<unsigned char>(input.at(i++))];
|
||||
std::uint32_t b =
|
||||
input.at(i) == '='
|
||||
? 0U & i++
|
||||
: kDecodingTable[static_cast<unsigned char>(input.at(i++))];
|
||||
std::uint32_t c =
|
||||
input.at(i) == '='
|
||||
? 0U & i++
|
||||
: kDecodingTable[static_cast<unsigned char>(input.at(i++))];
|
||||
std::uint32_t d =
|
||||
input.at(i) == '='
|
||||
? 0U & i++
|
||||
: kDecodingTable[static_cast<unsigned char>(input.at(i++))];
|
||||
|
||||
std::uint32_t triple =
|
||||
(a << 3U * 6U) + (b << 2U * 6U) + (c << 1U * 6U) + (d << 0U * 6U);
|
||||
|
||||
if (j < out_len)
|
||||
out[j++] = (triple >> 2U * 8U) & 0xFF;
|
||||
if (j < out_len)
|
||||
out[j++] = (triple >> 1U * 8U) & 0xFF;
|
||||
if (j < out_len)
|
||||
out[j++] = (triple >> 0U * 8U) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
} // namespace macaron::Base64
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif /* _MACARON_BASE64_H_ */
|
||||
|
||||
// NOLINTEND
|
||||
178
support/include/utils/collection.hpp
Normal file
178
support/include/utils/collection.hpp
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_COLLECTION_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_COLLECTION_HPP_
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace monitarr::utils::collection {
|
||||
template <typename col_t>
|
||||
[[nodiscard]] inline auto excludes(const col_t &collection,
|
||||
const typename col_t::value_type &val)
|
||||
-> bool;
|
||||
|
||||
template <typename col_t>
|
||||
[[nodiscard]] inline auto includes(const col_t &collection,
|
||||
const typename col_t::value_type &val)
|
||||
-> bool;
|
||||
|
||||
template <typename val_t>
|
||||
[[nodiscard]] inline auto from_hex_string(std::string_view str, val_t &val)
|
||||
-> bool;
|
||||
|
||||
template <typename val_t>
|
||||
[[nodiscard]] inline auto from_hex_string(std::wstring_view str, val_t &val)
|
||||
-> bool;
|
||||
template <typename col_t>
|
||||
inline auto remove_element(col_t &collection,
|
||||
const typename col_t::value_type &value) -> col_t &;
|
||||
|
||||
template <typename col_t>
|
||||
[[nodiscard]] inline auto to_hex_string(const col_t &collection) -> std::string;
|
||||
|
||||
template <typename col_t>
|
||||
[[nodiscard]] inline auto to_hex_wstring(const col_t &collection)
|
||||
-> std::wstring;
|
||||
|
||||
template <typename col_t>
|
||||
inline auto excludes(const col_t &collection,
|
||||
const typename col_t::value_type &val) -> bool {
|
||||
return std::find(collection.begin(), collection.end(), val) ==
|
||||
collection.end();
|
||||
}
|
||||
|
||||
template <typename col_t>
|
||||
inline auto includes(const col_t &collection,
|
||||
const typename col_t::value_type &val) -> bool {
|
||||
return std::find(collection.begin(), collection.end(), val) !=
|
||||
collection.end();
|
||||
}
|
||||
|
||||
template <typename val_t>
|
||||
[[nodiscard]] inline auto from_hex_string_t(std::string_view str, val_t &val)
|
||||
-> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
static constexpr const auto base16{16};
|
||||
|
||||
try {
|
||||
val.clear();
|
||||
|
||||
std::string fmt_val{str};
|
||||
utils::string::trim(fmt_val);
|
||||
if (fmt_val.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
fmt_val = utils::string::to_lower(fmt_val);
|
||||
if (utils::string::begins_with(fmt_val, "0x")) {
|
||||
fmt_val = fmt_val.substr(2U);
|
||||
}
|
||||
|
||||
if (fmt_val.empty()) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"hex string is invalid",
|
||||
str,
|
||||
});
|
||||
}
|
||||
|
||||
if (fmt_val.length() % 2U) {
|
||||
fmt_val = '0' + fmt_val;
|
||||
}
|
||||
|
||||
auto iter = std::find_if_not(
|
||||
fmt_val.begin(), fmt_val.end(), [](auto cur_char) -> bool {
|
||||
auto check = static_cast<std::uint32_t>(cur_char);
|
||||
return ((check >= 48U && check <= 57U) ||
|
||||
(check >= 97U && check <= 102U));
|
||||
});
|
||||
if (iter != fmt_val.end()) {
|
||||
auto invalid_idx{std::distance(fmt_val.begin(), iter)};
|
||||
throw std::range_error(utils::error::create_error_message({
|
||||
function_name,
|
||||
"invalid character in hex string",
|
||||
std::to_string(invalid_idx),
|
||||
std::string(1U, str.at(invalid_idx)),
|
||||
str,
|
||||
}));
|
||||
}
|
||||
|
||||
val.resize(fmt_val.length() / 2U);
|
||||
for (std::size_t idx = 0U; idx < fmt_val.length(); idx += 2U) {
|
||||
val.at(idx / 2U) = static_cast<typename val_t::value_type>(
|
||||
std::strtoul(fmt_val.substr(idx, 2U).c_str(), nullptr, base16));
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
val.clear();
|
||||
return false;
|
||||
}
|
||||
template <typename val_t>
|
||||
inline auto from_hex_string(std::string_view str, val_t &val) -> bool {
|
||||
return from_hex_string_t<val_t>(str, val);
|
||||
}
|
||||
|
||||
template <typename val_t>
|
||||
inline auto from_hex_string(std::wstring_view str, val_t &val) -> bool {
|
||||
return from_hex_string_t<val_t>(utils::string::to_utf8(str), val);
|
||||
}
|
||||
|
||||
template <typename col_t>
|
||||
inline auto remove_element(col_t &collection,
|
||||
const typename col_t::value_type &value) -> col_t & {
|
||||
collection.erase(std::remove(collection.begin(), collection.end(), value),
|
||||
collection.end());
|
||||
return collection;
|
||||
}
|
||||
|
||||
template <typename col_t>
|
||||
inline auto to_hex_string(const col_t &collection) -> std::string {
|
||||
static_assert(sizeof(typename col_t::value_type) == 1U,
|
||||
"value_type must be 1 byte in size");
|
||||
static constexpr const auto mask = 0xFF;
|
||||
|
||||
std::stringstream stream{};
|
||||
for (auto &&val : collection) {
|
||||
stream << std::setfill('0') << std::setw(2) << std::hex
|
||||
<< (static_cast<std::uint32_t>(val) & mask);
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
template <typename col_t>
|
||||
inline auto to_hex_wstring(const col_t &collection) -> std::wstring {
|
||||
return utils::string::from_utf8(to_hex_string<col_t>(collection));
|
||||
}
|
||||
} // namespace monitarr::utils::collection
|
||||
|
||||
#endif // MONITARR_INCLUDE_UTILS_COLLECTION_HPP_
|
||||
54
support/include/utils/com_init_wrapper.hpp
Normal file
54
support/include/utils/com_init_wrapper.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_COM_INIT_WRAPPER_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_COM_INIT_WRAPPER_HPP_
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
namespace monitarr::utils {
|
||||
struct com_init_wrapper final {
|
||||
com_init_wrapper()
|
||||
: initialized_(
|
||||
SUCCEEDED(::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED))) {}
|
||||
|
||||
com_init_wrapper(const com_init_wrapper &) = delete;
|
||||
com_init_wrapper(com_init_wrapper &&) = delete;
|
||||
|
||||
~com_init_wrapper() {
|
||||
if (initialized_) {
|
||||
::CoUninitialize();
|
||||
}
|
||||
}
|
||||
|
||||
auto operator=(const com_init_wrapper &) -> com_init_wrapper & = delete;
|
||||
auto operator=(com_init_wrapper &&) -> com_init_wrapper & = delete;
|
||||
|
||||
[[nodiscard]] auto is_initialized() const -> bool { return initialized_; }
|
||||
|
||||
private:
|
||||
BOOL initialized_;
|
||||
};
|
||||
} // namespace monitarr::utils
|
||||
|
||||
#endif // defined(_WIN32)
|
||||
#endif // MONITARR_INCLUDE_UTILS_COM_INIT_WRAPPER_HPP_
|
||||
145
support/include/utils/common.hpp
Normal file
145
support/include/utils/common.hpp
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_COMMON_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_COMMON_HPP_
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
namespace monitarr::utils {
|
||||
struct result final {
|
||||
std::string function_name;
|
||||
bool ok{true};
|
||||
std::string reason{"success"};
|
||||
|
||||
[[nodiscard]] operator bool() const { return ok; }
|
||||
};
|
||||
|
||||
using retryable_action_t = std::function<bool()>;
|
||||
|
||||
[[nodiscard]] inline constexpr auto
|
||||
calculate_read_size(std::uint64_t total_size, std::size_t read_size,
|
||||
std::uint64_t offset) -> std::size_t {
|
||||
return static_cast<std::size_t>(
|
||||
((offset + read_size) > total_size)
|
||||
? ((offset < total_size) ? total_size - offset : 0U)
|
||||
: read_size);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto compare_version_strings(std::string version1,
|
||||
std::string version2)
|
||||
-> std::int32_t;
|
||||
|
||||
[[nodiscard]] auto compare_version_strings(std::wstring_view version1,
|
||||
std::wstring_view version2)
|
||||
-> std::int32_t;
|
||||
|
||||
#if defined(PROJECT_ENABLE_STDUUID)
|
||||
[[nodiscard]] auto create_uuid_string() -> std::string;
|
||||
|
||||
[[nodiscard]] auto create_uuid_wstring() -> std::wstring;
|
||||
#endif // defined(PROJECT_ENABLE_STDUUID)
|
||||
|
||||
template <typename result_t, typename data_t>
|
||||
[[nodiscard]] inline constexpr auto divide_with_ceiling(result_t numerator,
|
||||
data_t denominator)
|
||||
-> result_t;
|
||||
|
||||
template <typename data_t>
|
||||
[[nodiscard]] inline auto generate_random_between(data_t begin, data_t end)
|
||||
-> data_t;
|
||||
|
||||
[[nodiscard]] auto generate_random_string(std::size_t length) -> std::string;
|
||||
|
||||
[[nodiscard]] auto generate_random_wstring(std::size_t length) -> std::wstring;
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
template <typename data_t>
|
||||
[[nodiscard]] inline auto generate_secure_random() -> data_t;
|
||||
|
||||
template <typename data_t>
|
||||
[[nodiscard]] inline auto generate_secure_random(std::size_t size) -> data_t;
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
[[nodiscard]] auto get_environment_variable(std::string_view variable)
|
||||
-> std::string;
|
||||
|
||||
[[nodiscard]] auto get_environment_variable(std::wstring_view variable)
|
||||
-> std::wstring;
|
||||
|
||||
#if defined(PROJECT_ENABLE_BOOST)
|
||||
[[nodiscard]] auto get_next_available_port(std::uint16_t first_port,
|
||||
std::uint16_t &available_port)
|
||||
-> bool;
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
[[nodiscard]] auto retry_action(
|
||||
retryable_action_t action, std::size_t retry_count = 200U,
|
||||
std::chrono::milliseconds retry_wait = std::chrono::milliseconds(10))
|
||||
-> bool;
|
||||
|
||||
template <typename result_t, typename data_t>
|
||||
inline constexpr auto divide_with_ceiling(result_t numerator,
|
||||
data_t denominator) -> result_t {
|
||||
static_assert(std::is_integral_v<std::remove_cv_t<data_t>>,
|
||||
"denominator must be an integral type");
|
||||
|
||||
return denominator == 0
|
||||
? 0
|
||||
: (numerator / denominator) + (numerator % denominator != 0);
|
||||
}
|
||||
|
||||
template <typename data_t>
|
||||
inline auto generate_random_between(data_t begin, data_t end) -> data_t {
|
||||
static_assert(std::is_integral_v<std::remove_cv_t<data_t>>,
|
||||
"data_t must be an integral type");
|
||||
if (end <= begin) {
|
||||
throw std::range_error("end must be greater than begin");
|
||||
}
|
||||
|
||||
thread_local std::mt19937 gen(
|
||||
static_cast<unsigned long>(std::time(nullptr) ^ std::random_device{}()));
|
||||
std::uniform_int_distribution<data_t> dis(begin, end);
|
||||
return dis(gen);
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
template <typename data_t> inline auto generate_secure_random() -> data_t {
|
||||
static_assert(!is_collection<std::decay_t<data_t>>::value,
|
||||
"data_t is a vector or collection");
|
||||
data_t ret{};
|
||||
randombytes_buf(&ret, sizeof(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename data_t>
|
||||
inline auto generate_secure_random(std::size_t size) -> data_t {
|
||||
static_assert(is_collection<std::decay_t<data_t>>::value,
|
||||
"data_t is not a vector or collection");
|
||||
data_t ret;
|
||||
ret.resize(size);
|
||||
randombytes_buf(ret.data(), ret.size() * sizeof(typename data_t::value_type));
|
||||
return ret;
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
} // namespace monitarr::utils
|
||||
|
||||
#endif // MONITARR_INCLUDE_UTILS_COMMON_HPP_
|
||||
482
support/include/utils/config.hpp
Normal file
482
support/include/utils/config.hpp
Normal file
@@ -0,0 +1,482 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_CONFIG_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_CONFIG_HPP_
|
||||
|
||||
#define NOMINMAX
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define WINVER 0x0602
|
||||
#define _WIN32_WINNT WINVER
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include <windows.h>
|
||||
#if defined(PROJECT_ENABLE_WINFSP)
|
||||
#include <sddl.h>
|
||||
#endif // defined(PROJECT_ENABLE_WINFSP)
|
||||
|
||||
#include <direct.h>
|
||||
#if !defined(__cplusplus)
|
||||
#include <errno.h>
|
||||
#endif // !defined(__cplusplus)
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <objbase.h>
|
||||
#include <psapi.h>
|
||||
#include <rpc.h>
|
||||
#include <share.h>
|
||||
#include <shellapi.h>
|
||||
#include <shlobj.h>
|
||||
#include <shlwapi.h>
|
||||
#if !defined(__cplusplus)
|
||||
#include <stdio.h>
|
||||
#endif // !defined(__cplusplus)
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#if !defined(__cplusplus)
|
||||
#include <time.h>
|
||||
#endif // !defined(__cplusplus)
|
||||
#else // !defined(_WIN32)
|
||||
#include <arpa/inet.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <grp.h>
|
||||
#include <libgen.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/socket.h>
|
||||
#if defined(__LFS64__)
|
||||
#include <sys/stat64.h>
|
||||
#else // !defined(__LFS64__)
|
||||
#include <sys/stat.h>
|
||||
#endif // defined(__LFS64__)
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/statfs.h>
|
||||
#endif // defined(HAS_SETXATTR)
|
||||
|
||||
#if defined(HAS_SETXATTR)
|
||||
#include <sys/types.h>
|
||||
#include <sys/xattr.h>
|
||||
#endif // defined(HAS_SETXATTR)
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <libproc.h>
|
||||
#include <sys/attr.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/vnode.h>
|
||||
#endif // defined(__APPLE__)
|
||||
|
||||
#include <unistd.h>
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
#if defined(HAS_WORDEXP_H)
|
||||
#include <wordexp.h>
|
||||
#endif // defined(HAS_WORDEXP_H)
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <ciso646>
|
||||
#include <climits>
|
||||
#include <codecvt>
|
||||
#include <condition_variable>
|
||||
#include <csignal>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <deque>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <locale>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <queue>
|
||||
#include <random>
|
||||
#include <ranges>
|
||||
#include <regex>
|
||||
#include <span>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#endif // defined(__cplusplus)
|
||||
|
||||
#if defined(PROJECT_ENABLE_CURL)
|
||||
#include "curl/curl.h"
|
||||
#include "curl/multi.h"
|
||||
#endif // defined(PROJECT_ENABLE_CURL)
|
||||
|
||||
#if defined(PROJECT_ENABLE_FUSE)
|
||||
#if FUSE_USE_VERSION >= 30
|
||||
#include <fuse.h>
|
||||
#include <fuse_lowlevel.h>
|
||||
#else
|
||||
#include <fuse/fuse.h>
|
||||
#endif
|
||||
#endif // defined(PROJECT_ENABLE_FUSE)
|
||||
|
||||
#if defined(PROJECT_ENABLE_FZF)
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif // defined(__cplusplus)
|
||||
#include "fzf.h"
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif // defined(__cplusplus)
|
||||
#endif // defined(PROJECT_ENABLE_FZF)
|
||||
|
||||
#if defined(PROJECT_ENABLE_OPENSSL)
|
||||
#include "openssl/ssl.h"
|
||||
#endif // defined(PROJECT_ENABLE_OPENSSL)
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBDSM)
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif // defined(__cplusplus)
|
||||
#include "bdsm/bdsm.h"
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
|
||||
struct netbios_ns_deleter final {
|
||||
void operator()(netbios_ns *ns) const {
|
||||
if (ns != nullptr) {
|
||||
netbios_ns_destroy(ns);
|
||||
}
|
||||
}
|
||||
};
|
||||
using netbios_ns_t = std::unique_ptr<netbios_ns, netbios_ns_deleter>;
|
||||
|
||||
inline const auto smb_session_deleter = [](smb_session *session) {
|
||||
if (session != nullptr) {
|
||||
smb_session_destroy(session);
|
||||
}
|
||||
};
|
||||
using smb_session_t = std::shared_ptr<smb_session>;
|
||||
|
||||
struct smb_stat_deleter final {
|
||||
void operator()(smb_stat st) const {
|
||||
if (st != nullptr) {
|
||||
smb_stat_destroy(st);
|
||||
}
|
||||
}
|
||||
};
|
||||
using smb_stat_t = std::unique_ptr<smb_file, smb_stat_deleter>;
|
||||
|
||||
struct smb_stat_list_deleter final {
|
||||
void operator()(smb_file *list) const {
|
||||
if (list != nullptr) {
|
||||
smb_stat_list_destroy(list);
|
||||
}
|
||||
}
|
||||
};
|
||||
using smb_stat_list_t = std::unique_ptr<smb_file, smb_stat_list_deleter>;
|
||||
#endif // defined(__cplusplus)
|
||||
#endif // defined(PROJECT_ENABLE_LIBDSM)
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBEVENT)
|
||||
#include "event2/buffer.h"
|
||||
#include "event2/bufferevent.h"
|
||||
#include "event2/listener.h"
|
||||
#include "event2/thread.h"
|
||||
#include "event2/util.h"
|
||||
#endif // defined(PROJECT_ENABLE_LIBEVENT)
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
#include "sodium.h"
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
#if defined(PROJECT_ENABLE_SDL)
|
||||
#include "SDL.h"
|
||||
#include "SDL_gamecontroller.h"
|
||||
#include "SDL_joystick.h"
|
||||
#endif // defined(PROJECT_ENABLE_SDL)
|
||||
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
#include "sqlite3.h"
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#if defined(PROJECT_ENABLE_VLC)
|
||||
#include <vlc/vlc.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
[[nodiscard]] inline auto get_libvlc_error_msg() -> std::string {
|
||||
const auto *msg = libvlc_errmsg();
|
||||
return msg == nullptr ? "none" : msg;
|
||||
}
|
||||
|
||||
struct vlc_deleter final {
|
||||
void operator()(libvlc_instance_t *inst) const {
|
||||
if (inst != nullptr) {
|
||||
libvlc_release(inst);
|
||||
}
|
||||
}
|
||||
};
|
||||
using vlc_t = std::unique_ptr<libvlc_instance_t, vlc_deleter>;
|
||||
|
||||
struct vlc_media_deleter final {
|
||||
void operator()(libvlc_media_t *media) const {
|
||||
if (media != nullptr) {
|
||||
libvlc_media_release(media);
|
||||
}
|
||||
}
|
||||
};
|
||||
using vlc_media_t = std::unique_ptr<libvlc_media_t, vlc_media_deleter>;
|
||||
|
||||
struct vlc_media_list_deleter final {
|
||||
void operator()(libvlc_media_list_t *media_list) const {
|
||||
if (media_list != nullptr) {
|
||||
libvlc_media_list_release(media_list);
|
||||
}
|
||||
}
|
||||
};
|
||||
using vlc_media_list_t =
|
||||
std::unique_ptr<libvlc_media_list_t, vlc_media_list_deleter>;
|
||||
|
||||
struct vlc_string_deleter final {
|
||||
void operator()(char *str) const {
|
||||
if (str != nullptr) {
|
||||
libvlc_free(str);
|
||||
}
|
||||
}
|
||||
};
|
||||
using vlc_string_t = std::unique_ptr<char, vlc_string_deleter>;
|
||||
#endif // defined(__cplusplus)
|
||||
#endif // defined(PROJECT_ENABLE_VLC)
|
||||
|
||||
#if !defined(fstat64)
|
||||
#define fstat64 fstat
|
||||
#endif // !defined(fstat64)
|
||||
|
||||
#if !defined(pread64)
|
||||
#define pread64 pread
|
||||
#endif // !defined(pread64)
|
||||
|
||||
#if !defined(pwrite64)
|
||||
#define pwrite64 pwrite
|
||||
#endif // !defined(pwrite64)
|
||||
|
||||
#if !defined(stat64)
|
||||
#define stat64 stat
|
||||
#endif // !defined(stat64)
|
||||
|
||||
#if !defined(statfs64)
|
||||
#define statfs64 statfs
|
||||
#endif // !defined(statfs64)
|
||||
|
||||
#if !defined(off64_t)
|
||||
#define off64_t std::size_t
|
||||
#endif // !defined(off64_t)
|
||||
|
||||
#if !defined(__off64_t)
|
||||
#define __off64_t off64_t
|
||||
#endif // !defined(__off64_t)
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#if defined(PROJECT_ENABLE_BOOST)
|
||||
#include "boost/archive/text_iarchive.hpp"
|
||||
#include "boost/archive/text_oarchive.hpp"
|
||||
#include "boost/asio.hpp"
|
||||
#include "boost/asio/io_context.hpp"
|
||||
#include "boost/bind/bind.hpp"
|
||||
#include "boost/dynamic_bitset.hpp"
|
||||
#include "boost/dynamic_bitset/serialization.hpp"
|
||||
#include "boost/endian/conversion.hpp"
|
||||
#include "boost/integer.hpp"
|
||||
#include "boost/interprocess/sync/named_mutex.hpp"
|
||||
#include "boost/interprocess/sync/scoped_lock.hpp"
|
||||
#include "boost/multiprecision/cpp_dec_float.hpp"
|
||||
#include "boost/multiprecision/cpp_int.hpp"
|
||||
#include "boost/serialization/vector.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
#if defined(PROJECT_ENABLE_CLI11)
|
||||
#if defined(PROJECT_IS_MINGW) && !defined(PROJECT_IS_MINGW_UNIX)
|
||||
#include "CLI/CLI.hpp"
|
||||
#else // !defined(PROJECT_IS_MINGW) || defined(PROJECT_IS_MINGW_UNIX)
|
||||
#include "CLI11.hpp"
|
||||
#endif // defined(PROJECT_IS_MINGW) && !defined(PROJECT_IS_MINGW_UNIX)
|
||||
#endif // defined(PROJECT_ENABLE_CLI11)
|
||||
|
||||
#if defined(PROJECT_ENABLE_CPP_HTTPLIB)
|
||||
#include "httplib.h"
|
||||
#endif // defined(PROJECT_ENABLE_JSON)
|
||||
|
||||
#if defined(PROJECT_ENABLE_DTL)
|
||||
#include "dtl/dtl.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_DTL)
|
||||
|
||||
#if defined(PROJECT_ENABLE_JSON)
|
||||
#include "json.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_JSON)
|
||||
|
||||
#if defined(PROJECT_ENABLE_NANA)
|
||||
#include "nana/gui.hpp"
|
||||
#include "nana/gui/timer.hpp"
|
||||
#include "nana/gui/widgets/button.hpp"
|
||||
#include "nana/gui/widgets/combox.hpp"
|
||||
#include "nana/gui/widgets/group.hpp"
|
||||
#include "nana/gui/widgets/label.hpp"
|
||||
#include "nana/gui/widgets/panel.hpp"
|
||||
#include "nana/gui/widgets/picture.hpp"
|
||||
#include "nana/gui/widgets/tabbar.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_NANA)
|
||||
|
||||
#if defined(PROJECT_ENABLE_PUGIXML)
|
||||
#include "pugixml.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_PUGIXML)
|
||||
|
||||
#if defined(PROJECT_ENABLE_ROCKSDB)
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/utilities/transaction_db.h"
|
||||
#endif // defined(PROJECT_ENABLE_ROCKSDB)
|
||||
|
||||
#if defined(PROJECT_ENABLE_SFML)
|
||||
#include "RoundedRectangleShape.hpp"
|
||||
#include "SFML/Graphics.hpp"
|
||||
#include "Text2.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_SFML)
|
||||
|
||||
#if defined(PROJECT_ENABLE_SAGO_PLATFORM_FOLDERS)
|
||||
#include "platform_folders.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_SAGO_PLATFORM_FOLDERS)
|
||||
|
||||
#if defined(PROJECT_ENABLE_SPDLOG)
|
||||
#include "spdlog/async.h"
|
||||
#include "spdlog/fmt/bundled/core.h"
|
||||
#include "spdlog/fmt/bundled/format.h"
|
||||
#include "spdlog/fmt/bundled/ranges.h"
|
||||
#include "spdlog/fmt/chrono.h"
|
||||
#include "spdlog/sinks/rotating_file_sink.h"
|
||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
#endif // defined(PROJECT_ENABLE_SPDLOG)
|
||||
|
||||
#if defined(PROJECT_ENABLE_STDUUID)
|
||||
#include "uuid.h"
|
||||
#endif // defined(PROJECT_ENABLE_STDUUID)
|
||||
|
||||
#if defined(PROJECT_ENABLE_TPL)
|
||||
#include "process.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_TPL)
|
||||
|
||||
#if defined(PROJECT_ENABLE_WINFSP)
|
||||
#include "winfsp/winfsp.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_WINFSP)
|
||||
|
||||
namespace monitarr {
|
||||
using data_buffer = std::vector<unsigned char>;
|
||||
using mutex_lock = std::lock_guard<std::mutex>;
|
||||
using recur_mutex_lock = std::lock_guard<std::recursive_mutex>;
|
||||
using stop_type = std::atomic_bool;
|
||||
using stop_type_callback = std::function<std::atomic_bool()>;
|
||||
using unique_mutex_lock = std::unique_lock<std::mutex>;
|
||||
using unique_recur_mutex_lock = std::unique_lock<std::recursive_mutex>;
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES)
|
||||
inline constexpr const auto max_path_length = std::size_t{32767U};
|
||||
#else // !defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES)
|
||||
inline constexpr const auto max_path_length = std::size_t{MAX_PATH};
|
||||
#endif // defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES)
|
||||
|
||||
using native_handle = HANDLE;
|
||||
#else // !defined(_WIN32)
|
||||
inline constexpr const auto max_path_length = std::size_t{PATH_MAX};
|
||||
using native_handle = int;
|
||||
#if !defined(INVALID_HANDLE_VALUE)
|
||||
#define INVALID_HANDLE_VALUE (-1)
|
||||
#endif // !defined(INVALID_HANDLE_VALUE)
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
template <class... Ts> struct overloaded : Ts... {
|
||||
using Ts::operator()...;
|
||||
};
|
||||
template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
template <typename T> struct is_collection {
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template <typename T, typename A>
|
||||
struct is_collection<std::vector<T, A>> : std::true_type {};
|
||||
|
||||
template <typename T> struct is_collection<std::deque<T>> : std::true_type {};
|
||||
|
||||
template <> struct is_collection<std::string> : std::true_type {};
|
||||
|
||||
template <> struct is_collection<std::wstring> : std::true_type {};
|
||||
|
||||
struct file_deleter final {
|
||||
void operator()(FILE *file) {
|
||||
if (file != nullptr) {
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
};
|
||||
using file_t = std::unique_ptr<FILE, file_deleter>;
|
||||
|
||||
struct http_range final {
|
||||
std::uint64_t begin{};
|
||||
std::uint64_t end{};
|
||||
};
|
||||
|
||||
using http_headers = std::map<std::string, std::string>;
|
||||
using http_query_parameters = std::map<std::string, std::string>;
|
||||
using http_ranges = std::vector<http_range>;
|
||||
} // namespace monitarr
|
||||
#endif // defined(__cplusplus)
|
||||
|
||||
#define MONITARR_USES_FUNCTION_NAME() \
|
||||
static constexpr const std::string_view function_name { \
|
||||
static_cast<const char *>(__FUNCTION__), \
|
||||
}
|
||||
|
||||
#endif // MONITARR_INCLUDE_UTILS_CONFIG_HPP_
|
||||
170
support/include/utils/db/sqlite/db_common.hpp
Normal file
170
support/include/utils/db/sqlite/db_common.hpp
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_COMMON_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_COMMON_HPP_
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
#include "utils/error.hpp"
|
||||
|
||||
namespace monitarr::utils::db::sqlite {
|
||||
using db_types_t = std::variant<std::int64_t, std::string>;
|
||||
|
||||
struct sqlite3_deleter final {
|
||||
void operator()(sqlite3 *db3) const;
|
||||
};
|
||||
|
||||
using db3_t = std::unique_ptr<sqlite3, sqlite3_deleter>;
|
||||
|
||||
struct sqlite3_statement_deleter final {
|
||||
void operator()(sqlite3_stmt *stmt) const {
|
||||
if (stmt != nullptr) {
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using db3_stmt_t = std::unique_ptr<sqlite3_stmt, sqlite3_statement_deleter>;
|
||||
|
||||
[[nodiscard]] auto
|
||||
create_db(std::string db_path,
|
||||
const std::map<std::string, std::string> &sql_create_tables) -> db3_t;
|
||||
|
||||
[[nodiscard]] auto execute_sql(sqlite3 &db3, const std::string &sql,
|
||||
std::string &err) -> bool;
|
||||
|
||||
void set_journal_mode(sqlite3 &db3);
|
||||
|
||||
struct db_comp_data_t final {
|
||||
std::string column_name;
|
||||
std::string op_type;
|
||||
};
|
||||
|
||||
struct db_context_t {
|
||||
db_context_t(sqlite3 *db3_, std::string table_name_)
|
||||
: db3(db3_), table_name(std::move(table_name_)) {}
|
||||
|
||||
sqlite3 *db3{};
|
||||
std::string table_name;
|
||||
};
|
||||
|
||||
struct db_result final {
|
||||
struct context final {
|
||||
db3_stmt_t stmt;
|
||||
};
|
||||
|
||||
class db_column final {
|
||||
public:
|
||||
db_column(std::int32_t index, std::string name, db_types_t value) noexcept;
|
||||
|
||||
db_column() noexcept = default;
|
||||
db_column(const db_column &) = default;
|
||||
db_column(db_column &&column) noexcept = default;
|
||||
|
||||
~db_column() = default;
|
||||
|
||||
auto operator=(const db_column &) -> db_column & = default;
|
||||
auto operator=(db_column &&) -> db_column & = default;
|
||||
|
||||
private:
|
||||
std::int32_t index_{};
|
||||
std::string name_;
|
||||
db_types_t value_;
|
||||
|
||||
public:
|
||||
[[nodiscard]] auto get_index() const -> std::int32_t { return index_; }
|
||||
|
||||
[[nodiscard]] auto get_name() const -> std::string { return name_; }
|
||||
|
||||
template <typename data_type>
|
||||
[[nodiscard]] auto get_value() const -> data_type {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
return std::visit(
|
||||
overloaded{
|
||||
[](const data_type &value) -> data_type { return value; },
|
||||
[](auto &&) -> data_type {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"data type not supported",
|
||||
});
|
||||
},
|
||||
},
|
||||
value_);
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_JSON)
|
||||
[[nodiscard]] auto get_value_as_json() const -> nlohmann::json;
|
||||
#endif // defined(PROJECT_ENABLE_JSON)
|
||||
};
|
||||
|
||||
class db_row final {
|
||||
public:
|
||||
db_row(std::shared_ptr<context> ctx);
|
||||
|
||||
private:
|
||||
std::map<std::string, db_column> columns_;
|
||||
|
||||
public:
|
||||
[[nodiscard]] auto get_columns() const -> std::vector<db_column>;
|
||||
|
||||
[[nodiscard]] auto get_column(std::int32_t index) const -> db_column;
|
||||
|
||||
[[nodiscard]] auto get_column(std::string name) const -> db_column;
|
||||
};
|
||||
|
||||
db_result(db3_stmt_t stmt, std::int32_t res);
|
||||
|
||||
db_result() = default;
|
||||
db_result(const db_result &) = default;
|
||||
db_result(db_result &&) noexcept = default;
|
||||
|
||||
auto operator=(const db_result &) -> db_result & = default;
|
||||
auto operator=(db_result &&) -> db_result & = default;
|
||||
|
||||
using row = db_row;
|
||||
|
||||
private:
|
||||
std::shared_ptr<context> ctx_;
|
||||
mutable std::int32_t res_{};
|
||||
|
||||
private:
|
||||
void set_res(std::int32_t res) const { res_ = res; }
|
||||
|
||||
public:
|
||||
[[nodiscard]] auto get_error() const -> std::int32_t { return res_; }
|
||||
|
||||
[[nodiscard]] auto get_error_str() const -> std::string;
|
||||
|
||||
[[nodiscard]] auto get_row(std::optional<row> &opt_row) const -> bool;
|
||||
|
||||
[[nodiscard]] auto has_row() const -> bool;
|
||||
|
||||
void next_row() const;
|
||||
|
||||
[[nodiscard]] auto ok() const -> bool;
|
||||
};
|
||||
} // namespace monitarr::utils::db::sqlite
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
#endif // MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_COMMON_HPP_
|
||||
73
support/include/utils/db/sqlite/db_delete.hpp
Normal file
73
support/include/utils/db/sqlite/db_delete.hpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_DELETE_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_DELETE_HPP_
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#include "utils/db/sqlite/db_common.hpp"
|
||||
|
||||
#include "utils/db/sqlite/db_where_t.hpp"
|
||||
|
||||
namespace monitarr::utils::db::sqlite {
|
||||
class db_delete final {
|
||||
public:
|
||||
struct context final : db_context_t {
|
||||
struct db_delete_op_t final {
|
||||
std::shared_ptr<context> ctx;
|
||||
|
||||
[[nodiscard]] auto dump() const -> std::string;
|
||||
|
||||
[[nodiscard]] auto go() const -> db_result;
|
||||
};
|
||||
|
||||
context(sqlite3 *db3_, std::string table_name_)
|
||||
: db_context_t(db3_, table_name_) {}
|
||||
|
||||
using w_t = db_where_t<context, db_delete_op_t>;
|
||||
using wd_t = where_data_t<w_t>;
|
||||
|
||||
std::unique_ptr<wd_t> where_data;
|
||||
};
|
||||
|
||||
public:
|
||||
db_delete(sqlite3 &db3, std::string table_name)
|
||||
: ctx_(std::make_shared<context>(&db3, table_name)) {}
|
||||
|
||||
db_delete(std::shared_ptr<context> ctx) : ctx_(std::move(ctx)) {}
|
||||
|
||||
private:
|
||||
std::shared_ptr<context> ctx_;
|
||||
|
||||
public:
|
||||
[[nodiscard]] auto dump() const -> std::string;
|
||||
|
||||
[[nodiscard]] auto go() const -> db_result;
|
||||
|
||||
[[nodiscard]] auto
|
||||
group(context::w_t::group_func_t func) -> context::w_t::wn_t;
|
||||
|
||||
[[nodiscard]] auto where(std::string column_name) const -> context::w_t::cn_t;
|
||||
};
|
||||
} // namespace monitarr::utils::db::sqlite
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
#endif // MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_DELETE_HPP_
|
||||
64
support/include/utils/db/sqlite/db_insert.hpp
Normal file
64
support/include/utils/db/sqlite/db_insert.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_INSERT_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_INSERT_HPP_
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#include "utils/db/sqlite/db_common.hpp"
|
||||
|
||||
namespace monitarr::utils::db::sqlite {
|
||||
class db_insert final {
|
||||
public:
|
||||
struct context final : db_context_t {
|
||||
context(sqlite3 *db3_, std::string table_name_)
|
||||
: db_context_t(db3_, table_name_) {}
|
||||
|
||||
bool or_replace{false};
|
||||
std::map<std::string, db_types_t> values;
|
||||
};
|
||||
|
||||
public:
|
||||
db_insert(sqlite3 &db3, std::string table_name)
|
||||
: ctx_(std::make_shared<context>(&db3, table_name)) {}
|
||||
|
||||
db_insert(std::shared_ptr<context> ctx) : ctx_(std::move(ctx)) {}
|
||||
|
||||
private:
|
||||
std::shared_ptr<context> ctx_;
|
||||
|
||||
public:
|
||||
[[nodiscard]] auto or_replace() -> db_insert {
|
||||
ctx_->or_replace = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto column_value(std::string column_name, db_types_t value)
|
||||
-> db_insert;
|
||||
|
||||
[[nodiscard]] auto dump() const -> std::string;
|
||||
|
||||
[[nodiscard]] auto go() const -> db_result;
|
||||
};
|
||||
} // namespace monitarr::utils::db::sqlite
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
#endif // MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_INSERT_HPP_
|
||||
104
support/include/utils/db/sqlite/db_select.hpp
Normal file
104
support/include/utils/db/sqlite/db_select.hpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_SELECT_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_SELECT_HPP_
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#include "utils/db/sqlite/db_common.hpp"
|
||||
|
||||
#include "utils/db/sqlite/db_where_t.hpp"
|
||||
|
||||
namespace monitarr::utils::db::sqlite {
|
||||
class db_select final {
|
||||
public:
|
||||
struct context final : db_context_t {
|
||||
struct db_select_op_t final {
|
||||
std::shared_ptr<context> ctx;
|
||||
|
||||
[[nodiscard]] auto dump() const -> std::string;
|
||||
|
||||
[[nodiscard]] auto go() const -> db_result;
|
||||
|
||||
[[nodiscard]] auto group_by(std::string column_name) -> db_select_op_t;
|
||||
|
||||
[[nodiscard]] auto limit(std::int32_t value) -> db_select_op_t;
|
||||
|
||||
[[nodiscard]] auto offset(std::int32_t value) -> db_select_op_t;
|
||||
|
||||
[[nodiscard]] auto order_by(std::string column_name, bool ascending)
|
||||
-> db_select_op_t;
|
||||
};
|
||||
|
||||
context(sqlite3 *db3_, std::string table_name_)
|
||||
: db_context_t(db3_, table_name_) {}
|
||||
|
||||
using w_t = db_where_t<context, db_select_op_t>;
|
||||
using wd_t = where_data_t<w_t>;
|
||||
|
||||
std::vector<std::string> columns;
|
||||
std::map<std::string, std::string> count_columns;
|
||||
|
||||
std::vector<std::string> group_by;
|
||||
std::optional<std::int32_t> limit;
|
||||
std::optional<std::int32_t> offset;
|
||||
std::optional<std::pair<std::string, bool>> order_by;
|
||||
|
||||
std::unique_ptr<wd_t> where_data;
|
||||
};
|
||||
|
||||
public:
|
||||
db_select(sqlite3 &db3, std::string table_name)
|
||||
: ctx_(std::make_shared<context>(&db3, table_name)) {}
|
||||
|
||||
db_select(std::shared_ptr<context> ctx) : ctx_(std::move(ctx)) {}
|
||||
|
||||
private:
|
||||
std::shared_ptr<context> ctx_;
|
||||
|
||||
public:
|
||||
[[nodiscard]] auto column(std::string column_name) -> db_select;
|
||||
|
||||
[[nodiscard]] auto count(std::string column_name, std::string as_column_name)
|
||||
-> db_select;
|
||||
|
||||
[[nodiscard]] auto dump() const -> std::string;
|
||||
|
||||
[[nodiscard]] auto go() const -> db_result;
|
||||
|
||||
[[nodiscard]] auto group_by(std::string column_name) -> db_select;
|
||||
[[nodiscard]] auto group(context::w_t::group_func_t func)
|
||||
|
||||
-> context::w_t::wn_t;
|
||||
|
||||
[[nodiscard]] auto limit(std::int32_t value) -> db_select;
|
||||
|
||||
[[nodiscard]] auto offset(std::int32_t value) -> db_select;
|
||||
|
||||
[[nodiscard]] auto order_by(std::string column_name, bool ascending)
|
||||
-> db_select;
|
||||
|
||||
[[nodiscard]] auto where(std::string column_name) const -> context::w_t::cn_t;
|
||||
};
|
||||
} // namespace monitarr::utils::db::sqlite
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
#endif // MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_SELECT_HPP_
|
||||
91
support/include/utils/db/sqlite/db_update.hpp
Normal file
91
support/include/utils/db/sqlite/db_update.hpp
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_UPDATE_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_UPDATE_HPP_
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#include "utils/db/sqlite/db_common.hpp"
|
||||
|
||||
#include "utils/db/sqlite/db_where_t.hpp"
|
||||
|
||||
namespace monitarr::utils::db::sqlite {
|
||||
class db_update final {
|
||||
public:
|
||||
struct context final : db_context_t {
|
||||
context(sqlite3 *db3_, std::string table_name_)
|
||||
: db_context_t(db3_, table_name_) {}
|
||||
|
||||
struct db_update_op_t final {
|
||||
std::shared_ptr<context> ctx;
|
||||
|
||||
[[nodiscard]] auto dump() const -> std::string;
|
||||
|
||||
[[nodiscard]] auto go() const -> db_result;
|
||||
|
||||
[[nodiscard]] auto limit(std::int32_t value) -> db_update_op_t;
|
||||
|
||||
[[nodiscard]] auto order_by(std::string column_name, bool ascending)
|
||||
|
||||
-> db_update_op_t;
|
||||
};
|
||||
|
||||
using w_t = db_where_t<context, db_update_op_t>;
|
||||
using wd_t = where_data_t<w_t>;
|
||||
|
||||
std::map<std::string, db_types_t> column_values;
|
||||
std::optional<std::int32_t> limit;
|
||||
std::optional<std::pair<std::string, bool>> order_by;
|
||||
|
||||
std::unique_ptr<wd_t> where_data;
|
||||
};
|
||||
|
||||
public:
|
||||
db_update(sqlite3 &db3, std::string table_name)
|
||||
: ctx_(std::make_shared<context>(&db3, table_name)) {}
|
||||
|
||||
db_update(std::shared_ptr<context> ctx) : ctx_(std::move(ctx)) {}
|
||||
|
||||
private:
|
||||
std::shared_ptr<context> ctx_;
|
||||
|
||||
public:
|
||||
[[nodiscard]] auto column_value(std::string column_name, db_types_t value)
|
||||
-> db_update;
|
||||
|
||||
[[nodiscard]] auto dump() const -> std::string;
|
||||
|
||||
[[nodiscard]] auto go() const -> db_result;
|
||||
|
||||
[[nodiscard]] auto group(context::w_t::group_func_t func)
|
||||
-> context::w_t::wn_t;
|
||||
|
||||
[[nodiscard]] auto limit(std::int32_t value) -> db_update;
|
||||
|
||||
[[nodiscard]] auto order_by(std::string column_name, bool ascending)
|
||||
-> db_update;
|
||||
|
||||
[[nodiscard]] auto where(std::string column_name) const -> context::w_t::cn_t;
|
||||
};
|
||||
} // namespace monitarr::utils::db::sqlite
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
#endif // MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_UPDATE_HPP_
|
||||
224
support/include/utils/db/sqlite/db_where_t.hpp
Normal file
224
support/include/utils/db/sqlite/db_where_t.hpp
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_WHERE_T_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_WHERE_T_HPP_
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#include "utils/db/sqlite/db_common.hpp"
|
||||
|
||||
namespace monitarr::utils::db::sqlite {
|
||||
template <typename w_t> struct where_data_t final {
|
||||
w_t base;
|
||||
std::map<std::size_t, std::vector<typename w_t::action_t>> actions;
|
||||
std::vector<db_types_t> values;
|
||||
};
|
||||
|
||||
template <typename cn_t, typename ctx_t, typename op_t, typename w_t,
|
||||
typename wn_t>
|
||||
struct db_next_t final {
|
||||
std::size_t action_idx{};
|
||||
std::shared_ptr<ctx_t> ctx;
|
||||
std::string action;
|
||||
|
||||
using group_func_t = std::function<void(w_t &)>;
|
||||
|
||||
[[nodiscard]] auto where(std::string column_name) -> cn_t {
|
||||
return w_t{action_idx, ctx}.where(column_name);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto dump() const -> std::string { return op_t{ctx}.dump(); }
|
||||
|
||||
[[nodiscard]] auto dump(std::int32_t &idx) const -> std::string {
|
||||
return ctx->where_data->base.dump(idx);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto go() const -> auto { return op_t{ctx}.go(); }
|
||||
|
||||
[[nodiscard]] auto group(group_func_t func) -> wn_t {
|
||||
return w_t{action_idx, ctx}.group(std::move(func));
|
||||
}
|
||||
|
||||
[[nodiscard]] auto op() -> op_t {
|
||||
return op_t{
|
||||
ctx,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename cn_t, typename ctx_t, typename op_t, typename w_t>
|
||||
struct db_where_next_t final {
|
||||
std::size_t action_idx{};
|
||||
std::shared_ptr<ctx_t> ctx;
|
||||
|
||||
using n_t = db_next_t<cn_t, ctx_t, op_t, w_t, db_where_next_t>;
|
||||
|
||||
[[nodiscard]] auto and_() -> n_t {
|
||||
n_t next{
|
||||
action_idx,
|
||||
ctx,
|
||||
"AND",
|
||||
};
|
||||
|
||||
ctx->where_data->actions[action_idx].emplace_back(next);
|
||||
return next;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto dump() const -> std::string { return op_t{ctx}.dump(); }
|
||||
|
||||
[[nodiscard]] auto dump(std::int32_t &idx) const -> std::string {
|
||||
return ctx->where_data->base.dump(idx);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto go() const -> auto { return op_t{ctx}.go(); }
|
||||
|
||||
[[nodiscard]] auto op() -> op_t {
|
||||
return op_t{
|
||||
ctx,
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] auto or_() -> n_t {
|
||||
n_t next{
|
||||
action_idx,
|
||||
ctx,
|
||||
"OR",
|
||||
};
|
||||
|
||||
ctx->where_data->actions[action_idx].emplace_back(next);
|
||||
return next;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ctx_t, typename op_t, typename w_t>
|
||||
struct db_comp_next_t final {
|
||||
std::size_t action_idx{};
|
||||
std::shared_ptr<ctx_t> ctx;
|
||||
std::string column_name;
|
||||
|
||||
using wn_t = db_where_next_t<db_comp_next_t, ctx_t, op_t, w_t>;
|
||||
|
||||
[[nodiscard]] auto create(std::string operation, db_types_t value) {
|
||||
ctx->where_data->actions[action_idx].emplace_back(db_comp_data_t{
|
||||
column_name,
|
||||
operation,
|
||||
});
|
||||
|
||||
ctx->where_data->values.push_back(value);
|
||||
|
||||
return wn_t{
|
||||
action_idx,
|
||||
ctx,
|
||||
};
|
||||
}
|
||||
|
||||
auto equals(db_types_t value) -> wn_t { return create("=", value); };
|
||||
|
||||
auto gt(db_types_t value) -> wn_t { return create(">", value); }
|
||||
|
||||
auto gte(db_types_t value) -> wn_t { return create(">=", value); }
|
||||
|
||||
auto like(db_types_t value) -> wn_t { return create("LIKE", value); }
|
||||
|
||||
auto lt(db_types_t value) -> wn_t { return create("<", value); }
|
||||
|
||||
auto lte(db_types_t value) -> wn_t { return create("<=", value); }
|
||||
|
||||
auto not_equals(db_types_t value) -> wn_t { return create("!=", value); };
|
||||
};
|
||||
|
||||
template <typename ctx_t, typename op_t> struct db_where_t final {
|
||||
std::size_t action_idx{0U};
|
||||
std::shared_ptr<ctx_t> ctx;
|
||||
|
||||
using cn_t = db_comp_next_t<ctx_t, op_t, db_where_t>;
|
||||
using wn_t = db_where_next_t<cn_t, ctx_t, op_t, db_where_t>;
|
||||
using n_t = db_next_t<cn_t, ctx_t, op_t, db_where_t, wn_t>;
|
||||
|
||||
using group_func_t = std::function<void(db_where_t &)>;
|
||||
|
||||
using action_t = std::variant<db_comp_data_t, n_t, db_where_t>;
|
||||
|
||||
[[nodiscard]] static auto dump(std::int32_t &idx,
|
||||
auto &&actions) -> std::string {
|
||||
std::stringstream stream;
|
||||
|
||||
for (auto &&action : actions) {
|
||||
std::visit(overloaded{
|
||||
[&idx, &stream](const db_comp_data_t &comp) {
|
||||
stream << '"' << comp.column_name << '"' << comp.op_type
|
||||
<< '?' + std::to_string(++idx);
|
||||
},
|
||||
[&idx, &stream](const n_t &next) {
|
||||
stream << ' ' << next.action << ' ';
|
||||
},
|
||||
[&idx, &stream](const db_where_t &where) {
|
||||
stream << '(' << dump(idx, where.get_actions()) << ')';
|
||||
},
|
||||
},
|
||||
action);
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto dump() const -> std::string { return op_t{ctx}.dump(); }
|
||||
|
||||
[[nodiscard]] auto dump(std::int32_t &idx) const -> std::string {
|
||||
return dump(idx, ctx->where_data->actions[action_idx]);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_actions() -> auto & {
|
||||
return ctx->where_data->actions[action_idx];
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_actions() const -> const auto & {
|
||||
return ctx->where_data->actions[action_idx];
|
||||
}
|
||||
|
||||
[[nodiscard]] auto group(group_func_t func) -> wn_t {
|
||||
ctx->where_data->actions[action_idx];
|
||||
|
||||
db_where_t where{ctx->where_data->actions.size(), ctx};
|
||||
func(where);
|
||||
|
||||
ctx->where_data->actions[action_idx].emplace_back(where);
|
||||
|
||||
return wn_t{
|
||||
action_idx,
|
||||
ctx,
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] auto where(std::string column_name) -> cn_t {
|
||||
ctx->where_data->actions[action_idx];
|
||||
|
||||
return cn_t{
|
||||
action_idx,
|
||||
ctx,
|
||||
column_name,
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace monitarr::utils::db::sqlite
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
#endif // MONITARR_INCLUDE_UTILS_DB_SQLITE_DB_WHERE_T_HPP_
|
||||
150
support/include/utils/encrypting_reader.hpp
Normal file
150
support/include/utils/encrypting_reader.hpp
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_ENCRYPTING_READER_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_ENCRYPTING_READER_HPP_
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
#include "utils/hash.hpp"
|
||||
#include "utils/types/file/i_file.hpp"
|
||||
|
||||
namespace monitarr::utils::encryption {
|
||||
class encrypting_reader final {
|
||||
public:
|
||||
encrypting_reader(std::string_view file_name, std::string_view source_path,
|
||||
stop_type_callback stop_requested_cb,
|
||||
std::string_view token,
|
||||
std::optional<std::string> relative_parent_path,
|
||||
std::size_t error_return = 0U);
|
||||
|
||||
encrypting_reader(std::string_view encrypted_file_path,
|
||||
std::string_view source_path,
|
||||
stop_type_callback stop_requested_cb,
|
||||
std::string_view token, std::size_t error_return = 0U);
|
||||
|
||||
encrypting_reader(
|
||||
std::string_view encrypted_file_path, std::string_view source_path,
|
||||
stop_type_callback stop_requested_cb, std::string_view token,
|
||||
std::vector<std::array<unsigned char,
|
||||
crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>>
|
||||
iv_list,
|
||||
std::size_t error_return = 0U);
|
||||
|
||||
encrypting_reader(const encrypting_reader &reader);
|
||||
encrypting_reader(encrypting_reader &&) = delete;
|
||||
|
||||
auto operator=(const encrypting_reader &) -> encrypting_reader & = delete;
|
||||
auto operator=(encrypting_reader &&) -> encrypting_reader & = delete;
|
||||
|
||||
~encrypting_reader() noexcept = default;
|
||||
|
||||
public:
|
||||
using iostream = std::basic_iostream<char, std::char_traits<char>>;
|
||||
using streambuf = std::basic_streambuf<char, std::char_traits<char>>;
|
||||
|
||||
private:
|
||||
utils::encryption::hash_256_t key_;
|
||||
stop_type_callback stop_requested_cb_;
|
||||
size_t error_return_;
|
||||
std::unique_ptr<utils::file::i_file> source_file_;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::size_t, data_buffer> chunk_buffers_;
|
||||
std::string encrypted_file_name_;
|
||||
std::string encrypted_file_path_;
|
||||
std::vector<
|
||||
std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>>
|
||||
iv_list_;
|
||||
std::size_t last_data_chunk_{};
|
||||
std::size_t last_data_chunk_size_{};
|
||||
std::uint64_t read_offset_{};
|
||||
std::uint64_t total_size_{};
|
||||
|
||||
private:
|
||||
static const std::size_t header_size_;
|
||||
static const std::size_t data_chunk_size_;
|
||||
static const std::size_t encrypted_chunk_size_;
|
||||
|
||||
private:
|
||||
auto reader_function(char *buffer, size_t size, size_t nitems) -> size_t;
|
||||
|
||||
public:
|
||||
[[nodiscard]] static auto calculate_decrypted_size(std::uint64_t total_size)
|
||||
-> std::uint64_t;
|
||||
|
||||
[[nodiscard]] static auto
|
||||
calculate_encrypted_size(std::string_view source_path) -> std::uint64_t;
|
||||
|
||||
[[nodiscard]] auto create_iostream() const -> std::shared_ptr<iostream>;
|
||||
|
||||
[[nodiscard]] static constexpr auto get_encrypted_chunk_size()
|
||||
-> std::size_t {
|
||||
return encrypted_chunk_size_;
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr auto get_data_chunk_size() -> std::size_t {
|
||||
return data_chunk_size_;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_encrypted_file_name() const -> std::string {
|
||||
return encrypted_file_name_;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_encrypted_file_path() const -> std::string {
|
||||
return encrypted_file_path_;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_error_return() const -> std::size_t {
|
||||
return error_return_;
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr auto get_header_size() -> std::size_t {
|
||||
return header_size_;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_iv_list() -> std::vector<
|
||||
std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>> {
|
||||
return iv_list_;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_stop_requested() const -> bool {
|
||||
return stop_requested_cb_();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_total_size() const -> std::uint64_t {
|
||||
return total_size_;
|
||||
}
|
||||
|
||||
[[nodiscard]] static auto reader_function(char *buffer, size_t size,
|
||||
size_t nitems, void *instream)
|
||||
-> size_t {
|
||||
return reinterpret_cast<encrypting_reader *>(instream)->reader_function(
|
||||
buffer, size, nitems);
|
||||
}
|
||||
|
||||
void set_read_position(std::uint64_t position) { read_offset_ = position; }
|
||||
};
|
||||
} // namespace monitarr::utils::encryption
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
#endif // MONITARR_INCLUDE_UTILS_ENCRYPTING_READER_HPP_
|
||||
224
support/include/utils/encryption.hpp
Normal file
224
support/include/utils/encryption.hpp
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_ENCRYPTION_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_ENCRYPTION_HPP_
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/hash.hpp"
|
||||
|
||||
namespace monitarr::utils::encryption {
|
||||
inline constexpr const std::uint32_t encryption_header_size{
|
||||
crypto_aead_xchacha20poly1305_IETF_NPUBBYTES +
|
||||
crypto_aead_xchacha20poly1305_IETF_ABYTES,
|
||||
};
|
||||
|
||||
template <typename hash_t>
|
||||
inline auto generate_key(
|
||||
std::string_view password,
|
||||
std::function<hash_t(const unsigned char *data, std::size_t size)> hasher =
|
||||
default_create_hash<hash_t>()) -> hash_t;
|
||||
|
||||
template <typename hash_t>
|
||||
inline auto generate_key(
|
||||
std::wstring_view password,
|
||||
std::function<hash_t(const unsigned char *data, std::size_t size)> hasher =
|
||||
default_create_hash<hash_t>()) -> hash_t;
|
||||
|
||||
#if defined(PROJECT_ENABLE_BOOST)
|
||||
[[nodiscard]] auto decrypt_file_name(std::string_view encryption_token,
|
||||
std::string &file_name) -> bool;
|
||||
|
||||
[[nodiscard]] auto decrypt_file_path(std::string_view encryption_token,
|
||||
std::string &file_path) -> bool;
|
||||
|
||||
template <typename result_t, typename arr_t, std::size_t arr_size>
|
||||
[[nodiscard]] inline auto decrypt_data(const std::array<arr_t, arr_size> &key,
|
||||
const unsigned char *buffer,
|
||||
std::size_t buffer_size,
|
||||
result_t &res) -> bool {
|
||||
if (buffer_size > encryption_header_size) {
|
||||
const std::uint32_t size =
|
||||
boost::endian::native_to_big(static_cast<std::uint32_t>(buffer_size));
|
||||
res.resize(buffer_size - encryption_header_size);
|
||||
return crypto_aead_xchacha20poly1305_ietf_decrypt_detached(
|
||||
reinterpret_cast<unsigned char *>(res.data()), nullptr,
|
||||
&buffer[encryption_header_size], res.size(),
|
||||
&buffer[crypto_aead_xchacha20poly1305_IETF_NPUBBYTES],
|
||||
reinterpret_cast<const unsigned char *>(&size), sizeof(size),
|
||||
buffer, key.data()) == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename buffer_t, typename result_t, typename arr_t,
|
||||
std::size_t arr_size>
|
||||
[[nodiscard]] inline auto decrypt_data(const std::array<arr_t, arr_size> &key,
|
||||
const buffer_t &buf,
|
||||
result_t &res) -> bool {
|
||||
return decrypt_data<result_t>(
|
||||
key, reinterpret_cast<const unsigned char *>(buf.data()), buf.size(),
|
||||
res);
|
||||
}
|
||||
|
||||
template <typename buffer_t, typename result_t, typename hash_t = hash_256_t>
|
||||
[[nodiscard]] inline auto decrypt_data(
|
||||
std::string_view password, const buffer_t &buf, result_t &res,
|
||||
std::function<hash_t(const unsigned char *data, std::size_t size)> hasher =
|
||||
default_create_hash<hash_t>()) -> bool {
|
||||
return decrypt_data<buffer_t, result_t>(generate_key(password, hasher), buf,
|
||||
res);
|
||||
}
|
||||
|
||||
template <typename result_t, typename hash_t = hash_256_t>
|
||||
[[nodiscard]] inline auto decrypt_data(
|
||||
std::string_view password, const unsigned char *buffer,
|
||||
std::size_t buffer_size, result_t &res,
|
||||
std::function<hash_t(const unsigned char *data, std::size_t size)> hasher =
|
||||
default_create_hash<hash_t>()) -> bool {
|
||||
return decrypt_data<result_t>(generate_key(password, hasher), buffer,
|
||||
buffer_size, res);
|
||||
}
|
||||
|
||||
template <typename result_t, typename arr_t, std::size_t arr_size>
|
||||
inline void
|
||||
encrypt_data(const std::array<unsigned char,
|
||||
crypto_aead_xchacha20poly1305_IETF_NPUBBYTES> &iv,
|
||||
const std::array<arr_t, arr_size> &key,
|
||||
const unsigned char *buffer, std::size_t buffer_size,
|
||||
result_t &res) {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_ABYTES> mac{};
|
||||
|
||||
const std::uint32_t size = boost::endian::native_to_big(
|
||||
static_cast<std::uint32_t>(buffer_size + encryption_header_size));
|
||||
|
||||
res.resize(buffer_size + encryption_header_size);
|
||||
|
||||
unsigned long long mac_length{};
|
||||
if (crypto_aead_xchacha20poly1305_ietf_encrypt_detached(
|
||||
reinterpret_cast<unsigned char *>(&res[encryption_header_size]),
|
||||
mac.data(), &mac_length, buffer, buffer_size,
|
||||
reinterpret_cast<const unsigned char *>(&size), sizeof(size), nullptr,
|
||||
iv.data(), key.data()) != 0) {
|
||||
throw monitarr::utils::error::create_exception(function_name,
|
||||
{
|
||||
"encryption failed",
|
||||
});
|
||||
}
|
||||
|
||||
std::memcpy(res.data(), iv.data(), iv.size());
|
||||
std::memcpy(&res[iv.size()], mac.data(), mac.size());
|
||||
}
|
||||
|
||||
template <typename result_t, typename arr_t, std::size_t arr_size>
|
||||
inline void encrypt_data(const std::array<arr_t, arr_size> &key,
|
||||
const unsigned char *buffer, std::size_t buffer_size,
|
||||
result_t &res) {
|
||||
std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_NPUBBYTES> iv{};
|
||||
randombytes_buf(iv.data(), iv.size());
|
||||
|
||||
encrypt_data<result_t>(iv, key, buffer, buffer_size, res);
|
||||
}
|
||||
|
||||
template <typename result_t, typename hash_t = hash_256_t>
|
||||
inline void encrypt_data(
|
||||
std::string_view password, const unsigned char *buffer,
|
||||
std::size_t buffer_size, result_t &res,
|
||||
std::function<hash_t(const unsigned char *data, std::size_t size)> hasher =
|
||||
default_create_hash<hash_t>()) {
|
||||
encrypt_data<result_t>(generate_key(password, hasher), buffer, buffer_size,
|
||||
res);
|
||||
}
|
||||
|
||||
template <typename buffer_t, typename result_t, typename hash_t = hash_256_t>
|
||||
inline void encrypt_data(
|
||||
std::string_view password, const buffer_t &buf, result_t &res,
|
||||
std::function<hash_t(const unsigned char *data, std::size_t size)> hasher =
|
||||
default_create_hash<hash_t>()) {
|
||||
encrypt_data<result_t>(generate_key(password, hasher),
|
||||
reinterpret_cast<const unsigned char *>(buf.data()),
|
||||
buf.size(), res);
|
||||
}
|
||||
|
||||
template <typename buffer_t, typename result_t, typename arr_t,
|
||||
std::size_t arr_size>
|
||||
inline void encrypt_data(const std::array<arr_t, arr_size> &key,
|
||||
const buffer_t &buf, result_t &res) {
|
||||
encrypt_data<result_t>(key,
|
||||
reinterpret_cast<const unsigned char *>(buf.data()),
|
||||
buf.size(), res);
|
||||
}
|
||||
|
||||
template <typename buffer_t, typename result_t, typename arr_t,
|
||||
std::size_t arr_size>
|
||||
inline void
|
||||
encrypt_data(const std::array<unsigned char,
|
||||
crypto_aead_xchacha20poly1305_IETF_NPUBBYTES> &iv,
|
||||
const std::array<arr_t, arr_size> &key, const buffer_t &buf,
|
||||
result_t &res) {
|
||||
encrypt_data<result_t>(iv, key,
|
||||
reinterpret_cast<const unsigned char *>(buf.data()),
|
||||
buf.size(), res);
|
||||
}
|
||||
|
||||
using reader_func_t =
|
||||
std::function<bool(data_buffer &cypher_text, std::uint64_t start_offset,
|
||||
std::uint64_t end_offset)>;
|
||||
|
||||
[[nodiscard]] auto
|
||||
read_encrypted_range(const http_range &range,
|
||||
const utils::encryption::hash_256_t &key,
|
||||
reader_func_t reader_func, std::uint64_t total_size,
|
||||
data_buffer &data) -> bool;
|
||||
|
||||
[[nodiscard]] auto read_encrypted_range(
|
||||
const http_range &range, const utils::encryption::hash_256_t &key,
|
||||
reader_func_t reader_func, std::uint64_t total_size, unsigned char *data,
|
||||
std::size_t size, std::size_t &bytes_read) -> bool;
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
template <typename hash_t>
|
||||
inline auto generate_key(
|
||||
std::string_view password,
|
||||
std::function<hash_t(const unsigned char *data, std::size_t size)> hasher)
|
||||
-> hash_t {
|
||||
return hasher(reinterpret_cast<const unsigned char *>(password.data()),
|
||||
password.size());
|
||||
}
|
||||
|
||||
template <typename hash_t>
|
||||
inline auto generate_key(
|
||||
std::wstring_view password,
|
||||
std::function<hash_t(const unsigned char *data, std::size_t size)> hasher)
|
||||
-> hash_t {
|
||||
return hasher(reinterpret_cast<const unsigned char *>(password.data()),
|
||||
password.size() * sizeof(wchar_t));
|
||||
}
|
||||
} // namespace monitarr::utils::encryption
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
#endif // MONITARR_INCLUDE_UTILS_ENCRYPTION_HPP_
|
||||
104
support/include/utils/error.hpp
Normal file
104
support/include/utils/error.hpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_ERROR_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_ERROR_HPP_
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
namespace monitarr::utils::error {
|
||||
[[nodiscard]] auto
|
||||
create_error_message(std::vector<std::string_view> items) -> std::string;
|
||||
|
||||
[[nodiscard]] auto
|
||||
create_exception(std::string_view function_name,
|
||||
std::vector<std::string_view> items) -> std::runtime_error;
|
||||
|
||||
struct i_exception_handler {
|
||||
virtual ~i_exception_handler() {}
|
||||
|
||||
i_exception_handler(const i_exception_handler &) noexcept = delete;
|
||||
i_exception_handler(i_exception_handler &&) noexcept = delete;
|
||||
auto operator=(const i_exception_handler &) noexcept = delete;
|
||||
auto operator=(i_exception_handler &&) noexcept = delete;
|
||||
|
||||
virtual void handle_error(std::string_view function_name,
|
||||
std::string_view msg) const = 0;
|
||||
|
||||
virtual void handle_exception(std::string_view function_name) const = 0;
|
||||
|
||||
virtual void handle_exception(std::string_view function_name,
|
||||
const std::exception &ex) const = 0;
|
||||
|
||||
protected:
|
||||
i_exception_handler() = default;
|
||||
};
|
||||
|
||||
struct iostream_exception_handler final : i_exception_handler {
|
||||
void handle_error(std::string_view function_name,
|
||||
std::string_view msg) const override {
|
||||
std::cerr << create_error_message({
|
||||
function_name,
|
||||
msg,
|
||||
})
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void handle_exception(std::string_view function_name) const override {
|
||||
std::cerr << create_error_message({
|
||||
function_name,
|
||||
"exception",
|
||||
"unknown",
|
||||
})
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void handle_exception(std::string_view function_name,
|
||||
const std::exception &ex) const override {
|
||||
std::cerr << create_error_message({
|
||||
function_name,
|
||||
"exception",
|
||||
(ex.what() == nullptr ? "unknown" : ex.what()),
|
||||
})
|
||||
<< std::endl;
|
||||
}
|
||||
};
|
||||
inline const iostream_exception_handler default_exception_handler{};
|
||||
|
||||
extern std::atomic<const i_exception_handler *> exception_handler;
|
||||
|
||||
#if defined(PROJECT_ENABLE_TESTING)
|
||||
[[nodiscard]] inline auto
|
||||
get_exception_handler() -> const i_exception_handler * {
|
||||
return exception_handler;
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_TESTING)
|
||||
|
||||
void handle_error(std::string_view function_name, std::string_view msg);
|
||||
|
||||
void handle_exception(std::string_view function_name);
|
||||
|
||||
void handle_exception(std::string_view function_name, const std::exception &ex);
|
||||
|
||||
void set_exception_handler(const i_exception_handler *handler);
|
||||
} // namespace monitarr::utils::error
|
||||
|
||||
#endif // MONITARR_INCLUDE_UTILS_ERROR_HPP_
|
||||
182
support/include/utils/file.hpp
Normal file
182
support/include/utils/file.hpp
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_FILE_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_FILE_HPP_
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
#include "utils/path.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "utils/types/file/i_directory.hpp"
|
||||
#include "utils/types/file/i_file.hpp"
|
||||
#include "utils/types/file/i_fs_item.hpp"
|
||||
|
||||
namespace monitarr::utils::file {
|
||||
[[nodiscard]] auto change_to_process_directory() -> bool;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto create_temp_name(std::string_view file_part) -> std::string;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto
|
||||
create_temp_name(std::wstring_view file_part) -> std::wstring;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] inline auto
|
||||
directory_exists_in_path(std::string_view path,
|
||||
std::string_view sub_directory) -> bool;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] inline auto
|
||||
directory_exists_in_path(std::wstring_view path,
|
||||
std::wstring_view sub_directory) -> bool;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] inline auto
|
||||
file_exists_in_path(std::string_view path, std::string_view file_name) -> bool;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] inline auto
|
||||
file_exists_in_path(std::wstring_view path,
|
||||
std::wstring_view file_name) -> bool;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto
|
||||
get_free_drive_space(std::string_view path) -> std::optional<std::uint64_t>;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto
|
||||
get_free_drive_space(std::wstring_view path) -> std::optional<std::uint64_t>;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto get_time(std::string_view path,
|
||||
time_type type) -> std::optional<std::uint64_t>;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto get_time(std::wstring_view path,
|
||||
time_type type) -> std::optional<std::uint64_t>;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto
|
||||
get_times(std::string_view path) -> std::optional<file_times>;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto
|
||||
get_times(std::wstring_view path) -> std::optional<file_times>;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto
|
||||
get_total_drive_space(std::string_view path) -> std::optional<std::uint64_t>;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto
|
||||
get_total_drive_space(std::wstring_view path) -> std::optional<std::uint64_t>;
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBDSM)
|
||||
[[nodiscard]] auto
|
||||
smb_create_and_validate_relative_path(std::string_view smb_path,
|
||||
std::string_view rel_path) -> std::string;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto
|
||||
smb_create_relative_path(std::string_view smb_path) -> std::string;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto
|
||||
smb_create_search_path(std::string_view smb_path) -> std::string;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto
|
||||
smb_create_smb_path(std::string_view smb_path,
|
||||
std::string_view rel_path) -> std::string;
|
||||
|
||||
[[nodiscard]] auto
|
||||
smb_get_parent_path(std::string_view smb_path) -> std::string;
|
||||
|
||||
[[nodiscard]] auto smb_get_root_path(std::string_view smb_path) -> std::string;
|
||||
|
||||
[[nodiscard]] auto smb_get_unc_path(std::string_view smb_path) -> std::string;
|
||||
|
||||
[[nodiscard]] auto smb_get_uri_path(std::string_view smb_path) -> std::string;
|
||||
|
||||
[[nodiscard]] auto smb_get_uri_path(std::string_view smb_path,
|
||||
std::string_view user,
|
||||
std::string_view password) -> std::string;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto smb_parent_is_same(std::string_view smb_path1,
|
||||
std::string_view smb_path2) -> bool;
|
||||
|
||||
#define SMB_MOD_RW2 \
|
||||
(SMB_MOD_READ | SMB_MOD_WRITE | SMB_MOD_READ_EXT | SMB_MOD_WRITE_EXT | \
|
||||
SMB_MOD_READ_ATTR | SMB_MOD_WRITE_ATTR | SMB_MOD_READ_CTL)
|
||||
#endif // defined(PROJECT_ENABLE_LIBDSM)
|
||||
|
||||
#if defined(PROJECT_ENABLE_JSON)
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto
|
||||
read_json_file(std::string_view path, nlohmann::json &data,
|
||||
std::optional<std::string_view> password = std::nullopt) -> bool;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto read_json_file(
|
||||
std::wstring_view path, nlohmann::json &data,
|
||||
std::optional<std::wstring_view> password = std::nullopt) -> bool;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto write_json_file(
|
||||
std::string_view path, const nlohmann::json &data,
|
||||
std::optional<std::string_view> password = std::nullopt) -> bool;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto write_json_file(
|
||||
std::wstring_view path, const nlohmann::json &data,
|
||||
std::optional<std::wstring_view> password = std::nullopt) -> bool;
|
||||
#else // !defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto read_json_file(std::string_view path,
|
||||
nlohmann::json &data) -> bool;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto read_json_file(std::wstring_view path,
|
||||
nlohmann::json &data) -> bool;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto write_json_file(std::string_view path,
|
||||
const nlohmann::json &data) -> bool;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] auto write_json_file(std::wstring_view path,
|
||||
const nlohmann::json &data) -> bool;
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
#endif // defined(PROJECT_ENABLE_JSON)
|
||||
} // namespace monitarr::utils::file
|
||||
|
||||
#endif // MONITARR_INCLUDE_UTILS_FILE_HPP_
|
||||
|
||||
#include "utils/file_directory.hpp"
|
||||
#include "utils/file_enc_file.hpp"
|
||||
#include "utils/file_file.hpp"
|
||||
#include "utils/file_smb_directory.hpp"
|
||||
#include "utils/file_smb_file.hpp"
|
||||
#include "utils/file_thread_file.hpp"
|
||||
124
support/include/utils/file_directory.hpp
Normal file
124
support/include/utils/file_directory.hpp
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_FILE_DIRECTORY_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_FILE_DIRECTORY_HPP_
|
||||
|
||||
#include "utils/file.hpp"
|
||||
|
||||
namespace monitarr::utils::file {
|
||||
class directory final : public i_directory {
|
||||
public:
|
||||
using directory_t = std::unique_ptr<directory>;
|
||||
|
||||
directory() noexcept = default;
|
||||
|
||||
directory(std::string_view path, stop_type *stop_requested = nullptr)
|
||||
: path_(utils::path::absolute(path)), stop_requested_(stop_requested) {}
|
||||
|
||||
directory(std::wstring_view path, stop_type *stop_requested = nullptr)
|
||||
: path_(utils::path::absolute(utils::string::to_utf8(path))),
|
||||
stop_requested_(stop_requested) {}
|
||||
|
||||
directory(const directory &) noexcept = delete;
|
||||
|
||||
directory(directory &&move_dir) noexcept = default;
|
||||
|
||||
~directory() override = default;
|
||||
|
||||
private:
|
||||
std::string path_;
|
||||
stop_type *stop_requested_{nullptr};
|
||||
|
||||
public:
|
||||
[[nodiscard]] auto copy_to(std::string_view new_path,
|
||||
bool overwrite) const -> bool override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
count(bool recursive = false) const -> std::uint64_t override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
create_directory(std::string_view path = "") const -> fs_directory_t override;
|
||||
|
||||
[[nodiscard]] auto create_file(std::string_view file_name,
|
||||
bool read_only) const -> fs_file_t override;
|
||||
|
||||
[[nodiscard]] auto exists() const -> bool override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
get_directory(std::string_view path) const -> fs_directory_t override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
get_directories() const -> std::vector<fs_directory_t> override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
get_file(std::string_view path) const -> fs_file_t override;
|
||||
|
||||
[[nodiscard]] auto get_files() const -> std::vector<fs_file_t> override;
|
||||
|
||||
[[nodiscard]] auto get_items() const -> std::vector<fs_item_t> override;
|
||||
|
||||
[[nodiscard]] auto get_path() const -> std::string override { return path_; }
|
||||
|
||||
[[nodiscard]] auto is_stop_requested() const -> bool override;
|
||||
|
||||
[[nodiscard]] auto is_symlink() const -> bool override;
|
||||
|
||||
[[nodiscard]] auto move_to(std::string_view new_path) -> bool override;
|
||||
|
||||
[[nodiscard]] auto remove() -> bool override;
|
||||
|
||||
[[nodiscard]] auto remove_recursively() -> bool override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
size(bool recursive = false) const -> std::uint64_t override;
|
||||
|
||||
public:
|
||||
auto operator=(const directory &) noexcept -> directory & = delete;
|
||||
|
||||
auto operator=(directory &&move_dir) noexcept -> directory & = default;
|
||||
|
||||
[[nodiscard]] operator bool() const override { return exists(); }
|
||||
};
|
||||
|
||||
// INFO: has test
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto directory_exists_in_path_t(
|
||||
std::basic_string_view<typename string_t::value_type> path,
|
||||
std::basic_string_view<typename string_t::value_type> sub_directory)
|
||||
-> bool {
|
||||
return directory(utils::path::combine(path, {sub_directory})).exists();
|
||||
}
|
||||
|
||||
// INFO: has test
|
||||
inline auto directory_exists_in_path(std::string_view path,
|
||||
std::string_view sub_directory) -> bool {
|
||||
return directory_exists_in_path_t<std::string>(path, sub_directory);
|
||||
}
|
||||
|
||||
// INFO: has test
|
||||
inline auto directory_exists_in_path(std::wstring_view path,
|
||||
std::wstring_view sub_directory) -> bool {
|
||||
return directory_exists_in_path_t<std::wstring>(path, sub_directory);
|
||||
}
|
||||
} // namespace monitarr::utils::file
|
||||
|
||||
#endif // MONITARR_INCLUDE_UTILS_FILE_DIRECTORY_HPP_
|
||||
124
support/include/utils/file_enc_file.hpp
Normal file
124
support/include/utils/file_enc_file.hpp
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_FILE_ENC_FILE_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_FILE_ENC_FILE_HPP_
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
#include "utils/file.hpp"
|
||||
|
||||
namespace monitarr::utils::file {
|
||||
class enc_file final : public i_file {
|
||||
public:
|
||||
[[nodiscard]] static auto attach_file(fs_file_t file) -> fs_file_t;
|
||||
|
||||
public:
|
||||
enc_file() noexcept = default;
|
||||
|
||||
protected:
|
||||
enc_file(fs_file_t file);
|
||||
|
||||
public:
|
||||
enc_file(const enc_file &) = delete;
|
||||
|
||||
enc_file(enc_file &&move_file) noexcept : file_(std::move(move_file.file_)) {}
|
||||
|
||||
~enc_file() override { close(); }
|
||||
|
||||
private:
|
||||
fs_file_t file_;
|
||||
std::string encryption_token_;
|
||||
|
||||
public:
|
||||
void close() override;
|
||||
|
||||
[[nodiscard]] auto copy_to(std::string_view new_path, bool overwrite) const
|
||||
-> bool override;
|
||||
|
||||
[[nodiscard]] auto exists() const -> bool override { return file_->exists(); }
|
||||
|
||||
void flush() const override;
|
||||
|
||||
[[nodiscard]] auto get_handle() const -> native_handle override {
|
||||
return file_->get_handle();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_path() const -> std::string override {
|
||||
return file_->get_path();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_read_buffer_size() const -> std::uint32_t override {
|
||||
return file_->get_read_buffer_size();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_time(time_type type) const
|
||||
-> std::optional<std::uint64_t> override {
|
||||
return file_->get_time(type);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_read_only() const -> bool override {
|
||||
return file_->is_read_only();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_symlink() const -> bool override {
|
||||
return file_->is_symlink();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto move_to(std::string_view new_path) -> bool override;
|
||||
|
||||
[[nodiscard]] auto read(unsigned char *data, std::size_t to_read,
|
||||
std::uint64_t offset,
|
||||
std::size_t *total_read = nullptr) -> bool override;
|
||||
|
||||
[[nodiscard]] auto remove() -> bool override;
|
||||
|
||||
auto set_read_buffer_size(std::uint32_t size) -> std::uint32_t override {
|
||||
return file_->set_read_buffer_size(size);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto size() const -> std::optional<std::uint64_t> override;
|
||||
|
||||
[[nodiscard]] auto truncate(std::size_t size) -> bool override;
|
||||
|
||||
[[nodiscard]] auto write(const unsigned char *data, std::size_t to_write,
|
||||
std::size_t offset,
|
||||
std::size_t *total_written = nullptr)
|
||||
-> bool override;
|
||||
|
||||
public:
|
||||
[[nodiscard]] operator bool() const override {
|
||||
return static_cast<bool>(*file_);
|
||||
}
|
||||
|
||||
auto operator=(const enc_file &) noexcept -> enc_file & = delete;
|
||||
|
||||
auto operator=(enc_file &&move_file) noexcept -> enc_file & {
|
||||
if (&move_file != this) {
|
||||
file_ = std::move(move_file.file_);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
} // namespace monitarr::utils::file
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
#endif // MONITARR_INCLUDE_UTILS_FILE_ENC_FILE_HPP_
|
||||
176
support/include/utils/file_file.hpp
Normal file
176
support/include/utils/file_file.hpp
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_FILE_FILE_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_FILE_FILE_HPP_
|
||||
|
||||
#include "utils/file.hpp"
|
||||
|
||||
namespace monitarr::utils::file {
|
||||
class file final : public i_file {
|
||||
public:
|
||||
// [[nodiscard]] static auto
|
||||
// attach_file(native_handle handle,
|
||||
// bool read_only = false) -> fs_file_t;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] static auto open_file(std::string_view path,
|
||||
bool read_only = false) -> fs_file_t;
|
||||
|
||||
[[nodiscard]] static auto open_file(std::wstring_view path,
|
||||
bool read_only = false) -> fs_file_t {
|
||||
return open_file(utils::string::to_utf8(path), read_only);
|
||||
}
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] static auto
|
||||
open_or_create_file(std::string_view path,
|
||||
bool read_only = false) -> fs_file_t;
|
||||
|
||||
[[nodiscard]] static auto
|
||||
open_or_create_file(std::wstring_view path,
|
||||
bool read_only = false) -> fs_file_t {
|
||||
return open_or_create_file(utils::string::to_utf8(path), read_only);
|
||||
}
|
||||
|
||||
public:
|
||||
file() noexcept = default;
|
||||
|
||||
file(std::string_view path)
|
||||
: file_(nullptr), path_(utils::path::absolute(path)) {}
|
||||
|
||||
file(std::wstring_view path)
|
||||
: file_(nullptr),
|
||||
path_(utils::path::absolute(utils::string::to_utf8(path))) {}
|
||||
|
||||
private:
|
||||
file(file_t file_ptr, std::string_view path, bool read_only)
|
||||
: file_(std::move(file_ptr)), path_(path), read_only_(read_only) {}
|
||||
|
||||
public:
|
||||
file(const file &) = delete;
|
||||
|
||||
file(file &&move_file) noexcept
|
||||
: file_(std::move(move_file.file_)),
|
||||
path_(std::move(move_file.path_)),
|
||||
read_only_(move_file.read_only_) {}
|
||||
|
||||
~file() override { close(); }
|
||||
|
||||
private:
|
||||
file_t file_;
|
||||
std::string path_;
|
||||
bool read_only_{false};
|
||||
|
||||
private:
|
||||
std::atomic_uint32_t read_buffer_size{65536U};
|
||||
|
||||
private:
|
||||
void open();
|
||||
|
||||
public:
|
||||
void close() override;
|
||||
|
||||
[[nodiscard]] auto copy_to(std::string_view new_path,
|
||||
bool overwrite) const -> bool override;
|
||||
|
||||
[[nodiscard]] auto exists() const -> bool override;
|
||||
|
||||
void flush() const override;
|
||||
|
||||
[[nodiscard]] auto get_handle() const -> native_handle override;
|
||||
|
||||
[[nodiscard]] auto get_path() const -> std::string override { return path_; }
|
||||
|
||||
[[nodiscard]] auto get_read_buffer_size() const -> std::uint32_t override {
|
||||
return read_buffer_size;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_read_only() const -> bool override {
|
||||
return read_only_;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_symlink() const -> bool override;
|
||||
|
||||
[[nodiscard]] auto move_to(std::string_view new_path) -> bool override;
|
||||
|
||||
[[nodiscard]] auto read(unsigned char *data, std::size_t to_read,
|
||||
std::uint64_t offset,
|
||||
std::size_t *total_read = nullptr) -> bool override;
|
||||
|
||||
[[nodiscard]] auto remove() -> bool override;
|
||||
|
||||
auto set_read_buffer_size(std::uint32_t size) -> std::uint32_t override {
|
||||
read_buffer_size = size;
|
||||
return read_buffer_size;
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
[[nodiscard]] auto sha256() -> std::optional<std::string>;
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
[[nodiscard]] auto size() const -> std::optional<std::uint64_t> override;
|
||||
|
||||
[[nodiscard]] auto truncate(std::size_t size) -> bool override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
write(const unsigned char *data, std::size_t to_write, std::size_t offset,
|
||||
std::size_t *total_written = nullptr) -> bool override;
|
||||
|
||||
public:
|
||||
auto operator=(const file &) noexcept -> file & = delete;
|
||||
|
||||
auto operator=(file &&move_file) noexcept -> file & {
|
||||
if (&move_file != this) {
|
||||
file_ = std::move(move_file.file_);
|
||||
path_ = std::move(move_file.path_);
|
||||
read_only_ = move_file.read_only_;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] operator bool() const override { return file_ != nullptr; }
|
||||
};
|
||||
|
||||
// INFO: has test
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto file_exists_in_path_t(
|
||||
std::basic_string_view<typename string_t::value_type> path,
|
||||
std::basic_string_view<typename string_t::value_type> file_name) -> bool {
|
||||
return file(utils::path::combine(path, {file_name})).exists();
|
||||
}
|
||||
|
||||
// INFO: has test
|
||||
inline auto file_exists_in_path(std::string_view path,
|
||||
std::string_view file_name) -> bool {
|
||||
return file_exists_in_path_t<std::string>(path, file_name);
|
||||
}
|
||||
|
||||
// INFO: has test
|
||||
inline auto file_exists_in_path(std::wstring_view path,
|
||||
std::wstring_view file_name) -> bool {
|
||||
return file_exists_in_path_t<std::wstring>(path, file_name);
|
||||
}
|
||||
} // namespace monitarr::utils::file
|
||||
|
||||
#endif // MONITARR_INCLUDE_UTILS_FILE_FILE_HPP_
|
||||
140
support/include/utils/file_smb_directory.hpp
Normal file
140
support/include/utils/file_smb_directory.hpp
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_FILE_SMB_DIRECTORY_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_FILE_SMB_DIRECTORY_HPP_
|
||||
#if defined(PROJECT_ENABLE_LIBDSM)
|
||||
|
||||
#include "utils/file.hpp"
|
||||
|
||||
namespace monitarr::utils::file {
|
||||
class smb_directory final : public i_directory {
|
||||
public:
|
||||
using smb_directory_t = std::unique_ptr<smb_directory>;
|
||||
|
||||
[[nodiscard]] static auto
|
||||
open(std::string_view host, std::string_view user, std::string_view password,
|
||||
std::string_view path,
|
||||
stop_type *stop_requested = nullptr) -> smb_directory_t;
|
||||
|
||||
[[nodiscard]] static auto
|
||||
open(std::wstring_view host, std::wstring_view user,
|
||||
std::wstring_view password, std::wstring_view path,
|
||||
stop_type *stop_requested = nullptr) -> smb_directory_t;
|
||||
|
||||
public:
|
||||
smb_directory() noexcept = default;
|
||||
|
||||
smb_directory(const smb_directory &) noexcept = delete;
|
||||
|
||||
smb_directory(smb_directory &&) noexcept = default;
|
||||
|
||||
~smb_directory() override = default;
|
||||
|
||||
private:
|
||||
smb_directory(std::string path, smb_session_t session,
|
||||
std::string_view share_name, smb_tid tid,
|
||||
stop_type *stop_requested)
|
||||
: path_(std::move(path)),
|
||||
session_(std::move(session)),
|
||||
share_name_(share_name),
|
||||
tid_(tid),
|
||||
stop_requested_(stop_requested) {}
|
||||
|
||||
private:
|
||||
std::string path_{};
|
||||
smb_session_t session_{};
|
||||
std::string share_name_{};
|
||||
smb_tid tid_{};
|
||||
stop_type *stop_requested_{nullptr};
|
||||
|
||||
public:
|
||||
[[nodiscard]] auto
|
||||
count(bool recursive = false) const -> std::uint64_t override;
|
||||
|
||||
[[nodiscard]] auto copy_to(std::string_view new_path,
|
||||
bool overwrite) const -> bool override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
create_directory(std::string_view path = "") const -> fs_directory_t override;
|
||||
|
||||
[[nodiscard]] auto create_file(std::string_view file_name,
|
||||
bool read_only) const -> fs_file_t override;
|
||||
|
||||
[[nodiscard]] auto exists() const -> bool override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
get_directory(std::string_view path) const -> fs_directory_t override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
get_directories() const -> std::vector<fs_directory_t> override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
get_file(std::string_view path) const -> fs_file_t override;
|
||||
|
||||
[[nodiscard]] auto get_files() const -> std::vector<fs_file_t> override;
|
||||
|
||||
[[nodiscard]] auto get_items() const -> std::vector<fs_item_t> override;
|
||||
|
||||
[[nodiscard]] auto get_path() const -> std::string override { return path_; }
|
||||
|
||||
[[nodiscard]] auto
|
||||
get_time(time_type type) const -> std::optional<std::uint64_t> override;
|
||||
|
||||
[[nodiscard]] auto get_unc_path() const -> std::string {
|
||||
return smb_get_unc_path(path_);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_uri_path() const -> std::string {
|
||||
return smb_get_uri_path(path_);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto
|
||||
get_uri_path(std::string_view user,
|
||||
std::string_view password) const -> std::string {
|
||||
return smb_get_uri_path(path_, user, password);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_stop_requested() const -> bool override;
|
||||
|
||||
[[nodiscard]] auto is_symlink() const -> bool override;
|
||||
|
||||
[[nodiscard]] auto move_to(std::string_view new_path) -> bool override;
|
||||
|
||||
[[nodiscard]] auto remove() -> bool override;
|
||||
|
||||
[[nodiscard]] auto remove_recursively() -> bool override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
size(bool recursive = false) const -> std::uint64_t override;
|
||||
|
||||
public:
|
||||
auto operator=(const smb_directory &) noexcept -> smb_directory & = delete;
|
||||
|
||||
auto
|
||||
operator=(smb_directory &&move_dir) noexcept -> smb_directory & = default;
|
||||
|
||||
[[nodiscard]] operator bool() const override { return session_ != nullptr; }
|
||||
};
|
||||
} // namespace monitarr::utils::file
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBDSM)
|
||||
#endif // MONITARR_INCLUDE_UTILS_FILE_SMB_DIRECTORY_HPP_
|
||||
157
support/include/utils/file_smb_file.hpp
Normal file
157
support/include/utils/file_smb_file.hpp
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_FILE_SMB_FILE_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_FILE_SMB_FILE_HPP_
|
||||
#if defined(PROJECT_ENABLE_LIBDSM)
|
||||
|
||||
#include "utils/file.hpp"
|
||||
|
||||
namespace monitarr::utils::file {
|
||||
class smb_file final : public i_file {
|
||||
public:
|
||||
smb_file() = default;
|
||||
|
||||
smb_file(std::optional<smb_fd> fd, std::string path, smb_session_t session,
|
||||
std::string_view share_name, smb_tid tid)
|
||||
: fd_(std::move(fd)),
|
||||
path_(std::move(path)),
|
||||
session_(std::move(session)),
|
||||
share_name_(share_name),
|
||||
tid_(tid) {}
|
||||
|
||||
smb_file(const smb_file &) = delete;
|
||||
|
||||
smb_file(smb_file &&f) noexcept
|
||||
: fd_(std::move(f.fd_)),
|
||||
path_(std::move(f.path_)),
|
||||
read_buffer_size(f.get_read_buffer_size()),
|
||||
read_only_(f.read_only_),
|
||||
session_(std::move(f.session_)),
|
||||
share_name_(std::move(f.share_name_)),
|
||||
tid_(f.tid_) {}
|
||||
|
||||
~smb_file() override { close(); }
|
||||
|
||||
private:
|
||||
std::optional<smb_fd> fd_;
|
||||
std::string path_;
|
||||
std::atomic_uint32_t read_buffer_size{65536U};
|
||||
bool read_only_;
|
||||
smb_session_t session_;
|
||||
std::string share_name_;
|
||||
smb_tid tid_;
|
||||
|
||||
public:
|
||||
void close() override;
|
||||
|
||||
[[nodiscard]] auto copy_to(std::string_view new_path,
|
||||
bool overwrite) const -> bool override;
|
||||
|
||||
[[nodiscard]] auto exists() const -> bool override;
|
||||
|
||||
void flush() const override;
|
||||
|
||||
[[nodiscard]] auto get_handle() const -> native_handle override {
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_path() const -> std::string override { return path_; }
|
||||
|
||||
[[nodiscard]] auto get_read_buffer_size() const -> std::uint32_t override {
|
||||
return read_buffer_size;
|
||||
}
|
||||
|
||||
[[nodiscard]] static auto
|
||||
get_time(smb_session *session, smb_tid tid, std::string path,
|
||||
time_type type) -> std::optional<std::uint64_t>;
|
||||
|
||||
[[nodiscard]] auto
|
||||
get_time(time_type type) const -> std::optional<std::uint64_t> override {
|
||||
return get_time(session_.get(), tid_, path_, type);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_unc_path() const -> std::string {
|
||||
return smb_get_unc_path(path_);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_uri_path() const -> std::string {
|
||||
return smb_get_uri_path(path_);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto
|
||||
get_uri_path(std::string_view user,
|
||||
std::string_view password) const -> std::string {
|
||||
return smb_get_uri_path(path_, user, password);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_read_only() const -> bool override {
|
||||
return read_only_;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_symlink() const -> bool override;
|
||||
|
||||
[[nodiscard]] auto move_to(std::string_view new_path) -> bool override;
|
||||
|
||||
[[nodiscard]] auto open(bool read_only) -> bool;
|
||||
|
||||
[[nodiscard]] auto read(unsigned char *data, std::size_t to_read,
|
||||
std::uint64_t offset,
|
||||
std::size_t *total_read = nullptr) -> bool override;
|
||||
|
||||
[[nodiscard]] auto remove() -> bool override;
|
||||
|
||||
auto set_read_buffer_size(std::uint32_t size) -> std::uint32_t override {
|
||||
read_buffer_size = size;
|
||||
return read_buffer_size;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto size() const -> std::optional<std::uint64_t> override;
|
||||
|
||||
[[nodiscard]] auto truncate(std::size_t size) -> bool override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
write(const unsigned char *data, std::size_t to_write, std::size_t offset,
|
||||
std::size_t *total_written = nullptr) -> bool override;
|
||||
|
||||
public:
|
||||
auto operator=(const smb_file &) noexcept -> smb_file & = delete;
|
||||
|
||||
auto operator=(smb_file &&move_file) noexcept -> smb_file & {
|
||||
if (this != &move_file) {
|
||||
fd_ = std::move(move_file.fd_);
|
||||
path_ = std::move(move_file.path_);
|
||||
read_buffer_size = move_file.get_read_buffer_size();
|
||||
read_only_ = move_file.read_only_;
|
||||
session_ = std::move(move_file.session_);
|
||||
share_name_ = std::move(move_file.share_name_);
|
||||
tid_ = move_file.tid_;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] operator bool() const override { return fd_.has_value(); }
|
||||
};
|
||||
} // namespace monitarr::utils::file
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBDSM)
|
||||
#endif // MONITARR_INCLUDE_UTILS_FILE_SMB_FILE_HPP_
|
||||
180
support/include/utils/file_thread_file.hpp
Normal file
180
support/include/utils/file_thread_file.hpp
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_FILE_THREAD_FILE_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_FILE_THREAD_FILE_HPP_
|
||||
|
||||
#include "utils/file.hpp"
|
||||
|
||||
namespace monitarr::utils::file {
|
||||
class thread_file final : public i_file {
|
||||
public:
|
||||
// [[nodiscard]] static auto
|
||||
// attach_file(native_handle handle,
|
||||
// bool read_only = false) -> fs_file_t;
|
||||
|
||||
[[nodiscard]] static auto attach_file(fs_file_t file) -> fs_file_t;
|
||||
|
||||
[[nodiscard]] static auto open_file(std::string_view path,
|
||||
bool read_only = false) -> fs_file_t;
|
||||
|
||||
[[nodiscard]] static auto open_file(std::wstring_view path,
|
||||
bool read_only = false) -> fs_file_t {
|
||||
return open_file(utils::string::to_utf8(path), read_only);
|
||||
}
|
||||
|
||||
[[nodiscard]] static auto
|
||||
open_or_create_file(std::string_view path,
|
||||
bool read_only = false) -> fs_file_t;
|
||||
|
||||
[[nodiscard]] static auto
|
||||
open_or_create_file(std::wstring_view path,
|
||||
bool read_only = false) -> fs_file_t {
|
||||
return open_or_create_file(utils::string::to_utf8(path), read_only);
|
||||
}
|
||||
|
||||
public:
|
||||
thread_file() noexcept = default;
|
||||
|
||||
thread_file(std::string_view path);
|
||||
|
||||
thread_file(std::wstring_view path);
|
||||
|
||||
protected:
|
||||
thread_file(fs_file_t file);
|
||||
|
||||
public:
|
||||
thread_file(const thread_file &) = delete;
|
||||
|
||||
thread_file(thread_file &&move_file) noexcept
|
||||
: file_(std::move(move_file.file_)) {}
|
||||
|
||||
~thread_file() override;
|
||||
|
||||
private:
|
||||
using action_t = std::function<bool()>;
|
||||
|
||||
struct io_item final {
|
||||
action_t action;
|
||||
bool complete{false};
|
||||
std::unique_ptr<std::mutex> mtx{
|
||||
std::make_unique<std::mutex>(),
|
||||
};
|
||||
mutable std::unique_ptr<std::condition_variable> notify{
|
||||
std::make_unique<std::condition_variable>(),
|
||||
};
|
||||
bool success{false};
|
||||
|
||||
void done(bool result);
|
||||
|
||||
void wait() const;
|
||||
};
|
||||
|
||||
private:
|
||||
fs_file_t file_;
|
||||
|
||||
private:
|
||||
mutable std::vector<std::shared_ptr<io_item>> actions_;
|
||||
mutable std::unique_ptr<std::thread> io_thread_;
|
||||
mutable std::unique_ptr<std::mutex> mtx_{std::make_unique<std::mutex>()};
|
||||
mutable std::unique_ptr<std::condition_variable> notify_{
|
||||
std::make_unique<std::condition_variable>(),
|
||||
};
|
||||
stop_type stop_requested_{false};
|
||||
|
||||
private:
|
||||
auto do_io(action_t action) const -> bool;
|
||||
|
||||
void thread_func() const;
|
||||
|
||||
public:
|
||||
void close() override;
|
||||
|
||||
[[nodiscard]] auto copy_to(std::string_view new_path,
|
||||
bool overwrite) const -> bool override;
|
||||
|
||||
[[nodiscard]] auto exists() const -> bool override { return file_->exists(); }
|
||||
|
||||
void flush() const override;
|
||||
|
||||
[[nodiscard]] auto get_handle() const -> native_handle override {
|
||||
return file_->get_handle();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_path() const -> std::string override {
|
||||
return file_->get_path();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_read_buffer_size() const -> std::uint32_t override {
|
||||
return file_->get_read_buffer_size();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto
|
||||
get_time(time_type type) const -> std::optional<std::uint64_t> override {
|
||||
return file_->get_time(type);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_read_only() const -> bool override {
|
||||
return file_->is_read_only();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_symlink() const -> bool override {
|
||||
return file_->is_symlink();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto move_to(std::string_view new_path) -> bool override;
|
||||
|
||||
[[nodiscard]] auto read(unsigned char *data, std::size_t to_read,
|
||||
std::uint64_t offset,
|
||||
std::size_t *total_read = nullptr) -> bool override;
|
||||
|
||||
[[nodiscard]] auto remove() -> bool override;
|
||||
|
||||
auto set_read_buffer_size(std::uint32_t size) -> std::uint32_t override {
|
||||
return file_->set_read_buffer_size(size);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto size() const -> std::optional<std::uint64_t> override;
|
||||
|
||||
[[nodiscard]] auto truncate(std::size_t size) -> bool override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
write(const unsigned char *data, std::size_t to_write, std::size_t offset,
|
||||
std::size_t *total_written = nullptr) -> bool override;
|
||||
|
||||
public:
|
||||
[[nodiscard]] operator bool() const override {
|
||||
return static_cast<bool>(*file_);
|
||||
}
|
||||
|
||||
auto operator=(const thread_file &) noexcept -> thread_file & = delete;
|
||||
|
||||
auto operator=(thread_file &&move_file) noexcept -> thread_file & {
|
||||
if (&move_file != this) {
|
||||
file_ = std::move(move_file.file_);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
} // namespace monitarr::utils::file
|
||||
|
||||
#endif // MONITARR_INCLUDE_UTILS_FILE_THREAD_FILE_HPP_
|
||||
181
support/include/utils/hash.hpp
Normal file
181
support/include/utils/hash.hpp
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_HASH_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_HASH_HPP_
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
#include "utils/error.hpp"
|
||||
|
||||
namespace monitarr::utils::encryption {
|
||||
using hash_256_t = std::array<unsigned char, 32U>;
|
||||
using hash_384_t = std::array<unsigned char, 48U>;
|
||||
using hash_512_t = std::array<unsigned char, 64U>;
|
||||
|
||||
[[nodiscard]] auto create_hash_blake2b_256(std::string_view data) -> hash_256_t;
|
||||
|
||||
[[nodiscard]] auto
|
||||
create_hash_blake2b_256(std::wstring_view data) -> hash_256_t;
|
||||
|
||||
[[nodiscard]] auto
|
||||
create_hash_blake2b_256(const data_buffer &data) -> hash_256_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_blake2b_384(std::string_view data) -> hash_384_t;
|
||||
|
||||
[[nodiscard]] auto
|
||||
create_hash_blake2b_384(std::wstring_view data) -> hash_384_t;
|
||||
|
||||
[[nodiscard]] auto
|
||||
create_hash_blake2b_384(const data_buffer &data) -> hash_384_t;
|
||||
|
||||
[[nodiscard]] auto
|
||||
create_hash_blake2b_512(std::wstring_view data) -> hash_512_t;
|
||||
|
||||
[[nodiscard]] auto
|
||||
create_hash_blake2b_512(const data_buffer &data) -> hash_512_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_blake2b_512(std::string_view data) -> hash_512_t;
|
||||
|
||||
template <typename hash_t>
|
||||
[[nodiscard]] auto create_hash_blake2b_t(const unsigned char *data,
|
||||
std::size_t data_size) -> hash_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_sha256(std::string_view data) -> hash_256_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_sha256(std::wstring_view data) -> hash_256_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_sha256(const data_buffer &data) -> hash_256_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_sha256(const unsigned char *data,
|
||||
std::size_t data_size) -> hash_256_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_sha512(std::string_view data) -> hash_512_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_sha512(std::wstring_view data) -> hash_512_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_sha512(const data_buffer &data) -> hash_512_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_sha512(const unsigned char *data,
|
||||
std::size_t data_size) -> hash_512_t;
|
||||
|
||||
template <typename hash_t>
|
||||
[[nodiscard]] inline auto default_create_hash() -> const
|
||||
std::function<hash_t(const unsigned char *data, std::size_t size)> &;
|
||||
|
||||
template <typename hash_t>
|
||||
auto create_hash_blake2b_t(const unsigned char *data,
|
||||
std::size_t data_size) -> hash_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
hash_t hash{};
|
||||
|
||||
crypto_generichash_blake2b_state state{};
|
||||
auto res = crypto_generichash_blake2b_init(&state, nullptr, 0U, hash.size());
|
||||
if (res != 0) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to initialize blake2b",
|
||||
std::to_string(hash.size() * 8U),
|
||||
std::to_string(res),
|
||||
});
|
||||
}
|
||||
|
||||
res = crypto_generichash_blake2b_update(&state, data, data_size);
|
||||
if (res != 0) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to update blake2b",
|
||||
std::to_string(hash.size() * 8U),
|
||||
std::to_string(res),
|
||||
});
|
||||
}
|
||||
|
||||
res = crypto_generichash_blake2b_final(&state, hash.data(), hash.size());
|
||||
if (res != 0) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to finalize blake2b",
|
||||
std::to_string(hash.size() * 8U),
|
||||
std::to_string(res),
|
||||
});
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
inline const std::function<hash_256_t(const unsigned char *data,
|
||||
std::size_t size)>
|
||||
blake2b_256_hasher =
|
||||
[](const unsigned char *data, std::size_t data_size) -> hash_256_t {
|
||||
return create_hash_blake2b_t<hash_256_t>(data, data_size);
|
||||
};
|
||||
|
||||
inline const std::function<hash_384_t(const unsigned char *data,
|
||||
std::size_t size)>
|
||||
blake2b_384_hasher =
|
||||
[](const unsigned char *data, std::size_t data_size) -> hash_384_t {
|
||||
return create_hash_blake2b_t<hash_384_t>(data, data_size);
|
||||
};
|
||||
|
||||
inline const std::function<hash_512_t(const unsigned char *data,
|
||||
std::size_t size)>
|
||||
blake2b_512_hasher =
|
||||
[](const unsigned char *data, std::size_t data_size) -> hash_512_t {
|
||||
return create_hash_blake2b_t<hash_512_t>(data, data_size);
|
||||
};
|
||||
|
||||
inline const std::function<hash_256_t(const unsigned char *data,
|
||||
std::size_t size)>
|
||||
sha256_hasher =
|
||||
[](const unsigned char *data, std::size_t data_size) -> hash_256_t {
|
||||
return create_hash_sha256(data, data_size);
|
||||
};
|
||||
|
||||
inline const std::function<hash_512_t(const unsigned char *data,
|
||||
std::size_t size)>
|
||||
sha512_hasher =
|
||||
[](const unsigned char *data, std::size_t data_size) -> hash_512_t {
|
||||
return create_hash_sha512(data, data_size);
|
||||
};
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline auto default_create_hash<hash_256_t>() -> const
|
||||
std::function<hash_256_t(const unsigned char *data, std::size_t size)> & {
|
||||
return blake2b_256_hasher;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline auto default_create_hash<hash_384_t>() -> const
|
||||
std::function<hash_384_t(const unsigned char *data, std::size_t size)> & {
|
||||
return blake2b_384_hasher;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline auto default_create_hash<hash_512_t>() -> const
|
||||
std::function<hash_512_t(const unsigned char *data, std::size_t size)> & {
|
||||
return blake2b_512_hasher;
|
||||
}
|
||||
} // namespace monitarr::utils::encryption
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
#endif // MONITARR_INCLUDE_UTILS_HASH_HPP_
|
||||
526
support/include/utils/path.hpp
Normal file
526
support/include/utils/path.hpp
Normal file
@@ -0,0 +1,526 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_PATH_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_PATH_HPP_
|
||||
|
||||
#include "utils/config.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace monitarr::utils::path {
|
||||
inline constexpr const std::string_view backslash{"\\"};
|
||||
inline constexpr const std::wstring_view backslash_w{L"\\"};
|
||||
inline constexpr const std::string_view dot{"."};
|
||||
inline constexpr const std::wstring_view dot_w{L"."};
|
||||
inline constexpr const std::string_view dot_backslash{".\\"};
|
||||
inline constexpr const std::wstring_view dot_backslash_w{L".\\"};
|
||||
inline constexpr const std::string_view dot_slash{"./"};
|
||||
inline constexpr const std::wstring_view dot_slash_w{L"./"};
|
||||
inline constexpr const std::string_view long_notation{"\\\\?\\"};
|
||||
inline constexpr const std::wstring_view long_notation_w{L"\\\\?\\"};
|
||||
inline constexpr const std::string_view slash{"/"};
|
||||
inline constexpr const std::wstring_view slash_w{L"/"};
|
||||
#if defined(_WIN32)
|
||||
inline constexpr const std::string_view directory_seperator{backslash};
|
||||
inline constexpr const std::wstring_view directory_seperator_w{backslash_w};
|
||||
inline constexpr const std::string_view not_directory_seperator{slash};
|
||||
inline constexpr const std::wstring_view not_directory_seperator_w{slash_w};
|
||||
inline constexpr const std::string_view unc_notation{"\\\\"};
|
||||
inline constexpr const std::wstring_view unc_notation_w{L"\\\\"};
|
||||
#else // !defined(_WIN32)
|
||||
inline constexpr const std::string_view directory_seperator{slash};
|
||||
inline constexpr const std::wstring_view directory_seperator_w{slash_w};
|
||||
inline constexpr const std::string_view not_directory_seperator{backslash};
|
||||
inline constexpr const std::wstring_view not_directory_seperator_w{backslash_w};
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
template <typename char_t>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_backslash() -> std::basic_string_view<char_t>;
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_backslash<char>() -> std::basic_string_view<char> {
|
||||
return backslash;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_backslash<wchar_t>() -> std::basic_string_view<wchar_t> {
|
||||
return backslash_w;
|
||||
}
|
||||
|
||||
template <typename char_t>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_directory_seperator() -> std::basic_string_view<char_t>;
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_directory_seperator<char>() -> std::basic_string_view<char> {
|
||||
return directory_seperator;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_directory_seperator<wchar_t>() -> std::basic_string_view<wchar_t> {
|
||||
return directory_seperator_w;
|
||||
}
|
||||
|
||||
template <typename char_t>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_not_directory_seperator() -> std::basic_string_view<char_t>;
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_not_directory_seperator<char>() -> std::basic_string_view<char> {
|
||||
return not_directory_seperator;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_not_directory_seperator<wchar_t>() -> std::basic_string_view<wchar_t> {
|
||||
return not_directory_seperator_w;
|
||||
}
|
||||
|
||||
template <typename char_t>
|
||||
[[nodiscard]] inline constexpr auto get_dot() -> std::basic_string_view<char_t>;
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_dot<char>() -> std::basic_string_view<char> {
|
||||
return dot;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_dot<wchar_t>() -> std::basic_string_view<wchar_t> {
|
||||
return dot_w;
|
||||
}
|
||||
|
||||
template <typename char_t>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_dot_backslash() -> std::basic_string_view<char_t>;
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_dot_backslash<char>() -> std::basic_string_view<char> {
|
||||
return dot_backslash;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_dot_backslash<wchar_t>() -> std::basic_string_view<wchar_t> {
|
||||
return dot_backslash_w;
|
||||
}
|
||||
|
||||
template <typename char_t>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_dot_slash() -> std::basic_string_view<char_t>;
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_dot_slash<char>() -> std::basic_string_view<char> {
|
||||
return dot_slash;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_dot_slash<wchar_t>() -> std::basic_string_view<wchar_t> {
|
||||
return dot_slash_w;
|
||||
}
|
||||
|
||||
template <typename char_t>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_long_notation() -> std::basic_string_view<char_t>;
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_long_notation<char>() -> std::basic_string_view<char> {
|
||||
return long_notation;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_long_notation<wchar_t>() -> std::basic_string_view<wchar_t> {
|
||||
return long_notation_w;
|
||||
}
|
||||
|
||||
template <typename char_t>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_slash() -> std::basic_string_view<char_t>;
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_slash<char>() -> std::basic_string_view<char> {
|
||||
return slash;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_slash<wchar_t>() -> std::basic_string_view<wchar_t> {
|
||||
return slash_w;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
template <typename char_t>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_unc_notation() -> std::basic_string_view<char_t>;
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_unc_notation<char>() -> std::basic_string_view<char> {
|
||||
return unc_notation;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline constexpr auto
|
||||
get_unc_notation<wchar_t>() -> std::basic_string_view<wchar_t> {
|
||||
return unc_notation_w;
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto get_current_path() -> string_t;
|
||||
|
||||
[[nodiscard]] auto absolute(std::string_view path) -> std::string;
|
||||
|
||||
[[nodiscard]] auto absolute(std::wstring_view path) -> std::wstring;
|
||||
|
||||
[[nodiscard]] inline auto
|
||||
combine(std::string_view path,
|
||||
const std::vector<std::string_view> &paths) -> std::string;
|
||||
|
||||
[[nodiscard]] inline auto
|
||||
combine(std::wstring_view path,
|
||||
const std::vector<std::wstring_view> &paths) -> std::wstring;
|
||||
|
||||
[[nodiscard]] auto contains_trash_directory(std::string_view path) -> bool;
|
||||
|
||||
[[nodiscard]] auto contains_trash_directory(std::wstring_view path) -> bool;
|
||||
|
||||
[[nodiscard]] auto inline create_api_path(std::string_view path) -> std::string;
|
||||
|
||||
[[nodiscard]] auto inline create_api_path(std::wstring_view path)
|
||||
-> std::wstring;
|
||||
|
||||
[[nodiscard]] auto exists(std::string_view path) -> bool;
|
||||
|
||||
[[nodiscard]] auto exists(std::wstring_view path) -> bool;
|
||||
|
||||
[[nodiscard]] inline auto finalize(std::string_view path) -> std::string;
|
||||
|
||||
[[nodiscard]] inline auto finalize(std::wstring_view path) -> std::wstring;
|
||||
|
||||
[[nodiscard]] auto
|
||||
find_program_in_path(const std::string &name_without_extension) -> std::string;
|
||||
|
||||
[[nodiscard]] auto
|
||||
find_program_in_path(std::wstring_view name_without_extension) -> std::wstring;
|
||||
|
||||
template <typename string_t>
|
||||
inline auto
|
||||
format_path(string_t &path,
|
||||
std::basic_string_view<typename string_t::value_type> sep,
|
||||
std::basic_string_view<typename string_t::value_type> not_sep)
|
||||
-> string_t &;
|
||||
|
||||
[[nodiscard]] inline auto
|
||||
get_parent_api_path(std::string_view path) -> std::string;
|
||||
|
||||
[[nodiscard]] inline auto
|
||||
get_parent_api_path(std::wstring_view path) -> std::wstring;
|
||||
|
||||
[[nodiscard]] auto get_parent_path(std::string_view path) -> std::string;
|
||||
|
||||
[[nodiscard]] auto get_parent_path(std::wstring_view path) -> std::wstring;
|
||||
|
||||
[[nodiscard]] inline auto
|
||||
get_parts(std::string_view path) -> std::vector<std::string>;
|
||||
|
||||
[[nodiscard]] inline auto
|
||||
get_parts_w(std::wstring_view path) -> std::vector<std::wstring>;
|
||||
|
||||
[[nodiscard]] auto get_relative_path(std::string_view path,
|
||||
std::string_view root_path) -> std::string;
|
||||
|
||||
[[nodiscard]] auto
|
||||
get_relative_path(std::wstring_view path,
|
||||
std::wstring_view root_path) -> std::wstring;
|
||||
|
||||
[[nodiscard]] auto make_file_uri(std::string_view path) -> std::string;
|
||||
|
||||
[[nodiscard]] auto make_file_uri(std::wstring_view path) -> std::wstring;
|
||||
|
||||
[[nodiscard]] auto strip_to_file_name(std::string path) -> std::string;
|
||||
|
||||
[[nodiscard]] auto strip_to_file_name(std::wstring path) -> std::wstring;
|
||||
|
||||
[[nodiscard]] auto unmake_file_uri(std::string_view uri) -> std::string;
|
||||
|
||||
[[nodiscard]] auto unmake_file_uri(std::wstring_view uri) -> std::wstring;
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto combine_t(
|
||||
std::basic_string_view<typename string_t::value_type> path,
|
||||
const std::vector<std::basic_string_view<typename string_t::value_type>>
|
||||
&paths) -> string_t {
|
||||
auto dir_sep_t =
|
||||
string_t{get_directory_seperator<typename string_t::value_type>()};
|
||||
return absolute(
|
||||
std::accumulate(paths.begin(), paths.end(),
|
||||
std::basic_string<typename string_t::value_type>{path},
|
||||
[&dir_sep_t](auto next_path, auto &&path_part) {
|
||||
if (next_path.empty()) {
|
||||
return string_t{path_part};
|
||||
}
|
||||
|
||||
return next_path + dir_sep_t + string_t{path_part};
|
||||
}));
|
||||
}
|
||||
|
||||
inline auto combine(std::string_view path,
|
||||
const std::vector<std::string_view> &paths) -> std::string {
|
||||
return combine_t<std::string>(path, paths);
|
||||
}
|
||||
|
||||
inline auto
|
||||
combine(std::wstring_view path,
|
||||
const std::vector<std::wstring_view> &paths) -> std::wstring {
|
||||
return combine_t<std::wstring>(path, paths);
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto create_api_path_t(
|
||||
std::basic_string_view<typename string_t::value_type> path) -> string_t {
|
||||
auto backslash_t = get_backslash<typename string_t::value_type>();
|
||||
auto dot_backslash_t = get_dot_backslash<typename string_t::value_type>();
|
||||
auto dot_slash_t = get_dot_slash<typename string_t::value_type>();
|
||||
auto dot_t = get_dot<typename string_t::value_type>();
|
||||
auto slash_t = get_slash<typename string_t::value_type>();
|
||||
|
||||
#if defined(_WIN32)
|
||||
auto long_notation_t = get_long_notation<typename string_t::value_type>();
|
||||
if (utils::string::begins_with(path, long_notation_t)) {
|
||||
path = path.substr(long_notation_t.size());
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
if (path.empty() || path == backslash_t || path == dot_t ||
|
||||
path == dot_slash_t || path == slash_t || path == dot_backslash_t) {
|
||||
return string_t{slash_t};
|
||||
}
|
||||
|
||||
string_t api_path{path};
|
||||
#if defined(_WIN32)
|
||||
if ((api_path.size() >= 2U) && (api_path.at(1U) == ':')) {
|
||||
api_path = api_path.substr(2U);
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
format_path(api_path, slash_t, backslash_t);
|
||||
|
||||
while (utils::string::begins_with(api_path, dot_slash_t)) {
|
||||
api_path = api_path.substr(dot_slash_t.size());
|
||||
}
|
||||
|
||||
if (api_path.at(0U) != slash_t.at(0U)) {
|
||||
return string_t{slash_t} + api_path;
|
||||
}
|
||||
|
||||
return api_path;
|
||||
}
|
||||
|
||||
inline auto create_api_path(std::string_view path) -> std::string {
|
||||
return create_api_path_t<std::string>(path);
|
||||
}
|
||||
|
||||
inline auto create_api_path(std::wstring_view path) -> std::wstring {
|
||||
return create_api_path_t<std::wstring>(path);
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto finalize_t(
|
||||
std::basic_string_view<typename string_t::value_type> path) -> string_t {
|
||||
string_t dir_sep_t{get_directory_seperator<typename string_t::value_type>()};
|
||||
string_t fmt_path{path};
|
||||
if (fmt_path.empty()) {
|
||||
return fmt_path;
|
||||
}
|
||||
|
||||
format_path(fmt_path, dir_sep_t,
|
||||
get_not_directory_seperator<typename string_t::value_type>());
|
||||
|
||||
#if defined(_WIN32)
|
||||
auto unc_notation_t = get_unc_notation<typename string_t::value_type>();
|
||||
if (utils::string::begins_with(fmt_path, unc_notation_t)) {
|
||||
return fmt_path;
|
||||
}
|
||||
|
||||
auto dot_t = get_dot<typename string_t::value_type>();
|
||||
auto dot_sep_t = string_t{dot_t} + dir_sep_t;
|
||||
if (fmt_path == dot_t || fmt_path == dot_sep_t) {
|
||||
return get_current_path<string_t>();
|
||||
}
|
||||
|
||||
if (fmt_path == dir_sep_t) {
|
||||
#if defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES)
|
||||
return get_current_path<string_t>().substr(0U, long_notation.size() + 2U);
|
||||
#else // !defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES)
|
||||
return get_current_path<string_t>().substr(0U, 2U);
|
||||
#endif // defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES)
|
||||
}
|
||||
|
||||
if (utils::string::begins_with(fmt_path, dir_sep_t)) {
|
||||
#if defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES)
|
||||
return get_current_path<string_t>().substr(0U, long_notation.size() + 2U) +
|
||||
fmt_path;
|
||||
#else // !defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES)
|
||||
return get_current_path<string_t>().substr(0U, 2U) + fmt_path;
|
||||
#endif // defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES)
|
||||
}
|
||||
|
||||
if (utils::string::begins_with(fmt_path, dot_sep_t)) {
|
||||
return get_current_path<string_t>() + dir_sep_t + fmt_path.substr(2U);
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES)
|
||||
return string_t{get_long_notation<typename string_t::value_type>()} +
|
||||
fmt_path;
|
||||
#endif // defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES)
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
return fmt_path;
|
||||
}
|
||||
|
||||
inline auto finalize(std::string_view path) -> std::string {
|
||||
return finalize_t<std::string>(path);
|
||||
}
|
||||
|
||||
inline auto finalize(std::wstring_view path) -> std::wstring {
|
||||
return finalize_t<std::wstring>(path);
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
inline auto
|
||||
format_path(string_t &path,
|
||||
std::basic_string_view<typename string_t::value_type> sep,
|
||||
std::basic_string_view<typename string_t::value_type> not_sep)
|
||||
-> string_t & {
|
||||
utils::string::replace(path, not_sep, sep);
|
||||
|
||||
#if defined(_WIN32)
|
||||
auto is_unc{false};
|
||||
auto long_notation_t = get_long_notation<typename string_t::value_type>();
|
||||
auto unc_notation_t = get_unc_notation<typename string_t::value_type>();
|
||||
if (utils::string::begins_with(path, long_notation_t)) {
|
||||
path = path.substr(long_notation_t.size());
|
||||
} else if (utils::string::begins_with(path, unc_notation_t)) {
|
||||
path = path.substr(unc_notation_t.size());
|
||||
utils::string::left_trim(path, sep.at(0U));
|
||||
is_unc = true;
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
string_t double_sep(2U, sep.at(0U));
|
||||
while (utils::string::contains(path, double_sep)) {
|
||||
utils::string::replace(path, double_sep, sep);
|
||||
}
|
||||
|
||||
if (path != sep) {
|
||||
utils::string::right_trim(path, sep.at(0U));
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
if (is_unc) {
|
||||
path = string_t{unc_notation_t} + path;
|
||||
} else if ((path.size() >= 2U) && (path.at(1U) == ':')) {
|
||||
path[0U] = utils::string::to_lower(string_t(1U, path.at(0U))).at(0U);
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline auto get_current_path<std::string>() -> std::string {
|
||||
#if defined(_WIN32)
|
||||
std::string path;
|
||||
path.resize(monitarr::max_path_length + 1U);
|
||||
::GetCurrentDirectoryA(static_cast<DWORD>(path.size()), path.data());
|
||||
path = path.c_str();
|
||||
return finalize(path);
|
||||
#else // !defined(_WIN32)
|
||||
return finalize(std::filesystem::current_path().string());
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline auto get_current_path<std::wstring>() -> std::wstring {
|
||||
return utils::string::from_utf8(get_current_path<std::string>());
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto get_parent_api_path_t(
|
||||
std::basic_string_view<typename string_t::value_type> path) -> string_t {
|
||||
auto slash_t = get_slash<typename string_t::value_type>();
|
||||
|
||||
string_t ret{path};
|
||||
utils::string::right_trim(ret, slash_t.at(0U));
|
||||
|
||||
if (ret == slash_t || ret.empty()) {
|
||||
return string_t{path};
|
||||
}
|
||||
|
||||
auto sub_path = ret.substr(0, ret.rfind(slash_t) + 1);
|
||||
if (sub_path == slash_t) {
|
||||
return string_t{sub_path};
|
||||
}
|
||||
|
||||
return sub_path;
|
||||
}
|
||||
|
||||
inline auto get_parent_api_path(std::string_view path) -> std::string {
|
||||
return create_api_path(get_parent_api_path_t<std::string>(path));
|
||||
}
|
||||
|
||||
inline auto get_parent_api_path(std::wstring_view path) -> std::wstring {
|
||||
return create_api_path(get_parent_api_path_t<std::wstring>(path));
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto
|
||||
get_parts_t(std::basic_string_view<typename string_t::value_type> path)
|
||||
-> std::vector<string_t> {
|
||||
return utils::string::split(
|
||||
path, get_directory_seperator<typename string_t::value_type>().at(0U),
|
||||
false);
|
||||
}
|
||||
|
||||
inline auto get_parts(std::string_view path) -> std::vector<std::string> {
|
||||
return get_parts_t<std::string>(path);
|
||||
}
|
||||
|
||||
inline auto get_parts_w(std::wstring_view path) -> std::vector<std::wstring> {
|
||||
return get_parts_t<std::wstring>(path);
|
||||
}
|
||||
} // namespace monitarr::utils::path
|
||||
|
||||
#endif // MONITARR_INCLUDE_UTILS_PATH_HPP_
|
||||
469
support/include/utils/string.hpp
Normal file
469
support/include/utils/string.hpp
Normal file
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_STRING_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_STRING_HPP_
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
namespace monitarr::utils::string {
|
||||
template <typename string_t> struct chain_replace_with_hex;
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto
|
||||
char_to_hex(typename string_t::value_type character) -> string_t;
|
||||
|
||||
[[nodiscard]] inline auto begins_with(std::string_view str,
|
||||
std::string_view val) -> bool;
|
||||
|
||||
[[nodiscard]] inline auto begins_with(std::wstring_view str,
|
||||
std::wstring_view val) -> bool;
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto case_insensitive_find_string(string_t in_str,
|
||||
string_t for_str) ->
|
||||
typename string_t::const_iterator;
|
||||
|
||||
[[nodiscard]] inline auto contains(std::string_view str,
|
||||
std::string_view search) -> bool;
|
||||
|
||||
[[nodiscard]] inline auto contains(std::wstring_view str,
|
||||
std::wstring_view search) -> bool;
|
||||
|
||||
[[nodiscard]] inline auto ends_with(std::string_view str,
|
||||
std::string_view val) -> bool;
|
||||
|
||||
[[nodiscard]] inline auto ends_with(std::wstring_view str,
|
||||
std::wstring_view val) -> bool;
|
||||
|
||||
[[nodiscard]] auto from_bool(bool val) -> std::string;
|
||||
|
||||
#if defined(PROJECT_ENABLE_BOOST)
|
||||
[[nodiscard]] auto
|
||||
from_dynamic_bitset(const boost::dynamic_bitset<> &bitset) -> std::string;
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
[[nodiscard]] auto from_utf8(std::string_view str) -> std::wstring;
|
||||
|
||||
[[nodiscard]] inline auto is_numeric(std::string_view str) -> bool;
|
||||
|
||||
[[nodiscard]] inline auto is_numeric(std::wstring_view str) -> bool;
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto join(const std::vector<string_t> &arr,
|
||||
typename string_t::value_type delim) -> string_t;
|
||||
|
||||
template <typename string_t>
|
||||
auto left_trim(string_t &str,
|
||||
typename string_t::value_type trim_ch = ' ') -> string_t &;
|
||||
|
||||
template <typename string_t>
|
||||
inline auto replace(string_t &src, typename string_t::value_type character,
|
||||
typename string_t::value_type with,
|
||||
std::size_t start_pos = 0U) -> string_t &;
|
||||
|
||||
template <typename string_t>
|
||||
inline auto replace(string_t &src,
|
||||
std::basic_string_view<typename string_t::value_type> find,
|
||||
std::basic_string_view<typename string_t::value_type> with,
|
||||
std::size_t start_pos = 0U) -> string_t &;
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto replace_copy(string_t src,
|
||||
typename string_t::value_type character,
|
||||
typename string_t::value_type with,
|
||||
std::size_t start_pos = 0U) -> string_t;
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto
|
||||
replace_copy(string_t src,
|
||||
std::basic_string_view<typename string_t::value_type> find,
|
||||
std::basic_string_view<typename string_t::value_type> with,
|
||||
std::size_t start_pos = 0U) -> string_t;
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto
|
||||
replace_with_hex(string_t &str, typename string_t::value_type character)
|
||||
-> chain_replace_with_hex<string_t>;
|
||||
|
||||
template <typename string_t>
|
||||
inline auto
|
||||
right_trim(string_t &str,
|
||||
typename string_t::value_type trim_ch = ' ') -> string_t &;
|
||||
|
||||
[[nodiscard]] inline auto split(std::string_view str, char delim,
|
||||
bool should_trim) -> std::vector<std::string>;
|
||||
|
||||
[[nodiscard]] inline auto split(std::wstring_view str, wchar_t delim,
|
||||
bool should_trim) -> std::vector<std::wstring>;
|
||||
|
||||
[[nodiscard]] inline auto split(std::string_view str, std::string_view delim,
|
||||
bool should_trim) -> std::vector<std::string>;
|
||||
|
||||
[[nodiscard]] inline auto split(std::wstring_view str, std::wstring_view delim,
|
||||
bool should_trim) -> std::vector<std::wstring>;
|
||||
|
||||
#if defined(PROJECT_ENABLE_SFML)
|
||||
auto replace_sf(sf::String &src, const sf::String &find, const sf::String &with,
|
||||
std::size_t start_pos = 0U) -> sf::String &;
|
||||
|
||||
[[nodiscard]] auto split_sf(sf::String str, wchar_t delim,
|
||||
bool should_trim) -> std::vector<sf::String>;
|
||||
#endif // defined(PROJECT_ENABLE_SFML)
|
||||
|
||||
[[nodiscard]] auto to_bool(std::string val) -> bool;
|
||||
|
||||
[[nodiscard]] auto to_double(const std::string &str) -> double;
|
||||
|
||||
#if defined(PROJECT_ENABLE_BOOST)
|
||||
[[nodiscard]] auto
|
||||
to_dynamic_bitset(const std::string &val) -> boost::dynamic_bitset<>;
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto to_lower(string_t str) -> string_t;
|
||||
|
||||
[[nodiscard]] auto to_int32(const std::string &val) -> std::int32_t;
|
||||
|
||||
[[nodiscard]] auto to_int64(const std::string &val) -> std::int64_t;
|
||||
|
||||
[[nodiscard]] auto to_size_t(const std::string &val) -> std::size_t;
|
||||
|
||||
[[nodiscard]] auto to_uint8(const std::string &val) -> std::uint8_t;
|
||||
|
||||
[[nodiscard]] auto to_uint16(const std::string &val) -> std::uint16_t;
|
||||
|
||||
[[nodiscard]] auto to_uint32(const std::string &val) -> std::uint32_t;
|
||||
|
||||
[[nodiscard]] auto to_uint64(const std::string &val) -> std::uint64_t;
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto to_upper(string_t str) -> string_t;
|
||||
|
||||
[[nodiscard]] auto to_utf8(std::string_view str) -> std::string;
|
||||
|
||||
[[nodiscard]] auto to_utf8(std::wstring_view str) -> std::string;
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto zero_pad(string_t str, std::size_t count) -> string_t;
|
||||
|
||||
template <typename string_t> struct chain_replace_with_hex final {
|
||||
explicit chain_replace_with_hex(string_t &value) : str(value) {}
|
||||
|
||||
chain_replace_with_hex(const chain_replace_with_hex &) = delete;
|
||||
|
||||
chain_replace_with_hex(chain_replace_with_hex &&) = delete;
|
||||
|
||||
~chain_replace_with_hex() = default;
|
||||
|
||||
auto operator=(const chain_replace_with_hex &) -> chain_replace_with_hex & =
|
||||
delete;
|
||||
|
||||
auto
|
||||
operator=(chain_replace_with_hex &&) -> chain_replace_with_hex & = delete;
|
||||
|
||||
auto operator()(typename string_t::value_type character)
|
||||
-> chain_replace_with_hex {
|
||||
return replace_with_hex<string_t>(str, character);
|
||||
}
|
||||
|
||||
string_t &str;
|
||||
};
|
||||
|
||||
template <typename string_t>
|
||||
inline auto trim(string_t &str,
|
||||
typename string_t::value_type trim_ch = ' ') -> string_t &;
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto
|
||||
trim_copy(string_t str,
|
||||
typename string_t::value_type trim_ch = ' ') -> string_t;
|
||||
|
||||
template <typename char_t>
|
||||
[[nodiscard]] inline auto
|
||||
begins_with_t(std::basic_string_view<char_t> str,
|
||||
std::basic_string_view<char_t> val) -> bool {
|
||||
return (str.find(val) == 0U);
|
||||
}
|
||||
|
||||
inline auto begins_with(std::string_view str, std::string_view val) -> bool {
|
||||
return begins_with_t<std::string_view::value_type>(str, val);
|
||||
}
|
||||
|
||||
inline auto begins_with(std::wstring_view str, std::wstring_view val) -> bool {
|
||||
return begins_with_t<std::wstring_view::value_type>(str, val);
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
inline auto case_insensitive_find_string(string_t in_str, string_t for_str) ->
|
||||
typename string_t::const_iterator {
|
||||
static const auto compare_chars =
|
||||
[](typename string_t::value_type char_a,
|
||||
typename string_t::value_type char_b) -> bool {
|
||||
return (std::tolower(char_a) == std::tolower(char_b));
|
||||
};
|
||||
|
||||
return (std::search(in_str.begin(), in_str.end(), for_str.begin(),
|
||||
for_str.end(), compare_chars));
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
inline auto char_to_hex(typename string_t::value_type character) -> string_t {
|
||||
std::basic_stringstream<typename string_t::value_type> stream;
|
||||
stream << '%' << std::setfill<typename string_t::value_type>('0')
|
||||
<< std::setw(sizeof(character)) << std::hex
|
||||
<< static_cast<std::uint32_t>(character);
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
template <typename char_t>
|
||||
[[nodiscard]] inline auto
|
||||
contains_t(std::basic_string_view<char_t> str,
|
||||
std::basic_string_view<char_t> search) -> bool {
|
||||
return (str.find(search) != std::basic_string_view<char_t>::npos);
|
||||
}
|
||||
|
||||
inline auto contains(std::string_view str, std::string_view search) -> bool {
|
||||
return contains_t<std::string_view::value_type>(str, search);
|
||||
}
|
||||
|
||||
inline auto contains(std::wstring_view str, std::wstring_view search) -> bool {
|
||||
return contains_t<std::wstring_view::value_type>(str, search);
|
||||
}
|
||||
|
||||
template <typename char_t>
|
||||
[[nodiscard]] inline auto
|
||||
ends_with_t(std::basic_string_view<char_t> str,
|
||||
std::basic_string_view<char_t> val) -> bool {
|
||||
if (val.size() > str.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::equal(val.rbegin(), val.rend(), str.rbegin());
|
||||
}
|
||||
|
||||
inline auto ends_with(std::string_view str, std::string_view val) -> bool {
|
||||
return ends_with_t<std::string_view::value_type>(str, val);
|
||||
}
|
||||
|
||||
inline auto ends_with(std::wstring_view str, std::wstring_view val) -> bool {
|
||||
return ends_with_t<std::wstring_view::value_type>(str, val);
|
||||
}
|
||||
|
||||
template <typename char_t>
|
||||
[[nodiscard]] inline auto
|
||||
is_numeric_t(std::basic_string_view<char_t> str) -> bool {
|
||||
if ((str.length() > 1U) && (str.at(0U) == '+' || str.at(0U) == '-')) {
|
||||
str = str.substr(1U);
|
||||
}
|
||||
|
||||
if (str.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto has_decimal{false};
|
||||
return std::find_if(str.begin(), str.end(),
|
||||
[&has_decimal](auto &&cur_ch) -> bool {
|
||||
if (has_decimal && cur_ch == '.') {
|
||||
return true;
|
||||
}
|
||||
|
||||
has_decimal = has_decimal || cur_ch == '.';
|
||||
if (has_decimal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::isdigit(cur_ch) == 0;
|
||||
}) == str.end();
|
||||
}
|
||||
|
||||
inline auto is_numeric(std::string_view str) -> bool {
|
||||
return is_numeric_t<std::string_view::value_type>(str);
|
||||
}
|
||||
|
||||
inline auto is_numeric(std::wstring_view str) -> bool {
|
||||
return is_numeric_t<std::wstring_view::value_type>(str);
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
inline auto join(const std::vector<string_t> &arr,
|
||||
typename string_t::value_type delim) -> string_t {
|
||||
if (arr.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return std::accumulate(
|
||||
std::next(arr.begin()), arr.end(), arr[0U],
|
||||
[&delim](auto str, const auto &cur) { return str + delim + cur; });
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
auto left_trim(string_t &str,
|
||||
typename string_t::value_type trim_ch) -> string_t & {
|
||||
str.erase(0, str.find_first_not_of(trim_ch));
|
||||
return str;
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
inline auto replace(string_t &src, typename string_t::value_type character,
|
||||
typename string_t::value_type with,
|
||||
std::size_t start_pos) -> string_t & {
|
||||
if (start_pos < src.size()) {
|
||||
std::replace(std::next(src.begin(), start_pos), src.end(), character, with);
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
inline auto replace(string_t &src,
|
||||
std::basic_string_view<typename string_t::value_type> find,
|
||||
std::basic_string_view<typename string_t::value_type> with,
|
||||
std::size_t start_pos) -> string_t & {
|
||||
if (start_pos < src.size()) {
|
||||
while ((start_pos = src.find(find, start_pos)) != string_t::npos) {
|
||||
src.replace(start_pos, find.size(), with);
|
||||
start_pos += with.size();
|
||||
}
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
inline auto replace_copy(string_t src, typename string_t::value_type character,
|
||||
typename string_t::value_type with,
|
||||
std::size_t start_pos) -> string_t {
|
||||
return replace(src, character, with, start_pos);
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
inline auto
|
||||
replace_copy(string_t src,
|
||||
std::basic_string_view<typename string_t::value_type> find,
|
||||
std::basic_string_view<typename string_t::value_type> with,
|
||||
std::size_t start_pos) -> string_t {
|
||||
return replace(src, find, with, start_pos);
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
inline auto replace_with_hex(string_t &str,
|
||||
typename string_t::value_type character)
|
||||
-> chain_replace_with_hex<string_t> {
|
||||
return chain_replace_with_hex(
|
||||
replace(str, string_t{1U, character}, char_to_hex<string_t>(character)));
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
inline auto right_trim(string_t &str,
|
||||
typename string_t::value_type trim_ch) -> string_t & {
|
||||
str.erase(str.find_last_not_of(trim_ch) + 1);
|
||||
return str;
|
||||
}
|
||||
|
||||
template <typename string_t> inline auto to_lower(string_t str) -> string_t {
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||
return str;
|
||||
}
|
||||
|
||||
template <typename string_t> inline auto to_upper(string_t str) -> string_t {
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
|
||||
return str;
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
inline auto trim(string_t &str,
|
||||
typename string_t::value_type trim_ch) -> string_t & {
|
||||
return right_trim(left_trim(str, trim_ch), trim_ch);
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
inline auto trim_copy(string_t str,
|
||||
typename string_t::value_type trim_ch) -> string_t {
|
||||
return trim(str, trim_ch);
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto
|
||||
split_t(std::basic_string_view<typename string_t::value_type> str,
|
||||
typename string_t::value_type delim,
|
||||
bool should_trim) -> std::vector<string_t> {
|
||||
std::vector<string_t> ret;
|
||||
std::basic_stringstream<typename string_t::value_type> stream{string_t{str}};
|
||||
|
||||
string_t val;
|
||||
while (std::getline(stream, val, delim)) {
|
||||
if (should_trim) {
|
||||
trim(val);
|
||||
}
|
||||
ret.push_back(std::move(val));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline auto split(std::string_view str, char delim,
|
||||
bool should_trim) -> std::vector<std::string> {
|
||||
return split_t<std::string>(str, delim, should_trim);
|
||||
}
|
||||
|
||||
inline auto split(std::wstring_view str, wchar_t delim,
|
||||
bool should_trim) -> std::vector<std::wstring> {
|
||||
return split_t<std::wstring>(str, delim, should_trim);
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] inline auto
|
||||
split_t(std::basic_string_view<typename string_t::value_type> str,
|
||||
std::basic_string_view<typename string_t::value_type> delim,
|
||||
bool should_trim) -> std::vector<string_t> {
|
||||
auto result = std::views::split(str, delim);
|
||||
|
||||
std::vector<string_t> ret{};
|
||||
for (auto &&word : result) {
|
||||
auto val = string_t{word.begin(), word.end()};
|
||||
if (should_trim) {
|
||||
trim(val);
|
||||
}
|
||||
ret.push_back(std::move(val));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline auto split(std::string_view str, std::string_view delim,
|
||||
bool should_trim) -> std::vector<std::string> {
|
||||
return split_t<std::string>(str, delim, should_trim);
|
||||
}
|
||||
|
||||
inline auto split(std::wstring_view str, std::wstring_view delim,
|
||||
bool should_trim) -> std::vector<std::wstring> {
|
||||
return split_t<std::wstring>(str, delim, should_trim);
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
inline auto zero_pad(string_t str, std::size_t count) -> string_t {
|
||||
str.insert(str.begin(), count - str.length(), '0');
|
||||
return str;
|
||||
}
|
||||
} // namespace monitarr::utils::string
|
||||
|
||||
#endif // MONITARR_INCLUDE_UTILS_STRING_HPP_
|
||||
69
support/include/utils/time.hpp
Normal file
69
support/include/utils/time.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_TIME_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_TIME_HPP_
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
namespace monitarr::utils::time {
|
||||
inline constexpr const auto NANOS_PER_SECOND{1000000000ULL};
|
||||
inline constexpr const auto WIN32_TIME_CONVERSION{116444736000000000ULL};
|
||||
inline constexpr const auto WIN32_TIME_NANOS_PER_TICK{100ULL};
|
||||
|
||||
#if defined(PROJECT_ENABLE_SPDLOG) || defined(PROJECT_ENABLE_FMT)
|
||||
[[nodiscard]] inline auto convert_to_utc(time_t time) -> std::time_t {
|
||||
auto calendar_time = fmt::gmtime(time);
|
||||
return std::mktime(&calendar_time);
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_SPDLOG) || defined(PROJECT_ENABLE_FMT)
|
||||
|
||||
#if defined(PROJECT_ENABLE_SPDLOG) || defined(PROJECT_ENABLE_FMT)
|
||||
[[nodiscard]] inline auto get_current_time_utc() -> std::time_t {
|
||||
auto calendar_time = fmt::gmtime(std::time(nullptr));
|
||||
return std::mktime(&calendar_time);
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_SPDLOG) || defined(PROJECT_ENABLE_FMT)
|
||||
|
||||
void get_local_time_now(struct tm &local_time);
|
||||
|
||||
[[nodiscard]] auto get_time_now() -> std::uint64_t;
|
||||
|
||||
#if defined(_WIN32)
|
||||
auto strptime(const char *s, const char *f, struct tm *tm) -> const char *;
|
||||
|
||||
[[nodiscard]] auto unix_time_to_filetime(std::uint64_t unix_time) -> FILETIME;
|
||||
|
||||
[[nodiscard]] auto
|
||||
windows_file_time_to_unix_time(FILETIME win_time) -> std::uint64_t;
|
||||
|
||||
[[nodiscard]] auto
|
||||
windows_time_t_to_unix_time(__time64_t win_time) -> std::uint64_t;
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
[[nodiscard]] auto
|
||||
unix_time_to_windows_time(std::uint64_t unix_time) -> std::uint64_t;
|
||||
|
||||
[[nodiscard]] auto
|
||||
windows_time_to_unix_time(std::uint64_t win_time) -> std::uint64_t;
|
||||
} // namespace monitarr::utils::time
|
||||
|
||||
#endif // MONITARR_INCLUDE_UTILS_TIME_HPP_
|
||||
81
support/include/utils/types/file/i_directory.hpp
Normal file
81
support/include/utils/types/file/i_directory.hpp
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_TYPES_FILE_I_DIRECTORY_HPP_
|
||||
#define MONITARR_INCLUDE_TYPES_FILE_I_DIRECTORY_HPP_
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
#include "utils/types/file/i_file.hpp"
|
||||
#include "utils/types/file/i_fs_item.hpp"
|
||||
|
||||
namespace monitarr::utils::file {
|
||||
struct i_directory : public i_fs_item {
|
||||
using fs_directory_t = std::unique_ptr<i_directory>;
|
||||
using fs_file_t = i_file::fs_file_t;
|
||||
|
||||
virtual ~i_directory() = default;
|
||||
|
||||
[[nodiscard]] virtual auto
|
||||
count(bool recursive = false) const -> std::uint64_t = 0;
|
||||
|
||||
[[nodiscard]] virtual auto
|
||||
create_directory(std::string_view path = "") const -> fs_directory_t = 0;
|
||||
|
||||
[[nodiscard]] virtual auto create_file(std::string_view file_name,
|
||||
bool read_only) const -> fs_file_t = 0;
|
||||
|
||||
[[nodiscard]] virtual auto
|
||||
get_directory(std::string_view path) const -> fs_directory_t = 0;
|
||||
|
||||
[[nodiscard]] virtual auto
|
||||
get_directories() const -> std::vector<fs_directory_t> = 0;
|
||||
|
||||
[[nodiscard]] virtual auto
|
||||
get_file(std::string_view path) const -> fs_file_t = 0;
|
||||
|
||||
[[nodiscard]] virtual auto get_files() const -> std::vector<fs_file_t> = 0;
|
||||
|
||||
[[nodiscard]] virtual auto get_items() const -> std::vector<fs_item_t> = 0;
|
||||
|
||||
[[nodiscard]] auto is_directory_item() const -> bool override { return true; }
|
||||
|
||||
[[nodiscard]] virtual auto is_stop_requested() const -> bool = 0;
|
||||
|
||||
[[nodiscard]] virtual auto remove_recursively() -> bool = 0;
|
||||
|
||||
[[nodiscard]] virtual auto
|
||||
size(bool recursive = false) const -> std::uint64_t = 0;
|
||||
|
||||
protected:
|
||||
i_directory() noexcept = default;
|
||||
|
||||
i_directory(const i_directory &) noexcept = default;
|
||||
|
||||
i_directory(i_directory &&) noexcept = default;
|
||||
|
||||
auto operator=(i_directory &&) noexcept -> i_directory & = default;
|
||||
|
||||
auto operator=(const i_directory &) noexcept -> i_directory & = default;
|
||||
};
|
||||
} // namespace monitarr::utils::file
|
||||
|
||||
#endif // MONITARR_INCLUDE_TYPES_FILE_I_DIRECTORY_HPP_
|
||||
93
support/include/utils/types/file/i_file.hpp
Normal file
93
support/include/utils/types/file/i_file.hpp
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_TYPES_FILE_I_FILE_HPP_
|
||||
#define MONITARR_INCLUDE_TYPES_FILE_I_FILE_HPP_
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
#include "utils/types/file/i_fs_item.hpp"
|
||||
|
||||
namespace monitarr::utils::file {
|
||||
struct i_file : public i_fs_item {
|
||||
using fs_file_t = std::unique_ptr<i_file>;
|
||||
|
||||
virtual ~i_file() = default;
|
||||
|
||||
virtual void close() = 0;
|
||||
|
||||
virtual void flush() const = 0;
|
||||
|
||||
[[nodiscard]] virtual auto get_handle() const -> native_handle = 0;
|
||||
|
||||
[[nodiscard]] virtual auto get_read_buffer_size() const -> std::uint32_t = 0;
|
||||
|
||||
[[nodiscard]] auto is_directory_item() const -> bool override {
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual auto is_read_only() const -> bool = 0;
|
||||
|
||||
[[nodiscard]] virtual auto read(data_buffer &data, std::uint64_t offset,
|
||||
std::size_t *total_read = nullptr) -> bool {
|
||||
return read(data.data(), data.size(), offset, total_read);
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual auto
|
||||
read(unsigned char *data, std::size_t to_read, std::uint64_t offset,
|
||||
std::size_t *total_read = nullptr) -> bool = 0;
|
||||
|
||||
[[nodiscard]] virtual auto
|
||||
read_all(data_buffer &data, std::uint64_t offset,
|
||||
std::size_t *total_read = nullptr) -> bool;
|
||||
|
||||
virtual auto set_read_buffer_size(std::uint32_t size) -> std::uint32_t = 0;
|
||||
|
||||
[[nodiscard]] virtual auto size() const -> std::optional<std::uint64_t> = 0;
|
||||
|
||||
[[nodiscard]] virtual auto truncate() -> bool { return truncate(0U); }
|
||||
|
||||
[[nodiscard]] virtual auto truncate(std::size_t size) -> bool = 0;
|
||||
|
||||
[[nodiscard]] virtual auto
|
||||
write(const data_buffer &data, std::uint64_t offset,
|
||||
std::size_t *total_written = nullptr) -> bool {
|
||||
return write(data.data(), data.size(), offset, total_written);
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual auto
|
||||
write(const unsigned char *data, std::size_t to_write, std::size_t offset,
|
||||
std::size_t *total_written = nullptr) -> bool = 0;
|
||||
|
||||
protected:
|
||||
i_file() noexcept = default;
|
||||
|
||||
i_file(const i_file &) noexcept = default;
|
||||
|
||||
i_file(i_file &&) noexcept = default;
|
||||
|
||||
auto operator=(i_file &&) noexcept -> i_file & = default;
|
||||
|
||||
auto operator=(const i_file &) noexcept -> i_file & = default;
|
||||
};
|
||||
} // namespace monitarr::utils::file
|
||||
|
||||
#endif // MONITARR_INCLUDE_TYPES_FILE_I_FILE_HPP_
|
||||
117
support/include/utils/types/file/i_fs_item.hpp
Normal file
117
support/include/utils/types/file/i_fs_item.hpp
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_TYPES_FILE_I_FS_ITEM_HPP_
|
||||
#define MONITARR_INCLUDE_TYPES_FILE_I_FS_ITEM_HPP_
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace monitarr::utils::file {
|
||||
enum class time_type {
|
||||
accessed,
|
||||
created,
|
||||
modified,
|
||||
written,
|
||||
};
|
||||
|
||||
struct file_times final {
|
||||
std::uint64_t accessed{};
|
||||
std::uint64_t created{};
|
||||
std::uint64_t modified{};
|
||||
std::uint64_t written{};
|
||||
|
||||
[[nodiscard]] auto get(time_type type) const -> std::uint64_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
switch (type) {
|
||||
case time_type::accessed:
|
||||
return accessed;
|
||||
case time_type::created:
|
||||
return created;
|
||||
case time_type::modified:
|
||||
return modified;
|
||||
case time_type::written:
|
||||
return written;
|
||||
}
|
||||
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"type_type not supported",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
struct i_fs_item {
|
||||
using fs_item_t = std::unique_ptr<i_fs_item>;
|
||||
|
||||
virtual ~i_fs_item() = default;
|
||||
|
||||
[[nodiscard]] virtual auto copy_to(std::string_view to_path,
|
||||
bool overwrite) const -> bool = 0;
|
||||
|
||||
[[nodiscard]] virtual auto copy_to(std::wstring_view new_path,
|
||||
bool overwrite) -> bool {
|
||||
return copy_to(utils::string::to_utf8(new_path), overwrite);
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual auto exists() const -> bool = 0;
|
||||
|
||||
[[nodiscard]] virtual auto get_path() const -> std::string = 0;
|
||||
|
||||
[[nodiscard]] virtual auto
|
||||
get_time(time_type type) const -> std::optional<std::uint64_t>;
|
||||
|
||||
[[nodiscard]] virtual auto is_directory_item() const -> bool = 0;
|
||||
|
||||
[[nodiscard]] auto is_file_item() const -> bool {
|
||||
return not is_directory_item();
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual auto is_symlink() const -> bool = 0;
|
||||
|
||||
[[nodiscard]] virtual auto move_to(std::string_view new_path) -> bool = 0;
|
||||
|
||||
[[nodiscard]] virtual auto move_to(std::wstring_view new_path) -> bool {
|
||||
return move_to(utils::string::to_utf8(new_path));
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual auto remove() -> bool = 0;
|
||||
|
||||
public:
|
||||
[[nodiscard]] virtual operator bool() const = 0;
|
||||
|
||||
protected:
|
||||
i_fs_item() noexcept = default;
|
||||
|
||||
i_fs_item(const i_fs_item &) noexcept = default;
|
||||
|
||||
i_fs_item(i_fs_item &&) noexcept = default;
|
||||
|
||||
auto operator=(i_fs_item &&) noexcept -> i_fs_item & = default;
|
||||
|
||||
auto operator=(const i_fs_item &) noexcept -> i_fs_item & = default;
|
||||
};
|
||||
} // namespace monitarr::utils::file
|
||||
|
||||
#endif // MONITARR_INCLUDE_TYPES_FILE_I_FS_ITEM_HPP_
|
||||
63
support/include/utils/unix.hpp
Normal file
63
support/include/utils/unix.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_UNIX_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_UNIX_HPP_
|
||||
#if !defined(_WIN32)
|
||||
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/config.hpp"
|
||||
|
||||
namespace monitarr::utils {
|
||||
using passwd_callback_t = std::function<void(struct passwd *pass)>;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
template <typename thread_t>
|
||||
[[nodiscard]] auto convert_to_uint64(const thread_t *thread_ptr)
|
||||
-> std::uint64_t;
|
||||
#else // !defined(__APPLE__)
|
||||
[[nodiscard]] auto convert_to_uint64(const pthread_t &thread) -> std::uint64_t;
|
||||
#endif // defined(__APPLE__)
|
||||
|
||||
[[nodiscard]] auto get_last_error_code() -> int;
|
||||
|
||||
[[nodiscard]] auto get_thread_id() -> std::uint64_t;
|
||||
|
||||
[[nodiscard]] auto is_uid_member_of_group(uid_t uid, gid_t gid) -> bool;
|
||||
|
||||
void set_last_error_code(int error_code);
|
||||
|
||||
[[nodiscard]] auto use_getpwuid(uid_t uid, passwd_callback_t callback)
|
||||
-> utils::result;
|
||||
|
||||
// template implementations
|
||||
#if defined(__APPLE__)
|
||||
template <typename t>
|
||||
[[nodiscard]] auto convert_to_uint64(const thread_t *thread_ptr)
|
||||
-> std::uint64_t {
|
||||
return static_cast<std::uint64_t>(
|
||||
reinterpret_cast<std::uintptr_t>(thread_ptr));
|
||||
}
|
||||
#endif // defined(__APPLE__)
|
||||
} // namespace monitarr::utils
|
||||
|
||||
#endif // !defined(_WIN32)
|
||||
#endif // MONITARR_INCLUDE_UTILS_UNIX_HPP_
|
||||
47
support/include/utils/windows.hpp
Normal file
47
support/include/utils/windows.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_INCLUDE_UTILS_WINDOWS_HPP_
|
||||
#define MONITARR_INCLUDE_UTILS_WINDOWS_HPP_
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
namespace monitarr::utils {
|
||||
void create_console();
|
||||
|
||||
void free_console();
|
||||
|
||||
[[nodiscard]] auto get_local_app_data_directory() -> const std::string &;
|
||||
|
||||
[[nodiscard]] auto get_last_error_code() -> DWORD;
|
||||
|
||||
[[nodiscard]] auto get_thread_id() -> std::uint64_t;
|
||||
|
||||
[[nodiscard]] auto is_process_elevated() -> bool;
|
||||
|
||||
[[nodiscard]] auto run_process_elevated(std::vector<const char *> args) -> int;
|
||||
|
||||
void set_last_error_code(DWORD errorCode);
|
||||
} // namespace monitarr::utils
|
||||
|
||||
#endif // defined(_WIN32)
|
||||
#endif // MONITARR_INCLUDE_UTILS_WINDOWS_HPP_
|
||||
45
support/src/backward.cpp
Normal file
45
support/src/backward.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
// Pick your poison.
|
||||
//
|
||||
// On GNU/Linux, you have few choices to get the most out of your stack trace.
|
||||
//
|
||||
// By default you get:
|
||||
// - object filename
|
||||
// - function name
|
||||
//
|
||||
// In order to add:
|
||||
// - source filename
|
||||
// - line and column numbers
|
||||
// - source code snippet (assuming the file is accessible)
|
||||
|
||||
// Install one of the following libraries then uncomment one of the macro (or
|
||||
// better, add the detection of the lib and the macro definition in your build
|
||||
// system)
|
||||
|
||||
// - apt-get install libdw-dev ...
|
||||
// - g++/clang++ -ldw ...
|
||||
// #define BACKWARD_HAS_DW 1
|
||||
|
||||
// - apt-get install binutils-dev ...
|
||||
// - g++/clang++ -lbfd ...
|
||||
// #define BACKWARD_HAS_BFD 1
|
||||
|
||||
// - apt-get install libdwarf-dev ...
|
||||
// - g++/clang++ -ldwarf ...
|
||||
// #define BACKWARD_HAS_DWARF 1
|
||||
|
||||
// Regardless of the library you choose to read the debug information,
|
||||
// for potentially more detailed stack traces you can use libunwind
|
||||
// - apt-get install libunwind-dev
|
||||
// - g++/clang++ -lunwind
|
||||
// #define BACKWARD_HAS_LIBUNWIND 1
|
||||
#include "backward.hpp"
|
||||
|
||||
#if defined(PROJECT_ENABLE_BACKWARD_CPP)
|
||||
|
||||
namespace backward {
|
||||
|
||||
backward::SignalHandling sh;
|
||||
|
||||
} // namespace backward
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_BACKWARD_CPP)
|
||||
174
support/src/utils/common.cpp
Normal file
174
support/src/utils/common.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/common.hpp"
|
||||
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace monitarr::utils {
|
||||
auto compare_version_strings(std::string version1,
|
||||
std::string version2) -> std::int32_t {
|
||||
|
||||
if (utils::string::contains(version1, "-")) {
|
||||
version1 = utils::string::split(version1, '-', true)[0U];
|
||||
}
|
||||
|
||||
if (utils::string::contains(version2, "-")) {
|
||||
version2 = utils::string::split(version2, '-', true)[0U];
|
||||
}
|
||||
|
||||
auto nums1 = utils::string::split(version1, '.', true);
|
||||
auto nums2 = utils::string::split(version2, '.', true);
|
||||
|
||||
while (nums1.size() > nums2.size()) {
|
||||
nums2.emplace_back("0");
|
||||
}
|
||||
|
||||
while (nums2.size() > nums1.size()) {
|
||||
nums1.emplace_back("0");
|
||||
}
|
||||
|
||||
for (std::size_t idx = 0U; idx < nums1.size(); idx++) {
|
||||
auto int1 = utils::string::to_uint32(nums1[idx]);
|
||||
auto int2 = utils::string::to_uint32(nums2[idx]);
|
||||
auto res = std::memcmp(&int1, &int2, sizeof(int1));
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto compare_version_strings(std::wstring_view version1,
|
||||
std::wstring_view version2) -> std::int32_t {
|
||||
return compare_version_strings(utils::string::to_utf8(version1),
|
||||
utils::string::to_utf8(version2));
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_STDUUID)
|
||||
auto create_uuid_string() -> std::string {
|
||||
std::random_device random_device{};
|
||||
auto seed_data = std::array<int, std::mt19937::state_size>{};
|
||||
std::generate(std::begin(seed_data), std::end(seed_data),
|
||||
std::ref(random_device));
|
||||
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
|
||||
std::mt19937 generator(seq);
|
||||
uuids::uuid_random_generator gen{generator};
|
||||
|
||||
return uuids::to_string(gen());
|
||||
}
|
||||
|
||||
auto create_uuid_wstring() -> std::wstring {
|
||||
return utils::string::from_utf8(create_uuid_string());
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_STDUUID)
|
||||
|
||||
auto generate_random_string(std::size_t length) -> std::string {
|
||||
std::string ret;
|
||||
if (length == 0U) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.resize(length);
|
||||
for (auto &ch : ret) {
|
||||
std::array<unsigned int, 3U> random_list{
|
||||
generate_random_between(0U, 57U),
|
||||
generate_random_between(65U, 90U),
|
||||
generate_random_between(97U, 255U),
|
||||
};
|
||||
ch = static_cast<char>(
|
||||
random_list.at(generate_random_between(0U, 2U)) % 74U + 48U);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto generate_random_wstring(std::size_t length) -> std::wstring {
|
||||
return utils::string::from_utf8(generate_random_string(length));
|
||||
}
|
||||
|
||||
auto get_environment_variable(std::string_view variable) -> std::string {
|
||||
static std::mutex mtx{};
|
||||
mutex_lock lock{mtx};
|
||||
|
||||
const auto *val = std::getenv(std::string{variable}.c_str());
|
||||
return std::string{val == nullptr ? "" : val};
|
||||
}
|
||||
|
||||
auto get_environment_variable(std::wstring_view variable) -> std::wstring {
|
||||
return utils::string::from_utf8(
|
||||
get_environment_variable(utils::string::to_utf8(variable)));
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_BOOST)
|
||||
auto get_next_available_port(std::uint16_t first_port,
|
||||
std::uint16_t &available_port) -> bool {
|
||||
if (first_port == 0U) {
|
||||
return false;
|
||||
}
|
||||
|
||||
using namespace boost::asio;
|
||||
using ip::tcp;
|
||||
|
||||
boost::system::error_code error_code{};
|
||||
while (first_port != 0U) {
|
||||
io_context ctx{};
|
||||
tcp::acceptor acceptor(ctx);
|
||||
acceptor.open(tcp::v4(), error_code) ||
|
||||
acceptor.bind({tcp::v4(), first_port}, error_code);
|
||||
if (not error_code) {
|
||||
break;
|
||||
}
|
||||
|
||||
++first_port;
|
||||
}
|
||||
|
||||
if (not error_code) {
|
||||
available_port = first_port;
|
||||
}
|
||||
|
||||
return not error_code;
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
auto retry_action(retryable_action_t action, std::size_t retry_count,
|
||||
std::chrono::milliseconds retry_wait) -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
for (std::size_t idx = 0U; idx < retry_count; ++idx) {
|
||||
if (action()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(retry_wait);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace monitarr::utils
|
||||
250
support/src/utils/db/sqlite/db_common.cpp
Normal file
250
support/src/utils/db/sqlite/db_common.cpp
Normal file
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#include "utils/db/sqlite/db_common.hpp"
|
||||
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/error.hpp"
|
||||
|
||||
namespace monitarr::utils::db::sqlite {
|
||||
void sqlite3_deleter::operator()(sqlite3 *db3) const {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (db3 == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string err_msg;
|
||||
if (not execute_sql(*db3, "VACUUM;", err_msg)) {
|
||||
utils::error::handle_error(function_name,
|
||||
utils::error::create_error_message({
|
||||
"failed to vacuum database",
|
||||
err_msg,
|
||||
}));
|
||||
}
|
||||
|
||||
if (not utils::retry_action([&db3]() -> bool {
|
||||
auto res = sqlite3_close_v2(db3);
|
||||
if (res == SQLITE_OK) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto &&err_str = sqlite3_errstr(res);
|
||||
utils::error::handle_error(
|
||||
function_name,
|
||||
utils::error::create_error_message({
|
||||
"failed to close database",
|
||||
(err_str == nullptr ? std::to_string(res) : err_str),
|
||||
}));
|
||||
return false;
|
||||
})) {
|
||||
monitarr::utils::error::handle_error(function_name,
|
||||
"failed to close database");
|
||||
}
|
||||
}
|
||||
|
||||
db_result::db_column::db_column(std::int32_t index, std::string name,
|
||||
db_types_t value) noexcept
|
||||
: index_(index), name_(std::move(name)), value_(std::move(value)) {}
|
||||
|
||||
#if defined(PROJECT_ENABLE_JSON)
|
||||
auto db_result::db_column::get_value_as_json() const -> nlohmann::json {
|
||||
return std::visit(
|
||||
overloaded{
|
||||
[this](std::int64_t value) -> auto {
|
||||
return nlohmann::json({{name_, value}});
|
||||
},
|
||||
[](auto &&value) -> auto { return nlohmann::json::parse(value); },
|
||||
},
|
||||
value_);
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_JSON)
|
||||
|
||||
db_result::db_row::db_row(std::shared_ptr<context> ctx) {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
auto column_count = sqlite3_column_count(ctx->stmt.get());
|
||||
for (std::int32_t col = 0; col < column_count; col++) {
|
||||
std::string name{sqlite3_column_name(ctx->stmt.get(), col)};
|
||||
auto column_type = sqlite3_column_type(ctx->stmt.get(), col);
|
||||
|
||||
db_types_t value;
|
||||
switch (column_type) {
|
||||
case SQLITE_INTEGER: {
|
||||
value = sqlite3_column_int64(ctx->stmt.get(), col);
|
||||
} break;
|
||||
|
||||
case SQLITE_TEXT: {
|
||||
const auto *text = reinterpret_cast<const char *>(
|
||||
sqlite3_column_text(ctx->stmt.get(), col));
|
||||
value = std::string(text == nullptr ? "" : text);
|
||||
} break;
|
||||
|
||||
default:
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"column type not implemented",
|
||||
std::to_string(column_type),
|
||||
});
|
||||
}
|
||||
|
||||
columns_[name] = db_column{col, name, value};
|
||||
}
|
||||
}
|
||||
|
||||
auto db_result::db_row::get_columns() const -> std::vector<db_column> {
|
||||
std::vector<db_column> ret;
|
||||
for (const auto &item : columns_) {
|
||||
ret.push_back(item.second);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto db_result::db_row::get_column(std::int32_t index) const -> db_column {
|
||||
auto iter = std::find_if(
|
||||
columns_.begin(), columns_.end(),
|
||||
[&index](auto &&col) -> bool { return col.second.get_index() == index; });
|
||||
if (iter == columns_.end()) {
|
||||
throw std::out_of_range("");
|
||||
}
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
auto db_result::db_row::get_column(std::string name) const -> db_column {
|
||||
return columns_.at(name);
|
||||
}
|
||||
|
||||
db_result::db_result(db3_stmt_t stmt, std::int32_t res)
|
||||
: ctx_(std::make_shared<context>()), res_(res) {
|
||||
ctx_->stmt = std::move(stmt);
|
||||
if (res == SQLITE_OK) {
|
||||
set_res(sqlite3_step(ctx_->stmt.get()));
|
||||
}
|
||||
}
|
||||
|
||||
auto db_result::get_error_str() const -> std::string {
|
||||
auto &&err_msg = sqlite3_errstr(res_);
|
||||
return err_msg == nullptr ? std::to_string(res_) : err_msg;
|
||||
}
|
||||
|
||||
auto db_result::get_row(std::optional<row> &opt_row) const -> bool {
|
||||
opt_row.reset();
|
||||
|
||||
if (not has_row()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
opt_row = db_row{ctx_};
|
||||
set_res(sqlite3_step(ctx_->stmt.get()));
|
||||
return true;
|
||||
}
|
||||
|
||||
auto db_result::has_row() const -> bool { return res_ == SQLITE_ROW; }
|
||||
|
||||
void db_result::next_row() const {
|
||||
if (not has_row()) {
|
||||
return;
|
||||
}
|
||||
|
||||
set_res(sqlite3_step(ctx_->stmt.get()));
|
||||
}
|
||||
|
||||
auto db_result::ok() const -> bool {
|
||||
return res_ == SQLITE_DONE || res_ == SQLITE_ROW;
|
||||
}
|
||||
|
||||
auto create_db(std::string db_path,
|
||||
const std::map<std::string, std::string> &sql_create_tables)
|
||||
-> db3_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
sqlite3 *db_ptr{nullptr};
|
||||
auto db_res =
|
||||
sqlite3_open_v2(db_path.c_str(), &db_ptr,
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr);
|
||||
if (db_res != SQLITE_OK) {
|
||||
const auto *msg = sqlite3_errstr(db_res);
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to open db",
|
||||
db_path,
|
||||
(msg == nullptr ? std::to_string(db_res) : msg),
|
||||
});
|
||||
}
|
||||
|
||||
auto db3 = db3_t{
|
||||
db_ptr,
|
||||
sqlite3_deleter(),
|
||||
};
|
||||
|
||||
for (auto &&create_item : sql_create_tables) {
|
||||
std::string err_msg;
|
||||
if (not sqlite::execute_sql(*db3, create_item.second, err_msg)) {
|
||||
db3.reset();
|
||||
throw utils::error::create_exception(function_name, {
|
||||
err_msg,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
set_journal_mode(*db3);
|
||||
|
||||
return db3;
|
||||
}
|
||||
|
||||
auto execute_sql(sqlite3 &db3, const std::string &sql, std::string &err)
|
||||
-> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
char *err_msg{nullptr};
|
||||
auto res = sqlite3_exec(&db3, sql.c_str(), nullptr, nullptr, &err_msg);
|
||||
if (err_msg != nullptr) {
|
||||
err = err_msg;
|
||||
sqlite3_free(err_msg);
|
||||
err_msg = nullptr;
|
||||
}
|
||||
|
||||
if (res == SQLITE_OK) {
|
||||
return true;
|
||||
}
|
||||
|
||||
err = utils::error::create_error_message({
|
||||
function_name,
|
||||
"failed to execute sql",
|
||||
err,
|
||||
sql,
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void set_journal_mode(sqlite3 &db3) {
|
||||
sqlite3_exec(&db3,
|
||||
"PRAGMA journal_mode = WAL;PRAGMA synchronous = NORMAL;PRAGMA "
|
||||
"auto_vacuum = NONE;",
|
||||
nullptr, nullptr, nullptr);
|
||||
}
|
||||
} // namespace monitarr::utils::db::sqlite
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
115
support/src/utils/db/sqlite/db_delete.cpp
Normal file
115
support/src/utils/db/sqlite/db_delete.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#include "utils/db/sqlite/db_delete.hpp"
|
||||
|
||||
namespace monitarr::utils::db::sqlite {
|
||||
auto db_delete::context::db_delete_op_t::dump() const -> std::string {
|
||||
return db_delete{ctx}.dump();
|
||||
}
|
||||
|
||||
auto db_delete::context::db_delete_op_t::go() const -> db_result {
|
||||
return db_delete{ctx}.go();
|
||||
}
|
||||
|
||||
auto db_delete::dump() const -> std::string {
|
||||
std::stringstream query;
|
||||
query << "DELETE FROM \"" << ctx_->table_name << "\"";
|
||||
|
||||
if (ctx_->where_data) {
|
||||
std::int32_t idx{};
|
||||
query << " WHERE " << ctx_->where_data->base.dump(idx);
|
||||
}
|
||||
|
||||
query << ';';
|
||||
|
||||
return query.str();
|
||||
}
|
||||
|
||||
auto db_delete::go() const -> db_result {
|
||||
sqlite3_stmt *stmt_ptr{nullptr};
|
||||
auto query_str = dump();
|
||||
auto res =
|
||||
sqlite3_prepare_v2(ctx_->db3, query_str.c_str(), -1, &stmt_ptr, nullptr);
|
||||
|
||||
auto stmt = db3_stmt_t{
|
||||
stmt_ptr,
|
||||
sqlite3_statement_deleter(),
|
||||
};
|
||||
|
||||
if (res != SQLITE_OK) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
if (not ctx_->where_data) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
for (std::int32_t idx = 0;
|
||||
idx < static_cast<std::int32_t>(ctx_->where_data->values.size());
|
||||
idx++) {
|
||||
res =
|
||||
std::visit(overloaded{
|
||||
[&stmt, &idx](std::int64_t data) -> std::int32_t {
|
||||
return sqlite3_bind_int64(stmt.get(), idx + 1, data);
|
||||
},
|
||||
[&stmt, &idx](const std::string &data) -> std::int32_t {
|
||||
return sqlite3_bind_text(stmt.get(), idx + 1,
|
||||
data.c_str(), -1, nullptr);
|
||||
},
|
||||
},
|
||||
ctx_->where_data->values.at(static_cast<std::size_t>(idx)));
|
||||
if (res != SQLITE_OK) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
}
|
||||
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
auto db_delete::group(context::w_t::group_func_t func) -> context::w_t::wn_t {
|
||||
if (not ctx_->where_data) {
|
||||
ctx_->where_data = std::make_unique<context::wd_t>(context::wd_t{
|
||||
context::w_t{0U, ctx_},
|
||||
{},
|
||||
{},
|
||||
});
|
||||
}
|
||||
|
||||
return ctx_->where_data->base.group(std::move(func));
|
||||
}
|
||||
|
||||
auto db_delete::where(std::string column_name) const -> context::w_t::cn_t {
|
||||
if (not ctx_->where_data) {
|
||||
ctx_->where_data = std::make_unique<context::wd_t>(context::wd_t{
|
||||
context::w_t{0U, ctx_},
|
||||
{},
|
||||
{},
|
||||
});
|
||||
}
|
||||
|
||||
return ctx_->where_data->base.where(column_name);
|
||||
}
|
||||
} // namespace monitarr::utils::db::sqlite
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
99
support/src/utils/db/sqlite/db_insert.cpp
Normal file
99
support/src/utils/db/sqlite/db_insert.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#include "utils/db/sqlite/db_insert.hpp"
|
||||
|
||||
namespace monitarr::utils::db::sqlite {
|
||||
auto db_insert::column_value(std::string column_name, db_types_t value)
|
||||
-> db_insert {
|
||||
ctx_->values[column_name] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_insert::dump() const -> std::string {
|
||||
std::stringstream query;
|
||||
query << "INSERT ";
|
||||
if (ctx_->or_replace) {
|
||||
query << "OR REPLACE ";
|
||||
}
|
||||
query << "INTO \"" << ctx_->table_name << "\" (";
|
||||
|
||||
for (std::int32_t idx = 0;
|
||||
idx < static_cast<std::int32_t>(ctx_->values.size()); idx++) {
|
||||
if (idx > 0) {
|
||||
query << ", ";
|
||||
}
|
||||
query << '"' << std::next(ctx_->values.begin(), idx)->first << '"';
|
||||
}
|
||||
|
||||
query << ") VALUES (";
|
||||
for (std::int32_t idx = 0;
|
||||
idx < static_cast<std::int32_t>(ctx_->values.size()); idx++) {
|
||||
if (idx > 0) {
|
||||
query << ", ";
|
||||
}
|
||||
query << "?" << (idx + 1);
|
||||
}
|
||||
query << ");";
|
||||
|
||||
return query.str();
|
||||
}
|
||||
|
||||
auto db_insert::go() const -> db_result {
|
||||
sqlite3_stmt *stmt_ptr{nullptr};
|
||||
auto query_str = dump();
|
||||
auto res =
|
||||
sqlite3_prepare_v2(ctx_->db3, query_str.c_str(), -1, &stmt_ptr, nullptr);
|
||||
|
||||
auto stmt = db3_stmt_t{
|
||||
stmt_ptr,
|
||||
sqlite3_statement_deleter(),
|
||||
};
|
||||
|
||||
if (res != SQLITE_OK) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
for (std::int32_t idx = 0;
|
||||
idx < static_cast<std::int32_t>(ctx_->values.size()); idx++) {
|
||||
res =
|
||||
std::visit(overloaded{
|
||||
[&idx, &stmt](std::int64_t data) -> std::int32_t {
|
||||
return sqlite3_bind_int64(stmt.get(), idx + 1, data);
|
||||
},
|
||||
[&idx, &stmt](const std::string &data) -> std::int32_t {
|
||||
return sqlite3_bind_text(stmt.get(), idx + 1,
|
||||
data.c_str(), -1, nullptr);
|
||||
},
|
||||
},
|
||||
std::next(ctx_->values.begin(), idx)->second);
|
||||
if (res != SQLITE_OK) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
}
|
||||
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
} // namespace monitarr::utils::db::sqlite
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
221
support/src/utils/db/sqlite/db_select.cpp
Normal file
221
support/src/utils/db/sqlite/db_select.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#include "utils/db/sqlite/db_select.hpp"
|
||||
|
||||
namespace monitarr::utils::db::sqlite {
|
||||
auto db_select::context::db_select_op_t::dump() const -> std::string {
|
||||
return db_select{ctx}.dump();
|
||||
}
|
||||
|
||||
auto db_select::context::db_select_op_t::go() const -> db_result {
|
||||
return db_select{ctx}.go();
|
||||
}
|
||||
|
||||
auto db_select::context::db_select_op_t::group_by(std::string column_name)
|
||||
-> db_select::context::db_select_op_t {
|
||||
db_select{ctx}.group_by(column_name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::context::db_select_op_t::limit(std::int32_t value)
|
||||
-> db_select::context::db_select_op_t {
|
||||
db_select{ctx}.limit(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::context::db_select_op_t::offset(std::int32_t value)
|
||||
-> db_select::context::db_select_op_t {
|
||||
db_select{ctx}.offset(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::context::db_select_op_t::order_by(std::string column_name,
|
||||
bool ascending)
|
||||
-> db_select::context::db_select_op_t {
|
||||
db_select{ctx}.order_by(column_name, ascending);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::column(std::string column_name) -> db_select {
|
||||
ctx_->columns.push_back(column_name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::count(std::string column_name, std::string as_column_name)
|
||||
-> db_select {
|
||||
ctx_->count_columns[column_name] = as_column_name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::dump() const -> std::string {
|
||||
std::stringstream query;
|
||||
query << "SELECT ";
|
||||
bool has_column{false};
|
||||
if (ctx_->columns.empty()) {
|
||||
if (ctx_->count_columns.empty()) {
|
||||
query << "*";
|
||||
has_column = true;
|
||||
}
|
||||
} else {
|
||||
has_column = not ctx_->columns.empty();
|
||||
for (std::size_t idx = 0U; idx < ctx_->columns.size(); idx++) {
|
||||
if (idx > 0U) {
|
||||
query << ", ";
|
||||
}
|
||||
query << ctx_->columns.at(idx);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::int32_t idx = 0U;
|
||||
idx < static_cast<std::int32_t>(ctx_->count_columns.size()); idx++) {
|
||||
if (has_column || idx > 0) {
|
||||
query << ", ";
|
||||
}
|
||||
query << "COUNT(\"";
|
||||
auto &count_column = *std::next(ctx_->count_columns.begin(), idx);
|
||||
query << count_column.first << "\") AS \"" << count_column.second << '"';
|
||||
}
|
||||
query << " FROM \"" << ctx_->table_name << "\"";
|
||||
|
||||
if (ctx_->where_data) {
|
||||
std::int32_t idx{};
|
||||
query << " WHERE " << ctx_->where_data->base.dump(idx);
|
||||
}
|
||||
|
||||
if (not ctx_->group_by.empty()) {
|
||||
query << " GROUP BY ";
|
||||
for (std::size_t idx = 0U; idx < ctx_->group_by.size(); idx++) {
|
||||
if (idx > 0U) {
|
||||
query << ", ";
|
||||
}
|
||||
|
||||
query << "\"" << ctx_->group_by.at(idx) << "\"";
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx_->order_by.has_value()) {
|
||||
query << " ORDER BY \"" << ctx_->order_by.value().first << "\" ";
|
||||
query << (ctx_->order_by.value().second ? "ASC" : "DESC");
|
||||
}
|
||||
|
||||
if (ctx_->limit.has_value()) {
|
||||
query << " LIMIT " << ctx_->limit.value();
|
||||
}
|
||||
|
||||
if (ctx_->offset.has_value()) {
|
||||
query << " OFFSET " << ctx_->offset.value();
|
||||
}
|
||||
|
||||
query << ';';
|
||||
|
||||
return query.str();
|
||||
}
|
||||
|
||||
auto db_select::go() const -> db_result {
|
||||
sqlite3_stmt *stmt_ptr{nullptr};
|
||||
auto query_str = dump();
|
||||
auto res =
|
||||
sqlite3_prepare_v2(ctx_->db3, query_str.c_str(), -1, &stmt_ptr, nullptr);
|
||||
|
||||
auto stmt = db3_stmt_t{
|
||||
stmt_ptr,
|
||||
sqlite3_statement_deleter(),
|
||||
};
|
||||
|
||||
if (res != SQLITE_OK) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
if (not ctx_->where_data) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
for (std::int32_t idx = 0;
|
||||
idx < static_cast<std::int32_t>(ctx_->where_data->values.size());
|
||||
idx++) {
|
||||
res =
|
||||
std::visit(overloaded{
|
||||
[&idx, &stmt](std::int64_t data) -> std::int32_t {
|
||||
return sqlite3_bind_int64(stmt.get(), idx + 1, data);
|
||||
},
|
||||
[&idx, &stmt](const std::string &data) -> std::int32_t {
|
||||
return sqlite3_bind_text(stmt.get(), idx + 1,
|
||||
data.c_str(), -1, nullptr);
|
||||
},
|
||||
},
|
||||
ctx_->where_data->values.at(static_cast<std::size_t>(idx)));
|
||||
if (res != SQLITE_OK) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
}
|
||||
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
auto db_select::group(context::w_t::group_func_t func) -> context::w_t::wn_t {
|
||||
if (not ctx_->where_data) {
|
||||
ctx_->where_data = std::make_unique<context::wd_t>(context::wd_t{
|
||||
context::w_t{0U, ctx_},
|
||||
{},
|
||||
{},
|
||||
});
|
||||
}
|
||||
|
||||
return ctx_->where_data->base.group(std::move(func));
|
||||
}
|
||||
|
||||
auto db_select::group_by(std::string column_name) -> db_select {
|
||||
ctx_->group_by.emplace_back(std::move(column_name));
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::limit(std::int32_t value) -> db_select {
|
||||
ctx_->limit = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::offset(std::int32_t value) -> db_select {
|
||||
ctx_->offset = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::order_by(std::string column_name, bool ascending) -> db_select {
|
||||
ctx_->order_by = {column_name, ascending};
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::where(std::string column_name) const -> context::w_t::cn_t {
|
||||
if (not ctx_->where_data) {
|
||||
ctx_->where_data = std::make_unique<context::wd_t>(context::wd_t{
|
||||
context::w_t{0U, ctx_},
|
||||
{},
|
||||
{},
|
||||
});
|
||||
}
|
||||
|
||||
return ctx_->where_data->base.where(column_name);
|
||||
}
|
||||
} // namespace monitarr::utils::db::sqlite
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
189
support/src/utils/db/sqlite/db_update.cpp
Normal file
189
support/src/utils/db/sqlite/db_update.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#include "utils/db/sqlite/db_update.hpp"
|
||||
|
||||
namespace monitarr::utils::db::sqlite {
|
||||
auto db_update::context::db_update_op_t::dump() const -> std::string {
|
||||
return db_update{ctx}.dump();
|
||||
}
|
||||
|
||||
auto db_update::context::db_update_op_t::go() const -> db_result {
|
||||
return db_update{ctx}.go();
|
||||
}
|
||||
|
||||
auto db_update::context::db_update_op_t::limit(std::int32_t value)
|
||||
-> db_update::context::db_update_op_t {
|
||||
db_update{ctx}.limit(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_update::context::db_update_op_t::order_by(std::string column_name,
|
||||
bool ascending)
|
||||
-> db_update::context::db_update_op_t {
|
||||
db_update{ctx}.order_by(column_name, ascending);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_update::column_value(std::string column_name, db_types_t value)
|
||||
-> db_update {
|
||||
ctx_->column_values[column_name] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_update::dump() const -> std::string {
|
||||
std::stringstream query;
|
||||
query << "UPDATE \"" << ctx_->table_name << "\" SET ";
|
||||
|
||||
for (std::int32_t idx = 0;
|
||||
idx < static_cast<std::int32_t>(ctx_->column_values.size()); idx++) {
|
||||
if (idx > 0) {
|
||||
query << ", ";
|
||||
}
|
||||
|
||||
auto column = std::next(ctx_->column_values.begin(), idx);
|
||||
query << '"' << column->first << "\"=?" + std::to_string(idx + 1);
|
||||
}
|
||||
|
||||
if (ctx_->where_data) {
|
||||
auto idx{static_cast<std::int32_t>(ctx_->column_values.size())};
|
||||
query << " WHERE " << ctx_->where_data->base.dump(idx);
|
||||
}
|
||||
|
||||
if (ctx_->order_by.has_value()) {
|
||||
query << " ORDER BY \"" << ctx_->order_by.value().first << "\" ";
|
||||
query << (ctx_->order_by.value().second ? "ASC" : "DESC");
|
||||
}
|
||||
|
||||
if (ctx_->limit.has_value()) {
|
||||
query << " LIMIT " << ctx_->limit.value();
|
||||
}
|
||||
|
||||
query << ';';
|
||||
|
||||
return query.str();
|
||||
}
|
||||
|
||||
auto db_update::go() const -> db_result {
|
||||
sqlite3_stmt *stmt_ptr{nullptr};
|
||||
|
||||
auto query_str = dump();
|
||||
auto res =
|
||||
sqlite3_prepare_v2(ctx_->db3, query_str.c_str(), -1, &stmt_ptr, nullptr);
|
||||
|
||||
auto stmt = db3_stmt_t{
|
||||
stmt_ptr,
|
||||
sqlite3_statement_deleter(),
|
||||
};
|
||||
|
||||
if (res != SQLITE_OK) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
for (std::int32_t idx = 0;
|
||||
idx < static_cast<std::int32_t>(ctx_->column_values.size()); idx++) {
|
||||
res =
|
||||
std::visit(overloaded{
|
||||
[&idx, &stmt](std::int64_t data) -> std::int32_t {
|
||||
return sqlite3_bind_int64(stmt.get(), idx + 1, data);
|
||||
},
|
||||
[&idx, &stmt](const std::string &data) -> std::int32_t {
|
||||
return sqlite3_bind_text(stmt.get(), idx + 1,
|
||||
data.c_str(), -1, nullptr);
|
||||
},
|
||||
},
|
||||
std::next(ctx_->column_values.begin(), idx)->second);
|
||||
if (res != SQLITE_OK) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
}
|
||||
|
||||
if (not ctx_->where_data) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
for (std::int32_t idx = 0;
|
||||
idx < static_cast<std::int32_t>(ctx_->where_data->values.size());
|
||||
idx++) {
|
||||
res = std::visit(
|
||||
overloaded{
|
||||
[this, &idx, &stmt](std::int64_t data) -> std::int32_t {
|
||||
return sqlite3_bind_int64(
|
||||
stmt.get(),
|
||||
idx + static_cast<std::int32_t>(ctx_->column_values.size()) +
|
||||
1,
|
||||
data);
|
||||
},
|
||||
[this, &idx, &stmt](const std::string &data) -> std::int32_t {
|
||||
return sqlite3_bind_text(
|
||||
stmt.get(),
|
||||
idx + static_cast<std::int32_t>(ctx_->column_values.size()) +
|
||||
1,
|
||||
data.c_str(), -1, nullptr);
|
||||
},
|
||||
},
|
||||
ctx_->where_data->values.at(static_cast<std::size_t>(idx)));
|
||||
if (res != SQLITE_OK) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
}
|
||||
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
auto db_update::group(context::w_t::group_func_t func) -> context::w_t::wn_t {
|
||||
if (not ctx_->where_data) {
|
||||
ctx_->where_data = std::make_unique<context::wd_t>(context::wd_t{
|
||||
context::w_t{0U, ctx_},
|
||||
{},
|
||||
{},
|
||||
});
|
||||
}
|
||||
|
||||
return ctx_->where_data->base.group(std::move(func));
|
||||
}
|
||||
|
||||
auto db_update::limit(std::int32_t value) -> db_update {
|
||||
ctx_->limit = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_update::order_by(std::string column_name, bool ascending) -> db_update {
|
||||
ctx_->order_by = {column_name, ascending};
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_update::where(std::string column_name) const -> context::w_t::cn_t {
|
||||
if (not ctx_->where_data) {
|
||||
ctx_->where_data = std::make_unique<context::wd_t>(context::wd_t{
|
||||
context::w_t{0U, ctx_},
|
||||
{},
|
||||
{},
|
||||
});
|
||||
}
|
||||
|
||||
return ctx_->where_data->base.where(column_name);
|
||||
}
|
||||
} // namespace monitarr::utils::db::sqlite
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
451
support/src/utils/encrypting_reader.cpp
Normal file
451
support/src/utils/encrypting_reader.cpp
Normal file
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
#include "utils/encrypting_reader.hpp"
|
||||
|
||||
#include "utils/collection.hpp"
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/encryption.hpp"
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/unix.hpp"
|
||||
#include "utils/windows.hpp"
|
||||
|
||||
#if !defined(CURL_READFUNC_ABORT)
|
||||
#define CURL_READFUNC_ABORT (-1)
|
||||
#endif // !defined(CURL_READFUNC_ABORT)
|
||||
|
||||
namespace monitarr::utils::encryption {
|
||||
class encrypting_streambuf final : public encrypting_reader::streambuf {
|
||||
public:
|
||||
encrypting_streambuf(const encrypting_streambuf &) = default;
|
||||
encrypting_streambuf(encrypting_streambuf &&) = delete;
|
||||
auto operator=(const encrypting_streambuf &)
|
||||
-> encrypting_streambuf & = delete;
|
||||
auto operator=(encrypting_streambuf &&) -> encrypting_streambuf & = delete;
|
||||
|
||||
explicit encrypting_streambuf(const encrypting_reader &reader)
|
||||
: reader_(reader) {
|
||||
setg(reinterpret_cast<char *>(0), reinterpret_cast<char *>(0),
|
||||
reinterpret_cast<char *>(reader_.get_total_size()));
|
||||
}
|
||||
|
||||
~encrypting_streambuf() override = default;
|
||||
|
||||
private:
|
||||
encrypting_reader reader_;
|
||||
|
||||
protected:
|
||||
auto seekoff(off_type off, std::ios_base::seekdir dir,
|
||||
std::ios_base::openmode which = std::ios_base::out |
|
||||
std::ios_base::in)
|
||||
-> pos_type override {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if ((which & std::ios_base::in) != std::ios_base::in) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"output is not supported",
|
||||
});
|
||||
}
|
||||
|
||||
const auto set_position = [this](char *next) -> pos_type {
|
||||
if ((next <= egptr()) && (next >= eback())) {
|
||||
setg(eback(), next, reinterpret_cast<char *>(reader_.get_total_size()));
|
||||
return static_cast<std::streamoff>(
|
||||
reinterpret_cast<std::uintptr_t>(gptr()));
|
||||
}
|
||||
|
||||
return {traits_type::eof()};
|
||||
};
|
||||
|
||||
switch (dir) {
|
||||
case std::ios_base::beg:
|
||||
return set_position(eback() + off);
|
||||
|
||||
case std::ios_base::cur:
|
||||
return set_position(gptr() + off);
|
||||
|
||||
case std::ios_base::end:
|
||||
return set_position(egptr() + off);
|
||||
}
|
||||
|
||||
return encrypting_reader::streambuf::seekoff(off, dir, which);
|
||||
}
|
||||
|
||||
auto seekpos(pos_type pos,
|
||||
std::ios_base::openmode which = std::ios_base::out |
|
||||
std::ios_base::in)
|
||||
-> pos_type override {
|
||||
return seekoff(pos, std::ios_base::beg, which);
|
||||
}
|
||||
|
||||
auto uflow() -> int_type override {
|
||||
auto ret = underflow();
|
||||
if (ret == traits_type::eof()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
gbump(1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto underflow() -> int_type override {
|
||||
if (gptr() == egptr()) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
reader_.set_read_position(reinterpret_cast<std::uintptr_t>(gptr()));
|
||||
|
||||
char c{};
|
||||
const auto res = encrypting_reader::reader_function(&c, 1U, 1U, &reader_);
|
||||
if (res != 1) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
auto xsgetn(char *ptr, std::streamsize count) -> std::streamsize override {
|
||||
if (gptr() == egptr()) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
reader_.set_read_position(reinterpret_cast<std::uintptr_t>(gptr()));
|
||||
|
||||
auto res = encrypting_reader::reader_function(
|
||||
ptr, 1U, static_cast<std::size_t>(count), &reader_);
|
||||
if ((res == reader_.get_error_return()) ||
|
||||
(reader_.get_stop_requested() &&
|
||||
(res == static_cast<std::size_t>(CURL_READFUNC_ABORT)))) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
setg(eback(), gptr() + res,
|
||||
reinterpret_cast<char *>(reader_.get_total_size()));
|
||||
return static_cast<std::streamsize>(res);
|
||||
}
|
||||
};
|
||||
|
||||
class encrypting_reader_iostream final : public encrypting_reader::iostream {
|
||||
public:
|
||||
encrypting_reader_iostream(const encrypting_reader_iostream &) = delete;
|
||||
encrypting_reader_iostream(encrypting_reader_iostream &&) = delete;
|
||||
|
||||
auto operator=(const encrypting_reader_iostream &)
|
||||
-> encrypting_reader_iostream & = delete;
|
||||
auto operator=(encrypting_reader_iostream &&)
|
||||
-> encrypting_reader_iostream & = delete;
|
||||
|
||||
explicit encrypting_reader_iostream(
|
||||
std::unique_ptr<encrypting_streambuf> buffer)
|
||||
: encrypting_reader::iostream(buffer.get()), buffer_(std::move(buffer)) {}
|
||||
|
||||
~encrypting_reader_iostream() override = default;
|
||||
|
||||
private:
|
||||
std::unique_ptr<encrypting_streambuf> buffer_;
|
||||
};
|
||||
|
||||
const std::size_t encrypting_reader::header_size_ = ([]() {
|
||||
return crypto_aead_xchacha20poly1305_IETF_NPUBBYTES +
|
||||
crypto_aead_xchacha20poly1305_IETF_ABYTES;
|
||||
})();
|
||||
const std::size_t encrypting_reader::data_chunk_size_ = (8UL * 1024UL * 1024UL);
|
||||
const std::size_t encrypting_reader::encrypted_chunk_size_ =
|
||||
data_chunk_size_ + header_size_;
|
||||
|
||||
encrypting_reader::encrypting_reader(
|
||||
std::string_view file_name, std::string_view source_path,
|
||||
stop_type_callback stop_requested_cb, std::string_view token,
|
||||
std::optional<std::string> relative_parent_path, std::size_t error_return)
|
||||
: key_(utils::encryption::generate_key<utils::encryption::hash_256_t>(
|
||||
token)),
|
||||
stop_requested_cb_(std::move(stop_requested_cb)),
|
||||
error_return_(error_return),
|
||||
source_file_(utils::file::file::open_or_create_file(source_path, true)) {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (not*source_file_) {
|
||||
throw utils::error::create_exception(function_name, {
|
||||
"file open failed",
|
||||
source_path,
|
||||
});
|
||||
}
|
||||
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(
|
||||
key_, reinterpret_cast<const unsigned char *>(file_name.data()),
|
||||
file_name.size(), result);
|
||||
encrypted_file_name_ = utils::collection::to_hex_string(result);
|
||||
|
||||
if (relative_parent_path.has_value()) {
|
||||
for (auto &&part :
|
||||
utils::string::split(relative_parent_path.value(),
|
||||
utils::path::directory_seperator, false)) {
|
||||
utils::encryption::encrypt_data(
|
||||
key_, reinterpret_cast<const unsigned char *>(part.c_str()),
|
||||
strnlen(part.c_str(), part.size()), result);
|
||||
encrypted_file_path_ += '/' + utils::collection::to_hex_string(result);
|
||||
}
|
||||
encrypted_file_path_ += '/' + encrypted_file_name_;
|
||||
}
|
||||
|
||||
auto opt_size = source_file_->size();
|
||||
if (not opt_size.has_value()) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to get file size",
|
||||
source_file_->get_path(),
|
||||
});
|
||||
}
|
||||
auto file_size = opt_size.value();
|
||||
|
||||
const auto total_chunks = utils::divide_with_ceiling(
|
||||
file_size, static_cast<std::uint64_t>(data_chunk_size_));
|
||||
total_size_ = file_size + (total_chunks * encryption_header_size);
|
||||
last_data_chunk_ = total_chunks - 1U;
|
||||
last_data_chunk_size_ = (file_size <= data_chunk_size_) ? file_size
|
||||
: (file_size % data_chunk_size_) == 0U
|
||||
? data_chunk_size_
|
||||
: file_size % data_chunk_size_;
|
||||
iv_list_.resize(total_chunks);
|
||||
for (auto &iv : iv_list_) {
|
||||
randombytes_buf(iv.data(), iv.size());
|
||||
}
|
||||
}
|
||||
|
||||
encrypting_reader::encrypting_reader(std::string_view encrypted_file_path,
|
||||
std::string_view source_path,
|
||||
stop_type_callback stop_requested_cb,
|
||||
std::string_view token,
|
||||
std::size_t error_return)
|
||||
: key_(utils::encryption::generate_key<utils::encryption::hash_256_t>(
|
||||
token)),
|
||||
stop_requested_cb_(std::move(stop_requested_cb)),
|
||||
error_return_(error_return),
|
||||
source_file_(utils::file::file::open_or_create_file(source_path, true)) {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (not*source_file_) {
|
||||
throw utils::error::create_exception(function_name, {
|
||||
"file open failed",
|
||||
source_path,
|
||||
});
|
||||
}
|
||||
|
||||
encrypted_file_path_ = encrypted_file_path;
|
||||
encrypted_file_name_ = utils::path::strip_to_file_name(encrypted_file_path_);
|
||||
|
||||
auto opt_size = source_file_->size();
|
||||
if (not opt_size.has_value()) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to get file size",
|
||||
source_file_->get_path(),
|
||||
});
|
||||
}
|
||||
auto file_size = opt_size.value();
|
||||
|
||||
const auto total_chunks = utils::divide_with_ceiling(
|
||||
file_size, static_cast<std::uint64_t>(data_chunk_size_));
|
||||
total_size_ = file_size + (total_chunks * encryption_header_size);
|
||||
last_data_chunk_ = total_chunks - 1U;
|
||||
last_data_chunk_size_ = (file_size <= data_chunk_size_) ? file_size
|
||||
: (file_size % data_chunk_size_) == 0U
|
||||
? data_chunk_size_
|
||||
: file_size % data_chunk_size_;
|
||||
iv_list_.resize(total_chunks);
|
||||
for (auto &iv : iv_list_) {
|
||||
randombytes_buf(iv.data(), iv.size());
|
||||
}
|
||||
}
|
||||
|
||||
encrypting_reader::encrypting_reader(
|
||||
std::string_view encrypted_file_path, std::string_view source_path,
|
||||
stop_type_callback stop_requested_cb, std::string_view token,
|
||||
std::vector<
|
||||
std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>>
|
||||
iv_list,
|
||||
std::size_t error_return)
|
||||
: key_(utils::encryption::generate_key<utils::encryption::hash_256_t>(
|
||||
token)),
|
||||
stop_requested_cb_(std::move(stop_requested_cb)),
|
||||
error_return_(error_return),
|
||||
source_file_(utils::file::file::open_or_create_file(source_path, true)) {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (not*source_file_) {
|
||||
throw utils::error::create_exception(function_name, {
|
||||
"file open failed",
|
||||
source_path,
|
||||
});
|
||||
}
|
||||
|
||||
encrypted_file_path_ = encrypted_file_path;
|
||||
encrypted_file_name_ = utils::path::strip_to_file_name(encrypted_file_path_);
|
||||
|
||||
auto opt_size = source_file_->size();
|
||||
if (not opt_size.has_value()) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"get file size failed",
|
||||
std::to_string(utils::get_last_error_code()),
|
||||
source_file_->get_path(),
|
||||
});
|
||||
}
|
||||
auto file_size{opt_size.value()};
|
||||
|
||||
const auto total_chunks = utils::divide_with_ceiling(
|
||||
file_size, static_cast<std::uint64_t>(data_chunk_size_));
|
||||
total_size_ = file_size + (total_chunks * encryption_header_size);
|
||||
last_data_chunk_ = total_chunks - 1U;
|
||||
last_data_chunk_size_ = (file_size <= data_chunk_size_) ? file_size
|
||||
: (file_size % data_chunk_size_) == 0U
|
||||
? data_chunk_size_
|
||||
: file_size % data_chunk_size_;
|
||||
iv_list_ = std::move(iv_list);
|
||||
}
|
||||
|
||||
encrypting_reader::encrypting_reader(const encrypting_reader &reader)
|
||||
: key_(reader.key_),
|
||||
stop_requested_cb_(reader.stop_requested_cb_),
|
||||
error_return_(reader.error_return_),
|
||||
source_file_(
|
||||
utils::file::file::open_file(reader.source_file_->get_path(), true)),
|
||||
chunk_buffers_(reader.chunk_buffers_),
|
||||
encrypted_file_name_(reader.encrypted_file_name_),
|
||||
encrypted_file_path_(reader.encrypted_file_path_),
|
||||
iv_list_(reader.iv_list_),
|
||||
last_data_chunk_(reader.last_data_chunk_),
|
||||
last_data_chunk_size_(reader.last_data_chunk_size_),
|
||||
read_offset_(reader.read_offset_),
|
||||
total_size_(reader.total_size_) {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (not*source_file_) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"file open failed",
|
||||
std::to_string(utils::get_last_error_code()),
|
||||
source_file_->get_path(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
auto encrypting_reader::calculate_decrypted_size(std::uint64_t total_size)
|
||||
-> std::uint64_t {
|
||||
return total_size - (utils::divide_with_ceiling(
|
||||
total_size, static_cast<std::uint64_t>(
|
||||
get_encrypted_chunk_size())) *
|
||||
encryption_header_size);
|
||||
}
|
||||
|
||||
auto encrypting_reader::calculate_encrypted_size(std::string_view source_path)
|
||||
-> std::uint64_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
auto opt_size = utils::file::file{source_path}.size();
|
||||
if (not opt_size.has_value()) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"get file size failed",
|
||||
std::to_string(utils::get_last_error_code()),
|
||||
source_path,
|
||||
});
|
||||
}
|
||||
auto file_size{opt_size.value()};
|
||||
|
||||
const auto total_chunks = utils::divide_with_ceiling(
|
||||
file_size, static_cast<std::uint64_t>(data_chunk_size_));
|
||||
return file_size + (total_chunks * encryption_header_size);
|
||||
}
|
||||
|
||||
auto encrypting_reader::create_iostream() const
|
||||
-> std::shared_ptr<encrypting_reader::iostream> {
|
||||
return std::make_shared<encrypting_reader_iostream>(
|
||||
std::make_unique<encrypting_streambuf>(*this));
|
||||
}
|
||||
|
||||
auto encrypting_reader::reader_function(char *buffer, size_t size,
|
||||
size_t nitems) -> size_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
const auto read_size = static_cast<std::size_t>(std::min(
|
||||
static_cast<std::uint64_t>(size * nitems), total_size_ - read_offset_));
|
||||
|
||||
auto chunk = read_offset_ / encrypted_chunk_size_;
|
||||
auto chunk_offset = read_offset_ % encrypted_chunk_size_;
|
||||
std::size_t total_read{};
|
||||
|
||||
auto ret = false;
|
||||
if (read_offset_ < total_size_) {
|
||||
try {
|
||||
ret = true;
|
||||
auto remain = read_size;
|
||||
while (not get_stop_requested() && ret && (remain != 0U)) {
|
||||
if (chunk_buffers_.find(chunk) == chunk_buffers_.end()) {
|
||||
auto &chunk_buffer = chunk_buffers_[chunk];
|
||||
data_buffer file_data(chunk == last_data_chunk_
|
||||
? last_data_chunk_size_
|
||||
: data_chunk_size_);
|
||||
chunk_buffer.resize(file_data.size() + encryption_header_size);
|
||||
|
||||
std::size_t bytes_read{};
|
||||
if ((ret = source_file_->read(file_data, chunk * data_chunk_size_,
|
||||
&bytes_read))) {
|
||||
utils::encryption::encrypt_data(iv_list_.at(chunk), key_, file_data,
|
||||
chunk_buffer);
|
||||
}
|
||||
} else if (chunk) {
|
||||
chunk_buffers_.erase(chunk - 1u);
|
||||
}
|
||||
|
||||
auto &chunk_buffer = chunk_buffers_[chunk];
|
||||
const auto to_read = std::min(
|
||||
static_cast<std::size_t>(chunk_buffer.size() - chunk_offset),
|
||||
remain);
|
||||
std::memcpy(buffer + total_read, &chunk_buffer[chunk_offset], to_read);
|
||||
total_read += to_read;
|
||||
remain -= to_read;
|
||||
chunk_offset = 0u;
|
||||
chunk++;
|
||||
read_offset_ += to_read;
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
ret = false;
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
return get_stop_requested() ? static_cast<std::size_t>(CURL_READFUNC_ABORT)
|
||||
: ret ? total_read
|
||||
: error_return_;
|
||||
}
|
||||
} // namespace monitarr::utils::encryption
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
162
support/src/utils/encryption.cpp
Normal file
162
support/src/utils/encryption.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
#include "utils/encryption.hpp"
|
||||
|
||||
#include "utils/collection.hpp"
|
||||
#include "utils/encrypting_reader.hpp"
|
||||
#include "utils/path.hpp"
|
||||
|
||||
namespace monitarr::utils::encryption {
|
||||
auto decrypt_file_path(std::string_view encryption_token,
|
||||
std::string &file_path) -> bool {
|
||||
std::vector<std::string> decrypted_parts;
|
||||
for (const auto &part : std::filesystem::path(file_path)) {
|
||||
auto file_name = part.string();
|
||||
if (file_name == "/") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (not decrypt_file_name(encryption_token, file_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
decrypted_parts.push_back(file_name);
|
||||
}
|
||||
|
||||
file_path =
|
||||
utils::path::create_api_path(utils::string::join(decrypted_parts, '/'));
|
||||
return true;
|
||||
}
|
||||
|
||||
auto decrypt_file_name(std::string_view encryption_token,
|
||||
std::string &file_name) -> bool {
|
||||
data_buffer buffer;
|
||||
if (not utils::collection::from_hex_string(file_name, buffer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
file_name.clear();
|
||||
return utils::encryption::decrypt_data(encryption_token, buffer, file_name);
|
||||
}
|
||||
|
||||
auto read_encrypted_range(const http_range &range,
|
||||
const utils::encryption::hash_256_t &key,
|
||||
reader_func_t reader_func, std::uint64_t total_size,
|
||||
data_buffer &data) -> bool {
|
||||
auto encrypted_chunk_size =
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size();
|
||||
auto data_chunk_size =
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size();
|
||||
|
||||
auto start_chunk = static_cast<std::size_t>(range.begin / data_chunk_size);
|
||||
auto end_chunk = static_cast<std::size_t>(range.end / data_chunk_size);
|
||||
auto remain = range.end - range.begin + 1U;
|
||||
auto source_offset = static_cast<std::size_t>(range.begin % data_chunk_size);
|
||||
|
||||
for (std::size_t chunk = start_chunk; chunk <= end_chunk; chunk++) {
|
||||
data_buffer cypher;
|
||||
auto start_offset = chunk * encrypted_chunk_size;
|
||||
auto end_offset = std::min(
|
||||
start_offset + (total_size - (chunk * data_chunk_size)) +
|
||||
encryption_header_size - 1U,
|
||||
static_cast<std::uint64_t>(start_offset + encrypted_chunk_size - 1U));
|
||||
|
||||
if (not reader_func(cypher, start_offset, end_offset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data_buffer source_buffer;
|
||||
if (not utils::encryption::decrypt_data(key, cypher, source_buffer)) {
|
||||
return false;
|
||||
}
|
||||
cypher.clear();
|
||||
|
||||
auto data_size = static_cast<std::size_t>(std::min(
|
||||
remain, static_cast<std::uint64_t>(data_chunk_size - source_offset)));
|
||||
std::copy(std::next(source_buffer.begin(),
|
||||
static_cast<std::int64_t>(source_offset)),
|
||||
std::next(source_buffer.begin(),
|
||||
static_cast<std::int64_t>(source_offset + data_size)),
|
||||
std::back_inserter(data));
|
||||
remain -= data_size;
|
||||
source_offset = 0U;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto read_encrypted_range(const http_range &range,
|
||||
const utils::encryption::hash_256_t &key,
|
||||
reader_func_t reader_func, std::uint64_t total_size,
|
||||
unsigned char *data, std::size_t size,
|
||||
std::size_t &bytes_read) -> bool {
|
||||
bytes_read = 0U;
|
||||
|
||||
auto encrypted_chunk_size =
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size();
|
||||
auto data_chunk_size =
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size();
|
||||
|
||||
auto start_chunk = static_cast<std::size_t>(range.begin / data_chunk_size);
|
||||
auto end_chunk = static_cast<std::size_t>(range.end / data_chunk_size);
|
||||
auto remain = range.end - range.begin + 1U;
|
||||
auto source_offset = static_cast<std::size_t>(range.begin % data_chunk_size);
|
||||
|
||||
std::span dest_buffer(data, size);
|
||||
for (std::size_t chunk = start_chunk; chunk <= end_chunk; chunk++) {
|
||||
data_buffer cypher;
|
||||
auto start_offset = chunk * encrypted_chunk_size;
|
||||
auto end_offset = std::min(
|
||||
start_offset + (total_size - (chunk * data_chunk_size)) +
|
||||
encryption_header_size - 1U,
|
||||
static_cast<std::uint64_t>(start_offset + encrypted_chunk_size - 1U));
|
||||
|
||||
if (not reader_func(cypher, start_offset, end_offset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data_buffer source_buffer;
|
||||
if (not utils::encryption::decrypt_data(key, cypher, source_buffer)) {
|
||||
return false;
|
||||
}
|
||||
cypher.clear();
|
||||
|
||||
auto data_size = static_cast<std::size_t>(std::min(
|
||||
remain, static_cast<std::uint64_t>(data_chunk_size - source_offset)));
|
||||
std::copy(
|
||||
std::next(source_buffer.begin(),
|
||||
static_cast<std::int64_t>(source_offset)),
|
||||
std::next(source_buffer.begin(),
|
||||
static_cast<std::int64_t>(source_offset + data_size)),
|
||||
std::next(dest_buffer.begin(), static_cast<std::int64_t>(bytes_read)));
|
||||
remain -= data_size;
|
||||
bytes_read += data_size;
|
||||
source_offset = 0U;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace monitarr::utils::encryption
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined (PROJECT_ENABLE_BOOST)
|
||||
82
support/src/utils/error.cpp
Normal file
82
support/src/utils/error.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/error.hpp"
|
||||
|
||||
namespace monitarr::utils::error {
|
||||
std::atomic<const i_exception_handler *> exception_handler{
|
||||
&default_exception_handler};
|
||||
|
||||
auto create_error_message(std::vector<std::string_view> items) -> std::string {
|
||||
std::stringstream stream{};
|
||||
for (std::size_t idx = 0U; idx < items.size(); ++idx) {
|
||||
if (idx > 0) {
|
||||
stream << '|';
|
||||
}
|
||||
|
||||
stream << items.at(idx);
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
auto create_exception(std::string_view function_name,
|
||||
std::vector<std::string_view> items)
|
||||
-> std::runtime_error {
|
||||
items.insert(items.begin(), function_name);
|
||||
return std::runtime_error(create_error_message(items));
|
||||
}
|
||||
|
||||
void handle_error(std::string_view function_name, std::string_view msg) {
|
||||
const i_exception_handler *handler{exception_handler};
|
||||
if (handler != nullptr) {
|
||||
handler->handle_error(function_name, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
default_exception_handler.handle_error(function_name, msg);
|
||||
}
|
||||
|
||||
void handle_exception(std::string_view function_name) {
|
||||
const i_exception_handler *handler{exception_handler};
|
||||
if (handler != nullptr) {
|
||||
handler->handle_exception(function_name);
|
||||
return;
|
||||
}
|
||||
|
||||
default_exception_handler.handle_exception(function_name);
|
||||
}
|
||||
|
||||
void handle_exception(std::string_view function_name,
|
||||
const std::exception &ex) {
|
||||
const i_exception_handler *handler{exception_handler};
|
||||
if (handler != nullptr) {
|
||||
handler->handle_exception(function_name, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
default_exception_handler.handle_exception(function_name, ex);
|
||||
}
|
||||
|
||||
void set_exception_handler(const i_exception_handler *handler) {
|
||||
exception_handler = handler;
|
||||
}
|
||||
} // namespace monitarr::utils::error
|
||||
713
support/src/utils/file.cpp
Normal file
713
support/src/utils/file.cpp
Normal file
@@ -0,0 +1,713 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/file.hpp"
|
||||
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/encryption.hpp"
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/path.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "utils/time.hpp"
|
||||
#include "utils/unix.hpp"
|
||||
#include "utils/windows.hpp"
|
||||
|
||||
namespace monitarr::utils::file {
|
||||
auto change_to_process_directory() -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
#if defined(_WIN32)
|
||||
std::string file_name;
|
||||
file_name.resize(monitarr::max_path_length + 1U);
|
||||
|
||||
::GetModuleFileNameA(nullptr, file_name.data(),
|
||||
static_cast<DWORD>(file_name.size() - 1U));
|
||||
auto path = utils::path::strip_to_file_name(file_name.c_str());
|
||||
::SetCurrentDirectoryA(path.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
std::string path;
|
||||
path.resize(PATH_MAX + 1);
|
||||
#if defined(__APPLE__)
|
||||
proc_pidpath(getpid(), path.c_str(), path.size());
|
||||
#else // !defined(__APPLE__)
|
||||
auto res = readlink("/proc/self/exe", path.data(), path.size());
|
||||
if (res == -1) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to readlink",
|
||||
std::to_string(utils::get_last_error_code()),
|
||||
path,
|
||||
});
|
||||
}
|
||||
#endif // defined(__APPLE__)
|
||||
path = utils::path::get_parent_path(path);
|
||||
res = chdir(path.c_str());
|
||||
if (res != 0) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to chdir",
|
||||
std::to_string(utils::get_last_error_code()),
|
||||
path,
|
||||
});
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto create_temp_name(std::string_view file_part) -> std::string {
|
||||
std::array<std::uint8_t, 8U> data{
|
||||
utils::generate_random_between<std::uint8_t>(0U, 9U),
|
||||
utils::generate_random_between<std::uint8_t>(0U, 9U),
|
||||
utils::generate_random_between<std::uint8_t>(0U, 9U),
|
||||
utils::generate_random_between<std::uint8_t>(0U, 9U),
|
||||
utils::generate_random_between<std::uint8_t>(0U, 9U),
|
||||
utils::generate_random_between<std::uint8_t>(0U, 9U),
|
||||
utils::generate_random_between<std::uint8_t>(0U, 9U),
|
||||
utils::generate_random_between<std::uint8_t>(0U, 9U),
|
||||
};
|
||||
|
||||
return std::accumulate(data.begin(), data.end(), std::string{file_part} + '_',
|
||||
[](auto &&name, auto &&val) -> auto {
|
||||
return name + std::to_string(val);
|
||||
});
|
||||
}
|
||||
|
||||
auto create_temp_name(std::wstring_view file_part) -> std::wstring {
|
||||
return utils::string::from_utf8(
|
||||
create_temp_name(utils::string::to_utf8(file_part)));
|
||||
}
|
||||
|
||||
auto get_free_drive_space(std::string_view path)
|
||||
-> std::optional<std::uint64_t> {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
#if defined(_WIN32)
|
||||
ULARGE_INTEGER li{};
|
||||
if (not::GetDiskFreeSpaceEx(std::string{path}.c_str(), &li, nullptr,
|
||||
nullptr)) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to get free disk space",
|
||||
std::to_string(utils::get_last_error_code()),
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
return li.QuadPart;
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
#if defined(__linux__)
|
||||
struct statfs64 st{};
|
||||
if (statfs64(std::string{path}.c_str(), &st) != 0) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to get free disk space",
|
||||
std::to_string(utils::get_last_error_code()),
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
return st.f_bfree * static_cast<std::uint64_t>(st.f_bsize);
|
||||
#endif // defined(__linux__)
|
||||
|
||||
#if defined(__APPLE__)
|
||||
struct statvfs st{};
|
||||
if (statvfs(path.c_str(), &st) != 0) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to get free disk space",
|
||||
std::to_string(utils::get_last_error_code()),
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
return st.f_bfree * static_cast<std::uint64_t>(st.f_frsize);
|
||||
#endif // defined(__APPLE__)
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto get_free_drive_space(std::wstring_view path)
|
||||
-> std::optional<std::uint64_t> {
|
||||
return get_free_drive_space(utils::string::to_utf8(path));
|
||||
}
|
||||
|
||||
auto get_time(std::string_view path, time_type type)
|
||||
-> std::optional<std::uint64_t> {
|
||||
auto times = get_times(path);
|
||||
if (times.has_value()) {
|
||||
return times->get(type);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto get_time(std::wstring_view path, time_type type)
|
||||
-> std::optional<std::uint64_t> {
|
||||
return get_time(utils::string::to_utf8(path), type);
|
||||
}
|
||||
|
||||
auto get_times(std::string_view path) -> std::optional<file_times> {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
file_times ret{};
|
||||
|
||||
#if defined(_WIN32)
|
||||
auto file_handle = ::CreateFileA(
|
||||
std::string{path}.c_str(), GENERIC_READ,
|
||||
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
|
||||
if (file_handle != INVALID_HANDLE_VALUE) {
|
||||
std::array<FILETIME, 3U> times{};
|
||||
auto res = ::GetFileTime(file_handle, ×.at(0U), ×.at(1U),
|
||||
×.at(2U));
|
||||
::CloseHandle(file_handle);
|
||||
if (res) {
|
||||
ret.accessed =
|
||||
utils::time::windows_file_time_to_unix_time(times.at(1U));
|
||||
ret.created = utils::time::windows_file_time_to_unix_time(times.at(0U));
|
||||
ret.modified =
|
||||
utils::time::windows_file_time_to_unix_time(times.at(2U));
|
||||
ret.written = utils::time::windows_file_time_to_unix_time(times.at(2U));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
struct _stat64 st{};
|
||||
if (_stat64(std::string{path}.c_str(), &st) != 0) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to get file times",
|
||||
std::to_string(utils::get_last_error_code()),
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
ret.accessed = utils::time::windows_time_t_to_unix_time(st.st_atime);
|
||||
ret.created = utils::time::windows_time_t_to_unix_time(st.st_ctime);
|
||||
ret.modified = utils::time::windows_time_t_to_unix_time(st.st_mtime);
|
||||
ret.written = utils::time::windows_time_t_to_unix_time(st.st_mtime);
|
||||
#else // !defined(_WIN32)
|
||||
struct stat64 st{};
|
||||
if (stat64(std::string{path}.c_str(), &st) != 0) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to get file times",
|
||||
std::to_string(utils::get_last_error_code()),
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
ret.accessed = static_cast<std::uint64_t>(st.st_atim.tv_nsec) +
|
||||
static_cast<std::uint64_t>(st.st_atim.tv_sec) *
|
||||
utils::time::NANOS_PER_SECOND;
|
||||
ret.created = static_cast<std::uint64_t>(st.st_ctim.tv_nsec) +
|
||||
static_cast<std::uint64_t>(st.st_ctim.tv_sec) *
|
||||
utils::time::NANOS_PER_SECOND;
|
||||
ret.modified = static_cast<std::uint64_t>(st.st_mtim.tv_nsec) +
|
||||
static_cast<std::uint64_t>(st.st_mtim.tv_sec) *
|
||||
utils::time::NANOS_PER_SECOND;
|
||||
ret.written = static_cast<std::uint64_t>(st.st_mtim.tv_nsec) +
|
||||
static_cast<std::uint64_t>(st.st_mtim.tv_sec) *
|
||||
utils::time::NANOS_PER_SECOND;
|
||||
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto get_times(std::wstring_view path) -> std::optional<file_times> {
|
||||
return get_times(utils::string::to_utf8(path));
|
||||
}
|
||||
|
||||
auto get_total_drive_space(std::string_view path)
|
||||
-> std::optional<std::uint64_t> {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
#if defined(_WIN32)
|
||||
ULARGE_INTEGER li{};
|
||||
if (not::GetDiskFreeSpaceEx(std::string{path}.c_str(), nullptr, &li,
|
||||
nullptr)) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to get total disk space",
|
||||
std::to_string(utils::get_last_error_code()),
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
return li.QuadPart;
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
#if defined(__linux__)
|
||||
struct statfs64 st{};
|
||||
if (statfs64(std::string{path}.c_str(), &st) != 0) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to get total disk space",
|
||||
std::to_string(utils::get_last_error_code()),
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
return st.f_blocks * static_cast<std::uint64_t>(st.f_bsize);
|
||||
#endif // defined(__linux__)
|
||||
|
||||
#if defined(__APPLE__)
|
||||
struct statvfs st{};
|
||||
if (statvfs(path.c_str(), &st) != 0) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to get total disk space",
|
||||
std::to_string(utils::get_last_error_code()),
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
return st.f_blocks * static_cast<std::uint64_t>(st.f_frsize);
|
||||
#endif // defined(__APPLE__)
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto get_total_drive_space(std::wstring_view path)
|
||||
-> std::optional<std::uint64_t> {
|
||||
return get_total_drive_space(utils::string::to_utf8(path));
|
||||
}
|
||||
|
||||
auto i_fs_item::get_time(time_type type) const -> std::optional<std::uint64_t> {
|
||||
return utils::file::get_time(get_path(), type);
|
||||
}
|
||||
|
||||
auto i_file::read_all(data_buffer &data, std::uint64_t offset,
|
||||
std::size_t *total_read) -> bool {
|
||||
data_buffer buffer;
|
||||
buffer.resize(get_read_buffer_size());
|
||||
|
||||
std::size_t current_read{};
|
||||
while (read(reinterpret_cast<unsigned char *>(buffer.data()),
|
||||
buffer.size() * sizeof(data_buffer::value_type), offset,
|
||||
¤t_read)) {
|
||||
if (total_read != nullptr) {
|
||||
*total_read += current_read;
|
||||
}
|
||||
|
||||
if (current_read != 0U) {
|
||||
offset += current_read;
|
||||
|
||||
data.insert(
|
||||
data.end(), buffer.begin(),
|
||||
std::next(buffer.begin(),
|
||||
static_cast<std::int64_t>(
|
||||
current_read / sizeof(data_buffer::value_type))));
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_JSON)
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
auto read_json_file(std::string_view path, nlohmann::json &data,
|
||||
std::optional<std::string_view> password) -> bool {
|
||||
#else // !defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
auto read_json_file(std::string_view path, nlohmann::json &data) -> bool {
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
auto abs_path = utils::path::absolute(path);
|
||||
auto file = file::open_file(abs_path);
|
||||
if (not*file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
data_buffer buffer{};
|
||||
if (not file->read_all(buffer, 0U)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
if (password.has_value()) {
|
||||
data_buffer decrypted_data{};
|
||||
if (not utils::encryption::decrypt_data(*password, buffer,
|
||||
decrypted_data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer = decrypted_data;
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
std::string json_str(buffer.begin(), buffer.end());
|
||||
if (not json_str.empty()) {
|
||||
data = nlohmann::json::parse(json_str);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
return false;
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
auto write_json_file(std::string_view path, const nlohmann::json &data,
|
||||
std::optional<std::string_view> password) -> bool {
|
||||
#else // !defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
auto write_json_file(std::string_view path, const nlohmann::json &data)
|
||||
-> bool {
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
auto file = file::open_or_create_file(path);
|
||||
if (not file->truncate()) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to truncate file",
|
||||
std::to_string(utils::get_last_error_code()),
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
if (password.has_value()) {
|
||||
const auto str_data = data.dump(2);
|
||||
|
||||
data_buffer encrypted_data{};
|
||||
utils::encryption::encrypt_data(
|
||||
*password, reinterpret_cast<const unsigned char *>(str_data.c_str()),
|
||||
str_data.size(), encrypted_data);
|
||||
return file->write(encrypted_data, 0U);
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
auto json_str = data.dump(2);
|
||||
return file->write(
|
||||
reinterpret_cast<const unsigned char *>(json_str.c_str()),
|
||||
json_str.size(), 0U);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
auto read_json_file(std::wstring_view path, nlohmann::json &data,
|
||||
std::optional<std::wstring_view> password) -> bool {
|
||||
if (password.has_value()) {
|
||||
auto password_a = utils::string::to_utf8(*password);
|
||||
return read_json_file(utils::string::to_utf8(path), data, password_a);
|
||||
}
|
||||
|
||||
return read_json_file(utils::string::to_utf8(path), data, std::nullopt);
|
||||
}
|
||||
|
||||
auto write_json_file(std::wstring_view path, const nlohmann::json &data,
|
||||
std::optional<std::wstring_view> password) -> bool {
|
||||
if (password.has_value()) {
|
||||
auto password_a = utils::string::to_utf8(*password);
|
||||
return write_json_file(utils::string::to_utf8(path), data, password_a);
|
||||
}
|
||||
|
||||
return write_json_file(utils::string::to_utf8(path), data, std::nullopt);
|
||||
}
|
||||
#else // !defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
auto read_json_file(std::wstring_view path, nlohmann::json &data) -> bool {
|
||||
return read_json_file(utils::string::to_utf8(path), data);
|
||||
}
|
||||
|
||||
auto write_json_file(std::wstring_view path, const nlohmann::json &data)
|
||||
-> bool {
|
||||
return write_json_file(utils::string::to_utf8(path), data);
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
#endif // defined(PROJECT_ENABLE_JSON)
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBDSM)
|
||||
static constexpr const auto validate_smb_path =
|
||||
[](std::string_view path) -> bool {
|
||||
return (not utils::string::begins_with(path, "///") &&
|
||||
utils::string::begins_with(path, "//") &&
|
||||
// not utils::string::contains(path, " ") &&
|
||||
std::count(path.begin(), path.end(), '/') >= 3U);
|
||||
};
|
||||
|
||||
auto smb_create_smb_path(std::string_view smb_path, std::string_view rel_path)
|
||||
-> std::string {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw utils::error::create_exception(function_name, {
|
||||
"invalid smb path",
|
||||
smb_path,
|
||||
});
|
||||
}
|
||||
|
||||
std::string path{rel_path};
|
||||
utils::path::format_path(path, "/", "\\");
|
||||
utils::string::left_trim(path, '/');
|
||||
|
||||
auto old_parts =
|
||||
monitarr::utils::string::split(smb_path.substr(2U), '/', false);
|
||||
|
||||
auto new_parts = monitarr::utils::string::split(path, '/', false);
|
||||
old_parts.insert(old_parts.end(), new_parts.begin(), new_parts.end());
|
||||
|
||||
path = utils::string::join(old_parts, '/');
|
||||
path = "//" + utils::path::format_path(path, "/", "\\");
|
||||
|
||||
if (not validate_smb_path(path)) {
|
||||
throw utils::error::create_exception(function_name, {
|
||||
"invalid smb path",
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
auto smb_create_and_validate_relative_path(std::string_view smb_path,
|
||||
std::string_view path)
|
||||
-> std::string {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw utils::error::create_exception(function_name, {
|
||||
"invalid smb path",
|
||||
smb_path,
|
||||
});
|
||||
}
|
||||
|
||||
std::string dir_path;
|
||||
if (utils::string::begins_with(path, "//")) {
|
||||
if (not utils::file::smb_parent_is_same(smb_path, path)) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to validate path",
|
||||
"parent paths are not the same",
|
||||
smb_path,
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
return utils::file::smb_create_relative_path(path);
|
||||
}
|
||||
|
||||
return utils::file::smb_create_relative_path(std::string{smb_path} + '/' +
|
||||
std::string{path});
|
||||
}
|
||||
|
||||
auto smb_create_relative_path(std::string_view smb_path) -> std::string {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw utils::error::create_exception(function_name, {
|
||||
"invalid smb path",
|
||||
smb_path,
|
||||
});
|
||||
}
|
||||
|
||||
std::string path{smb_path};
|
||||
utils::path::format_path(path, "\\", "/");
|
||||
utils::string::left_trim(path, '\\');
|
||||
|
||||
auto parts = monitarr::utils::string::split(path, '\\', false);
|
||||
parts.erase(parts.begin(), std::next(parts.begin(), 2U));
|
||||
return "\\" + utils::string::join(parts, '\\');
|
||||
}
|
||||
|
||||
auto smb_create_search_path(std::string_view smb_path) -> std::string {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw utils::error::create_exception(function_name, {
|
||||
"invalid smb path",
|
||||
smb_path,
|
||||
});
|
||||
}
|
||||
|
||||
std::string path{smb_path};
|
||||
utils::string::left_trim(path, '/');
|
||||
|
||||
auto parts = monitarr::utils::string::split(path, '/', false);
|
||||
parts.erase(parts.begin(), std::next(parts.begin(), 2U));
|
||||
|
||||
auto search_path = monitarr::utils::string::join(parts, '\\');
|
||||
return search_path.empty() ? "\\*" : "\\" + search_path + "\\*";
|
||||
}
|
||||
|
||||
auto smb_get_parent_path(std::string_view smb_path) -> std::string {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw utils::error::create_exception(function_name, {
|
||||
"invalid smb path",
|
||||
smb_path,
|
||||
});
|
||||
}
|
||||
|
||||
auto parts = monitarr::utils::string::split(smb_path.substr(2U), '/', false);
|
||||
if (parts.size() > 2U) {
|
||||
parts.erase(std::prev(parts.end()), parts.end());
|
||||
}
|
||||
|
||||
auto parent_smb_path = "//" + utils::string::join(parts, '/');
|
||||
if (not validate_smb_path(parent_smb_path)) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"invalid parent smb path",
|
||||
parent_smb_path,
|
||||
});
|
||||
}
|
||||
|
||||
return parent_smb_path;
|
||||
}
|
||||
|
||||
auto smb_get_root_path(std::string_view smb_path) -> std::string {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw utils::error::create_exception(function_name, {
|
||||
"invalid smb path",
|
||||
smb_path,
|
||||
});
|
||||
}
|
||||
|
||||
auto parts = monitarr::utils::string::split(smb_path.substr(2U), '/', false);
|
||||
if (parts.size() > 2U) {
|
||||
parts.erase(std::next(parts.begin(), 2U), parts.end());
|
||||
}
|
||||
|
||||
return "//" + utils::string::join(parts, '/');
|
||||
}
|
||||
|
||||
auto smb_get_unc_path(std::string_view smb_path) -> std::string {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw utils::error::create_exception(function_name, {
|
||||
"invalid smb path",
|
||||
smb_path,
|
||||
});
|
||||
}
|
||||
|
||||
std::string unc_path{smb_path};
|
||||
utils::path::format_path(unc_path, "\\", "/");
|
||||
return '\\' + unc_path;
|
||||
}
|
||||
|
||||
auto smb_get_uri_path(std::string_view smb_path) -> std::string {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw utils::error::create_exception(function_name, {
|
||||
"invalid smb path",
|
||||
smb_path,
|
||||
});
|
||||
}
|
||||
|
||||
return "smb:" + std::string{smb_path};
|
||||
}
|
||||
|
||||
auto smb_get_uri_path(std::string_view smb_path, std::string_view user,
|
||||
std::string_view password) -> std::string {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw utils::error::create_exception(function_name, {
|
||||
"invalid smb path",
|
||||
smb_path,
|
||||
});
|
||||
}
|
||||
|
||||
return "smb://" + std::string{user} + ':' + std::string{password} + '@' +
|
||||
std::string{smb_path.substr(2U)};
|
||||
}
|
||||
|
||||
auto smb_parent_is_same(std::string_view smb_path1, std::string_view smb_path2)
|
||||
-> bool {
|
||||
if (not(validate_smb_path(smb_path1) && validate_smb_path(smb_path2))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto parts1 = utils::string::split(smb_path1.substr(2U), "/", false);
|
||||
auto parts2 = utils::string::split(smb_path2.substr(2U), "/", false);
|
||||
if (parts1.size() < 2U || parts2.size() < 2U) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parts2.at(1U).empty() || parts1.at(1U).empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::equal(parts1.begin(), std::next(parts1.begin(), 2U),
|
||||
parts2.begin());
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBDSM)
|
||||
} // namespace monitarr::utils::file
|
||||
486
support/src/utils/file_directory.cpp
Normal file
486
support/src/utils/file_directory.cpp
Normal file
@@ -0,0 +1,486 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/file_directory.hpp"
|
||||
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "utils/unix.hpp"
|
||||
#include "utils/windows.hpp"
|
||||
|
||||
namespace {
|
||||
auto traverse_directory(
|
||||
std::string_view path,
|
||||
std::function<bool(monitarr::utils::file::directory)> directory_action,
|
||||
std::function<bool(monitarr::utils::file::file)> file_action,
|
||||
monitarr::stop_type *stop_requested) -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
auto res{true};
|
||||
|
||||
const auto is_stop_requested = [&stop_requested]() -> bool {
|
||||
return (stop_requested != nullptr && *stop_requested);
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
WIN32_FIND_DATAA fd{};
|
||||
auto search = monitarr::utils::path::combine(path, {"*.*"});
|
||||
auto find = ::FindFirstFileA(search.c_str(), &fd);
|
||||
if (find == INVALID_HANDLE_VALUE) {
|
||||
throw monitarr::utils::error::create_exception(
|
||||
function_name,
|
||||
{
|
||||
"failed to open directory",
|
||||
std::to_string(monitarr::utils::get_last_error_code()),
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
do {
|
||||
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
if ((std::string_view(fd.cFileName) != ".") &&
|
||||
(std::string_view(fd.cFileName) != "..")) {
|
||||
res = directory_action(monitarr::utils::file::directory{
|
||||
monitarr::utils::path::combine(path, {fd.cFileName}),
|
||||
stop_requested,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
res = file_action(monitarr::utils::file::file(
|
||||
monitarr::utils::path::combine(path, {fd.cFileName})));
|
||||
}
|
||||
} while (res && (::FindNextFileA(find, &fd) != 0) && !is_stop_requested());
|
||||
|
||||
::FindClose(find);
|
||||
#else // !defined(_WIN32)
|
||||
auto *root = opendir(std::string{path}.c_str());
|
||||
if (root == nullptr) {
|
||||
throw monitarr::utils::error::create_exception(
|
||||
function_name,
|
||||
{
|
||||
"failed to open directory",
|
||||
std::to_string(monitarr::utils::get_last_error_code()),
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
struct dirent *de{nullptr};
|
||||
while (res && (de = readdir(root)) && !is_stop_requested()) {
|
||||
if (de->d_type == DT_DIR) {
|
||||
if ((std::string_view(de->d_name) != ".") &&
|
||||
(std::string_view(de->d_name) != "..")) {
|
||||
res = directory_action(monitarr::utils::file::directory(
|
||||
monitarr::utils::path::combine(path, {de->d_name})));
|
||||
}
|
||||
} else {
|
||||
res = file_action(monitarr::utils::file::file(
|
||||
monitarr::utils::path::combine(path, {de->d_name})));
|
||||
}
|
||||
}
|
||||
|
||||
closedir(root);
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
return res;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace monitarr::utils::file {
|
||||
auto directory::copy_to(std::string_view new_path,
|
||||
bool overwrite) const -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to copy directory",
|
||||
"not implemented",
|
||||
utils::string::from_bool(overwrite),
|
||||
new_path,
|
||||
path_,
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto directory::count(bool recursive) const -> std::uint64_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
std::uint64_t ret{0U};
|
||||
|
||||
traverse_directory(
|
||||
path_,
|
||||
[&ret, &recursive](auto dir_item) -> bool {
|
||||
if (recursive) {
|
||||
ret += dir_item.count(true);
|
||||
}
|
||||
|
||||
++ret;
|
||||
return true;
|
||||
},
|
||||
[&ret](auto /* file_item */) -> bool {
|
||||
++ret;
|
||||
return true;
|
||||
},
|
||||
stop_requested_);
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return 0U;
|
||||
}
|
||||
|
||||
auto directory::create_directory(std::string_view path) const
|
||||
-> fs_directory_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
auto abs_path = utils::path::combine(path_, {path});
|
||||
if (directory{abs_path, stop_requested_}.exists()) {
|
||||
return std::make_unique<directory>(abs_path);
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
auto res = ::SHCreateDirectory(nullptr,
|
||||
utils::string::from_utf8(abs_path).c_str());
|
||||
if (res != ERROR_SUCCESS) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to create directory",
|
||||
std::to_string(res),
|
||||
abs_path,
|
||||
});
|
||||
}
|
||||
#else // !defined(_WIN32)
|
||||
auto ret{true};
|
||||
auto paths =
|
||||
utils::string::split(abs_path, utils::path::directory_seperator, false);
|
||||
|
||||
std::string current_path;
|
||||
for (std::size_t idx = 0U;
|
||||
!is_stop_requested() && ret && (idx < paths.size()); ++idx) {
|
||||
if (paths.at(idx).empty()) {
|
||||
current_path = utils::path::directory_seperator;
|
||||
continue;
|
||||
}
|
||||
|
||||
current_path = utils::path::combine(current_path, {paths.at(idx)});
|
||||
auto status = mkdir(current_path.c_str(), S_IRWXU);
|
||||
ret = ((status == 0) || (errno == EEXIST));
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
return std::make_unique<directory>(abs_path);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto directory::exists() const -> bool {
|
||||
#if defined(_WIN32)
|
||||
return ::PathIsDirectoryA(path_.c_str()) != 0;
|
||||
#else // !defined(_WIN32)
|
||||
struct stat64 st {};
|
||||
return (stat64(path_.c_str(), &st) == 0 && S_ISDIR(st.st_mode));
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto directory::get_directory(std::string_view path) const -> fs_directory_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
auto dir_path = utils::path::combine(path_, {path});
|
||||
return fs_directory_t{
|
||||
directory{dir_path, stop_requested_}.exists()
|
||||
? new directory(dir_path, stop_requested_)
|
||||
: nullptr,
|
||||
};
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto directory::get_directories() const -> std::vector<fs_directory_t> {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
std::vector<fs_directory_t> ret{};
|
||||
|
||||
traverse_directory(
|
||||
path_,
|
||||
[this, &ret](auto dir_item) -> bool {
|
||||
ret.emplace_back(fs_directory_t{
|
||||
new directory(dir_item.get_path(), stop_requested_),
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
[](auto /* file_item */) -> bool { return true; }, stop_requested_);
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto directory::create_file(std::string_view file_name,
|
||||
bool read_only) const -> fs_file_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
auto file_path = utils::path::combine(path_, {file_name});
|
||||
return file::open_or_create_file(file_path, read_only);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto directory::get_file(std::string_view path) const -> fs_file_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
auto file_path = utils::path::combine(path_, {path});
|
||||
return fs_file_t{
|
||||
file{file_path}.exists() ? new file(file_path) : nullptr,
|
||||
};
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto directory::get_files() const -> std::vector<fs_file_t> {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
std::vector<fs_file_t> ret{};
|
||||
|
||||
traverse_directory(
|
||||
path_, [](auto /* dir_item */) -> bool { return true; },
|
||||
[&ret](auto file_item) -> bool {
|
||||
ret.emplace_back(fs_file_t{
|
||||
new file(file_item.get_path()),
|
||||
});
|
||||
return true;
|
||||
},
|
||||
stop_requested_);
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto directory::get_items() const -> std::vector<fs_item_t> {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
std::vector<fs_item_t> ret{};
|
||||
|
||||
traverse_directory(
|
||||
path_,
|
||||
[this, &ret](auto dir_item) -> bool {
|
||||
ret.emplace_back(fs_item_t{
|
||||
new directory(dir_item.get_path(), stop_requested_),
|
||||
});
|
||||
return true;
|
||||
},
|
||||
[&ret](auto file_item) -> bool {
|
||||
ret.emplace_back(fs_item_t{
|
||||
new file(file_item.get_path()),
|
||||
});
|
||||
return true;
|
||||
},
|
||||
stop_requested_);
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto directory::is_stop_requested() const -> bool {
|
||||
return (stop_requested_ != nullptr) && *stop_requested_;
|
||||
}
|
||||
|
||||
auto directory::is_symlink() const -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
return std::filesystem::is_symlink(path_);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto directory::move_to(std::string_view new_path) -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to move directory",
|
||||
"not implemented",
|
||||
new_path,
|
||||
path_,
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto directory::remove() -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
return utils::retry_action([this]() -> bool {
|
||||
try {
|
||||
#if defined(_WIN32)
|
||||
auto ret = not exists() || (::RemoveDirectoryA(path_.c_str()) != 0);
|
||||
#else // !defined(_WIN32)
|
||||
auto ret = not exists() || (rmdir(path_.c_str()) == 0);
|
||||
#endif // defined(_WIN32)
|
||||
if (not ret) {
|
||||
utils::error::handle_error(function_name,
|
||||
utils::error::create_error_message({
|
||||
"failed to remove directory",
|
||||
path_,
|
||||
}));
|
||||
}
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
auto directory::remove_recursively() -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (not traverse_directory(
|
||||
path_,
|
||||
[](auto dir_item) -> bool { return dir_item.remove_recursively(); },
|
||||
[](auto file_item) -> bool { return file_item.remove(); },
|
||||
stop_requested_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return remove();
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto directory::size(bool recursive) const -> std::uint64_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
std::uint64_t ret{0U};
|
||||
|
||||
traverse_directory(
|
||||
path_,
|
||||
[&ret, &recursive](auto dir_item) -> bool {
|
||||
if (recursive) {
|
||||
ret += dir_item.size(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
[&ret](auto file_item) -> bool {
|
||||
auto cur_size = file_item.size();
|
||||
if (cur_size.has_value()) {
|
||||
ret += cur_size.value();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
stop_requested_);
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return 0U;
|
||||
}
|
||||
} // namespace monitarr::utils::file
|
||||
188
support/src/utils/file_enc_file.cpp
Normal file
188
support/src/utils/file_enc_file.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
#include "utils/file_enc_file.hpp"
|
||||
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/encrypting_reader.hpp"
|
||||
#include "utils/encryption.hpp"
|
||||
|
||||
namespace monitarr::utils::file {
|
||||
auto enc_file::attach_file(fs_file_t file) -> fs_file_t {
|
||||
return fs_file_t{
|
||||
new enc_file(std::move(file)),
|
||||
};
|
||||
}
|
||||
|
||||
enc_file::enc_file(fs_file_t file) : file_(std::move(file)) {}
|
||||
|
||||
void enc_file::close() { file_->close(); }
|
||||
|
||||
auto enc_file::copy_to(std::string_view new_path, bool overwrite) const
|
||||
-> bool {
|
||||
return file_->copy_to(new_path, overwrite);
|
||||
}
|
||||
|
||||
void enc_file::flush() const { return file_->flush(); }
|
||||
|
||||
auto enc_file::move_to(std::string_view path) -> bool {
|
||||
return file_->move_to(path);
|
||||
}
|
||||
|
||||
auto enc_file::read(unsigned char *data, std::size_t to_read,
|
||||
std::uint64_t offset, std::size_t *total_read) -> bool {
|
||||
if (total_read != nullptr) {
|
||||
*total_read = 0U;
|
||||
}
|
||||
|
||||
auto file_size{size()};
|
||||
if (not file_size.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
to_read = utils::calculate_read_size(file_size.value(), to_read, offset);
|
||||
if (to_read == 0U) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t bytes_read{};
|
||||
auto ret{
|
||||
utils::encryption::read_encrypted_range(
|
||||
{offset, offset + to_read - 1U},
|
||||
utils::encryption::generate_key<utils::encryption::hash_256_t>(
|
||||
encryption_token_),
|
||||
[&](auto &&ct_buffer, auto &&start_offset,
|
||||
auto &&end_offset) -> bool {
|
||||
ct_buffer.resize(end_offset - start_offset + 1U);
|
||||
return file_->read(ct_buffer, start_offset);
|
||||
},
|
||||
file_size.value(), data, to_read, bytes_read),
|
||||
};
|
||||
if (ret && total_read != nullptr) {
|
||||
*total_read = bytes_read;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto enc_file::remove() -> bool { return file_->remove(); }
|
||||
|
||||
auto enc_file::truncate(std::size_t size) -> bool {
|
||||
if (size == 0U) {
|
||||
return file_->truncate(size);
|
||||
}
|
||||
|
||||
auto file_size{this->size()};
|
||||
if (not file_size.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size == file_size.value()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto chunks{
|
||||
size / utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
};
|
||||
auto real_size{
|
||||
(chunks * utils::encryption::encrypting_reader::get_data_chunk_size()) +
|
||||
(chunks * utils::encryption::encrypting_reader::get_header_size()),
|
||||
};
|
||||
auto remain{
|
||||
size % utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
};
|
||||
if (remain > 0U) {
|
||||
real_size +=
|
||||
(remain + utils::encryption::encrypting_reader::get_header_size());
|
||||
}
|
||||
|
||||
if (size < file_size.value()) {
|
||||
if (remain == 0U) {
|
||||
return file_->truncate(real_size);
|
||||
}
|
||||
|
||||
auto begin_chunk{
|
||||
size / utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
};
|
||||
|
||||
auto offset{
|
||||
begin_chunk *
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
};
|
||||
|
||||
std::size_t total_read{};
|
||||
data_buffer data(
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
if (not i_file::read(data, offset, &total_read)) {
|
||||
return false;
|
||||
}
|
||||
data.resize(remain);
|
||||
|
||||
if (not file_->truncate(real_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return i_file::write(data, offset);
|
||||
}
|
||||
|
||||
auto begin_chunk{
|
||||
file_size.value() /
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
};
|
||||
auto end_chunk{
|
||||
utils::divide_with_ceiling(
|
||||
file_size.value(),
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size()),
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto enc_file::write(const unsigned char *data, std::size_t to_write,
|
||||
std::size_t offset, std::size_t *total_written) -> bool {
|
||||
auto file_size{size()};
|
||||
if (not file_size.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((offset + to_write) > file_size.value()) {
|
||||
if (not truncate((offset + to_write) - file_size.value())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto enc_file::size() const -> std::optional<std::uint64_t> {
|
||||
auto file_size = file_->size();
|
||||
if (not file_size.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return utils::encryption::encrypting_reader::calculate_decrypted_size(
|
||||
file_size.value());
|
||||
}
|
||||
} // namespace monitarr::utils::file
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
594
support/src/utils/file_file.cpp
Normal file
594
support/src/utils/file_file.cpp
Normal file
@@ -0,0 +1,594 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/file_file.hpp"
|
||||
|
||||
#include "utils/collection.hpp"
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/path.hpp"
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] auto get_file_size(std::string_view path,
|
||||
std::uint64_t &file_size) -> bool {
|
||||
auto abs_path = monitarr::utils::path::absolute(path);
|
||||
file_size = 0U;
|
||||
|
||||
#if defined(_WIN32)
|
||||
struct _stat64 st{};
|
||||
auto res = _stat64(std::string{path}.c_str(), &st);
|
||||
if (res != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
file_size = static_cast<std::uint64_t>(st.st_size);
|
||||
return true;
|
||||
#else // !defined(_WIN32)
|
||||
std::error_code ec{};
|
||||
file_size = std::filesystem::file_size(abs_path, ec);
|
||||
return (ec.value() == 0);
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_file(std::string_view path) -> bool {
|
||||
auto abs_path = monitarr::utils::path::absolute(path);
|
||||
|
||||
#if defined(_WIN32)
|
||||
return ((::PathFileExistsA(abs_path.c_str()) != 0) &&
|
||||
(::PathIsDirectoryA(abs_path.c_str()) == 0));
|
||||
#else // !defined(_WIN32)
|
||||
struct stat64 st{};
|
||||
return (stat64(abs_path.c_str(), &st) == 0 && not S_ISDIR(st.st_mode));
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace monitarr::utils::file {
|
||||
// auto file::attach_file(native_handle handle,
|
||||
// bool read_only) -> fs_file_t {
|
||||
// MONITARR_USES_FUNCTION_NAME();
|
||||
//
|
||||
// try {
|
||||
// std::string path;
|
||||
//
|
||||
// #if defined(_WIN32)
|
||||
// path.resize(monitarr::max_path_length + 1U);
|
||||
// ::GetFinalPathNameByHandleA(handle, path.data(),
|
||||
// static_cast<DWORD>(path.size()),
|
||||
// FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
|
||||
// #else // !defined(_WIN32)
|
||||
// path.resize(monitarr::max_path_length + 1U);
|
||||
//
|
||||
// #if defined(__APPLE__)
|
||||
// fcntl(handle, F_GETPATH, source_path.data());
|
||||
// #else // !defined(__APPLE__)
|
||||
// readlink(("/proc/self/fd/" + std::to_string(handle)).c_str(),
|
||||
// path.data(),
|
||||
// path.size());
|
||||
// #endif // defined(__APPLE__)
|
||||
// #endif // defined(_WIN32)
|
||||
//
|
||||
// path = path.c_str();
|
||||
//
|
||||
// #if defined(_WIN32)
|
||||
// auto *ptr = _fdopen(
|
||||
// static_cast<int>(_open_osfhandle(reinterpret_cast<intptr_t>(handle),
|
||||
// read_only ? _O_RDONLY : _O_RDWR)),
|
||||
// read_only ? "rb" : "rb+");
|
||||
// #else // !defined(_WIN32)
|
||||
// auto *ptr = fdopen(handle, read_only ? "rb" : "rb+");
|
||||
// #endif // defined(_WIN32)
|
||||
//
|
||||
// return fs_file_t(new file{
|
||||
// file_t{ptr},
|
||||
// utils::path::absolute(path),
|
||||
// read_only,
|
||||
// });
|
||||
// } catch (const std::exception &e) {
|
||||
// utils::error::handle_exception(function_name, e);
|
||||
// } catch (...) {
|
||||
// utils::error::handle_exception(function_name);
|
||||
// }
|
||||
//
|
||||
// return nullptr;
|
||||
// }
|
||||
|
||||
void file::open() {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (not is_file(path_)) {
|
||||
throw utils::error::create_exception(function_name, {
|
||||
"file not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
file_ = file_t{
|
||||
_fsopen(path_.c_str(), read_only_ ? "rb" : "rb+", _SH_DENYNO),
|
||||
file_deleter(),
|
||||
};
|
||||
#else // !defined(_WIN32)
|
||||
file_ = file_t{
|
||||
fopen(path_.c_str(), read_only_ ? "rb" : "rb+"),
|
||||
file_deleter(),
|
||||
};
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
auto file::open_file(std::string_view path, bool read_only) -> fs_file_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
auto *ptr = new file{
|
||||
nullptr,
|
||||
utils::path::absolute(path),
|
||||
read_only,
|
||||
};
|
||||
auto new_file = fs_file_t(ptr);
|
||||
|
||||
try {
|
||||
ptr->open();
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return new_file;
|
||||
}
|
||||
|
||||
auto file::open_or_create_file(std::string_view path, bool read_only)
|
||||
-> fs_file_t {
|
||||
auto abs_path = utils::path::absolute(path);
|
||||
if (not is_file(abs_path)) {
|
||||
#if defined(_WIN32)
|
||||
int old_mode{};
|
||||
_umask_s(077, &old_mode);
|
||||
#else // !defined(_WIN32)
|
||||
auto old_mode = umask(077);
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
#if defined(_WIN32)
|
||||
auto *ptr = _fsopen(abs_path.c_str(), "ab+", _SH_DENYNO);
|
||||
#else // !defined(_WIN32)
|
||||
auto *ptr = fopen(abs_path.c_str(), "ab+");
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
if (ptr != nullptr) {
|
||||
fclose(ptr);
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
_umask_s(old_mode, nullptr);
|
||||
#else // !defined(_WIN32)
|
||||
umask(old_mode);
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
return open_file(abs_path, read_only);
|
||||
}
|
||||
|
||||
void file::close() { file_.reset(); }
|
||||
|
||||
auto file::copy_to(std::string_view new_path, bool overwrite) const -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
auto to_path = utils::path::absolute(new_path);
|
||||
if (directory(to_path).exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
return ::CopyFileA(path_.c_str(), to_path.c_str(),
|
||||
overwrite ? TRUE : FALSE) != 0;
|
||||
#else // !defined(_WIN32)
|
||||
return std::filesystem::copy_file(
|
||||
path_, to_path,
|
||||
overwrite ? std::filesystem::copy_options::overwrite_existing
|
||||
: std::filesystem::copy_options::skip_existing);
|
||||
#endif // defined(_WIN32)
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto file::exists() const -> bool { return is_file(path_); }
|
||||
|
||||
void file::flush() const {
|
||||
if (file_) {
|
||||
fflush(file_.get());
|
||||
}
|
||||
}
|
||||
|
||||
auto file::get_handle() const -> native_handle {
|
||||
if (file_) {
|
||||
#if defined(_WIN32)
|
||||
return reinterpret_cast<native_handle>(
|
||||
_get_osfhandle(_fileno(file_.get())));
|
||||
#else // !defined(_WIN32)
|
||||
return fileno(file_.get());
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
auto file::is_symlink() const -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
return std::filesystem::is_symlink(path_);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto file::move_to(std::string_view path) -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
auto abs_path = utils::path::absolute(path);
|
||||
|
||||
auto reopen{false};
|
||||
if (file_) {
|
||||
reopen = true;
|
||||
close();
|
||||
}
|
||||
|
||||
auto success{false};
|
||||
#if defined(_WIN32)
|
||||
success = ::MoveFileExA(path_.c_str(), abs_path.c_str(),
|
||||
MOVEFILE_REPLACE_EXISTING) != 0;
|
||||
#else // !// defined(_WIN32)
|
||||
std::error_code ec{};
|
||||
std::filesystem::rename(path_, abs_path, ec);
|
||||
success = ec.value() == 0;
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
if (success) {
|
||||
path_ = abs_path;
|
||||
}
|
||||
|
||||
if (reopen) {
|
||||
try {
|
||||
open();
|
||||
return success;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto file::read(unsigned char *data, std::size_t to_read, std::uint64_t offset,
|
||||
std::size_t *total_read) -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (total_read != nullptr) {
|
||||
(*total_read) = 0U;
|
||||
}
|
||||
|
||||
try {
|
||||
if (not file_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"file is not open for reading",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
if (fseeko(file_.get(), static_cast<std::int64_t>(offset), SEEK_SET) ==
|
||||
-1) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to seek before read",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
std::size_t bytes_read{0U};
|
||||
while (bytes_read != to_read) {
|
||||
auto res =
|
||||
fread(&data[bytes_read], 1U, to_read - bytes_read, file_.get());
|
||||
if (not feof(file_.get()) && ferror(file_.get())) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to read file bytes",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
bytes_read += static_cast<std::size_t>(res);
|
||||
}
|
||||
|
||||
if (total_read != nullptr) {
|
||||
(*total_read) = bytes_read;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
auto file::sha256() -> std::optional<std::string> {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
auto should_close{false};
|
||||
auto read_only{read_only_};
|
||||
std::optional<std::string> ret;
|
||||
|
||||
try {
|
||||
if (file_ == nullptr) {
|
||||
should_close = true;
|
||||
read_only_ = true;
|
||||
this->open();
|
||||
}
|
||||
|
||||
crypto_hash_sha256_state state{};
|
||||
auto res = crypto_hash_sha256_init(&state);
|
||||
if (res != 0) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to initialize sha256",
|
||||
std::to_string(res),
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
data_buffer buffer(get_read_buffer_size());
|
||||
std::uint64_t read_offset{0U};
|
||||
std::size_t bytes_read{0U};
|
||||
while (i_file::read(buffer, read_offset, &bytes_read)) {
|
||||
if (not bytes_read) {
|
||||
break;
|
||||
}
|
||||
|
||||
read_offset += bytes_read;
|
||||
res = crypto_hash_sha256_update(
|
||||
&state, reinterpret_cast<const unsigned char *>(buffer.data()),
|
||||
bytes_read);
|
||||
if (res != 0) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to update sha256",
|
||||
std::to_string(res),
|
||||
path_,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::array<unsigned char, crypto_hash_sha256_BYTES> out{};
|
||||
res = crypto_hash_sha256_final(&state, out.data());
|
||||
if (res != 0) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to finalize sha256",
|
||||
std::to_string(res),
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
ret = utils::collection::to_hex_string(out);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
if (should_close) {
|
||||
read_only_ = read_only;
|
||||
close();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
auto file::remove() -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
close();
|
||||
|
||||
return utils::retry_action([this]() -> bool {
|
||||
try {
|
||||
#if defined(_WIN32)
|
||||
auto ret = not exists() || (::DeleteFileA(path_.c_str()) != 0);
|
||||
#else // !defined(_WIN32)
|
||||
std::error_code ec{};
|
||||
auto ret = not exists() || std::filesystem::remove(path_, ec);
|
||||
#endif // defined(_WIN32)
|
||||
if (not ret) {
|
||||
utils::error::handle_error(function_name,
|
||||
utils::error::create_error_message({
|
||||
"failed to remove file",
|
||||
path_,
|
||||
}));
|
||||
}
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
auto file::truncate(std::size_t size) -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
auto reopen{false};
|
||||
if (file_) {
|
||||
reopen = true;
|
||||
close();
|
||||
}
|
||||
|
||||
std::error_code ec{};
|
||||
std::filesystem::resize_file(path_, size, ec);
|
||||
|
||||
auto success{ec.value() == 0};
|
||||
|
||||
if (reopen) {
|
||||
try {
|
||||
open();
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
success = false;
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
auto file::write(const unsigned char *data, std::size_t to_write,
|
||||
std::size_t offset, std::size_t *total_written) -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (total_written != nullptr) {
|
||||
(*total_written) = 0U;
|
||||
}
|
||||
|
||||
try {
|
||||
if (not file_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"file is not open for writing",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
auto res = fseeko(file_.get(), static_cast<std::int64_t>(offset), SEEK_SET);
|
||||
if (res == -1) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to seek before write",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
std::size_t bytes_written{0U};
|
||||
while (bytes_written != to_write) {
|
||||
auto written =
|
||||
fwrite(reinterpret_cast<const char *>(&data[bytes_written]), 1U,
|
||||
to_write - bytes_written, file_.get());
|
||||
if (not feof(file_.get()) && ferror(file_.get())) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to write file bytes",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
if (written == 0U) {
|
||||
break;
|
||||
}
|
||||
|
||||
bytes_written += written;
|
||||
}
|
||||
|
||||
flush();
|
||||
|
||||
if (total_written != nullptr) {
|
||||
(*total_written) = bytes_written;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto file::size() const -> std::optional<std::uint64_t> {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (file_) {
|
||||
if (fseeko(file_.get(), 0, SEEK_END) == -1) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to seek",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
auto size = ftello(file_.get());
|
||||
if (size == -1) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to get position",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
return static_cast<std::uint64_t>(size);
|
||||
}
|
||||
|
||||
std::uint64_t size{};
|
||||
if (not get_file_size(path_, size)) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to get file size",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
return size;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
} // namespace monitarr::utils::file
|
||||
789
support/src/utils/file_smb_directory.cpp
Normal file
789
support/src/utils/file_smb_directory.cpp
Normal file
@@ -0,0 +1,789 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(PROJECT_ENABLE_LIBDSM)
|
||||
|
||||
#include "utils/file_smb_directory.hpp"
|
||||
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/unix.hpp"
|
||||
#include "utils/windows.hpp"
|
||||
|
||||
namespace monitarr::utils::file {
|
||||
auto smb_directory::open(std::string_view host, std::string_view user,
|
||||
std::string_view password, std::string_view path,
|
||||
stop_type *stop_requested) -> smb_directory_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
smb_session_t session{
|
||||
smb_session_new(),
|
||||
smb_session_deleter,
|
||||
};
|
||||
netbios_ns_t ns{
|
||||
netbios_ns_new(),
|
||||
netbios_ns_deleter(),
|
||||
};
|
||||
|
||||
sockaddr_in addr{};
|
||||
|
||||
auto res = netbios_ns_resolve(
|
||||
ns.get(), std::string{host}.c_str(), NETBIOS_FILESERVER,
|
||||
reinterpret_cast<std::uint32_t *>(&addr.sin_addr.s_addr));
|
||||
if (res != DSM_SUCCESS) {
|
||||
res = inet_pton(AF_INET, std::string{host}.c_str(), &addr.sin_addr);
|
||||
if (res != 1) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to resolve host",
|
||||
std::to_string(utils::get_last_error_code()),
|
||||
host,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
res = smb_session_connect(session.get(), std::string{host}.c_str(),
|
||||
static_cast<std::uint32_t>(addr.sin_addr.s_addr),
|
||||
SMB_TRANSPORT_TCP);
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to connect to host",
|
||||
std::to_string(res),
|
||||
host,
|
||||
});
|
||||
}
|
||||
|
||||
smb_session_set_creds(session.get(), std::string{host}.c_str(),
|
||||
std::string{user}.c_str(),
|
||||
std::string{password}.c_str());
|
||||
res = smb_session_login(session.get());
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to logon to host",
|
||||
std::to_string(res),
|
||||
host,
|
||||
user,
|
||||
});
|
||||
}
|
||||
|
||||
auto share_name = utils::string::split(path, '/', false).at(0U);
|
||||
|
||||
smb_tid tid{};
|
||||
res = smb_tree_connect(session.get(), share_name.c_str(), &tid);
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to connect to share",
|
||||
std::to_string(res),
|
||||
share_name,
|
||||
});
|
||||
}
|
||||
|
||||
return smb_directory_t{
|
||||
new smb_directory{
|
||||
"//" + std::string{host} + "/" + std::string{path},
|
||||
session,
|
||||
share_name,
|
||||
tid,
|
||||
stop_requested,
|
||||
},
|
||||
};
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto smb_directory::open(std::wstring_view host, std::wstring_view user,
|
||||
std::wstring_view password, std::wstring_view path,
|
||||
stop_type *stop_requested) -> smb_directory_t {
|
||||
return open(utils::string::to_utf8(host), utils::string::to_utf8(user),
|
||||
utils::string::to_utf8(password), utils::string::to_utf8(path),
|
||||
stop_requested);
|
||||
}
|
||||
|
||||
auto smb_directory::copy_to(std::string_view new_path,
|
||||
bool /* overwrite */) const -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
// auto to_path = utils::path::absolute(new_path);
|
||||
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to copy directory",
|
||||
"not implemented",
|
||||
new_path,
|
||||
path_,
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_directory::count(bool recursive) const -> std::uint64_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
smb_stat_list_t list{
|
||||
smb_find(session_.get(), tid_, smb_create_search_path(path_).c_str()),
|
||||
smb_stat_list_deleter(),
|
||||
};
|
||||
auto count = smb_stat_list_count(list.get());
|
||||
|
||||
if (not recursive) {
|
||||
return count;
|
||||
}
|
||||
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to get directory count recursively",
|
||||
"not implemented",
|
||||
utils::string::from_bool(recursive),
|
||||
path_,
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return 0U;
|
||||
}
|
||||
|
||||
auto smb_directory::create_directory(std::string_view path) const
|
||||
-> fs_directory_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
auto dir = get_directory(path);
|
||||
if (dir) {
|
||||
return dir;
|
||||
}
|
||||
|
||||
auto res = smb_directory_create(
|
||||
session_.get(), tid_,
|
||||
smb_create_and_validate_relative_path(path_, path).c_str());
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to create directory",
|
||||
std::to_string(res),
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
return get_directory(path);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto smb_directory::create_file(std::string_view file_name,
|
||||
bool read_only) const -> fs_file_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
auto fs_file = get_file(file_name);
|
||||
if (fs_file) {
|
||||
if (not dynamic_cast<smb_file *>(fs_file.get())->open(read_only)) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to open existing file",
|
||||
file_name,
|
||||
});
|
||||
}
|
||||
|
||||
return fs_file;
|
||||
}
|
||||
|
||||
auto rel_path = smb_create_and_validate_relative_path(path_, file_name);
|
||||
|
||||
smb_fd fd{};
|
||||
auto res =
|
||||
smb_fopen(session_.get(), tid_, rel_path.c_str(), SMB_MOD_RW, &fd);
|
||||
if (res != DSM_SUCCESS) {
|
||||
return nullptr;
|
||||
}
|
||||
smb_fclose(session_.get(), fd);
|
||||
|
||||
res = smb_fopen(session_.get(), tid_, rel_path.c_str(),
|
||||
read_only ? SMB_MOD_RO : SMB_MOD_RW2, &fd);
|
||||
if (res != DSM_SUCCESS) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<smb_file>(
|
||||
fd, smb_create_smb_path(path_, std::string{rel_path}), session_,
|
||||
share_name_, tid_);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
auto smb_directory::exists() const -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
smb_stat_t st{
|
||||
smb_fstat(session_.get(), tid_,
|
||||
smb_create_relative_path(path_).c_str()),
|
||||
smb_stat_deleter(),
|
||||
};
|
||||
if (not st) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return smb_stat_get(st.get(), SMB_STAT_ISDIR) != 0U;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_directory::get_directory(std::string_view path) const
|
||||
-> fs_directory_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
auto rel_path = smb_create_and_validate_relative_path(path_, path);
|
||||
smb_stat_t st{
|
||||
smb_fstat(session_.get(), tid_, rel_path.c_str()),
|
||||
smb_stat_deleter(),
|
||||
};
|
||||
if (not st) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to stat directory",
|
||||
rel_path,
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
bool is_dir{smb_stat_get(st.get(), SMB_STAT_ISDIR) != 0U};
|
||||
if (not is_dir) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"path is not a directory",
|
||||
rel_path,
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
return smb_directory_t{
|
||||
new smb_directory{
|
||||
smb_create_smb_path(path_, rel_path),
|
||||
session_,
|
||||
share_name_,
|
||||
tid_,
|
||||
stop_requested_,
|
||||
},
|
||||
};
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto smb_directory::get_directories() const -> std::vector<fs_directory_t> {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
smb_stat_list_t list{
|
||||
smb_find(session_.get(), tid_, smb_create_search_path(path_).c_str()),
|
||||
|
||||
smb_stat_list_deleter(),
|
||||
};
|
||||
if (not list) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to get directory list",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<fs_directory_t> ret{};
|
||||
|
||||
auto count = smb_stat_list_count(list.get());
|
||||
for (std::size_t idx = 0U; !is_stop_requested() && idx < count; ++idx) {
|
||||
auto *item_st = smb_stat_list_at(list.get(), idx);
|
||||
|
||||
bool is_dir{smb_stat_get(item_st, SMB_STAT_ISDIR) != 0U};
|
||||
if (not is_dir) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string name{smb_stat_name(item_st)};
|
||||
if (name == "." || name == "..") {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
ret.emplace_back(smb_directory_t{
|
||||
new smb_directory{
|
||||
smb_create_smb_path(path_, name),
|
||||
session_,
|
||||
share_name_,
|
||||
tid_,
|
||||
stop_requested_,
|
||||
},
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto smb_directory::get_file(std::string_view path) const -> fs_file_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
auto rel_path = smb_create_and_validate_relative_path(path_, path);
|
||||
smb_stat_t st{
|
||||
smb_fstat(session_.get(), tid_, rel_path.c_str()),
|
||||
smb_stat_deleter(),
|
||||
};
|
||||
if (not st) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to stat file",
|
||||
rel_path,
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
bool is_dir{smb_stat_get(st.get(), SMB_STAT_ISDIR) != 0U};
|
||||
if (is_dir) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"path is not a file",
|
||||
rel_path,
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
return std::make_unique<smb_file>(
|
||||
std::nullopt, smb_create_smb_path(path_, std::string{rel_path}),
|
||||
session_, share_name_, tid_);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto smb_directory::get_files() const -> std::vector<fs_file_t> {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
smb_stat_list_t list{
|
||||
smb_find(session_.get(), tid_, smb_create_search_path(path_).c_str()),
|
||||
smb_stat_list_deleter(),
|
||||
};
|
||||
if (not list) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to get file list",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<fs_file_t> ret{};
|
||||
|
||||
auto count = smb_stat_list_count(list.get());
|
||||
for (std::size_t idx = 0U; !is_stop_requested() && idx < count; ++idx) {
|
||||
auto *item_st = smb_stat_list_at(list.get(), idx);
|
||||
|
||||
bool is_dir{smb_stat_get(item_st, SMB_STAT_ISDIR) != 0U};
|
||||
if (is_dir) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
std::string name{smb_stat_name(item_st)};
|
||||
ret.emplace_back(std::make_unique<smb_file>(
|
||||
std::nullopt, smb_create_smb_path(path_, name), session_,
|
||||
share_name_, tid_));
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto smb_directory::get_items() const -> std::vector<fs_item_t> {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
smb_stat_list_t list{
|
||||
smb_find(session_.get(), tid_, smb_create_search_path(path_).c_str()),
|
||||
smb_stat_list_deleter(),
|
||||
};
|
||||
if (not list) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to get item list",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
std::vector<fs_item_t> ret{};
|
||||
|
||||
auto count = smb_stat_list_count(list.get());
|
||||
for (std::size_t idx = 0U; !is_stop_requested() && idx < count; ++idx) {
|
||||
auto *item_st = smb_stat_list_at(list.get(), idx);
|
||||
|
||||
bool is_dir{smb_stat_get(item_st, SMB_STAT_ISDIR) != 0U};
|
||||
std::string name{smb_stat_name(item_st)};
|
||||
|
||||
if (is_dir) {
|
||||
if (name == "." || name == "..") {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
ret.emplace_back(smb_directory_t{
|
||||
new smb_directory{
|
||||
path_ + '/' + name,
|
||||
session_,
|
||||
share_name_,
|
||||
tid_,
|
||||
stop_requested_,
|
||||
},
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
ret.emplace_back(std::make_unique<smb_file>(
|
||||
std::nullopt, smb_create_smb_path(path_, name), session_,
|
||||
share_name_, tid_));
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto smb_directory::get_time(time_type type) const
|
||||
-> std::optional<std::uint64_t> {
|
||||
return smb_file::get_time(session_.get(), tid_, path_, type);
|
||||
}
|
||||
|
||||
auto smb_directory::is_stop_requested() const -> bool {
|
||||
return (stop_requested_ != nullptr) && *stop_requested_;
|
||||
}
|
||||
|
||||
auto smb_directory::is_symlink() const -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_directory::move_to(std::string_view new_path) -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to move directory",
|
||||
"not implemented",
|
||||
new_path,
|
||||
path_,
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_directory::remove() -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
return utils::retry_action([this]() -> bool {
|
||||
if (not exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
auto res = smb_directory_rm(session_.get(), tid_,
|
||||
smb_create_relative_path(path_).c_str());
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to remove directory",
|
||||
std::to_string(res),
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_directory::remove_recursively() -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
if (not exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to remove directory recursively",
|
||||
"not implemented",
|
||||
path_,
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_directory::size(bool /* recursive */) const -> std::uint64_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to get directory size",
|
||||
"not implemented",
|
||||
path_,
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace monitarr::utils::file
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBDSM)
|
||||
549
support/src/utils/file_smb_file.cpp
Normal file
549
support/src/utils/file_smb_file.cpp
Normal file
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(PROJECT_ENABLE_LIBDSM)
|
||||
|
||||
#include "utils/file_smb_file.hpp"
|
||||
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace monitarr::utils::file {
|
||||
void smb_file::close() {
|
||||
if (fd_.has_value()) {
|
||||
smb_fclose(session_.get(), *fd_);
|
||||
fd_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
auto smb_file::copy_to(std::string_view new_path,
|
||||
bool overwrite) const -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
// auto to_path = utils::path::absolute(new_path);
|
||||
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to copy file",
|
||||
"not implemented",
|
||||
std::to_string(overwrite),
|
||||
new_path,
|
||||
path_,
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_file::exists() const -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
smb_stat_t st{
|
||||
smb_fstat(session_.get(), tid_,
|
||||
smb_create_relative_path(path_).c_str()),
|
||||
smb_stat_deleter(),
|
||||
};
|
||||
if (not st) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return smb_stat_get(st.get(), SMB_STAT_ISDIR) == 0U;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void smb_file::flush() const {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to flush file",
|
||||
"not implemented",
|
||||
path_,
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
}
|
||||
|
||||
auto smb_file::get_time(smb_session *session, smb_tid tid, std::string path,
|
||||
time_type type) -> std::optional<std::uint64_t> {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (session == nullptr) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
auto rel_path = smb_create_relative_path(path);
|
||||
smb_stat_t st{
|
||||
smb_fstat(session, tid, rel_path.c_str()),
|
||||
smb_stat_deleter(),
|
||||
};
|
||||
if (not st) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to stat file",
|
||||
"not implemented",
|
||||
rel_path,
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case time_type::accessed:
|
||||
return smb_stat_get(st.get(), SMB_STAT_ATIME);
|
||||
|
||||
case time_type::created:
|
||||
return smb_stat_get(st.get(), SMB_STAT_CTIME);
|
||||
|
||||
case time_type::modified:
|
||||
return smb_stat_get(st.get(), SMB_STAT_MTIME);
|
||||
|
||||
case time_type::written:
|
||||
return smb_stat_get(st.get(), SMB_STAT_WTIME);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto smb_file::is_symlink() const -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_file::move_to(std::string_view new_path) -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (utils::string::begins_with(new_path, "//")) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to move file",
|
||||
"new path must be in same share",
|
||||
new_path,
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
auto from_path = smb_create_relative_path(path_);
|
||||
auto to_path = smb_create_and_validate_relative_path(
|
||||
utils::string::begins_with(new_path, "/") ? smb_get_root_path(path_)
|
||||
: smb_get_parent_path(path_),
|
||||
new_path);
|
||||
|
||||
auto was_open{false};
|
||||
if (fd_.has_value()) {
|
||||
close();
|
||||
was_open = true;
|
||||
}
|
||||
|
||||
auto res = smb_tree_connect(session_.get(), share_name_.c_str(), &tid_);
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to connect to share",
|
||||
std::to_string(res),
|
||||
share_name_,
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
res = smb_file_mv(session_.get(), tid_, from_path.c_str(), to_path.c_str());
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to move file",
|
||||
std::to_string(res),
|
||||
from_path,
|
||||
to_path,
|
||||
});
|
||||
}
|
||||
|
||||
path_ = smb_create_smb_path(path_, to_path);
|
||||
if (was_open) {
|
||||
return open(read_only_);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_file::open(bool read_only) -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (fd_.has_value()) {
|
||||
if (read_only == read_only_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
auto rel_path = smb_create_relative_path(path_);
|
||||
|
||||
auto res = smb_tree_connect(session_.get(), share_name_.c_str(), &tid_);
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to connect to share",
|
||||
std::to_string(res),
|
||||
share_name_,
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
smb_fd fd{};
|
||||
res = smb_fopen(session_.get(), tid_, rel_path.c_str(),
|
||||
read_only ? SMB_MOD_RO : SMB_MOD_RW2, &fd);
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to open file",
|
||||
std::to_string(res),
|
||||
utils::string::from_bool(read_only),
|
||||
rel_path,
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
fd_ = fd;
|
||||
read_only_ = read_only;
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_file::read(unsigned char *data, std::size_t to_read,
|
||||
std::uint64_t offset, std::size_t *total_read) -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (total_read != nullptr) {
|
||||
(*total_read) = 0U;
|
||||
}
|
||||
|
||||
if (not fd_.has_value()) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to read file",
|
||||
"file not open",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
auto res = smb_fseek(session_.get(), *fd_, static_cast<off_t>(offset),
|
||||
SMB_SEEK_SET);
|
||||
if (res == -1) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to seek file",
|
||||
std::to_string(res),
|
||||
std::to_string(offset),
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
std::size_t bytes_read{0U};
|
||||
while (bytes_read != to_read) {
|
||||
res = smb_fread(session_.get(), *fd_, &data[bytes_read],
|
||||
to_read - bytes_read);
|
||||
if (res == -1) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to read file",
|
||||
std::to_string(res),
|
||||
std::to_string(offset),
|
||||
std::to_string(to_read),
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
bytes_read += static_cast<std::size_t>(res);
|
||||
}
|
||||
|
||||
if (total_read != nullptr) {
|
||||
(*total_read) = bytes_read;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_file::remove() -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
close();
|
||||
|
||||
return utils::retry_action([this]() -> bool {
|
||||
if (not exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
auto res = smb_tree_connect(session_.get(), share_name_.c_str(), &tid_);
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to connect to share",
|
||||
std::to_string(res),
|
||||
share_name_,
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
auto rel_path = smb_create_relative_path(path_);
|
||||
res = smb_file_rm(session_.get(), tid_, rel_path.c_str());
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw utils::error::create_exception(
|
||||
function_name,
|
||||
{
|
||||
"failed to remove file",
|
||||
std::to_string(res),
|
||||
std::to_string(smb_session_get_nt_status(session_.get())),
|
||||
rel_path,
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_file::size() const -> std::optional<std::uint64_t> {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"session not found",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
auto rel_path = smb_create_relative_path(path_);
|
||||
smb_stat_t st{
|
||||
smb_fstat(session_.get(), tid_, rel_path.c_str()),
|
||||
smb_stat_deleter(),
|
||||
};
|
||||
if (not st) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to stat directory",
|
||||
rel_path,
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
return smb_stat_get(st.get(), SMB_STAT_SIZE);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto smb_file::truncate(std::size_t size) -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to truncate file",
|
||||
"not implemented",
|
||||
std::to_string(size),
|
||||
path_,
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_file::write(const unsigned char *data, std::size_t to_write,
|
||||
std::size_t offset, std::size_t *total_written) -> bool {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
if (total_written != nullptr) {
|
||||
(*total_written) = 0U;
|
||||
}
|
||||
|
||||
if (not fd_.has_value()) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to write file",
|
||||
"file not open",
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
auto res = smb_fseek(session_.get(), *fd_, static_cast<off_t>(offset),
|
||||
SMB_SEEK_SET);
|
||||
if (res == -1) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to seek file",
|
||||
std::to_string(res),
|
||||
std::to_string(offset),
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
std::size_t bytes_written{0U};
|
||||
while (bytes_written != to_write) {
|
||||
res = smb_fwrite(session_.get(), *fd_,
|
||||
const_cast<unsigned char *>(&data[bytes_written]),
|
||||
to_write - bytes_written);
|
||||
if (res == -1) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to write file",
|
||||
std::to_string(res),
|
||||
std::to_string(offset),
|
||||
std::to_string(to_write),
|
||||
path_,
|
||||
});
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
bytes_written += static_cast<std::size_t>(res);
|
||||
}
|
||||
|
||||
if (total_written != nullptr) {
|
||||
(*total_written) = bytes_written;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace monitarr::utils::file
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBDSM)
|
||||
198
support/src/utils/file_thread_file.cpp
Normal file
198
support/src/utils/file_thread_file.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/file_thread_file.hpp"
|
||||
|
||||
namespace monitarr::utils::file {
|
||||
// auto thread_file::attach_file(native_handle handle,
|
||||
// bool read_only) -> fs_file_t {}
|
||||
|
||||
auto thread_file::attach_file(fs_file_t file) -> fs_file_t {
|
||||
return fs_file_t{
|
||||
new thread_file(std::move(file)),
|
||||
};
|
||||
}
|
||||
|
||||
auto thread_file::open_file(std::string_view path,
|
||||
bool read_only) -> fs_file_t {
|
||||
return fs_file_t{
|
||||
new thread_file(file::open_file(path, read_only)),
|
||||
};
|
||||
}
|
||||
|
||||
auto thread_file::open_or_create_file(std::string_view path,
|
||||
bool read_only) -> fs_file_t {
|
||||
return fs_file_t{
|
||||
new thread_file(file::open_or_create_file(path, read_only)),
|
||||
};
|
||||
}
|
||||
|
||||
void thread_file::io_item::done(bool result) {
|
||||
unique_mutex_lock lock(*mtx);
|
||||
complete = true;
|
||||
success = result;
|
||||
notify->notify_all();
|
||||
}
|
||||
|
||||
void thread_file::io_item::wait() const {
|
||||
if (complete) {
|
||||
return;
|
||||
}
|
||||
|
||||
unique_mutex_lock lock(*mtx);
|
||||
while (not complete) {
|
||||
notify->wait(lock);
|
||||
}
|
||||
notify->notify_all();
|
||||
}
|
||||
|
||||
thread_file::thread_file(std::string_view path) : file_(new file(path)) {}
|
||||
|
||||
thread_file::thread_file(std::wstring_view path)
|
||||
: file_(new file(utils::string::to_utf8(path))) {}
|
||||
|
||||
thread_file::thread_file(fs_file_t file) : file_(std::move(file)) {}
|
||||
|
||||
thread_file::~thread_file() {
|
||||
close();
|
||||
|
||||
if (io_thread_) {
|
||||
io_thread_->join();
|
||||
}
|
||||
}
|
||||
|
||||
void thread_file::close() {
|
||||
do_io([this]() -> bool {
|
||||
file_->close();
|
||||
stop_requested_ = true;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
auto thread_file::copy_to(std::string_view new_path,
|
||||
bool overwrite) const -> bool {
|
||||
return do_io([this, &new_path, &overwrite]() -> bool {
|
||||
return file_->copy_to(new_path, overwrite);
|
||||
});
|
||||
}
|
||||
|
||||
auto thread_file::do_io(action_t action) const -> bool {
|
||||
unique_mutex_lock lock(*mtx_);
|
||||
if (stop_requested_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (not io_thread_) {
|
||||
io_thread_ = std::make_unique<std::thread>([this]() { thread_func(); });
|
||||
}
|
||||
|
||||
auto item = std::make_shared<io_item>(action);
|
||||
actions_.emplace_back(item);
|
||||
notify_->notify_all();
|
||||
lock.unlock();
|
||||
|
||||
item->wait();
|
||||
|
||||
return item->success;
|
||||
}
|
||||
|
||||
void thread_file::flush() const {
|
||||
do_io([this]() -> bool {
|
||||
file_->flush();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
auto thread_file::move_to(std::string_view path) -> bool {
|
||||
return do_io([this, &path]() -> bool { return file_->move_to(path); });
|
||||
}
|
||||
|
||||
auto thread_file::read(unsigned char *data, std::size_t to_read,
|
||||
std::uint64_t offset, std::size_t *total_read) -> bool {
|
||||
return do_io([this, &data, &to_read, &offset, &total_read]() -> bool {
|
||||
return file_->read(data, to_read, offset, total_read);
|
||||
});
|
||||
}
|
||||
|
||||
auto thread_file::remove() -> bool {
|
||||
return do_io([this]() -> bool { return file_->remove(); });
|
||||
}
|
||||
|
||||
void thread_file::thread_func() const {
|
||||
unique_mutex_lock lock(*mtx_);
|
||||
notify_->notify_all();
|
||||
lock.unlock();
|
||||
|
||||
const auto run_actions = [this, &lock]() {
|
||||
auto actions = actions_;
|
||||
actions_.clear();
|
||||
notify_->notify_all();
|
||||
lock.unlock();
|
||||
|
||||
for (auto &&action : actions) {
|
||||
action->done(action->action());
|
||||
}
|
||||
};
|
||||
|
||||
while (not stop_requested_) {
|
||||
lock.lock();
|
||||
if (stop_requested_) {
|
||||
lock.unlock();
|
||||
break;
|
||||
}
|
||||
|
||||
while (not stop_requested_ && actions_.empty()) {
|
||||
notify_->wait(lock);
|
||||
}
|
||||
|
||||
if (stop_requested_) {
|
||||
lock.unlock();
|
||||
break;
|
||||
}
|
||||
|
||||
run_actions();
|
||||
}
|
||||
|
||||
lock.lock();
|
||||
run_actions();
|
||||
}
|
||||
|
||||
auto thread_file::truncate(std::size_t size) -> bool {
|
||||
return do_io([this, &size]() -> bool { return file_->truncate(size); });
|
||||
}
|
||||
|
||||
auto thread_file::write(const unsigned char *data, std::size_t to_write,
|
||||
std::size_t offset,
|
||||
std::size_t *total_written) -> bool {
|
||||
return do_io([this, &data, &to_write, &offset, &total_written]() -> bool {
|
||||
return file_->write(data, to_write, offset, total_written);
|
||||
});
|
||||
}
|
||||
|
||||
auto thread_file::size() const -> std::optional<std::uint64_t> {
|
||||
std::optional<std::uint64_t> size;
|
||||
do_io([this, &size]() -> bool {
|
||||
size = file_->size();
|
||||
return size.has_value();
|
||||
});
|
||||
return size;
|
||||
}
|
||||
} // namespace monitarr::utils::file
|
||||
189
support/src/utils/hash.cpp
Normal file
189
support/src/utils/hash.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
#include "utils/hash.hpp"
|
||||
|
||||
#include "utils/error.hpp"
|
||||
|
||||
namespace monitarr::utils::encryption {
|
||||
auto create_hash_blake2b_256(std::string_view data) -> hash_256_t {
|
||||
return create_hash_blake2b_t<hash_256_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()), data.size());
|
||||
}
|
||||
|
||||
auto create_hash_blake2b_256(std::wstring_view data) -> hash_256_t {
|
||||
return create_hash_blake2b_t<hash_256_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
auto create_hash_blake2b_256(const data_buffer &data) -> hash_256_t {
|
||||
return create_hash_blake2b_t<hash_256_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(data_buffer::value_type));
|
||||
}
|
||||
|
||||
auto create_hash_blake2b_384(std::string_view data) -> hash_384_t {
|
||||
return create_hash_blake2b_t<hash_384_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()), data.size());
|
||||
}
|
||||
|
||||
auto create_hash_blake2b_384(std::wstring_view data) -> hash_384_t {
|
||||
return create_hash_blake2b_t<hash_384_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
auto create_hash_blake2b_384(const data_buffer &data) -> hash_384_t {
|
||||
return create_hash_blake2b_t<hash_384_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(data_buffer::value_type));
|
||||
}
|
||||
|
||||
auto create_hash_blake2b_512(std::string_view data) -> hash_512_t {
|
||||
return create_hash_blake2b_t<hash_512_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()), data.size());
|
||||
}
|
||||
|
||||
auto create_hash_blake2b_512(std::wstring_view data) -> hash_512_t {
|
||||
return create_hash_blake2b_t<hash_512_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
auto create_hash_blake2b_512(const data_buffer &data) -> hash_512_t {
|
||||
return create_hash_blake2b_t<hash_512_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(data_buffer::value_type));
|
||||
}
|
||||
|
||||
auto create_hash_sha256(std::string_view data) -> hash_256_t {
|
||||
return create_hash_sha256(
|
||||
reinterpret_cast<const unsigned char *>(data.data()), data.size());
|
||||
}
|
||||
|
||||
auto create_hash_sha256(std::wstring_view data) -> hash_256_t {
|
||||
return create_hash_sha256(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
auto create_hash_sha256(const data_buffer &data) -> hash_256_t {
|
||||
return create_hash_sha256(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(data_buffer::value_type));
|
||||
}
|
||||
|
||||
auto create_hash_sha512(std::string_view data) -> hash_512_t {
|
||||
return create_hash_sha512(
|
||||
reinterpret_cast<const unsigned char *>(data.data()), data.size());
|
||||
}
|
||||
|
||||
auto create_hash_sha512(std::wstring_view data) -> hash_512_t {
|
||||
return create_hash_sha512(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
auto create_hash_sha512(const data_buffer &data) -> hash_512_t {
|
||||
return create_hash_sha512(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(data_buffer::value_type));
|
||||
}
|
||||
|
||||
auto create_hash_sha512(const unsigned char *data,
|
||||
std::size_t data_size) -> hash_512_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
hash_512_t hash{};
|
||||
|
||||
crypto_hash_sha512_state state{};
|
||||
auto res = crypto_hash_sha512_init(&state);
|
||||
if (res != 0) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to initialize sha-512",
|
||||
std::to_string(res),
|
||||
});
|
||||
}
|
||||
|
||||
res = crypto_hash_sha512_update(&state, data, data_size);
|
||||
if (res != 0) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to update sha-512",
|
||||
std::to_string(res),
|
||||
});
|
||||
}
|
||||
|
||||
res = crypto_hash_sha512_final(&state, hash.data());
|
||||
if (res != 0) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to finalize sha-512",
|
||||
std::to_string(res),
|
||||
});
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
auto create_hash_sha256(const unsigned char *data,
|
||||
std::size_t data_size) -> hash_256_t {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
hash_256_t hash{};
|
||||
|
||||
crypto_hash_sha256_state state{};
|
||||
auto res = crypto_hash_sha256_init(&state);
|
||||
if (res != 0) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to initialize sha-256",
|
||||
std::to_string(res),
|
||||
});
|
||||
}
|
||||
|
||||
res = crypto_hash_sha256_update(&state, data, data_size);
|
||||
if (res != 0) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to update sha-256",
|
||||
std::to_string(res),
|
||||
});
|
||||
}
|
||||
|
||||
res = crypto_hash_sha256_final(&state, hash.data());
|
||||
if (res != 0) {
|
||||
throw utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to finalize sha-256",
|
||||
std::to_string(res),
|
||||
});
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
} // namespace monitarr::utils::encryption
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
315
support/src/utils/path.cpp
Normal file
315
support/src/utils/path.cpp
Normal file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/path.hpp"
|
||||
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "utils/unix.hpp"
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] auto resolve(std::string path) -> std::string {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
#if defined(_WIN32)
|
||||
if (monitarr::utils::string::contains(path, "~\\")) {
|
||||
monitarr::utils::string::replace(path, "~\\", "%USERPROFILE%\\");
|
||||
}
|
||||
|
||||
if (monitarr::utils::string::contains(path, "~/")) {
|
||||
monitarr::utils::string::replace(path, "~/", "%USERPROFILE%\\");
|
||||
}
|
||||
|
||||
if (monitarr::utils::string::contains(path, "%")) {
|
||||
auto size = ::ExpandEnvironmentStringsA(path.c_str(), nullptr, 0);
|
||||
|
||||
std::string dest;
|
||||
dest.resize(size);
|
||||
::ExpandEnvironmentStringsA(path.c_str(), dest.data(),
|
||||
static_cast<DWORD>(dest.size()));
|
||||
path = dest.c_str();
|
||||
}
|
||||
#else // !defined (_WIN32)
|
||||
if (monitarr::utils::string::contains(path, "~\\")) {
|
||||
monitarr::utils::string::replace(path, "~\\", "~/");
|
||||
}
|
||||
|
||||
if (monitarr::utils::string::contains(path, "~/")) {
|
||||
std::string home{};
|
||||
auto res =
|
||||
monitarr::utils::use_getpwuid(getuid(), [&home](struct passwd *pw) {
|
||||
home = (pw->pw_dir ? pw->pw_dir : "");
|
||||
if (home.empty() ||
|
||||
((home == monitarr::utils::path::slash) && (getuid() != 0))) {
|
||||
home = monitarr::utils::path::combine("/home", {pw->pw_name});
|
||||
}
|
||||
});
|
||||
if (not res) {
|
||||
throw monitarr::utils::error::create_exception(function_name,
|
||||
{
|
||||
"failed to getpwuid",
|
||||
res.reason,
|
||||
});
|
||||
}
|
||||
|
||||
path = monitarr::utils::string::replace(path, "~/", home + "/");
|
||||
}
|
||||
#endif // defined (_WIN32)
|
||||
|
||||
return monitarr::utils::path::finalize(path);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace monitarr::utils::path {
|
||||
auto absolute(std::string_view path) -> std::string {
|
||||
std::string abs_path{path};
|
||||
if (abs_path.empty()) {
|
||||
return abs_path;
|
||||
}
|
||||
|
||||
abs_path = resolve(abs_path);
|
||||
#if defined(_WIN32)
|
||||
if (not utils::string::contains(abs_path, dot)) {
|
||||
return abs_path;
|
||||
}
|
||||
|
||||
std::string temp;
|
||||
temp.resize(monitarr::max_path_length + 1U);
|
||||
::GetFullPathNameA(abs_path.c_str(), static_cast<DWORD>(temp.size()),
|
||||
temp.data(), nullptr);
|
||||
#else // !defined(_WIN32)
|
||||
if (not utils::string::contains(abs_path, dot) ||
|
||||
utils::string::begins_with(abs_path, slash)) {
|
||||
return abs_path;
|
||||
}
|
||||
|
||||
auto found{false};
|
||||
std::string tmp{abs_path};
|
||||
do {
|
||||
auto *res = realpath(tmp.c_str(), nullptr);
|
||||
if (res != nullptr) {
|
||||
abs_path =
|
||||
res + std::string{directory_seperator} + abs_path.substr(tmp.size());
|
||||
free(res);
|
||||
found = true;
|
||||
} else if (tmp == dot) {
|
||||
found = true;
|
||||
} else {
|
||||
tmp = dirname(tmp.data());
|
||||
}
|
||||
} while (not found);
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
return finalize(abs_path);
|
||||
}
|
||||
|
||||
auto absolute(std::wstring_view path) -> std::wstring {
|
||||
return utils::string::from_utf8(absolute(utils::string::to_utf8(path)));
|
||||
}
|
||||
|
||||
auto exists(std::string_view path) -> bool {
|
||||
return utils::file::file{path}.exists() ||
|
||||
utils::file::directory{path}.exists();
|
||||
}
|
||||
|
||||
auto exists(std::wstring_view path) -> bool {
|
||||
return exists(utils::string::to_utf8(path));
|
||||
}
|
||||
|
||||
auto find_program_in_path(const std::string &name_without_extension)
|
||||
-> std::string {
|
||||
static std::mutex mtx{};
|
||||
static std::unordered_map<std::string, std::string> found_items{};
|
||||
|
||||
mutex_lock lock(mtx);
|
||||
if (found_items.contains(name_without_extension)) {
|
||||
return found_items.at(name_without_extension);
|
||||
}
|
||||
|
||||
auto path = utils::get_environment_variable("PATH");
|
||||
if (path.empty()) {
|
||||
return path;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
static constexpr const std::array<std::string_view, 4U> extension_list{
|
||||
".bat",
|
||||
".cmd",
|
||||
".exe",
|
||||
".ps1",
|
||||
};
|
||||
static constexpr const auto split_char = ';';
|
||||
#else // !defined(_WIN32)
|
||||
static constexpr const std::array<std::string_view, 2U> extension_list{
|
||||
"",
|
||||
".sh",
|
||||
};
|
||||
static constexpr const auto split_char = ':';
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
auto search_path_list = utils::string::split(path, split_char, false);
|
||||
for (auto &&search_path : search_path_list) {
|
||||
for (auto &&extension : extension_list) {
|
||||
auto exec_path = combine(
|
||||
search_path, {name_without_extension + std::string{extension}});
|
||||
if (utils::file::file(exec_path).exists()) {
|
||||
found_items[name_without_extension] = exec_path;
|
||||
return exec_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
[[nodiscard]] auto
|
||||
find_program_in_path(std::wstring_view name_without_extension) -> std::wstring {
|
||||
return utils::string::from_utf8(
|
||||
find_program_in_path(utils::string::to_utf8(name_without_extension)));
|
||||
}
|
||||
|
||||
auto get_parent_path(std::string_view path) -> std::string {
|
||||
auto abs_path = absolute(path);
|
||||
|
||||
#if defined(_WIN32)
|
||||
::PathRemoveFileSpecA(abs_path.data());
|
||||
abs_path = abs_path.c_str();
|
||||
#else // !defined(_WIN32)
|
||||
abs_path = std::filesystem::path{abs_path}.parent_path().string();
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
return finalize(abs_path);
|
||||
}
|
||||
|
||||
auto get_parent_path(std::wstring_view path) -> std::wstring {
|
||||
return utils::string::from_utf8(
|
||||
get_parent_path(utils::string::to_utf8(path)));
|
||||
}
|
||||
|
||||
auto get_relative_path(std::string_view path, std::string_view root_path)
|
||||
-> std::string {
|
||||
auto abs_path = absolute(path);
|
||||
auto abs_root_path =
|
||||
absolute(root_path) + std::string{get_directory_seperator<char>()};
|
||||
#if defined(_WIN32)
|
||||
if (utils::string::to_lower(abs_path).starts_with(
|
||||
utils::string::to_lower(abs_root_path))) {
|
||||
#else // !defined(_WIN32)
|
||||
if (abs_path.starts_with(abs_root_path)) {
|
||||
#endif // defined(_WIN32)
|
||||
return abs_path.substr(abs_root_path.size());
|
||||
}
|
||||
|
||||
return abs_path;
|
||||
}
|
||||
|
||||
auto get_relative_path(std::wstring_view path, std::wstring_view root_path)
|
||||
-> std::wstring {
|
||||
return utils::string::from_utf8(get_relative_path(
|
||||
utils::string::to_utf8(path), utils::string::to_utf8(root_path)));
|
||||
}
|
||||
|
||||
auto contains_trash_directory(std::string_view path) -> bool {
|
||||
auto parts = utils::string::split(utils::string::to_lower(absolute(path)),
|
||||
get_directory_seperator<char>(), false);
|
||||
|
||||
return std::find_if(parts.begin(), parts.end(), [](auto &&part) -> bool {
|
||||
return utils::string::begins_with(part, ".trash-") ||
|
||||
part == ".trashes" || part == "$recycle.bin";
|
||||
}) != parts.end();
|
||||
}
|
||||
|
||||
auto contains_trash_directory(std::wstring_view path) -> bool {
|
||||
return contains_trash_directory(utils::string::to_utf8(path));
|
||||
}
|
||||
|
||||
auto make_file_uri(std::string_view path) -> std::string {
|
||||
auto abs_path = absolute(path);
|
||||
#if defined(_WIN32)
|
||||
utils::string::replace(abs_path, backslash, slash);
|
||||
abs_path = std::string{slash} + abs_path;
|
||||
#endif // defined(_WIN32)
|
||||
return "file://" + abs_path;
|
||||
}
|
||||
|
||||
auto make_file_uri(std::wstring_view path) -> std::wstring {
|
||||
return utils::string::from_utf8(make_file_uri(utils::string::to_utf8(path)));
|
||||
}
|
||||
|
||||
auto strip_to_file_name(std::string path) -> std::string {
|
||||
if (path == "." || path == "..") {
|
||||
return path;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
return ::PathFindFileNameA(path.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
return utils::string::contains(path, slash) ? basename(path.data()) : path;
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
auto strip_to_file_name(std::wstring path) -> std::wstring {
|
||||
return utils::string::from_utf8(
|
||||
strip_to_file_name(utils::string::to_utf8(path)));
|
||||
}
|
||||
|
||||
auto unmake_file_uri(std::string_view uri) -> std::string {
|
||||
static constexpr const std::array<std::string_view, 23U> escape_characters = {
|
||||
{
|
||||
" ", "<", ">", "#", "%", "+", "{", "}", "|", "\\", "^", "~",
|
||||
"[", "]", "`", ";", "/", "?", ":", "@", "=", "&", "$",
|
||||
}};
|
||||
static constexpr const std::array<std::string_view, 23U>
|
||||
escape_sequences_lower = {{
|
||||
"%20", "%3c", "%3e", "%23", "%25", "%2b", "%7b", "%7d",
|
||||
"%7c", "%5c", "%5e", "%7e", "%5b", "%5d", "%60", "%3b",
|
||||
"%2f", "%3f", "%3a", "%40", "%3d", "%26", "%24",
|
||||
}};
|
||||
static constexpr const std::array<std::string_view, 23U>
|
||||
escape_sequences_upper = {{
|
||||
"%20", "%3C", "%3E", "%23", "%25", "%2B", "%7B", "%7D",
|
||||
"%7C", "%5C", "%5E", "%7E", "%5B", "%5D", "%60", "%3B",
|
||||
"%2F", "%3F", "%3A", "%40", "%3D", "%26", "%24",
|
||||
}};
|
||||
|
||||
std::string ret_uri{uri};
|
||||
#if defined(_WIN32)
|
||||
ret_uri = ret_uri.substr(8U);
|
||||
#else
|
||||
ret_uri = ret_uri.substr(7U);
|
||||
#endif
|
||||
|
||||
for (std::size_t idx = 0U; idx < escape_characters.size(); idx++) {
|
||||
utils::string::replace(ret_uri, escape_sequences_lower.at(idx),
|
||||
escape_characters.at(idx));
|
||||
utils::string::replace(ret_uri, escape_sequences_upper.at(idx),
|
||||
escape_characters.at(idx));
|
||||
}
|
||||
|
||||
return absolute(ret_uri);
|
||||
}
|
||||
|
||||
auto unmake_file_uri(std::wstring_view uri) -> std::wstring {
|
||||
return utils::string::from_utf8(unmake_file_uri(utils::string::to_utf8(uri)));
|
||||
}
|
||||
} // namespace monitarr::utils::path
|
||||
138
support/src/utils/string.cpp
Normal file
138
support/src/utils/string.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace monitarr::utils::string {
|
||||
auto from_bool(bool val) -> std::string {
|
||||
return std::to_string(static_cast<int>(val));
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_BOOST)
|
||||
auto from_dynamic_bitset(const boost::dynamic_bitset<> &bitset) -> std::string {
|
||||
std::stringstream stream;
|
||||
boost::archive::text_oarchive archive(stream);
|
||||
archive << bitset;
|
||||
return stream.str();
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
auto from_utf8(std::string_view str) -> std::wstring {
|
||||
return str.empty()
|
||||
? L""
|
||||
: std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>()
|
||||
.from_bytes(std::string{str});
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_SFML)
|
||||
auto replace_sf(sf::String &src, const sf::String &find, const sf::String &with,
|
||||
std::size_t start_pos) -> sf::String & {
|
||||
if (not src.isEmpty() && (start_pos < src.getSize())) {
|
||||
while ((start_pos = src.find(find, start_pos)) != std::string::npos) {
|
||||
src.replace(start_pos, find.getSize(), with);
|
||||
start_pos += with.getSize();
|
||||
}
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
auto split_sf(sf::String str, wchar_t delim,
|
||||
bool should_trim) -> std::vector<sf::String> {
|
||||
auto result = std::views::split(str.toWideString(), delim);
|
||||
|
||||
std::vector<sf::String> ret{};
|
||||
for (auto &&word : result) {
|
||||
auto val = std::wstring{word.begin(), word.end()};
|
||||
if (should_trim) {
|
||||
trim(val);
|
||||
}
|
||||
ret.emplace_back(val);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_SFML)
|
||||
|
||||
auto to_bool(std::string val) -> bool {
|
||||
auto ret = false;
|
||||
|
||||
trim(val);
|
||||
if (is_numeric(val)) {
|
||||
if (contains(val, ".")) {
|
||||
ret = (to_double(val) != 0.0);
|
||||
} else {
|
||||
std::istringstream(val) >> ret;
|
||||
}
|
||||
} else {
|
||||
std::istringstream(to_lower(val)) >> std::boolalpha >> ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto to_double(const std::string &str) -> double { return std::stod(str); }
|
||||
|
||||
#if defined(PROJECT_ENABLE_BOOST)
|
||||
auto to_dynamic_bitset(const std::string &val) -> boost::dynamic_bitset<> {
|
||||
std::stringstream stream(val);
|
||||
boost::dynamic_bitset<> bitset;
|
||||
boost::archive::text_iarchive archive(stream);
|
||||
archive >> bitset;
|
||||
return bitset;
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
auto to_int32(const std::string &val) -> std::int32_t { return std::stoi(val); }
|
||||
|
||||
auto to_int64(const std::string &val) -> std::int64_t {
|
||||
return std::stoll(val);
|
||||
}
|
||||
|
||||
auto to_size_t(const std::string &val) -> std::size_t {
|
||||
return static_cast<std::size_t>(std::stoull(val));
|
||||
}
|
||||
|
||||
auto to_uint8(const std::string &val) -> std::uint8_t {
|
||||
return static_cast<std::uint8_t>(std::stoul(val));
|
||||
}
|
||||
|
||||
auto to_uint16(const std::string &val) -> std::uint16_t {
|
||||
return static_cast<std::uint16_t>(std::stoul(val));
|
||||
}
|
||||
|
||||
auto to_uint32(const std::string &val) -> std::uint32_t {
|
||||
return static_cast<std::uint32_t>(std::stoul(val));
|
||||
}
|
||||
|
||||
auto to_uint64(const std::string &val) -> std::uint64_t {
|
||||
return std::stoull(val);
|
||||
}
|
||||
|
||||
auto to_utf8(std::string_view str) -> std::string { return std::string{str}; }
|
||||
|
||||
auto to_utf8(std::wstring_view str) -> std::string {
|
||||
return str.empty()
|
||||
? ""
|
||||
: std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>()
|
||||
.to_bytes(std::wstring{str});
|
||||
}
|
||||
} // namespace monitarr::utils::string
|
||||
88
support/src/utils/time.cpp
Normal file
88
support/src/utils/time.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/time.hpp"
|
||||
|
||||
namespace monitarr::utils::time {
|
||||
void get_local_time_now(struct tm &local_time) {
|
||||
std::memset(&local_time, 0, sizeof(local_time));
|
||||
|
||||
const auto now =
|
||||
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||
#if defined(_WIN32)
|
||||
localtime_s(&local_time, &now);
|
||||
#else // !defined(_WIN32)
|
||||
localtime_r(&now, &local_time);
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
auto get_time_now() -> std::uint64_t {
|
||||
return static_cast<std::uint64_t>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
// https://stackoverflow.com/questions/321849/strptime-equivalent-on-windows
|
||||
auto strptime(const char *s, const char *f, struct tm *tm) -> const char * {
|
||||
std::istringstream input{s};
|
||||
input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
|
||||
input >> std::get_time(tm, f);
|
||||
if (input.fail()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return reinterpret_cast<const char *>(s + input.tellg());
|
||||
}
|
||||
|
||||
// https://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/
|
||||
auto unix_time_to_filetime(std::uint64_t unix_time) -> FILETIME {
|
||||
auto win_time = unix_time_to_windows_time(unix_time);
|
||||
|
||||
FILETIME file_time{};
|
||||
file_time.dwHighDateTime = static_cast<DWORD>(win_time >> 32U);
|
||||
file_time.dwLowDateTime = win_time & 0xFFFFFFFF;
|
||||
return file_time;
|
||||
}
|
||||
|
||||
auto windows_file_time_to_unix_time(FILETIME win_time) -> std::uint64_t {
|
||||
return windows_time_to_unix_time(
|
||||
(static_cast<std::uint64_t>(win_time.dwHighDateTime) << 32ULL) |
|
||||
static_cast<std::uint64_t>(win_time.dwLowDateTime));
|
||||
}
|
||||
|
||||
auto windows_time_t_to_unix_time(__time64_t win_time) -> std::uint64_t {
|
||||
return static_cast<std::uint64_t>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::system_clock::from_time_t(win_time).time_since_epoch())
|
||||
.count());
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
auto unix_time_to_windows_time(std::uint64_t unix_time) -> std::uint64_t {
|
||||
return (unix_time / WIN32_TIME_NANOS_PER_TICK) + WIN32_TIME_CONVERSION;
|
||||
}
|
||||
|
||||
auto windows_time_to_unix_time(std::uint64_t win_time) -> std::uint64_t {
|
||||
return (win_time - WIN32_TIME_CONVERSION) * WIN32_TIME_NANOS_PER_TICK;
|
||||
}
|
||||
} // namespace monitarr::utils::time
|
||||
83
support/src/utils/unix.cpp
Normal file
83
support/src/utils/unix.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if !defined(_WIN32)
|
||||
|
||||
#include "utils/unix.hpp"
|
||||
#include "utils/collection.hpp"
|
||||
|
||||
namespace monitarr::utils {
|
||||
#if !defined(__APPLE__)
|
||||
auto convert_to_uint64(const pthread_t &thread) -> std::uint64_t {
|
||||
return static_cast<std::uint64_t>(thread);
|
||||
}
|
||||
#endif // !defined(__APPLE__)
|
||||
|
||||
auto get_last_error_code() -> int { return errno; }
|
||||
|
||||
auto get_thread_id() -> std::uint64_t {
|
||||
return convert_to_uint64(pthread_self());
|
||||
}
|
||||
|
||||
auto is_uid_member_of_group(uid_t uid, gid_t gid) -> bool {
|
||||
std::vector<gid_t> groups{};
|
||||
auto res = use_getpwuid(uid, [&groups](struct passwd *pass) {
|
||||
int group_count{};
|
||||
if (getgrouplist(pass->pw_name, pass->pw_gid, nullptr, &group_count) < 0) {
|
||||
groups.resize(static_cast<std::size_t>(group_count));
|
||||
#if defined(__APPLE__)
|
||||
getgrouplist(pass->pw_name, pass->pw_gid,
|
||||
reinterpret_cast<int *>(groups.data()), &group_count);
|
||||
#else // !defined(__APPLE__)
|
||||
getgrouplist(pass->pw_name, pass->pw_gid, groups.data(), &group_count);
|
||||
#endif // defined(__APPLE__)
|
||||
}
|
||||
});
|
||||
|
||||
if (not res) {
|
||||
throw utils::error::create_exception(res.function_name,
|
||||
{"use_getpwuid failed", res.reason});
|
||||
}
|
||||
|
||||
return collection::includes(groups, gid);
|
||||
}
|
||||
|
||||
auto use_getpwuid(uid_t uid, passwd_callback_t callback) -> result {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
static std::mutex mtx{};
|
||||
mutex_lock lock{mtx};
|
||||
|
||||
auto *temp_pw = getpwuid(uid);
|
||||
if (temp_pw == nullptr) {
|
||||
return {
|
||||
std::string{function_name},
|
||||
false,
|
||||
"'getpwuid' returned nullptr",
|
||||
};
|
||||
}
|
||||
|
||||
callback(temp_pw);
|
||||
return {std::string{function_name}};
|
||||
}
|
||||
} // namespace monitarr::utils
|
||||
|
||||
#endif // !defined(_WIN32)
|
||||
144
support/src/utils/windows.cpp
Normal file
144
support/src/utils/windows.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include "utils/windows.hpp"
|
||||
|
||||
#include "utils/com_init_wrapper.hpp"
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace monitarr::utils {
|
||||
void create_console() {
|
||||
if (::AllocConsole() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *dummy{nullptr};
|
||||
freopen_s(&dummy, "CONOUT$", "w", stdout);
|
||||
freopen_s(&dummy, "CONOUT$", "w", stderr);
|
||||
freopen_s(&dummy, "CONIN$", "r", stdin);
|
||||
std::cout.clear();
|
||||
std::clog.clear();
|
||||
std::cerr.clear();
|
||||
std::cin.clear();
|
||||
|
||||
auto *out_w = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
auto *in_w = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, out_w);
|
||||
SetStdHandle(STD_ERROR_HANDLE, out_w);
|
||||
SetStdHandle(STD_INPUT_HANDLE, in_w);
|
||||
std::wcout.clear();
|
||||
std::wclog.clear();
|
||||
std::wcerr.clear();
|
||||
std::wcin.clear();
|
||||
}
|
||||
|
||||
void free_console() { ::FreeConsole(); }
|
||||
|
||||
auto get_last_error_code() -> DWORD { return ::GetLastError(); }
|
||||
|
||||
auto get_local_app_data_directory() -> const std::string & {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
static std::string app_data = ([]() -> std::string {
|
||||
com_init_wrapper cw;
|
||||
PWSTR local_app_data{};
|
||||
if (SUCCEEDED(::SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr,
|
||||
&local_app_data))) {
|
||||
auto ret = utils::string::to_utf8(local_app_data);
|
||||
::CoTaskMemFree(local_app_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"unable to detect local application data folder",
|
||||
});
|
||||
})();
|
||||
|
||||
return app_data;
|
||||
}
|
||||
|
||||
auto get_thread_id() -> std::uint64_t {
|
||||
return static_cast<std::uint64_t>(::GetCurrentThreadId());
|
||||
}
|
||||
|
||||
auto is_process_elevated() -> bool {
|
||||
auto ret{false};
|
||||
HANDLE token{INVALID_HANDLE_VALUE};
|
||||
if (::OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
|
||||
TOKEN_ELEVATION te{};
|
||||
DWORD sz = sizeof(te);
|
||||
if (::GetTokenInformation(token, TokenElevation, &te, sizeof(te), &sz)) {
|
||||
ret = (te.TokenIsElevated != 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (token != INVALID_HANDLE_VALUE) {
|
||||
::CloseHandle(token);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto run_process_elevated(std::vector<const char *> args) -> int {
|
||||
std::cout << "Elevating Process" << std::endl;
|
||||
std::string parameters{"-hidden"};
|
||||
for (std::size_t idx = 1U; idx < args.size(); idx++) {
|
||||
parameters +=
|
||||
(parameters.empty() ? args.at(idx) : " " + std::string(args.at(idx)));
|
||||
}
|
||||
|
||||
std::string full_path;
|
||||
full_path.resize(monitarr::max_path_length + 1);
|
||||
|
||||
if (::GetModuleFileNameA(nullptr, full_path.data(),
|
||||
monitarr::max_path_length)) {
|
||||
SHELLEXECUTEINFOA sei{};
|
||||
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
|
||||
sei.cbSize = sizeof(sei);
|
||||
sei.lpVerb = "runas";
|
||||
sei.lpFile = full_path.c_str();
|
||||
sei.lpParameters = parameters.c_str();
|
||||
sei.hwnd = nullptr;
|
||||
sei.nShow = SW_NORMAL;
|
||||
if (::ShellExecuteExA(&sei)) {
|
||||
::WaitForSingleObject(sei.hProcess, INFINITE);
|
||||
DWORD exit_code{};
|
||||
::GetExitCodeProcess(sei.hProcess, &exit_code);
|
||||
::CloseHandle(sei.hProcess);
|
||||
return static_cast<int>(exit_code);
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<int>(::GetLastError());
|
||||
}
|
||||
|
||||
void set_last_error_code(DWORD error_code) { ::SetLastError(error_code); }
|
||||
} // namespace monitarr::utils
|
||||
|
||||
#endif // defined(_WIN32)
|
||||
63
support/test/include/test.hpp
Normal file
63
support/test/include/test.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef MONITARR_TEST_INCLUDE_TEST_HPP_
|
||||
#define MONITARR_TEST_INCLUDE_TEST_HPP_
|
||||
|
||||
#if defined(U)
|
||||
#undef U
|
||||
#endif // defined(U)
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using ::testing::_;
|
||||
using namespace ::testing;
|
||||
|
||||
#define COMMA ,
|
||||
|
||||
#include "utils/all.hpp"
|
||||
|
||||
namespace monitarr::test {
|
||||
[[nodiscard]] auto create_random_file(std::size_t size)
|
||||
-> utils::file::i_file &;
|
||||
|
||||
[[nodiscard]] auto
|
||||
generate_test_file_name(std::string_view file_name_no_extension) -> std::string;
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
template <typename buffer_t, typename result_t>
|
||||
static void decrypt_and_verify(const buffer_t &buffer, std::string_view token,
|
||||
result_t &result) {
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(token, buffer, result));
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
auto generate_test_directory() -> utils::file::i_directory &;
|
||||
|
||||
[[nodiscard]] auto get_test_config_dir() -> std::string;
|
||||
|
||||
[[nodiscard]] auto get_test_input_dir() -> std::string;
|
||||
|
||||
[[nodiscard]] auto get_test_output_dir() -> std::string;
|
||||
} // namespace monitarr::test
|
||||
|
||||
#endif // MONITARR_TEST_INCLUDE_TEST_HPP_
|
||||
148
support/test/src/test.cpp
Normal file
148
support/test/src/test.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "test.hpp"
|
||||
|
||||
extern int PROJECT_TEST_RESULT;
|
||||
|
||||
namespace {
|
||||
static std::recursive_mutex file_mtx{};
|
||||
|
||||
static std::vector<std::unique_ptr<monitarr::utils::file::i_fs_item>>
|
||||
generated_files{};
|
||||
|
||||
struct file_deleter final {
|
||||
std::string test_output_dir;
|
||||
|
||||
~file_deleter() {
|
||||
generated_files.clear();
|
||||
|
||||
if (PROJECT_TEST_RESULT == 0) {
|
||||
EXPECT_TRUE(monitarr::utils::file::directory(test_output_dir)
|
||||
.remove_recursively());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const auto deleter{
|
||||
std::make_unique<file_deleter>(monitarr::test::get_test_output_dir()),
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace monitarr::test {
|
||||
auto create_random_file(std::size_t size) -> utils::file::i_file & {
|
||||
auto path = generate_test_file_name("random");
|
||||
auto file = utils::file::file::open_or_create_file(path);
|
||||
EXPECT_TRUE(*file);
|
||||
if (*file) {
|
||||
data_buffer buf(size);
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
randombytes_buf(buf.data(), buf.size());
|
||||
#else // !defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
thread_local std::mt19937 gen{
|
||||
static_cast<std::uint_fast32_t>(std::time(nullptr)) ^
|
||||
static_cast<std::uint_fast32_t>(std::random_device{}()),
|
||||
};
|
||||
std::uniform_int_distribution<std::uint8_t> dis(0U, 255U);
|
||||
std::generate(buf.begin(), buf.end(), [&]() -> auto { return dis(gen); });
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
std::size_t bytes_written{};
|
||||
EXPECT_TRUE(file->write(buf, 0U, &bytes_written));
|
||||
EXPECT_EQ(size, bytes_written);
|
||||
|
||||
EXPECT_EQ(size, file->size());
|
||||
}
|
||||
|
||||
recur_mutex_lock lock{file_mtx};
|
||||
generated_files.emplace_back(std::move(file));
|
||||
return *dynamic_cast<utils::file::i_file *>(generated_files.back().get());
|
||||
}
|
||||
|
||||
auto generate_test_directory() -> utils::file::i_directory & {
|
||||
auto path = utils::path::combine(
|
||||
get_test_output_dir(),
|
||||
{
|
||||
std::string{"test_dir"} + std::to_string(generated_files.size()),
|
||||
});
|
||||
|
||||
recur_mutex_lock lock{file_mtx};
|
||||
generated_files.emplace_back(std::unique_ptr<utils::file::i_fs_item>(
|
||||
new utils::file::directory{path}));
|
||||
|
||||
auto &ret =
|
||||
*dynamic_cast<utils::file::i_directory *>(generated_files.back().get());
|
||||
EXPECT_TRUE(ret.create_directory());
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto generate_test_file_name(std::string_view file_name_no_extension)
|
||||
-> std::string {
|
||||
auto path = utils::path::combine(
|
||||
get_test_output_dir(), {
|
||||
std::string{file_name_no_extension} +
|
||||
std::to_string(generated_files.size()),
|
||||
});
|
||||
|
||||
recur_mutex_lock lock{file_mtx};
|
||||
generated_files.emplace_back(
|
||||
std::unique_ptr<utils::file::i_file>(new utils::file::file{path}));
|
||||
return generated_files.back()->get_path();
|
||||
}
|
||||
|
||||
auto get_test_config_dir() -> std::string {
|
||||
static auto test_path = ([]() -> std::string {
|
||||
auto dir = utils::get_environment_variable("PROJECT_TEST_CONFIG_DIR");
|
||||
return utils::path::combine(dir.empty() ? "." : dir, {"test_config"});
|
||||
})();
|
||||
|
||||
return test_path;
|
||||
}
|
||||
|
||||
auto get_test_input_dir() -> std::string {
|
||||
static auto test_path = ([]() -> std::string {
|
||||
auto dir = utils::get_environment_variable("PROJECT_TEST_INPUT_DIR");
|
||||
return utils::path::combine(dir.empty() ? "." : dir, {"test_input"});
|
||||
})();
|
||||
|
||||
return test_path;
|
||||
}
|
||||
|
||||
auto get_test_output_dir() -> std::string {
|
||||
static auto test_path = ([]() -> std::string {
|
||||
auto temp = utils::file::create_temp_name("project_test");
|
||||
|
||||
#if defined(_WIN32)
|
||||
auto path = utils::path::combine("%TEMP%", {temp});
|
||||
#else // !defined(_WIN32)
|
||||
auto path = utils::path::combine("/tmp", {temp});
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
if (not utils::file::directory(path).exists()) {
|
||||
EXPECT_TRUE(utils::file::directory{path}.create_directory());
|
||||
}
|
||||
|
||||
return path;
|
||||
})();
|
||||
|
||||
return test_path;
|
||||
}
|
||||
} // namespace monitarr::test
|
||||
250
support/test/src/utils/collection_test.cpp
Normal file
250
support/test/src/utils/collection_test.cpp
Normal file
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "test.hpp"
|
||||
|
||||
namespace monitarr {
|
||||
TEST(utils_collection, excludes) {
|
||||
auto data = {"cow", "moose", "dog", "chicken"};
|
||||
EXPECT_FALSE(utils::collection::excludes(data, "chicken"));
|
||||
EXPECT_FALSE(utils::collection::excludes(data, "cow"));
|
||||
EXPECT_FALSE(utils::collection::excludes(data, "dog"));
|
||||
EXPECT_FALSE(utils::collection::excludes(data, "moose"));
|
||||
EXPECT_TRUE(utils::collection::excludes(data, "mouse"));
|
||||
}
|
||||
|
||||
TEST(utils_collection, includes) {
|
||||
auto data = {"cow", "moose", "dog", "chicken"};
|
||||
EXPECT_FALSE(utils::collection::includes(data, "mice"));
|
||||
EXPECT_TRUE(utils::collection::includes(data, "chicken"));
|
||||
EXPECT_TRUE(utils::collection::includes(data, "cow"));
|
||||
EXPECT_TRUE(utils::collection::includes(data, "dog"));
|
||||
EXPECT_TRUE(utils::collection::includes(data, "moose"));
|
||||
}
|
||||
|
||||
TEST(utils_collection, from_hex_string) {
|
||||
{
|
||||
auto data = "0xABCDEF10";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(4U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = " 0xABCDEF10 ";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(4U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = "ABCDEF10";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(4U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = "ACDEF";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(3U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = " ACDEF ";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(3U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = "";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L"0xABCDEF10";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(4U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L" 0xABCDEF10 ";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(4U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L"ABCDEF10";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(4U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L"ACDEF";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(3U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L" ACDEF ";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_EQ(3U, val.size());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L"";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_TRUE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_collection, from_hex_string_fails) {
|
||||
{
|
||||
auto data = "ABCDEF1Z";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_FALSE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = "ABC DEF1";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_FALSE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = "0x";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_FALSE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = " 0x ";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_FALSE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L"ABCDEF1Z";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_FALSE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L"ABC DEF1";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_FALSE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L"0x";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_FALSE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
|
||||
{
|
||||
auto data = L" 0x";
|
||||
std::vector<std::uint8_t> val{};
|
||||
EXPECT_FALSE(utils::collection::from_hex_string(data, val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_collection, to_hex_string) {
|
||||
{
|
||||
std::array<std::int8_t, 2U> col{
|
||||
static_cast<std::int8_t>(0xFF),
|
||||
static_cast<std::int8_t>(0xEE),
|
||||
};
|
||||
|
||||
auto str = utils::collection::to_hex_string(col);
|
||||
EXPECT_STREQ("ffee", str.c_str());
|
||||
|
||||
auto w_str = utils::collection::to_hex_wstring(col);
|
||||
EXPECT_STREQ(L"ffee", w_str.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
std::array<std::uint8_t, 2U> col{
|
||||
static_cast<std::uint8_t>(0xFF),
|
||||
static_cast<std::uint8_t>(0xEE),
|
||||
};
|
||||
|
||||
auto str = utils::collection::to_hex_string(col);
|
||||
EXPECT_STREQ("ffee", str.c_str());
|
||||
|
||||
auto w_str = utils::collection::to_hex_wstring(col);
|
||||
EXPECT_STREQ(L"ffee", w_str.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_collection, remove_element) {
|
||||
{
|
||||
std::vector<std::uint8_t> col{
|
||||
static_cast<std::uint8_t>(0xFF),
|
||||
static_cast<std::uint8_t>(0xEE),
|
||||
};
|
||||
|
||||
utils::collection::remove_element(col, 0xFF);
|
||||
EXPECT_EQ(1U, col.size());
|
||||
EXPECT_EQ(static_cast<std::uint8_t>(0xEE), col.at(0U));
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::uint8_t> col{
|
||||
static_cast<std::uint8_t>(0xFF),
|
||||
static_cast<std::uint8_t>(0xEE),
|
||||
};
|
||||
|
||||
utils::collection::remove_element(col, 0xEE);
|
||||
EXPECT_EQ(1U, col.size());
|
||||
EXPECT_EQ(static_cast<std::uint8_t>(0xFF), col.at(0U));
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::uint8_t> col{
|
||||
static_cast<std::uint8_t>(0xFF),
|
||||
static_cast<std::uint8_t>(0xEE),
|
||||
};
|
||||
|
||||
utils::collection::remove_element(col, 0xEF);
|
||||
EXPECT_EQ(2U, col.size());
|
||||
EXPECT_EQ(static_cast<std::uint8_t>(0xFF), col.at(0U));
|
||||
EXPECT_EQ(static_cast<std::uint8_t>(0xEE), col.at(1U));
|
||||
}
|
||||
}
|
||||
} // namespace monitarr
|
||||
306
support/test/src/utils/common_test.cpp
Normal file
306
support/test/src/utils/common_test.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "test.hpp"
|
||||
|
||||
namespace monitarr {
|
||||
TEST(utils_common, calculate_read_size) {
|
||||
auto read_size = utils::calculate_read_size(0U, 0U, 0U);
|
||||
EXPECT_EQ(0U, read_size);
|
||||
|
||||
read_size = utils::calculate_read_size(5U, 0U, 0U);
|
||||
EXPECT_EQ(0U, read_size);
|
||||
|
||||
read_size = utils::calculate_read_size(0U, 6U, 7U);
|
||||
EXPECT_EQ(0U, read_size);
|
||||
|
||||
read_size = utils::calculate_read_size(7U, 1U, 7U);
|
||||
EXPECT_EQ(0U, read_size);
|
||||
|
||||
read_size = utils::calculate_read_size(5U, 5U, 0U);
|
||||
EXPECT_EQ(5U, read_size);
|
||||
|
||||
read_size = utils::calculate_read_size(5U, 5U, 1U);
|
||||
EXPECT_EQ(4U, read_size);
|
||||
}
|
||||
|
||||
TEST(utils_common, version_equal) {
|
||||
EXPECT_EQ(0, utils::compare_version_strings("", ""));
|
||||
EXPECT_EQ(0, utils::compare_version_strings("1.0", "1.0"));
|
||||
EXPECT_EQ(0, utils::compare_version_strings("1.0.0", "1.0"));
|
||||
EXPECT_EQ(0, utils::compare_version_strings("1.0.0.0", "1.0"));
|
||||
EXPECT_EQ(0, utils::compare_version_strings("1.0.0.0", "1.0.0"));
|
||||
|
||||
EXPECT_EQ(0, utils::compare_version_strings(L"", L""));
|
||||
EXPECT_EQ(0, utils::compare_version_strings(L"1.0", L"1.0"));
|
||||
EXPECT_EQ(0, utils::compare_version_strings(L"1.0.0", L"1.0"));
|
||||
EXPECT_EQ(0, utils::compare_version_strings(L"1.0.0.0", L"1.0"));
|
||||
EXPECT_EQ(0, utils::compare_version_strings(L"1.0.0.0", L"1.0.0"));
|
||||
}
|
||||
|
||||
TEST(utils_common, version_greater) {
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1", ""));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1", "1.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1", "1.0.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1", "1.0.0.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1.0", "1.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1.0", "1.0.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1.0", "1.0.0.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0", "0.9.9"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1", "0.9.9"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1.0", "0.9.9"));
|
||||
|
||||
EXPECT_EQ(1, utils::compare_version_strings("1.0.1", ""));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0.1", L"1.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0.1", L"1.0.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0.1", L"1.0.0.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0.1.0", L"1.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0.1.0", L"1.0.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0.1.0", L"1.0.0.0"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0", L"0.9.9"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0.1", L"0.9.9"));
|
||||
EXPECT_EQ(1, utils::compare_version_strings(L"1.0.1.0", L"0.9.9"));
|
||||
}
|
||||
|
||||
TEST(utils_common, version_less) {
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("", "1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("0.9.9", "1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("0.9.9", "1.0.1"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("0.9.9", "1.0.1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("1.0", "1.0.1"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("1.0", "1.0.1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("1.0.0", "1.0.1"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("1.0.0", "1.0.1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("1.0.0.0", "1.0.1"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings("1.0.0.0", "1.0.1.0"));
|
||||
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"", L"1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"0.9.9", L"1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"0.9.9", L"1.0.1"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"0.9.9", L"1.0.1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"1.0", L"1.0.1"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"1.0", L"1.0.1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"1.0.0", L"1.0.1"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"1.0.0", L"1.0.1.0"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"1.0.0.0", L"1.0.1"));
|
||||
EXPECT_EQ(-1, utils::compare_version_strings(L"1.0.0.0", L"1.0.1.0"));
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_STDUUID)
|
||||
TEST(utils_common, create_uuid_string) {
|
||||
{
|
||||
const auto uuid1 = utils::create_uuid_string();
|
||||
const auto uuid2 = utils::create_uuid_string();
|
||||
ASSERT_EQ(36U, uuid1.size());
|
||||
ASSERT_EQ(36U, uuid2.size());
|
||||
ASSERT_STRNE(uuid1.c_str(), uuid2.c_str());
|
||||
}
|
||||
{
|
||||
const auto uuid1 = utils::create_uuid_wstring();
|
||||
const auto uuid2 = utils::create_uuid_wstring();
|
||||
ASSERT_EQ(36U, uuid1.size());
|
||||
ASSERT_EQ(36U, uuid2.size());
|
||||
ASSERT_STRNE(uuid1.c_str(), uuid2.c_str());
|
||||
}
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_STDUUID)
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
TEST(utils_common, generate_secure_random) {
|
||||
{
|
||||
auto r1 = utils::generate_secure_random<std::size_t>();
|
||||
auto r2 = utils::generate_secure_random<std::size_t>();
|
||||
EXPECT_NE(r1, r2);
|
||||
}
|
||||
|
||||
{
|
||||
auto r1 = utils::generate_secure_random<std::vector<std::uint8_t>>(6U);
|
||||
auto r2 = utils::generate_secure_random<std::vector<std::uint8_t>>(6U);
|
||||
EXPECT_EQ(6U, r1.size());
|
||||
EXPECT_EQ(r1.size(), r2.size());
|
||||
EXPECT_NE(r1, r2);
|
||||
}
|
||||
|
||||
{
|
||||
auto r1 = utils::generate_secure_random<std::array<std::uint8_t, 4U>>();
|
||||
auto r2 = utils::generate_secure_random<std::array<std::uint8_t, 4U>>();
|
||||
EXPECT_EQ(4U, r1.size());
|
||||
EXPECT_EQ(r1.size(), r2.size());
|
||||
EXPECT_NE(0, std::memcmp(r1.data(), r2.data(), r1.size()));
|
||||
}
|
||||
|
||||
{
|
||||
auto r1 = utils::generate_secure_random<std::string>(6U);
|
||||
auto r2 = utils::generate_secure_random<std::string>(6U);
|
||||
EXPECT_EQ(6U, r1.size());
|
||||
EXPECT_EQ(r1.size(), r2.size());
|
||||
EXPECT_NE(0, std::memcmp(r1.data(), r2.data(), r1.size()));
|
||||
}
|
||||
|
||||
{
|
||||
auto r1 = utils::generate_secure_random<std::wstring>(6U);
|
||||
auto r2 = utils::generate_secure_random<std::wstring>(6U);
|
||||
EXPECT_EQ(6U, r1.size());
|
||||
EXPECT_EQ(r1.size(), r2.size());
|
||||
EXPECT_NE(0, std::memcmp(r1.data(), r2.data(), r1.size()));
|
||||
}
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
TEST(utils_common, divide_with_ceiling) {
|
||||
auto r = utils::divide_with_ceiling(12, 5);
|
||||
EXPECT_EQ(3, r);
|
||||
|
||||
r = utils::divide_with_ceiling(12, 4);
|
||||
EXPECT_EQ(3, r);
|
||||
|
||||
r = utils::divide_with_ceiling(1, 2);
|
||||
EXPECT_EQ(1, r);
|
||||
|
||||
r = utils::divide_with_ceiling(2, 2);
|
||||
EXPECT_EQ(1, r);
|
||||
|
||||
r = utils::divide_with_ceiling(0, 2);
|
||||
EXPECT_EQ(0, r);
|
||||
}
|
||||
|
||||
TEST(utils_common, generate_random_between_for_signed_integers) {
|
||||
static constexpr const auto max_iterations{1000000UL};
|
||||
|
||||
for (std::size_t idx = 0U; idx < max_iterations; ++idx) {
|
||||
auto res = utils::generate_random_between(5, 12);
|
||||
EXPECT_GE(res, 5);
|
||||
EXPECT_LE(res, 12);
|
||||
}
|
||||
|
||||
for (std::size_t idx = 0U; idx < max_iterations; ++idx) {
|
||||
auto res = utils::generate_random_between(-5, 12);
|
||||
EXPECT_GE(res, -5);
|
||||
EXPECT_LE(res, 12);
|
||||
}
|
||||
|
||||
for (std::size_t idx = 0U; idx < max_iterations; ++idx) {
|
||||
auto res = utils::generate_random_between(-5, -1);
|
||||
EXPECT_GE(res, -5);
|
||||
EXPECT_LE(res, -1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_common, generate_random_between_for_unsigned_integers) {
|
||||
static constexpr const auto max_iterations{1000000UL};
|
||||
|
||||
for (std::size_t idx = 0U; idx < max_iterations; ++idx) {
|
||||
auto res = utils::generate_random_between(5U, 12U);
|
||||
EXPECT_GE(res, 5);
|
||||
EXPECT_LE(res, 12);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_common, generate_random_between_throws_error_on_invalid_range) {
|
||||
EXPECT_THROW(
|
||||
{
|
||||
try {
|
||||
[[maybe_unused]] auto res = utils::generate_random_between(12, 5);
|
||||
} catch (const std::range_error &e) {
|
||||
EXPECT_STREQ("end must be greater than begin", e.what());
|
||||
throw;
|
||||
}
|
||||
},
|
||||
std::range_error);
|
||||
|
||||
EXPECT_THROW(
|
||||
{
|
||||
try {
|
||||
[[maybe_unused]] auto res = utils::generate_random_between(12, 12);
|
||||
} catch (const std::range_error &e) {
|
||||
EXPECT_STREQ("end must be greater than begin", e.what());
|
||||
throw;
|
||||
}
|
||||
},
|
||||
std::range_error);
|
||||
}
|
||||
|
||||
TEST(utils_common, generate_random_string) {
|
||||
static constexpr const auto max_iterations{10000L};
|
||||
|
||||
const auto test_string = [](auto str) {
|
||||
static std::vector<decltype(str)> list{};
|
||||
|
||||
EXPECT_FALSE(utils::collection::includes(list, str));
|
||||
list.push_back(str);
|
||||
|
||||
EXPECT_EQ(16U, str.size());
|
||||
for (auto &&ch : str) {
|
||||
auto ch_int = static_cast<std::uint32_t>(ch);
|
||||
EXPECT_GE(ch_int, 48U);
|
||||
EXPECT_LE(ch_int, 73U + 48U);
|
||||
}
|
||||
};
|
||||
|
||||
for (std::size_t idx = 0U; idx < max_iterations; ++idx) {
|
||||
test_string(utils::generate_random_string(16U));
|
||||
test_string(utils::generate_random_wstring(16U));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_common, generate_random_string_for_zero_length) {
|
||||
EXPECT_TRUE(utils::generate_random_string(0U).empty());
|
||||
EXPECT_TRUE(utils::generate_random_wstring(0U).empty());
|
||||
}
|
||||
|
||||
TEST(utils_common, get_environment_variable) {
|
||||
static constexpr const std::string path_env{"PATH"};
|
||||
std::string path;
|
||||
|
||||
#if defined(_WIN32)
|
||||
path.resize(monitarr::max_path_length + 1U);
|
||||
auto size = ::GetEnvironmentVariableA(path_env.c_str(), path.data(), 0U);
|
||||
|
||||
path.resize(size);
|
||||
::GetEnvironmentVariableA(path_env.c_str(), path.data(),
|
||||
static_cast<DWORD>(path.size()));
|
||||
#else // !defined(_WIN32)
|
||||
path = std::getenv(path_env.c_str());
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
EXPECT_STREQ(path.c_str(), utils::get_environment_variable(path_env).c_str());
|
||||
EXPECT_STREQ(
|
||||
utils::string::from_utf8(path).c_str(),
|
||||
utils::get_environment_variable(utils::string::from_utf8(path_env))
|
||||
.c_str());
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_BOOST)
|
||||
TEST(utils_common, get_next_available_port) {
|
||||
std::uint16_t available_port{};
|
||||
for (std::uint16_t port = 1025U; port < 1030U; ++port) {
|
||||
EXPECT_TRUE(utils::get_next_available_port(port, available_port));
|
||||
EXPECT_GE(available_port, port);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_common, get_next_available_port_fails_if_starting_point_is_zero) {
|
||||
std::uint16_t available_port{};
|
||||
EXPECT_FALSE(utils::get_next_available_port(0U, available_port));
|
||||
EXPECT_EQ(0U, available_port);
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
} // namespace monitarr
|
||||
300
support/test/src/utils/db_sqlite_test.cpp
Normal file
300
support/test/src/utils/db_sqlite_test.cpp
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "test.hpp"
|
||||
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
namespace monitarr {
|
||||
class utils_db_sqlite : public ::testing::Test {
|
||||
public:
|
||||
utils::db::sqlite::db3_t db3;
|
||||
|
||||
void SetUp() override {
|
||||
{
|
||||
sqlite3 *db3_ptr{nullptr};
|
||||
auto res = sqlite3_open(":memory:", &db3_ptr);
|
||||
ASSERT_EQ(SQLITE_OK, res);
|
||||
ASSERT_TRUE(db3_ptr != nullptr);
|
||||
db3 = utils::db::sqlite::db3_t{
|
||||
db3_ptr,
|
||||
utils::db::sqlite::sqlite3_deleter(),
|
||||
};
|
||||
}
|
||||
|
||||
utils::db::sqlite::db3_stmt_t db3_stmt;
|
||||
{
|
||||
std::string sql{
|
||||
"CREATE TABLE [table] (column1 TEXT PRIMARY KEY UNIQUE "
|
||||
"NOT NULL, column2 TEXT NOT NULL);",
|
||||
};
|
||||
|
||||
sqlite3_stmt *stmt_ptr{nullptr};
|
||||
auto res =
|
||||
sqlite3_prepare_v2(db3.get(), sql.c_str(), -1, &stmt_ptr, nullptr);
|
||||
db3_stmt = utils::db::sqlite::db3_stmt_t{
|
||||
stmt_ptr,
|
||||
utils::db::sqlite::sqlite3_statement_deleter(),
|
||||
};
|
||||
ASSERT_EQ(SQLITE_OK, res);
|
||||
}
|
||||
|
||||
auto res = sqlite3_step(db3_stmt.get());
|
||||
ASSERT_EQ(SQLITE_DONE, res);
|
||||
}
|
||||
|
||||
void TearDown() override { db3.reset(); }
|
||||
};
|
||||
|
||||
static void common_insert(sqlite3 &db3, bool dump = false) {
|
||||
auto query = utils::db::sqlite::db_insert{db3, "table"}
|
||||
.column_value("column1", "test0")
|
||||
.column_value("column2", "test1");
|
||||
if (dump) {
|
||||
std::cout << query.dump() << std::endl;
|
||||
}
|
||||
|
||||
auto res = query.go();
|
||||
EXPECT_TRUE(res.ok());
|
||||
}
|
||||
|
||||
static void common_select(sqlite3 &db3, std::string value1, std::string value2,
|
||||
bool dump = false) {
|
||||
auto query = utils::db::sqlite::db_select{db3, "table"};
|
||||
if (dump) {
|
||||
std::cout << query.dump() << std::endl;
|
||||
}
|
||||
|
||||
auto res = query.go();
|
||||
EXPECT_TRUE(res.ok());
|
||||
EXPECT_TRUE(res.has_row());
|
||||
|
||||
std::size_t row_count{};
|
||||
while (res.has_row()) {
|
||||
std::optional<utils::db::sqlite::db_result::row> row;
|
||||
EXPECT_TRUE(res.get_row(row));
|
||||
EXPECT_TRUE(row.has_value());
|
||||
if (row.has_value()) {
|
||||
auto columns = row.value().get_columns();
|
||||
EXPECT_EQ(std::size_t(2U), columns.size());
|
||||
EXPECT_STREQ("column1", columns[0U].get_name().c_str());
|
||||
EXPECT_STREQ(value1.c_str(),
|
||||
columns[0U].get_value<std::string>().c_str());
|
||||
EXPECT_STREQ("column2", columns[1U].get_name().c_str());
|
||||
EXPECT_STREQ(value2.c_str(),
|
||||
columns[1U].get_value<std::string>().c_str());
|
||||
for (auto &&column : columns) {
|
||||
std::cout << column.get_index() << ':';
|
||||
std::cout << column.get_name() << ':';
|
||||
std::cout << column.get_value<std::string>() << std::endl;
|
||||
}
|
||||
}
|
||||
++row_count;
|
||||
}
|
||||
|
||||
EXPECT_EQ(std::size_t(1U), row_count);
|
||||
}
|
||||
|
||||
static void common_delete(sqlite3 &db3, bool dump = false) {
|
||||
{
|
||||
auto query = utils::db::sqlite::db_delete{db3, "table"};
|
||||
if (dump) {
|
||||
std::cout << query.dump() << std::endl;
|
||||
}
|
||||
|
||||
auto res = query.go();
|
||||
EXPECT_TRUE(res.ok());
|
||||
}
|
||||
|
||||
{
|
||||
auto query = utils::db::sqlite::db_select{db3, "table"};
|
||||
auto res = query.go();
|
||||
EXPECT_TRUE(res.ok());
|
||||
|
||||
std::size_t row_count{};
|
||||
while (res.has_row()) {
|
||||
++row_count;
|
||||
}
|
||||
|
||||
EXPECT_EQ(std::size_t(0U), row_count);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_delete_query) {
|
||||
auto query = utils::db::sqlite::db_delete{*db3.get(), "table"};
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(R"(DELETE FROM "table";)", query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_delete_where_query) {
|
||||
auto query = utils::db::sqlite::db_delete{*db3.get(), "table"}
|
||||
.where("column1")
|
||||
.equals("test1")
|
||||
.and_()
|
||||
.where("column2")
|
||||
.equals("test2");
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(R"(DELETE FROM "table" WHERE "column1"=?1 AND "column2"=?2;)",
|
||||
query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_insert_query) {
|
||||
auto query = utils::db::sqlite::db_insert{*db3.get(), "table"}
|
||||
.column_value("column1", "test9")
|
||||
.column_value("column2", "test9");
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(R"(INSERT INTO "table" ("column1", "column2") VALUES (?1, ?2);)",
|
||||
query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_insert_or_replace_query) {
|
||||
auto query = utils::db::sqlite::db_insert{*db3.get(), "table"}
|
||||
.or_replace()
|
||||
.column_value("column1", "test1")
|
||||
.column_value("column2", "test2");
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(
|
||||
R"(INSERT OR REPLACE INTO "table" ("column1", "column2") VALUES (?1, ?2);)",
|
||||
query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_select_query) {
|
||||
auto query = utils::db::sqlite::db_select{*db3.get(), "table"};
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(R"(SELECT * FROM "table";)", query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_select_where_query) {
|
||||
auto query = utils::db::sqlite::db_select{*db3.get(), "table"}
|
||||
.where("column1")
|
||||
.equals("test1")
|
||||
.and_()
|
||||
.where("column2")
|
||||
.equals("test2");
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(R"(SELECT * FROM "table" WHERE "column1"=?1 AND "column2"=?2;)",
|
||||
query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_select_where_with_group_query) {
|
||||
auto query =
|
||||
utils::db::sqlite::db_select{*db3.get(), "table"}
|
||||
.group([](auto &grp) {
|
||||
grp.where("column1").equals("a").or_().where("column1").equals("b");
|
||||
})
|
||||
.and_()
|
||||
.group([](auto &grp) {
|
||||
grp.where("column2").equals("c").or_().where("column2").equals("d");
|
||||
})
|
||||
.or_()
|
||||
.group([](auto &grp) {
|
||||
grp.where("column1").equals("e").or_().where("column2").equals("f");
|
||||
});
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(
|
||||
R"(SELECT * FROM "table" WHERE ("column1"=?1 OR "column1"=?2) AND ("column2"=?3 OR "column2"=?4) OR ("column1"=?5 OR "column2"=?6);)",
|
||||
query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_select_columns_query) {
|
||||
auto query = utils::db::sqlite::db_select{*db3.get(), "table"}
|
||||
.column("column1")
|
||||
.column("column2")
|
||||
.where("column1")
|
||||
.equals("test1")
|
||||
.and_()
|
||||
.where("column2")
|
||||
.equals("test2");
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(
|
||||
R"(SELECT column1, column2 FROM "table" WHERE "column1"=?1 AND "column2"=?2;)",
|
||||
query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, db_update_query) {
|
||||
auto query = utils::db::sqlite::db_update{*db3.get(), "table"}
|
||||
.column_value("column1", "moose")
|
||||
.where("column1")
|
||||
.equals("test1")
|
||||
.and_()
|
||||
.where("column2")
|
||||
.equals("test2");
|
||||
auto query_str = query.dump();
|
||||
std::cout << query_str << std::endl;
|
||||
EXPECT_STREQ(
|
||||
R"(UPDATE "table" SET "column1"=?1 WHERE "column1"=?2 AND "column2"=?3;)",
|
||||
query_str.c_str());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, insert_select_delete) {
|
||||
common_insert(*db3.get(), true);
|
||||
|
||||
common_select(*db3.get(), "test0", "test1", true);
|
||||
|
||||
common_delete(*db3.get(), true);
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, insert_update_delete) {
|
||||
common_insert(*db3.get());
|
||||
|
||||
{
|
||||
auto query = utils::db::sqlite::db_update{*db3.get(), "table"}
|
||||
.column_value("column1", "moose")
|
||||
.where("column1")
|
||||
.equals("test0");
|
||||
std::cout << query.dump() << std::endl;
|
||||
auto res = query.go();
|
||||
EXPECT_TRUE(res.ok());
|
||||
}
|
||||
|
||||
common_select(*db3.get(), "moose", "test1");
|
||||
|
||||
common_delete(*db3.get());
|
||||
}
|
||||
|
||||
TEST_F(utils_db_sqlite, insert_or_replace_and_delete) {
|
||||
common_insert(*db3.get());
|
||||
|
||||
{
|
||||
auto query = utils::db::sqlite::db_insert{*db3.get(), "table"}
|
||||
.or_replace()
|
||||
.column_value("column1", "test0")
|
||||
.column_value("column2", "moose");
|
||||
std::cout << query.dump() << std::endl;
|
||||
auto res = query.go();
|
||||
EXPECT_TRUE(res.ok());
|
||||
}
|
||||
|
||||
common_select(*db3.get(), "test0", "moose");
|
||||
|
||||
common_delete(*db3.get());
|
||||
}
|
||||
} // namespace monitarr
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
225
support/test/src/utils/encrypting_reader_test.cpp
Normal file
225
support/test/src/utils/encrypting_reader_test.cpp
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "test.hpp"
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
namespace {
|
||||
const auto get_stop_requested = []() -> bool { return false; };
|
||||
} // namespace
|
||||
|
||||
namespace monitarr {
|
||||
TEST(utils_encrypting_reader, read_file_data) {
|
||||
const auto token = std::string("moose");
|
||||
auto &source_file = test::create_random_file(
|
||||
8U * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, token);
|
||||
|
||||
for (std::uint8_t i = 0U; i < 8U; i++) {
|
||||
data_buffer buffer(
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size());
|
||||
for (std::uint8_t j = 0U; j < 2U; j++) {
|
||||
ASSERT_EQ(
|
||||
buffer.size() / 2U,
|
||||
utils::encryption::encrypting_reader::reader_function(
|
||||
reinterpret_cast<char *>(&buffer[(buffer.size() / 2U) * j]),
|
||||
buffer.size() / 2U, 1U, &reader));
|
||||
}
|
||||
|
||||
data_buffer decrypted_data;
|
||||
EXPECT_TRUE(
|
||||
utils::encryption::decrypt_data(token, buffer, decrypted_data));
|
||||
|
||||
EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
decrypted_data.size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
data_buffer file_data(decrypted_data.size());
|
||||
EXPECT_TRUE(source_file.read(
|
||||
file_data,
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size() * i,
|
||||
&bytes_read));
|
||||
EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(),
|
||||
file_data.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encrypting_reader, read_file_data_in_multiple_chunks) {
|
||||
const auto token = std::string("moose");
|
||||
auto &source_file = test::create_random_file(
|
||||
8U * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, token);
|
||||
|
||||
for (std::uint8_t i = 0U; i < 8U; i += 2U) {
|
||||
data_buffer buffer(
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size() *
|
||||
2U);
|
||||
EXPECT_EQ(buffer.size(),
|
||||
utils::encryption::encrypting_reader::reader_function(
|
||||
reinterpret_cast<char *>(buffer.data()), buffer.size(), 1U,
|
||||
&reader));
|
||||
|
||||
for (std::uint8_t j = 0U; j < 2U; j++) {
|
||||
data_buffer decrypted_data;
|
||||
const auto offset = (j * (buffer.size() / 2U));
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(
|
||||
token,
|
||||
data_buffer(
|
||||
std::next(buffer.begin(), static_cast<std::int64_t>(offset)),
|
||||
std::next(buffer.begin(), static_cast<std::int64_t>(
|
||||
offset + (buffer.size() / 2U)))),
|
||||
decrypted_data));
|
||||
|
||||
EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
decrypted_data.size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
data_buffer file_data(decrypted_data.size());
|
||||
EXPECT_TRUE(source_file.read(
|
||||
file_data,
|
||||
(utils::encryption::encrypting_reader::get_data_chunk_size() * i) +
|
||||
(j *
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size()),
|
||||
&bytes_read));
|
||||
EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(),
|
||||
file_data.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encrypting_reader, read_file_data_as_stream) {
|
||||
const auto token = std::string("moose");
|
||||
auto &source_file = test::create_random_file(
|
||||
8U * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, token);
|
||||
auto io_stream = reader.create_iostream();
|
||||
EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
EXPECT_EQ(reader.get_total_size(),
|
||||
static_cast<std::uint64_t>(io_stream->tellg()));
|
||||
EXPECT_FALSE(io_stream->seekg(0, std::ios_base::beg).fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
|
||||
for (std::uint8_t i = 0U; i < 8U; i++) {
|
||||
data_buffer buffer(
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size());
|
||||
EXPECT_FALSE(
|
||||
io_stream->seekg(static_cast<std::streamoff>(i * buffer.size()))
|
||||
.fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
for (std::uint8_t j = 0U; j < 2U; j++) {
|
||||
EXPECT_FALSE(
|
||||
io_stream
|
||||
->read(
|
||||
reinterpret_cast<char *>(&buffer[(buffer.size() / 2U) * j]),
|
||||
static_cast<std::streamsize>(buffer.size()) / 2U)
|
||||
.fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
}
|
||||
|
||||
data_buffer decrypted_data;
|
||||
EXPECT_TRUE(
|
||||
utils::encryption::decrypt_data(token, buffer, decrypted_data));
|
||||
|
||||
EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
decrypted_data.size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
data_buffer file_data(decrypted_data.size());
|
||||
EXPECT_TRUE(source_file.read(
|
||||
file_data,
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size() * i,
|
||||
&bytes_read));
|
||||
EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(),
|
||||
file_data.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encrypting_reader, read_file_data_in_multiple_chunks_as_stream) {
|
||||
const auto token = std::string("moose");
|
||||
auto &source_file = test::create_random_file(
|
||||
8u * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, token);
|
||||
auto io_stream = reader.create_iostream();
|
||||
EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
EXPECT_EQ(reader.get_total_size(),
|
||||
static_cast<std::uint64_t>(io_stream->tellg()));
|
||||
EXPECT_FALSE(io_stream->seekg(0, std::ios_base::beg).fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
|
||||
for (std::uint8_t i = 0U; i < 8U; i += 2U) {
|
||||
data_buffer buffer(
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size() *
|
||||
2U);
|
||||
EXPECT_FALSE(io_stream
|
||||
->read(reinterpret_cast<char *>(buffer.data()),
|
||||
static_cast<std::streamsize>(buffer.size()))
|
||||
.fail());
|
||||
EXPECT_TRUE(io_stream->good());
|
||||
|
||||
for (std::uint8_t j = 0U; j < 2U; j++) {
|
||||
data_buffer decrypted_data;
|
||||
const auto offset = (j * (buffer.size() / 2U));
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(
|
||||
token,
|
||||
data_buffer(
|
||||
std::next(buffer.begin(), static_cast<std::int64_t>(offset)),
|
||||
std::next(buffer.begin(), static_cast<std::int64_t>(
|
||||
offset + (buffer.size() / 2U)))),
|
||||
decrypted_data));
|
||||
|
||||
EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(),
|
||||
decrypted_data.size());
|
||||
|
||||
std::size_t bytes_read{};
|
||||
data_buffer file_data(decrypted_data.size());
|
||||
EXPECT_TRUE(source_file.read(
|
||||
file_data,
|
||||
(utils::encryption::encrypting_reader::get_data_chunk_size() * i) +
|
||||
(j *
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size()),
|
||||
&bytes_read));
|
||||
EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(),
|
||||
file_data.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace monitarr
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
286
support/test/src/utils/encryption_test.cpp
Normal file
286
support/test/src/utils/encryption_test.cpp
Normal file
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "test.hpp"
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
namespace {
|
||||
const auto get_stop_requested = []() -> bool { return false; };
|
||||
} // namespace
|
||||
|
||||
namespace monitarr {
|
||||
static constexpr const std::string_view token{"moose"};
|
||||
static constexpr const std::wstring_view token_w{L"moose"};
|
||||
|
||||
TEST(utils_encryption, generate_key) {
|
||||
auto key1 =
|
||||
utils::encryption::generate_key<utils::encryption::hash_256_t>(token);
|
||||
EXPECT_STREQ(
|
||||
"ab4a0b004e824962913f7c0f79582b6ec7a3b8726426ca61d1a0a28ce5049e96",
|
||||
utils::collection::to_hex_string(key1).c_str());
|
||||
|
||||
auto key2 =
|
||||
utils::encryption::generate_key<utils::encryption::hash_256_t>("moose");
|
||||
auto key3 =
|
||||
utils::encryption::generate_key<utils::encryption::hash_256_t>("moose");
|
||||
EXPECT_EQ(key2, key3);
|
||||
|
||||
auto key4 =
|
||||
utils::encryption::generate_key<utils::encryption::hash_256_t>("moose2");
|
||||
EXPECT_NE(key2, key4);
|
||||
|
||||
auto key1_w =
|
||||
utils::encryption::generate_key<utils::encryption::hash_256_t>(token_w);
|
||||
EXPECT_NE(key1, key1_w);
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
L"4f5eb2a2ab34e3777b230465283923080b9ba59311e74058ccd74185131d11fe",
|
||||
utils::collection::to_hex_wstring(key1_w).c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
L"0392d95ed3eee9772fbb9af68fedf829a8eb0adbe8575d9691cc9a752196766a",
|
||||
utils::collection::to_hex_wstring(key1_w).c_str());
|
||||
#endif
|
||||
|
||||
auto key2_w =
|
||||
utils::encryption::generate_key<utils::encryption::hash_256_t>(L"moose");
|
||||
auto key3_w =
|
||||
utils::encryption::generate_key<utils::encryption::hash_256_t>(L"moose");
|
||||
EXPECT_EQ(key2_w, key3_w);
|
||||
EXPECT_NE(key2_w, key2);
|
||||
EXPECT_NE(key3_w, key3);
|
||||
|
||||
auto key4_w =
|
||||
utils::encryption::generate_key<utils::encryption::hash_256_t>(L"moose2");
|
||||
EXPECT_NE(key2_w, key4_w);
|
||||
EXPECT_NE(key4_w, key4);
|
||||
}
|
||||
|
||||
TEST(utils_encryption, generate_key_default_hasher_is_blake2b_256) {
|
||||
auto key1 =
|
||||
utils::encryption::generate_key<utils::encryption::hash_256_t>(token);
|
||||
auto key2 = utils::encryption::generate_key<utils::encryption::hash_256_t>(
|
||||
token, [](auto &&data, auto &&size) -> auto {
|
||||
return utils::encryption::create_hash_blake2b_256(
|
||||
std::string_view(reinterpret_cast<const char *>(data), size));
|
||||
});
|
||||
EXPECT_EQ(key1, key2);
|
||||
|
||||
auto key1_w =
|
||||
utils::encryption::generate_key<utils::encryption::hash_256_t>(token_w);
|
||||
auto key2_w = utils::encryption::generate_key<utils::encryption::hash_256_t>(
|
||||
token_w, [](auto &&data, auto &&size) -> auto {
|
||||
return utils::encryption::create_hash_blake2b_256(std::wstring_view(
|
||||
reinterpret_cast<const wchar_t *>(data), size / sizeof(wchar_t)));
|
||||
});
|
||||
EXPECT_EQ(key1_w, key2_w);
|
||||
|
||||
EXPECT_NE(key1_w, key1);
|
||||
EXPECT_NE(key2_w, key2);
|
||||
}
|
||||
|
||||
TEST(utils_encryption, generate_key_with_hasher) {
|
||||
auto key1 = utils::encryption::generate_key<utils::encryption::hash_256_t>(
|
||||
token, utils::encryption::blake2b_256_hasher);
|
||||
EXPECT_STREQ(
|
||||
"ab4a0b004e824962913f7c0f79582b6ec7a3b8726426ca61d1a0a28ce5049e96",
|
||||
utils::collection::to_hex_string(key1).c_str());
|
||||
|
||||
auto key2 = utils::encryption::generate_key<utils::encryption::hash_256_t>(
|
||||
token, utils::encryption::sha256_hasher);
|
||||
EXPECT_NE(key1, key2);
|
||||
|
||||
EXPECT_STREQ(
|
||||
"182072537ada59e4d6b18034a80302ebae935f66adbdf0f271d3d36309c2d481",
|
||||
utils::collection::to_hex_string(key2).c_str());
|
||||
|
||||
auto key1_w = utils::encryption::generate_key<utils::encryption::hash_256_t>(
|
||||
token_w, utils::encryption::blake2b_256_hasher);
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
L"4f5eb2a2ab34e3777b230465283923080b9ba59311e74058ccd74185131d11fe",
|
||||
utils::collection::to_hex_wstring(key1_w).c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
L"0392d95ed3eee9772fbb9af68fedf829a8eb0adbe8575d9691cc9a752196766a",
|
||||
utils::collection::to_hex_wstring(key1_w).c_str());
|
||||
#endif
|
||||
|
||||
auto key2_w = utils::encryption::generate_key<utils::encryption::hash_256_t>(
|
||||
token_w, utils::encryption::sha256_hasher);
|
||||
EXPECT_NE(key1_w, key2_w);
|
||||
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
L"918e4c6d39bb373f139b5fac8ec0548a9770da399b2835608974ffeac7fab6c4",
|
||||
utils::collection::to_hex_wstring(key2_w).c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
L"590ac70125bec4501172937f6a2cbdeb22a87b5e40d5595eccd06b2b20548d8f",
|
||||
utils::collection::to_hex_wstring(key2_w).c_str());
|
||||
#endif
|
||||
|
||||
EXPECT_NE(key1_w, key1);
|
||||
EXPECT_NE(key2_w, key2);
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_BOOST)
|
||||
static const std::string buffer = "cow moose dog chicken";
|
||||
|
||||
static void test_encrypted_result(const data_buffer &result) {
|
||||
EXPECT_EQ(buffer.size() + utils::encryption::encryption_header_size,
|
||||
result.size());
|
||||
std::string data;
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(token, result, data));
|
||||
EXPECT_EQ(buffer.size(), data.size());
|
||||
EXPECT_STREQ(buffer.c_str(), data.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_encryption, encrypt_data_buffer) {
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(token, buffer, result);
|
||||
test_encrypted_result(result);
|
||||
}
|
||||
|
||||
TEST(utils_encryption, encrypt_data_buffer_with_key) {
|
||||
const auto key =
|
||||
utils::encryption::generate_key<utils::encryption::hash_256_t>(token);
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(key, buffer, result);
|
||||
test_encrypted_result(result);
|
||||
}
|
||||
|
||||
TEST(utils_encryption, encrypt_data_pointer) {
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(
|
||||
token, reinterpret_cast<const unsigned char *>(buffer.data()),
|
||||
buffer.size(), result);
|
||||
test_encrypted_result(result);
|
||||
}
|
||||
|
||||
TEST(utils_encryption, encrypt_data_pointer_with_key) {
|
||||
const auto key =
|
||||
utils::encryption::generate_key<utils::encryption::hash_256_t>(token);
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(
|
||||
key, reinterpret_cast<const unsigned char *>(buffer.data()),
|
||||
buffer.size(), result);
|
||||
test_encrypted_result(result);
|
||||
}
|
||||
|
||||
TEST(utils_encryption, decrypt_data_pointer) {
|
||||
const auto key =
|
||||
utils::encryption::generate_key<utils::encryption::hash_256_t>(token);
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(
|
||||
key, reinterpret_cast<const unsigned char *>(buffer.data()),
|
||||
buffer.size(), result);
|
||||
|
||||
std::string data;
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(token, result.data(),
|
||||
result.size(), data));
|
||||
|
||||
EXPECT_EQ(buffer.size(), data.size());
|
||||
EXPECT_STREQ(buffer.c_str(), data.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_encryption, decrypt_data_buffer_with_key) {
|
||||
const auto key =
|
||||
utils::encryption::generate_key<utils::encryption::hash_256_t>(token);
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(
|
||||
key, reinterpret_cast<const unsigned char *>(buffer.data()),
|
||||
buffer.size(), result);
|
||||
|
||||
std::string data;
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data(key, result, data));
|
||||
|
||||
EXPECT_EQ(buffer.size(), data.size());
|
||||
EXPECT_STREQ(buffer.c_str(), data.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_encryption, decrypt_data_pointer_with_key) {
|
||||
const auto key =
|
||||
utils::encryption::generate_key<utils::encryption::hash_256_t>(token);
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(
|
||||
key, reinterpret_cast<const unsigned char *>(buffer.data()),
|
||||
buffer.size(), result);
|
||||
|
||||
std::string data;
|
||||
EXPECT_TRUE(
|
||||
utils::encryption::decrypt_data(key, result.data(), result.size(), data));
|
||||
|
||||
EXPECT_EQ(buffer.size(), data.size());
|
||||
EXPECT_STREQ(buffer.c_str(), data.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_encryption, decryption_failure) {
|
||||
const auto key =
|
||||
utils::encryption::generate_key<utils::encryption::hash_256_t>(token);
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(
|
||||
key, reinterpret_cast<const unsigned char *>(buffer.data()),
|
||||
buffer.size(), result);
|
||||
result[0U] = 0U;
|
||||
result[1U] = 1U;
|
||||
result[2U] = 2U;
|
||||
|
||||
std::string data;
|
||||
EXPECT_FALSE(utils::encryption::decrypt_data(key, result, data));
|
||||
}
|
||||
|
||||
TEST(utils_encryption, decrypt_file_name) {
|
||||
auto &source_file = test::create_random_file(
|
||||
8U * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, token,
|
||||
std::nullopt);
|
||||
|
||||
auto file_name = reader.get_encrypted_file_name();
|
||||
|
||||
EXPECT_EQ(true, utils::encryption::decrypt_file_name(token, file_name));
|
||||
EXPECT_STREQ("test.dat", file_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_encryption, decrypt_file_path) {
|
||||
auto &source_file = test::create_random_file(
|
||||
8U * utils::encryption::encrypting_reader::get_data_chunk_size());
|
||||
EXPECT_TRUE(source_file);
|
||||
if (source_file) {
|
||||
utils::encryption::encrypting_reader reader(
|
||||
"test.dat", source_file.get_path(), get_stop_requested, token,
|
||||
"moose/cow");
|
||||
|
||||
auto file_path = reader.get_encrypted_file_path();
|
||||
|
||||
EXPECT_EQ(true, utils::encryption::decrypt_file_path(token, file_path));
|
||||
EXPECT_STREQ("/moose/cow/test.dat", file_path.c_str());
|
||||
}
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
} // namespace monitarr
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
73
support/test/src/utils/error_test.cpp
Normal file
73
support/test/src/utils/error_test.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "test.hpp"
|
||||
|
||||
namespace monitarr {
|
||||
template <typename T, typename U>
|
||||
constexpr bool is_decay_equ = std::is_same_v<std::decay_t<T>, U>;
|
||||
|
||||
TEST(utils_error, check_default_exception_handler) {
|
||||
EXPECT_TRUE(utils::error::get_exception_handler() != nullptr);
|
||||
if (&utils::error::default_exception_handler ==
|
||||
utils::error::get_exception_handler()) {
|
||||
auto default_handler_is_iostream =
|
||||
is_decay_equ<decltype(utils::error::default_exception_handler),
|
||||
utils::error::iostream_exception_handler>;
|
||||
EXPECT_TRUE(default_handler_is_iostream);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_error, can_override_exception_handler) {
|
||||
struct my_exc_handler final : public utils::error::i_exception_handler {
|
||||
MOCK_METHOD(void, handle_error,
|
||||
(std::string_view function_name, std::string_view msg),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(void, handle_exception, (std::string_view function_name),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(void, handle_exception,
|
||||
(std::string_view function_name, const std::exception &ex),
|
||||
(const, override));
|
||||
};
|
||||
|
||||
my_exc_handler handler{};
|
||||
utils::error::set_exception_handler(&handler);
|
||||
|
||||
EXPECT_CALL(handler, handle_error("test_func", "error")).WillOnce(Return());
|
||||
utils::error::handle_error("test_func", "error");
|
||||
|
||||
EXPECT_CALL(handler, handle_exception("test_func")).WillOnce(Return());
|
||||
utils::error::handle_exception("test_func");
|
||||
|
||||
auto ex = std::runtime_error("moose");
|
||||
EXPECT_CALL(handler, handle_exception(_, _))
|
||||
.WillOnce(
|
||||
[&ex](std::string_view function_name, const std::exception &ex2) {
|
||||
EXPECT_EQ("test_func_ex", function_name);
|
||||
EXPECT_STREQ(ex.what(), ex2.what());
|
||||
});
|
||||
utils::error::handle_exception("test_func_ex", ex);
|
||||
|
||||
utils::error::set_exception_handler(&utils::error::default_exception_handler);
|
||||
}
|
||||
} // namespace monitarr
|
||||
556
support/test/src/utils/file_test.cpp
Normal file
556
support/test/src/utils/file_test.cpp
Normal file
@@ -0,0 +1,556 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "test.hpp"
|
||||
|
||||
namespace {
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
#include "utils/file_enc_file.hpp"
|
||||
constexpr const auto file_type_count{3U};
|
||||
#else
|
||||
constexpr const auto file_type_count{2U};
|
||||
#endif
|
||||
|
||||
[[nodiscard]] auto create_file(auto idx, auto path,
|
||||
bool read_only = false) -> auto {
|
||||
switch (idx) {
|
||||
case 0U:
|
||||
return monitarr::utils::file::file::open_or_create_file(path, read_only);
|
||||
case 1U:
|
||||
return monitarr::utils::file::thread_file::open_or_create_file(path,
|
||||
read_only);
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
case 2U:
|
||||
return monitarr::utils::file::enc_file::attach_file(
|
||||
monitarr::utils::file::file::open_or_create_file(path, read_only));
|
||||
#endif
|
||||
default:
|
||||
throw std::runtime_error("not supported");
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto open_file(auto idx, auto path,
|
||||
bool read_only = false) -> auto {
|
||||
switch (idx) {
|
||||
case 0U:
|
||||
return monitarr::utils::file::file::open_file(path, read_only);
|
||||
case 1U:
|
||||
return monitarr::utils::file::thread_file::open_file(path, read_only);
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
case 2U:
|
||||
return monitarr::utils::file::enc_file::attach_file(
|
||||
monitarr::utils::file::file::open_file(path, read_only));
|
||||
#endif
|
||||
default:
|
||||
throw std::runtime_error("not supported");
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace monitarr {
|
||||
TEST(utils_file, can_create_and_remove_file) {
|
||||
for (auto idx = 0U; idx < file_type_count; ++idx) {
|
||||
auto path = test::generate_test_file_name("utils_file");
|
||||
EXPECT_FALSE(utils::file::file(path).exists() ||
|
||||
utils::file::directory(path).exists());
|
||||
|
||||
auto file{create_file(idx, path)};
|
||||
EXPECT_TRUE(*file);
|
||||
|
||||
EXPECT_TRUE(utils::file::file(path).exists());
|
||||
EXPECT_TRUE(file->exists());
|
||||
|
||||
EXPECT_TRUE(file->remove());
|
||||
|
||||
EXPECT_FALSE(utils::file::file(path).exists());
|
||||
EXPECT_FALSE(file->exists());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_file, can_open_file) {
|
||||
for (auto idx = 0U; idx < file_type_count; ++idx) {
|
||||
auto path = test::generate_test_file_name("utils_file");
|
||||
|
||||
{
|
||||
auto file{create_file(idx, path)};
|
||||
EXPECT_TRUE(*file);
|
||||
}
|
||||
|
||||
{
|
||||
auto file{create_file(idx, path)};
|
||||
EXPECT_TRUE(*file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_file, open_file_fails_if_not_found) {
|
||||
for (auto idx = 0U; idx < file_type_count; ++idx) {
|
||||
auto path = test::generate_test_file_name("utils_file");
|
||||
|
||||
auto file{open_file(idx, path)};
|
||||
EXPECT_FALSE(*file);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_file, write_fails_for_read_only_file) {
|
||||
for (auto idx = 0U; idx < file_type_count; ++idx) {
|
||||
auto path = test::generate_test_file_name("utils_file");
|
||||
|
||||
auto file{create_file(idx, path, true)};
|
||||
EXPECT_TRUE(utils::file::file(path).exists());
|
||||
EXPECT_TRUE(*file);
|
||||
std::size_t bytes_written{};
|
||||
EXPECT_FALSE(file->write(reinterpret_cast<const unsigned char *>("0"), 1U,
|
||||
0U, &bytes_written));
|
||||
EXPECT_EQ(0U, bytes_written);
|
||||
}
|
||||
}
|
||||
|
||||
// TEST(utils_file, can_attach_file) {
|
||||
// for (auto idx = 0U; idx < file_type_count; ++idx) {
|
||||
// auto path = test::generate_test_file_name("utils_file");
|
||||
// auto file = idx == 0U ? utils::file::file::open_or_create_file(path)
|
||||
// :
|
||||
// utils::file::thread_file::open_or_create_file(path);
|
||||
// auto file2 =
|
||||
// idx == 0U ? utils::file::file::attach_file(file->get_handle())
|
||||
// :
|
||||
// utils::file::thread_file::attach_file(file->get_handle());
|
||||
// EXPECT_TRUE(*file);
|
||||
// EXPECT_TRUE(*file2);
|
||||
// EXPECT_EQ(file->get_path(), file2->get_path());
|
||||
// }
|
||||
// }
|
||||
|
||||
#if defined(PROJECT_ENABLE_JSON)
|
||||
TEST(utils_file, read_and_write_json_file) {
|
||||
auto path = test::generate_test_file_name("utils_file");
|
||||
|
||||
auto json_data = nlohmann::json({{"moose", "cow"}});
|
||||
EXPECT_TRUE(utils::file::write_json_file(path, json_data));
|
||||
|
||||
{
|
||||
nlohmann::json result_data{};
|
||||
EXPECT_TRUE(utils::file::read_json_file(path, result_data));
|
||||
EXPECT_STREQ(json_data.dump().c_str(), result_data.dump().c_str());
|
||||
}
|
||||
|
||||
{
|
||||
nlohmann::json result_data{};
|
||||
EXPECT_TRUE(utils::file::read_json_file(utils::string::from_utf8(path),
|
||||
result_data));
|
||||
EXPECT_STREQ(json_data.dump().c_str(), result_data.dump().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
TEST(utils_file, read_and_write_json_file_encrypted) {
|
||||
{
|
||||
auto path = test::generate_test_file_name("utils_file");
|
||||
|
||||
auto json_data = nlohmann::json({{"moose", "cow"}});
|
||||
EXPECT_TRUE(utils::file::write_json_file(path, json_data, "moose"));
|
||||
|
||||
nlohmann::json result_data{};
|
||||
EXPECT_TRUE(utils::file::read_json_file(path, result_data, "moose"));
|
||||
EXPECT_STREQ(json_data.dump().c_str(), result_data.dump().c_str());
|
||||
|
||||
{
|
||||
auto file = utils::file::file::open_file(path);
|
||||
data_buffer encrypted_data{};
|
||||
EXPECT_TRUE(file->read_all(encrypted_data, 0U));
|
||||
|
||||
data_buffer decrypted_data{};
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data("moose", encrypted_data,
|
||||
decrypted_data));
|
||||
EXPECT_STREQ(json_data.dump().c_str(),
|
||||
nlohmann::json::parse(std::string(decrypted_data.begin(),
|
||||
decrypted_data.end()))
|
||||
.dump()
|
||||
.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto path =
|
||||
utils::string::from_utf8(test::generate_test_file_name("utils_file"));
|
||||
|
||||
auto json_data = nlohmann::json({{"moose", "cow"}});
|
||||
EXPECT_TRUE(utils::file::write_json_file(path, json_data, L"moose"));
|
||||
|
||||
nlohmann::json result_data{};
|
||||
EXPECT_TRUE(utils::file::read_json_file(path, result_data, L"moose"));
|
||||
EXPECT_STREQ(json_data.dump().c_str(), result_data.dump().c_str());
|
||||
|
||||
{
|
||||
auto file = utils::file::file::open_file(path);
|
||||
data_buffer encrypted_data{};
|
||||
EXPECT_TRUE(file->read_all(encrypted_data, 0U));
|
||||
|
||||
data_buffer decrypted_data{};
|
||||
EXPECT_TRUE(utils::encryption::decrypt_data("moose", encrypted_data,
|
||||
decrypted_data));
|
||||
EXPECT_STREQ(json_data.dump().c_str(),
|
||||
nlohmann::json::parse(std::string(decrypted_data.begin(),
|
||||
decrypted_data.end()))
|
||||
.dump()
|
||||
.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
#endif // defined(PROJECT_ENABLE_JSON)
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBDSM)
|
||||
TEST(utils_file, smb_create_smb_path) {
|
||||
const auto *path = "//server/share";
|
||||
const auto *rel_path = "test/test.txt";
|
||||
auto smb_path = utils::file::smb_create_smb_path(path, rel_path);
|
||||
EXPECT_STREQ("//server/share/test/test.txt", smb_path.c_str());
|
||||
|
||||
rel_path = "/test/test.txt";
|
||||
smb_path = utils::file::smb_create_smb_path(path, rel_path);
|
||||
EXPECT_STREQ("//server/share/test/test.txt", smb_path.c_str());
|
||||
|
||||
rel_path = "test\\test.txt";
|
||||
smb_path = utils::file::smb_create_smb_path(path, rel_path);
|
||||
EXPECT_STREQ("//server/share/test/test.txt", smb_path.c_str());
|
||||
|
||||
rel_path = "\\test\\test.txt";
|
||||
smb_path = utils::file::smb_create_smb_path(path, rel_path);
|
||||
EXPECT_STREQ("//server/share/test/test.txt", smb_path.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_file, smb_create_relative_path) {
|
||||
const auto *path = "//server/share/test.txt";
|
||||
auto rel_path = utils::file::smb_create_relative_path(path);
|
||||
EXPECT_STREQ("\\test.txt", rel_path.c_str());
|
||||
|
||||
path = "//server/share/test";
|
||||
rel_path = utils::file::smb_create_relative_path(path);
|
||||
EXPECT_STREQ("\\test", rel_path.c_str());
|
||||
|
||||
path = "//server/share/test/";
|
||||
rel_path = utils::file::smb_create_relative_path(path);
|
||||
EXPECT_STREQ("\\test", rel_path.c_str());
|
||||
|
||||
path = "//server/share/test/";
|
||||
rel_path = utils::file::smb_create_relative_path(path);
|
||||
EXPECT_STREQ("\\test", rel_path.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_file, smb_create_search_path) {
|
||||
const auto *path = "//server/share";
|
||||
auto search_path = utils::file::smb_create_search_path(path);
|
||||
EXPECT_STREQ("\\*", search_path.c_str());
|
||||
|
||||
path = "//server/share/";
|
||||
search_path = utils::file::smb_create_search_path(path);
|
||||
EXPECT_STREQ("\\*", search_path.c_str());
|
||||
|
||||
path = "//server/share/folder";
|
||||
search_path = utils::file::smb_create_search_path(path);
|
||||
EXPECT_STREQ("\\folder\\*", search_path.c_str());
|
||||
|
||||
path = "//server/share/folder/";
|
||||
search_path = utils::file::smb_create_search_path(path);
|
||||
EXPECT_STREQ("\\folder\\*", search_path.c_str());
|
||||
|
||||
path = "//server/share/folder/next";
|
||||
search_path = utils::file::smb_create_search_path(path);
|
||||
EXPECT_STREQ("\\folder\\next\\*", search_path.c_str());
|
||||
|
||||
path = "//server/share/folder/next/";
|
||||
search_path = utils::file::smb_create_search_path(path);
|
||||
EXPECT_STREQ("\\folder\\next\\*", search_path.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_file, smb_parent_is_same) {
|
||||
const auto *path1 = "//server/share";
|
||||
const auto *path2 = "//server/share";
|
||||
EXPECT_TRUE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server/share/";
|
||||
path2 = "//server/share/";
|
||||
EXPECT_TRUE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server/share/one";
|
||||
path2 = "//server/share/two";
|
||||
EXPECT_TRUE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "// server/cow";
|
||||
path2 = "// server/cow";
|
||||
EXPECT_TRUE(utils::file::smb_parent_is_same(path1, path2));
|
||||
}
|
||||
|
||||
TEST(utils_file, smb_parent_is_not_same) {
|
||||
const auto *path1 = "server/share";
|
||||
const auto *path2 = "//server/share";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "server/share/";
|
||||
path2 = "server/share/";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server1/share/one";
|
||||
path2 = "//server/share/two";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server/share";
|
||||
path2 = "//server/share2";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server/share/";
|
||||
path2 = "//server/share2/";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server/share/one";
|
||||
path2 = "//server/share2/two";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server";
|
||||
path2 = "//server/share/two";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server/";
|
||||
path2 = "//server/";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
|
||||
path1 = "//server";
|
||||
path2 = "//server";
|
||||
EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2));
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBDSM)
|
||||
|
||||
TEST(utils_file, directory_exists_in_path) {
|
||||
auto &test_dir = test::generate_test_directory();
|
||||
EXPECT_FALSE(
|
||||
utils::file::directory_exists_in_path(test_dir.get_path(), "moose"));
|
||||
|
||||
EXPECT_FALSE(utils::file::directory_exists_in_path(
|
||||
utils::string::from_utf8(test_dir.get_path()), L"moose"));
|
||||
|
||||
EXPECT_FALSE(utils::file::file_exists_in_path(test_dir.get_path(), "moose"));
|
||||
|
||||
EXPECT_FALSE(utils::file::file_exists_in_path(
|
||||
utils::string::from_utf8(test_dir.get_path()), L"moose"));
|
||||
|
||||
auto sub_dir = test_dir.create_directory("moose");
|
||||
EXPECT_TRUE(sub_dir != nullptr);
|
||||
if (sub_dir) {
|
||||
EXPECT_TRUE(
|
||||
utils::file::directory_exists_in_path(test_dir.get_path(), "moose"));
|
||||
|
||||
EXPECT_FALSE(
|
||||
utils::file::file_exists_in_path(test_dir.get_path(), "moose"));
|
||||
|
||||
EXPECT_TRUE(utils::file::directory_exists_in_path(
|
||||
utils::string::from_utf8(test_dir.get_path()), L"moose"));
|
||||
|
||||
EXPECT_FALSE(utils::file::file_exists_in_path(
|
||||
utils::string::from_utf8(test_dir.get_path()), L"moose"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_file, file_exists_in_path) {
|
||||
auto &test_dir = test::generate_test_directory();
|
||||
EXPECT_FALSE(
|
||||
utils::file::file_exists_in_path(test_dir.get_path(), "moose.txt"));
|
||||
|
||||
EXPECT_FALSE(utils::file::file_exists_in_path(
|
||||
utils::string::from_utf8(test_dir.get_path()), L"moose.txt"));
|
||||
|
||||
EXPECT_FALSE(
|
||||
utils::file::directory_exists_in_path(test_dir.get_path(), "moose.txt"));
|
||||
|
||||
EXPECT_FALSE(utils::file::directory_exists_in_path(
|
||||
utils::string::from_utf8(test_dir.get_path()), L"moose.txt"));
|
||||
|
||||
auto sub_file = test_dir.create_file("moose.txt", false);
|
||||
EXPECT_TRUE(sub_file != nullptr);
|
||||
if (sub_file) {
|
||||
EXPECT_TRUE(
|
||||
utils::file::file_exists_in_path(test_dir.get_path(), "moose.txt"));
|
||||
|
||||
EXPECT_FALSE(utils::file::directory_exists_in_path(test_dir.get_path(),
|
||||
"moose.txt"));
|
||||
|
||||
EXPECT_TRUE(utils::file::file_exists_in_path(
|
||||
utils::string::from_utf8(test_dir.get_path()), L"moose.txt"));
|
||||
|
||||
EXPECT_FALSE(utils::file::directory_exists_in_path(
|
||||
utils::string::from_utf8(test_dir.get_path()), L"moose.txt"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_file, get_free_drive_space) {
|
||||
#if defined(_WIN32)
|
||||
auto space = utils::file::get_free_drive_space("C:");
|
||||
auto space2 = utils::file::get_free_drive_space(L"C:");
|
||||
#else // defined(_WIN32)
|
||||
auto space = utils::file::get_free_drive_space("/");
|
||||
auto space2 = utils::file::get_free_drive_space(L"/");
|
||||
#endif // !defined(_WIN32)
|
||||
|
||||
EXPECT_TRUE(space.has_value());
|
||||
EXPECT_LT(0U, space.value());
|
||||
|
||||
EXPECT_TRUE(space2.has_value());
|
||||
EXPECT_EQ(space.value(), space2.value());
|
||||
}
|
||||
|
||||
TEST(utils_file, get_free_drive_space_fails_for_bad_path) {
|
||||
std::string name{"free_drive_space_test_XXXXXX"};
|
||||
auto temp = utils::file::create_temp_name("free_drive_space_test");
|
||||
|
||||
auto space = utils::file::get_free_drive_space(temp);
|
||||
EXPECT_FALSE(space.has_value());
|
||||
}
|
||||
|
||||
TEST(utils_file, get_total_drive_space) {
|
||||
#if defined(_WIN32)
|
||||
auto space = utils::file::get_total_drive_space("C:");
|
||||
auto space2 = utils::file::get_total_drive_space(L"C:");
|
||||
#else // defined(_WIN32)
|
||||
auto space = utils::file::get_total_drive_space("/");
|
||||
auto space2 = utils::file::get_total_drive_space(L"/");
|
||||
#endif // !defined(_WIN32)
|
||||
|
||||
EXPECT_TRUE(space.has_value());
|
||||
EXPECT_LT(0U, space.value());
|
||||
|
||||
EXPECT_TRUE(space2.has_value());
|
||||
EXPECT_EQ(space.value(), space2.value());
|
||||
}
|
||||
|
||||
TEST(utils_file, create_temp_name) {
|
||||
{
|
||||
auto temp = utils::file::create_temp_name("test_temp");
|
||||
EXPECT_EQ(18U, temp.size());
|
||||
|
||||
auto temp2 = utils::file::create_temp_name("test_temp");
|
||||
EXPECT_STRNE(temp.c_str(), temp2.c_str());
|
||||
|
||||
EXPECT_TRUE(utils::string::begins_with(temp, "test_temp_"));
|
||||
}
|
||||
|
||||
{
|
||||
auto temp = utils::file::create_temp_name(L"test_temp");
|
||||
EXPECT_EQ(18U, temp.size());
|
||||
|
||||
auto temp2 = utils::file::create_temp_name(L"test_temp");
|
||||
EXPECT_STRNE(temp.c_str(), temp2.c_str());
|
||||
|
||||
EXPECT_TRUE(utils::string::begins_with(temp, L"test_temp_"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_file, get_total_drive_space_fails_for_bad_path) {
|
||||
auto temp = utils::file::create_temp_name("total_drive_space_test");
|
||||
auto space = utils::file::get_total_drive_space(temp);
|
||||
EXPECT_FALSE(space.has_value());
|
||||
}
|
||||
|
||||
TEST(utils_file, get_times) {
|
||||
{
|
||||
auto times =
|
||||
utils::file::get_times(test::create_random_file(1U).get_path());
|
||||
EXPECT_TRUE(times.has_value());
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::accessed));
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::created));
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::modified));
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::written));
|
||||
}
|
||||
|
||||
{
|
||||
auto times = utils::file::get_times(
|
||||
utils::string::from_utf8(test::create_random_file(1U).get_path()));
|
||||
EXPECT_TRUE(times.has_value());
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::accessed));
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::created));
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::modified));
|
||||
EXPECT_LT(0U, times->get(utils::file::time_type::written));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_file, get_times_fails_if_not_found) {
|
||||
auto temp = utils::path::combine(".", {"get_times_test"});
|
||||
auto times = utils::file::get_times(temp);
|
||||
EXPECT_FALSE(times.has_value());
|
||||
}
|
||||
|
||||
TEST(utils_file, get_time) {
|
||||
{
|
||||
auto file_path = test::create_random_file(1U).get_path();
|
||||
auto file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::accessed);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
|
||||
file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::created);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
|
||||
file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::modified);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
|
||||
file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::written);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
}
|
||||
|
||||
{
|
||||
auto file_path =
|
||||
utils::string::from_utf8(test::create_random_file(1U).get_path());
|
||||
|
||||
auto file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::accessed);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
|
||||
file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::created);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
|
||||
file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::modified);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
|
||||
file_time =
|
||||
utils::file::get_time(file_path, utils::file::time_type::written);
|
||||
EXPECT_TRUE(file_time.has_value());
|
||||
EXPECT_LT(0U, file_time.value());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(utils_file, get_time_fails_if_not_found) {
|
||||
auto temp = utils::path::combine(".", {"get_times_test"});
|
||||
auto file_time =
|
||||
utils::file::get_time(temp, utils::file::time_type::accessed);
|
||||
EXPECT_FALSE(file_time.has_value());
|
||||
}
|
||||
} // namespace monitarr
|
||||
186
support/test/src/utils/hash_test.cpp
Normal file
186
support/test/src/utils/hash_test.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "test.hpp"
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
namespace monitarr {
|
||||
TEST(utils_hash, hash_type_sizes) {
|
||||
EXPECT_EQ(32U, utils::encryption::hash_256_t{}.size());
|
||||
EXPECT_EQ(48U, utils::encryption::hash_384_t{}.size());
|
||||
EXPECT_EQ(64U, utils::encryption::hash_512_t{}.size());
|
||||
}
|
||||
|
||||
TEST(utils_hash, default_hasher_is_blake2b) {
|
||||
EXPECT_EQ(
|
||||
&utils::encryption::blake2b_256_hasher,
|
||||
&utils::encryption::default_create_hash<utils::encryption::hash_256_t>());
|
||||
|
||||
EXPECT_EQ(
|
||||
&utils::encryption::blake2b_384_hasher,
|
||||
&utils::encryption::default_create_hash<utils::encryption::hash_384_t>());
|
||||
|
||||
EXPECT_EQ(
|
||||
&utils::encryption::blake2b_512_hasher,
|
||||
&utils::encryption::default_create_hash<utils::encryption::hash_512_t>());
|
||||
}
|
||||
|
||||
TEST(utils_hash, blake2b_256) {
|
||||
auto hash = utils::collection::to_hex_string(
|
||||
utils::encryption::create_hash_blake2b_256("a"));
|
||||
EXPECT_STREQ(
|
||||
"8928aae63c84d87ea098564d1e03ad813f107add474e56aedd286349c0c03ea4",
|
||||
hash.c_str());
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::encryption::create_hash_blake2b_256(L"a"));
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
"d2373b17cd8a8e19e39f52fa4905a274f93805fbb8bb4c7f3cb4b2cd6708ec8a",
|
||||
hash.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
"9fdf5757d7eea386f0d34d2c0e202527986febf1ebb4315fcf7fff40776fa41d",
|
||||
hash.c_str());
|
||||
#endif
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::encryption::create_hash_blake2b_256({1U}));
|
||||
EXPECT_STREQ(
|
||||
"ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25",
|
||||
hash.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_hash, blake2b_384) {
|
||||
auto hash = utils::collection::to_hex_string(
|
||||
utils::encryption::create_hash_blake2b_384("a"));
|
||||
EXPECT_STREQ("7d40de16ff771d4595bf70cbda0c4ea0a066a6046fa73d34471cd4d93d827d7"
|
||||
"c94c29399c50de86983af1ec61d5dcef0",
|
||||
hash.c_str());
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::encryption::create_hash_blake2b_384(L"a"));
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ("637fe31d1e955760ef31043d525d9321826a778ddbe82fcde45a98394241380"
|
||||
"96675e2f87e36b53ab223a7fd254198fd",
|
||||
hash.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ("9d469bd9dab9d4b48b8688de7c22704a8de1b081294f9be294100dfa9f05c92"
|
||||
"e8d3616476e46cd14f9e613fed80fd157",
|
||||
hash.c_str());
|
||||
#endif
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::encryption::create_hash_blake2b_384({1U}));
|
||||
EXPECT_STREQ("42cfe875d08d816538103b906bb0b05202e0b09c4e981680c1110684fc7845b"
|
||||
"c91c178fa167afcc445490644b2bf5f5b",
|
||||
hash.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_hash, blake2b_512) {
|
||||
auto hash = utils::collection::to_hex_string(
|
||||
utils::encryption::create_hash_blake2b_512("a"));
|
||||
EXPECT_STREQ(
|
||||
"333fcb4ee1aa7c115355ec66ceac917c8bfd815bf7587d325aec1864edd24e34d5abe2c6"
|
||||
"b1b5ee3face62fed78dbef802f2a85cb91d455a8f5249d330853cb3c",
|
||||
hash.c_str());
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::encryption::create_hash_blake2b_512(L"a"));
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
"05970b95468b0b1941066ff189091493e73859ce41cde5ad08118e93ea1d81a57a144296"
|
||||
"a26a9fe7781481bde97b886725e36e30b305d8bd5cce1ae36bf1564a",
|
||||
hash.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
"bbc187c6e4d8525655d0ada62d16eed59f3db3ab07e04fb0483fd4ae21d88b984774add9"
|
||||
"b3fbcff56f9638091013994f8e2d4646fdbbcb4879e2b5160bbb755d",
|
||||
hash.c_str());
|
||||
#endif
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::encryption::create_hash_blake2b_512({1U}));
|
||||
EXPECT_STREQ(
|
||||
"9545ba37b230d8a2e716c4707586542780815b7c4088edcb9af6a9452d50f32474d5ba9a"
|
||||
"ab52a67aca864ef2696981c2eadf49020416136afd838fb048d21653",
|
||||
hash.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_hash, sha256) {
|
||||
auto hash = utils::collection::to_hex_string(
|
||||
utils::encryption::create_hash_sha256("a"));
|
||||
EXPECT_STREQ(
|
||||
"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb",
|
||||
hash.c_str());
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::encryption::create_hash_sha256(L"a"));
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
"ffe9aaeaa2a2d5048174df0b80599ef0197ec024c4b051bc9860cff58ef7f9f3",
|
||||
hash.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
"a2d398922901344d08180dc41d3e9d73d8c148c7f6e092835bbb28e02dbcf184",
|
||||
hash.c_str());
|
||||
#endif
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::encryption::create_hash_sha256({1U}));
|
||||
EXPECT_STREQ(
|
||||
"4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a",
|
||||
hash.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_hash, sha512) {
|
||||
auto hash = utils::collection::to_hex_string(
|
||||
utils::encryption::create_hash_sha512("a"));
|
||||
EXPECT_STREQ(
|
||||
"1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c65"
|
||||
"2bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75",
|
||||
hash.c_str());
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::encryption::create_hash_sha512(L"a"));
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
"5c2ca3d50f46ece6066c53bd1a490cbe5f72d2738ae9417332e91e5c3f75205c639d71a9"
|
||||
"a41d67d965fa137dddf439e0ab9443a6ea44915e90d8b5b566d1c076",
|
||||
hash.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_STREQ(
|
||||
"a93498d992e81915075144cb304d2bdf040b336283f888252244882d8366dd3a6e2d9749"
|
||||
"077114dda1a9aa1a7b69d33f7a781f003ccd12e599a6341014f29aaf",
|
||||
hash.c_str());
|
||||
#endif
|
||||
|
||||
hash = utils::collection::to_hex_string(
|
||||
utils::encryption::create_hash_sha512({1U}));
|
||||
EXPECT_STREQ(
|
||||
"7b54b66836c1fbdd13d2441d9e1434dc62ca677fb68f5fe66a464baadecdbd00576f8d6b"
|
||||
"5ac3bcc80844b7d50b1cc6603444bbe7cfcf8fc0aa1ee3c636d9e339",
|
||||
hash.c_str());
|
||||
}
|
||||
} // namespace monitarr
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
552
support/test/src/utils/path_test.cpp
Normal file
552
support/test/src/utils/path_test.cpp
Normal file
@@ -0,0 +1,552 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "test.hpp"
|
||||
|
||||
#if defined(_WIN32)
|
||||
namespace {
|
||||
static const auto test_path = [](std::string str) -> std::string {
|
||||
#if defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES)
|
||||
if (monitarr::utils::string::begins_with(str, "\\")) {
|
||||
str = monitarr::utils::string::to_lower(
|
||||
std::filesystem::current_path().string().substr(0U, 2U)) +
|
||||
str;
|
||||
}
|
||||
|
||||
str = std::string{monitarr::utils::path::long_notation} + str;
|
||||
#else // !defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES)
|
||||
if (monitarr::utils::string::begins_with(str, "\\")) {
|
||||
str = monitarr::utils::string::to_lower(
|
||||
std::filesystem::current_path().string().substr(0U, 2U)) +
|
||||
str;
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES)
|
||||
|
||||
return monitarr::utils::string::right_trim(str, '\\');
|
||||
};
|
||||
} // namespace
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
namespace monitarr {
|
||||
TEST(utils_path, constants) {
|
||||
EXPECT_EQ(std::string_view{"\\"}, utils::path::backslash);
|
||||
EXPECT_EQ(std::wstring_view{L"\\"}, utils::path::backslash_w);
|
||||
EXPECT_EQ(std::string_view{"."}, utils::path::dot);
|
||||
EXPECT_EQ(std::wstring_view{L"."}, utils::path::dot_w);
|
||||
EXPECT_EQ(std::string_view{".\\"}, utils::path::dot_backslash);
|
||||
EXPECT_EQ(std::wstring_view{L".\\"}, utils::path::dot_backslash_w);
|
||||
EXPECT_EQ(std::string_view{"./"}, utils::path::dot_slash);
|
||||
EXPECT_EQ(std::wstring_view{L"./"}, utils::path::dot_slash_w);
|
||||
EXPECT_EQ(std::string_view{"/"}, utils::path::slash);
|
||||
EXPECT_EQ(std::wstring_view{L"/"}, utils::path::slash_w);
|
||||
|
||||
#if defined(_WIN32)
|
||||
EXPECT_EQ(std::string_view{"\\\\"}, utils::path::unc_notation);
|
||||
EXPECT_EQ(std::wstring_view{L"\\\\"}, utils::path::unc_notation_w);
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
TEST(utils_path, directory_seperator) {
|
||||
#if defined(_WIN32)
|
||||
EXPECT_EQ(utils::path::backslash, utils::path::directory_seperator);
|
||||
EXPECT_EQ(utils::path::backslash_w, utils::path::directory_seperator_w);
|
||||
|
||||
EXPECT_EQ(utils::path::slash, utils::path::not_directory_seperator);
|
||||
EXPECT_EQ(utils::path::slash_w, utils::path::not_directory_seperator_w);
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_EQ(utils::path::slash, utils::path::directory_seperator);
|
||||
EXPECT_EQ(utils::path::slash_w, utils::path::directory_seperator_w);
|
||||
|
||||
EXPECT_EQ(utils::path::backslash, utils::path::not_directory_seperator);
|
||||
EXPECT_EQ(utils::path::backslash_w, utils::path::not_directory_seperator_w);
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
TEST(utils_path, get_directory_seperator) {
|
||||
#if defined(_WIN32)
|
||||
EXPECT_EQ(utils::path::backslash,
|
||||
utils::path::get_directory_seperator<char>());
|
||||
EXPECT_EQ(utils::path::backslash_w,
|
||||
utils::path::get_directory_seperator<wchar_t>());
|
||||
|
||||
EXPECT_EQ(utils::path::slash,
|
||||
utils::path::get_not_directory_seperator<char>());
|
||||
EXPECT_EQ(utils::path::slash_w,
|
||||
utils::path::get_not_directory_seperator<wchar_t>());
|
||||
#else // !defined(_WIN32)
|
||||
EXPECT_EQ(utils::path::slash, utils::path::get_directory_seperator<char>());
|
||||
EXPECT_EQ(utils::path::slash_w,
|
||||
utils::path::get_directory_seperator<wchar_t>());
|
||||
|
||||
EXPECT_EQ(utils::path::backslash,
|
||||
utils::path::get_not_directory_seperator<char>());
|
||||
EXPECT_EQ(utils::path::backslash_w,
|
||||
utils::path::get_not_directory_seperator<wchar_t>());
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
TEST(utils_path, get_backslash) {
|
||||
EXPECT_EQ(utils::path::backslash, utils::path::get_backslash<char>());
|
||||
EXPECT_EQ(utils::path::backslash_w, utils::path::get_backslash<wchar_t>());
|
||||
}
|
||||
|
||||
TEST(utils_path, get_dot) {
|
||||
EXPECT_EQ(utils::path::dot, utils::path::get_dot<char>());
|
||||
EXPECT_EQ(utils::path::dot_w, utils::path::get_dot<wchar_t>());
|
||||
}
|
||||
|
||||
TEST(utils_path, get_dot_backslash) {
|
||||
EXPECT_EQ(utils::path::dot_backslash, utils::path::get_dot_backslash<char>());
|
||||
EXPECT_EQ(utils::path::dot_backslash_w,
|
||||
utils::path::get_dot_backslash<wchar_t>());
|
||||
}
|
||||
|
||||
TEST(utils_path, get_dot_slash) {
|
||||
EXPECT_EQ(utils::path::dot_slash, utils::path::get_dot_slash<char>());
|
||||
EXPECT_EQ(utils::path::dot_slash_w, utils::path::get_dot_slash<wchar_t>());
|
||||
}
|
||||
|
||||
TEST(utils_path, get_slash) {
|
||||
EXPECT_EQ(utils::path::slash, utils::path::get_slash<char>());
|
||||
EXPECT_EQ(utils::path::slash_w, utils::path::get_slash<wchar_t>());
|
||||
}
|
||||
|
||||
TEST(utils_path, get_long_notation) {
|
||||
EXPECT_EQ(utils::path::long_notation, utils::path::get_long_notation<char>());
|
||||
EXPECT_EQ(utils::path::long_notation_w,
|
||||
utils::path::get_long_notation<wchar_t>());
|
||||
}
|
||||
|
||||
TEST(utils_path, combine) {
|
||||
auto s = utils::path::combine(R"(\test\path)", {});
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\test\path)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/test/path", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::combine(R"(\test)", {R"(\path)"});
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\test\path)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/test/path", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::combine(R"(\test)", {R"(\path)", R"(\again\)"});
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\test\path\again)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/test/path/again", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::combine("/home/test/.dest", {".state"});
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\home\test\.dest\.state)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/home/test/.dest/.state", s.c_str());
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
s = utils::path::combine(R"(R:\test)", {R"(\path)", R"(\again\)"});
|
||||
EXPECT_STREQ(test_path(R"(r:\test\path\again)").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::combine("R:", {R"(\path)", R"(\again\)"});
|
||||
EXPECT_STREQ(test_path(R"(r:\path\again)").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::combine("R:", {});
|
||||
EXPECT_STREQ(test_path("r:").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::combine("R:", {"\\"});
|
||||
EXPECT_STREQ(test_path("r:").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::combine("\\\\moose", {"cow"});
|
||||
EXPECT_STREQ("\\\\moose\\cow", s.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(utils_path, format_path) {
|
||||
std::string path{"./"};
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ(".", path.c_str());
|
||||
|
||||
path = "~/.test";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("~/.test", path.c_str());
|
||||
|
||||
path = "\\";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("/", path.c_str());
|
||||
|
||||
path = "\\\\";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("/", path.c_str());
|
||||
|
||||
path = "\\\\\\";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("/", path.c_str());
|
||||
|
||||
path = "\\\\\\\\";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("/", path.c_str());
|
||||
|
||||
path = "/";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("/", path.c_str());
|
||||
path = "//";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("/", path.c_str());
|
||||
|
||||
path = "///";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("/", path.c_str());
|
||||
|
||||
path = "////";
|
||||
utils::path::format_path(path, utils::path::slash, utils::path::backslash);
|
||||
EXPECT_STREQ("/", path.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_path, create_api_path) {
|
||||
auto s = utils::path::create_api_path("");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path(R"(\)");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path("/");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path(".");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path("./");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path(R"(\\)");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path("//");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path("/cow///moose/////dog/chicken");
|
||||
EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path("\\cow\\\\\\moose\\\\\\\\dog\\chicken/");
|
||||
EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path("/cow\\\\/moose\\\\/\\dog\\chicken\\");
|
||||
EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path(".state");
|
||||
EXPECT_STREQ("/.state", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path("/.state/.local");
|
||||
EXPECT_STREQ("/.state/.local", s.c_str());
|
||||
|
||||
s = utils::path::create_api_path("./.state/.local");
|
||||
EXPECT_STREQ("/.state/.local", s.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_path, get_parent_api_path) {
|
||||
auto s = utils::path::get_parent_api_path("");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::get_parent_api_path("/");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::get_parent_api_path("/moose");
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
|
||||
s = utils::path::get_parent_api_path("/moose/cow");
|
||||
EXPECT_STREQ("/moose", s.c_str());
|
||||
|
||||
s = utils::path::get_parent_api_path("/moose/cow/");
|
||||
EXPECT_STREQ("/moose", s.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_path, finalize) {
|
||||
auto s = utils::path::finalize("");
|
||||
EXPECT_STREQ("", s.c_str());
|
||||
|
||||
s = utils::path::finalize(R"(\)");
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::finalize("/");
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::finalize(R"(\\)");
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ("\\\\", s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::finalize("//");
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ("\\\\", s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::finalize("/cow///moose/////dog/chicken");
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\cow\moose\dog\chicken)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::finalize("\\cow\\\\\\moose\\\\\\\\dog\\chicken/");
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\cow\moose\dog\chicken)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str());
|
||||
#endif
|
||||
|
||||
s = utils::path::finalize("/cow\\\\/moose\\\\/\\dog\\chicken\\");
|
||||
#if defined(_WIN32)
|
||||
EXPECT_STREQ(test_path(R"(\cow\moose\dog\chicken)").c_str(), s.c_str());
|
||||
#else
|
||||
EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str());
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
s = utils::path::finalize("D:");
|
||||
EXPECT_STREQ(test_path("d:").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::finalize("D:\\");
|
||||
EXPECT_STREQ(test_path("d:").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::finalize("D:\\moose");
|
||||
EXPECT_STREQ(test_path("d:\\moose").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::finalize("D:\\moose\\");
|
||||
EXPECT_STREQ(test_path("d:\\moose").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::finalize("D:/");
|
||||
EXPECT_STREQ(test_path("d:").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::finalize("D:/moose");
|
||||
EXPECT_STREQ(test_path("d:\\moose").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::finalize("D:/moose/");
|
||||
EXPECT_STREQ(test_path("d:\\moose").c_str(), s.c_str());
|
||||
|
||||
s = utils::path::finalize("\\\\moose\\cow");
|
||||
EXPECT_STREQ("\\\\moose\\cow", s.c_str());
|
||||
|
||||
s = utils::path::finalize("//moose/cow");
|
||||
EXPECT_STREQ("\\\\moose\\cow", s.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
s = utils::path::finalize("\\\\moose\\cow");
|
||||
EXPECT_STREQ("/moose/cow", s.c_str());
|
||||
|
||||
s = utils::path::finalize("//moose/cow");
|
||||
EXPECT_STREQ("/moose/cow", s.c_str());
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
TEST(utils_path, absolute) {
|
||||
auto dir = utils::path::get_current_path<std::string>();
|
||||
auto path = utils::path::absolute(".");
|
||||
EXPECT_STREQ(dir.c_str(), path.c_str());
|
||||
|
||||
path = utils::path::absolute("./");
|
||||
EXPECT_STREQ(dir.c_str(), path.c_str());
|
||||
|
||||
path = utils::path::absolute(R"(.\)");
|
||||
EXPECT_STREQ(dir.c_str(), path.c_str());
|
||||
|
||||
#if defined(_WIN32)
|
||||
path = utils::path::absolute(R"(.\moose)");
|
||||
EXPECT_STREQ((dir + R"(\moose)").c_str(), path.c_str());
|
||||
|
||||
path = utils::path::absolute(R"(./moose)");
|
||||
EXPECT_STREQ((dir + R"(\moose)").c_str(), path.c_str());
|
||||
|
||||
path = utils::path::absolute(R"(\\server\share)");
|
||||
EXPECT_STREQ(R"(\\server\share)", path.c_str());
|
||||
|
||||
path = utils::path::absolute(R"(//server/share)");
|
||||
EXPECT_STREQ(R"(\\server\share)", path.c_str());
|
||||
|
||||
auto home_env = utils::get_environment_variable("USERPROFILE");
|
||||
#else // !defined(_WIN32)
|
||||
path = utils::path::absolute(R"(.\moose)");
|
||||
EXPECT_STREQ((dir + R"(/moose)").c_str(), path.c_str());
|
||||
|
||||
path = utils::path::absolute(R"(./moose)");
|
||||
EXPECT_STREQ((dir + R"(/moose)").c_str(), path.c_str());
|
||||
|
||||
path = utils::path::absolute(R"(\\server\share)");
|
||||
EXPECT_STREQ(R"(/server/share)", path.c_str());
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
TEST(utils_path, absolute_can_resolve_path_variables) {
|
||||
#if defined(_WIN32)
|
||||
auto home =
|
||||
utils::path::absolute(utils::get_environment_variable("USERPROFILE"));
|
||||
EXPECT_STREQ(home.c_str(), utils::path::absolute("%USERPROFILE%").c_str());
|
||||
#else // !defined(_WIN32)
|
||||
auto home = utils::path::absolute(utils::get_environment_variable("HOME"));
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
auto expanded_str = utils::path::absolute("~\\");
|
||||
EXPECT_STREQ(home.c_str(), expanded_str.c_str());
|
||||
|
||||
expanded_str = utils::path::absolute("~/");
|
||||
EXPECT_STREQ(home.c_str(), expanded_str.c_str());
|
||||
|
||||
expanded_str = utils::path::absolute("~");
|
||||
EXPECT_STREQ("~", expanded_str.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_path, get_parent_path) {
|
||||
#if defined(_WIN32)
|
||||
{
|
||||
auto dir = R"(c:\test)";
|
||||
auto parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ("c:", parent.c_str());
|
||||
|
||||
dir = R"(c:\test\file.txt)";
|
||||
parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ(R"(c:\test)", parent.c_str());
|
||||
|
||||
dir = "c:";
|
||||
parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ("c:", parent.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
auto dir = LR"(c:\test)";
|
||||
auto parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ(L"c:", parent.c_str());
|
||||
|
||||
dir = LR"(c:\test\file.txt)";
|
||||
parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ(LR"(c:\test)", parent.c_str());
|
||||
|
||||
dir = L"c:";
|
||||
parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ(L"c:", parent.c_str());
|
||||
}
|
||||
#else // !defined(_WIN32)
|
||||
{
|
||||
auto dir = "/test";
|
||||
auto parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ("/", parent.c_str());
|
||||
|
||||
dir = "/test/test";
|
||||
parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ("/test", parent.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
auto dir = L"/test";
|
||||
auto parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ(L"/", parent.c_str());
|
||||
|
||||
dir = L"/test/test";
|
||||
parent = utils::path::get_parent_path(dir);
|
||||
EXPECT_STREQ(L"/test", parent.c_str());
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
TEST(utils_path, contains_trash_directory) {
|
||||
#if defined(_WIN32)
|
||||
{
|
||||
auto dir = R"(c:\$recycle.bin)";
|
||||
EXPECT_TRUE(utils::path::contains_trash_directory(dir));
|
||||
|
||||
dir = R"(c:\$recycle.bin\moose.txt)";
|
||||
EXPECT_TRUE(utils::path::contains_trash_directory(dir));
|
||||
}
|
||||
|
||||
{
|
||||
auto dir = LR"(c:\$recycle.bin)";
|
||||
EXPECT_TRUE(utils::path::contains_trash_directory(dir));
|
||||
|
||||
dir = LR"(c:\$recycle.bin\moose.txt)";
|
||||
EXPECT_TRUE(utils::path::contains_trash_directory(dir));
|
||||
}
|
||||
#else // !defined(_WIN32)
|
||||
{
|
||||
auto dir = "/$recycle.bin";
|
||||
EXPECT_TRUE(utils::path::contains_trash_directory(dir));
|
||||
|
||||
dir = "/$recycle.bin/moose.txt";
|
||||
EXPECT_TRUE(utils::path::contains_trash_directory(dir));
|
||||
}
|
||||
|
||||
{
|
||||
auto dir = L"/$recycle.bin";
|
||||
EXPECT_TRUE(utils::path::contains_trash_directory(dir));
|
||||
|
||||
dir = L"/$recycle.bin/moose.txt";
|
||||
EXPECT_TRUE(utils::path::contains_trash_directory(dir));
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
TEST(utils_path, does_not_contain_trash_directory) {
|
||||
#if defined(_WIN32)
|
||||
{
|
||||
auto dir = R"(c:\recycle.bin)";
|
||||
EXPECT_FALSE(utils::path::contains_trash_directory(dir));
|
||||
|
||||
dir = R"(c:\recycle.bin\moose.txt)";
|
||||
EXPECT_FALSE(utils::path::contains_trash_directory(dir));
|
||||
}
|
||||
|
||||
{
|
||||
auto dir = LR"(c:\recycle.bin)";
|
||||
EXPECT_FALSE(utils::path::contains_trash_directory(dir));
|
||||
|
||||
dir = LR"(c:\recycle.bin\moose.txt)";
|
||||
EXPECT_FALSE(utils::path::contains_trash_directory(dir));
|
||||
}
|
||||
#else // !defined(_WIN32)
|
||||
{
|
||||
auto dir = "/recycle.bin";
|
||||
EXPECT_FALSE(utils::path::contains_trash_directory(dir));
|
||||
|
||||
dir = "/recycle.bin/moose.txt)";
|
||||
EXPECT_FALSE(utils::path::contains_trash_directory(dir));
|
||||
}
|
||||
|
||||
{
|
||||
auto dir = L"/recycle.bin";
|
||||
EXPECT_FALSE(utils::path::contains_trash_directory(dir));
|
||||
|
||||
dir = L"/recycle.bin/moose.txt)";
|
||||
EXPECT_FALSE(utils::path::contains_trash_directory(dir));
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
} // namespace monitarr
|
||||
137
support/test/src/utils/string_test.cpp
Normal file
137
support/test/src/utils/string_test.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "test.hpp"
|
||||
|
||||
namespace monitarr {
|
||||
TEST(utils_string, begins_with) {
|
||||
std::string str{"moose"};
|
||||
EXPECT_TRUE(utils::string::begins_with(str, "m"));
|
||||
EXPECT_TRUE(utils::string::begins_with(str, "mo"));
|
||||
EXPECT_TRUE(utils::string::begins_with(str, "moo"));
|
||||
EXPECT_TRUE(utils::string::begins_with(str, "moos"));
|
||||
EXPECT_TRUE(utils::string::begins_with(str, "moose"));
|
||||
|
||||
EXPECT_FALSE(utils::string::begins_with(str, "a"));
|
||||
EXPECT_FALSE(utils::string::begins_with(str, "ma"));
|
||||
EXPECT_FALSE(utils::string::begins_with(str, "moose1"));
|
||||
|
||||
std::wstring str_w{L"moose"};
|
||||
EXPECT_TRUE(utils::string::begins_with(str_w, L"m"));
|
||||
EXPECT_TRUE(utils::string::begins_with(str_w, L"mo"));
|
||||
EXPECT_TRUE(utils::string::begins_with(str_w, L"moo"));
|
||||
EXPECT_TRUE(utils::string::begins_with(str_w, L"moos"));
|
||||
EXPECT_TRUE(utils::string::begins_with(str_w, L"moose"));
|
||||
|
||||
EXPECT_FALSE(utils::string::begins_with(str_w, L"a"));
|
||||
EXPECT_FALSE(utils::string::begins_with(str_w, L"ma"));
|
||||
EXPECT_FALSE(utils::string::begins_with(str_w, L"moose1"));
|
||||
}
|
||||
|
||||
TEST(utils_string, contains) {
|
||||
std::string str{R"(\\)"};
|
||||
EXPECT_TRUE(utils::string::contains(str, "\\"));
|
||||
EXPECT_FALSE(utils::string::contains(str, "/"));
|
||||
|
||||
std::wstring str_w{LR"(\\)"};
|
||||
EXPECT_TRUE(utils::string::contains(str_w, L"\\"));
|
||||
EXPECT_FALSE(utils::string::contains(str_w, L"/"));
|
||||
}
|
||||
|
||||
TEST(utils_string, replace) {
|
||||
std::string str{"moose"};
|
||||
utils::string::replace(str, 'o', '0');
|
||||
EXPECT_STREQ("m00se", str.c_str());
|
||||
|
||||
std::wstring str_w{L"moose"};
|
||||
utils::string::replace(str_w, 'o', '0');
|
||||
EXPECT_STREQ(L"m00se", str_w.c_str());
|
||||
|
||||
std::string str2{"\\\\\\"};
|
||||
utils::string::replace(str2, '\\', '/');
|
||||
EXPECT_STREQ("///", str2.c_str());
|
||||
|
||||
std::wstring str_w2{L"\\\\\\"};
|
||||
utils::string::replace(str_w2, '\\', '/');
|
||||
EXPECT_STREQ(L"///", str_w2.c_str());
|
||||
|
||||
std::string str3{"///"};
|
||||
utils::string::replace(str3, '/', '\\');
|
||||
EXPECT_STREQ("\\\\\\", str3.c_str());
|
||||
|
||||
std::wstring str_w3{L"///"};
|
||||
utils::string::replace(str_w3, '/', '\\');
|
||||
EXPECT_STREQ(L"\\\\\\", str_w3.c_str());
|
||||
|
||||
str.clear();
|
||||
utils::string::replace(str, '/', '\\');
|
||||
EXPECT_STREQ("", str.c_str());
|
||||
|
||||
str_w.clear();
|
||||
utils::string::replace(str_w, '/', '\\');
|
||||
EXPECT_STREQ(L"", str_w.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_string, replace_string) {
|
||||
std::string str{"moose"};
|
||||
utils::string::replace(str, "o", "0");
|
||||
EXPECT_STREQ("m00se", str.c_str());
|
||||
|
||||
std::wstring str_w{L"moose"};
|
||||
utils::string::replace(str_w, L"o", L"0");
|
||||
EXPECT_STREQ(L"m00se", str_w.c_str());
|
||||
}
|
||||
|
||||
TEST(utils_string, is_numeric) {
|
||||
EXPECT_TRUE(utils::string::is_numeric("100"));
|
||||
EXPECT_TRUE(utils::string::is_numeric("+100"));
|
||||
EXPECT_TRUE(utils::string::is_numeric("-100"));
|
||||
|
||||
EXPECT_TRUE(utils::string::is_numeric("100.00"));
|
||||
EXPECT_TRUE(utils::string::is_numeric("+100.00"));
|
||||
EXPECT_TRUE(utils::string::is_numeric("-100.00"));
|
||||
|
||||
EXPECT_FALSE(utils::string::is_numeric("1.00.00"));
|
||||
EXPECT_FALSE(utils::string::is_numeric("+1.00.00"));
|
||||
EXPECT_FALSE(utils::string::is_numeric("-1.00.00"));
|
||||
|
||||
EXPECT_FALSE(utils::string::is_numeric("a1"));
|
||||
EXPECT_FALSE(utils::string::is_numeric("1a"));
|
||||
|
||||
EXPECT_FALSE(utils::string::is_numeric("+"));
|
||||
EXPECT_FALSE(utils::string::is_numeric("-"));
|
||||
|
||||
EXPECT_FALSE(utils::string::is_numeric(""));
|
||||
}
|
||||
|
||||
TEST(utils_string, to_bool) {
|
||||
EXPECT_TRUE(utils::string::to_bool("1"));
|
||||
EXPECT_TRUE(utils::string::to_bool("-1"));
|
||||
EXPECT_TRUE(utils::string::to_bool("0.1"));
|
||||
EXPECT_TRUE(utils::string::to_bool("-0.1"));
|
||||
EXPECT_TRUE(utils::string::to_bool("00000.1000000"));
|
||||
EXPECT_TRUE(utils::string::to_bool("true"));
|
||||
|
||||
EXPECT_FALSE(utils::string::to_bool("false"));
|
||||
EXPECT_FALSE(utils::string::to_bool("0"));
|
||||
EXPECT_FALSE(utils::string::to_bool("00000.00000"));
|
||||
}
|
||||
} // namespace monitarr
|
||||
Reference in New Issue
Block a user