diff --git a/CHANGELOG.md b/CHANGELOG.md index c78efb4e..58f0bad0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * Remote mounts must be upgraded to v2.1.0+ to support new authentication scheme * Protocol handshake added for DoS protection + ### Issues * \#12 [unit test] Complete all providers unit tests @@ -43,6 +44,8 @@ * Enhanced remote mount client thread mapping * Threads are now mapped 1-1 from client to server instead of being tied to a fixed-size thread pool +--- + ## v2.0.7-release @@ -51,6 +54,8 @@ * \#55 [bug] UI is unable to launch `repertory.exe` on Windows when absolute path contains spaces * \#57 [bug] Directory entries . and .. are incorrectly being reported as files in Linux remote mounts +--- + ## v2.0.6-release @@ -75,6 +80,8 @@ * Migrated to v2 error handling * Upgraded WinFSP to v2.1 (2025) +--- + ## v2.0.5-rc @@ -92,6 +99,8 @@ * Renamed setting `ApiAuth` to `ApiPassword` * Require `--name,-na` option for encryption provider +--- + ## v2.0.4-rc ### BREAKING CHANGES @@ -119,6 +128,8 @@ * Refactored `app_config` unit tests * Refactored polling to be more accurate on scheduling tasks +--- + ## v2.0.3-rc @@ -143,6 +154,8 @@ * Updated build system to MinGW-w64 12.0.0 * Updated copyright to 2018-2025 +--- + ## v2.0.2-rc @@ -175,6 +188,8 @@ * Corrected handling of `chown()` and `chmod()` * Fixed erroneous download of chunks after resize +--- + ## v2.0.1-rc @@ -197,6 +212,8 @@ * Updated `curl` to v8.4.0 * Updated `libsodium` to v1.0.19 +--- + ## v2.0.0-rc diff --git a/CMakeLists.txt b/CMakeLists.txt index 225ee213..b382cef6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,7 +120,7 @@ if(PROJECT_BUILD) @ONLY ) - if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}/${PROJECT_NAME}.iss.in") + if (PROJECT_IS_MINGW AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}/${PROJECT_NAME}.iss.in") configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}/${PROJECT_NAME}.iss.in ${PROJECT_DIST_DIR}/../${PROJECT_NAME}.iss @@ -128,6 +128,14 @@ if(PROJECT_BUILD) ) endif() + if (PROJECT_IS_DARWIN AND EXISTS "${CMAKE_SOURCE_DIR}/${PROJECT_NAME}/Info.plist.in") + configure_file( + ${CMAKE_SOURCE_DIR}/${PROJECT_NAME}/Info.plist.in + ${CMAKE_SOURCE_DIR}/${PROJECT_NAME}/Info.plist + @ONLY + ) + endif() + find_package(ICU REQUIRED COMPONENTS data i18n io uc) else() message(STATUS "-=[CMake Settings]=-") diff --git a/project.cmake b/project.cmake index 88e2b90d..cc4085d8 100644 --- a/project.cmake +++ b/project.cmake @@ -1,17 +1,9 @@ set(CMAKE_CXX_FLAGS "-include common.hpp ${CMAKE_CXX_FLAGS}") -if (PROJECT_IS_DARWIN) - configure_file( - ${CMAKE_SOURCE_DIR}/${PROJECT_NAME}/Info.plist.in - ${CMAKE_SOURCE_DIR}/${PROJECT_NAME}/Info.plist - @ONLY - ) -endif() - add_project_library(lib${PROJECT_NAME} "" "" "${PROJECT_ADDITIONAL_SOURCES}") add_project_executable(${PROJECT_NAME} lib${PROJECT_NAME} lib${PROJECT_NAME}) -if (PROJECT_IS_DARWIN) +if (PROJECT_IS_DARWIN AND EXISTS "${CMAKE_SOURCE_DIR}/${PROJECT_NAME}/Info.plist") set_target_properties(${PROJECT_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}/Info.plist ) diff --git a/repertory/librepertory/include/file_manager/open_file_base.hpp b/repertory/librepertory/include/file_manager/open_file_base.hpp index f306b04b..6ee82b47 100644 --- a/repertory/librepertory/include/file_manager/open_file_base.hpp +++ b/repertory/librepertory/include/file_manager/open_file_base.hpp @@ -129,21 +129,15 @@ protected: [[nodiscard]] auto do_io(std::function action) -> api_error; [[nodiscard]] auto get_active_downloads() - -> std::unordered_map> & { - return active_downloads_; - } + -> std::unordered_map> &; - [[nodiscard]] auto get_mutex() const -> std::recursive_mutex & { - return file_mtx_; - } + [[nodiscard]] auto get_mutex() const -> std::recursive_mutex &; [[nodiscard]] auto get_last_chunk_size() const -> std::size_t; - [[nodiscard]] auto get_provider() -> i_provider & { return provider_; } + [[nodiscard]] auto get_provider() -> i_provider &; - [[nodiscard]] auto get_provider() const -> const i_provider & { - return provider_; - } + [[nodiscard]] auto get_provider() const -> const i_provider &; [[nodiscard]] auto is_removed() const -> bool; @@ -208,9 +202,7 @@ public: [[nodiscard]] auto has_handle(std::uint64_t handle) const -> bool override; - [[nodiscard]] auto is_directory() const -> bool override { - return fsi_.directory; - } + [[nodiscard]] auto is_directory() const -> bool override; [[nodiscard]] auto is_unlinked() const -> bool override; diff --git a/repertory/librepertory/src/comm/packet/packet_server.cpp b/repertory/librepertory/src/comm/packet/packet_server.cpp index be0061fc..1d067cb1 100644 --- a/repertory/librepertory/src/comm/packet/packet_server.cpp +++ b/repertory/librepertory/src/comm/packet/packet_server.cpp @@ -122,64 +122,62 @@ auto packet_server::handshake(std::shared_ptr conn) const -> bool { conn->socket, boost::asio::buffer(boost::asio::buffer(buffer))); write_timeout.disable(); - if (bytes_written == buffer.size()) { - conn->buffer.resize(to_read); + if (bytes_written != buffer.size()) { + throw std::runtime_error("failed to send handshake"); + } + conn->buffer.resize(to_read); - utils::timeout read_timeout( - timeout_handler, - std::chrono::milliseconds(server_handshake_timeout_ms)); + utils::timeout read_timeout( + timeout_handler, + std::chrono::milliseconds(server_handshake_timeout_ms)); - std::uint32_t total_read{}; - while ((total_read < to_read) && conn->socket.is_open()) { - auto bytes_read = boost::asio::read( - conn->socket, - boost::asio::buffer(&conn->buffer[total_read], - conn->buffer.size() - total_read)); - if (bytes_read == 0) { - throw std::runtime_error("0 bytes read"); - } - - total_read += static_cast(bytes_read); - } - read_timeout.disable(); - - if (total_read == to_read) { - packet response(conn->buffer); - if (response.decrypt(encryption_token_) == 0) { - std::uint32_t client_version{}; - if (response.decode(client_version) == 0) { - std::uint32_t client_version_check{}; - if (response.decode(client_version_check) == 0) { - if (~client_version != client_version_check) { - throw std::runtime_error("client version check failed"); - } - - std::string nonce; - if (response.decode(nonce) == 0) { - if (nonce == conn->nonce) { - conn->generate_nonce(); - return true; - } - - throw std::runtime_error("nonce mismatch"); - } - - throw std::runtime_error("invalid nonce"); - } - - throw std::runtime_error("invalid client version"); - } - - throw std::runtime_error("invalid client version"); - } - - throw std::runtime_error("decryption failed"); + std::uint32_t total_read{}; + while ((total_read < to_read) && conn->socket.is_open()) { + auto bytes_read = boost::asio::read( + conn->socket, boost::asio::buffer(&conn->buffer[total_read], + conn->buffer.size() - total_read)); + if (bytes_read == 0) { + throw std::runtime_error("0 bytes read"); } + total_read += static_cast(bytes_read); + } + read_timeout.disable(); + + if (total_read != to_read) { throw std::runtime_error("invalid handshake"); } - throw std::runtime_error("failed to send handshake"); + packet response(conn->buffer); + if (response.decrypt(encryption_token_) != 0) { + throw std::runtime_error("decryption failed"); + } + + std::uint32_t client_version{}; + if (response.decode(client_version) != 0) { + throw std::runtime_error("invalid client version"); + } + + std::uint32_t client_version_check{}; + if (response.decode(client_version_check) != 0) { + throw std::runtime_error("invalid client version"); + } + + if (~client_version != client_version_check) { + throw std::runtime_error("client version check failed"); + } + + std::string nonce; + if (response.decode(nonce) != 0) { + throw std::runtime_error("invalid nonce"); + } + + if (nonce != conn->nonce) { + throw std::runtime_error("nonce mismatch"); + } + + conn->generate_nonce(); + return true; } catch (const std::exception &e) { repertory::utils::error::raise_error(function_name, e, "handlshake failed"); } @@ -303,57 +301,59 @@ void packet_server::read_packet(std::shared_ptr conn, packet::error_type ret{}; auto request = std::make_shared(conn->buffer); - if (request->decrypt(encryption_token_) == 0) { - std::string nonce; - ret = request->decode(nonce); - if (ret == 0) { - if (nonce != conn->nonce) { - throw std::runtime_error("nonce mismatch"); - } - conn->generate_nonce(); - - std::string version; - ret = request->decode(version); - if (ret == 0) { - if (utils::compare_version_strings( - version, std::string{REPERTORY_MIN_REMOTE_VERSION}) >= 0) { - std::uint32_t service_flags{}; - DECODE_OR_IGNORE(request, service_flags); - - std::string client_id; - DECODE_OR_IGNORE(request, client_id); - - std::uint64_t thread_id{}; - DECODE_OR_IGNORE(request, thread_id); - - std::string method; - DECODE_OR_IGNORE(request, method); - - if (ret == 0) { - if (conn->client_id.empty()) { - add_client(*conn, client_id); - } - - should_send_response = false; - message_handler_(service_flags, client_id, thread_id, method, - request.get(), *response, - [this, conn, request, - response](const packet::error_type &result) { - this->send_response(conn, result, *response); - }); - } - } else { - ret = utils::from_api_error(api_error::incompatible_version); - } - } else { - ret = utils::from_api_error(api_error::invalid_version); - } - } else { - throw std::runtime_error("invalid nonce"); - } - } else { + if (request->decrypt(encryption_token_) != 0) { throw std::runtime_error("decryption failed"); } + + std::string nonce; + ret = request->decode(nonce); + if (ret != 0) { + throw std::runtime_error("invalid nonce"); + } + + if (nonce != conn->nonce) { + throw std::runtime_error("nonce mismatch"); + } + + conn->generate_nonce(); + + std::string version; + ret = request->decode(version); + if (ret == 0) { + if (utils::compare_version_strings( + version, std::string{REPERTORY_MIN_REMOTE_VERSION}) >= 0) { + std::uint32_t service_flags{}; + DECODE_OR_IGNORE(request, service_flags); + + std::string client_id; + DECODE_OR_IGNORE(request, client_id); + + std::uint64_t thread_id{}; + DECODE_OR_IGNORE(request, thread_id); + + std::string method; + DECODE_OR_IGNORE(request, method); + + if (ret == 0) { + if (conn->client_id.empty()) { + add_client(*conn, client_id); + } + + should_send_response = false; + message_handler_(service_flags, client_id, thread_id, method, + request.get(), *response, + [this, conn, request, + response](const packet::error_type &result) { + this->send_response(conn, result, *response); + }); + } + } else { + ret = utils::from_api_error(api_error::incompatible_version); + } + } else { + ret = utils::from_api_error(api_error::invalid_version); + } + if (should_send_response) { send_response(conn, ret, *response); } diff --git a/repertory/librepertory/src/file_manager/open_file_base.cpp b/repertory/librepertory/src/file_manager/open_file_base.cpp index 822641cf..a8b368aa 100644 --- a/repertory/librepertory/src/file_manager/open_file_base.cpp +++ b/repertory/librepertory/src/file_manager/open_file_base.cpp @@ -206,6 +206,11 @@ void open_file_base::file_io_thread() { process_queue(); } +auto open_file_base::get_active_downloads() + -> std::unordered_map> & { + return active_downloads_; +} + auto open_file_base::get_api_error() const -> api_error { mutex_lock error_lock(error_mtx_); return error_; @@ -221,11 +226,23 @@ auto open_file_base::get_file_size() const -> std::uint64_t { return fsi_.size; } -[[nodiscard]] auto open_file_base::get_last_chunk_size() const -> std::size_t { +auto open_file_base::get_last_chunk_size() const -> std::size_t { recur_mutex_lock file_lock(file_mtx_); return last_chunk_size_; } +auto open_file_base::get_mutex() const -> std::recursive_mutex & { + return file_mtx_; +} + +auto open_file_base::get_provider() -> i_provider & { return provider_; } + +auto open_file_base::get_provider() const -> const i_provider & { + return provider_; +} + +auto open_file_base::is_directory() const -> bool { return fsi_.directory; } + void open_file_base::set_file_size(std::uint64_t size) { recur_mutex_lock file_lock(file_mtx_); fsi_.size = size;