From 734dab801d03de5542e1a5be32ca4104b42da9f4 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 20 Aug 2024 08:48:29 -0500 Subject: [PATCH] updated build system --- cmake/flags.cmake | 1 - cmake/versions.cmake | 4 + docker/aarch64/alpine | 1 + docker/x86_64/alpine | 1 + docker/x86_64/mingw64 | 52 ++ .../librepertory/include/utils/encrypt.hpp | 6 - repertory/librepertory/src/utils/encrypt.cpp | 37 -- scripts/copy_mingw64_deps.sh | 4 + scripts/env.sh | 5 + support/include/utils/collection.hpp | 80 ++- support/include/utils/config.hpp | 100 ++- support/include/utils/encrypting_reader.hpp | 2 +- support/include/utils/encryption.hpp | 6 + support/include/utils/file.hpp | 552 ++++++++++++++-- support/src/utils/encrypting_reader.cpp | 3 +- support/src/utils/encryption.cpp | 67 ++ support/src/utils/file.cpp | 203 +++++- .../src/utils/file_directory.cpp | 108 ++-- support/src/utils/file_file.cpp | 134 ++-- support/src/utils/file_smb_directory.cpp | 610 ++++++++++++++++++ support/src/utils/file_smb_file.cpp | 308 +++++++++ support/src/utils/file_thread_file.cpp | 24 +- support/test/src/utils/collection_test.cpp | 250 +++++++ .../test/src/utils/encrypting_reader_test.cpp | 1 + support/test/src/utils/encryption_test.cpp | 37 +- support/test/src/utils/file_test.cpp | 121 ++++ 26 files changed, 2440 insertions(+), 277 deletions(-) create mode 100644 support/src/utils/encryption.cpp rename repertory/repertory_test/src/encrypting_reader_test.cpp => support/src/utils/file_directory.cpp (51%) create mode 100644 support/src/utils/file_smb_directory.cpp create mode 100644 support/src/utils/file_smb_file.cpp create mode 100644 support/test/src/utils/collection_test.cpp diff --git a/cmake/flags.cmake b/cmake/flags.cmake index 97fe7d62..b6691f55 100644 --- a/cmake/flags.cmake +++ b/cmake/flags.cmake @@ -59,7 +59,6 @@ list(APPEND PROJECT_CFLAGS_LIST list(APPEND PROJECT_CXXFLAGS_LIST ${PROJECT_COMMON_FLAG_LIST} - -fext-numeric-literals -std=gnu++${CMAKE_CXX_STANDARD} ) diff --git a/cmake/versions.cmake b/cmake/versions.cmake index ec773485..d35960fd 100644 --- a/cmake/versions.cmake +++ b/cmake/versions.cmake @@ -21,10 +21,13 @@ set(GTKMM_VERSION 3.0) set(ICU_VERSION 75-1) set(JSON_VERSION 3.11.3) set(LIBBITCOIN_SYSTEM_VERSION 3.8.0) +set(LIBDSM_VERSION 0.4.3) set(LIBEVENT_VERSION 2.1.12) set(LIBJPEG_TURBO_VERSION 3.0.3) set(LIBPNG_VERSION 1.6.43) set(LIBSODIUM_VERSION 1.0.20) +set(LIBTASN_VERSION 4.19.0) +set(LIBICONV_VERSION 1.17) set(MESA_VERSION 23.3.3) set(MINGW_VERSION 11.0.1) set(NANA_VERSION 1.7.4) @@ -41,6 +44,7 @@ set(SFML_VERSION 2.6.1) set(SPDLOG_VERSION 1.14.1) set(SQLITE_VERSION 3460000) set(STDUUID_VERSION 1.2.3) +set(VLC_VERSION 3.0) set(VORBIS_VERSION 1.3.7) set(WXWIDGETS_VERSION 3.2.5) set(ZLIB_VERSION 1.3.1) diff --git a/docker/aarch64/alpine b/docker/aarch64/alpine index 77103cec..b0ca5ae3 100644 --- a/docker/aarch64/alpine +++ b/docker/aarch64/alpine @@ -10,6 +10,7 @@ RUN apk add \ bash \ binutils \ binutils-dev \ + bison \ boost-dev \ bzip2-static \ cmake \ diff --git a/docker/x86_64/alpine b/docker/x86_64/alpine index 3baa1759..f23b5b3f 100644 --- a/docker/x86_64/alpine +++ b/docker/x86_64/alpine @@ -10,6 +10,7 @@ RUN apk add \ bash \ binutils \ binutils-dev \ + bison \ boost-dev \ bzip2-static \ cmake \ diff --git a/docker/x86_64/mingw64 b/docker/x86_64/mingw64 index 0d21aa64..cfaa53ca 100644 --- a/docker/x86_64/mingw64 +++ b/docker/x86_64/mingw64 @@ -1004,6 +1004,58 @@ RUN if [ -f "/3rd_party/sdl-${MY_SDL_VERSION}.tar.gz" ]; then \ && rm -r SDL-release-${MY_SDL_VERSION} \ ; fi +ARG LIBTASN_VERSION +ENV MY_LIBTASN_VERSION=${LIBTASN_VERSION} +RUN if [ -f "/3rd_party/libtasn1-${MY_LIBTASN_VERSION}.tar.gz" ]; then \ + tar xvzf /3rd_party/libtasn1-${MY_LIBTASN_VERSION}.tar.gz \ + && cd libtasn1-${MY_LIBTASN_VERSION} \ + && ./configure \ + --disable-doc \ + --enable-static=yes \ + --enable-shared=no \ + --host=${MY_MINGW_PREFIX} \ + --prefix=${MY_MINGW_DIR} \ + && make -j${MY_NUM_JOBS} \ + && make install \ + && cd ${MY_WORKDIR} \ + && rm -r libtasn1-${MY_LIBTASN_VERSION} \ + ; fi + +ARG LIBICONV_VERSION +ENV MY_LIBICONV_VERSION=${LIBICONV_VERSION} +RUN if [ -f "/3rd_party/libiconv-${MY_LIBICONV_VERSION}.tar.gz" ]; then \ + tar xvzf /3rd_party/libiconv-${MY_LIBICONV_VERSION}.tar.gz \ + && cd libiconv-${MY_LIBICONV_VERSION} \ + && ./configure \ + --enable-static=yes \ + --enable-shared=no \ + --host=${MY_MINGW_PREFIX} \ + --prefix=${MY_MINGW_DIR} \ + && make -j${MY_NUM_JOBS} \ + && make install \ + && cd ${MY_WORKDIR} \ + && rm -r libiconv-${MY_LIBICONV_VERSION} \ + ; fi + +ARG LIBDSM_VERSION +ENV MY_LIBDSM_VERSION=${LIBDSM_VERSION} +RUN if [ -f "/3rd_party/libdsm-${MY_LIBDSM_VERSION}.tar.gz" ]; then \ + tar xvxf /3rd_party/libdsm-${MY_LIBDSM_VERSION}.tar.gz \ + && cd libdsm-${MY_LIBDSM_VERSION} \ + && meson setup \ + --cross-file ${MY_TOOLCHAIN_FILE_MESON} \ + --prefix=${MY_MINGW_DIR} \ + -Dbinaries=false \ + -Ddefault_library=static \ + _build \ + && meson compile \ + -C _build \ + && meson install \ + -C _build \ + && cd ${MY_WORKDIR} \ + && rm -r libdsm-${MY_LIBDSM_VERSION} \ + ; fi + RUN (mv ${MY_MINGW_DIR}/lib/*.dll ${MY_MINGW_DIR}/bin || echo "no dll's found") \ && chmod 0777 -R ${MY_MINGW_DIR} \ && rm -rf /3rd_party diff --git a/repertory/librepertory/include/utils/encrypt.hpp b/repertory/librepertory/include/utils/encrypt.hpp index 13147422..840855c4 100644 --- a/repertory/librepertory/include/utils/encrypt.hpp +++ b/repertory/librepertory/include/utils/encrypt.hpp @@ -29,12 +29,6 @@ namespace repertory::utils::encryption { using reader_func = std::function; -// Prototypes -[[nodiscard]] auto decrypt_file_path(std::string_view encryption_token, - std::string &file_path) -> api_error; - -[[nodiscard]] auto decrypt_file_name(std::string_view encryption_token, - std::string &file_name) -> api_error; [[nodiscard]] auto read_encrypted_range(const http_range &range, diff --git a/repertory/librepertory/src/utils/encrypt.cpp b/repertory/librepertory/src/utils/encrypt.cpp index b9982743..80402210 100644 --- a/repertory/librepertory/src/utils/encrypt.cpp +++ b/repertory/librepertory/src/utils/encrypt.cpp @@ -30,43 +30,6 @@ #include "utils/utils.hpp" namespace repertory::utils::encryption { -auto decrypt_file_path(std::string_view encryption_token, - std::string &file_path) -> api_error { - std::string decrypted_file_path{}; - for (const auto &part : std::filesystem::path(file_path)) { - auto file_name = part.string(); - if (file_name == "/") { - continue; - } - - auto res = decrypt_file_name(encryption_token, file_name); - if (res != api_error::success) { - return res; - } - - decrypted_file_path += '/' + file_name; - } - - file_path = decrypted_file_path; - return api_error::success; -} - -auto decrypt_file_name(std::string_view encryption_token, - std::string &file_name) -> api_error { - data_buffer buffer; - if (not utils::collection::from_hex_string(file_name, buffer)) { - return api_error::error; - } - - file_name.clear(); - if (not utils::encryption::decrypt_data(encryption_token, buffer, - file_name)) { - return api_error::decryption_error; - } - - return api_error::success; -} - auto read_encrypted_range(const http_range &range, const utils::encryption::hash_256_t &key, reader_func reader, std::uint64_t total_size, diff --git a/scripts/copy_mingw64_deps.sh b/scripts/copy_mingw64_deps.sh index c2772b06..06fff5a2 100755 --- a/scripts/copy_mingw64_deps.sh +++ b/scripts/copy_mingw64_deps.sh @@ -151,6 +151,10 @@ if [ "${PROJECT_IS_MINGW}" == "1" ] && [ "${PROJECT_ENABLE_WINFSP}" == "ON" ]; t fi fi +if [ "${PROJECT_IS_MINGW}" == "1" ] && [ "${PROJECT_ENABLE_VLC}" == "ON" ]; then + rsync -av --progress ${PROJECT_3RD_PARTY_DIR}/vlc/ "${PROJECT_DIST_DIR}/vlc/" +fi + for PROJECT_DEPENDENCY in "${PROJECT_MINGW64_COPY_DEPENDENCIES[@]}"; do rsync -av --progress ${PROJECT_DEPENDENCY} "${PROJECT_DIST_DIR}/" done diff --git a/scripts/env.sh b/scripts/env.sh index 9104bc88..26104456 100755 --- a/scripts/env.sh +++ b/scripts/env.sh @@ -222,6 +222,11 @@ else PROJECT_BUILD_SHARED_LIBS=ON fi +if [ "${PROJECT_ENABLE_LIBDSM}" == "ON" ]; then + PROJECT_ENABLE_LIBICONV=ON + PROJECT_ENABLE_LIBTASN=ON +fi + PROJECT_CMAKE_OPTS="-DPROJECT_3RD_PARTY_DIR=${PROJECT_3RD_PARTY_DIR} ${PROJECT_CMAKE_OPTS}" PROJECT_CMAKE_OPTS="-DPROJECT_BUILD_ARCH=${PROJECT_BUILD_ARCH} ${PROJECT_CMAKE_OPTS}" PROJECT_CMAKE_OPTS="-DPROJECT_BUILD_DIR=${PROJECT_BUILD_DIR} ${PROJECT_CMAKE_OPTS}" diff --git a/support/include/utils/collection.hpp b/support/include/utils/collection.hpp index 61ea7e11..e03f3d86 100644 --- a/support/include/utils/collection.hpp +++ b/support/include/utils/collection.hpp @@ -24,6 +24,7 @@ #include "utils/config.hpp" +#include "utils/error.hpp" #include "utils/string.hpp" namespace repertory::utils::collection { @@ -69,24 +70,75 @@ inline auto includes(const col_t &collection, collection.end(); } -template -[[nodiscard]] inline auto -from_hex_string_t(std::basic_string_view str, - val_t &val) -> bool { +template +[[nodiscard]] inline auto from_hex_string_t(std::string_view str, + val_t &val) -> bool { static constexpr const auto base16{16}; - val.clear(); - if (not(str.length() % 2U)) { - for (std::size_t i = 0U; i < str.length(); i += 2U) { - val.emplace_back(static_cast( - std::strtol(string_t{str.substr(i, 2U)}.c_str(), nullptr, base16))); + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + 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 std::runtime_error("hex string is invalid|" + std::string{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(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( + "invalid character in hex string|" + std::to_string(invalid_idx) + + '|' + std::string(1U, str.at(invalid_idx)) + '|' + std::string{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( + 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 +inline auto from_hex_string(std::string_view str, val_t &val) -> bool { + return from_hex_string_t(str, val); +} + +template +inline auto from_hex_string(std::wstring_view str, val_t &val) -> bool { + return from_hex_string_t(utils::string::to_utf8(str), val); +} template inline auto remove_element(col_t &collection, @@ -96,16 +148,6 @@ inline auto remove_element(col_t &collection, return collection; } -template -inline auto from_hex_string(std::string_view str, val_t &val) -> bool { - return from_hex_string_t(str, val); -} - -template -inline auto from_hex_string(std::wstring_view str, val_t &val) -> bool { - return from_hex_string_t(str, val); -} - template inline auto to_hex_string(const col_t &collection) -> std::string { static_assert(sizeof(typename col_t::value_type) == 1U, diff --git a/support/include/utils/config.hpp b/support/include/utils/config.hpp index cb8c1dab..6161bb3f 100644 --- a/support/include/utils/config.hpp +++ b/support/include/utils/config.hpp @@ -165,6 +165,50 @@ extern "C" { #endif // defined(__cplusplus) #endif // defined(PROJECT_ENABLE_FZF) +#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) { + if (ns != nullptr) { + netbios_ns_destroy(ns); + } + } +}; +using netbios_ns_t = std::unique_ptr; + +inline const auto smb_session_deleter = [](smb_session *session) { + if (session != nullptr) { + smb_session_destroy(session); + } +}; +using smb_session_t = std::shared_ptr; + +struct smb_stat_deleter final { + void operator()(smb_stat st) { + if (st != nullptr) { + smb_stat_destroy(st); + } + } +}; +using smb_stat_t = std::unique_ptr; + +struct smb_stat_list_deleter final { + void operator()(smb_file *list) { + if (list != nullptr) { + smb_stat_list_destroy(list); + } + } +}; +using smb_stat_list_t = std::unique_ptr; +#endif // defined(__cplusplus) +#endif // defined(PROJECT_ENABLE_LIBDSM) + #if defined(PROJECT_ENABLE_LIBEVENT) #include "event2/buffer.h" #include "event2/bufferevent.h" @@ -173,20 +217,68 @@ extern "C" { #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_LIBSODIUM) -#include "sodium.h" -#endif // defined(PROJECT_ENABLE_LIBSODIUM) - #if defined(PROJECT_ENABLE_SQLITE) #include "sqlite3.h" #endif // defined(PROJECT_ENABLE_SQLITE) +#if defined(PROJECT_ENABLE_VLC) +#include + +#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) { + if (inst != nullptr) { + libvlc_release(inst); + } + } +}; +using vlc_t = std::unique_ptr; + +struct vlc_media_deleter final { + void operator()(libvlc_media_t *media) { + if (media != nullptr) { + libvlc_media_release(media); + } + } +}; +using vlc_media_t = std::unique_ptr; + +struct vlc_media_list_deleter final { + void operator()(libvlc_media_list_t *media_list) { + if (media_list != nullptr) { + libvlc_media_list_release(media_list); + } + } +}; +using vlc_media_list_t = + std::unique_ptr; + +struct vlc_string_deleter final { + void operator()(char *str) { + if (str != nullptr) { + libvlc_free(str); + } + } +}; +using vlc_string_t = std::unique_ptr; +#endif // defined(__cplusplus) +#endif // defined(PROJECT_ENABLE_VLC) + #if !defined(fstat64) #define fstat64 fstat #endif // !defined(fstat64) diff --git a/support/include/utils/encrypting_reader.hpp b/support/include/utils/encrypting_reader.hpp index aaf085da..c49eb8a0 100644 --- a/support/include/utils/encrypting_reader.hpp +++ b/support/include/utils/encrypting_reader.hpp @@ -34,7 +34,7 @@ class encrypting_reader final { public: encrypting_reader(std::string_view file_name, std::string_view source_path, stop_type &stop_requested, std::string_view token, - std::optional relative_parent_path, + std::optional relative_parent_path, std::size_t error_return = 0U); encrypting_reader(std::string_view encrypted_file_path, diff --git a/support/include/utils/encryption.hpp b/support/include/utils/encryption.hpp index 029c6667..07c9f306 100644 --- a/support/include/utils/encryption.hpp +++ b/support/include/utils/encryption.hpp @@ -46,6 +46,12 @@ inline auto generate_key( default_create_hash()) -> 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 [[nodiscard]] inline auto decrypt_data(const std::array &key, const unsigned char *buffer, diff --git a/support/include/utils/file.hpp b/support/include/utils/file.hpp index a295accf..0ea91979 100644 --- a/support/include/utils/file.hpp +++ b/support/include/utils/file.hpp @@ -25,31 +25,106 @@ #include "utils/config.hpp" #include "utils/path.hpp" -#include namespace repertory::utils::file { -struct i_file { +#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; + +[[nodiscard]] auto +smb_create_relative_path(std::string_view smb_path) -> std::string; + +[[nodiscard]] auto +smb_create_search_path(std::string_view smb_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_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; + +[[nodiscard]] auto smb_parent_is_same(std::string_view smb_path1, + std::string_view smb_path2) -> bool; +#endif // defined(PROJECT_ENABLE_LIBDSM) + +struct i_fs_item { + using fs_item_t = std::unique_ptr; + enum class time_types { + access, + creation, + modified, + write, + }; + + virtual ~i_fs_item() = default; + + [[nodiscard]] virtual auto exists() const -> bool = 0; + + [[nodiscard]] virtual auto get_path() const -> std::string = 0; + + [[nodiscard]] virtual auto + get_time(time_types type) const -> std::uint64_t = 0; + + [[nodiscard]] virtual auto is_directory_item() 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; +}; + +struct i_file : public i_fs_item { + using fs_file_t = std::unique_ptr; + virtual ~i_file() = default; virtual void close() = 0; - [[nodiscard]] virtual auto exists() const -> bool = 0; - virtual void flush() const = 0; [[nodiscard]] virtual auto get_handle() const -> native_handle = 0; - [[nodiscard]] virtual auto get_path() const -> std::string = 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 move_to(std::string_view new_path) -> bool = 0; - - [[nodiscard]] virtual auto - read(data_buffer &data, std::uint64_t offset, - std::size_t *total_read = nullptr) -> 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, @@ -57,64 +132,59 @@ struct i_file { [[nodiscard]] virtual auto read_all(data_buffer &data, std::uint64_t offset, - std::size_t *total_read = nullptr) -> bool = 0; - - [[nodiscard]] virtual auto remove() -> bool = 0; + 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::uint64_t = 0; - [[nodiscard]] virtual auto truncate() -> bool = 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 = 0; + 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; -public: - [[nodiscard]] virtual operator bool() const = 0; - protected: i_file() noexcept = default; - i_file(const i_file &) noexcept = delete; + i_file(const i_file &) noexcept = default; - i_file(i_file &&) noexcept = delete; + i_file(i_file &&) noexcept = default; - auto operator=(i_file &&) noexcept -> i_file & = delete; + auto operator=(i_file &&) noexcept -> i_file & = default; - auto operator=(const i_file &) noexcept -> i_file & = delete; + auto operator=(const i_file &) noexcept -> i_file & = default; }; class file final : public i_file { public: // [[nodiscard]] static auto // attach_file(native_handle handle, - // bool read_only = false) -> std::unique_ptr; + // bool read_only = false) -> fs_file_t; - [[nodiscard]] static auto - open_file(std::string_view path, - bool read_only = false) -> std::unique_ptr; + [[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) -> std::unique_ptr { + [[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) -> std::unique_ptr; + bool read_only = false) -> fs_file_t; [[nodiscard]] static auto open_or_create_file(std::wstring_view path, - bool read_only = false) -> std::unique_ptr { + bool read_only = false) -> fs_file_t { return open_or_create_file(utils::string::to_utf8(path), read_only); } @@ -179,23 +249,18 @@ public: return read_buffer_size; } + [[nodiscard]] auto get_time(time_types type) const -> std::uint64_t override; + [[nodiscard]] auto is_read_only() const -> bool override { return read_only_; }; [[nodiscard]] auto move_to(std::string_view new_path) -> bool override; - [[nodiscard]] auto read(data_buffer &data, std::uint64_t offset, - std::size_t *total_read = nullptr) -> 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 - read_all(data_buffer &data, 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 { @@ -205,14 +270,8 @@ public: [[nodiscard]] auto size() const -> std::uint64_t override; - [[nodiscard]] auto truncate() -> bool override { return truncate(0U); } - [[nodiscard]] auto truncate(std::size_t size) -> bool override; - [[nodiscard]] auto - write(const data_buffer &data, std::uint64_t offset, - std::size_t *total_written = nullptr) -> 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; @@ -240,25 +299,25 @@ class thread_file final : public i_file { public: // [[nodiscard]] static auto // attach_file(native_handle handle, - // bool read_only = false) -> std::unique_ptr; + // bool read_only = false) -> fs_file_t; - [[nodiscard]] static auto - open_file(std::string_view path, - bool read_only = false) -> std::unique_ptr; + [[nodiscard]] static auto attach_file(fs_file_t file) -> fs_file_t; - [[nodiscard]] static auto - open_file(std::wstring_view path, - bool read_only = false) -> std::unique_ptr { + [[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) -> std::unique_ptr; + bool read_only = false) -> fs_file_t; [[nodiscard]] static auto open_or_create_file(std::wstring_view path, - bool read_only = false) -> std::unique_ptr { + bool read_only = false) -> fs_file_t { return open_or_create_file(utils::string::to_utf8(path), read_only); } @@ -271,7 +330,7 @@ public: : file_(new file(utils::string::to_utf8(path))) {} protected: - thread_file(std::unique_ptr file); + thread_file(fs_file_t file); public: thread_file(const thread_file &) = delete; @@ -282,7 +341,7 @@ public: ~thread_file() override { close(); } private: - std::unique_ptr file_; + fs_file_t file_; public: void close() override; @@ -303,23 +362,18 @@ public: return file_->get_read_buffer_size(); } + [[nodiscard]] auto get_time(time_types type) const -> std::uint64_t override; + [[nodiscard]] auto is_read_only() const -> bool override { return file_->is_read_only(); }; [[nodiscard]] auto move_to(std::string_view new_path) -> bool override; - [[nodiscard]] auto read(data_buffer &data, std::uint64_t offset, - std::size_t *total_read = nullptr) -> 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 - read_all(data_buffer &data, 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 { @@ -328,14 +382,8 @@ public: [[nodiscard]] auto size() const -> std::uint64_t override; - [[nodiscard]] auto truncate() -> bool override { return truncate(0U); } - [[nodiscard]] auto truncate(std::size_t size) -> bool override; - [[nodiscard]] auto - write(const data_buffer &data, std::uint64_t offset, - std::size_t *total_written = nullptr) -> 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; @@ -356,6 +404,372 @@ public: } }; +struct i_directory : public i_fs_item { + using fs_directory_t = std::unique_ptr; + 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]] auto is_directory_item() const -> bool override { return true; } + + [[nodiscard]] virtual auto + get_directory(std::string_view path) const -> fs_directory_t = 0; + + [[nodiscard]] virtual auto + get_directories() const -> std::vector = 0; + + [[nodiscard]] virtual auto + get_file(std::string_view path) const -> fs_file_t = 0; + + [[nodiscard]] virtual auto get_files() const -> std::vector = 0; + + [[nodiscard]] virtual auto get_items() const -> std::vector = 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; +}; + +class directory final : public i_directory { +public: + using directory_t = std::unique_ptr; + + directory() noexcept = default; + + directory(std::string_view path) : path_(utils::path::absolute(path)) {} + + directory(std::wstring_view path) + : path_(utils::path::absolute(utils::string::to_utf8(path))) {} + + directory(const directory &) noexcept = delete; + + directory(directory &&move_dir) noexcept = default; + + ~directory() override = default; + +private: + std::string path_; + +public: + [[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 override; + + [[nodiscard]] auto + get_file(std::string_view path) const -> fs_file_t override; + + [[nodiscard]] auto get_files() const -> std::vector override; + + [[nodiscard]] auto get_items() const -> std::vector override; + + [[nodiscard]] auto get_path() const -> std::string override { return path_; } + + [[nodiscard]] auto get_time(time_types type) const -> std::uint64_t 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(); } +}; + +#if defined(PROJECT_ENABLE_LIBDSM) +#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) + +class smb_file final : public i_file { +public: + smb_file() = default; + + smb_file(std::uint64_t access_time, std::uint64_t creation_time, + std::optional fd, std::uint64_t modified_time, + std::string path, smb_session_t session, std::string_view share_name, + smb_tid tid, std::uint64_t size, std::uint64_t write_time) + : access_time_(access_time), + creation_time_(creation_time), + fd_(std::move(fd)), + modified_time_(modified_time), + path_(std::move(path)), + session_(std::move(session)), + share_name_(share_name), + size_(size), + tid_(tid), + write_time_(write_time) {} + + smb_file(const smb_file &) = delete; + + smb_file(smb_file &&f) noexcept + : access_time_(f.access_time_), + creation_time_(f.creation_time_), + fd_(std::move(f.fd_)), + modified_time_(f.modified_time_), + 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_)), + size_(f.size_), + tid_(f.tid_), + write_time_(f.write_time_) {} + + ~smb_file() override { close(); } + +private: + std::uint64_t access_time_; + std::uint64_t creation_time_; + std::optional fd_; + std::uint64_t modified_time_; + std::string path_; + std::atomic_uint32_t read_buffer_size{65536U}; + bool read_only_; + smb_session_t session_; + std::string share_name_; + std::uint64_t size_; + smb_tid tid_; + std::uint64_t write_time_; + +public: + void close() 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]] auto get_time(time_types type) const -> std::uint64_t override { + switch (type) { + case time_types::access: + return access_time_; + + case time_types::creation: + return creation_time_; + + case time_types::modified: + return modified_time_; + + case time_types::write: + return write_time_; + } + } + + [[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 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::uint64_t override { return size_; } + + [[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) { + access_time_ = move_file.access_time_; + creation_time_ = move_file.creation_time_; + fd_ = std::move(move_file.fd_); + modified_time_ = move_file.modified_time_; + 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_); + size_ = move_file.size_; + tid_ = move_file.tid_; + write_time_ = move_file.write_time_; + } + + return *this; + } + + [[nodiscard]] operator bool() const override { return fd_.has_value(); } +}; + +class smb_directory final : public i_directory { +public: + using smb_directory_t = std::unique_ptr; + + [[nodiscard]] static auto + open(std::string_view host, std::string_view user, std::string_view password, + std::string_view share_name) -> smb_directory_t; + + [[nodiscard]] static auto + open(std::wstring_view host, std::wstring_view user, + std::wstring_view password, + std::wstring_view share_name) -> 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) + : path_(std::move(path)), + session_(std::move(session)), + share_name_(share_name), + tid_(tid) {} + +private: + std::string path_{}; + smb_session_t session_{}; + std::string share_name_{}; + smb_tid tid_{}; + +public: + [[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 override; + + [[nodiscard]] auto + get_file(std::string_view path) const -> fs_file_t override; + + [[nodiscard]] auto get_files() const -> std::vector override; + + [[nodiscard]] auto get_items() const -> std::vector override; + + [[nodiscard]] auto get_path() const -> std::string override { return path_; } + + [[nodiscard]] auto get_time(time_types type) const -> 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 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; } +}; +#endif // defined(PROJECT_ENABLE_LIBDSM) + [[nodiscard]] auto create_directories(std::string_view path) -> bool; [[nodiscard]] auto create_directories(std::wstring_view path) -> bool; diff --git a/support/src/utils/encrypting_reader.cpp b/support/src/utils/encrypting_reader.cpp index bf69f239..614a8bec 100644 --- a/support/src/utils/encrypting_reader.cpp +++ b/support/src/utils/encrypting_reader.cpp @@ -171,8 +171,7 @@ const std::size_t encrypting_reader::encrypted_chunk_size_ = encrypting_reader::encrypting_reader( std::string_view file_name, std::string_view source_path, stop_type &stop_requested, std::string_view token, - std::optional relative_parent_path, - std::size_t error_return) + std::optional relative_parent_path, std::size_t error_return) : key_(utils::encryption::generate_key( token)), stop_requested_(stop_requested), diff --git a/support/src/utils/encryption.cpp b/support/src/utils/encryption.cpp new file mode 100644 index 00000000..c60b2d87 --- /dev/null +++ b/support/src/utils/encryption.cpp @@ -0,0 +1,67 @@ +/* + Copyright <2018-2024> + + 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/encryption.hpp" + +#include "utils/collection.hpp" + +#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) + +namespace repertory::utils::encryption { +auto decrypt_file_path(std::string_view encryption_token, + std::string &file_path) -> bool { + std::string decrypted_file_path{}; + for (const auto &part : std::filesystem::path(file_path)) { + auto file_name = part.string(); + if (file_name == "/") { + continue; + } + + auto res = decrypt_file_name(encryption_token, file_name); + if (not res) { + return res; + } + + decrypted_file_path += '/' + file_name; + } + + file_path = decrypted_file_path; + 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(); + if (not utils::encryption::decrypt_data(encryption_token, buffer, + file_name)) { + return false; + } + + return true; +} +} // namespace repertory::utils::encryption + +#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined (PROJECT_ENABLE_BOOST) diff --git a/support/src/utils/file.cpp b/support/src/utils/file.cpp index f7d825e1..8979d4c4 100644 --- a/support/src/utils/file.cpp +++ b/support/src/utils/file.cpp @@ -77,10 +77,43 @@ namespace { } // namespace namespace repertory::utils::file { +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(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( + current_read / sizeof(data_buffer::value_type)))); + continue; + } + + return true; + } + + return false; +} + auto create_directories(std::string_view path) -> bool { + if (is_directory(path)) { + return true; + } + #if defined(_WIN32) - return is_directory(path) || - (::SHCreateDirectory( + return (::SHCreateDirectory( nullptr, utils::string::from_utf8(utils::path::absolute(path)).c_str()) == ERROR_SUCCESS); @@ -332,4 +365,170 @@ auto write_json_file(std::wstring_view path, } #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 { + if (not validate_smb_path(smb_path)) { + throw std::runtime_error("invalid smb path|" + std::string{smb_path}); + } + + std::string path{rel_path}; + utils::path::format_path(path, "/", "\\"); + utils::string::left_trim(path, '/'); + + auto old_parts = + repertory::utils::string::split(smb_path.substr(2U), '/', false); + old_parts.erase(std::next(old_parts.begin(), 2U), old_parts.end()); + + auto new_parts = repertory::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 std::runtime_error("invalid smb path|" + std::string{path}); + } + + return path; +} + +auto smb_create_and_validate_relative_path( + std::string_view smb_path, std::string_view path) -> std::string { + if (not validate_smb_path(smb_path)) { + throw std::runtime_error("invalid smb path|" + std::string{smb_path}); + } + + std::string dir_path; + if (utils::string::begins_with(path, "//")) { + if (not utils::file::smb_parent_is_same(smb_path, path)) { + throw std::runtime_error("failed to validate path|" + + std::string{smb_path} + '|' + std::string{path} + + "|parent paths are not the same"); + } + + 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 { + if (not validate_smb_path(smb_path)) { + throw std::runtime_error("invalid smb path|" + std::string{smb_path}); + } + + std::string path{smb_path}; + utils::path::format_path(path, "\\", "/"); + utils::string::left_trim(path, '\\'); + + auto parts = repertory::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 { + if (not validate_smb_path(smb_path)) { + throw std::runtime_error("invalid smb path|" + std::string{smb_path}); + } + + std::string path{smb_path}; + utils::string::left_trim(path, '/'); + + auto parts = repertory::utils::string::split(path, '/', false); + parts.erase(parts.begin(), std::next(parts.begin(), 2U)); + + auto search_path = repertory::utils::string::join(parts, '\\'); + return search_path.empty() ? "\\*" : "\\" + search_path + "\\*"; +} + +auto smb_get_parent_path(std::string_view smb_path) -> std::string { + if (not validate_smb_path(smb_path)) { + throw std::runtime_error("invalid smb path|" + std::string{smb_path}); + } + + auto parts = repertory::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 std::runtime_error("invalid smb path|" + parent_smb_path); + } + + return parent_smb_path; +} + +auto smb_get_root_path(std::string_view smb_path) -> std::string { + if (not validate_smb_path(smb_path)) { + throw std::runtime_error("invalid smb path|" + std::string{smb_path}); + } + + auto parts = repertory::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 { + if (not validate_smb_path(smb_path)) { + throw std::runtime_error("invalid smb path|" + std::string{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 { + if (not validate_smb_path(smb_path)) { + throw std::runtime_error("invalid smb path|" + std::string{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 { + if (not validate_smb_path(smb_path)) { + throw std::runtime_error("invalid smb path|" + std::string{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 repertory::utils::file diff --git a/repertory/repertory_test/src/encrypting_reader_test.cpp b/support/src/utils/file_directory.cpp similarity index 51% rename from repertory/repertory_test/src/encrypting_reader_test.cpp rename to support/src/utils/file_directory.cpp index ee8b5972..aff658f8 100644 --- a/repertory/repertory_test/src/encrypting_reader_test.cpp +++ b/support/src/utils/file_directory.cpp @@ -1,53 +1,55 @@ -/* - Copyright <2018-2024> - - 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_common.hpp" - -#include "types/repertory.hpp" -#include "utils/encrypting_reader.hpp" -#include "utils/file_utils.hpp" -#include "utils/path.hpp" - -namespace repertory { -/* TEST(encrypting_reader, get_encrypted_file_name) { - const auto source_file_name = get_source_file_name(); - ASSERT_TRUE(utils::file::retry_delete_file(source_file_name)); - - const auto token = std::string("moose"); - auto &source_file = create_random_file(source_file_name, 1024UL); - EXPECT_TRUE(source_file != nullptr); - if (source_file) { - stop_type stop_requested = false; - utils::encryption::encrypting_reader reader( - "test.dat", source_file_name, stop_requested, token, std::nullopt); - - auto file_name = reader.get_encrypted_file_name(); - - EXPECT_EQ(api_error::success, - utils::encryption::decrypt_file_name(token, file_name)); - EXPECT_STREQ("test.dat", file_name.c_str()); - - source_file->close(); - } - - EXPECT_TRUE(utils::file::retry_delete_file(source_file_name)); -} */ -} // namespace repertory +/* + Copyright <2018-2024> + + 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" + +namespace repertory::utils::file { +auto directory::count(bool recursive) const -> std::uint64_t { return 0U; } + +auto directory::create_directory(std::string_view path) const + -> fs_directory_t {} + +auto directory::exists() const -> bool { return false; } + +auto directory::get_directory(std::string_view path) const -> fs_directory_t { + return {}; +} + +auto directory::get_directories() const -> std::vector { + return {}; +} + +auto directory::get_time(time_types type) const -> std::uint64_t {} + +auto directory::get_file(std::string_view path) const -> fs_file_t {} + +auto directory::get_files() const -> std::vector { return {}; } + +auto directory::get_items() const -> std::vector { return {}; } + +auto directory::move_to(std::string_view new_path) -> bool { return false; } + +auto directory::remove() -> bool { return false; } + +auto directory::remove_recursively() -> bool { return false; } + +auto directory::size(bool recursive) const -> std::uint64_t { return 0U; } +} // namespace repertory::utils::file diff --git a/support/src/utils/file_file.cpp b/support/src/utils/file_file.cpp index 9a7b876d..9dac9def 100644 --- a/support/src/utils/file_file.cpp +++ b/support/src/utils/file_file.cpp @@ -25,10 +25,11 @@ #include "utils/error.hpp" #include "utils/path.hpp" #include "utils/string.hpp" +#include "utils/time.hpp" namespace repertory::utils::file { // auto file::attach_file(native_handle handle, -// bool read_only) -> std::unique_ptr { +// bool read_only) -> fs_file_t { // static constexpr const std::string_view function_name{ // static_cast(__FUNCTION__), // }; @@ -64,7 +65,7 @@ namespace repertory::utils::file { // auto *ptr = fdopen(handle, read_only ? "rb" : "rb+"); // #endif // defined(_WIN32) // -// return std::unique_ptr(new file{ +// return fs_file_t(new file{ // file_t{ptr}, // utils::path::absolute(path), // read_only, @@ -90,8 +91,7 @@ void file::open() { #endif // defined(_WIN32) } -auto file::open_file(std::string_view path, - bool read_only) -> std::unique_ptr { +auto file::open_file(std::string_view path, bool read_only) -> fs_file_t { static constexpr const std::string_view function_name{ static_cast(__FUNCTION__), }; @@ -101,7 +101,7 @@ auto file::open_file(std::string_view path, utils::path::absolute(path), read_only, }; - auto new_file = std::unique_ptr(ptr); + auto new_file = fs_file_t(ptr); try { ptr->open(); @@ -115,7 +115,7 @@ auto file::open_file(std::string_view path, } auto file::open_or_create_file(std::string_view path, - bool read_only) -> std::unique_ptr { + bool read_only) -> fs_file_t { auto abs_path = utils::path::absolute(path); if (not is_file(abs_path)) { #if defined(_WIN32) @@ -188,6 +188,68 @@ auto file::get_handle() const -> native_handle { return INVALID_HANDLE_VALUE; } +auto file::get_time(time_types type) const -> std::uint64_t { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + +#if defined(_WIN32) + recur_mutex_lock lock{*mtx_}; +#endif // defined(_WIN32) + + try { +#if defined(_WIN32) +#else // !defined(_WIN32) + struct stat st {}; + stat(path_.c_str(), &st); +#endif // defined(_WIN32) + + switch (type) { + case time_types::access: +#if defined(_WIN32) + break; +#else // !defined(_WIN32) + return static_cast(st.st_atim.tv_nsec + + st.st_atim.tv_sec * + utils::time::NANOS_PER_SECOND); +#endif // defined(_WIN32) + + case time_types::creation: +#if defined(_WIN32) + break; +#else // !defined(_WIN32) + return static_cast(st.st_ctim.tv_nsec + + st.st_ctim.tv_sec * + utils::time::NANOS_PER_SECOND); +#endif // defined(_WIN32) + + case time_types::modified: +#if defined(_WIN32) + break; +#else // !defined(_WIN32) + return static_cast(st.st_mtim.tv_nsec + + st.st_mtim.tv_sec * + utils::time::NANOS_PER_SECOND); +#endif // defined(_WIN32) + + case time_types::write: +#if defined(_WIN32) + break; +#else // !defined(_WIN32) + return static_cast(st.st_mtim.tv_nsec + + st.st_mtim.tv_sec * + utils::time::NANOS_PER_SECOND); +#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::move_to(std::string_view path) -> bool { static constexpr const std::string_view function_name{ static_cast(__FUNCTION__), @@ -233,59 +295,6 @@ auto file::move_to(std::string_view path) -> bool { return false; } -auto file::read_all(data_buffer &data, std::uint64_t offset, - std::size_t *total_read) -> bool { -#if defined(_WIN32) - recur_mutex_lock lock{*mtx_}; -#endif // defined(_WIN32) - - data_buffer buffer; - buffer.resize(read_buffer_size); - - std::size_t current_read{}; - while (read(reinterpret_cast(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( - current_read / sizeof(data_buffer::value_type)))); - continue; - } - - return true; - } - - return false; -} - -auto file::read(data_buffer &data, std::uint64_t offset, - std::size_t *total_read) -> bool { -#if defined(_WIN32) - recur_mutex_lock lock{*mtx_}; -#endif // defined(_WIN32) - - std::size_t bytes_read{}; - auto ret = - read(reinterpret_cast(data.data()), - data.size() * sizeof(data_buffer::value_type), offset, &bytes_read); - data.resize(bytes_read / sizeof(data_buffer::value_type)); - - if (total_read != nullptr) { - (*total_read) = bytes_read; - } - - return ret; -} - auto file::read(unsigned char *data, std::size_t to_read, std::uint64_t offset, std::size_t *total_read) -> bool { static constexpr const std::string_view function_name{ @@ -466,11 +475,4 @@ auto file::size() const -> std::uint64_t { return 0U; } - -auto file::write(const data_buffer &data, std::uint64_t offset, - std::size_t *total_written) -> bool { - return write(reinterpret_cast(data.data()), - data.size() * sizeof(data_buffer::value_type), offset, - total_written); -} } // namespace repertory::utils::file diff --git a/support/src/utils/file_smb_directory.cpp b/support/src/utils/file_smb_directory.cpp new file mode 100644 index 00000000..c09bb51f --- /dev/null +++ b/support/src/utils/file_smb_directory.cpp @@ -0,0 +1,610 @@ +/* + Copyright <2018-2024> + + 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/error.hpp" + +#if defined(PROJECT_ENABLE_LIBDSM) +namespace repertory::utils::file { +auto smb_directory::open(std::string_view host, std::string_view user, + std::string_view password, + std::string_view share_name) -> smb_directory_t { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + smb_session_t session{smb_session_new(), smb_session_deleter}; + netbios_ns_t ns{netbios_ns_new()}; + + sockaddr_in addr{}; + + auto res = netbios_ns_resolve( + ns.get(), std::string{host}.c_str(), NETBIOS_FILESERVER, + reinterpret_cast(&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 std::runtime_error("failed to resolve host|" + std::string{host} + + '|' + std::to_string(errno)); + } + } + + res = smb_session_connect(session.get(), std::string{host}.c_str(), + static_cast(addr.sin_addr.s_addr), + SMB_TRANSPORT_TCP); + if (res != DSM_SUCCESS) { + throw std::runtime_error("failed to connect to host|" + + std::string{host} + '|' + std::to_string(res)); + } + + 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 std::runtime_error("failed to logon to host|" + std::string{host} + + '|' + std::string{user} + '|' + + std::to_string(res)); + } + + smb_tid tid{}; + res = + smb_tree_connect(session.get(), std::string{share_name}.c_str(), &tid); + if (res != DSM_SUCCESS) { + throw std::runtime_error("failed to connect to share|" + + std::string{share_name} + '|' + + std::to_string(res)); + } + + return smb_directory_t{ + new smb_directory{ + "//" + std::string{host} + "/" + std::string{share_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 nullptr; +} + +auto smb_directory::open(std::wstring_view host, std::wstring_view user, + std::wstring_view password, + std::wstring_view share_name) -> smb_directory_t { + return open(utils::string::to_utf8(host), utils::string::to_utf8(user), + utils::string::to_utf8(password), + utils::string::to_utf8(share_name)); +} + +auto smb_directory::count(bool recursive) const -> std::uint64_t { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (not session_) { + throw std::runtime_error("session not found|" + path_); + } + + smb_stat_list_t list{ + smb_find(session_.get(), tid_, smb_create_search_path(path_).c_str())}; + auto count = smb_stat_list_count(list.get()); + + if (not recursive) { + return count; + } + + throw std::runtime_error("failed to get directory count recursively|" + + path_ + "|not implemented"); + } 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 { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (not session_) { + throw std::runtime_error("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 std::runtime_error("failed to create directory|" + path_ + '/' + + std::string{path} + '|' + std::to_string(res)); + } + + 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 { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (not session_) { + throw std::runtime_error("session not found|" + path_); + } + + auto fs_file = get_file(file_name); + if (fs_file) { + if (not dynamic_cast(fs_file.get())->open(read_only)) { + throw std::runtime_error("failed to open existing file|" + + std::string{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; + } + + smb_stat_t st{smb_fstat(session_.get(), tid_, rel_path.c_str())}; + if (not st) { + smb_fclose(session_.get(), fd); + throw std::runtime_error("failed to stat file|" + rel_path); + } + + return std::make_unique( + smb_stat_get(st.get(), SMB_STAT_ATIME), + smb_stat_get(st.get(), SMB_STAT_CTIME), fd, + smb_stat_get(st.get(), SMB_STAT_MTIME), + smb_create_smb_path(path_, std::string{rel_path}), session_, + share_name_, smb_stat_get(st.get(), SMB_STAT_SIZE), tid_, + 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 nullptr; +} +auto smb_directory::exists() const -> bool { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (not session_) { + throw std::runtime_error("session not found|" + path_); + } + + smb_stat_t st{smb_fstat(session_.get(), tid_, + smb_create_relative_path(path_).c_str())}; + 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 { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (not session_) { + throw std::runtime_error("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())}; + if (not st) { + throw std::runtime_error("failed to stat directory|" + rel_path); + } + + bool is_dir{smb_stat_get(st.get(), SMB_STAT_ISDIR) != 0U}; + if (not is_dir) { + throw std::runtime_error("path is not a directory|" + rel_path); + } + + return smb_directory_t{ + new smb_directory{ + smb_create_smb_path(path_, 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_directories() const -> std::vector { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (not session_) { + throw std::runtime_error("session not found|" + path_); + } + + smb_stat_list_t list{ + smb_find(session_.get(), tid_, smb_create_search_path(path_).c_str())}; + if (not list) { + throw std::runtime_error("failed to get directory list|" + path_); + } + + std::vector ret{}; + + auto count = smb_stat_list_count(list.get()); + for (std::size_t idx = 0U; idx < count; ++idx) { + auto st = smb_stat_list_at(list.get(), idx); + + bool is_dir{smb_stat_get(st, SMB_STAT_ISDIR) != 0U}; + if (not is_dir) { + continue; + } + + std::string name{smb_stat_name(st)}; + if (name == "." || name == "..") { + continue; + } + + ret.emplace_back(smb_directory_t{ + new smb_directory{ + smb_create_smb_path(path_, name), + session_, + share_name_, + tid_, + }, + }); + } + + 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 { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (not session_) { + throw std::runtime_error("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())}; + if (not st) { + throw std::runtime_error("failed to stat file|" + rel_path); + } + + bool is_dir{smb_stat_get(st.get(), SMB_STAT_ISDIR) != 0U}; + if (is_dir) { + throw std::runtime_error("path is not a file|" + rel_path); + } + + return std::make_unique( + smb_stat_get(st.get(), SMB_STAT_ATIME), + smb_stat_get(st.get(), SMB_STAT_CTIME), std::nullopt, + smb_stat_get(st.get(), SMB_STAT_MTIME), + smb_create_smb_path(path_, std::string{rel_path}), session_, + share_name_, smb_stat_get(st.get(), SMB_STAT_SIZE), tid_, + 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 nullptr; +} + +auto smb_directory::get_files() const -> std::vector { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (not session_) { + throw std::runtime_error("session not found|" + path_); + } + + smb_stat_list_t list{ + smb_find(session_.get(), tid_, smb_create_search_path(path_).c_str())}; + if (not list) { + throw std::runtime_error("failed to get file list|" + path_); + } + + std::vector ret{}; + + auto count = smb_stat_list_count(list.get()); + for (std::size_t idx = 0U; idx < count; ++idx) { + auto st = smb_stat_list_at(list.get(), idx); + bool is_dir{smb_stat_get(st, SMB_STAT_ISDIR) != 0U}; + if (is_dir) { + continue; + } + + std::string name{smb_stat_name(st)}; + ret.emplace_back(std::make_unique( + smb_stat_get(st, SMB_STAT_ATIME), smb_stat_get(st, SMB_STAT_CTIME), + std::nullopt, smb_stat_get(st, SMB_STAT_MTIME), + smb_create_smb_path(path_, name), session_, share_name_, + smb_stat_get(st, SMB_STAT_SIZE), tid_, + smb_stat_get(st, SMB_STAT_WTIME))); + } + + 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 { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (not session_) { + throw std::runtime_error("session not found|" + path_); + } + + smb_stat_list_t list{ + smb_find(session_.get(), tid_, smb_create_search_path(path_).c_str())}; + if (not list) { + throw std::runtime_error("failed to get item list|" + path_); + } + std::vector ret{}; + + auto count = smb_stat_list_count(list.get()); + for (std::size_t idx = 0U; idx < count; ++idx) { + auto st = smb_stat_list_at(list.get(), idx); + + bool is_dir{smb_stat_get(st, SMB_STAT_ISDIR) != 0U}; + std::string name{smb_stat_name(st)}; + + if (is_dir) { + if (name == "." || name == "..") { + continue; + } + + ret.emplace_back(smb_directory_t{ + new smb_directory{ + path_ + '/' + name, + session_, + share_name_, + tid_, + }, + }); + continue; + } + + ret.emplace_back(std::make_unique( + smb_stat_get(st, SMB_STAT_ATIME), smb_stat_get(st, SMB_STAT_CTIME), + std::nullopt, smb_stat_get(st, SMB_STAT_MTIME), + smb_create_smb_path(path_, name), session_, share_name_, tid_, + smb_stat_get(st, SMB_STAT_SIZE), smb_stat_get(st, SMB_STAT_WTIME))); + } + + 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_types type) const -> std::uint64_t { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (not session_) { + throw std::runtime_error("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())}; + if (not st) { + throw std::runtime_error("failed to stat directory|" + rel_path); + } + + switch (type) { + case time_types::access: + return smb_stat_get(st.get(), SMB_STAT_ATIME); + + case time_types::creation: + return smb_stat_get(st.get(), SMB_STAT_CTIME); + + case time_types::modified: + return smb_stat_get(st.get(), SMB_STAT_MTIME); + + case time_types::write: + 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 0U; +} + +auto smb_directory::move_to(std::string_view new_path) -> bool { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (not session_) { + throw std::runtime_error("session not found|" + path_); + } + + throw std::runtime_error("failed to move directory|" + path_ + '|' + + std::string{new_path} + "|not implemented"); + } 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 { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (not session_) { + throw std::runtime_error("session not found|" + path_); + } + + auto res = smb_directory_rm(session_.get(), tid_, + smb_create_relative_path(path_).c_str()); + if (res != DSM_SUCCESS) { + throw std::runtime_error("failed to remove directory|" + path_ + '|' + + std::to_string(res)); + } + + 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_directory::remove_recursively() -> bool { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (not session_) { + throw std::runtime_error("session not found|" + path_); + } + + throw std::runtime_error("failed to remove directory recursively|" + path_ + + "|not implemented"); + } 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 { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (not session_) { + throw std::runtime_error("session not found|" + path_); + } + + throw std::runtime_error("failed to get directory size|" + path_ + + "|not implemented"); + } catch (const std::exception &e) { + utils::error::handle_exception(function_name, e); + } catch (...) { + utils::error::handle_exception(function_name); + } + + return false; +} +} // namespace repertory::utils::file + +#endif // defined(PROJECT_ENABLE_LIBDSM) diff --git a/support/src/utils/file_smb_file.cpp b/support/src/utils/file_smb_file.cpp new file mode 100644 index 00000000..d920e193 --- /dev/null +++ b/support/src/utils/file_smb_file.cpp @@ -0,0 +1,308 @@ +/* + Copyright <2018-2024> + + 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/error.hpp" +#include "utils/string.hpp" + +#if defined(PROJECT_ENABLE_LIBDSM) + +namespace repertory::utils::file { +void smb_file::close() { + if (fd_.has_value()) { + smb_fclose(session_.get(), *fd_); + fd_.reset(); + } +} + +auto smb_file::exists() const -> bool { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (not session_) { + throw std::runtime_error("session not found|" + path_); + } + + smb_stat_t st{smb_fstat(session_.get(), tid_, + smb_create_relative_path(path_).c_str())}; + 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 { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + throw std::runtime_error("failed to flush file|" + path_ + + "|not implemented"); + } catch (const std::exception &e) { + utils::error::handle_exception(function_name, e); + } catch (...) { + utils::error::handle_exception(function_name); + } +} + +auto smb_file::move_to(std::string_view new_path) -> bool { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (utils::string::begins_with(new_path, "//")) { + throw std::runtime_error("failed to move file|" + path_ + '|' + + std::string{new_path} + + "|new path must be in same share"); + } + + 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 std::runtime_error("failed to connect to share|" + share_name_ + + '|' + std::to_string(res)); + } + + res = smb_file_mv(session_.get(), tid_, from_path.c_str(), to_path.c_str()); + if (res != DSM_SUCCESS) { + throw std::runtime_error("failed to move file|" + path_ + '|' + + from_path + '|' + to_path + '|' + + std::to_string(res)); + } + + 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 { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + 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 std::runtime_error("failed to connect to share|" + share_name_ + + '|' + std::to_string(res)); + } + + 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 std::runtime_error("failed to open file|" + path_ + '|' + rel_path + + '|' + utils::string::from_bool(read_only) + '|' + + std::to_string(res)); + } + + 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 { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (not fd_.has_value()) { + throw std::runtime_error("failed to read file|" + path_ + + "|file not open"); + } + + auto res = smb_fseek(session_.get(), *fd_, static_cast(offset), + SMB_SEEK_SET); + if (res == -1) { + throw std::runtime_error("failed to seek file|" + path_ + '|' + + std::to_string(offset) + '|' + + std::to_string(res)); + } + + res = smb_fread(session_.get(), *fd_, data, to_read); + if (res == -1) { + throw std::runtime_error("failed to read file|" + path_ + '|' + + std::to_string(to_read) + '|' + + std::to_string(res)); + } + + if (total_read != nullptr) { + (*total_read) = static_cast(res); + } + + 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 { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + close(); + + auto res = smb_tree_connect(session_.get(), share_name_.c_str(), &tid_); + if (res != DSM_SUCCESS) { + throw std::runtime_error("failed to connect to share|" + share_name_ + + '|' + std::to_string(res)); + } + + auto rel_path = smb_create_relative_path(path_); + res = smb_file_rm(session_.get(), tid_, rel_path.c_str()); + if (res != DSM_SUCCESS) { + throw std::runtime_error( + "failed to remove file|" + path_ + '|' + rel_path + '|' + + std::to_string(res) + '|' + + std::to_string(smb_session_get_nt_status(session_.get()))); + } + + 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::truncate(std::size_t size) -> bool { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + throw std::runtime_error("failed to truncate file|" + path_ + '|' + + std::to_string(size) + "|not implemented"); + } 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 { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + try { + if (not fd_.has_value()) { + throw std::runtime_error("failed to write file|" + path_ + + "|file not open"); + } + + auto res = smb_fseek(session_.get(), *fd_, static_cast(offset), + SMB_SEEK_SET); + if (res == -1) { + throw std::runtime_error("failed to seek file|" + path_ + '|' + + std::to_string(offset) + '|' + + std::to_string(res)); + } + + res = smb_fwrite(session_.get(), *fd_, const_cast(data), + to_write); + if (res == -1) { + throw std::runtime_error("failed to write file|" + path_ + '|' + + std::to_string(to_write) + '|' + + std::to_string(res)); + } + + if (total_written != nullptr) { + (*total_written) = static_cast(res); + } + + return true; + } catch (const std::exception &e) { + utils::error::handle_exception(function_name, e); + } catch (...) { + utils::error::handle_exception(function_name); + } + + return false; +} +} // namespace repertory::utils::file + +#endif // defined(PROJECT_ENABLE_LIBDSM) diff --git a/support/src/utils/file_thread_file.cpp b/support/src/utils/file_thread_file.cpp index 35eaff17..e2e170ef 100644 --- a/support/src/utils/file_thread_file.cpp +++ b/support/src/utils/file_thread_file.cpp @@ -23,29 +23,26 @@ namespace repertory::utils::file { // auto thread_file::attach_file(native_handle handle, -// bool read_only) -> std::unique_ptr {} +// bool read_only) -> fs_file_t {} + +auto thread_file::attach_file(fs_file_t file) -> fs_file_t {} auto thread_file::open_file(std::string_view path, - bool read_only) -> std::unique_ptr {} + bool read_only) -> fs_file_t {} -auto thread_file::open_or_create_file(std::string_view path, bool read_only) - -> std::unique_ptr {} +auto thread_file::open_or_create_file(std::string_view path, + bool read_only) -> fs_file_t {} -thread_file::thread_file(std::unique_ptr file) - : file_(std::move(file)) {} +thread_file::thread_file(fs_file_t file) : file_(std::move(file)) {} void thread_file::close() {} void thread_file::flush() const {} +auto thread_file::get_time(time_types type) const -> std::uint64_t {} + auto thread_file::move_to(std::string_view path) -> bool {} -auto thread_file::read_all(data_buffer &data, std::uint64_t offset, - std::size_t *total_read) -> bool {} - -auto thread_file::read(data_buffer &data, std::uint64_t offset, - std::size_t *total_read) -> bool {} - auto thread_file::read(unsigned char *data, std::size_t to_read, std::uint64_t offset, std::size_t *total_read) -> bool {} @@ -58,7 +55,4 @@ auto thread_file::write(const unsigned char *data, std::size_t to_write, std::size_t *total_written) -> bool {} auto thread_file::size() const -> std::uint64_t {} - -auto thread_file::write(const data_buffer &data, std::uint64_t offset, - std::size_t *total_written) -> bool {} } // namespace repertory::utils::file diff --git a/support/test/src/utils/collection_test.cpp b/support/test/src/utils/collection_test.cpp new file mode 100644 index 00000000..39d85bb5 --- /dev/null +++ b/support/test/src/utils/collection_test.cpp @@ -0,0 +1,250 @@ +/* + Copyright <2018-2024> + + 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 repertory { +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 val{}; + EXPECT_TRUE(utils::collection::from_hex_string(data, val)); + EXPECT_EQ(4U, val.size()); + } + + { + auto data = " 0xABCDEF10 "; + std::vector val{}; + EXPECT_TRUE(utils::collection::from_hex_string(data, val)); + EXPECT_EQ(4U, val.size()); + } + + { + auto data = "ABCDEF10"; + std::vector val{}; + EXPECT_TRUE(utils::collection::from_hex_string(data, val)); + EXPECT_EQ(4U, val.size()); + } + + { + auto data = "ACDEF"; + std::vector val{}; + EXPECT_TRUE(utils::collection::from_hex_string(data, val)); + EXPECT_EQ(3U, val.size()); + } + + { + auto data = " ACDEF "; + std::vector val{}; + EXPECT_TRUE(utils::collection::from_hex_string(data, val)); + EXPECT_EQ(3U, val.size()); + } + + { + auto data = ""; + std::vector val{}; + EXPECT_TRUE(utils::collection::from_hex_string(data, val)); + EXPECT_TRUE(val.empty()); + } + + { + auto data = L"0xABCDEF10"; + std::vector val{}; + EXPECT_TRUE(utils::collection::from_hex_string(data, val)); + EXPECT_EQ(4U, val.size()); + } + + { + auto data = L" 0xABCDEF10 "; + std::vector val{}; + EXPECT_TRUE(utils::collection::from_hex_string(data, val)); + EXPECT_EQ(4U, val.size()); + } + + { + auto data = L"ABCDEF10"; + std::vector val{}; + EXPECT_TRUE(utils::collection::from_hex_string(data, val)); + EXPECT_EQ(4U, val.size()); + } + + { + auto data = L"ACDEF"; + std::vector val{}; + EXPECT_TRUE(utils::collection::from_hex_string(data, val)); + EXPECT_EQ(3U, val.size()); + } + + { + auto data = L" ACDEF "; + std::vector val{}; + EXPECT_TRUE(utils::collection::from_hex_string(data, val)); + EXPECT_EQ(3U, val.size()); + } + + { + auto data = L""; + std::vector 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 val{}; + EXPECT_FALSE(utils::collection::from_hex_string(data, val)); + EXPECT_TRUE(val.empty()); + } + + { + auto data = "ABC DEF1"; + std::vector val{}; + EXPECT_FALSE(utils::collection::from_hex_string(data, val)); + EXPECT_TRUE(val.empty()); + } + + { + auto data = "0x"; + std::vector val{}; + EXPECT_FALSE(utils::collection::from_hex_string(data, val)); + EXPECT_TRUE(val.empty()); + } + + { + auto data = " 0x "; + std::vector val{}; + EXPECT_FALSE(utils::collection::from_hex_string(data, val)); + EXPECT_TRUE(val.empty()); + } + + { + auto data = L"ABCDEF1Z"; + std::vector val{}; + EXPECT_FALSE(utils::collection::from_hex_string(data, val)); + EXPECT_TRUE(val.empty()); + } + + { + auto data = L"ABC DEF1"; + std::vector val{}; + EXPECT_FALSE(utils::collection::from_hex_string(data, val)); + EXPECT_TRUE(val.empty()); + } + + { + auto data = L"0x"; + std::vector val{}; + EXPECT_FALSE(utils::collection::from_hex_string(data, val)); + EXPECT_TRUE(val.empty()); + } + + { + auto data = L" 0x"; + std::vector val{}; + EXPECT_FALSE(utils::collection::from_hex_string(data, val)); + EXPECT_TRUE(val.empty()); + } +} + +TEST(utils_collection, to_hex_string) { + { + std::array col{ + static_cast(0xFF), + static_cast(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 col{ + static_cast(0xFF), + static_cast(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 col{ + static_cast(0xFF), + static_cast(0xEE), + }; + + utils::collection::remove_element(col, 0xFF); + EXPECT_EQ(1U, col.size()); + EXPECT_EQ(static_cast(0xEE), col.at(0U)); + } + + { + std::vector col{ + static_cast(0xFF), + static_cast(0xEE), + }; + + utils::collection::remove_element(col, 0xEE); + EXPECT_EQ(1U, col.size()); + EXPECT_EQ(static_cast(0xFF), col.at(0U)); + } + + { + std::vector col{ + static_cast(0xFF), + static_cast(0xEE), + }; + + utils::collection::remove_element(col, 0xEF); + EXPECT_EQ(2U, col.size()); + EXPECT_EQ(static_cast(0xFF), col.at(0U)); + EXPECT_EQ(static_cast(0xEE), col.at(1U)); + } +} +} // namespace repertory diff --git a/support/test/src/utils/encrypting_reader_test.cpp b/support/test/src/utils/encrypting_reader_test.cpp index e273abba..078dba8a 100644 --- a/support/test/src/utils/encrypting_reader_test.cpp +++ b/support/test/src/utils/encrypting_reader_test.cpp @@ -22,6 +22,7 @@ #include "test.hpp" #if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) + namespace repertory { TEST(utils_encrypting_reader, read_file_data) { const auto token = std::string("moose"); diff --git a/support/test/src/utils/encryption_test.cpp b/support/test/src/utils/encryption_test.cpp index de1a933c..6a0e1913 100644 --- a/support/test/src/utils/encryption_test.cpp +++ b/support/test/src/utils/encryption_test.cpp @@ -24,8 +24,8 @@ #if defined(PROJECT_ENABLE_LIBSODIUM) namespace repertory { -static const std::string token{"moose"}; -static const std::wstring token_w{L"moose"}; +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 = @@ -244,6 +244,39 @@ TEST(utils_encryption, decryption_failure) { 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) { + stop_type stop_requested{false}; + utils::encryption::encrypting_reader reader( + "test.dat", source_file.get_path(), 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) { + stop_type stop_requested{false}; + utils::encryption::encrypting_reader reader( + "test.dat", source_file.get_path(), 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 repertory diff --git a/support/test/src/utils/file_test.cpp b/support/test/src/utils/file_test.cpp index e380b543..fa48df10 100644 --- a/support/test/src/utils/file_test.cpp +++ b/support/test/src/utils/file_test.cpp @@ -140,4 +140,125 @@ TEST(utils_file, read_and_write_json_file_encrypted) { } #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) { + auto path = "//server/share"; + 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) { + 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) { + 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) { + auto path1 = "//server/share"; + 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)); +} + +TEST(utils_file, smb_parent_is_not_same) { + auto path1 = "server/share"; + 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)); + + path1 = "// server/cow"; + path2 = "// server/cow"; + EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2)); +} +#endif // defined(PROJECT_ENABLE_LIBDSM) } // namespace repertory