@@ -24,6 +24,7 @@
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
#include "utils/atomic.hpp"
|
||||
#include "utils/base64.hpp"
|
||||
#include "utils/collection.hpp"
|
||||
#if defined(_WIN32)
|
||||
@@ -49,6 +50,7 @@
|
||||
#include "utils/path.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "utils/time.hpp"
|
||||
#include "utils/ttl_cache.hpp"
|
||||
#if !defined(_WIN32)
|
||||
#include "utils/unix.hpp"
|
||||
#endif // !defined(_WIN32)
|
||||
|
||||
118
support/include/utils/atomic.hpp
Normal file
118
support/include/utils/atomic.hpp
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
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 REPERTORY_INCLUDE_UTILS_ATOMIC_HPP_
|
||||
#define REPERTORY_INCLUDE_UTILS_ATOMIC_HPP_
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
namespace repertory::utils {
|
||||
template <typename data_t> class atomic final {
|
||||
public:
|
||||
atomic() : mtx_(std::make_shared<std::mutex>()) {}
|
||||
|
||||
atomic(const atomic &at_data)
|
||||
: data_(at_data.load()), mtx_(std::make_shared<std::mutex>()) {}
|
||||
|
||||
atomic(data_t data)
|
||||
: data_(std::move(data)), mtx_(std::make_shared<std::mutex>()) {}
|
||||
|
||||
atomic(atomic &&) = default;
|
||||
|
||||
~atomic() = default;
|
||||
|
||||
private:
|
||||
data_t data_;
|
||||
std::shared_ptr<std::mutex> mtx_;
|
||||
|
||||
public:
|
||||
[[nodiscard]] auto load() const -> data_t {
|
||||
mutex_lock lock(*mtx_);
|
||||
return data_;
|
||||
}
|
||||
|
||||
auto store(data_t data) -> data_t {
|
||||
mutex_lock lock(*mtx_);
|
||||
data_ = std::move(data);
|
||||
return data_;
|
||||
}
|
||||
|
||||
auto operator=(const atomic &at_data) -> atomic & {
|
||||
if (&at_data == this) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
store(at_data.load());
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator=(atomic &&) -> atomic & = default;
|
||||
|
||||
auto operator=(data_t data) -> atomic & {
|
||||
if (&data == &data_) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
store(std::move(data));
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto operator==(const atomic &at_data) const -> bool {
|
||||
if (&at_data == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
mutex_lock lock(*mtx_);
|
||||
return at_data.load() == data_;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto operator==(const data_t &data) const -> bool {
|
||||
if (&data == &data_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
mutex_lock lock(*mtx_);
|
||||
return data == data_;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto operator!=(const atomic &at_data) const -> bool {
|
||||
if (&at_data == this) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mutex_lock lock(*mtx_);
|
||||
return at_data.load() != data_;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto operator!=(const data_t &data) const -> bool {
|
||||
if (&data == &data_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mutex_lock lock(*mtx_);
|
||||
return data != data_;
|
||||
}
|
||||
|
||||
[[nodiscard]] operator data_t() const { return load(); }
|
||||
};
|
||||
} // namespace repertory::utils
|
||||
|
||||
#endif // REPERTORY_INCLUDE_UTILS_ATOMIC_HPP_
|
||||
@@ -1,10 +1,11 @@
|
||||
// NOLINTBEGIN
|
||||
#ifndef _MACARON_BASE64_H_
|
||||
#define _MACARON_BASE64_H_
|
||||
#ifndef MACARON_BASE64_H_
|
||||
#define MACARON_BASE64_H_
|
||||
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2016 tomykaira
|
||||
* Copyright (c) 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
|
||||
@@ -39,121 +40,272 @@
|
||||
#endif
|
||||
|
||||
#include <array>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#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());
|
||||
// --- Alphabets --------------------------------------------------------------
|
||||
|
||||
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];
|
||||
static constexpr std::array<unsigned char, 64U> kStdAlphabet{
|
||||
'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', '+', '/',
|
||||
};
|
||||
|
||||
static constexpr std::array<unsigned char, 64U> kUrlAlphabet{
|
||||
'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', '-', '_',
|
||||
};
|
||||
|
||||
// Decoding table that accepts BOTH standard and URL-safe alphabets.
|
||||
static constexpr std::array<unsigned char, 256U> kDecodingTable = [] {
|
||||
std::array<unsigned char, 256U> t{};
|
||||
t.fill(64U);
|
||||
// 'A'-'Z'
|
||||
for (unsigned char c = 'A'; c <= 'Z'; ++c)
|
||||
t[c] = static_cast<unsigned char>(c - 'A');
|
||||
// 'a'-'z'
|
||||
for (unsigned char c = 'a'; c <= 'z'; ++c)
|
||||
t[c] = static_cast<unsigned char>(26 + c - 'a');
|
||||
// '0'-'9'
|
||||
for (unsigned char c = '0'; c <= '9'; ++c)
|
||||
t[c] = static_cast<unsigned char>(52 + c - '0');
|
||||
// Standard extras
|
||||
t[static_cast<unsigned char>('+')] = 62U;
|
||||
t[static_cast<unsigned char>('/')] = 63U;
|
||||
// URL-safe extras
|
||||
t[static_cast<unsigned char>('-')] = 62U;
|
||||
t[static_cast<unsigned char>('_')] = 63U;
|
||||
return t;
|
||||
}();
|
||||
|
||||
// --- Encoding ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Encode to Base64.
|
||||
* @param data pointer to bytes
|
||||
* @param len number of bytes
|
||||
* @param url_safe if true, use URL-safe alphabet ("-","_") instead of ("+","/")
|
||||
* @param pad if true, add '=' padding; if false, omit padding (RFC 4648
|
||||
* §5)
|
||||
*/
|
||||
static std::string Encode(const unsigned char *data, std::size_t len,
|
||||
bool url_safe = false, bool pad = true) {
|
||||
const auto &alpha = url_safe ? kUrlAlphabet : kStdAlphabet;
|
||||
|
||||
std::string out;
|
||||
if (len == 0U) {
|
||||
return out;
|
||||
}
|
||||
|
||||
const std::size_t full_blocks = len / 3U;
|
||||
const std::size_t rem = len % 3U;
|
||||
|
||||
std::size_t out_len{};
|
||||
if (pad) {
|
||||
out_len = 4U * ((len + 2U) / 3U);
|
||||
} else {
|
||||
// Unpadded length per RFC 4648 §5
|
||||
out_len = 4U * full_blocks + (rem == 0U ? 0U : (rem == 1U ? 2U : 3U));
|
||||
}
|
||||
out.assign(out_len, '\0');
|
||||
|
||||
auto *p = reinterpret_cast<unsigned char *>(out.data());
|
||||
std::size_t i = 0;
|
||||
|
||||
// Full 3-byte blocks -> 4 chars
|
||||
for (; i + 2U < len; i += 3U) {
|
||||
const unsigned char b0 = data[i + 0U];
|
||||
const unsigned char b1 = data[i + 1U];
|
||||
const unsigned char b2 = data[i + 2U];
|
||||
|
||||
*p++ = alpha[(b0 >> 2U) & 0x3F];
|
||||
*p++ = alpha[((b0 & 0x03U) << 4U) | ((b1 >> 4U) & 0x0FU)];
|
||||
*p++ = alpha[((b1 & 0x0FU) << 2U) | ((b2 >> 6U) & 0x03U)];
|
||||
*p++ = alpha[b2 & 0x3FU];
|
||||
}
|
||||
|
||||
// Remainder
|
||||
if (rem == 1U) {
|
||||
const unsigned char b0 = data[i];
|
||||
*p++ = alpha[(b0 >> 2U) & 0x3F];
|
||||
*p++ = alpha[(b0 & 0x03U) << 4U];
|
||||
if (pad) {
|
||||
*p++ = '=';
|
||||
*p++ = '=';
|
||||
}
|
||||
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)];
|
||||
}
|
||||
} else if (rem == 2U) {
|
||||
const unsigned char b0 = data[i + 0U];
|
||||
const unsigned char b1 = data[i + 1U];
|
||||
*p++ = alpha[(b0 >> 2U) & 0x3F];
|
||||
*p++ = alpha[((b0 & 0x03U) << 4U) | ((b1 >> 4U) & 0x0FU)];
|
||||
*p++ = alpha[(b1 & 0x0FU) << 2U];
|
||||
if (pad) {
|
||||
*p++ = '=';
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return out;
|
||||
}
|
||||
|
||||
[[maybe_unused]] static std::string Encode(std::string_view data) {
|
||||
[[maybe_unused]] static std::string
|
||||
Encode(std::string_view data, bool url_safe = false, bool pad = true) {
|
||||
return Encode(reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size());
|
||||
data.size(), url_safe, pad);
|
||||
}
|
||||
|
||||
[[maybe_unused]] static std::string
|
||||
EncodeUrlSafe(const unsigned char *data, std::size_t len, bool pad = false) {
|
||||
return Encode(data, len, /*url_safe=*/true, /*pad=*/pad);
|
||||
}
|
||||
|
||||
[[maybe_unused]] static std::string EncodeUrlSafe(std::string_view data,
|
||||
bool pad = false) {
|
||||
return Encode(reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size(), /*url_safe=*/true, /*pad=*/pad);
|
||||
}
|
||||
|
||||
// --- Decoding ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Decode standard OR URL-safe Base64.
|
||||
* Accepts inputs with or without '=' padding.
|
||||
* Throws std::runtime_error on malformed input.
|
||||
*/
|
||||
[[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 (input.empty()) {
|
||||
return out;
|
||||
}
|
||||
|
||||
std::size_t inLen = input.size();
|
||||
std::size_t rem = inLen % 4U;
|
||||
|
||||
// padded if multiple of 4 and last char is '='
|
||||
bool hasPadding = (rem == 0U) && (inLen >= 4U) && (input[inLen - 1U] == '=');
|
||||
|
||||
// compute output length
|
||||
std::size_t outLen{};
|
||||
if (hasPadding) {
|
||||
outLen = (inLen / 4U) * 3U;
|
||||
if (input[inLen - 1U] == '=')
|
||||
outLen--;
|
||||
if (input[inLen - 2U] == '=')
|
||||
outLen--;
|
||||
} else {
|
||||
if (rem == 1U) {
|
||||
throw std::runtime_error("Invalid Base64 length (mod 4 == 1)");
|
||||
}
|
||||
outLen = (inLen / 4U) * 3U + (rem == 0U ? 0U : (rem == 2U ? 1U : 2U));
|
||||
}
|
||||
|
||||
out.resize(outLen);
|
||||
|
||||
auto readVal = [](unsigned char c) -> unsigned char {
|
||||
unsigned char v = kDecodingTable[c];
|
||||
if (v == 64U) {
|
||||
throw std::runtime_error("Invalid Base64 character");
|
||||
}
|
||||
return v;
|
||||
};
|
||||
|
||||
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 i = 0U;
|
||||
std::size_t j = 0U;
|
||||
|
||||
std::size_t out_len{in_len / 4U * 3U};
|
||||
if (input[in_len - 1U] == '=') {
|
||||
out_len--;
|
||||
}
|
||||
if (input[in_len - 2U] == '=') {
|
||||
out_len--;
|
||||
}
|
||||
// process all full unpadded quartets
|
||||
std::size_t lastFull =
|
||||
hasPadding ? (inLen - 4U) : (rem == 0U ? inLen : (inLen - rem));
|
||||
|
||||
out.resize(out_len);
|
||||
while (i + 4U <= lastFull) {
|
||||
unsigned char a = readVal(static_cast<unsigned char>(input[i + 0U]));
|
||||
unsigned char b = readVal(static_cast<unsigned char>(input[i + 1U]));
|
||||
unsigned char c = readVal(static_cast<unsigned char>(input[i + 2U]));
|
||||
unsigned char d = readVal(static_cast<unsigned char>(input[i + 3U]));
|
||||
i += 4U;
|
||||
|
||||
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 = (static_cast<std::uint32_t>(a) << 18U) |
|
||||
(static_cast<std::uint32_t>(b) << 12U) |
|
||||
(static_cast<std::uint32_t>(c) << 6U) |
|
||||
(static_cast<std::uint32_t>(d));
|
||||
|
||||
std::uint32_t triple =
|
||||
(a << 3U * 6U) + (b << 2U * 6U) + (c << 1U * 6U) + (d << 0U * 6U);
|
||||
if (j < outLen)
|
||||
out[j++] = static_cast<unsigned char>((triple >> 16U) & 0xFFU);
|
||||
if (j < outLen)
|
||||
out[j++] = static_cast<unsigned char>((triple >> 8U) & 0xFFU);
|
||||
if (j < outLen)
|
||||
out[j++] = static_cast<unsigned char>(triple & 0xFFU);
|
||||
}
|
||||
|
||||
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;
|
||||
// tail: padded quartet or unpadded remainder
|
||||
if (i < inLen) {
|
||||
std::size_t left = inLen - i;
|
||||
|
||||
if (left == 4U) {
|
||||
bool thirdIsPad = (input[i + 2U] == '=');
|
||||
bool fourthIsPad = (input[i + 3U] == '=');
|
||||
|
||||
// '=' is never allowed in positions 1 or 2 of any quartet
|
||||
if (input[i + 0U] == '=' || input[i + 1U] == '=') {
|
||||
throw std::runtime_error("Invalid Base64 padding placement");
|
||||
}
|
||||
|
||||
unsigned char a = readVal(static_cast<unsigned char>(input[i + 0U]));
|
||||
unsigned char b = readVal(static_cast<unsigned char>(input[i + 1U]));
|
||||
unsigned char c = 0U;
|
||||
unsigned char d = 0U;
|
||||
|
||||
if (!thirdIsPad) {
|
||||
c = readVal(static_cast<unsigned char>(input[i + 2U]));
|
||||
if (!fourthIsPad) {
|
||||
d = readVal(static_cast<unsigned char>(input[i + 3U]));
|
||||
}
|
||||
} else {
|
||||
// if the 3rd is '=', the 4th must also be '='
|
||||
if (!fourthIsPad) {
|
||||
throw std::runtime_error("Invalid Base64 padding placement");
|
||||
}
|
||||
}
|
||||
i += 4U;
|
||||
|
||||
std::uint32_t triple = (static_cast<std::uint32_t>(a) << 18U) |
|
||||
(static_cast<std::uint32_t>(b) << 12U) |
|
||||
(static_cast<std::uint32_t>(c) << 6U) |
|
||||
(static_cast<std::uint32_t>(d));
|
||||
|
||||
if (j < outLen)
|
||||
out[j++] = static_cast<unsigned char>((triple >> 16U) & 0xFFU);
|
||||
if (!thirdIsPad && j < outLen)
|
||||
out[j++] = static_cast<unsigned char>((triple >> 8U) & 0xFFU);
|
||||
if (!fourthIsPad && !thirdIsPad && j < outLen)
|
||||
out[j++] = static_cast<unsigned char>(triple & 0xFFU);
|
||||
|
||||
} else if (left == 2U || left == 3U) {
|
||||
unsigned char a = readVal(static_cast<unsigned char>(input[i + 0U]));
|
||||
unsigned char b = readVal(static_cast<unsigned char>(input[i + 1U]));
|
||||
unsigned char c = (left == 3U)
|
||||
? readVal(static_cast<unsigned char>(input[i + 2U]))
|
||||
: 0U;
|
||||
i += left;
|
||||
|
||||
std::uint32_t triple = (static_cast<std::uint32_t>(a) << 18U) |
|
||||
(static_cast<std::uint32_t>(b) << 12U) |
|
||||
(static_cast<std::uint32_t>(c) << 6U);
|
||||
|
||||
if (j < outLen)
|
||||
out[j++] = static_cast<unsigned char>((triple >> 16U) & 0xFFU);
|
||||
if (left == 3U && j < outLen)
|
||||
out[j++] = static_cast<unsigned char>((triple >> 8U) & 0xFFU);
|
||||
} else {
|
||||
throw std::runtime_error("Invalid Base64 length (mod 4 == 1)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,6 +321,5 @@ Decode(std::string_view input) {
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif /* _MACARON_BASE64_H_ */
|
||||
|
||||
// NOLINTEND
|
||||
#endif /* MACARON_BASE64_H_ */
|
||||
// NOLINTEND
|
||||
|
||||
@@ -46,7 +46,7 @@ struct com_init_wrapper final {
|
||||
[[nodiscard]] auto is_initialized() const -> bool { return initialized_; }
|
||||
|
||||
private:
|
||||
BOOL initialized_;
|
||||
BOOL initialized_{};
|
||||
};
|
||||
} // namespace repertory::utils
|
||||
|
||||
|
||||
@@ -35,9 +35,10 @@ struct result final {
|
||||
|
||||
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 {
|
||||
[[nodiscard]] 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)
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#define NOMINMAX
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define WINVER 0x0602
|
||||
#define WINVER 0x0A00
|
||||
#define _WIN32_WINNT WINVER
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
@@ -107,7 +107,6 @@
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <codecvt>
|
||||
#include <condition_variable>
|
||||
#include <csignal>
|
||||
#include <cstdint>
|
||||
@@ -149,6 +148,9 @@
|
||||
#include <version>
|
||||
#endif // defined(__cplusplus)
|
||||
|
||||
#include <unicode/uchar.h>
|
||||
#include <unicode/utf.h>
|
||||
|
||||
#if defined(PROJECT_ENABLE_CURL)
|
||||
#include "curl/curl.h"
|
||||
#include "curl/multi.h"
|
||||
@@ -423,6 +425,8 @@ using vlc_string_t = std::unique_ptr<char, vlc_string_deleter>;
|
||||
|
||||
namespace repertory {
|
||||
using data_buffer = std::vector<unsigned char>;
|
||||
using data_span = std::span<unsigned char>;
|
||||
using data_cspan = std::span<const 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;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
#include "utils/encryption.hpp"
|
||||
#include "utils/hash.hpp"
|
||||
#include "utils/types/file/i_file.hpp"
|
||||
|
||||
@@ -37,14 +38,74 @@ public:
|
||||
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(stop_type_callback stop_requested_cb,
|
||||
std::string_view encrypted_file_path,
|
||||
std::string_view source_path, std::string_view token,
|
||||
std::size_t error_return = 0U);
|
||||
|
||||
encrypting_reader(
|
||||
stop_type_callback stop_requested_cb,
|
||||
std::string_view encrypted_file_path, std::string_view source_path,
|
||||
stop_type_callback stop_requested_cb, std::string_view token,
|
||||
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(std::string_view file_name, std::string_view source_path,
|
||||
stop_type_callback stop_requested_cb,
|
||||
std::string_view token, kdf_config cfg,
|
||||
std::optional<std::string> relative_parent_path,
|
||||
std::size_t error_return = 0U);
|
||||
|
||||
encrypting_reader(stop_type_callback stop_requested_cb,
|
||||
std::string_view encrypted_file_path,
|
||||
std::string_view source_path, std::string_view token,
|
||||
kdf_config cfg, std::size_t error_return = 0U);
|
||||
|
||||
encrypting_reader(
|
||||
stop_type_callback stop_requested_cb,
|
||||
std::string_view encrypted_file_path, std::string_view source_path,
|
||||
std::string_view token, kdf_config cfg,
|
||||
std::vector<std::array<unsigned char,
|
||||
crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>>
|
||||
iv_list,
|
||||
std::size_t error_return = 0U);
|
||||
|
||||
encrypting_reader(std::string_view file_name, std::string_view source_path,
|
||||
stop_type_callback stop_requested_cb,
|
||||
const utils::hash::hash_256_t &master_key,
|
||||
const kdf_config &cfg,
|
||||
std::optional<std::string> relative_parent_path,
|
||||
std::size_t error_return = 0U);
|
||||
|
||||
encrypting_reader(std::string_view file_name, std::string_view source_path,
|
||||
stop_type_callback stop_requested_cb,
|
||||
const utils::hash::hash_256_t &master_key,
|
||||
const std::pair<kdf_config, kdf_config> &configs,
|
||||
std::optional<std::string> relative_parent_path,
|
||||
std::size_t error_return = 0U);
|
||||
|
||||
encrypting_reader(stop_type_callback stop_requested_cb,
|
||||
std::string_view encrypted_file_path,
|
||||
std::string_view source_path,
|
||||
const utils::hash::hash_256_t &master_key,
|
||||
const kdf_config &cfg, std::size_t error_return = 0U);
|
||||
|
||||
encrypting_reader(
|
||||
stop_type_callback stop_requested_cb,
|
||||
std::string_view encrypted_file_path, std::string_view source_path,
|
||||
const utils::hash::hash_256_t &master_key, const kdf_config &cfg,
|
||||
std::vector<std::array<unsigned char,
|
||||
crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>>
|
||||
iv_list,
|
||||
std::size_t error_return = 0U);
|
||||
|
||||
encrypting_reader(
|
||||
stop_type_callback stop_requested_cb,
|
||||
std::string_view encrypted_file_path, std::string_view source_path,
|
||||
const utils::hash::hash_256_t &master_key,
|
||||
const std::pair<kdf_config, kdf_config> &configs,
|
||||
std::vector<std::array<unsigned char,
|
||||
crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>>
|
||||
iv_list,
|
||||
@@ -60,21 +121,25 @@ public:
|
||||
|
||||
public:
|
||||
using iostream = std::basic_iostream<char, std::char_traits<char>>;
|
||||
using kdf_pair_t = std::pair<data_buffer, data_buffer>;
|
||||
using key_pair_t =
|
||||
std::pair<utils::hash::hash_256_t, utils::hash::hash_256_t>;
|
||||
using streambuf = std::basic_streambuf<char, std::char_traits<char>>;
|
||||
|
||||
private:
|
||||
utils::encryption::hash_256_t key_;
|
||||
key_pair_t keys_;
|
||||
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_;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::size_t, data_buffer> chunk_buffers_;
|
||||
std::optional<kdf_pair_t> kdf_headers_;
|
||||
std::size_t last_data_chunk_{};
|
||||
std::size_t last_data_chunk_size_{};
|
||||
std::uint64_t read_offset_{};
|
||||
@@ -88,12 +153,30 @@ private:
|
||||
private:
|
||||
auto reader_function(char *buffer, size_t size, size_t nitems) -> size_t;
|
||||
|
||||
void common_initialize(bool procces_iv_list);
|
||||
|
||||
void common_initialize_kdf_data(const kdf_config &cfg,
|
||||
const utils::hash::hash_256_t &master_key);
|
||||
|
||||
void common_initialize_kdf_keys(std::string_view token, kdf_config &cfg);
|
||||
|
||||
void common_initialize_kdf_path(const utils::hash::hash_256_t &master_key);
|
||||
|
||||
void create_encrypted_paths(std::string_view file_name,
|
||||
std::optional<std::string> relative_parent_path);
|
||||
|
||||
public:
|
||||
[[nodiscard]] static auto calculate_decrypted_size(std::uint64_t total_size)
|
||||
[[nodiscard]] static auto calculate_decrypted_size(std::uint64_t total_size,
|
||||
bool uses_kdf)
|
||||
-> std::uint64_t;
|
||||
|
||||
[[nodiscard]] static auto
|
||||
calculate_encrypted_size(std::string_view source_path) -> std::uint64_t;
|
||||
calculate_encrypted_size(std::string_view source_path, bool uses_kdf)
|
||||
-> std::uint64_t;
|
||||
|
||||
[[nodiscard]] static auto calculate_encrypted_size(std::uint64_t size,
|
||||
bool uses_kdf)
|
||||
-> std::uint64_t;
|
||||
|
||||
[[nodiscard]] auto create_iostream() const -> std::shared_ptr<iostream>;
|
||||
|
||||
@@ -127,6 +210,12 @@ public:
|
||||
return iv_list_;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_kdf_config_for_data() const
|
||||
-> std::optional<kdf_config>;
|
||||
|
||||
[[nodiscard]] auto get_kdf_config_for_path() const
|
||||
-> std::optional<kdf_config>;
|
||||
|
||||
[[nodiscard]] auto get_stop_requested() const -> bool {
|
||||
return stop_requested_cb_();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
#if defined(PROJECT_ENABLE_BOOST) && defined(PROJECT_ENABLE_JSON)
|
||||
#include "utils/collection.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_BOOST) && defined(PROJECT_ENABLE_JSON)
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/hash.hpp"
|
||||
|
||||
@@ -34,32 +37,236 @@ inline constexpr std::uint32_t encryption_header_size{
|
||||
crypto_aead_xchacha20poly1305_IETF_ABYTES,
|
||||
};
|
||||
|
||||
#if defined(PROJECT_ENABLE_BOOST)
|
||||
enum class kdf_version : std::uint8_t { v1 };
|
||||
|
||||
enum class kdf_type : std::uint8_t { argon2id };
|
||||
|
||||
enum class memlimit_level : std::uint8_t {
|
||||
level1, // 64MiB
|
||||
level2, // 256MiB
|
||||
level3, // 512MiB
|
||||
level4, // 1GiB
|
||||
};
|
||||
|
||||
enum class opslimit_level : std::uint8_t {
|
||||
level1, // interactive
|
||||
level2, // moderate
|
||||
level3, // sensitive
|
||||
};
|
||||
|
||||
[[nodiscard]] inline auto get_memlimit(memlimit_level memlimit) -> size_t {
|
||||
constexpr auto mib512{512ULL * 1024ULL * 1024ULL};
|
||||
|
||||
switch (memlimit) {
|
||||
case memlimit_level::level1:
|
||||
return crypto_pwhash_MEMLIMIT_INTERACTIVE;
|
||||
|
||||
case memlimit_level::level2:
|
||||
return crypto_pwhash_MEMLIMIT_MODERATE;
|
||||
|
||||
case memlimit_level::level3:
|
||||
return mib512;
|
||||
|
||||
case memlimit_level::level4:
|
||||
return crypto_pwhash_MEMLIMIT_SENSITIVE;
|
||||
}
|
||||
|
||||
return mib512;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline auto get_opslimit(opslimit_level opslimit)
|
||||
-> unsigned long long {
|
||||
switch (opslimit) {
|
||||
case opslimit_level::level1:
|
||||
return crypto_pwhash_OPSLIMIT_INTERACTIVE;
|
||||
|
||||
case opslimit_level::level2:
|
||||
return crypto_pwhash_OPSLIMIT_MODERATE;
|
||||
|
||||
case opslimit_level::level3:
|
||||
return crypto_pwhash_OPSLIMIT_SENSITIVE;
|
||||
}
|
||||
|
||||
return crypto_pwhash_OPSLIMIT_MODERATE;
|
||||
}
|
||||
|
||||
enum class kdf_context : std::uint8_t {
|
||||
data,
|
||||
path,
|
||||
undefined,
|
||||
};
|
||||
using kdf_ctx_t = std::array<char, crypto_kdf_CONTEXTBYTES>;
|
||||
|
||||
namespace kdf {
|
||||
constexpr inline std::array<
|
||||
kdf_ctx_t, static_cast<std::size_t>(kdf_context::undefined) + 1U>
|
||||
KDF_CTXS{
|
||||
{
|
||||
{'D', 'A', 'T', 'A', '_', 'C', 'T', 'X'},
|
||||
{'F', 'I', 'L', 'E', '_', 'C', 'T', 'X'},
|
||||
{'D', 'E', 'F', 'L', '_', 'C', 'T', 'X'},
|
||||
},
|
||||
};
|
||||
} // namespace kdf
|
||||
|
||||
[[nodiscard]] constexpr inline auto get_kdf_context_name(kdf_context ctx)
|
||||
-> kdf_ctx_t {
|
||||
const auto idx = static_cast<std::size_t>(ctx);
|
||||
return idx < kdf::KDF_CTXS.size() ? kdf::KDF_CTXS.at(idx)
|
||||
: kdf::KDF_CTXS.back();
|
||||
}
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct kdf_config final {
|
||||
using salt_t = std::array<std::uint8_t, crypto_pwhash_SALTBYTES>;
|
||||
|
||||
kdf_version version{kdf_version::v1};
|
||||
kdf_type kdf{kdf_type::argon2id};
|
||||
memlimit_level memlimit{memlimit_level::level3};
|
||||
opslimit_level opslimit{opslimit_level::level2};
|
||||
std::uint64_t unique_id{};
|
||||
salt_t salt{};
|
||||
std::uint64_t checksum{};
|
||||
|
||||
template <typename hash_t>
|
||||
[[nodiscard]] auto create_subkey(kdf_context ctx, std::size_t unique_id_,
|
||||
const hash_t &master_key) const
|
||||
-> std::pair<hash_t, kdf_config> {
|
||||
auto sub_key = derive_subkey<hash_t>(ctx, unique_id_, master_key);
|
||||
|
||||
auto cfg = *this;
|
||||
cfg.unique_id = unique_id_;
|
||||
cfg.checksum = cfg.generate_checksum();
|
||||
return {sub_key, cfg};
|
||||
}
|
||||
|
||||
template <typename hash_t>
|
||||
[[nodiscard]] static auto derive_subkey(kdf_context ctx,
|
||||
std::size_t unique_id_,
|
||||
const hash_t &master_key) -> hash_t {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
hash_t sub_key{};
|
||||
auto res = crypto_kdf_derive_from_key(
|
||||
sub_key.data(), sub_key.size(), unique_id_,
|
||||
get_kdf_context_name(ctx).data(), master_key.data());
|
||||
if (res != 0) {
|
||||
throw repertory::utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to derive sub-key",
|
||||
std::to_string(res),
|
||||
});
|
||||
}
|
||||
|
||||
return sub_key;
|
||||
}
|
||||
|
||||
template <typename hash_t>
|
||||
[[nodiscard]] auto recreate_subkey(kdf_context ctx,
|
||||
const hash_t &master_key) const -> hash_t {
|
||||
return derive_subkey<hash_t>(ctx, unique_id, master_key);
|
||||
}
|
||||
|
||||
[[nodiscard]] static auto from_header(data_cspan data, kdf_config &cfg,
|
||||
bool ignore_checksum = false) -> bool;
|
||||
|
||||
[[nodiscard]] auto generate_checksum() const -> std::uint64_t;
|
||||
|
||||
void seal();
|
||||
|
||||
[[nodiscard]] static constexpr auto size() -> std::size_t {
|
||||
return sizeof(kdf_config);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto to_header() const -> data_buffer;
|
||||
|
||||
[[nodiscard]] auto operator==(const kdf_config &) const -> bool = default;
|
||||
[[nodiscard]] auto operator!=(const kdf_config &) const -> bool = default;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
template <typename hash_t>
|
||||
inline auto generate_key(
|
||||
[[nodiscard]] 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;
|
||||
utils::hash::default_create_hash<hash_t>()) -> hash_t;
|
||||
|
||||
template <typename hash_t>
|
||||
inline auto generate_key(
|
||||
[[nodiscard]] 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;
|
||||
utils::hash::default_create_hash<hash_t>()) -> hash_t;
|
||||
|
||||
#if defined(PROJECT_ENABLE_BOOST)
|
||||
template <typename hash_t>
|
||||
[[nodiscard]] inline auto generate_key(std::string_view password,
|
||||
kdf_config &cfg) -> hash_t;
|
||||
|
||||
template <typename hash_t>
|
||||
[[nodiscard]] inline auto generate_key(std::wstring_view password,
|
||||
kdf_config &cfg) -> hash_t;
|
||||
|
||||
template <typename hash_t>
|
||||
[[nodiscard]] inline auto recreate_key(std::string_view password,
|
||||
const kdf_config &cfg) -> hash_t;
|
||||
|
||||
template <typename hash_t>
|
||||
[[nodiscard]] inline auto recreate_key(std::wstring_view password,
|
||||
const kdf_config &cfg) -> hash_t;
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] auto create_key_argon2id(string_t password, kdf_config &cfg,
|
||||
utils::hash::hash_256_t &key) -> bool;
|
||||
|
||||
template <typename string_t>
|
||||
[[nodiscard]] auto recreate_key_argon2id(string_t password,
|
||||
const kdf_config &cfg,
|
||||
utils::hash::hash_256_t &key) -> bool;
|
||||
|
||||
template <typename hash_t, typename string_t>
|
||||
[[nodiscard]] inline auto
|
||||
detect_and_recreate_key(string_t password, data_cspan header, hash_t &key,
|
||||
std::optional<kdf_config> &cfg) -> bool;
|
||||
|
||||
template <typename hash_t>
|
||||
[[nodiscard]] inline auto
|
||||
detect_and_recreate_key(std::string_view password, data_cspan header,
|
||||
hash_t &key, std::optional<kdf_config> &cfg) -> bool;
|
||||
|
||||
template <typename hash_t>
|
||||
[[nodiscard]] inline auto
|
||||
detect_and_recreate_key(std::wstring_view password, data_cspan header,
|
||||
hash_t &key, std::optional<kdf_config> &cfg) -> bool;
|
||||
|
||||
[[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;
|
||||
|
||||
[[nodiscard]] auto decrypt_file_name(std::string_view encryption_token,
|
||||
const kdf_config &cfg,
|
||||
std::string &file_name) -> bool;
|
||||
|
||||
[[nodiscard]] auto decrypt_file_path(std::string_view encryption_token,
|
||||
const kdf_config &cfg,
|
||||
std::string &file_path) -> bool;
|
||||
|
||||
[[nodiscard]] auto decrypt_file_name(const utils::hash::hash_256_t &master_key,
|
||||
std::string &file_name) -> bool;
|
||||
|
||||
[[nodiscard]] auto decrypt_file_path(const utils::hash::hash_256_t &master_key,
|
||||
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 {
|
||||
std::size_t buffer_size, result_t &res)
|
||||
-> bool {
|
||||
if (buffer_size > encryption_header_size) {
|
||||
const std::uint32_t size =
|
||||
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(
|
||||
@@ -76,32 +283,53 @@ template <typename result_t, typename arr_t, std::size_t arr_size>
|
||||
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 {
|
||||
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>
|
||||
template <typename buffer_t, typename result_t,
|
||||
typename hash_t = utils::hash::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 {
|
||||
utils::hash::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>
|
||||
template <typename buffer_t, typename result_t,
|
||||
typename hash_t = utils::hash::hash_256_t>
|
||||
[[nodiscard]] inline auto decrypt_data(std::string_view password,
|
||||
const kdf_config &cfg,
|
||||
const buffer_t &buf, result_t &res)
|
||||
-> bool {
|
||||
return decrypt_data<buffer_t, result_t>(recreate_key<hash_t>(password, cfg),
|
||||
buf, res);
|
||||
}
|
||||
|
||||
template <typename result_t, typename hash_t = utils::hash::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 {
|
||||
utils::hash::default_create_hash<hash_t>()) -> bool {
|
||||
return decrypt_data<result_t>(generate_key(password, hasher), buffer,
|
||||
buffer_size, res);
|
||||
}
|
||||
|
||||
template <typename result_t, typename hash_t = utils::hash::hash_256_t>
|
||||
[[nodiscard]] inline auto decrypt_data(std::string_view password,
|
||||
const kdf_config &cfg,
|
||||
const unsigned char *buffer,
|
||||
std::size_t buffer_size, result_t &res)
|
||||
-> bool {
|
||||
return decrypt_data<result_t>(recreate_key<hash_t>(password, cfg), 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,
|
||||
@@ -144,26 +372,44 @@ inline void encrypt_data(const std::array<arr_t, arr_size> &key,
|
||||
encrypt_data<result_t>(iv, key, buffer, buffer_size, res);
|
||||
}
|
||||
|
||||
template <typename result_t, typename hash_t = hash_256_t>
|
||||
template <typename result_t, typename hash_t = utils::hash::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>()) {
|
||||
utils::hash::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>
|
||||
template <typename result_t, typename hash_t = utils::hash::hash_256_t>
|
||||
inline void encrypt_data(std::string_view password, kdf_config &cfg,
|
||||
const unsigned char *buffer, std::size_t buffer_size,
|
||||
result_t &res) {
|
||||
encrypt_data<result_t>(generate_key<hash_t>(password, cfg), buffer,
|
||||
buffer_size, res);
|
||||
}
|
||||
|
||||
template <typename buffer_t, typename result_t,
|
||||
typename hash_t = utils::hash::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>()) {
|
||||
utils::hash::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 hash_t = utils::hash::hash_256_t>
|
||||
inline void encrypt_data(std::string_view password, kdf_config &cfg,
|
||||
const buffer_t &buf, result_t &res) {
|
||||
encrypt_data<result_t>(generate_key<hash_t>(password, cfg),
|
||||
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,
|
||||
@@ -189,16 +435,52 @@ 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::hash::hash_256_t &key,
|
||||
bool uses_kdf,
|
||||
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,
|
||||
const http_range &range, const utils::hash::hash_256_t &key, bool uses_kdf,
|
||||
reader_func_t reader_func, std::uint64_t total_size, unsigned char *data,
|
||||
std::size_t size, std::size_t &bytes_read) -> bool;
|
||||
|
||||
[[nodiscard]] inline auto
|
||||
read_encrypted_range(const http_range &range,
|
||||
const utils::hash::hash_256_t &key,
|
||||
reader_func_t reader_func, std::uint64_t total_size,
|
||||
data_buffer &data) -> bool {
|
||||
return read_encrypted_range(range, key, false, reader_func, total_size, data);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline auto read_encrypted_range(
|
||||
const http_range &range, const utils::hash::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 {
|
||||
return read_encrypted_range(range, key, false, reader_func, total_size, data,
|
||||
size, bytes_read);
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
auto create_key_argon2id(string_t password, kdf_config &cfg,
|
||||
utils::hash::hash_256_t &key) -> bool {
|
||||
cfg.seal();
|
||||
|
||||
return recreate_key_argon2id(password, cfg, key);
|
||||
}
|
||||
|
||||
template <typename string_t>
|
||||
auto recreate_key_argon2id(string_t password, const kdf_config &cfg,
|
||||
utils::hash::hash_256_t &key) -> bool {
|
||||
return crypto_pwhash(
|
||||
reinterpret_cast<unsigned char *>(key.data()), key.size(),
|
||||
reinterpret_cast<const char *>(password.data()),
|
||||
password.size() * sizeof(typename string_t::value_type),
|
||||
cfg.salt.data(), get_opslimit(cfg.opslimit),
|
||||
get_memlimit(cfg.memlimit), crypto_pwhash_ALG_ARGON2ID13) == 0;
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
template <typename hash_t>
|
||||
@@ -218,7 +500,223 @@ inline auto generate_key(
|
||||
return hasher(reinterpret_cast<const unsigned char *>(password.data()),
|
||||
password.size() * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_BOOST)
|
||||
template <typename hash_t, typename string_t>
|
||||
inline auto generate_key_impl(string_t password, kdf_config &cfg) -> hash_t {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
switch (cfg.version) {
|
||||
case kdf_version::v1:
|
||||
switch (cfg.kdf) {
|
||||
case kdf_type::argon2id: {
|
||||
hash_t key{};
|
||||
if (not create_key_argon2id(password, cfg, key)) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to generate argon2id key",
|
||||
});
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
default:
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"unsupported kdf type",
|
||||
std::to_string(static_cast<std::uint8_t>(cfg.kdf)),
|
||||
});
|
||||
}
|
||||
|
||||
default:
|
||||
throw utils::error::create_exception(
|
||||
function_name,
|
||||
{
|
||||
"unsupported kdf version",
|
||||
std::to_string(static_cast<std::uint8_t>(cfg.version)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template <typename hash_t, typename string_t>
|
||||
inline auto recreate_key_impl(string_t password, const kdf_config &cfg)
|
||||
-> hash_t {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
switch (cfg.version) {
|
||||
case kdf_version::v1:
|
||||
switch (cfg.kdf) {
|
||||
case kdf_type::argon2id: {
|
||||
hash_t key{};
|
||||
if (not recreate_key_argon2id(password, cfg, key)) {
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to generate argon2id key",
|
||||
});
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
default:
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"unsupported kdf type",
|
||||
std::to_string(static_cast<std::uint8_t>(cfg.kdf)),
|
||||
});
|
||||
}
|
||||
|
||||
default:
|
||||
throw utils::error::create_exception(
|
||||
function_name,
|
||||
{
|
||||
"unsupported kdf version",
|
||||
std::to_string(static_cast<std::uint8_t>(cfg.version)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template <typename hash_t>
|
||||
inline auto generate_key(std::string_view password, kdf_config &cfg) -> hash_t {
|
||||
return generate_key_impl<hash_t, std::string_view>(password, cfg);
|
||||
}
|
||||
|
||||
template <typename hash_t>
|
||||
inline auto generate_key(std::wstring_view password, kdf_config &cfg)
|
||||
-> hash_t {
|
||||
return generate_key_impl<hash_t, std::wstring_view>(password, cfg);
|
||||
}
|
||||
|
||||
template <typename hash_t>
|
||||
inline auto recreate_key(std::string_view password, const kdf_config &cfg)
|
||||
-> hash_t {
|
||||
return recreate_key_impl<hash_t, std::string_view>(password, cfg);
|
||||
}
|
||||
|
||||
template <typename hash_t>
|
||||
inline auto recreate_key(std::wstring_view password, const kdf_config &cfg)
|
||||
-> hash_t {
|
||||
return recreate_key_impl<hash_t, std::wstring_view>(password, cfg);
|
||||
}
|
||||
|
||||
template <typename hash_t, typename string_t>
|
||||
inline auto detect_and_recreate_key(string_t password, data_cspan header,
|
||||
hash_t &key, std::optional<kdf_config> &cfg)
|
||||
-> bool {
|
||||
if (header.size() >= kdf_config::size()) {
|
||||
kdf_config tmp{};
|
||||
if (kdf_config::from_header(header.first(kdf_config::size()), tmp)) {
|
||||
cfg = tmp;
|
||||
key = recreate_key<hash_t>(password, *cfg);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
key = generate_key<hash_t>(password);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename hash_t>
|
||||
inline auto detect_and_recreate_key(std::string_view password,
|
||||
data_cspan header, hash_t &key,
|
||||
std::optional<kdf_config> &cfg) -> bool {
|
||||
return detect_and_recreate_key<hash_t, std::string_view>(password, header,
|
||||
key, cfg);
|
||||
}
|
||||
|
||||
template <typename hash_t>
|
||||
inline auto detect_and_recreate_key(std::wstring_view password,
|
||||
data_cspan header, hash_t &key,
|
||||
std::optional<kdf_config> &cfg) -> bool {
|
||||
return detect_and_recreate_key<hash_t, std::wstring_view>(password, header,
|
||||
key, cfg);
|
||||
}
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
} // namespace repertory::utils::encryption
|
||||
|
||||
#if defined(PROJECT_ENABLE_BOOST) && defined(PROJECT_ENABLE_JSON)
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
namespace kdf {
|
||||
inline constexpr std::string_view JSON_CHECKSUM{"checksum"};
|
||||
inline constexpr std::string_view JSON_KDF{"kdf"};
|
||||
inline constexpr std::string_view JSON_MEMLIMIT{"memlimit"};
|
||||
inline constexpr std::string_view JSON_OPSLIMIT{"opslimit"};
|
||||
inline constexpr std::string_view JSON_SALT{"salt"};
|
||||
inline constexpr std::string_view JSON_UNIQUE_ID{"unique_id"};
|
||||
inline constexpr std::string_view JSON_VERSION{"version"};
|
||||
} // namespace kdf
|
||||
|
||||
template <>
|
||||
struct adl_serializer<repertory::utils::encryption::kdf_config::salt_t> {
|
||||
static void
|
||||
to_json(json &data,
|
||||
const repertory::utils::encryption::kdf_config::salt_t &value) {
|
||||
data = repertory::utils::collection::to_hex_string(value);
|
||||
}
|
||||
|
||||
static void
|
||||
from_json(const json &data,
|
||||
repertory::utils::encryption::kdf_config::salt_t &value) {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
repertory::data_buffer buffer{};
|
||||
if (not repertory::utils::collection::from_hex_string(
|
||||
data.get<std::string>(), buffer)) {
|
||||
throw repertory::utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to convert hex string to salt",
|
||||
data.get<std::string>(),
|
||||
});
|
||||
}
|
||||
|
||||
if (buffer.size() != value.size()) {
|
||||
throw repertory::utils::error::create_exception(
|
||||
function_name, {
|
||||
"unexpected length for salt after hex conversion",
|
||||
"expected",
|
||||
std::to_string(value.size()),
|
||||
"actual",
|
||||
std::to_string(buffer.size()),
|
||||
});
|
||||
}
|
||||
|
||||
std::copy_n(buffer.begin(), value.size(), value.begin());
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct adl_serializer<repertory::utils::encryption::kdf_config> {
|
||||
static void to_json(json &data,
|
||||
const repertory::utils::encryption::kdf_config &value) {
|
||||
data[kdf::JSON_CHECKSUM] = value.checksum;
|
||||
data[kdf::JSON_KDF] = value.kdf;
|
||||
data[kdf::JSON_MEMLIMIT] = value.memlimit;
|
||||
data[kdf::JSON_OPSLIMIT] = value.opslimit;
|
||||
data[kdf::JSON_SALT] = value.salt;
|
||||
data[kdf::JSON_UNIQUE_ID] = value.unique_id;
|
||||
data[kdf::JSON_VERSION] = value.version;
|
||||
}
|
||||
|
||||
static void from_json(const json &data,
|
||||
repertory::utils::encryption::kdf_config &value) {
|
||||
data.at(kdf::JSON_CHECKSUM).get_to<std::uint64_t>(value.checksum);
|
||||
data.at(kdf::JSON_KDF)
|
||||
.get_to<repertory::utils::encryption::kdf_type>(value.kdf);
|
||||
data.at(kdf::JSON_MEMLIMIT)
|
||||
.get_to<repertory::utils::encryption::memlimit_level>(value.memlimit);
|
||||
data.at(kdf::JSON_OPSLIMIT)
|
||||
.get_to<repertory::utils::encryption::opslimit_level>(value.opslimit);
|
||||
data.at(kdf::JSON_SALT)
|
||||
.get_to<repertory::utils::encryption::kdf_config::salt_t>(value.salt);
|
||||
data.at(kdf::JSON_UNIQUE_ID).get_to<std::uint64_t>(value.unique_id);
|
||||
data.at(kdf::JSON_VERSION)
|
||||
.get_to<repertory::utils::encryption::kdf_version>(value.version);
|
||||
}
|
||||
};
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
#endif // defined(PROJECT_ENABLE_BOOST) && defined(PROJECT_ENABLE_JSON)
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
#endif // REPERTORY_INCLUDE_UTILS_ENCRYPTION_HPP_
|
||||
|
||||
@@ -41,13 +41,13 @@ namespace repertory::utils::file {
|
||||
[[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;
|
||||
[[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;
|
||||
directory_exists_in_path(std::string_view path, std::string_view sub_directory)
|
||||
-> bool;
|
||||
|
||||
// INFO: has test
|
||||
[[nodiscard]] inline auto
|
||||
@@ -55,45 +55,46 @@ 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;
|
||||
[[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;
|
||||
[[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>;
|
||||
[[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>;
|
||||
[[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>;
|
||||
[[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>;
|
||||
[[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>;
|
||||
[[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>;
|
||||
[[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>;
|
||||
[[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>;
|
||||
[[nodiscard]] auto get_total_drive_space(std::wstring_view path)
|
||||
-> std::optional<std::uint64_t>;
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBDSM)
|
||||
[[nodiscard]] auto
|
||||
@@ -101,20 +102,20 @@ 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;
|
||||
[[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;
|
||||
[[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_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_parent_path(std::string_view smb_path)
|
||||
-> std::string;
|
||||
|
||||
[[nodiscard]] auto smb_get_root_path(std::string_view smb_path) -> std::string;
|
||||
|
||||
@@ -143,27 +144,30 @@ 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;
|
||||
[[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;
|
||||
[[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;
|
||||
[[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;
|
||||
[[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;
|
||||
[[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,
|
||||
|
||||
@@ -49,28 +49,28 @@ private:
|
||||
stop_type *stop_requested_{nullptr};
|
||||
|
||||
public:
|
||||
[[nodiscard]] auto copy_to(std::string_view new_path,
|
||||
bool overwrite) const -> bool override;
|
||||
[[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 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_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_directory(std::string_view path) const
|
||||
-> fs_directory_t override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
get_directories() const -> std::vector<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_file(std::string_view path) const
|
||||
-> fs_file_t override;
|
||||
|
||||
[[nodiscard]] auto get_files() const -> std::vector<fs_file_t> override;
|
||||
|
||||
@@ -88,8 +88,8 @@ public:
|
||||
|
||||
[[nodiscard]] auto remove_recursively() -> bool override;
|
||||
|
||||
[[nodiscard]] auto
|
||||
size(bool recursive = false) const -> std::uint64_t override;
|
||||
[[nodiscard]] auto size(bool recursive = false) const
|
||||
-> std::uint64_t override;
|
||||
|
||||
public:
|
||||
auto operator=(const directory &) noexcept -> directory & = delete;
|
||||
|
||||
@@ -27,32 +27,55 @@
|
||||
|
||||
#include "utils/error.hpp"
|
||||
|
||||
namespace repertory::utils::encryption {
|
||||
namespace repertory::utils::hash {
|
||||
using hash_32_t = std::array<unsigned char, 4U>;
|
||||
using hash_64_t = std::array<unsigned char, 8U>;
|
||||
using hash_128_t = std::array<unsigned char, 16U>;
|
||||
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_32(std::string_view data) -> hash_32_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_blake2b_32(std::wstring_view data) -> hash_32_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_blake2b_32(const data_buffer &data) -> hash_32_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_blake2b_64(std::string_view data) -> hash_64_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_blake2b_64(std::wstring_view data) -> hash_64_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_blake2b_64(const data_buffer &data) -> hash_64_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_blake2b_128(std::string_view data) -> hash_128_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_blake2b_128(std::wstring_view data)
|
||||
-> hash_128_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_blake2b_128(const data_buffer &data)
|
||||
-> hash_128_t;
|
||||
|
||||
[[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(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_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(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_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(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(const data_buffer &data)
|
||||
-> hash_512_t;
|
||||
|
||||
[[nodiscard]] auto create_hash_blake2b_512(std::string_view data) -> hash_512_t;
|
||||
|
||||
@@ -83,8 +106,8 @@ template <typename hash_t>
|
||||
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 {
|
||||
auto create_hash_blake2b_t(const unsigned char *data, std::size_t data_size)
|
||||
-> hash_t {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
hash_t hash{};
|
||||
@@ -123,6 +146,27 @@ auto create_hash_blake2b_t(const unsigned char *data,
|
||||
return hash;
|
||||
}
|
||||
|
||||
inline const std::function<hash_32_t(const unsigned char *data,
|
||||
std::size_t size)>
|
||||
blake2b_32_hasher =
|
||||
[](const unsigned char *data, std::size_t data_size) -> hash_32_t {
|
||||
return create_hash_blake2b_t<hash_32_t>(data, data_size);
|
||||
};
|
||||
|
||||
inline const std::function<hash_64_t(const unsigned char *data,
|
||||
std::size_t size)>
|
||||
blake2b_64_hasher =
|
||||
[](const unsigned char *data, std::size_t data_size) -> hash_64_t {
|
||||
return create_hash_blake2b_t<hash_64_t>(data, data_size);
|
||||
};
|
||||
|
||||
inline const std::function<hash_128_t(const unsigned char *data,
|
||||
std::size_t size)>
|
||||
blake2b_128_hasher =
|
||||
[](const unsigned char *data, std::size_t data_size) -> hash_128_t {
|
||||
return create_hash_blake2b_t<hash_128_t>(data, data_size);
|
||||
};
|
||||
|
||||
inline const std::function<hash_256_t(const unsigned char *data,
|
||||
std::size_t size)>
|
||||
blake2b_256_hasher =
|
||||
@@ -158,6 +202,24 @@ inline const std::function<hash_512_t(const unsigned char *data,
|
||||
return create_hash_sha512(data, data_size);
|
||||
};
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline auto default_create_hash<hash_32_t>() -> const
|
||||
std::function<hash_32_t(const unsigned char *data, std::size_t size)> & {
|
||||
return blake2b_32_hasher;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline auto default_create_hash<hash_64_t>() -> const
|
||||
std::function<hash_64_t(const unsigned char *data, std::size_t size)> & {
|
||||
return blake2b_64_hasher;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline auto default_create_hash<hash_128_t>() -> const
|
||||
std::function<hash_128_t(const unsigned char *data, std::size_t size)> & {
|
||||
return blake2b_128_hasher;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline auto default_create_hash<hash_256_t>() -> const
|
||||
std::function<hash_256_t(const unsigned char *data, std::size_t size)> & {
|
||||
@@ -175,7 +237,7 @@ template <>
|
||||
std::function<hash_512_t(const unsigned char *data, std::size_t size)> & {
|
||||
return blake2b_512_hasher;
|
||||
}
|
||||
} // namespace repertory::utils::encryption
|
||||
} // namespace repertory::utils::hash
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
#endif // REPERTORY_INCLUDE_UTILS_HASH_HPP_
|
||||
|
||||
59
support/include/utils/timeout.hpp
Normal file
59
support/include/utils/timeout.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 REPERTORY_INCLUDE_UTILS_TIMEOUT_HPP_
|
||||
#define REPERTORY_INCLUDE_UTILS_TIMEOUT_HPP_
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
namespace repertory::utils {
|
||||
class timeout final {
|
||||
public:
|
||||
using callback_t = std::function<void()>;
|
||||
|
||||
public:
|
||||
timeout(const timeout &) noexcept = delete;
|
||||
timeout(timeout &&) noexcept = delete;
|
||||
auto operator=(const timeout &) noexcept -> timeout & = delete;
|
||||
auto operator=(timeout &&) noexcept -> timeout & = delete;
|
||||
|
||||
public:
|
||||
timeout(callback_t timeout_callback,
|
||||
std::chrono::system_clock::duration duration);
|
||||
|
||||
~timeout();
|
||||
|
||||
private:
|
||||
std::chrono::system_clock::duration duration_;
|
||||
callback_t timeout_callback_;
|
||||
std::atomic<bool> timeout_killed_{false};
|
||||
std::unique_ptr<std::thread> timeout_thread_{nullptr};
|
||||
std::mutex timeout_mutex_;
|
||||
std::condition_variable timeout_notify_;
|
||||
|
||||
public:
|
||||
void disable();
|
||||
|
||||
void reset();
|
||||
};
|
||||
} // namespace repertory::utils
|
||||
|
||||
#endif // REPERTORY_INCLUDE_UTILS_TIMEOUT_HPP_
|
||||
100
support/include/utils/ttl_cache.hpp
Normal file
100
support/include/utils/ttl_cache.hpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef REPERTORY_INCLUDE_UTILS_TTL_CACHE_HPP_
|
||||
#define REPERTORY_INCLUDE_UTILS_TTL_CACHE_HPP_
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
namespace repertory::utils {
|
||||
template <typename data_t, template <typename> class atomic_t = std::atomic>
|
||||
class ttl_cache final {
|
||||
public:
|
||||
using clock = std::chrono::steady_clock;
|
||||
using duration = std::chrono::milliseconds;
|
||||
using entry_t = atomic_t<data_t>;
|
||||
using entry_ptr_t = std::shared_ptr<entry_t>;
|
||||
|
||||
static constexpr auto default_expiration{duration(60000U)};
|
||||
|
||||
private:
|
||||
struct entry final {
|
||||
entry_ptr_t data;
|
||||
clock::time_point expires_at;
|
||||
};
|
||||
|
||||
public:
|
||||
ttl_cache(duration ttl = default_expiration) : ttl_{ttl} {}
|
||||
|
||||
private:
|
||||
duration ttl_;
|
||||
|
||||
private:
|
||||
mutable std::mutex mutex_;
|
||||
std::unordered_map<std::string, entry> entries_;
|
||||
|
||||
public:
|
||||
void clear() {
|
||||
mutex_lock lock(mutex_);
|
||||
entries_.clear();
|
||||
}
|
||||
|
||||
void erase(const std::string &api_path) {
|
||||
mutex_lock lock(mutex_);
|
||||
entries_.erase(api_path);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto contains(const std::string &api_path) -> bool {
|
||||
mutex_lock lock(mutex_);
|
||||
return entries_.contains(api_path);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get(const std::string &api_path) -> entry_ptr_t {
|
||||
mutex_lock lock(mutex_);
|
||||
auto iter = entries_.find(api_path);
|
||||
if (iter == entries_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
iter->second.expires_at = clock::now() + ttl_;
|
||||
return iter->second.data;
|
||||
}
|
||||
|
||||
void purge_expired() {
|
||||
mutex_lock lock(mutex_);
|
||||
auto now = clock::now();
|
||||
for (auto iter = entries_.begin(); iter != entries_.end();) {
|
||||
if (iter->second.expires_at <= now) {
|
||||
iter = entries_.erase(iter);
|
||||
continue;
|
||||
}
|
||||
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_ttl() const -> duration {
|
||||
mutex_lock lock(mutex_);
|
||||
return ttl_;
|
||||
}
|
||||
|
||||
void set(const std::string &api_path, const data_t &data) {
|
||||
mutex_lock lock(mutex_);
|
||||
if (entries_.contains(api_path)) {
|
||||
auto &entry = entries_.at(api_path);
|
||||
entry.data->store(data);
|
||||
entry.expires_at = clock::now() + ttl_;
|
||||
return;
|
||||
}
|
||||
|
||||
entries_.emplace(api_path, entry{
|
||||
.data = std::make_shared<entry_t>(data),
|
||||
.expires_at = clock::now() + ttl_,
|
||||
});
|
||||
}
|
||||
|
||||
void set_ttl(duration ttl) {
|
||||
mutex_lock lock(mutex_);
|
||||
ttl_ = ttl;
|
||||
}
|
||||
};
|
||||
} // namespace repertory::utils
|
||||
|
||||
#endif // REPERTORY_INCLUDE_UTILS_TTL_CACHE_HPP_
|
||||
@@ -30,6 +30,7 @@
|
||||
namespace repertory::utils::file {
|
||||
enum class time_type {
|
||||
accessed,
|
||||
changed,
|
||||
created,
|
||||
modified,
|
||||
written,
|
||||
@@ -37,6 +38,7 @@ enum class time_type {
|
||||
|
||||
struct file_times final {
|
||||
std::uint64_t accessed{};
|
||||
std::uint64_t changed{};
|
||||
std::uint64_t created{};
|
||||
std::uint64_t modified{};
|
||||
std::uint64_t written{};
|
||||
@@ -47,6 +49,8 @@ struct file_times final {
|
||||
switch (type) {
|
||||
case time_type::accessed:
|
||||
return accessed;
|
||||
case time_type::changed:
|
||||
return changed;
|
||||
case time_type::created:
|
||||
return created;
|
||||
case time_type::modified:
|
||||
@@ -70,8 +74,8 @@ struct i_fs_item {
|
||||
[[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 {
|
||||
[[nodiscard]] virtual auto copy_to(std::wstring_view new_path, bool overwrite)
|
||||
-> bool {
|
||||
return copy_to(utils::string::to_utf8(new_path), overwrite);
|
||||
}
|
||||
|
||||
@@ -79,8 +83,8 @@ struct i_fs_item {
|
||||
|
||||
[[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 get_time(time_type type) const
|
||||
-> std::optional<std::uint64_t>;
|
||||
|
||||
[[nodiscard]] virtual auto is_directory_item() const -> bool = 0;
|
||||
|
||||
|
||||
@@ -27,6 +27,40 @@
|
||||
#include "utils/config.hpp"
|
||||
|
||||
namespace repertory::utils {
|
||||
#if defined(__linux__)
|
||||
struct autostart_cfg final {
|
||||
std::string app_name;
|
||||
std::optional<std::string> comment;
|
||||
bool enabled{true};
|
||||
std::vector<std::string> exec_args;
|
||||
std::string exec_path;
|
||||
std::optional<std::string> icon_path;
|
||||
std::vector<std::string> only_show_in;
|
||||
bool terminal{false};
|
||||
};
|
||||
#endif // defined(__linux__)
|
||||
|
||||
#if defined(__APPLE__)
|
||||
enum class launchctl_type : std::uint8_t {
|
||||
bootout,
|
||||
bootstrap,
|
||||
kickstart,
|
||||
};
|
||||
|
||||
#if defined(PROJECT_ENABLE_PUGIXML)
|
||||
struct plist_cfg final {
|
||||
std::vector<std::string> args;
|
||||
bool keep_alive{false};
|
||||
std::string label;
|
||||
std::string plist_path;
|
||||
bool run_at_load{false};
|
||||
std::string stderr_log{"/tmp/stderr.log"};
|
||||
std::string stdout_log{"/tmp/stdout.log"};
|
||||
std::string working_dir{"/tmp"};
|
||||
};
|
||||
#endif // defined(PROJECT_ENABLE_PUGIXML)
|
||||
#endif // defined(__APPLE__)
|
||||
|
||||
using passwd_callback_t = std::function<void(struct passwd *pass)>;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
@@ -37,6 +71,12 @@ template <typename thread_t>
|
||||
[[nodiscard]] auto convert_to_uint64(const pthread_t &thread) -> std::uint64_t;
|
||||
#endif // defined(__APPLE__)
|
||||
|
||||
#if defined(__linux__)
|
||||
[[nodiscard]] auto create_autostart_entry(const autostart_cfg &cfg,
|
||||
bool overwrite_existing = true)
|
||||
-> bool;
|
||||
#endif // defined(__linux__)
|
||||
|
||||
[[nodiscard]] auto get_last_error_code() -> int;
|
||||
|
||||
[[nodiscard]] auto get_thread_id() -> std::uint64_t;
|
||||
@@ -48,9 +88,30 @@ void set_last_error_code(int error_code);
|
||||
[[nodiscard]] auto use_getpwuid(uid_t uid, passwd_callback_t callback)
|
||||
-> utils::result;
|
||||
|
||||
#if defined(__linux__)
|
||||
[[nodiscard]] auto remove_autostart_entry(std::string_view name) -> bool;
|
||||
#endif // defined(__linux__)
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(PROJECT_ENABLE_PUGIXML)
|
||||
[[nodiscard]] auto generate_launchd_plist(const plist_cfg &cfg,
|
||||
bool overwrite_existing = true)
|
||||
-> bool;
|
||||
#endif // defined(PROJECT_ENABLE_PUGIXML)
|
||||
|
||||
#if defined(PROJECT_ENABLE_SPDLOG) || defined(PROJECT_ENABLE_FMT)
|
||||
[[nodiscard]] auto launchctl_command(std::string_view label,
|
||||
launchctl_type type) -> int;
|
||||
|
||||
[[nodiscard]] auto remove_launchd_plist(std::string_view plist_path,
|
||||
std::string_view label,
|
||||
bool should_bootout) -> bool;
|
||||
#endif // defined(PROJECT_ENABLE_SPDLOG) || defined(PROJECT_ENABLE_FMT)
|
||||
#endif // defined(__APPLE__)
|
||||
|
||||
// template implementations
|
||||
#if defined(__APPLE__)
|
||||
template <typename t>
|
||||
template <typename thread_t>
|
||||
[[nodiscard]] auto convert_to_uint64(const thread_t *thread_ptr)
|
||||
-> std::uint64_t {
|
||||
return static_cast<std::uint64_t>(
|
||||
|
||||
@@ -30,17 +30,42 @@ void create_console();
|
||||
|
||||
void free_console();
|
||||
|
||||
[[nodiscard]] auto get_available_drive_letter(char first = 'a')
|
||||
-> std::optional<std::string_view>;
|
||||
|
||||
[[nodiscard]] auto get_available_drive_letters(char first = 'a')
|
||||
-> std::vector<std::string_view>;
|
||||
|
||||
[[nodiscard]] auto get_local_app_data_directory() -> const std::string &;
|
||||
|
||||
[[nodiscard]] auto get_last_error_code() -> DWORD;
|
||||
|
||||
[[nodiscard]] auto get_startup_folder() -> std::wstring;
|
||||
|
||||
[[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);
|
||||
struct shortcut_cfg final {
|
||||
std::wstring arguments;
|
||||
std::wstring exe_path;
|
||||
std::wstring icon_path;
|
||||
std::wstring location{get_startup_folder()};
|
||||
std::wstring shortcut_name;
|
||||
std::wstring working_directory;
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
auto create_shortcut(const shortcut_cfg &cfg, bool overwrite_existing = true)
|
||||
-> bool;
|
||||
|
||||
[[nodiscard]] auto
|
||||
remove_shortcut(std::wstring shortcut_name,
|
||||
const std::wstring &location = get_startup_folder()) -> bool;
|
||||
|
||||
void set_last_error_code(DWORD error_code);
|
||||
} // namespace repertory::utils
|
||||
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
Reference in New Issue
Block a user