This commit is contained in:
		@@ -5,31 +5,43 @@ set(CMAKE_CXX_STANDARD 23)
 | 
				
			|||||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
 | 
					set(CMAKE_CXX_STANDARD_REQUIRED YES)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
include(CPM.cmake)
 | 
					include(CPM.cmake)
 | 
				
			||||||
CPMAddPackage("gh:richgel999/miniz#3.0.2")
 | 
					 | 
				
			||||||
CPMAddPackage("gh:gabime/spdlog#v1.13.0")
 | 
					CPMAddPackage("gh:gabime/spdlog#v1.13.0")
 | 
				
			||||||
CPMAddPackage(
 | 
					
 | 
				
			||||||
    NAME restbed
 | 
					set(BOOST_LIBS
 | 
				
			||||||
    VERSION 4.8
 | 
					    beast asio assert bind config container core endian
 | 
				
			||||||
    GITHUB_REPOSITORY Corvusoft/restbed
 | 
					    intrusive logic mp11 optional smart_ptr static_assert static_string
 | 
				
			||||||
    GIT_TAG 4.8
 | 
					    system throw_exception type_traits utility winapi
 | 
				
			||||||
    DOWNLOAD_ONLY YES
 | 
					    align context coroutine date_time move container_hash detail predef
 | 
				
			||||||
 | 
					    variant2 io preprocessor pool exception algorithm lexical_cast numeric_conversion
 | 
				
			||||||
 | 
					    range tokenizer describe integer tuple array concept_check
 | 
				
			||||||
 | 
					    function regex unordered iterator mpl conversion function_types fusion typeof functional
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					foreach(BOOST_LIB ${BOOST_LIBS})
 | 
				
			||||||
 | 
					    CPMAddPackage("gh:boostorg/${BOOST_LIB}#boost-1.85.0")
 | 
				
			||||||
 | 
					endforeach()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CPMAddPackage(
 | 
					CPMAddPackage(
 | 
				
			||||||
    NAME asio
 | 
					    NAME uring
 | 
				
			||||||
    VERSION 1.29.0
 | 
					    VERSION 2.5
 | 
				
			||||||
    GITHUB_REPOSITORY chriskohlhoff/asio
 | 
					    GITHUB_REPOSITORY axboe/liburing
 | 
				
			||||||
    GIT_TAG asio-1-29-0
 | 
					    GIT_TAG liburing-2.5
 | 
				
			||||||
    DOWNLOAD_ONLY YES
 | 
					    DOWNLOAD_ONLY YES
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if(asio_ADDED AND restbed_ADDED)
 | 
					if(uring_ADDED)
 | 
				
			||||||
    file(GLOB_RECURSE restbed_SOURCE "${restbed_SOURCE_DIR}/source/corvusoft/restbed/*.cpp")
 | 
					    add_library(uring STATIC
 | 
				
			||||||
    add_library(restbed-static STATIC ${restbed_SOURCE} "${asio_SOURCE_DIR}/asio/src/asio.cpp")
 | 
					        ${uring_SOURCE_DIR}/src/setup.c
 | 
				
			||||||
    target_compile_definitions(restbed-static PUBLIC ASIO_SEPARATE_COMPILATION)
 | 
					        ${uring_SOURCE_DIR}/src/queue.c
 | 
				
			||||||
    target_include_directories(restbed-static PUBLIC "${restbed_SOURCE_DIR}/source" "${asio_SOURCE_DIR}/asio/include")
 | 
					        ${uring_SOURCE_DIR}/src/register.c
 | 
				
			||||||
 | 
					        ${uring_SOURCE_DIR}/src/syscall.c
 | 
				
			||||||
 | 
					        ${uring_SOURCE_DIR}/src/version.c
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    target_include_directories(uring PUBLIC ${uring_SOURCE_DIR}/src/include)
 | 
				
			||||||
 | 
					    target_compile_definitions(uring PUBLIC BOOST_ASIO_HAS_IO_URING PRIVATE LIBURING_INTERNAL _LARGEFILE_SOURCE _FILE_OFFSET_BITS=64 _GNU_SOURCE)
 | 
				
			||||||
endif()
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
set(BOTAN_MODULES argon2fmt hotp base32 auto_rng system_rng tls13 certstor_system certstor_flatfile md5)
 | 
					set(BOTAN_MODULES argon2fmt hotp base32 auto_rng system_rng tls13 certstor_system certstor_flatfile md5 asio)
 | 
				
			||||||
CPMAddPackage(
 | 
					CPMAddPackage(
 | 
				
			||||||
    NAME botan
 | 
					    NAME botan
 | 
				
			||||||
    VERSION 3.4.0
 | 
					    VERSION 3.4.0
 | 
				
			||||||
@@ -48,25 +60,21 @@ if(botan_ADDED)
 | 
				
			|||||||
            --amalgamation
 | 
					            --amalgamation
 | 
				
			||||||
            --minimized-build
 | 
					            --minimized-build
 | 
				
			||||||
            --without-documentation
 | 
					            --without-documentation
 | 
				
			||||||
 | 
					            --with-boost
 | 
				
			||||||
            --enable-modules=${BOTAN_MODULES_STR}
 | 
					            --enable-modules=${BOTAN_MODULES_STR}
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    add_library(botan STATIC ${CMAKE_CURRENT_BINARY_DIR}/botan_all.cpp ${CMAKE_CURRENT_BINARY_DIR}/botan_all.h)
 | 
					    add_library(botan STATIC ${CMAKE_CURRENT_BINARY_DIR}/botan_all.cpp ${CMAKE_CURRENT_BINARY_DIR}/botan_all.h)
 | 
				
			||||||
endif()
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#FetchContent_Declare(botan GIT_REPOSITORY https://gitea.mattv.de/root/cmake-libraries.git GIT_TAG origin/botan)
 | 
					 | 
				
			||||||
#FetchContent_MakeAvailable(botan)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#add_subdirectory(lib EXCLUDE_FROM_ALL)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
find_package(Threads REQUIRED)
 | 
					find_package(Threads REQUIRED)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_custom_command(
 | 
					#add_custom_command(
 | 
				
			||||||
    COMMAND ./mrpc ARGS -n src/server/mrpc/fileserver -s cpp fileserver.rs
 | 
					#    COMMAND ./mrpc ARGS -n src/server/mrpc/fileserver -s cpp fileserver.rs
 | 
				
			||||||
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
 | 
					#    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
 | 
				
			||||||
    MAIN_DEPENDENCY fileserver.rs
 | 
					#    MAIN_DEPENDENCY fileserver.rs
 | 
				
			||||||
    OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/server/mrpc/fileserver.cxx ${CMAKE_CURRENT_SOURCE_DIR}/src/server/mrpc/fileserver.hxx
 | 
					#    OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/server/mrpc/fileserver.cxx ${CMAKE_CURRENT_SOURCE_DIR}/src/server/mrpc/fileserver.hxx
 | 
				
			||||||
)
 | 
					#)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_custom_command(
 | 
					add_custom_command(
 | 
				
			||||||
    COMMAND sh ARGS -c 'xxd -i -n index_html ${CMAKE_CURRENT_SOURCE_DIR}/frontend/dist/index.html > index_html.h'
 | 
					    COMMAND sh ARGS -c 'xxd -i -n index_html ${CMAKE_CURRENT_SOURCE_DIR}/frontend/dist/index.html > index_html.h'
 | 
				
			||||||
@@ -84,10 +92,12 @@ add_executable(fileserver
 | 
				
			|||||||
    src/server/mrpc/fileserver.hxx
 | 
					    src/server/mrpc/fileserver.hxx
 | 
				
			||||||
    src/server/mrpc/fileserver.cxx
 | 
					    src/server/mrpc/fileserver.cxx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    src/util/logging.hxx
 | 
					 | 
				
			||||||
    src/util/crash.hxx
 | 
					    src/util/crash.hxx
 | 
				
			||||||
    src/util/timed_mutex.hxx
 | 
					 | 
				
			||||||
    src/util/stb.cxx
 | 
					    src/util/stb.cxx
 | 
				
			||||||
 | 
					    src/util/miniz.hxx
 | 
				
			||||||
 | 
					    src/util/miniz.cxx
 | 
				
			||||||
 | 
					    src/util/boost.hxx
 | 
				
			||||||
 | 
					    src/util/botan.hxx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    src/data/data.hxx
 | 
					    src/data/data.hxx
 | 
				
			||||||
    src/data/data_internal.hxx
 | 
					    src/data/data_internal.hxx
 | 
				
			||||||
@@ -114,13 +124,13 @@ add_executable(fileserver
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
target_include_directories(fileserver PRIVATE include ${CMAKE_CURRENT_BINARY_DIR})
 | 
					target_include_directories(fileserver PRIVATE include ${CMAKE_CURRENT_BINARY_DIR})
 | 
				
			||||||
target_compile_options(fileserver PRIVATE -msse2)
 | 
					target_compile_options(fileserver PRIVATE -msse2)
 | 
				
			||||||
 | 
					target_compile_definitions(fileserver PRIVATE BOOST_BEAST_FILE_BUFFER_SIZE=65535)
 | 
				
			||||||
target_link_options(fileserver PRIVATE -static)
 | 
					target_link_options(fileserver PRIVATE -static)
 | 
				
			||||||
target_link_libraries(fileserver PRIVATE
 | 
					target_link_libraries(fileserver PRIVATE
 | 
				
			||||||
    spdlog::spdlog
 | 
					    spdlog::spdlog
 | 
				
			||||||
    restbed-static
 | 
					    Boost::beast
 | 
				
			||||||
    #Botan::Botan
 | 
					 | 
				
			||||||
    botan
 | 
					    botan
 | 
				
			||||||
    miniz
 | 
					    uring
 | 
				
			||||||
    Threads::Threads
 | 
					    Threads::Threads
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,8 +8,7 @@
 | 
				
			|||||||
    let username = '', key = '', password = '', password2 = '';
 | 
					    let username = '', key = '', password = '', password2 = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async function sendKey() {
 | 
					    async function sendKey() {
 | 
				
			||||||
        if (await workingWrapper(() => rpc.Auth_send_recovery_key(username)) == null)
 | 
					        await workingWrapper(() => rpc.Auth_send_recovery_key(username));
 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        info_banner.set('A message has been sent');
 | 
					        info_banner.set('A message has been sent');
 | 
				
			||||||
        enter_key = true;
 | 
					        enter_key = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,300 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
* Helpers for TLS ASIO Stream
 | 
					 | 
				
			||||||
* (C) 2018-2020 Jack Lloyd
 | 
					 | 
				
			||||||
*     2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
 | 
					 | 
				
			||||||
*
 | 
					 | 
				
			||||||
* Botan is released under the Simplified BSD License (see license.txt)
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef BOTAN_ASIO_ASYNC_OPS_H_
 | 
					 | 
				
			||||||
#define BOTAN_ASIO_ASYNC_OPS_H_
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <botan_all.h>
 | 
					 | 
				
			||||||
#include <asio.hpp>
 | 
					 | 
				
			||||||
#include <asio/yield.hpp>
 | 
					 | 
				
			||||||
#include "asio_error.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Botan::TLS::detail {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Base class for asynchronous stream operations.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Asynchronous operations, used for example to implement an interface for boost::asio::async_read_some and
 | 
					 | 
				
			||||||
 * boost::asio::async_write_some, are based on boost::asio::coroutines.
 | 
					 | 
				
			||||||
 * Derived operations should implement a call operator and invoke it with the correct parameters upon construction. The
 | 
					 | 
				
			||||||
 * call operator needs to make sure that the user-provided handler is not called directly. Typically, yield / reenter is
 | 
					 | 
				
			||||||
 * used for this in the following fashion:
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * ```
 | 
					 | 
				
			||||||
 * void operator()(boost::system::error_code ec, std::size_t bytes_transferred, bool isContinuation = true)
 | 
					 | 
				
			||||||
 *    {
 | 
					 | 
				
			||||||
 *    reenter(this)
 | 
					 | 
				
			||||||
 *       {
 | 
					 | 
				
			||||||
 *       // operation specific logic, repeatedly interacting with the stream_core and the next_layer (socket)
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 *       // make sure intermediate initiating function is called
 | 
					 | 
				
			||||||
 *       if(!isContinuation)
 | 
					 | 
				
			||||||
 *          {
 | 
					 | 
				
			||||||
 *          yield next_layer.async_operation(empty_buffer, this);
 | 
					 | 
				
			||||||
 *          }
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 *       // call the completion handler
 | 
					 | 
				
			||||||
 *       complete_now(error_code, bytes_transferred);
 | 
					 | 
				
			||||||
 *       }
 | 
					 | 
				
			||||||
 *    }
 | 
					 | 
				
			||||||
 * ```
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Once the operation is completed and ready to call the completion handler it checks if an intermediate initiating
 | 
					 | 
				
			||||||
 * function has been called using the `isContinuation` parameter. If not, it will call an asynchronous operation, such
 | 
					 | 
				
			||||||
 * as `async_read_some`, with and empty buffer, set the object itself as the handler, and `yield`. As a result, the call
 | 
					 | 
				
			||||||
 * operator will be invoked again, this time as a continuation, and will jump to the location where it yielded before
 | 
					 | 
				
			||||||
 * using `reenter`. It is now safe to call the handler function via `complete_now`.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * \tparam Handler Type of the completion handler
 | 
					 | 
				
			||||||
 * \tparam Executor1 Type of the asio executor (usually derived from the lower layer)
 | 
					 | 
				
			||||||
 * \tparam Allocator Type of the allocator to be used
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
template <class Handler, class Executor1, class Allocator>
 | 
					 | 
				
			||||||
class AsyncBase : public boost::asio::coroutine {
 | 
					 | 
				
			||||||
   public:
 | 
					 | 
				
			||||||
      using allocator_type = boost::asio::associated_allocator_t<Handler, Allocator>;
 | 
					 | 
				
			||||||
      using executor_type = boost::asio::associated_executor_t<Handler, Executor1>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      allocator_type get_allocator() const noexcept { return boost::asio::get_associated_allocator(m_handler); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      executor_type get_executor() const noexcept {
 | 
					 | 
				
			||||||
         return boost::asio::get_associated_executor(m_handler, m_work_guard_1.get_executor());
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   protected:
 | 
					 | 
				
			||||||
      template <class HandlerT>
 | 
					 | 
				
			||||||
      AsyncBase(HandlerT&& handler, const Executor1& executor) :
 | 
					 | 
				
			||||||
            m_handler(std::forward<HandlerT>(handler)), m_work_guard_1(executor) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * Call the completion handler.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * This function should only be called after an intermediate initiating function has been called.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param args Arguments forwarded to the completion handler function.
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      template <class... Args>
 | 
					 | 
				
			||||||
      void complete_now(Args&&... args) {
 | 
					 | 
				
			||||||
         m_work_guard_1.reset();
 | 
					 | 
				
			||||||
         m_handler(std::forward<Args>(args)...);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      Handler m_handler;
 | 
					 | 
				
			||||||
      boost::asio::executor_work_guard<Executor1> m_work_guard_1;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template <class Handler, class Stream, class MutableBufferSequence, class Allocator = std::allocator<void>>
 | 
					 | 
				
			||||||
class AsyncReadOperation : public AsyncBase<Handler, typename Stream::executor_type, Allocator> {
 | 
					 | 
				
			||||||
   public:
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * Construct and invoke an AsyncReadOperation.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param handler Handler function to be called upon completion.
 | 
					 | 
				
			||||||
       * @param stream The stream from which the data will be read
 | 
					 | 
				
			||||||
       * @param buffers The buffers into which the data will be read.
 | 
					 | 
				
			||||||
       * @param ec Optional error code; used to report an error to the handler function.
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      template <class HandlerT>
 | 
					 | 
				
			||||||
      AsyncReadOperation(HandlerT&& handler,
 | 
					 | 
				
			||||||
                         Stream& stream,
 | 
					 | 
				
			||||||
                         const MutableBufferSequence& buffers,
 | 
					 | 
				
			||||||
                         const boost::system::error_code& ec = {}) :
 | 
					 | 
				
			||||||
            AsyncBase<Handler, typename Stream::executor_type, Allocator>(std::forward<HandlerT>(handler),
 | 
					 | 
				
			||||||
                                                                          stream.get_executor()),
 | 
					 | 
				
			||||||
            m_stream(stream),
 | 
					 | 
				
			||||||
            m_buffers(buffers),
 | 
					 | 
				
			||||||
            m_decodedBytes(0) {
 | 
					 | 
				
			||||||
         this->operator()(ec, std::size_t(0), false);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      AsyncReadOperation(AsyncReadOperation&&) = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      void operator()(boost::system::error_code ec, std::size_t bytes_transferred, bool isContinuation = true) {
 | 
					 | 
				
			||||||
         reenter(this) {
 | 
					 | 
				
			||||||
            if(bytes_transferred > 0 && !ec) {
 | 
					 | 
				
			||||||
               // We have received encrypted data from the network, now hand it to TLS::Channel for decryption.
 | 
					 | 
				
			||||||
               boost::asio::const_buffer read_buffer{m_stream.input_buffer().data(), bytes_transferred};
 | 
					 | 
				
			||||||
               m_stream.process_encrypted_data(read_buffer, ec);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(m_stream.shutdown_received()) {
 | 
					 | 
				
			||||||
               // we just received a 'close_notify' from the peer and don't expect any more data
 | 
					 | 
				
			||||||
               ec = boost::asio::error::eof;
 | 
					 | 
				
			||||||
            } else if(ec == boost::asio::error::eof) {
 | 
					 | 
				
			||||||
               // we did not expect this disconnection from the peer
 | 
					 | 
				
			||||||
               ec.assign(StreamError::StreamTruncated, std::generic_category());
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(!m_stream.has_received_data() && !ec && boost::asio::buffer_size(m_buffers) > 0) {
 | 
					 | 
				
			||||||
               // The channel did not decrypt a complete record yet, we need more data from the socket.
 | 
					 | 
				
			||||||
               m_stream.next_layer().async_read_some(m_stream.input_buffer(), std::move(*this));
 | 
					 | 
				
			||||||
               return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(m_stream.has_received_data() && !ec) {
 | 
					 | 
				
			||||||
               // The channel has decrypted a TLS record, now copy it to the output buffers.
 | 
					 | 
				
			||||||
               m_decodedBytes = m_stream.copy_received_data(m_buffers);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(!isContinuation) {
 | 
					 | 
				
			||||||
               // Make sure the handler is not called without an intermediate initiating function.
 | 
					 | 
				
			||||||
               // "Reading" into a zero-byte buffer will complete immediately.
 | 
					 | 
				
			||||||
               m_ec = ec;
 | 
					 | 
				
			||||||
               yield m_stream.next_layer().async_read_some(boost::asio::mutable_buffer(), std::move(*this));
 | 
					 | 
				
			||||||
               ec = m_ec;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            this->complete_now(ec, m_decodedBytes);
 | 
					 | 
				
			||||||
         }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   private:
 | 
					 | 
				
			||||||
      Stream& m_stream;
 | 
					 | 
				
			||||||
      MutableBufferSequence m_buffers;
 | 
					 | 
				
			||||||
      std::size_t m_decodedBytes;
 | 
					 | 
				
			||||||
      boost::system::error_code m_ec;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template <typename Handler, class Stream, class Allocator = std::allocator<void>>
 | 
					 | 
				
			||||||
class AsyncWriteOperation : public AsyncBase<Handler, typename Stream::executor_type, Allocator> {
 | 
					 | 
				
			||||||
   public:
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * Construct and invoke an AsyncWriteOperation.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param handler Handler function to be called upon completion.
 | 
					 | 
				
			||||||
       * @param stream The stream from which the data will be read
 | 
					 | 
				
			||||||
       * @param plainBytesTransferred Number of bytes to be reported to the user-provided handler function as
 | 
					 | 
				
			||||||
       *                              bytes_transferred. This needs to be provided since the amount of plaintext data
 | 
					 | 
				
			||||||
       *                              consumed from the input buffer can differ from the amount of encrypted data written
 | 
					 | 
				
			||||||
       *                              to the next layer.
 | 
					 | 
				
			||||||
       * @param ec Optional error code; used to report an error to the handler function.
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      template <class HandlerT>
 | 
					 | 
				
			||||||
      AsyncWriteOperation(HandlerT&& handler,
 | 
					 | 
				
			||||||
                          Stream& stream,
 | 
					 | 
				
			||||||
                          std::size_t plainBytesTransferred,
 | 
					 | 
				
			||||||
                          const boost::system::error_code& ec = {}) :
 | 
					 | 
				
			||||||
            AsyncBase<Handler, typename Stream::executor_type, Allocator>(std::forward<HandlerT>(handler),
 | 
					 | 
				
			||||||
                                                                          stream.get_executor()),
 | 
					 | 
				
			||||||
            m_stream(stream),
 | 
					 | 
				
			||||||
            m_plainBytesTransferred(plainBytesTransferred) {
 | 
					 | 
				
			||||||
         this->operator()(ec, std::size_t(0), false);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      AsyncWriteOperation(AsyncWriteOperation&&) = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      void operator()(boost::system::error_code ec, std::size_t bytes_transferred, bool isContinuation = true) {
 | 
					 | 
				
			||||||
         reenter(this) {
 | 
					 | 
				
			||||||
            // mark the number of encrypted bytes sent to the network as "consumed"
 | 
					 | 
				
			||||||
            // Note: bytes_transferred will be zero on first call
 | 
					 | 
				
			||||||
            m_stream.consume_send_buffer(bytes_transferred);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(m_stream.has_data_to_send() && !ec) {
 | 
					 | 
				
			||||||
               m_stream.next_layer().async_write_some(m_stream.send_buffer(), std::move(*this));
 | 
					 | 
				
			||||||
               return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(ec == boost::asio::error::eof && !m_stream.shutdown_received()) {
 | 
					 | 
				
			||||||
               // transport layer was closed by peer without receiving 'close_notify'
 | 
					 | 
				
			||||||
               ec.assign(StreamError::StreamTruncated, std::generic_category());
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(!isContinuation) {
 | 
					 | 
				
			||||||
               // Make sure the handler is not called without an intermediate initiating function.
 | 
					 | 
				
			||||||
               // "Writing" to a zero-byte buffer will complete immediately.
 | 
					 | 
				
			||||||
               m_ec = ec;
 | 
					 | 
				
			||||||
               yield m_stream.next_layer().async_write_some(boost::asio::const_buffer(), std::move(*this));
 | 
					 | 
				
			||||||
               ec = m_ec;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // The size of the sent TLS record can differ from the size of the payload due to TLS encryption. We need to
 | 
					 | 
				
			||||||
            // tell the handler how many bytes of the original data we already processed.
 | 
					 | 
				
			||||||
            this->complete_now(ec, m_plainBytesTransferred);
 | 
					 | 
				
			||||||
         }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   private:
 | 
					 | 
				
			||||||
      Stream& m_stream;
 | 
					 | 
				
			||||||
      std::size_t m_plainBytesTransferred;
 | 
					 | 
				
			||||||
      boost::system::error_code m_ec;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template <class Handler, class Stream, class Allocator = std::allocator<void>>
 | 
					 | 
				
			||||||
class AsyncHandshakeOperation : public AsyncBase<Handler, typename Stream::executor_type, Allocator> {
 | 
					 | 
				
			||||||
   public:
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * Construct and invoke an AsyncHandshakeOperation.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param handler Handler function to be called upon completion.
 | 
					 | 
				
			||||||
       * @param stream The stream from which the data will be read
 | 
					 | 
				
			||||||
       * @param ec Optional error code; used to report an error to the handler function.
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      template <class HandlerT>
 | 
					 | 
				
			||||||
      AsyncHandshakeOperation(HandlerT&& handler, Stream& stream, const boost::system::error_code& ec = {}) :
 | 
					 | 
				
			||||||
            AsyncBase<Handler, typename Stream::executor_type, Allocator>(std::forward<HandlerT>(handler),
 | 
					 | 
				
			||||||
                                                                          stream.get_executor()),
 | 
					 | 
				
			||||||
            m_stream(stream) {
 | 
					 | 
				
			||||||
         this->operator()(ec, std::size_t(0), false);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      AsyncHandshakeOperation(AsyncHandshakeOperation&&) = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      void operator()(boost::system::error_code ec, std::size_t bytesTransferred, bool isContinuation = true) {
 | 
					 | 
				
			||||||
         reenter(this) {
 | 
					 | 
				
			||||||
            if(ec == boost::asio::error::eof) {
 | 
					 | 
				
			||||||
               ec.assign(StreamError::StreamTruncated, std::generic_category());
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(bytesTransferred > 0 && !ec) {
 | 
					 | 
				
			||||||
               // Provide encrypted TLS data received from the network to TLS::Channel for decryption
 | 
					 | 
				
			||||||
               boost::asio::const_buffer read_buffer{m_stream.input_buffer().data(), bytesTransferred};
 | 
					 | 
				
			||||||
               m_stream.process_encrypted_data(read_buffer, ec);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(m_stream.has_data_to_send() && !ec) {
 | 
					 | 
				
			||||||
               // Write encrypted TLS data provided by the TLS::Channel on the wire
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
               // Note: we construct `AsyncWriteOperation` with 0 as its last parameter (`plainBytesTransferred`). This
 | 
					 | 
				
			||||||
               // operation will eventually call `*this` as its own handler, passing the 0 back to this call operator.
 | 
					 | 
				
			||||||
               // This is necessary because the check of `bytesTransferred > 0` assumes that `bytesTransferred` bytes
 | 
					 | 
				
			||||||
               // were just read and are available in input_buffer for further processing.
 | 
					 | 
				
			||||||
               AsyncWriteOperation<AsyncHandshakeOperation<typename std::decay<Handler>::type, Stream, Allocator>,
 | 
					 | 
				
			||||||
                                   Stream,
 | 
					 | 
				
			||||||
                                   Allocator>
 | 
					 | 
				
			||||||
                  op{std::move(*this), m_stream, 0};
 | 
					 | 
				
			||||||
               return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(!m_stream.native_handle()->is_active() && !ec) {
 | 
					 | 
				
			||||||
               // Read more encrypted TLS data from the network
 | 
					 | 
				
			||||||
               m_stream.next_layer().async_read_some(m_stream.input_buffer(), std::move(*this));
 | 
					 | 
				
			||||||
               return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(!isContinuation) {
 | 
					 | 
				
			||||||
               // Make sure the handler is not called without an intermediate initiating function.
 | 
					 | 
				
			||||||
               // "Reading" into a zero-byte buffer will complete immediately.
 | 
					 | 
				
			||||||
               m_ec = ec;
 | 
					 | 
				
			||||||
               yield m_stream.next_layer().async_read_some(boost::asio::mutable_buffer(), std::move(*this));
 | 
					 | 
				
			||||||
               ec = m_ec;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            this->complete_now(ec);
 | 
					 | 
				
			||||||
         }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   private:
 | 
					 | 
				
			||||||
      Stream& m_stream;
 | 
					 | 
				
			||||||
      boost::system::error_code m_ec;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}  // namespace Botan::TLS::detail
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   #include <asio/unyield.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif  // BOTAN_ASIO_ASYNC_OPS_H_
 | 
					 | 
				
			||||||
@@ -1,90 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * TLS Context
 | 
					 | 
				
			||||||
 * (C) 2018-2020 Jack Lloyd
 | 
					 | 
				
			||||||
 *     2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Botan is released under the Simplified BSD License (see license.txt)
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef BOTAN_ASIO_TLS_CONTEXT_H_
 | 
					 | 
				
			||||||
#define BOTAN_ASIO_TLS_CONTEXT_H_
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <botan_all.h>
 | 
					 | 
				
			||||||
#include <functional>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Botan::TLS {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace detail {
 | 
					 | 
				
			||||||
template <typename FunT>
 | 
					 | 
				
			||||||
struct fn_signature_helper : public std::false_type {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template <typename R, typename D, typename... Args>
 | 
					 | 
				
			||||||
struct fn_signature_helper<R (D::*)(Args...)> {
 | 
					 | 
				
			||||||
      using type = std::function<R(Args...)>;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
}  // namespace detail
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * A helper class to initialize and configure Botan::TLS::Stream
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
class Context {
 | 
					 | 
				
			||||||
   public:
 | 
					 | 
				
			||||||
      // statically extract the function signature type from Callbacks::tls_verify_cert_chain
 | 
					 | 
				
			||||||
      // and reuse it as an std::function<> for the verify callback signature
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * The signature of the callback function should correspond to the signature of
 | 
					 | 
				
			||||||
       * Callbacks::tls_verify_cert_chain
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      using Verify_Callback = detail::fn_signature_helper<decltype(&Callbacks::tls_verify_cert_chain)>::type;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      Context(std::shared_ptr<Credentials_Manager> credentials_manager,
 | 
					 | 
				
			||||||
              std::shared_ptr<RandomNumberGenerator> rng,
 | 
					 | 
				
			||||||
              std::shared_ptr<Session_Manager> session_manager,
 | 
					 | 
				
			||||||
              std::shared_ptr<const Policy> policy,
 | 
					 | 
				
			||||||
              Server_Information server_info = Server_Information()) :
 | 
					 | 
				
			||||||
            m_credentials_manager(credentials_manager),
 | 
					 | 
				
			||||||
            m_rng(rng),
 | 
					 | 
				
			||||||
            m_session_manager(session_manager),
 | 
					 | 
				
			||||||
            m_policy(policy),
 | 
					 | 
				
			||||||
            m_server_info(std::move(server_info)) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      virtual ~Context() = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      Context(Context&&) = default;
 | 
					 | 
				
			||||||
      Context(const Context&) = delete;
 | 
					 | 
				
			||||||
      Context& operator=(const Context&) = delete;
 | 
					 | 
				
			||||||
      Context& operator=(Context&&) = delete;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Override the tls_verify_cert_chain callback
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * This changes the verify_callback in the stream's TLS::Context, and hence the tls_verify_cert_chain callback
 | 
					 | 
				
			||||||
       * used in the handshake.
 | 
					 | 
				
			||||||
       * Using this function is equivalent to setting the callback via @see Botan::TLS::Stream::set_verify_callback
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @note This function should only be called before initiating the TLS handshake
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      void set_verify_callback(Verify_Callback callback) { m_verify_callback = std::move(callback); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      bool has_verify_callback() const { return static_cast<bool>(m_verify_callback); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const Verify_Callback& get_verify_callback() const { return m_verify_callback; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      void set_server_info(Server_Information server_info) { m_server_info = std::move(server_info); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   protected:
 | 
					 | 
				
			||||||
      template <class S, class C>
 | 
					 | 
				
			||||||
      friend class Stream;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      std::shared_ptr<Credentials_Manager> m_credentials_manager;
 | 
					 | 
				
			||||||
      std::shared_ptr<RandomNumberGenerator> m_rng;
 | 
					 | 
				
			||||||
      std::shared_ptr<Session_Manager> m_session_manager;
 | 
					 | 
				
			||||||
      std::shared_ptr<const Policy> m_policy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      Server_Information m_server_info;
 | 
					 | 
				
			||||||
      Verify_Callback m_verify_callback;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}  // namespace Botan::TLS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif  // BOTAN_ASIO_TLS_CONTEXT_H_
 | 
					 | 
				
			||||||
@@ -1,125 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
* TLS Stream Errors
 | 
					 | 
				
			||||||
* (C) 2018-2020 Jack Lloyd
 | 
					 | 
				
			||||||
*     2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
 | 
					 | 
				
			||||||
*
 | 
					 | 
				
			||||||
* Botan is released under the Simplified BSD License (see license.txt)
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef BOTAN_ASIO_ERROR_H_
 | 
					 | 
				
			||||||
#define BOTAN_ASIO_ERROR_H_
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <botan_all.h>
 | 
					 | 
				
			||||||
#include <asio/error_code.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace boost{
 | 
					 | 
				
			||||||
    namespace asio = ::asio;
 | 
					 | 
				
			||||||
    namespace system {
 | 
					 | 
				
			||||||
        template <typename T>
 | 
					 | 
				
			||||||
        struct is_error_code_enum { static const bool value = false; };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        typedef asio::error_category error_category;
 | 
					 | 
				
			||||||
        typedef asio::error_code error_code;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    namespace beast {
 | 
					 | 
				
			||||||
        using flat_buffer = asio::streambuf;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Botan {
 | 
					 | 
				
			||||||
namespace TLS {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enum StreamError { StreamTruncated = 1 };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//! @brief An error category for errors from the TLS::Stream
 | 
					 | 
				
			||||||
struct StreamCategory : public boost::system::error_category {
 | 
					 | 
				
			||||||
      virtual ~StreamCategory() = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const char* name() const noexcept override { return "Botan TLS Stream"; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      std::string message(int value) const override {
 | 
					 | 
				
			||||||
         if(value == StreamTruncated) {
 | 
					 | 
				
			||||||
            return "stream truncated";
 | 
					 | 
				
			||||||
         } else {
 | 
					 | 
				
			||||||
            return "generic error";
 | 
					 | 
				
			||||||
         }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
inline const StreamCategory& botan_stream_category() {
 | 
					 | 
				
			||||||
   static StreamCategory category;
 | 
					 | 
				
			||||||
   return category;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
inline boost::system::error_code make_error_code(Botan::TLS::StreamError e) {
 | 
					 | 
				
			||||||
   return boost::system::error_code(static_cast<int>(e), Botan::TLS::botan_stream_category());
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//! @brief An error category for TLS alerts
 | 
					 | 
				
			||||||
struct BotanAlertCategory : boost::system::error_category {
 | 
					 | 
				
			||||||
      virtual ~BotanAlertCategory() = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const char* name() const noexcept override { return "Botan TLS Alert"; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      std::string message(int ev) const override {
 | 
					 | 
				
			||||||
         Botan::TLS::Alert alert(static_cast<Botan::TLS::Alert::Type>(ev));
 | 
					 | 
				
			||||||
         return alert.type_string();
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
inline const BotanAlertCategory& botan_alert_category() noexcept {
 | 
					 | 
				
			||||||
   static BotanAlertCategory category;
 | 
					 | 
				
			||||||
   return category;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
inline boost::system::error_code make_error_code(Botan::TLS::Alert::Type c) {
 | 
					 | 
				
			||||||
   return boost::system::error_code(static_cast<int>(c), Botan::TLS::botan_alert_category());
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}  // namespace TLS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//! @brief An error category for errors from Botan (other than TLS alerts)
 | 
					 | 
				
			||||||
struct BotanErrorCategory : boost::system::error_category {
 | 
					 | 
				
			||||||
      virtual ~BotanErrorCategory() = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const char* name() const noexcept override { return "Botan"; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      std::string message(int ev) const override { return Botan::to_string(static_cast<Botan::ErrorType>(ev)); }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
inline const BotanErrorCategory& botan_category() noexcept {
 | 
					 | 
				
			||||||
   static BotanErrorCategory category;
 | 
					 | 
				
			||||||
   return category;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
inline boost::system::error_code make_error_code(Botan::ErrorType e) {
 | 
					 | 
				
			||||||
   return boost::system::error_code(static_cast<int>(e), Botan::botan_category());
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}  // namespace Botan
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * Add a template specialization of `is_error_code_enum` for each kind of error to allow automatic conversion to an
 | 
					 | 
				
			||||||
 * error code.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
namespace boost::system {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template <>
 | 
					 | 
				
			||||||
struct is_error_code_enum<Botan::TLS::Alert::Type> {
 | 
					 | 
				
			||||||
      static const bool value = true;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template <>
 | 
					 | 
				
			||||||
struct is_error_code_enum<Botan::TLS::StreamError> {
 | 
					 | 
				
			||||||
      static const bool value = true;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template <>
 | 
					 | 
				
			||||||
struct is_error_code_enum<Botan::ErrorType> {
 | 
					 | 
				
			||||||
      static const bool value = true;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}  // namespace boost::system
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif  // BOTAN_ASIO_ERROR_H_
 | 
					 | 
				
			||||||
@@ -1,740 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
* TLS ASIO Stream
 | 
					 | 
				
			||||||
* (C) 2018-2021 Jack Lloyd
 | 
					 | 
				
			||||||
*     2018-2021 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
 | 
					 | 
				
			||||||
*
 | 
					 | 
				
			||||||
* Botan is released under the Simplified BSD License (see license.txt)
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef BOTAN_ASIO_STREAM_H_
 | 
					 | 
				
			||||||
#define BOTAN_ASIO_STREAM_H_
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <botan_all.h>
 | 
					 | 
				
			||||||
#include <asio.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "asio_async_ops.h"
 | 
					 | 
				
			||||||
#include "asio_context.h"
 | 
					 | 
				
			||||||
#include "asio_error.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <algorithm>
 | 
					 | 
				
			||||||
#include <memory>
 | 
					 | 
				
			||||||
#include <type_traits>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Botan::TLS {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @brief boost::asio compatible SSL/TLS stream
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @tparam StreamLayer type of the next layer, usually a network socket
 | 
					 | 
				
			||||||
 * @tparam ChannelT type of the native_handle, defaults to TLS::Channel, only needed for testing purposes
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
template <class StreamLayer, class ChannelT = Channel>
 | 
					 | 
				
			||||||
class Stream {
 | 
					 | 
				
			||||||
   public:
 | 
					 | 
				
			||||||
      //! \name construction
 | 
					 | 
				
			||||||
      //! @{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Construct a new Stream
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param context The context parameter is used to set up the underlying native handle. Using code is
 | 
					 | 
				
			||||||
       *                responsible for lifetime management of the context and must ensure that it is available for the
 | 
					 | 
				
			||||||
       *                lifetime of the stream.
 | 
					 | 
				
			||||||
       * @param args Arguments to be forwarded to the construction of the next layer.
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      template <typename... Args>
 | 
					 | 
				
			||||||
      explicit Stream(std::shared_ptr<Context> context, Args&&... args) :
 | 
					 | 
				
			||||||
            m_context(context),
 | 
					 | 
				
			||||||
            m_nextLayer(std::forward<Args>(args)...),
 | 
					 | 
				
			||||||
            m_core(std::make_shared<StreamCore>(context)),
 | 
					 | 
				
			||||||
            m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0'),
 | 
					 | 
				
			||||||
            m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size()) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Construct a new Stream
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * Convenience overload for boost::asio::ssl::stream compatibility.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param arg This argument is forwarded to the construction of the next layer.
 | 
					 | 
				
			||||||
       * @param context The context parameter is used to set up the underlying native handle. Using code is
 | 
					 | 
				
			||||||
       *                responsible for lifetime management of the context and must ensure that is available for the
 | 
					 | 
				
			||||||
       *                lifetime of the stream.
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      template <typename Arg>
 | 
					 | 
				
			||||||
      explicit Stream(Arg&& arg, std::shared_ptr<Context> context) :
 | 
					 | 
				
			||||||
            m_context(context),
 | 
					 | 
				
			||||||
            m_nextLayer(std::forward<Arg>(arg)),
 | 
					 | 
				
			||||||
            m_core(std::make_shared<StreamCore>(context)),
 | 
					 | 
				
			||||||
            m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0'),
 | 
					 | 
				
			||||||
            m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size()) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      virtual ~Stream() = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      Stream(Stream&& other) = default;
 | 
					 | 
				
			||||||
      Stream& operator=(Stream&& other) = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      Stream(const Stream& other) = delete;
 | 
					 | 
				
			||||||
      Stream& operator=(const Stream& other) = delete;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //! @}
 | 
					 | 
				
			||||||
      //! \name boost::asio accessor methods
 | 
					 | 
				
			||||||
      //! @{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      using next_layer_type = typename std::remove_reference<StreamLayer>::type;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const next_layer_type& next_layer() const { return m_nextLayer; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      next_layer_type& next_layer() { return m_nextLayer; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   #if VERSION >= 107000
 | 
					 | 
				
			||||||
      /*
 | 
					 | 
				
			||||||
       * From Boost 1.70 onwards Beast types no longer provide public access to the member function `lowest_layer()`.
 | 
					 | 
				
			||||||
       * Instead, the new free-standing functions in Beast need to be used.
 | 
					 | 
				
			||||||
       * See also: https://github.com/boostorg/beast/commit/6a658b5c3a36f8d58334f8b6582c01c3e87768ae
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      using lowest_layer_type = typename boost::beast::lowest_layer_type<StreamLayer>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      lowest_layer_type& lowest_layer() { return boost::beast::get_lowest_layer(m_nextLayer); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const lowest_layer_type& lowest_layer() const { return boost::beast::get_lowest_layer(m_nextLayer); }
 | 
					 | 
				
			||||||
   #else
 | 
					 | 
				
			||||||
      using lowest_layer_type = typename next_layer_type::lowest_layer_type;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      lowest_layer_type& lowest_layer() { return m_nextLayer.lowest_layer(); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const lowest_layer_type& lowest_layer() const { return m_nextLayer.lowest_layer(); }
 | 
					 | 
				
			||||||
   #endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      using executor_type = typename next_layer_type::executor_type;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      executor_type get_executor() noexcept { return m_nextLayer.get_executor(); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      using native_handle_type = typename std::add_pointer<ChannelT>::type;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      native_handle_type native_handle() {
 | 
					 | 
				
			||||||
         if(m_native_handle == nullptr) {
 | 
					 | 
				
			||||||
            throw Invalid_State("Invalid handshake state");
 | 
					 | 
				
			||||||
         }
 | 
					 | 
				
			||||||
         return m_native_handle.get();
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //! @}
 | 
					 | 
				
			||||||
      //! \name configuration and callback setters
 | 
					 | 
				
			||||||
      //! @{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Override the tls_verify_cert_chain callback
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * This changes the verify_callback in the stream's TLS::Context, and hence the tls_verify_cert_chain callback
 | 
					 | 
				
			||||||
       * used in the handshake.
 | 
					 | 
				
			||||||
       * Using this function is equivalent to setting the callback via @see Botan::TLS::Context::set_verify_callback
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @note This function should only be called before initiating the TLS handshake
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      void set_verify_callback(Context::Verify_Callback callback) {
 | 
					 | 
				
			||||||
         m_context->set_verify_callback(std::move(callback));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Compatibility overload of @ref set_verify_callback
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param callback the callback implementation
 | 
					 | 
				
			||||||
       * @param ec This parameter is unused.
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      void set_verify_callback(Context::Verify_Callback callback, boost::system::error_code& ec) {
 | 
					 | 
				
			||||||
         BOTAN_UNUSED(ec);
 | 
					 | 
				
			||||||
         m_context->set_verify_callback(std::move(callback));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //! @throws Not_Implemented
 | 
					 | 
				
			||||||
      void set_verify_depth(int depth) {
 | 
					 | 
				
			||||||
         BOTAN_UNUSED(depth);
 | 
					 | 
				
			||||||
         throw Not_Implemented("set_verify_depth is not implemented");
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * Not Implemented.
 | 
					 | 
				
			||||||
       * @param depth the desired verification depth
 | 
					 | 
				
			||||||
       * @param ec Will be set to `Botan::ErrorType::NotImplemented`
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      void set_verify_depth(int depth, boost::system::error_code& ec) {
 | 
					 | 
				
			||||||
         BOTAN_UNUSED(depth);
 | 
					 | 
				
			||||||
         ec.assign((int)ErrorType::NotImplemented, std::generic_category());
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //! @throws Not_Implemented
 | 
					 | 
				
			||||||
      template <typename verify_mode>
 | 
					 | 
				
			||||||
      void set_verify_mode(verify_mode v) {
 | 
					 | 
				
			||||||
         BOTAN_UNUSED(v);
 | 
					 | 
				
			||||||
         throw Not_Implemented("set_verify_mode is not implemented");
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * Not Implemented.
 | 
					 | 
				
			||||||
       * @param v the desired verify mode
 | 
					 | 
				
			||||||
       * @param ec Will be set to `Botan::ErrorType::NotImplemented`
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      template <typename verify_mode>
 | 
					 | 
				
			||||||
      void set_verify_mode(verify_mode v, boost::system::error_code& ec) {
 | 
					 | 
				
			||||||
         BOTAN_UNUSED(v);
 | 
					 | 
				
			||||||
         ec.assign((int)ErrorType::NotImplemented, std::generic_category());
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //! @}
 | 
					 | 
				
			||||||
      //! \name handshake methods
 | 
					 | 
				
			||||||
      //! @{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Performs SSL handshaking.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * The function call will block until handshaking is complete or an error occurs.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param side The type of handshaking to be performed, i.e. as a client or as a server.
 | 
					 | 
				
			||||||
       * @throws boost::system::system_error if error occured
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      void handshake(Connection_Side side) {
 | 
					 | 
				
			||||||
         boost::system::error_code ec;
 | 
					 | 
				
			||||||
         handshake(side, ec);
 | 
					 | 
				
			||||||
         boost::asio::detail::throw_error(ec, "handshake");
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Performs SSL handshaking.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * The function call will block until handshaking is complete or an error occurs.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param side The type of handshaking to be performed, i.e. as a client or as a server.
 | 
					 | 
				
			||||||
       * @param ec Set to indicate what error occurred, if any.
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      void handshake(Connection_Side side, boost::system::error_code& ec) {
 | 
					 | 
				
			||||||
         setup_native_handle(side, ec);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         if(side == Connection_Side::Client) {
 | 
					 | 
				
			||||||
            // send client hello, which was written to the send buffer on client instantiation
 | 
					 | 
				
			||||||
            send_pending_encrypted_data(ec);
 | 
					 | 
				
			||||||
         }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         while(!native_handle()->is_active() && !ec) {
 | 
					 | 
				
			||||||
            boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
 | 
					 | 
				
			||||||
            if(ec) {
 | 
					 | 
				
			||||||
               return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            process_encrypted_data(read_buffer, ec);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            send_pending_encrypted_data(ec);
 | 
					 | 
				
			||||||
         }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Starts an asynchronous SSL handshake.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * This function call always returns immediately.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param side The type of handshaking to be performed, i.e. as a client or as a server.
 | 
					 | 
				
			||||||
       * @param completion_token The completion handler to be called when the handshake operation completes.
 | 
					 | 
				
			||||||
       *                         The completion signature of the handler must be: void(boost::system::error_code).
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      template <typename CompletionToken>
 | 
					 | 
				
			||||||
      auto async_handshake(Botan::TLS::Connection_Side side, CompletionToken&& completion_token) {
 | 
					 | 
				
			||||||
         return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code)>(
 | 
					 | 
				
			||||||
            [this](auto&& completion_handler, TLS::Connection_Side connection_side) {
 | 
					 | 
				
			||||||
               using completion_handler_t = std::decay_t<decltype(completion_handler)>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
               ASIO_HANDSHAKE_HANDLER_CHECK(completion_handler_t, completion_handler) type_check;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
               boost::system::error_code ec;
 | 
					 | 
				
			||||||
               setup_native_handle(connection_side, ec);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
               detail::AsyncHandshakeOperation<completion_handler_t, Stream> op{
 | 
					 | 
				
			||||||
                  std::forward<completion_handler_t>(completion_handler), *this, ec};
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            completion_token,
 | 
					 | 
				
			||||||
            side);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //! @throws Not_Implemented
 | 
					 | 
				
			||||||
      template <typename ConstBufferSequence, typename BufferedHandshakeHandler>
 | 
					 | 
				
			||||||
      ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler, void(boost::system::error_code, std::size_t))
 | 
					 | 
				
			||||||
      async_handshake(Connection_Side side, const ConstBufferSequence& buffers, BufferedHandshakeHandler&& handler) {
 | 
					 | 
				
			||||||
         BOTAN_UNUSED(side, buffers, handler);
 | 
					 | 
				
			||||||
         ASIO_HANDSHAKE_HANDLER_CHECK(BufferedHandshakeHandler, handler) type_check;
 | 
					 | 
				
			||||||
         throw Not_Implemented("buffered async handshake is not implemented");
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //! @}
 | 
					 | 
				
			||||||
      //! \name shutdown methods
 | 
					 | 
				
			||||||
      //! @{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Shut down SSL on the stream.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * This function is used to shut down SSL on the stream. The function call will block until SSL has been shut down
 | 
					 | 
				
			||||||
       * or an error occurs. Note that this will not close the lowest layer.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * Note that this can be used in reaction of a received shutdown alert from the peer.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param ec Set to indicate what error occured, if any.
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      void shutdown(boost::system::error_code& ec) {
 | 
					 | 
				
			||||||
         try_with_error_code([&] { native_handle()->close(); }, ec);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         send_pending_encrypted_data(ec);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Shut down SSL on the stream.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * This function is used to shut down SSL on the stream. The function call will block until SSL has been shut down
 | 
					 | 
				
			||||||
       * or an error occurs. Note that this will not close the lowest layer.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * Note that this can be used in reaction of a received shutdown alert from the peer.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @throws boost::system::system_error if error occured
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      void shutdown() {
 | 
					 | 
				
			||||||
         boost::system::error_code ec;
 | 
					 | 
				
			||||||
         shutdown(ec);
 | 
					 | 
				
			||||||
         boost::asio::detail::throw_error(ec, "shutdown");
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   private:
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Internal wrapper type to adapt the expected signature of `async_shutdown` to the completion handler
 | 
					 | 
				
			||||||
       *        signature of `AsyncWriteOperation`.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * This is boilerplate to ignore the `size_t` parameter that is passed to the completion handler of
 | 
					 | 
				
			||||||
       * `AsyncWriteOperation`. Note that it needs to retain the wrapped handler's executor.
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      template <typename Handler, typename Executor>
 | 
					 | 
				
			||||||
      struct Wrapper {
 | 
					 | 
				
			||||||
            void operator()(boost::system::error_code ec, std::size_t) { handler(ec); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            using executor_type = boost::asio::associated_executor_t<Handler, Executor>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            executor_type get_executor() const noexcept {
 | 
					 | 
				
			||||||
               return boost::asio::get_associated_executor(handler, io_executor);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            using allocator_type = boost::asio::associated_allocator_t<Handler>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            allocator_type get_allocator() const noexcept { return boost::asio::get_associated_allocator(handler); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Handler handler;
 | 
					 | 
				
			||||||
            Executor io_executor;
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   public:
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Asynchronously shut down SSL on the stream.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * This function call always returns immediately.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * Note that this can be used in reaction of a received shutdown alert from the peer.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param completion_token The completion handler to be called when the shutdown operation completes.
 | 
					 | 
				
			||||||
       *                         The completion signature of the handler must be: void(boost::system::error_code).
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      template <typename CompletionToken>
 | 
					 | 
				
			||||||
      auto async_shutdown(CompletionToken&& completion_token) {
 | 
					 | 
				
			||||||
         return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code)>(
 | 
					 | 
				
			||||||
            [this](auto&& completion_handler) {
 | 
					 | 
				
			||||||
               using completion_handler_t = std::decay_t<decltype(completion_handler)>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
               ASIO_SHUTDOWN_HANDLER_CHECK(completion_handler_t, completion_handler) type_check;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
               boost::system::error_code ec;
 | 
					 | 
				
			||||||
               try_with_error_code([&] { native_handle()->close(); }, ec);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
               using write_handler_t = Wrapper<completion_handler_t, typename Stream::executor_type>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
               TLS::detail::AsyncWriteOperation<write_handler_t, Stream> op{
 | 
					 | 
				
			||||||
                  write_handler_t{std::forward<completion_handler_t>(completion_handler), get_executor()},
 | 
					 | 
				
			||||||
                  *this,
 | 
					 | 
				
			||||||
                  boost::asio::buffer_size(send_buffer()),
 | 
					 | 
				
			||||||
                  ec};
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            completion_token);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //! @}
 | 
					 | 
				
			||||||
      //! \name I/O methods
 | 
					 | 
				
			||||||
      //! @{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Read some data from the stream.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * The function call will block until one or more bytes of data has been read successfully, or until an error
 | 
					 | 
				
			||||||
       * occurs.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param buffers The buffers into which the data will be read.
 | 
					 | 
				
			||||||
       * @param ec Set to indicate what error occurred, if any. Specifically, StreamTruncated will be set if the peer
 | 
					 | 
				
			||||||
       *           has closed the connection but did not properly shut down the SSL connection.
 | 
					 | 
				
			||||||
       * @return The number of bytes read. Returns 0 if an error occurred.
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      template <typename MutableBufferSequence>
 | 
					 | 
				
			||||||
      std::size_t read_some(const MutableBufferSequence& buffers, boost::system::error_code& ec) {
 | 
					 | 
				
			||||||
         if(has_received_data()) {
 | 
					 | 
				
			||||||
            return copy_received_data(buffers);
 | 
					 | 
				
			||||||
         }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
 | 
					 | 
				
			||||||
         if(ec) {
 | 
					 | 
				
			||||||
            return 0;
 | 
					 | 
				
			||||||
         }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         process_encrypted_data(read_buffer, ec);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         if(ec)  // something went wrong in process_encrypted_data()
 | 
					 | 
				
			||||||
         {
 | 
					 | 
				
			||||||
            return 0;
 | 
					 | 
				
			||||||
         }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         if(shutdown_received()) {
 | 
					 | 
				
			||||||
            // we just received a 'close_notify' from the peer and don't expect any more data
 | 
					 | 
				
			||||||
            ec = boost::asio::error::eof;
 | 
					 | 
				
			||||||
         } else if(ec == boost::asio::error::eof) {
 | 
					 | 
				
			||||||
            // we did not expect this disconnection from the peer
 | 
					 | 
				
			||||||
            ec.assign(StreamError::StreamTruncated, std::generic_category());
 | 
					 | 
				
			||||||
         }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         return !ec ? copy_received_data(buffers) : 0;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Read some data from the stream.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * The function call will block until one or more bytes of data has been read successfully, or until an error
 | 
					 | 
				
			||||||
       * occurs.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param buffers The buffers into which the data will be read.
 | 
					 | 
				
			||||||
       * @return The number of bytes read. Returns 0 if an error occurred.
 | 
					 | 
				
			||||||
       * @throws boost::system::system_error if error occured
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      template <typename MutableBufferSequence>
 | 
					 | 
				
			||||||
      std::size_t read_some(const MutableBufferSequence& buffers) {
 | 
					 | 
				
			||||||
         boost::system::error_code ec;
 | 
					 | 
				
			||||||
         const auto n = read_some(buffers, ec);
 | 
					 | 
				
			||||||
         boost::asio::detail::throw_error(ec, "read_some");
 | 
					 | 
				
			||||||
         return n;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Write some data to the stream.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * The function call will block until one or more bytes of data has been written successfully, or until an error
 | 
					 | 
				
			||||||
       * occurs.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param buffers The data to be written.
 | 
					 | 
				
			||||||
       * @param ec Set to indicate what error occurred, if any.
 | 
					 | 
				
			||||||
       * @return The number of bytes processed from the input buffers.
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      template <typename ConstBufferSequence>
 | 
					 | 
				
			||||||
      std::size_t write_some(const ConstBufferSequence& buffers, boost::system::error_code& ec) {
 | 
					 | 
				
			||||||
         tls_encrypt(buffers, ec);
 | 
					 | 
				
			||||||
         send_pending_encrypted_data(ec);
 | 
					 | 
				
			||||||
         return !ec ? boost::asio::buffer_size(buffers) : 0;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Write some data to the stream.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * The function call will block until one or more bytes of data has been written successfully, or until an error
 | 
					 | 
				
			||||||
       * occurs.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param buffers The data to be written.
 | 
					 | 
				
			||||||
       * @return The number of bytes written.
 | 
					 | 
				
			||||||
       * @throws boost::system::system_error if error occured
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      template <typename ConstBufferSequence>
 | 
					 | 
				
			||||||
      std::size_t write_some(const ConstBufferSequence& buffers) {
 | 
					 | 
				
			||||||
         boost::system::error_code ec;
 | 
					 | 
				
			||||||
         const auto n = write_some(buffers, ec);
 | 
					 | 
				
			||||||
         boost::asio::detail::throw_error(ec, "write_some");
 | 
					 | 
				
			||||||
         return n;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Start an asynchronous write. The function call always returns immediately.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param buffers The data to be written.
 | 
					 | 
				
			||||||
       * @param completion_token The completion handler to be called when the write operation completes. Copies of the
 | 
					 | 
				
			||||||
       *                         handler will be made as required. The completion signature of the handler must be:
 | 
					 | 
				
			||||||
       *                         void(boost::system::error_code, std::size_t).
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      template <typename ConstBufferSequence, typename CompletionToken>
 | 
					 | 
				
			||||||
      auto async_write_some(const ConstBufferSequence& buffers, CompletionToken&& completion_token) {
 | 
					 | 
				
			||||||
         return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code, std::size_t)>(
 | 
					 | 
				
			||||||
            [this](auto&& completion_handler, const auto& bufs) {
 | 
					 | 
				
			||||||
               using completion_handler_t = std::decay_t<decltype(completion_handler)>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
               ASIO_WRITE_HANDLER_CHECK(completion_handler_t, completion_handler) type_check;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
               boost::system::error_code ec;
 | 
					 | 
				
			||||||
               tls_encrypt(bufs, ec);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
               if(ec) {
 | 
					 | 
				
			||||||
                  // we cannot be sure how many bytes were committed here so clear the send_buffer and let the
 | 
					 | 
				
			||||||
                  // AsyncWriteOperation call the handler with the error_code set
 | 
					 | 
				
			||||||
                  consume_send_buffer(m_core->send_buffer.size());
 | 
					 | 
				
			||||||
               }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
               detail::AsyncWriteOperation<completion_handler_t, Stream> op{
 | 
					 | 
				
			||||||
                  std::forward<completion_handler_t>(completion_handler),
 | 
					 | 
				
			||||||
                  *this,
 | 
					 | 
				
			||||||
                  ec ? 0 : boost::asio::buffer_size(bufs),
 | 
					 | 
				
			||||||
                  ec};
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            completion_token,
 | 
					 | 
				
			||||||
            buffers);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Start an asynchronous read. The function call always returns immediately.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param buffers The buffers into which the data will be read. Although the buffers object may be copied as
 | 
					 | 
				
			||||||
       *                necessary, ownership of the underlying buffers is retained by the caller, which must guarantee
 | 
					 | 
				
			||||||
       *                that they remain valid until the handler is called.
 | 
					 | 
				
			||||||
       * @param completion_token The completion handler to be called when the read operation completes. The completion
 | 
					 | 
				
			||||||
       *                         signature of the handler must be: void(boost::system::error_code, std::size_t).
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      template <typename MutableBufferSequence, typename CompletionToken>
 | 
					 | 
				
			||||||
      auto async_read_some(const MutableBufferSequence& buffers, CompletionToken&& completion_token) {
 | 
					 | 
				
			||||||
         return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code, std::size_t)>(
 | 
					 | 
				
			||||||
            [this](auto&& completion_handler, const auto& bufs) {
 | 
					 | 
				
			||||||
               using completion_handler_t = std::decay_t<decltype(completion_handler)>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
               ASIO_READ_HANDLER_CHECK(completion_handler_t, completion_handler) type_check;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
               detail::AsyncReadOperation<completion_handler_t, Stream, MutableBufferSequence> op{
 | 
					 | 
				
			||||||
                  std::forward<completion_handler_t>(completion_handler), *this, bufs};
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            completion_token,
 | 
					 | 
				
			||||||
            buffers);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //! @}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //! @brief Indicates whether a close_notify alert has been received from the peer.
 | 
					 | 
				
			||||||
      //!
 | 
					 | 
				
			||||||
      //! Note that we cannot m_core.is_closed_for_reading() because this wants to
 | 
					 | 
				
			||||||
      //! explicitly check that the peer sent close_notify.
 | 
					 | 
				
			||||||
      bool shutdown_received() const { return m_core->shutdown_received; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   protected:
 | 
					 | 
				
			||||||
      template <class H, class S, class M, class A>
 | 
					 | 
				
			||||||
      friend class detail::AsyncReadOperation;
 | 
					 | 
				
			||||||
      template <class H, class S, class A>
 | 
					 | 
				
			||||||
      friend class detail::AsyncWriteOperation;
 | 
					 | 
				
			||||||
      template <class H, class S, class A>
 | 
					 | 
				
			||||||
      friend class detail::AsyncHandshakeOperation;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Helper class that implements TLS::Callbacks
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * This class is provided to the stream's native_handle (TLS::Channel) and implements the callback
 | 
					 | 
				
			||||||
       * functions triggered by the native_handle.
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      class StreamCore : public TLS::Callbacks {
 | 
					 | 
				
			||||||
         public:
 | 
					 | 
				
			||||||
            StreamCore(std::weak_ptr<Botan::TLS::Context> context) : shutdown_received(false), m_context(context) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            ~StreamCore() override = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            void tls_emit_data(std::span<const uint8_t> data) override {
 | 
					 | 
				
			||||||
               send_buffer.commit(boost::asio::buffer_copy(send_buffer.prepare(data.size()),
 | 
					 | 
				
			||||||
                                                           boost::asio::buffer(data.data(), data.size())));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            void tls_record_received(uint64_t, std::span<const uint8_t> data) override {
 | 
					 | 
				
			||||||
               receive_buffer.commit(boost::asio::buffer_copy(receive_buffer.prepare(data.size()),
 | 
					 | 
				
			||||||
                                                              boost::asio::const_buffer(data.data(), data.size())));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            bool tls_peer_closed_connection() override {
 | 
					 | 
				
			||||||
               // Instruct the TLS implementation to reply with our close_notify to obtain
 | 
					 | 
				
			||||||
               // the same behaviour for TLS 1.2 and TLS 1.3.
 | 
					 | 
				
			||||||
               return true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            void tls_alert(TLS::Alert alert) override {
 | 
					 | 
				
			||||||
               if(alert.type() == TLS::AlertType::CloseNotify) {
 | 
					 | 
				
			||||||
                  shutdown_received = true;
 | 
					 | 
				
			||||||
                  // Channel::process_alert will automatically write the corresponding close_notify response to the
 | 
					 | 
				
			||||||
                  // send_buffer and close the native_handle after this function returns.
 | 
					 | 
				
			||||||
               }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const override {
 | 
					 | 
				
			||||||
               return std::chrono::milliseconds(1000);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            void tls_verify_cert_chain(const std::vector<X509_Certificate>& cert_chain,
 | 
					 | 
				
			||||||
                                       const std::vector<std::optional<OCSP::Response>>& ocsp_responses,
 | 
					 | 
				
			||||||
                                       const std::vector<Certificate_Store*>& trusted_roots,
 | 
					 | 
				
			||||||
                                       Usage_Type usage,
 | 
					 | 
				
			||||||
                                       std::string_view hostname,
 | 
					 | 
				
			||||||
                                       const TLS::Policy& policy) override {
 | 
					 | 
				
			||||||
               auto ctx = m_context.lock();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
               if(ctx && ctx->has_verify_callback()) {
 | 
					 | 
				
			||||||
                  ctx->get_verify_callback()(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
 | 
					 | 
				
			||||||
               } else {
 | 
					 | 
				
			||||||
                  Callbacks::tls_verify_cert_chain(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
 | 
					 | 
				
			||||||
               }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            bool shutdown_received;
 | 
					 | 
				
			||||||
            boost::beast::flat_buffer receive_buffer;
 | 
					 | 
				
			||||||
            boost::beast::flat_buffer send_buffer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         private:
 | 
					 | 
				
			||||||
            std::weak_ptr<TLS::Context> m_context;
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const boost::asio::mutable_buffer& input_buffer() { return m_input_buffer; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      boost::asio::const_buffer send_buffer() const { return m_core->send_buffer.data(); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //! @brief Check if decrypted data is available in the receive buffer
 | 
					 | 
				
			||||||
      bool has_received_data() const { return m_core->receive_buffer.size() > 0; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //! @brief Copy decrypted data into the user-provided buffer
 | 
					 | 
				
			||||||
      template <typename MutableBufferSequence>
 | 
					 | 
				
			||||||
      std::size_t copy_received_data(MutableBufferSequence buffers) {
 | 
					 | 
				
			||||||
         // Note: It would be nice to avoid this buffer copy. This could be achieved by equipping the StreamCore with
 | 
					 | 
				
			||||||
         // the user's desired target buffer once a read is started, and reading directly into that buffer in tls_record
 | 
					 | 
				
			||||||
         // received. However, we need to deal with the case that the receive buffer provided by the caller is smaller
 | 
					 | 
				
			||||||
         // than the decrypted record, so this optimization might not be worth the additional complexity.
 | 
					 | 
				
			||||||
         const auto copiedBytes = boost::asio::buffer_copy(buffers, m_core->receive_buffer.data());
 | 
					 | 
				
			||||||
         m_core->receive_buffer.consume(copiedBytes);
 | 
					 | 
				
			||||||
         return copiedBytes;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //! @brief Check if encrypted data is available in the send buffer
 | 
					 | 
				
			||||||
      bool has_data_to_send() const { return m_core->send_buffer.size() > 0; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //! @brief Mark bytes in the send buffer as consumed, removing them from the buffer
 | 
					 | 
				
			||||||
      void consume_send_buffer(std::size_t bytesConsumed) { m_core->send_buffer.consume(bytesConsumed); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Create the native handle.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * Depending on the desired connection side, this function will create a TLS::Client or a
 | 
					 | 
				
			||||||
       * TLS::Server.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param side The desired connection side (client or server)
 | 
					 | 
				
			||||||
       * @param ec Set to indicate what error occurred, if any.
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      void setup_native_handle(Connection_Side side, boost::system::error_code& ec) {
 | 
					 | 
				
			||||||
         BOTAN_UNUSED(side);  // workaround: GCC 9 produces a warning claiming side is unused
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         // Do not attempt to instantiate the native_handle when a custom (mocked) channel type template parameter has
 | 
					 | 
				
			||||||
         // been specified. This allows mocking the native_handle in test code.
 | 
					 | 
				
			||||||
         if constexpr(std::is_same<ChannelT, Channel>::value) {
 | 
					 | 
				
			||||||
            try_with_error_code(
 | 
					 | 
				
			||||||
               [&] {
 | 
					 | 
				
			||||||
                  if(side == Connection_Side::Client) {
 | 
					 | 
				
			||||||
                     m_native_handle = std::unique_ptr<Client>(
 | 
					 | 
				
			||||||
                        new Client(m_core,
 | 
					 | 
				
			||||||
                                   m_context->m_session_manager,
 | 
					 | 
				
			||||||
                                   m_context->m_credentials_manager,
 | 
					 | 
				
			||||||
                                   m_context->m_policy,
 | 
					 | 
				
			||||||
                                   m_context->m_rng,
 | 
					 | 
				
			||||||
                                   m_context->m_server_info,
 | 
					 | 
				
			||||||
                                   m_context->m_policy->latest_supported_version(false /* no DTLS */)));
 | 
					 | 
				
			||||||
                  } else {
 | 
					 | 
				
			||||||
                     m_native_handle = std::unique_ptr<Server>(new Server(m_core,
 | 
					 | 
				
			||||||
                                                                          m_context->m_session_manager,
 | 
					 | 
				
			||||||
                                                                          m_context->m_credentials_manager,
 | 
					 | 
				
			||||||
                                                                          m_context->m_policy,
 | 
					 | 
				
			||||||
                                                                          m_context->m_rng,
 | 
					 | 
				
			||||||
                                                                          false /* no DTLS */));
 | 
					 | 
				
			||||||
                  }
 | 
					 | 
				
			||||||
               },
 | 
					 | 
				
			||||||
               ec);
 | 
					 | 
				
			||||||
         }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /** @brief Synchronously write encrypted data from the send buffer to the next layer.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * If this function is called with an error code other than 'Success', it will do nothing and return 0.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param ec Set to indicate what error occurred, if any. Specifically, StreamTruncated will be set if the peer
 | 
					 | 
				
			||||||
       *           has closed the connection but did not properly shut down the SSL connection.
 | 
					 | 
				
			||||||
       * @return The number of bytes written.
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      size_t send_pending_encrypted_data(boost::system::error_code& ec) {
 | 
					 | 
				
			||||||
         if(ec) {
 | 
					 | 
				
			||||||
            return 0;
 | 
					 | 
				
			||||||
         }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         auto writtenBytes = boost::asio::write(m_nextLayer, send_buffer(), ec);
 | 
					 | 
				
			||||||
         consume_send_buffer(writtenBytes);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         if(ec == boost::asio::error::eof && !shutdown_received()) {
 | 
					 | 
				
			||||||
            // transport layer was closed by peer without receiving 'close_notify'
 | 
					 | 
				
			||||||
            ec.assign(StreamError::StreamTruncated, std::generic_category());
 | 
					 | 
				
			||||||
         }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         return writtenBytes;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Pass plaintext data to the native handle for processing.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * The native handle will then create TLS records and hand them back to the Stream via the tls_emit_data callback.
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      template <typename ConstBufferSequence>
 | 
					 | 
				
			||||||
      void tls_encrypt(const ConstBufferSequence& buffers, boost::system::error_code& ec) {
 | 
					 | 
				
			||||||
         // NOTE: This is not asynchronous: it encrypts the data synchronously.
 | 
					 | 
				
			||||||
         // The data encrypted by native_handle()->send() is synchronously stored in the send_buffer of m_core,
 | 
					 | 
				
			||||||
         // but is not actually written to the wire, yet.
 | 
					 | 
				
			||||||
         for(auto it = boost::asio::buffer_sequence_begin(buffers);
 | 
					 | 
				
			||||||
             !ec && it != boost::asio::buffer_sequence_end(buffers);
 | 
					 | 
				
			||||||
             it++) {
 | 
					 | 
				
			||||||
            const boost::asio::const_buffer buffer = *it;
 | 
					 | 
				
			||||||
            try_with_error_code(
 | 
					 | 
				
			||||||
               [&] {
 | 
					 | 
				
			||||||
                  native_handle()->send({static_cast<const uint8_t*>(buffer.data()), buffer.size()});
 | 
					 | 
				
			||||||
               },
 | 
					 | 
				
			||||||
               ec);
 | 
					 | 
				
			||||||
         }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      /**
 | 
					 | 
				
			||||||
       * @brief Pass encrypted data to the native handle for processing.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * If an exception occurs while processing the data, an error code will be set.
 | 
					 | 
				
			||||||
       *
 | 
					 | 
				
			||||||
       * @param read_buffer Input buffer containing the encrypted data.
 | 
					 | 
				
			||||||
       * @param ec Set to indicate what error occurred, if any.
 | 
					 | 
				
			||||||
       */
 | 
					 | 
				
			||||||
      void process_encrypted_data(const boost::asio::const_buffer& read_buffer, boost::system::error_code& ec) {
 | 
					 | 
				
			||||||
         try_with_error_code(
 | 
					 | 
				
			||||||
            [&] {
 | 
					 | 
				
			||||||
               native_handle()->received_data({static_cast<const uint8_t*>(read_buffer.data()), read_buffer.size()});
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            ec);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //! @brief Catch exceptions and set an error_code
 | 
					 | 
				
			||||||
      template <typename Fun>
 | 
					 | 
				
			||||||
      void try_with_error_code(Fun f, boost::system::error_code& ec) {
 | 
					 | 
				
			||||||
          f();
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      std::shared_ptr<Context> m_context;
 | 
					 | 
				
			||||||
      StreamLayer m_nextLayer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      std::shared_ptr<StreamCore> m_core;
 | 
					 | 
				
			||||||
      std::unique_ptr<ChannelT> m_native_handle;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // Buffer space used to read input intended for the core
 | 
					 | 
				
			||||||
      std::vector<uint8_t> m_input_buffer_space;
 | 
					 | 
				
			||||||
      const boost::asio::mutable_buffer m_input_buffer;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}  // namespace Botan::TLS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif  // BOTAN_ASIO_STREAM_H_
 | 
					 | 
				
			||||||
@@ -35,7 +35,8 @@ void Data::load_config() {
 | 
				
			|||||||
    inipp::Ini<char> ini;
 | 
					    inipp::Ini<char> ini;
 | 
				
			||||||
    if (!std::filesystem::exists(ini_name)) {
 | 
					    if (!std::filesystem::exists(ini_name)) {
 | 
				
			||||||
        ini.sections["web"] = {
 | 
					        ini.sections["web"] = {
 | 
				
			||||||
            {"port", "80"}
 | 
					            {"port", "80"},
 | 
				
			||||||
 | 
					            {"threads", "6"}
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        ini.sections["smtp"] = {
 | 
					        ini.sections["smtp"] = {
 | 
				
			||||||
            {"host", "127.0.0.1"},
 | 
					            {"host", "127.0.0.1"},
 | 
				
			||||||
@@ -68,6 +69,7 @@ void Data::load_config() {
 | 
				
			|||||||
    std::string entry;
 | 
					    std::string entry;
 | 
				
			||||||
#define get_entry(section, key) if (!ini.sections[#section].contains(#key)) {data_logger->critical("Missing " #section ":" #key); crash();} entry = ini.sections[#section][#key]
 | 
					#define get_entry(section, key) if (!ini.sections[#section].contains(#key)) {data_logger->critical("Missing " #section ":" #key); crash();} entry = ini.sections[#section][#key]
 | 
				
			||||||
    get_entry(web, port); config.server_port = std::stoul(entry);
 | 
					    get_entry(web, port); config.server_port = std::stoul(entry);
 | 
				
			||||||
 | 
					    get_entry(web, threads); config.threads = std::stoul(entry);
 | 
				
			||||||
    get_entry(smtp, host); config.smtp_host = entry;
 | 
					    get_entry(smtp, host); config.smtp_host = entry;
 | 
				
			||||||
    get_entry(smtp, port); config.smtp_port = std::stoul(entry);
 | 
					    get_entry(smtp, port); config.smtp_port = std::stoul(entry);
 | 
				
			||||||
    get_entry(smtp, user); config.smtp_user = entry;
 | 
					    get_entry(smtp, user); config.smtp_user = entry;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,7 +49,7 @@ struct Token {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
struct Config {
 | 
					struct Config {
 | 
				
			||||||
    std::string smtp_host, smtp_user, smtp_pass, smtp_from, admin_mail;
 | 
					    std::string smtp_host, smtp_user, smtp_pass, smtp_from, admin_mail;
 | 
				
			||||||
    std::uint16_t smtp_port, server_port;
 | 
					    std::uint16_t smtp_port, server_port, threads;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Data {
 | 
					struct Data {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										211
									
								
								src/main.cxx
									
									
									
									
									
								
							
							
						
						
									
										211
									
								
								src/main.cxx
									
									
									
									
									
								
							@@ -1,36 +1,122 @@
 | 
				
			|||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
#include <csignal>
 | 
					#include <csignal>
 | 
				
			||||||
#include <botan_all.h>
 | 
					 | 
				
			||||||
#include <corvusoft/restbed/request.hpp>
 | 
					 | 
				
			||||||
#include <corvusoft/restbed/resource.hpp>
 | 
					 | 
				
			||||||
#include <corvusoft/restbed/session.hpp>
 | 
					 | 
				
			||||||
#include <corvusoft/restbed/settings.hpp>
 | 
					 | 
				
			||||||
#include <corvusoft/restbed/service.hpp>
 | 
					 | 
				
			||||||
#include <spdlog/sinks/basic_file_sink.h>
 | 
					#include <spdlog/sinks/basic_file_sink.h>
 | 
				
			||||||
#include "util/logging.hxx"
 | 
					#include <boost/asio/co_spawn.hpp>
 | 
				
			||||||
 | 
					#include <boost/asio/signal_set.hpp>
 | 
				
			||||||
#include "server/server.hxx"
 | 
					#include "server/server.hxx"
 | 
				
			||||||
 | 
					#include "util/botan.hxx"
 | 
				
			||||||
#include "index_html.h"
 | 
					#include "index_html.h"
 | 
				
			||||||
#include "favicon_svg.h"
 | 
					#include "favicon_svg.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::shared_ptr<restbed::Service> g_service = nullptr;
 | 
					using tcp = boost::asio::ip::tcp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const static restbed::Bytes index_html_bytes{index_html, index_html + index_html_len};
 | 
					std::function<void()> g_stop_service;
 | 
				
			||||||
const static restbed::Bytes favicon_bytes{favicon_svg, favicon_svg + favicon_svg_len};
 | 
					std::shared_ptr<spdlog::logger> err_handler::logger{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void signal_shutdown(const int) {
 | 
					Server server{};
 | 
				
			||||||
    spdlog::info("Received stop signal");
 | 
					std::string index_etag{};
 | 
				
			||||||
    g_service->stop();
 | 
					
 | 
				
			||||||
 | 
					net::awaitable<void> do_session(tcp_stream stream) {
 | 
				
			||||||
 | 
					    tcp_buffer buffer;
 | 
				
			||||||
 | 
					    beast::error_code ec;
 | 
				
			||||||
 | 
					    stream.expires_after(std::chrono::seconds(30));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        while (true) {
 | 
				
			||||||
 | 
					            http::request_parser<http::empty_body> req_base_parser;
 | 
				
			||||||
 | 
					            req_base_parser.body_limit(boost::none);
 | 
				
			||||||
 | 
					            co_await http::async_read_header(stream, buffer, req_base_parser);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auto &req_base = req_base_parser.get();
 | 
				
			||||||
 | 
					            bool keep_alive = req_base.keep_alive();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auto addr = stream.socket().remote_endpoint().address().to_string();
 | 
				
			||||||
 | 
					            if (!req_base["x-forwarded-for"].empty())
 | 
				
			||||||
 | 
					                addr = std::string{req_base["x-forwarded-for"]} + "' via '" + addr;
 | 
				
			||||||
 | 
					            spdlog::info("Request: '{} {}' from '{}'", req_base.method_string(), req_base.target(), addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (req_base.method() == http::verb::post && req_base.target() == "/mrpc") {
 | 
				
			||||||
 | 
					                req_base_parser.body_limit(1024*1024*8); // 8 MB
 | 
				
			||||||
 | 
					                http::request_parser<http::string_body> req{std::move(req_base_parser)};
 | 
				
			||||||
 | 
					                co_await http::async_read(stream, buffer, req);
 | 
				
			||||||
 | 
					                co_await server.msg_handler(stream, req.get());
 | 
				
			||||||
 | 
					            } else if (req_base.method() == http::verb::get && req_base.target() == "/") {
 | 
				
			||||||
 | 
					                co_await http::async_read(stream, buffer, req_base_parser);
 | 
				
			||||||
 | 
					                if (req_base["If-None-Match"] == index_etag) {
 | 
				
			||||||
 | 
					                    auto res = create_response<http::status::not_modified, http::empty_body, http::empty_body>(req_base);
 | 
				
			||||||
 | 
					                    res.set(http::field::cache_control, "no-cache");
 | 
				
			||||||
 | 
					                    res.set(http::field::etag, index_etag);
 | 
				
			||||||
 | 
					                    co_await http::async_write(stream, res, net::use_awaitable);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    auto res = create_response<http::status::ok, http::buffer_body, http::empty_body>(req_base);
 | 
				
			||||||
 | 
					                    res.set(http::field::content_type, "text/html");
 | 
				
			||||||
 | 
					                    res.content_length(index_html_len);
 | 
				
			||||||
 | 
					                    res.set(http::field::cache_control, "no-cache");
 | 
				
			||||||
 | 
					                    res.set(http::field::etag, index_etag);
 | 
				
			||||||
 | 
					                    res.body().data = index_html;
 | 
				
			||||||
 | 
					                    res.body().size = index_html_len;
 | 
				
			||||||
 | 
					                    res.body().more = false;
 | 
				
			||||||
 | 
					                    co_await http::async_write(stream, res, net::use_awaitable);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else if (req_base.method() == http::verb::post && req_base.target() == "/download") {
 | 
				
			||||||
 | 
					                req_base_parser.body_limit(1024*1024*8);
 | 
				
			||||||
 | 
					                http::request_parser<http::string_body> req{std::move(req_base_parser)};
 | 
				
			||||||
 | 
					                co_await http::async_read(stream, buffer, req);
 | 
				
			||||||
 | 
					                co_await server.download(stream, req.get());
 | 
				
			||||||
 | 
					            } else if (req_base.method() == http::verb::post && req_base.target() == "/upload") {
 | 
				
			||||||
 | 
					                http::request_parser<http::file_body> req_parser{std::move(req_base_parser)};
 | 
				
			||||||
 | 
					                co_await server.upload(stream, buffer, req_parser);
 | 
				
			||||||
 | 
					                keep_alive = false;
 | 
				
			||||||
 | 
					            } else if (req_base.method() == http::verb::post && req_base.target() == "/download_multi") {
 | 
				
			||||||
 | 
					                req_base_parser.body_limit(1024*1024*8);
 | 
				
			||||||
 | 
					                http::request_parser<http::string_body> req{std::move(req_base_parser)};
 | 
				
			||||||
 | 
					                co_await http::async_read(stream, buffer, req);
 | 
				
			||||||
 | 
					                co_await server.download_multi(stream, req.get());
 | 
				
			||||||
 | 
					            } else if (req_base.method() == http::verb::get && req_base.target() == "/favicon.svg") {
 | 
				
			||||||
 | 
					                co_await http::async_read(stream, buffer, req_base_parser);
 | 
				
			||||||
 | 
					                auto res = create_response<http::status::ok, http::buffer_body, http::empty_body>(req_base);
 | 
				
			||||||
 | 
					                res.set(http::field::content_type, "image/svg+xml");
 | 
				
			||||||
 | 
					                res.content_length(favicon_svg_len);
 | 
				
			||||||
 | 
					                res.body().data = favicon_svg;
 | 
				
			||||||
 | 
					                res.body().size = favicon_svg_len;
 | 
				
			||||||
 | 
					                res.body().more = false;
 | 
				
			||||||
 | 
					                co_await http::async_write(stream, res, net::use_awaitable);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                http::response<http::string_body> res{http::status::not_found, req_base.version()};
 | 
				
			||||||
 | 
					                res.set(http::field::content_type, "text/plain");
 | 
				
			||||||
 | 
					                res.keep_alive(false);
 | 
				
			||||||
 | 
					                keep_alive = false;
 | 
				
			||||||
 | 
					                res.body() = "404 not found";
 | 
				
			||||||
 | 
					                res.prepare_payload();
 | 
				
			||||||
 | 
					                co_await http::async_write(stream, res, net::use_awaitable);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!keep_alive)
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } catch (beast::system_error &se) {
 | 
				
			||||||
 | 
					        if (se.code() != http::error::end_of_stream)
 | 
				
			||||||
 | 
					            throw;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ec = stream.socket().shutdown(tcp::socket::shutdown_send, ec);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void error_handler(const int code, const std::exception& ex, const std::shared_ptr<restbed::Session> session) {
 | 
					net::awaitable<void> do_listen(tcp::endpoint endpoint) {
 | 
				
			||||||
    std::stringstream ss;
 | 
					    auto acceptor = net::use_awaitable.as_default_on(tcp::acceptor(co_await net::this_coro::executor));
 | 
				
			||||||
    ss << "Encountered error with code '" << std::to_string(code) << "'";
 | 
					
 | 
				
			||||||
    if (session != nullptr)
 | 
					    acceptor.open(endpoint.protocol());
 | 
				
			||||||
        ss << " in session from '" << session->get_origin() << "'";
 | 
					    acceptor.set_option(net::socket_base::reuse_address(true));
 | 
				
			||||||
    ss << ": " << ex.what();
 | 
					    acceptor.bind(endpoint);
 | 
				
			||||||
    spdlog::error(ss.str());
 | 
					    acceptor.listen(net::socket_base::max_listen_connections);
 | 
				
			||||||
    if (session != nullptr)
 | 
					
 | 
				
			||||||
        session->close(code, ex.what());
 | 
					    while (true) {
 | 
				
			||||||
 | 
					        net::co_spawn(
 | 
				
			||||||
 | 
					            acceptor.get_executor(),
 | 
				
			||||||
 | 
					            do_session(tcp_stream(co_await acceptor.async_accept())),
 | 
				
			||||||
 | 
					            err_handler{}
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int main() {
 | 
					int main() {
 | 
				
			||||||
@@ -39,83 +125,28 @@ int main() {
 | 
				
			|||||||
    spdlog::default_logger()->sinks().push_back(file_sink);
 | 
					    spdlog::default_logger()->sinks().push_back(file_sink);
 | 
				
			||||||
    spdlog::set_level(spdlog::level::trace);
 | 
					    spdlog::set_level(spdlog::level::trace);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::string index_etag;
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        auto md5_hash = Botan::HashFunction::create_or_throw("MD5");
 | 
					        auto md5_hash = Botan::HashFunction::create_or_throw("MD5");
 | 
				
			||||||
        md5_hash->update(index_html_bytes);
 | 
					        md5_hash->update(index_html, index_html_len);
 | 
				
			||||||
        index_etag = "\"" + Botan::hex_encode(md5_hash->final()) + "\"";
 | 
					        index_etag = "\"" + Botan::hex_encode(md5_hash->final()) + "\"";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto mrpc_resource = std::make_shared<restbed::Resource>();
 | 
					    net::io_context ioc{server.config.threads};
 | 
				
			||||||
    mrpc_resource->set_path("/mrpc");
 | 
					    net::co_spawn(ioc, do_listen(tcp::endpoint{net::ip::address_v4::any(), server.config.server_port}), err_handler{});
 | 
				
			||||||
    Server server{mrpc_resource};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define mk_res(url) auto url##_resource = std::make_shared<restbed::Resource>(); \
 | 
					    net::signal_set signals{ioc, SIGINT, SIGTERM};
 | 
				
			||||||
    url##_resource->set_path("/" #url); \
 | 
					    signals.async_wait([&ioc](const beast::error_code&, int){ ioc.stop(); });
 | 
				
			||||||
    url##_resource->set_method_handler("POST", [&server](auto s){ server.url(s); })
 | 
					    g_stop_service = [&ioc] { ioc.stop(); };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mk_res(download);
 | 
					    spdlog::info("Listening on :{}", server.config.server_port);
 | 
				
			||||||
    mk_res(download_multi);
 | 
					 | 
				
			||||||
    mk_res(upload);
 | 
					 | 
				
			||||||
#undef mk_res
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto index_resource = std::make_shared<restbed::Resource>();
 | 
					    std::vector<std::thread> v;
 | 
				
			||||||
    index_resource->set_path("/");
 | 
					    for (std::uint16_t i = server.config.threads-1; i > 0; --i)
 | 
				
			||||||
    index_resource->set_method_handler("GET", [&index_etag](const std::shared_ptr<restbed::Session>& s){
 | 
					        v.emplace_back([&ioc] { ioc.run(); });
 | 
				
			||||||
        auto req = s->get_request();
 | 
					    ioc.run();
 | 
				
			||||||
        if (req->get_header("If-None-Match", "") == index_etag) {
 | 
					 | 
				
			||||||
            s->yield(
 | 
					 | 
				
			||||||
                304,
 | 
					 | 
				
			||||||
                "",
 | 
					 | 
				
			||||||
                std::multimap<std::string, std::string>{
 | 
					 | 
				
			||||||
                    {"Cache-Control", "no-cache"},
 | 
					 | 
				
			||||||
                    {"ETag", index_etag}
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            s->yield(
 | 
					 | 
				
			||||||
                200,
 | 
					 | 
				
			||||||
                index_html_bytes,
 | 
					 | 
				
			||||||
                std::multimap<std::string, std::string>{
 | 
					 | 
				
			||||||
                    {"Content-Type",   "text/html"},
 | 
					 | 
				
			||||||
                    {"Content-Length", std::to_string(index_html_len)},
 | 
					 | 
				
			||||||
                    {"Cache-Control",  "no-cache"},
 | 
					 | 
				
			||||||
                    {"ETag",           index_etag}
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto favicon_resource = std::make_shared<restbed::Resource>();
 | 
					    for (auto &t : v)
 | 
				
			||||||
    favicon_resource->set_path("/favicon.svg");
 | 
					        t.join();
 | 
				
			||||||
    favicon_resource->set_method_handler("GET", [](const std::shared_ptr<restbed::Session>& s){
 | 
					 | 
				
			||||||
        s->yield(
 | 
					 | 
				
			||||||
            200,
 | 
					 | 
				
			||||||
            favicon_bytes,
 | 
					 | 
				
			||||||
            std::multimap<std::string, std::string>{
 | 
					 | 
				
			||||||
                {"Content-Type", "image/svg+xml"},
 | 
					 | 
				
			||||||
                {"Content-Length", std::to_string(favicon_svg_len)}
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    auto settings = std::make_shared<restbed::Settings>();
 | 
					 | 
				
			||||||
    settings->set_port(server.config.server_port);
 | 
					 | 
				
			||||||
    settings->set_default_header("Connection", "keep-alive");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    g_service = std::make_shared<restbed::Service>();
 | 
					 | 
				
			||||||
    g_service->set_error_handler(error_handler);
 | 
					 | 
				
			||||||
    g_service->set_logger(std::make_shared<logging::RestbedLogger>());
 | 
					 | 
				
			||||||
    g_service->set_signal_handler(SIGINT, signal_shutdown);
 | 
					 | 
				
			||||||
    g_service->set_signal_handler(SIGTERM, signal_shutdown);
 | 
					 | 
				
			||||||
    g_service->publish(mrpc_resource);
 | 
					 | 
				
			||||||
    g_service->publish(download_resource);
 | 
					 | 
				
			||||||
    g_service->publish(download_multi_resource);
 | 
					 | 
				
			||||||
    g_service->publish(upload_resource);
 | 
					 | 
				
			||||||
    g_service->publish(index_resource);
 | 
					 | 
				
			||||||
    g_service->publish(favicon_resource);
 | 
					 | 
				
			||||||
    g_service->start(settings);
 | 
					 | 
				
			||||||
    g_service.reset();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -97,6 +97,6 @@ std::optional<std::string> Server::Admin_unsudo(std::string &&token) {
 | 
				
			|||||||
std::optional<std::string> Server::Admin_shutdown(std::string &&token) {
 | 
					std::optional<std::string> Server::Admin_shutdown(std::string &&token) {
 | 
				
			||||||
    check_admin_optional();
 | 
					    check_admin_optional();
 | 
				
			||||||
    spdlog::info("Received rpc shutdown request from admin user {}", user->name);
 | 
					    spdlog::info("Received rpc shutdown request from admin user {}", user->name);
 | 
				
			||||||
    g_service->stop();
 | 
					    g_stop_service();
 | 
				
			||||||
    return std::nullopt;
 | 
					    return std::nullopt;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,181 +4,146 @@
 | 
				
			|||||||
#include <fstream>
 | 
					#include <fstream>
 | 
				
			||||||
#include <deque>
 | 
					#include <deque>
 | 
				
			||||||
#include <charconv>
 | 
					#include <charconv>
 | 
				
			||||||
#include <miniz.h>
 | 
					#include "../util/miniz.hxx"
 | 
				
			||||||
#include <corvusoft/restbed/session.hpp>
 | 
					 | 
				
			||||||
#include <corvusoft/restbed/request.hpp>
 | 
					 | 
				
			||||||
#include <corvusoft/restbed/response.hpp>
 | 
					 | 
				
			||||||
#include "server_internal.hxx"
 | 
					#include "server_internal.hxx"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Server::download(const std::shared_ptr<restbed::Session> &s) {
 | 
					net::awaitable<void> Server::download(tcp_stream &s, const http::request<http::string_body> &req) {
 | 
				
			||||||
    const auto req = s->get_request();
 | 
					    auto body = req.body();
 | 
				
			||||||
    std::size_t body_len = req->get_header("Content-Length", 0);
 | 
					    if (body.empty()) { co_await send_error(s, req, "Empty body"); co_return; }
 | 
				
			||||||
    s->fetch(body_len, [this](const std::shared_ptr<restbed::Session> &s, const restbed::Bytes &b){
 | 
					 | 
				
			||||||
        std::string body{b.cbegin(), b.cend()};
 | 
					 | 
				
			||||||
        if (body.empty())
 | 
					 | 
				
			||||||
            return s->close(400, "empty body");
 | 
					 | 
				
			||||||
        std::string node_str, token;
 | 
					 | 
				
			||||||
        for (const auto part : std::views::split(body, '&')) {
 | 
					 | 
				
			||||||
            std::string_view part_view{part};
 | 
					 | 
				
			||||||
            auto equal_pos = part_view.find_first_of('=');
 | 
					 | 
				
			||||||
            auto key = part_view.substr(0, equal_pos);
 | 
					 | 
				
			||||||
            if (key == "node")
 | 
					 | 
				
			||||||
                node_str = part_view.substr(equal_pos+1);
 | 
					 | 
				
			||||||
            else if (key == "token")
 | 
					 | 
				
			||||||
                token = part_view.substr(equal_pos+1);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (node_str.empty())
 | 
					 | 
				
			||||||
            return s->close(400, "Missing node");
 | 
					 | 
				
			||||||
        if (token.empty())
 | 
					 | 
				
			||||||
            return s->close(400, "Missing token");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::uint64_t node_id;
 | 
					    std::string node_str, token;
 | 
				
			||||||
        auto res = std::from_chars(node_str.data(), node_str.data() + node_str.size(), node_id);
 | 
					    for (const auto part : std::views::split(body, '&')) {
 | 
				
			||||||
        if (res.ec != std::errc{})
 | 
					        std::string_view part_view{part};
 | 
				
			||||||
            return s->close(400, "Invalid node");
 | 
					        auto equal_pos = part_view.find_first_of('=');
 | 
				
			||||||
 | 
					        auto key = part_view.substr(0, equal_pos);
 | 
				
			||||||
        check_user() return s->close(400, "Invalid user");
 | 
					        if (key == "node")
 | 
				
			||||||
        {
 | 
					            node_str = part_view.substr(equal_pos+1);
 | 
				
			||||||
            std::shared_lock lock{user->node_lock};
 | 
					        else if (key == "token")
 | 
				
			||||||
            auto node = get_node(user, node_id);
 | 
					            token = part_view.substr(equal_pos+1);
 | 
				
			||||||
            if (!node) return s->close(400, "Invalid node");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            auto mime = get_mime_type(node->name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            s->yield(
 | 
					 | 
				
			||||||
                200,
 | 
					 | 
				
			||||||
                "",
 | 
					 | 
				
			||||||
                std::multimap<std::string, std::string>{
 | 
					 | 
				
			||||||
                    {"Content-Type", mime},
 | 
					 | 
				
			||||||
                    {"Content-Length", std::to_string(node->size)},
 | 
					 | 
				
			||||||
                    {"Content-Disposition", "attachment; filename=\"" + node->name + "\""}
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                [user=user, node=node](const std::shared_ptr<restbed::Session>& s) {
 | 
					 | 
				
			||||||
                    std::shared_lock lock{user->node_lock};
 | 
					 | 
				
			||||||
                    restbed::Bytes buf(1024*1024*4, 0);
 | 
					 | 
				
			||||||
                    std::ifstream f{user->user_dir / std::to_string(node->id)};
 | 
					 | 
				
			||||||
                    while (!f.eof()) {
 | 
					 | 
				
			||||||
                        buf.resize(buf.capacity());
 | 
					 | 
				
			||||||
                        f.read((char*)buf.data(), buf.size());
 | 
					 | 
				
			||||||
                        buf.resize(f.gcount());
 | 
					 | 
				
			||||||
                        s->yield(buf);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    s->close();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
size_t zip_write_func(void *pOpaque, mz_uint64 _file_ofs, const void *pBuf, size_t n) {
 | 
					 | 
				
			||||||
    auto s = (restbed::Session*)pOpaque;
 | 
					 | 
				
			||||||
    if (n > 0) {
 | 
					 | 
				
			||||||
        restbed::Bytes buf(n, 0);
 | 
					 | 
				
			||||||
        std::memcpy(buf.data(), pBuf, n);
 | 
					 | 
				
			||||||
        std::stringstream ss;
 | 
					 | 
				
			||||||
        ss << std::hex << n;
 | 
					 | 
				
			||||||
        s->yield(ss.str() + "\r\n");
 | 
					 | 
				
			||||||
        s->yield(buf);
 | 
					 | 
				
			||||||
        s->yield("\r\n");
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return n;
 | 
					
 | 
				
			||||||
 | 
					    if (node_str.empty()) { co_await send_error(s, req, "Missing node"); co_return; }
 | 
				
			||||||
 | 
					    if (token.empty()) { co_await send_error(s, req, "Missing token"); co_return; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::uint64_t node_id;
 | 
				
			||||||
 | 
					    auto fc_res = std::from_chars(node_str.data(), node_str.data() + node_str.size(), node_id);
 | 
				
			||||||
 | 
					    if (fc_res.ec != std::errc{}) { co_await send_error(s, req, "Invalid node"); co_return; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    check_user() { co_await send_error(s, req, "Invalid user"); co_return; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::shared_lock lock{user->node_lock};
 | 
				
			||||||
 | 
					    auto node = get_node(user, node_id);
 | 
				
			||||||
 | 
					    if (!node) { co_await send_error(s, req, "Invalid node"); co_return; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    beast::error_code ec;
 | 
				
			||||||
 | 
					    auto mime = get_mime_type(node->name);
 | 
				
			||||||
 | 
					    auto res = create_response<http::status::ok, http::file_body, http::string_body>(req);
 | 
				
			||||||
 | 
					    res.content_length(node->size);
 | 
				
			||||||
 | 
					    res.set(http::field::content_type, mime);
 | 
				
			||||||
 | 
					    res.set(http::field::content_disposition, "attachment; filename=\"" + node->name + "\"");
 | 
				
			||||||
 | 
					    res.body().open((user->user_dir / std::to_string(node->id)).c_str(), beast::file_mode::read, ec);
 | 
				
			||||||
 | 
					    co_await http::async_write(s, res, net::use_awaitable);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Server::download_multi(const std::shared_ptr<restbed::Session> &s) {
 | 
					struct Zip : public ZipArchive {
 | 
				
			||||||
    const auto req = s->get_request();
 | 
					    tcp_stream *s;
 | 
				
			||||||
    const auto body_len = req->get_header("Content-Length", 0);
 | 
					    http::response<http::buffer_body> *body;
 | 
				
			||||||
    s->fetch(body_len, [this](const std::shared_ptr<restbed::Session> &s, const restbed::Bytes &b){
 | 
					    http::response_serializer<http::buffer_body> *sr;
 | 
				
			||||||
        std::string body{b.cbegin(), b.cend()};
 | 
					 | 
				
			||||||
        if (body.empty())
 | 
					 | 
				
			||||||
            return s->close(400, "empty body");
 | 
					 | 
				
			||||||
        std::string nodes_str, token;
 | 
					 | 
				
			||||||
        for (const auto part : std::views::split(body, '&')) {
 | 
					 | 
				
			||||||
            std::string_view part_view{part};
 | 
					 | 
				
			||||||
            auto equal_pos = part_view.find_first_of('=');
 | 
					 | 
				
			||||||
            auto key = part_view.substr(0, equal_pos);
 | 
					 | 
				
			||||||
            if (key == "nodes")
 | 
					 | 
				
			||||||
                nodes_str = part_view.substr(equal_pos+1);
 | 
					 | 
				
			||||||
            else if (key == "token")
 | 
					 | 
				
			||||||
                token = part_view.substr(equal_pos+1);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (nodes_str.empty())
 | 
					 | 
				
			||||||
            return s->close(400, "Missing nodes");
 | 
					 | 
				
			||||||
        if (token.empty())
 | 
					 | 
				
			||||||
            return s->close(400, "Missing token");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::vector<std::uint64_t> node_ids;
 | 
					    Zip(tcp_stream *s, http::response<http::buffer_body> *body, http::response_serializer<http::buffer_body> *sr)
 | 
				
			||||||
        for (const auto part : std::views::split(nodes_str, '.')) {
 | 
					        : s(s), body(body), sr(sr) {}
 | 
				
			||||||
            std::uint64_t node_id;
 | 
					 | 
				
			||||||
            auto res = std::from_chars(part.data(), part.data() + part.size(), node_id);
 | 
					 | 
				
			||||||
            if (res.ec != std::errc{})
 | 
					 | 
				
			||||||
                return s->close(400, "Invalid node " + std::string{std::string_view{part}});
 | 
					 | 
				
			||||||
            node_ids.push_back(node_id);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        check_user() return s->close(400, "Invalid user");
 | 
					protected:
 | 
				
			||||||
        {
 | 
					    net::awaitable<void> write(const void *pVoid, size_t n) override {
 | 
				
			||||||
            std::shared_lock lock{user->node_lock};
 | 
					        if (n == 0)
 | 
				
			||||||
            std::vector<std::shared_ptr<Node>> nodes;
 | 
					            co_return;
 | 
				
			||||||
            for (auto node_id : node_ids) {
 | 
					
 | 
				
			||||||
                auto node = get_node(user, node_id);
 | 
					        body->body().data = (void*)pVoid;
 | 
				
			||||||
                if (!node) return s->close(400, "Invalid node " + std::to_string(node_id));
 | 
					        body->body().size = n;
 | 
				
			||||||
                nodes.push_back(node);
 | 
					        body->body().more = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            co_await http::async_write(*s, *sr, net::use_awaitable);
 | 
				
			||||||
 | 
					        } catch (beast::system_error &se) {
 | 
				
			||||||
 | 
					            if (se.code() != http::error::need_buffer)
 | 
				
			||||||
 | 
					                throw;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					net::awaitable<void> Server::download_multi(tcp_stream &s, const http::request<http::string_body> &req) {
 | 
				
			||||||
 | 
					    auto body = req.body();
 | 
				
			||||||
 | 
					    if (body.empty()) { co_await send_error(s, req, "Empty body"); co_return; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string nodes_str, token;
 | 
				
			||||||
 | 
					    for (const auto part : std::views::split(body, '&')) {
 | 
				
			||||||
 | 
					        std::string_view part_view{part};
 | 
				
			||||||
 | 
					        auto equal_pos = part_view.find_first_of('=');
 | 
				
			||||||
 | 
					        auto key = part_view.substr(0, equal_pos);
 | 
				
			||||||
 | 
					        if (key == "nodes")
 | 
				
			||||||
 | 
					            nodes_str = part_view.substr(equal_pos+1);
 | 
				
			||||||
 | 
					        else if (key == "token")
 | 
				
			||||||
 | 
					            token = part_view.substr(equal_pos+1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (nodes_str.empty()) { co_await send_error(s, req, "Missing nodes"); co_return; }
 | 
				
			||||||
 | 
					    if (token.empty()) { co_await send_error(s, req, "Missing token"); co_return; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<std::uint64_t> node_ids;
 | 
				
			||||||
 | 
					    for (const auto part : std::views::split(nodes_str, '.')) {
 | 
				
			||||||
 | 
					        std::uint64_t node_id;
 | 
				
			||||||
 | 
					        auto res = std::from_chars(part.data(), part.data() + part.size(), node_id);
 | 
				
			||||||
 | 
					        if (res.ec != std::errc{}) { co_await send_error(s, req, "Invalid node " + std::string{std::string_view{part}}); co_return; }
 | 
				
			||||||
 | 
					        node_ids.push_back(node_id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    check_user() { co_await send_error(s, req, "Invalid user"); co_return; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::shared_lock lock{user->node_lock};
 | 
				
			||||||
 | 
					    std::vector<std::shared_ptr<Node>> nodes;
 | 
				
			||||||
 | 
					    for (auto node_id : node_ids) {
 | 
				
			||||||
 | 
					        auto node = get_node(user, node_id);
 | 
				
			||||||
 | 
					        if (!node) { co_await send_error(s, req, "Invalid node " + std::to_string(node_id)); co_return; }
 | 
				
			||||||
 | 
					        nodes.push_back(node);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto res = create_response<http::status::ok, http::buffer_body, http::string_body>(req);
 | 
				
			||||||
 | 
					    res.chunked(true);
 | 
				
			||||||
 | 
					    res.set(http::field::content_type, "application/zip");
 | 
				
			||||||
 | 
					    res.set(http::field::content_disposition, "attachment; filename=\"files.zip\"");
 | 
				
			||||||
 | 
					    res.body().data = nullptr;
 | 
				
			||||||
 | 
					    res.body().more = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http::response_serializer<http::buffer_body> sr{res};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    co_await http::async_write_header(s, sr, net::use_awaitable);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Zip zip{&s, &res, &sr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::deque<std::pair<std::shared_ptr<Node>, std::filesystem::path>> todo;
 | 
				
			||||||
 | 
					    for (const auto &node : nodes)
 | 
				
			||||||
 | 
					        todo.emplace_back(node, std::filesystem::path{});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (!todo.empty()) {
 | 
				
			||||||
 | 
					        const auto &node = todo.front();
 | 
				
			||||||
 | 
					        if (node.first->file) {
 | 
				
			||||||
 | 
					            auto path = node.second / node.first->name;
 | 
				
			||||||
 | 
					            auto real_path = user->user_dir / std::to_string(node.first->id);
 | 
				
			||||||
 | 
					            co_await zip.add_file(real_path, path);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            auto path = node.second / node.first->name;
 | 
				
			||||||
 | 
					            auto dir_path = path.string() + "/";
 | 
				
			||||||
 | 
					            co_await zip.add_dir(dir_path);
 | 
				
			||||||
 | 
					            for (const auto &child : node.first->children) {
 | 
				
			||||||
 | 
					                auto p = std::make_pair(child, path);
 | 
				
			||||||
 | 
					                todo.push_back(p);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					 | 
				
			||||||
            s->yield(
 | 
					 | 
				
			||||||
                200,
 | 
					 | 
				
			||||||
                "",
 | 
					 | 
				
			||||||
                std::multimap<std::string, std::string>{
 | 
					 | 
				
			||||||
                    {"Content-Type", "application/zip"},
 | 
					 | 
				
			||||||
                    {"Content-Disposition", "attachment; filename=\"files.zip\""},
 | 
					 | 
				
			||||||
                    {"Transfer-Encoding", "chunked"}
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
            std::thread zip_thread{[nodes = nodes, user = user, s = s] {
 | 
					 | 
				
			||||||
                std::shared_lock lock{user->node_lock};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                mz_zip_archive archive;
 | 
					 | 
				
			||||||
                mz_zip_zero_struct(&archive);
 | 
					 | 
				
			||||||
                archive.m_pWrite = zip_write_func;
 | 
					 | 
				
			||||||
                archive.m_pIO_opaque = s.get();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                mz_zip_writer_init_v2(&archive, 0, MZ_ZIP_FLAG_WRITE_ZIP64);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                std::deque<std::pair<std::shared_ptr<Node>, std::filesystem::path>> todo;
 | 
					 | 
				
			||||||
                for (const auto &node : nodes)
 | 
					 | 
				
			||||||
                    todo.emplace_back(node, std::filesystem::path{});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                auto handle_file = [&user, &archive](const std::pair<std::shared_ptr<Node>, std::filesystem::path> &i) {
 | 
					 | 
				
			||||||
                    auto path = i.second / i.first->name;
 | 
					 | 
				
			||||||
                    auto real_path = user->user_dir / std::to_string(i.first->id);
 | 
					 | 
				
			||||||
                    mz_zip_writer_add_file(&archive, path.c_str(), real_path.c_str(), nullptr, 0, MZ_DEFAULT_COMPRESSION);
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                while (!todo.empty()) {
 | 
					 | 
				
			||||||
                    const auto &node = todo.front();
 | 
					 | 
				
			||||||
                    if (node.first->file) {
 | 
					 | 
				
			||||||
                        handle_file(node);
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        auto path = node.second / node.first->name;
 | 
					 | 
				
			||||||
                        auto dir_path = path.string() + "/";
 | 
					 | 
				
			||||||
                        mz_zip_writer_add_mem(&archive, dir_path.c_str(), nullptr, 0, 0);
 | 
					 | 
				
			||||||
                        for (const auto &child : node.first->children) {
 | 
					 | 
				
			||||||
                            auto p = std::make_pair(child, path);
 | 
					 | 
				
			||||||
                            if (child->file)
 | 
					 | 
				
			||||||
                                handle_file(p);
 | 
					 | 
				
			||||||
                            else
 | 
					 | 
				
			||||||
                                todo.push_back(p);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    todo.pop_front();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                mz_zip_writer_finalize_archive(&archive);
 | 
					 | 
				
			||||||
                mz_zip_writer_end(&archive);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                s->close("0\r\n\r\n");
 | 
					 | 
				
			||||||
            }};
 | 
					 | 
				
			||||||
            zip_thread.detach();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    });
 | 
					        todo.pop_front();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    co_await zip.end();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res.body().data = nullptr;
 | 
				
			||||||
 | 
					    res.body().more = false;
 | 
				
			||||||
 | 
					    co_await http::async_write(s, sr, net::use_awaitable);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,43 +34,6 @@ std::string get_path(std::shared_ptr<Node> node) {
 | 
				
			|||||||
    return ret.empty() ? "/" : ret;
 | 
					    return ret.empty() ? "/" : ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Server::delete_node(const std::shared_ptr<User> &user, std::uint64_t id, const std::function<void(std::string)>& log) {
 | 
					 | 
				
			||||||
    std::unique_lock lock{user->node_lock};
 | 
					 | 
				
			||||||
    std::stack<std::shared_ptr<Node>> todo;
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        auto start = user->nodes.find(id);
 | 
					 | 
				
			||||||
        if (start == user->nodes.end()) return;
 | 
					 | 
				
			||||||
        todo.push(start->second);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    while (!todo.empty()) {
 | 
					 | 
				
			||||||
        auto node = todo.top();
 | 
					 | 
				
			||||||
        auto log_path = get_path(node);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (!node->children.empty()) {
 | 
					 | 
				
			||||||
            log("Entering " + log_path + "\n");
 | 
					 | 
				
			||||||
            for (const auto &child : node->children)
 | 
					 | 
				
			||||||
                todo.push(child);
 | 
					 | 
				
			||||||
            node->children.clear();
 | 
					 | 
				
			||||||
            continue;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        log("Deleting " + log_path + "...");
 | 
					 | 
				
			||||||
        if (node->file) {
 | 
					 | 
				
			||||||
            auto path = user->user_dir / std::to_string(node->id);
 | 
					 | 
				
			||||||
            std::filesystem::remove(path);
 | 
					 | 
				
			||||||
            if (node->preview)
 | 
					 | 
				
			||||||
                std::filesystem::remove(path.replace_extension("png"));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (node->parent)
 | 
					 | 
				
			||||||
            node->parent->children.remove(node);
 | 
					 | 
				
			||||||
        node->parent.reset();
 | 
					 | 
				
			||||||
        user->nodes.erase(node->id);
 | 
					 | 
				
			||||||
        log(" Done\n");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        todo.pop();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::uint64_t Server::nodes_size(const std::shared_ptr<User> &user, const std::vector<std::uint64_t> &ids) {
 | 
					std::uint64_t Server::nodes_size(const std::shared_ptr<User> &user, const std::vector<std::uint64_t> &ids) {
 | 
				
			||||||
    std::uint64_t total = 0;
 | 
					    std::uint64_t total = 0;
 | 
				
			||||||
    std::deque<Node*> todo;
 | 
					    std::deque<Node*> todo;
 | 
				
			||||||
@@ -231,22 +194,53 @@ std::optional<std::string> Server::FS_move_nodes(std::string &&token, std::vecto
 | 
				
			|||||||
    return std::nullopt;
 | 
					    return std::nullopt;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Server::FS_delete_nodes(std::string &&token, std::vector<std::uint64_t> &&nodes, mrpc::MRPCStream<std::string> &&stream) {
 | 
					void Server::FS_delete_nodes(std::string &&token, std::vector<std::uint64_t> &&nodes, mrpc::MRPCStream<std::string> &stream) {
 | 
				
			||||||
    check_user() {
 | 
					    check_user() {
 | 
				
			||||||
        stream.close();
 | 
					        stream.close();
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::thread deleter{[this, nodes = std::move(nodes), user = std::move(user), stream = std::move(stream)](){
 | 
					    std::unique_lock lock{user->node_lock};
 | 
				
			||||||
        for (const auto& node : nodes) {
 | 
					    std::stack<std::shared_ptr<Node>> todo;
 | 
				
			||||||
            if (node == 0)
 | 
					    for (const auto &node_id: nodes) {
 | 
				
			||||||
                continue;
 | 
					        if (node_id == 0)
 | 
				
			||||||
            delete_node(user, node, [&stream](const std::string &log){ stream.send(log); });
 | 
					            continue;
 | 
				
			||||||
 | 
					        auto node = user->nodes.find(node_id);
 | 
				
			||||||
 | 
					        if (node == user->nodes.end())
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        todo.push(node->second);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    while (!todo.empty()) {
 | 
				
			||||||
 | 
					        auto node = todo.top();
 | 
				
			||||||
 | 
					        auto log_path = get_path(node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!node->children.empty()) {
 | 
				
			||||||
 | 
					            stream.send("Entering " + log_path + "\n");
 | 
				
			||||||
 | 
					            for (const auto &child: node->children)
 | 
				
			||||||
 | 
					                todo.push(child);
 | 
				
			||||||
 | 
					            node->children.clear();
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        stream.close();
 | 
					
 | 
				
			||||||
        save();
 | 
					        stream.send("Deleting " + log_path + "...");
 | 
				
			||||||
    }};
 | 
					        if (node->file) {
 | 
				
			||||||
    deleter.detach();
 | 
					            auto path = user->user_dir / std::to_string(node->id);
 | 
				
			||||||
 | 
					            std::filesystem::remove(path);
 | 
				
			||||||
 | 
					            if (node->preview)
 | 
				
			||||||
 | 
					                std::filesystem::remove(path.replace_extension("png"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (node->parent)
 | 
				
			||||||
 | 
					            node->parent->children.remove(node);
 | 
				
			||||||
 | 
					        node->parent.reset();
 | 
				
			||||||
 | 
					        user->nodes.erase(node->id);
 | 
				
			||||||
 | 
					        stream.send(" Done\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        todo.pop();
 | 
				
			||||||
 | 
					        std::this_thread::sleep_for(std::chrono::seconds{1});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    stream.close();
 | 
				
			||||||
 | 
					    save();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mrpc::Response<std::string> Server::FS_download_preview(std::string &&token, std::uint64_t &&node_id) {
 | 
					mrpc::Response<std::string> Server::FS_download_preview(std::string &&token, std::uint64_t &&node_id) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,10 @@
 | 
				
			|||||||
#include <asio.hpp>
 | 
					#include <boost/asio/streambuf.hpp>
 | 
				
			||||||
#include <botan_asio/asio_stream.h>
 | 
					 | 
				
			||||||
#include <spdlog/spdlog.h>
 | 
					#include <spdlog/spdlog.h>
 | 
				
			||||||
#include "server_internal.hxx"
 | 
					#include "server_internal.hxx"
 | 
				
			||||||
 | 
					#include "../util/boost.hxx"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct SocketData {
 | 
					struct SocketData {
 | 
				
			||||||
    asio::streambuf socket_buf;
 | 
					    net::streambuf socket_buf;
 | 
				
			||||||
    std::istream socket_istream{&socket_buf};
 | 
					    std::istream socket_istream{&socket_buf};
 | 
				
			||||||
    std::string last_send;
 | 
					    std::string last_send;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@@ -20,7 +20,7 @@ void expect_code(SocketData &data, Socket &s, const std::string& code) {
 | 
				
			|||||||
    std::string line;
 | 
					    std::string line;
 | 
				
			||||||
    do {
 | 
					    do {
 | 
				
			||||||
        std::string buf;
 | 
					        std::string buf;
 | 
				
			||||||
        asio::read_until(s, data.socket_buf, "\n");
 | 
					        net::read_until(s, data.socket_buf, "\n");
 | 
				
			||||||
        std::getline(data.socket_istream, line, '\n');
 | 
					        std::getline(data.socket_istream, line, '\n');
 | 
				
			||||||
    } while (line[3] == '-');
 | 
					    } while (line[3] == '-');
 | 
				
			||||||
    if (std::string_view{line}.substr(0, 3) != code)
 | 
					    if (std::string_view{line}.substr(0, 3) != code)
 | 
				
			||||||
@@ -31,7 +31,7 @@ template<typename Socket>
 | 
				
			|||||||
void send(SocketData &data, Socket &s, std::string l) {
 | 
					void send(SocketData &data, Socket &s, std::string l) {
 | 
				
			||||||
    data.last_send = l.substr(0, l.find_first_of('\r'));
 | 
					    data.last_send = l.substr(0, l.find_first_of('\r'));
 | 
				
			||||||
    l += "\r\n";
 | 
					    l += "\r\n";
 | 
				
			||||||
    asio::write(s, asio::buffer(l, l.size()));
 | 
					    net::write(s, net::buffer(l, l.size()));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::string get_date() {
 | 
					std::string get_date() {
 | 
				
			||||||
@@ -44,7 +44,7 @@ std::string get_date() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
std::string get_hostname() {
 | 
					std::string get_hostname() {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
        return asio::ip::host_name();
 | 
					        return net::ip::host_name();
 | 
				
			||||||
    } catch (std::exception &_) {
 | 
					    } catch (std::exception &_) {
 | 
				
			||||||
        return "";
 | 
					        return "";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -58,7 +58,7 @@ struct CredMan : public Botan::Credentials_Manager {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Policy : public Botan::TLS::Strict_Policy {
 | 
					struct Policy : public Botan::TLS::Policy {
 | 
				
			||||||
    [[nodiscard]] bool require_cert_revocation_info() const override { return false; }
 | 
					    [[nodiscard]] bool require_cert_revocation_info() const override { return false; }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -76,7 +76,7 @@ void Server::send_mail(const std::string &email, const std::string &title, const
 | 
				
			|||||||
            get_date(), email, config.smtp_from, title, body
 | 
					            get_date(), email, config.smtp_from, title, body
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        asio::io_service ctx;
 | 
					        net::io_service ctx;
 | 
				
			||||||
        auto ssl_ctx = std::make_shared<Botan::TLS::Context>(
 | 
					        auto ssl_ctx = std::make_shared<Botan::TLS::Context>(
 | 
				
			||||||
            std::make_shared<CredMan>(),
 | 
					            std::make_shared<CredMan>(),
 | 
				
			||||||
#if defined(BOTAN_HAS_SYSTEM_RNG)
 | 
					#if defined(BOTAN_HAS_SYSTEM_RNG)
 | 
				
			||||||
@@ -88,12 +88,12 @@ void Server::send_mail(const std::string &email, const std::string &title, const
 | 
				
			|||||||
            std::make_shared<Policy>()
 | 
					            std::make_shared<Policy>()
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        asio::ip::tcp::socket s{ctx};
 | 
					        net::ip::tcp::socket s{ctx};
 | 
				
			||||||
        asio::ip::tcp::resolver res{ctx};
 | 
					        net::ip::tcp::resolver res{ctx};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        SocketData data;
 | 
					        SocketData data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        asio::connect(s, res.resolve(config.smtp_host, std::to_string(config.smtp_port)));
 | 
					        net::connect(s, res.resolve(config.smtp_host, std::to_string(config.smtp_port)));
 | 
				
			||||||
        expect_code(data, s, "220");
 | 
					        expect_code(data, s, "220");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        send(data, s, "EHLO " + host_name);
 | 
					        send(data, s, "EHLO " + host_name);
 | 
				
			||||||
@@ -103,7 +103,7 @@ void Server::send_mail(const std::string &email, const std::string &title, const
 | 
				
			|||||||
        expect_code(data, s, "220");
 | 
					        expect_code(data, s, "220");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // switch_to_ssl
 | 
					        // switch_to_ssl
 | 
				
			||||||
        Botan::TLS::Stream<asio::ip::tcp::socket> ss(std::move(s), ssl_ctx);
 | 
					        Botan::TLS::Stream<net::ip::tcp::socket> ss(std::move(s), ssl_ctx);
 | 
				
			||||||
        ss.handshake(Botan::TLS::Connection_Side::Client);
 | 
					        ss.handshake(Botan::TLS::Connection_Side::Client);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        send(data, ss, "EHLO " + host_name);
 | 
					        send(data, ss, "EHLO " + host_name);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
#include "fileserver.hxx"
 | 
					#include "fileserver.hxx"
 | 
				
			||||||
#include <corvusoft/restbed/session.hpp>
 | 
					#include <boost/asio/co_spawn.hpp>
 | 
				
			||||||
#include <corvusoft/restbed/resource.hpp>
 | 
					#include <boost/asio/experimental/awaitable_operators.hpp>
 | 
				
			||||||
#include <corvusoft/restbed/request.hpp>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace boost::asio::experimental::awaitable_operators;
 | 
				
			||||||
using namespace mrpc;
 | 
					using namespace mrpc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -356,67 +356,68 @@ PathSegment& PathSegment::operator<<(const rapidjson::Value &__j) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template<typename T>
 | 
					template<typename T>
 | 
				
			||||||
void send_msg(const std::shared_ptr<restbed::Session> &c, const T &v) {
 | 
					net::awaitable<void> send_msg(tcp_stream &c, const http::request<http::string_body> &req, const T &v) {
 | 
				
			||||||
    if (c->is_closed())
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    rapidjson::StringBuffer s;
 | 
					    rapidjson::StringBuffer s;
 | 
				
			||||||
    mrpc::MRPCJWriter writer{s};
 | 
					    mrpc::MRPCJWriter writer{s};
 | 
				
			||||||
    v >> writer;
 | 
					    v >> writer;
 | 
				
			||||||
    const auto body_ptr = s.GetString();
 | 
					    auto res = create_response<http::status::ok, http::string_body, http::string_body>(req);
 | 
				
			||||||
    const auto body = restbed::Bytes{body_ptr, body_ptr+s.GetLength()};
 | 
					    res.set(http::field::content_type, "application/json");
 | 
				
			||||||
    c->yield(
 | 
					    res.body() = s.GetString();
 | 
				
			||||||
        200,
 | 
					    res.prepare_payload();
 | 
				
			||||||
        body,
 | 
					    co_await http::async_write(c, res, net::use_awaitable);
 | 
				
			||||||
        std::multimap<std::string, std::string>{
 | 
					 | 
				
			||||||
            {"Content-Type", "application/json"},
 | 
					 | 
				
			||||||
            {"Content-Length", std::to_string(body.size())}
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template<typename T>
 | 
					template<typename T>
 | 
				
			||||||
void send_sse_msg(const std::shared_ptr<restbed::Session> &c, const T &v) {
 | 
					net::awaitable<void> send_sse_msg(tcp_stream *c, const T &v) {
 | 
				
			||||||
    if (c->is_closed())
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    rapidjson::StringBuffer s;
 | 
					    rapidjson::StringBuffer s;
 | 
				
			||||||
    std::memcpy(s.Push(5), "data:", 5);
 | 
					    std::memcpy(s.Push(5), "data:", 5);
 | 
				
			||||||
    mrpc::MRPCJWriter writer{s};
 | 
					    mrpc::MRPCJWriter writer{s};
 | 
				
			||||||
    v >> writer;
 | 
					    v >> writer;
 | 
				
			||||||
    std::memcpy(s.Push(2), "\n\n", 2);
 | 
					    std::memcpy(s.Push(2), "\n\n", 2);
 | 
				
			||||||
    const auto body_ptr = s.GetString();
 | 
					    co_await net::async_write(*c, net::buffer(s.GetString(), s.GetLength()), net::use_awaitable);
 | 
				
			||||||
    const auto body = restbed::Bytes{body_ptr, body_ptr+s.GetLength()};
 | 
					 | 
				
			||||||
    c->yield(body);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mrpc::MRPCStreamImpl::MRPCStreamImpl(const std::shared_ptr<restbed::Session> &conn) : conn(conn) {
 | 
					template<typename T>
 | 
				
			||||||
    conn->yield(
 | 
					net::awaitable<void> process_channel(tcp_stream *conn, std::shared_ptr<net::experimental::concurrent_channel<void(boost::system::error_code, T)>> chan) {
 | 
				
			||||||
        200,
 | 
					    http::response<http::empty_body> res{http::status::ok, 11};
 | 
				
			||||||
        std::multimap<std::string, std::string>{
 | 
					    res.keep_alive(false);
 | 
				
			||||||
		    {"Cache-Control", "no-cache"},
 | 
					    res.set(http::field::cache_control, "no-cache");
 | 
				
			||||||
		    {"Content-Type", "text/event-stream"}
 | 
					    res.set(http::field::content_type, "text/event-stream");
 | 
				
			||||||
 | 
					    co_await http::async_write(*conn, res, net::use_awaitable);
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        while (true) {
 | 
				
			||||||
 | 
					            auto v = co_await chan->async_receive(net::use_awaitable);
 | 
				
			||||||
 | 
					            co_await send_sse_msg(conn, v);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    } catch (beast::system_error &se) {
 | 
				
			||||||
 | 
					        if (se.code() != http::error::end_of_stream)
 | 
				
			||||||
 | 
					            throw;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    co_await net::async_write(*conn, net::buffer("data:null\n\n"), net::use_awaitable);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<> void mrpc::MRPCStream<std::string>::close() noexcept {
 | 
				
			||||||
 | 
					    if (!chan->try_send(http::error::end_of_stream, "")) {
 | 
				
			||||||
 | 
					        std::this_thread::sleep_for(std::chrono::seconds{1});
 | 
				
			||||||
 | 
					        chan->try_send(http::error::end_of_stream, "");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    chan->close();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<> MRPCStream<std::string>::MRPCStream(tcp_stream *conn) {
 | 
				
			||||||
 | 
					    chan = std::make_shared<net::experimental::concurrent_channel<void(boost::system::error_code, std::string)>>(conn->get_executor(), 32);
 | 
				
			||||||
 | 
					    net::co_spawn(
 | 
				
			||||||
 | 
					        conn->get_executor(),
 | 
				
			||||||
 | 
					        process_channel<std::string>(conn, chan),
 | 
				
			||||||
 | 
					        err_handler{}
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void mrpc::MRPCStreamImpl::close() const noexcept { conn->close("data:null\n\n"); }
 | 
					template<> void MRPCStream<std::string>::send(const std::string &v) noexcept { chan->try_send(beast::error_code{}, v); }
 | 
				
			||||||
bool mrpc::MRPCStreamImpl::is_open() const noexcept { return conn->is_open(); }
 | 
					 | 
				
			||||||
template<> void MRPCStream<std::string>::send(const std::string &v) const noexcept { send_sse_msg(conn, v); }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					net::awaitable<void> mrpc::MRPCServer::msg_handler(tcp_stream &__c, const http::request<http::string_body> &__req) {
 | 
				
			||||||
mrpc::MRPCServer::MRPCServer(std::shared_ptr<restbed::Resource> &r) {
 | 
					 | 
				
			||||||
    r->set_method_handler("POST", [this](const std::shared_ptr<restbed::Session>& s) {
 | 
					 | 
				
			||||||
        const auto req = s->get_request();
 | 
					 | 
				
			||||||
        const auto body_len = req->get_header("Content-Length", 0);
 | 
					 | 
				
			||||||
        s->fetch(body_len, [this](const std::shared_ptr<restbed::Session> &s, auto &&body) {
 | 
					 | 
				
			||||||
            try { msg_handler(s, body); }
 | 
					 | 
				
			||||||
            catch (const std::exception &_) { s->close(400); }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void mrpc::MRPCServer::msg_handler(const std::shared_ptr<restbed::Session> __c, const restbed::Bytes &__msg) {
 | 
					 | 
				
			||||||
    rapidjson::Document __j;
 | 
					    rapidjson::Document __j;
 | 
				
			||||||
    __j.Parse((const char*)__msg.data(), __msg.size());
 | 
					    __j.Parse(__req.body().data(), __req.body().size());
 | 
				
			||||||
    if (__j.HasParseError())
 | 
					    if (__j.HasParseError())
 | 
				
			||||||
        throw std::exception{};
 | 
					        throw std::exception{};
 | 
				
			||||||
    std::string __service, __method;
 | 
					    std::string __service, __method;
 | 
				
			||||||
@@ -432,73 +433,75 @@ void mrpc::MRPCServer::msg_handler(const std::shared_ptr<restbed::Session> __c,
 | 
				
			|||||||
            std::string username; username << json_get(__data, "username");
 | 
					            std::string username; username << json_get(__data, "username");
 | 
				
			||||||
                        std::string password; password << json_get(__data, "password");
 | 
					                        std::string password; password << json_get(__data, "password");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            send_msg(__c, Auth_signup(std::move(username), std::move(password)));
 | 
					            co_await send_msg(__c, __req, Auth_signup(std::move(username), std::move(password)));
 | 
				
			||||||
        } else if (__method == "login") {
 | 
					        } else if (__method == "login") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string username; username << json_get(__data, "username");
 | 
					            std::string username; username << json_get(__data, "username");
 | 
				
			||||||
                        std::string password; password << json_get(__data, "password");
 | 
					                        std::string password; password << json_get(__data, "password");
 | 
				
			||||||
                        std::optional<std::string> otp; otp << json_get(__data, "otp");
 | 
					                        std::optional<std::string> otp; otp << json_get(__data, "otp");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Auth_login(std::move(username), std::move(password), std::move(otp)));
 | 
					            co_await send_msg(__c, __req, Auth_login(std::move(username), std::move(password), std::move(otp)));
 | 
				
			||||||
        } else if (__method == "send_recovery_key") {
 | 
					        } else if (__method == "send_recovery_key") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string username; username << json_get(__data, "username");
 | 
					            std::string username; username << json_get(__data, "username");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Auth_send_recovery_key(std::move(username)); send_msg(__c, nullptr);
 | 
					            Auth_send_recovery_key(std::move(username));
 | 
				
			||||||
 | 
					            co_await send_msg(__c, __req, nullptr);
 | 
				
			||||||
        } else if (__method == "reset_password") {
 | 
					        } else if (__method == "reset_password") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string key; key << json_get(__data, "key");
 | 
					            std::string key; key << json_get(__data, "key");
 | 
				
			||||||
                        std::string password; password << json_get(__data, "password");
 | 
					                        std::string password; password << json_get(__data, "password");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Auth_reset_password(std::move(key), std::move(password)));
 | 
					            co_await send_msg(__c, __req, Auth_reset_password(std::move(key), std::move(password)));
 | 
				
			||||||
        } else if (__method == "change_password") {
 | 
					        } else if (__method == "change_password") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
                        std::string old_pw; old_pw << json_get(__data, "old_pw");
 | 
					                        std::string old_pw; old_pw << json_get(__data, "old_pw");
 | 
				
			||||||
                        std::string new_pw; new_pw << json_get(__data, "new_pw");
 | 
					                        std::string new_pw; new_pw << json_get(__data, "new_pw");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Auth_change_password(std::move(token), std::move(old_pw), std::move(new_pw)));
 | 
					            co_await send_msg(__c, __req, Auth_change_password(std::move(token), std::move(old_pw), std::move(new_pw)));
 | 
				
			||||||
        } else if (__method == "logout") {
 | 
					        } else if (__method == "logout") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Auth_logout(std::move(token)); send_msg(__c, nullptr);
 | 
					            Auth_logout(std::move(token));
 | 
				
			||||||
 | 
					            co_await send_msg(__c, __req, nullptr);
 | 
				
			||||||
        } else if (__method == "logout_all") {
 | 
					        } else if (__method == "logout_all") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Auth_logout_all(std::move(token)));
 | 
					            co_await send_msg(__c, __req, Auth_logout_all(std::move(token)));
 | 
				
			||||||
        } else if (__method == "tfa_setup_mail") {
 | 
					        } else if (__method == "tfa_setup_mail") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Auth_tfa_setup_mail(std::move(token)));
 | 
					            co_await send_msg(__c, __req, Auth_tfa_setup_mail(std::move(token)));
 | 
				
			||||||
        } else if (__method == "tfa_setup_totp") {
 | 
					        } else if (__method == "tfa_setup_totp") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Auth_tfa_setup_totp(std::move(token)));
 | 
					            co_await send_msg(__c, __req, Auth_tfa_setup_totp(std::move(token)));
 | 
				
			||||||
        } else if (__method == "tfa_complete") {
 | 
					        } else if (__method == "tfa_complete") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
                        std::string otp; otp << json_get(__data, "otp");
 | 
					                        std::string otp; otp << json_get(__data, "otp");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Auth_tfa_complete(std::move(token), std::move(otp)));
 | 
					            co_await send_msg(__c, __req, Auth_tfa_complete(std::move(token), std::move(otp)));
 | 
				
			||||||
        } else if (__method == "tfa_disable") {
 | 
					        } else if (__method == "tfa_disable") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Auth_tfa_disable(std::move(token)));
 | 
					            co_await send_msg(__c, __req, Auth_tfa_disable(std::move(token)));
 | 
				
			||||||
        } else if (__method == "delete_user") {
 | 
					        } else if (__method == "delete_user") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Auth_delete_user(std::move(token)));
 | 
					            co_await send_msg(__c, __req, Auth_delete_user(std::move(token)));
 | 
				
			||||||
        } else if (__method == "session_info") {
 | 
					        } else if (__method == "session_info") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Auth_session_info(std::move(token)));
 | 
					            co_await send_msg(__c, __req, Auth_session_info(std::move(token)));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else { throw std::exception{}; }
 | 
					        else { throw std::exception{}; }
 | 
				
			||||||
    } else if (__service == "Admin") {
 | 
					    } else if (__service == "Admin") {
 | 
				
			||||||
@@ -506,55 +509,55 @@ void mrpc::MRPCServer::msg_handler(const std::shared_ptr<restbed::Session> __c,
 | 
				
			|||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Admin_list_users(std::move(token)));
 | 
					            co_await send_msg(__c, __req, Admin_list_users(std::move(token)));
 | 
				
			||||||
        } else if (__method == "delete_user") {
 | 
					        } else if (__method == "delete_user") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
                        std::uint64_t user; user << json_get(__data, "user");
 | 
					                        std::uint64_t user; user << json_get(__data, "user");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Admin_delete_user(std::move(token), std::move(user)));
 | 
					            co_await send_msg(__c, __req, Admin_delete_user(std::move(token), std::move(user)));
 | 
				
			||||||
        } else if (__method == "logout") {
 | 
					        } else if (__method == "logout") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
                        std::uint64_t user; user << json_get(__data, "user");
 | 
					                        std::uint64_t user; user << json_get(__data, "user");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Admin_logout(std::move(token), std::move(user)));
 | 
					            co_await send_msg(__c, __req, Admin_logout(std::move(token), std::move(user)));
 | 
				
			||||||
        } else if (__method == "disable_tfa") {
 | 
					        } else if (__method == "disable_tfa") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
                        std::uint64_t user; user << json_get(__data, "user");
 | 
					                        std::uint64_t user; user << json_get(__data, "user");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Admin_disable_tfa(std::move(token), std::move(user)));
 | 
					            co_await send_msg(__c, __req, Admin_disable_tfa(std::move(token), std::move(user)));
 | 
				
			||||||
        } else if (__method == "set_admin") {
 | 
					        } else if (__method == "set_admin") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
                        std::uint64_t user; user << json_get(__data, "user");
 | 
					                        std::uint64_t user; user << json_get(__data, "user");
 | 
				
			||||||
                        bool admin; admin << json_get(__data, "admin");
 | 
					                        bool admin; admin << json_get(__data, "admin");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Admin_set_admin(std::move(token), std::move(user), std::move(admin)));
 | 
					            co_await send_msg(__c, __req, Admin_set_admin(std::move(token), std::move(user), std::move(admin)));
 | 
				
			||||||
        } else if (__method == "set_enabled") {
 | 
					        } else if (__method == "set_enabled") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
                        std::uint64_t user; user << json_get(__data, "user");
 | 
					                        std::uint64_t user; user << json_get(__data, "user");
 | 
				
			||||||
                        bool enabled; enabled << json_get(__data, "enabled");
 | 
					                        bool enabled; enabled << json_get(__data, "enabled");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Admin_set_enabled(std::move(token), std::move(user), std::move(enabled)));
 | 
					            co_await send_msg(__c, __req, Admin_set_enabled(std::move(token), std::move(user), std::move(enabled)));
 | 
				
			||||||
        } else if (__method == "sudo") {
 | 
					        } else if (__method == "sudo") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
                        std::uint64_t user; user << json_get(__data, "user");
 | 
					                        std::uint64_t user; user << json_get(__data, "user");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Admin_sudo(std::move(token), std::move(user)));
 | 
					            co_await send_msg(__c, __req, Admin_sudo(std::move(token), std::move(user)));
 | 
				
			||||||
        } else if (__method == "unsudo") {
 | 
					        } else if (__method == "unsudo") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Admin_unsudo(std::move(token)));
 | 
					            co_await send_msg(__c, __req, Admin_unsudo(std::move(token)));
 | 
				
			||||||
        } else if (__method == "shutdown") {
 | 
					        } else if (__method == "shutdown") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, Admin_shutdown(std::move(token)));
 | 
					            co_await send_msg(__c, __req, Admin_shutdown(std::move(token)));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else { throw std::exception{}; }
 | 
					        else { throw std::exception{}; }
 | 
				
			||||||
    } else if (__service == "FS") {
 | 
					    } else if (__service == "FS") {
 | 
				
			||||||
@@ -563,19 +566,19 @@ void mrpc::MRPCServer::msg_handler(const std::shared_ptr<restbed::Session> __c,
 | 
				
			|||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
                        std::uint64_t node; node << json_get(__data, "node");
 | 
					                        std::uint64_t node; node << json_get(__data, "node");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, FS_get_node(std::move(token), std::move(node)));
 | 
					            co_await send_msg(__c, __req, FS_get_node(std::move(token), std::move(node)));
 | 
				
			||||||
        } else if (__method == "get_path") {
 | 
					        } else if (__method == "get_path") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
                        std::uint64_t node; node << json_get(__data, "node");
 | 
					                        std::uint64_t node; node << json_get(__data, "node");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, FS_get_path(std::move(token), std::move(node)));
 | 
					            co_await send_msg(__c, __req, FS_get_path(std::move(token), std::move(node)));
 | 
				
			||||||
        } else if (__method == "get_nodes_size") {
 | 
					        } else if (__method == "get_nodes_size") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
                        std::vector<std::uint64_t> nodes; nodes << json_get(__data, "nodes");
 | 
					                        std::vector<std::uint64_t> nodes; nodes << json_get(__data, "nodes");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, FS_get_nodes_size(std::move(token), std::move(nodes)));
 | 
					            co_await send_msg(__c, __req, FS_get_nodes_size(std::move(token), std::move(nodes)));
 | 
				
			||||||
        } else if (__method == "create_node") {
 | 
					        } else if (__method == "create_node") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
@@ -583,33 +586,33 @@ void mrpc::MRPCServer::msg_handler(const std::shared_ptr<restbed::Session> __c,
 | 
				
			|||||||
                        std::uint64_t parent; parent << json_get(__data, "parent");
 | 
					                        std::uint64_t parent; parent << json_get(__data, "parent");
 | 
				
			||||||
                        std::string name; name << json_get(__data, "name");
 | 
					                        std::string name; name << json_get(__data, "name");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, FS_create_node(std::move(token), std::move(file), std::move(parent), std::move(name)));
 | 
					            co_await send_msg(__c, __req, FS_create_node(std::move(token), std::move(file), std::move(parent), std::move(name)));
 | 
				
			||||||
        } else if (__method == "move_nodes") {
 | 
					        } else if (__method == "move_nodes") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
                        std::vector<std::uint64_t> nodes; nodes << json_get(__data, "nodes");
 | 
					                        std::vector<std::uint64_t> nodes; nodes << json_get(__data, "nodes");
 | 
				
			||||||
                        std::uint64_t parent; parent << json_get(__data, "parent");
 | 
					                        std::uint64_t parent; parent << json_get(__data, "parent");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, FS_move_nodes(std::move(token), std::move(nodes), std::move(parent)));
 | 
					            co_await send_msg(__c, __req, FS_move_nodes(std::move(token), std::move(nodes), std::move(parent)));
 | 
				
			||||||
        } else if (__method == "delete_nodes") {
 | 
					        } else if (__method == "delete_nodes") {
 | 
				
			||||||
            auto __stream = MRPCStream<std::string>{__c};
 | 
					            MRPCStream<std::string> __stream{&__c};
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
                        std::vector<std::uint64_t> nodes; nodes << json_get(__data, "nodes");
 | 
					                        std::vector<std::uint64_t> nodes; nodes << json_get(__data, "nodes");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            FS_delete_nodes(std::move(token), std::move(nodes), std::move(__stream));
 | 
					            FS_delete_nodes(std::move(token), std::move(nodes), __stream);
 | 
				
			||||||
        } else if (__method == "download_preview") {
 | 
					        } else if (__method == "download_preview") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
                        std::uint64_t node; node << json_get(__data, "node");
 | 
					                        std::uint64_t node; node << json_get(__data, "node");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, FS_download_preview(std::move(token), std::move(node)));
 | 
					            co_await send_msg(__c, __req, FS_download_preview(std::move(token), std::move(node)));
 | 
				
			||||||
        } else if (__method == "get_mime") {
 | 
					        } else if (__method == "get_mime") {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            std::string token; token << json_get(__data, "token");
 | 
					            std::string token; token << json_get(__data, "token");
 | 
				
			||||||
                        std::uint64_t node; node << json_get(__data, "node");
 | 
					                        std::uint64_t node; node << json_get(__data, "node");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            send_msg(__c, FS_get_mime(std::move(token), std::move(node)));
 | 
					            co_await send_msg(__c, __req, FS_get_mime(std::move(token), std::move(node)));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else { throw std::exception{}; }
 | 
					        else { throw std::exception{}; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,16 +8,11 @@
 | 
				
			|||||||
#include <optional>
 | 
					#include <optional>
 | 
				
			||||||
#include <cstdint>
 | 
					#include <cstdint>
 | 
				
			||||||
#include <cmath>
 | 
					#include <cmath>
 | 
				
			||||||
#include <corvusoft/restbed/byte.hpp>
 | 
					 | 
				
			||||||
#define RAPIDJSON_HAS_STDSTRING 1
 | 
					#define RAPIDJSON_HAS_STDSTRING 1
 | 
				
			||||||
#include <rapidjson/stringbuffer.h>
 | 
					#include <rapidjson/stringbuffer.h>
 | 
				
			||||||
#include <rapidjson/writer.h>
 | 
					#include <rapidjson/writer.h>
 | 
				
			||||||
#include <rapidjson/document.h>
 | 
					#include <rapidjson/document.h>
 | 
				
			||||||
 | 
					#include "../../util/boost.hxx"
 | 
				
			||||||
namespace restbed {
 | 
					 | 
				
			||||||
    class Resource;
 | 
					 | 
				
			||||||
    class Session;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace mrpc {
 | 
					namespace mrpc {
 | 
				
			||||||
using MRPCJWriter = rapidjson::Writer<rapidjson::StringBuffer>;
 | 
					using MRPCJWriter = rapidjson::Writer<rapidjson::StringBuffer>;
 | 
				
			||||||
@@ -103,25 +98,20 @@ struct PathSegment {
 | 
				
			|||||||
    PathSegment& operator <<(const rapidjson::Value&);
 | 
					    PathSegment& operator <<(const rapidjson::Value&);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct MRPCStreamImpl {
 | 
					 | 
				
			||||||
    void close() const noexcept;
 | 
					 | 
				
			||||||
    bool is_open() const noexcept;
 | 
					 | 
				
			||||||
protected:
 | 
					 | 
				
			||||||
    explicit MRPCStreamImpl(const std::shared_ptr<restbed::Session> &conn);
 | 
					 | 
				
			||||||
    std::shared_ptr<restbed::Session> conn;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template<typename T>
 | 
					template<typename T>
 | 
				
			||||||
struct MRPCStream final : MRPCStreamImpl {
 | 
					struct MRPCStream final {
 | 
				
			||||||
    explicit MRPCStream(const std::shared_ptr<restbed::Session> &conn) : MRPCStreamImpl(conn) {}
 | 
					    explicit MRPCStream(tcp_stream *conn);
 | 
				
			||||||
    void send(const T &v) const noexcept;
 | 
					    void send(const T &v) noexcept;
 | 
				
			||||||
 | 
					    void close() noexcept;
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    std::shared_ptr<net::experimental::concurrent_channel<void(boost::system::error_code, T)>> chan;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template struct MRPCStream<std::string>;
 | 
					template struct MRPCStream<std::string>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct MRPCServer {
 | 
					struct MRPCServer {
 | 
				
			||||||
    MRPCServer() = delete;
 | 
					    net::awaitable<void> msg_handler(tcp_stream&, const http::request<http::string_body>&);
 | 
				
			||||||
    explicit MRPCServer(std::shared_ptr<restbed::Resource>&);
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    virtual std::optional<std::string> Auth_signup(std::string &&username, std::string &&password) = 0;
 | 
					    virtual std::optional<std::string> Auth_signup(std::string &&username, std::string &&password) = 0;
 | 
				
			||||||
    virtual Response<LoginResponse> Auth_login(std::string &&username, std::string &&password, std::optional<std::string> &&otp) = 0;
 | 
					    virtual Response<LoginResponse> Auth_login(std::string &&username, std::string &&password, std::optional<std::string> &&otp) = 0;
 | 
				
			||||||
@@ -150,11 +140,9 @@ private:
 | 
				
			|||||||
    virtual Response<std::uint64_t> FS_get_nodes_size(std::string &&token, std::vector<std::uint64_t> &&nodes) = 0;
 | 
					    virtual Response<std::uint64_t> FS_get_nodes_size(std::string &&token, std::vector<std::uint64_t> &&nodes) = 0;
 | 
				
			||||||
    virtual Response<CreateNodeInfo> FS_create_node(std::string &&token, bool &&file, std::uint64_t &&parent, std::string &&name) = 0;
 | 
					    virtual Response<CreateNodeInfo> FS_create_node(std::string &&token, bool &&file, std::uint64_t &&parent, std::string &&name) = 0;
 | 
				
			||||||
    virtual std::optional<std::string> FS_move_nodes(std::string &&token, std::vector<std::uint64_t> &&nodes, std::uint64_t &&parent) = 0;
 | 
					    virtual std::optional<std::string> FS_move_nodes(std::string &&token, std::vector<std::uint64_t> &&nodes, std::uint64_t &&parent) = 0;
 | 
				
			||||||
    virtual void FS_delete_nodes(std::string &&token, std::vector<std::uint64_t> &&nodes, MRPCStream<std::string>&&) = 0;
 | 
					    virtual void FS_delete_nodes(std::string &&token, std::vector<std::uint64_t> &&nodes, MRPCStream<std::string>&) = 0;
 | 
				
			||||||
    virtual Response<std::string> FS_download_preview(std::string &&token, std::uint64_t &&node) = 0;
 | 
					    virtual Response<std::string> FS_download_preview(std::string &&token, std::uint64_t &&node) = 0;
 | 
				
			||||||
    virtual Response<std::string> FS_get_mime(std::string &&token, std::uint64_t &&node) = 0;
 | 
					    virtual Response<std::string> FS_get_mime(std::string &&token, std::uint64_t &&node) = 0;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    virtual void msg_handler(std::shared_ptr<restbed::Session>, const restbed::Bytes&) final;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,7 +44,16 @@ void Server::logout_user(std::uint64_t id) {
 | 
				
			|||||||
void Server::delete_user(const std::shared_ptr<User> &user) {
 | 
					void Server::delete_user(const std::shared_ptr<User> &user) {
 | 
				
			||||||
    std::unique_lock lock{user_lock};
 | 
					    std::unique_lock lock{user_lock};
 | 
				
			||||||
    logout_user(user->id);
 | 
					    logout_user(user->id);
 | 
				
			||||||
    delete_node(user, 0, [](const std::string&){});
 | 
					    for (const auto &[node_id, node] : user->nodes) {
 | 
				
			||||||
 | 
					        node->parent.reset();
 | 
				
			||||||
 | 
					        node->children.clear();
 | 
				
			||||||
 | 
					        if (node->file) {
 | 
				
			||||||
 | 
					            auto path = user->user_dir / std::to_string(node->id);
 | 
				
			||||||
 | 
					            std::filesystem::remove(path);
 | 
				
			||||||
 | 
					            if (node->preview)
 | 
				
			||||||
 | 
					                std::filesystem::remove(path.replace_extension("png"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    users.erase(user->id);
 | 
					    users.erase(user->id);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,20 +1,18 @@
 | 
				
			|||||||
#ifndef FILESERVER_SERVER_HXX
 | 
					#ifndef FILESERVER_SERVER_HXX
 | 
				
			||||||
#define FILESERVER_SERVER_HXX
 | 
					#define FILESERVER_SERVER_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <corvusoft/restbed/service.hpp>
 | 
					 | 
				
			||||||
#include "mrpc/fileserver.hxx"
 | 
					#include "mrpc/fileserver.hxx"
 | 
				
			||||||
#include "../data/data.hxx"
 | 
					#include "../data/data.hxx"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern std::shared_ptr<restbed::Service> g_service;
 | 
					extern std::function<void()> g_stop_service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Server final : public mrpc::MRPCServer, public Data {
 | 
					struct Server final : public mrpc::MRPCServer, public Data {
 | 
				
			||||||
    explicit Server(std::shared_ptr<restbed::Resource> &ptr) : MRPCServer(ptr), Data() {}
 | 
					    explicit Server() : MRPCServer(), Data() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::shared_ptr<Token> get_token(const std::string&);
 | 
					    std::shared_ptr<Token> get_token(const std::string&);
 | 
				
			||||||
    std::shared_ptr<User> is_token_valid(const std::string&);
 | 
					    std::shared_ptr<User> is_token_valid(const std::string&);
 | 
				
			||||||
    std::shared_ptr<User> get_user(std::uint64_t id);
 | 
					    std::shared_ptr<User> get_user(std::uint64_t id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static void delete_node(const std::shared_ptr<User> &user, std::uint64_t id, const std::function<void(std::string)>& log);
 | 
					 | 
				
			||||||
    void logout_user(std::uint64_t id);
 | 
					    void logout_user(std::uint64_t id);
 | 
				
			||||||
    void delete_user(const std::shared_ptr<User> &user);
 | 
					    void delete_user(const std::shared_ptr<User> &user);
 | 
				
			||||||
    void send_tfa_mail(const std::shared_ptr<User> &user);
 | 
					    void send_tfa_mail(const std::shared_ptr<User> &user);
 | 
				
			||||||
@@ -23,10 +21,9 @@ struct Server final : public mrpc::MRPCServer, public Data {
 | 
				
			|||||||
    void send_mail(const std::string& email, const std::string& title, const std::string& body);
 | 
					    void send_mail(const std::string& email, const std::string& title, const std::string& body);
 | 
				
			||||||
    std::uint64_t nodes_size(const std::shared_ptr<User> &user, const std::vector<std::uint64_t> &ids);
 | 
					    std::uint64_t nodes_size(const std::shared_ptr<User> &user, const std::vector<std::uint64_t> &ids);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void download(const std::shared_ptr<restbed::Session>&);
 | 
					    net::awaitable<void> download(tcp_stream&, const http::request<http::string_body>&);
 | 
				
			||||||
    void download_multi(const std::shared_ptr<restbed::Session>&);
 | 
					    net::awaitable<void> download_multi(tcp_stream&, const http::request<http::string_body>&);
 | 
				
			||||||
    void upload(const std::shared_ptr<restbed::Session>&);
 | 
					    net::awaitable<void> upload(tcp_stream&, tcp_buffer&, http::request_parser<http::file_body>&);
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    std::optional<std::string> Auth_signup(std::string &&username, std::string &&password) override;
 | 
					    std::optional<std::string> Auth_signup(std::string &&username, std::string &&password) override;
 | 
				
			||||||
@@ -58,7 +55,7 @@ private:
 | 
				
			|||||||
    mrpc::Response<std::uint64_t> FS_get_nodes_size(std::string &&token, std::vector<std::uint64_t> &&nodes) override;
 | 
					    mrpc::Response<std::uint64_t> FS_get_nodes_size(std::string &&token, std::vector<std::uint64_t> &&nodes) override;
 | 
				
			||||||
    mrpc::Response<mrpc::CreateNodeInfo> FS_create_node(std::string &&token, bool &&file, std::uint64_t &&parent, std::string &&name) override;
 | 
					    mrpc::Response<mrpc::CreateNodeInfo> FS_create_node(std::string &&token, bool &&file, std::uint64_t &&parent, std::string &&name) override;
 | 
				
			||||||
    std::optional<std::string> FS_move_nodes(std::string &&token, std::vector<std::uint64_t> &&nodes, std::uint64_t &&parent) override;
 | 
					    std::optional<std::string> FS_move_nodes(std::string &&token, std::vector<std::uint64_t> &&nodes, std::uint64_t &&parent) override;
 | 
				
			||||||
    void FS_delete_nodes(std::string &&token, std::vector<std::uint64_t> &&nodes, mrpc::MRPCStream<std::string> &&stream) override;
 | 
					    void FS_delete_nodes(std::string &&token, std::vector<std::uint64_t> &&nodes, mrpc::MRPCStream<std::string> &stream) override;
 | 
				
			||||||
    mrpc::Response<std::string> FS_download_preview(std::string &&token, std::uint64_t &&node) override;
 | 
					    mrpc::Response<std::string> FS_download_preview(std::string &&token, std::uint64_t &&node) override;
 | 
				
			||||||
    mrpc::Response<std::string> FS_get_mime(std::string &&token, std::uint64_t &&node) override;
 | 
					    mrpc::Response<std::string> FS_get_mime(std::string &&token, std::uint64_t &&node) override;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
#ifndef FILESERVER_SERVER_INTERNAL_HXX
 | 
					#ifndef FILESERVER_SERVER_INTERNAL_HXX
 | 
				
			||||||
#define FILESERVER_SERVER_INTERNAL_HXX
 | 
					#define FILESERVER_SERVER_INTERNAL_HXX
 | 
				
			||||||
 | 
					#include "../util/botan.hxx"
 | 
				
			||||||
#include <botan_all.h>
 | 
					 | 
				
			||||||
#include "server.hxx"
 | 
					#include "server.hxx"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO log user action with __FUNC__
 | 
					// TODO log user action with __FUNC__
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,4 @@
 | 
				
			|||||||
#include <fstream>
 | 
					#include <fstream>
 | 
				
			||||||
#include <corvusoft/restbed/session.hpp>
 | 
					 | 
				
			||||||
#include <corvusoft/restbed/request.hpp>
 | 
					 | 
				
			||||||
#include <corvusoft/restbed/response.hpp>
 | 
					 | 
				
			||||||
#include <spdlog/spdlog.h>
 | 
					#include <spdlog/spdlog.h>
 | 
				
			||||||
#include <stb_image.h>
 | 
					#include <stb_image.h>
 | 
				
			||||||
#include <stb_image_resize2.h>
 | 
					#include <stb_image_resize2.h>
 | 
				
			||||||
@@ -20,10 +17,10 @@ struct UploadInfo {
 | 
				
			|||||||
    std::shared_ptr<Node> node;
 | 
					    std::shared_ptr<Node> node;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void make_preview(const std::shared_ptr<UploadInfo>& info) {
 | 
					void make_preview(std::filesystem::path &path, const std::shared_ptr<Node> &node) {
 | 
				
			||||||
    int x, y, channels;
 | 
					    int x, y, channels;
 | 
				
			||||||
    auto img = std::unique_ptr<stbi_uc, decltype(&free)>
 | 
					    auto img = std::unique_ptr<stbi_uc, decltype(&free)>
 | 
				
			||||||
        {stbi_load(info->path.c_str(), &x, &y, &channels, 0), &free};
 | 
					        {stbi_load(path.c_str(), &x, &y, &channels, 0), &free};
 | 
				
			||||||
    if (!img)
 | 
					    if (!img)
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -44,70 +41,66 @@ void make_preview(const std::shared_ptr<UploadInfo>& info) {
 | 
				
			|||||||
    if (!rimg)
 | 
					    if (!rimg)
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto png_path = info->path.replace_extension("png");
 | 
					    auto png_path = path.replace_extension("png");
 | 
				
			||||||
    if (!stbi_write_png(png_path.c_str(), new_x, new_y, channels, rimg.get(), 0))
 | 
					    if (!stbi_write_png(png_path.c_str(), new_x, new_y, channels, rimg.get(), 0))
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    info->node->preview = true;
 | 
					    node->preview = true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void fetch_handler(const std::shared_ptr<restbed::Session> &s, const restbed::Bytes &bytes) {
 | 
					net::awaitable<void> Server::upload(tcp_stream &s, tcp_buffer &buf, http::request_parser<http::file_body> &req) {
 | 
				
			||||||
    std::shared_ptr<UploadInfo> info = s->get("upload");
 | 
					    auto &body = req.get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::size_t read = bytes.size();
 | 
					    if (body["X-Node"].empty()) { co_await send_error(s, body, "Missing node"); co_return; }
 | 
				
			||||||
    info->to_read -= std::min(read, info->to_read);
 | 
					    if (body["X-Token"].empty()) { co_await send_error(s, body, "Missing token"); co_return; }
 | 
				
			||||||
    info->file.write((char*)bytes.data(), bytes.size());
 | 
					 | 
				
			||||||
    if (info->to_read > 0)
 | 
					 | 
				
			||||||
        return s->fetch(std::min(info->to_read, chunk_size), fetch_handler);
 | 
					 | 
				
			||||||
    info->file.close();
 | 
					 | 
				
			||||||
    s->close(200);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::size_t real_size = std::filesystem::file_size(info->path);
 | 
					    if (body[http::field::transfer_encoding] == "chunked") {
 | 
				
			||||||
    info->node->size = real_size;
 | 
					        spdlog::critical("Encountered a chunked upload!");
 | 
				
			||||||
    auto ext = std::filesystem::path{info->node->name}.extension().string();
 | 
					        co_await send_error(s, body, "Sorry but your browser is not supported yet"); co_return;
 | 
				
			||||||
    if (real_size < max_image_size && image_extension.contains(ext))
 | 
					 | 
				
			||||||
        make_preview(info);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    info->node_lock.unlock();
 | 
					 | 
				
			||||||
    info->server->save();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void Server::upload(const std::shared_ptr<restbed::Session> &s) {
 | 
					 | 
				
			||||||
    const auto req = s->get_request();
 | 
					 | 
				
			||||||
    if (!req->has_header("X-Node"))
 | 
					 | 
				
			||||||
        return s->close(400, "Missing node");
 | 
					 | 
				
			||||||
    if (!req->has_header("X-Token"))
 | 
					 | 
				
			||||||
        return s->close(400, "Missing token");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (req->get_header("Transfer-Encoding") == "chunked") {
 | 
					 | 
				
			||||||
        spdlog::error("Encountered a chunked upload!");
 | 
					 | 
				
			||||||
        return s->close(500, "Sorry but your browser is not supported yet");
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::uint64_t node_id = req->get_header("X-Node", 0);
 | 
					    auto node_str = body["X-Node"];
 | 
				
			||||||
    std::string token = req->get_header("X-Token");
 | 
					    std::uint64_t node_id = 0;
 | 
				
			||||||
 | 
					    std::from_chars(node_str.data(), node_str.data() + node_str.size(), node_id);
 | 
				
			||||||
 | 
					    std::string token = body["X-Token"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    check_user() return s->close(400, "Invalid user");
 | 
					    check_user() { co_await send_error(s, body, "Invalid user"); co_return; }
 | 
				
			||||||
    {
 | 
					
 | 
				
			||||||
        std::shared_lock lock{user->node_lock};
 | 
					    auto node = get_node(user, node_id);
 | 
				
			||||||
        auto node = get_node(user, node_id);
 | 
					    if (!node) { co_await send_error(s, body, "Invalid node"); co_return; }
 | 
				
			||||||
        if (!node) return s->close(400, "Invalid node");
 | 
					    if (!node->file) { co_await send_error(s, body, "Can't upload to a directory"); co_return; }
 | 
				
			||||||
        if (!node->file) return s->close(400, "Can't upload to a directory");
 | 
					
 | 
				
			||||||
        auto to_read = req->get_header<std::size_t>("Content-Length", 0);
 | 
					    auto path = user->user_dir / std::to_string(node->id);
 | 
				
			||||||
        auto path = user->user_dir / std::to_string(node->id);
 | 
					    if (node->preview) {
 | 
				
			||||||
        if (node->preview) {
 | 
					        node->preview = false;
 | 
				
			||||||
            node->preview = false;
 | 
					        std::filesystem::remove(path.replace_extension("png"));
 | 
				
			||||||
            std::filesystem::remove(path.replace_extension("png"));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        std::shared_ptr<UploadInfo> info{new UploadInfo{
 | 
					 | 
				
			||||||
            .server = this,
 | 
					 | 
				
			||||||
            .node_lock = std::shared_lock{user->node_lock},
 | 
					 | 
				
			||||||
            .to_read = to_read,
 | 
					 | 
				
			||||||
            .path = path,
 | 
					 | 
				
			||||||
            .file = std::ofstream{path, std::ios_base::out|std::ios_base::trunc|std::ios_base::binary},
 | 
					 | 
				
			||||||
            .node = node
 | 
					 | 
				
			||||||
        }};
 | 
					 | 
				
			||||||
        s->set("upload", info);
 | 
					 | 
				
			||||||
        s->fetch(std::min(to_read, chunk_size), fetch_handler);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::exception_ptr ex_ptr;
 | 
				
			||||||
 | 
					    beast::error_code ec;
 | 
				
			||||||
 | 
					    body.body().open(path.c_str(), beast::file_mode::write, ec);
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        co_await http::async_read(s, buf, req);
 | 
				
			||||||
 | 
					    } catch (beast::system_error &se) {
 | 
				
			||||||
 | 
					        ex_ptr = make_exception_ptr(se);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    body.body().close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (ex_ptr)
 | 
				
			||||||
 | 
					        std::filesystem::resize_file(path, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::size_t real_size = std::filesystem::file_size(path);
 | 
				
			||||||
 | 
					    node->size = real_size;
 | 
				
			||||||
 | 
					    auto ext = std::filesystem::path{node->name}.extension().string();
 | 
				
			||||||
 | 
					    if (real_size > 0 && real_size < max_image_size && image_extension.contains(ext))
 | 
				
			||||||
 | 
					        make_preview(path, node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto res = create_response<http::status::ok, http::empty_body, http::file_body>(body);
 | 
				
			||||||
 | 
					    res.keep_alive(false);
 | 
				
			||||||
 | 
					    co_await http::async_write(s, res, net::use_awaitable);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (ex_ptr)
 | 
				
			||||||
 | 
					        rethrow_exception(ex_ptr);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										66
									
								
								src/util/boost.hxx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/util/boost.hxx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					#ifndef FILESERVER_BOOST_HXX
 | 
				
			||||||
 | 
					#define FILESERVER_BOOST_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <spdlog/spdlog.h>
 | 
				
			||||||
 | 
					#include <boost/beast/core.hpp>
 | 
				
			||||||
 | 
					#include <boost/beast/http.hpp>
 | 
				
			||||||
 | 
					#include <boost/asio/write.hpp>
 | 
				
			||||||
 | 
					#include <boost/asio/awaitable.hpp>
 | 
				
			||||||
 | 
					#include <boost/asio/use_awaitable.hpp>
 | 
				
			||||||
 | 
					#include <boost/asio/experimental/concurrent_channel.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace beast = boost::beast;
 | 
				
			||||||
 | 
					namespace http = beast::http;
 | 
				
			||||||
 | 
					namespace net = boost::asio;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using tcp_stream = typename beast::tcp_stream::rebind_executor<net::use_awaitable_t<>::executor_with_default<net::any_io_executor>>::other;
 | 
				
			||||||
 | 
					using tcp_buffer = beast::flat_static_buffer<65535>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<http::status Status, typename BodyType, typename ReqBody>
 | 
				
			||||||
 | 
					http::response<BodyType> create_response(const http::request<ReqBody> &req) {
 | 
				
			||||||
 | 
					    http::response<BodyType> res{Status, req.version()};
 | 
				
			||||||
 | 
					    res.keep_alive(req.keep_alive());
 | 
				
			||||||
 | 
					    return std::move(res);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename ReqBody>
 | 
				
			||||||
 | 
					net::awaitable<void> send_error(tcp_stream &s, const http::request<ReqBody> &req, std::string &&body) {
 | 
				
			||||||
 | 
					    auto res = create_response<http::status::bad_request, http::string_body, ReqBody>(req);
 | 
				
			||||||
 | 
					    res.body() = body;
 | 
				
			||||||
 | 
					    co_await http::async_write(s, res, net::use_awaitable);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct err_handler {
 | 
				
			||||||
 | 
					    std::source_location loc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    explicit err_handler(std::source_location loc = std::source_location::current()) : loc(loc) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void operator()(const std::exception_ptr& e) const {
 | 
				
			||||||
 | 
					        if (!e) return;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            std::rethrow_exception(e);
 | 
				
			||||||
 | 
					        } catch (beast::system_error &se) {
 | 
				
			||||||
 | 
					            log(se.what());
 | 
				
			||||||
 | 
					        } catch (std::exception &e1) {
 | 
				
			||||||
 | 
					            log(e1.what());
 | 
				
			||||||
 | 
					            //std::cerr << "Error in " << loc.file_name() << ":" << loc.line() << " `" << loc.function_name() << "`\n" << e1.what() << "\n";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    static std::shared_ptr<spdlog::logger> logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    template<typename MsgType>
 | 
				
			||||||
 | 
					    void log(MsgType msg) const {
 | 
				
			||||||
 | 
					        if (!logger)
 | 
				
			||||||
 | 
					            logger = spdlog::default_logger()->clone("err_handler");
 | 
				
			||||||
 | 
					        spdlog::source_loc spd_loc;
 | 
				
			||||||
 | 
					        spd_loc.filename = loc.file_name();
 | 
				
			||||||
 | 
					        spd_loc.line = (int)loc.line();
 | 
				
			||||||
 | 
					        spd_loc.funcname = loc.function_name();
 | 
				
			||||||
 | 
					        spdlog::log(spd_loc, spdlog::level::err, msg);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif //FILESERVER_BOOST_HXX
 | 
				
			||||||
							
								
								
									
										8
									
								
								src/util/botan.hxx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/util/botan.hxx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					#ifndef FILESERVER_BOTAN_HXX
 | 
				
			||||||
 | 
					#define FILESERVER_BOTAN_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <liburing.h>
 | 
				
			||||||
 | 
					#undef BLOCK_SIZE
 | 
				
			||||||
 | 
					#include <botan_all.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif //FILESERVER_BOTAN_HXX
 | 
				
			||||||
@@ -1,60 +0,0 @@
 | 
				
			|||||||
#ifndef FILESERVER_LOGGING_HXX
 | 
					 | 
				
			||||||
#define FILESERVER_LOGGING_HXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <cstdarg>
 | 
					 | 
				
			||||||
#include <spdlog/spdlog.h>
 | 
					 | 
				
			||||||
#include <corvusoft/restbed/logger.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace {
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace logging {
 | 
					 | 
				
			||||||
    struct RestbedLogger : public restbed::Logger {
 | 
					 | 
				
			||||||
        void stop() override {}
 | 
					 | 
				
			||||||
        void start(const std::shared_ptr<const restbed::Settings>&) override {
 | 
					 | 
				
			||||||
            logger = spdlog::default_logger()->clone("restbed");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        void log(Level level, const char *format, ...) override {
 | 
					 | 
				
			||||||
            std::va_list args;
 | 
					 | 
				
			||||||
            va_start(args, format);
 | 
					 | 
				
			||||||
            restbed_log(level, format, args);
 | 
					 | 
				
			||||||
            va_end(args);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        void log_if(bool expression, Level level, const char *format, ...) override {
 | 
					 | 
				
			||||||
            if (expression) {
 | 
					 | 
				
			||||||
                va_list args;
 | 
					 | 
				
			||||||
                va_start(args, format);
 | 
					 | 
				
			||||||
                restbed_log(level, format, args);
 | 
					 | 
				
			||||||
                va_end(args);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private:
 | 
					 | 
				
			||||||
        std::shared_ptr<spdlog::logger> logger;
 | 
					 | 
				
			||||||
        void restbed_log(const restbed::Logger::Level restbed_level, const char* format, va_list args) {
 | 
					 | 
				
			||||||
            spdlog::level::level_enum level;
 | 
					 | 
				
			||||||
            switch (restbed_level) {
 | 
					 | 
				
			||||||
                case restbed::Logger::DEBUG:   level = spdlog::level::level_enum::debug;    break;
 | 
					 | 
				
			||||||
                case restbed::Logger::INFO:    level = spdlog::level::level_enum::info;     break;
 | 
					 | 
				
			||||||
                case restbed::Logger::WARNING: level = spdlog::level::level_enum::warn;     break;
 | 
					 | 
				
			||||||
                case restbed::Logger::ERROR:   level = spdlog::level::level_enum::err;      break;
 | 
					 | 
				
			||||||
                case restbed::Logger::SECURITY:
 | 
					 | 
				
			||||||
                case restbed::Logger::FATAL:   level = spdlog::level::level_enum::critical; break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            std::string buf;
 | 
					 | 
				
			||||||
            buf.resize(1024);
 | 
					 | 
				
			||||||
            int written = vsnprintf(buf.data(), 1024, format, args);
 | 
					 | 
				
			||||||
            //if (std::string_view{buf.cbegin(), buf.cbegin()+10} == "Incoming '")
 | 
					 | 
				
			||||||
            //    return;
 | 
					 | 
				
			||||||
            if (written >= 1024) {
 | 
					 | 
				
			||||||
                buf.resize(written + 10);
 | 
					 | 
				
			||||||
                written = vsnprintf(buf.data(), written + 10, format, args);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            buf.resize(written);
 | 
					 | 
				
			||||||
            logger->log(level, buf);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif //FILESERVER_LOGGING_HXX
 | 
					 | 
				
			||||||
							
								
								
									
										476
									
								
								src/util/miniz.cxx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										476
									
								
								src/util/miniz.cxx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,476 @@
 | 
				
			|||||||
 | 
					#include <cstring>
 | 
				
			||||||
 | 
					#include <boost/asio/stream_file.hpp>
 | 
				
			||||||
 | 
					#include <boost/asio/use_awaitable.hpp>
 | 
				
			||||||
 | 
					#include <boost/array.hpp>
 | 
				
			||||||
 | 
					#include "miniz.hxx"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace asio = boost::asio;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MZ_UINT16_MAX (0xFFFFU)
 | 
				
			||||||
 | 
					#define MZ_UINT32_MAX (0xFFFFFFFFU)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef _MSC_VER
 | 
				
			||||||
 | 
					#define MZ_FORCEINLINE __forceinline
 | 
				
			||||||
 | 
					#elif defined(__GNUC__)
 | 
				
			||||||
 | 
					#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__))
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define MZ_FORCEINLINE inline
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /* ZIP archive identifiers and record sizes */
 | 
				
			||||||
 | 
					    MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50,
 | 
				
			||||||
 | 
					    MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50,
 | 
				
			||||||
 | 
					    MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50,
 | 
				
			||||||
 | 
					    MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30,
 | 
				
			||||||
 | 
					    MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46,
 | 
				
			||||||
 | 
					    MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* ZIP64 archive identifier and record sizes */
 | 
				
			||||||
 | 
					    MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50,
 | 
				
			||||||
 | 
					    MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50,
 | 
				
			||||||
 | 
					    MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56,
 | 
				
			||||||
 | 
					    MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20,
 | 
				
			||||||
 | 
					    MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001,
 | 
				
			||||||
 | 
					    MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50,
 | 
				
			||||||
 | 
					    MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24,
 | 
				
			||||||
 | 
					    MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Central directory header record offsets */
 | 
				
			||||||
 | 
					    MZ_ZIP_CDH_SIG_OFS = 0,
 | 
				
			||||||
 | 
					    MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4,
 | 
				
			||||||
 | 
					    MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6,
 | 
				
			||||||
 | 
					    MZ_ZIP_CDH_BIT_FLAG_OFS = 8,
 | 
				
			||||||
 | 
					    MZ_ZIP_CDH_METHOD_OFS = 10,
 | 
				
			||||||
 | 
					    MZ_ZIP_CDH_FILE_TIME_OFS = 12,
 | 
				
			||||||
 | 
					    MZ_ZIP_CDH_FILE_DATE_OFS = 14,
 | 
				
			||||||
 | 
					    MZ_ZIP_CDH_CRC32_OFS = 16,
 | 
				
			||||||
 | 
					    MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20,
 | 
				
			||||||
 | 
					    MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24,
 | 
				
			||||||
 | 
					    MZ_ZIP_CDH_FILENAME_LEN_OFS = 28,
 | 
				
			||||||
 | 
					    MZ_ZIP_CDH_EXTRA_LEN_OFS = 30,
 | 
				
			||||||
 | 
					    MZ_ZIP_CDH_COMMENT_LEN_OFS = 32,
 | 
				
			||||||
 | 
					    MZ_ZIP_CDH_DISK_START_OFS = 34,
 | 
				
			||||||
 | 
					    MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36,
 | 
				
			||||||
 | 
					    MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38,
 | 
				
			||||||
 | 
					    MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Local directory header offsets */
 | 
				
			||||||
 | 
					    MZ_ZIP_LDH_SIG_OFS = 0,
 | 
				
			||||||
 | 
					    MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4,
 | 
				
			||||||
 | 
					    MZ_ZIP_LDH_BIT_FLAG_OFS = 6,
 | 
				
			||||||
 | 
					    MZ_ZIP_LDH_METHOD_OFS = 8,
 | 
				
			||||||
 | 
					    MZ_ZIP_LDH_FILE_TIME_OFS = 10,
 | 
				
			||||||
 | 
					    MZ_ZIP_LDH_FILE_DATE_OFS = 12,
 | 
				
			||||||
 | 
					    MZ_ZIP_LDH_CRC32_OFS = 14,
 | 
				
			||||||
 | 
					    MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18,
 | 
				
			||||||
 | 
					    MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22,
 | 
				
			||||||
 | 
					    MZ_ZIP_LDH_FILENAME_LEN_OFS = 26,
 | 
				
			||||||
 | 
					    MZ_ZIP_LDH_EXTRA_LEN_OFS = 28,
 | 
				
			||||||
 | 
					    MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* End of central directory offsets */
 | 
				
			||||||
 | 
					    MZ_ZIP_ECDH_SIG_OFS = 0,
 | 
				
			||||||
 | 
					    MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4,
 | 
				
			||||||
 | 
					    MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6,
 | 
				
			||||||
 | 
					    MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8,
 | 
				
			||||||
 | 
					    MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10,
 | 
				
			||||||
 | 
					    MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12,
 | 
				
			||||||
 | 
					    MZ_ZIP_ECDH_CDIR_OFS_OFS = 16,
 | 
				
			||||||
 | 
					    MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* ZIP64 End of central directory locator offsets */
 | 
				
			||||||
 | 
					    MZ_ZIP64_ECDL_SIG_OFS = 0,                    /* 4 bytes */
 | 
				
			||||||
 | 
					    MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4,          /* 4 bytes */
 | 
				
			||||||
 | 
					    MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8,  /* 8 bytes */
 | 
				
			||||||
 | 
					    MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* ZIP64 End of central directory header offsets */
 | 
				
			||||||
 | 
					    MZ_ZIP64_ECDH_SIG_OFS = 0,                       /* 4 bytes */
 | 
				
			||||||
 | 
					    MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4,            /* 8 bytes */
 | 
				
			||||||
 | 
					    MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12,          /* 2 bytes */
 | 
				
			||||||
 | 
					    MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14,           /* 2 bytes */
 | 
				
			||||||
 | 
					    MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16,            /* 4 bytes */
 | 
				
			||||||
 | 
					    MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20,            /* 4 bytes */
 | 
				
			||||||
 | 
					    MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */
 | 
				
			||||||
 | 
					    MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32,       /* 8 bytes */
 | 
				
			||||||
 | 
					    MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40,                /* 8 bytes */
 | 
				
			||||||
 | 
					    MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48,                 /* 8 bytes */
 | 
				
			||||||
 | 
					    MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0,
 | 
				
			||||||
 | 
					    MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10,
 | 
				
			||||||
 | 
					    MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1,
 | 
				
			||||||
 | 
					    MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32,
 | 
				
			||||||
 | 
					    MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64,
 | 
				
			||||||
 | 
					    MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192,
 | 
				
			||||||
 | 
					    MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) {
 | 
				
			||||||
 | 
					    p[0] = (mz_uint8)v;
 | 
				
			||||||
 | 
					    p[1] = (mz_uint8)(v >> 8);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) {
 | 
				
			||||||
 | 
					    p[0] = (mz_uint8)v;
 | 
				
			||||||
 | 
					    p[1] = (mz_uint8)(v >> 8);
 | 
				
			||||||
 | 
					    p[2] = (mz_uint8)(v >> 16);
 | 
				
			||||||
 | 
					    p[3] = (mz_uint8)(v >> 24);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) {
 | 
				
			||||||
 | 
					    mz_write_le32(p, (mz_uint32)v);
 | 
				
			||||||
 | 
					    mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v))
 | 
				
			||||||
 | 
					#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v))
 | 
				
			||||||
 | 
					#define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3)
 | 
				
			||||||
 | 
					static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, const mz_uint64 *pUncomp_size, const mz_uint64 *pComp_size, const mz_uint64 *pLocal_header_ofs) {
 | 
				
			||||||
 | 
					    mz_uint8 *pDst = pBuf;
 | 
				
			||||||
 | 
					    mz_uint32 field_size = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pDst[0] = MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID;
 | 
				
			||||||
 | 
					    memset(pDst+1, 0, 3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pDst += sizeof(mz_uint16) * 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (pUncomp_size)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        MZ_WRITE_LE64(pDst, *pUncomp_size);
 | 
				
			||||||
 | 
					        pDst += sizeof(mz_uint64);
 | 
				
			||||||
 | 
					        field_size += sizeof(mz_uint64);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (pComp_size)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        MZ_WRITE_LE64(pDst, *pComp_size);
 | 
				
			||||||
 | 
					        pDst += sizeof(mz_uint64);
 | 
				
			||||||
 | 
					        field_size += sizeof(mz_uint64);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (pLocal_header_ofs)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        MZ_WRITE_LE64(pDst, *pLocal_header_ofs);
 | 
				
			||||||
 | 
					        pDst += sizeof(mz_uint64);
 | 
				
			||||||
 | 
					        field_size += sizeof(mz_uint64);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MZ_WRITE_LE16(pBuf + 2, field_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (mz_uint32)(pDst - pBuf);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef unsigned long mz_ulong;
 | 
				
			||||||
 | 
					mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) {
 | 
				
			||||||
 | 
					    static const mz_uint32 s_crc_table[256] = {
 | 
				
			||||||
 | 
					          0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535,
 | 
				
			||||||
 | 
					          0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD,
 | 
				
			||||||
 | 
					          0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D,
 | 
				
			||||||
 | 
					          0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
 | 
				
			||||||
 | 
					          0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
 | 
				
			||||||
 | 
					          0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
 | 
				
			||||||
 | 
					          0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC,
 | 
				
			||||||
 | 
					          0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
 | 
				
			||||||
 | 
					          0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB,
 | 
				
			||||||
 | 
					          0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F,
 | 
				
			||||||
 | 
					          0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB,
 | 
				
			||||||
 | 
					          0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
 | 
				
			||||||
 | 
					          0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA,
 | 
				
			||||||
 | 
					          0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE,
 | 
				
			||||||
 | 
					          0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A,
 | 
				
			||||||
 | 
					          0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
 | 
				
			||||||
 | 
					          0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409,
 | 
				
			||||||
 | 
					          0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
 | 
				
			||||||
 | 
					          0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739,
 | 
				
			||||||
 | 
					          0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
 | 
				
			||||||
 | 
					          0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268,
 | 
				
			||||||
 | 
					          0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0,
 | 
				
			||||||
 | 
					          0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8,
 | 
				
			||||||
 | 
					          0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
 | 
				
			||||||
 | 
					          0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF,
 | 
				
			||||||
 | 
					          0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703,
 | 
				
			||||||
 | 
					          0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7,
 | 
				
			||||||
 | 
					          0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
 | 
				
			||||||
 | 
					          0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE,
 | 
				
			||||||
 | 
					          0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
 | 
				
			||||||
 | 
					          0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6,
 | 
				
			||||||
 | 
					          0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
 | 
				
			||||||
 | 
					          0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D,
 | 
				
			||||||
 | 
					          0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5,
 | 
				
			||||||
 | 
					          0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605,
 | 
				
			||||||
 | 
					          0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
 | 
				
			||||||
 | 
					          0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF;
 | 
				
			||||||
 | 
					    const auto* pByte_buf = (const mz_uint8 *)ptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (buf_len >= 4) {
 | 
				
			||||||
 | 
					        crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF];
 | 
				
			||||||
 | 
					        crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF];
 | 
				
			||||||
 | 
					        crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF];
 | 
				
			||||||
 | 
					        crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF];
 | 
				
			||||||
 | 
					        pByte_buf += 4;
 | 
				
			||||||
 | 
					        buf_len -= 4;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (buf_len) {
 | 
				
			||||||
 | 
					        crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF];
 | 
				
			||||||
 | 
					        ++pByte_buf;
 | 
				
			||||||
 | 
					        --buf_len;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return ~crc32;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					mz_zip_writer_create_local_dir_header(mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 bit_flags) {
 | 
				
			||||||
 | 
					    memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG);
 | 
				
			||||||
 | 
					    //MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, 0);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags);
 | 
				
			||||||
 | 
					    //MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, 0);
 | 
				
			||||||
 | 
					    //MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, 0);
 | 
				
			||||||
 | 
					    //MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, 0);
 | 
				
			||||||
 | 
					    //MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, 0);
 | 
				
			||||||
 | 
					    //MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, 0);
 | 
				
			||||||
 | 
					    //MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, 0);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void mz_zip_writer_create_central_dir_header(mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size,
 | 
				
			||||||
 | 
					                                                    mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32,
 | 
				
			||||||
 | 
					                                                    mz_uint16 bit_flags, mz_uint64 local_header_ofs,
 | 
				
			||||||
 | 
					                                                    mz_uint32 ext_attributes) {
 | 
				
			||||||
 | 
					    memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG);
 | 
				
			||||||
 | 
					    //MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, 0);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags);
 | 
				
			||||||
 | 
					    //MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, 0);
 | 
				
			||||||
 | 
					    //MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, 0);
 | 
				
			||||||
 | 
					    //MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, 0);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size);
 | 
				
			||||||
 | 
					    //MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, 0);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ZipArchive::add_to_central_dir(const char *pFilename, mz_uint16 filename_size,
 | 
				
			||||||
 | 
					                                                const char *pExtra, mz_uint16 extra_size,
 | 
				
			||||||
 | 
					                                                mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32,
 | 
				
			||||||
 | 
					                                                mz_uint16 bit_flags,
 | 
				
			||||||
 | 
					                                                mz_uint64 local_header_ofs, mz_uint32 ext_attributes) {
 | 
				
			||||||
 | 
					    auto central_dir_ofs = (mz_uint32)m_central_dir.size();
 | 
				
			||||||
 | 
					    mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mz_zip_writer_create_central_dir_header(central_dir_header, filename_size, extra_size, uncomp_size, comp_size,
 | 
				
			||||||
 | 
					                                            uncomp_crc32, bit_flags, local_header_ofs,
 | 
				
			||||||
 | 
					                                            ext_attributes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m_central_dir.insert(m_central_dir.end(), central_dir_header, central_dir_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
 | 
				
			||||||
 | 
					    m_central_dir.insert(m_central_dir.end(), pFilename, pFilename+filename_size);
 | 
				
			||||||
 | 
					    if (extra_size)
 | 
				
			||||||
 | 
					        m_central_dir.insert(m_central_dir.end(), pExtra, pExtra + extra_size);
 | 
				
			||||||
 | 
					    //m_pState.m_central_dir.write((const char*) nullptr, 0);
 | 
				
			||||||
 | 
					    //m_pState.m_central_dir.write((const char*) nullptr, 0);
 | 
				
			||||||
 | 
					    m_central_dir_offsets.push_back(central_dir_ofs);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3)
 | 
				
			||||||
 | 
					asio::awaitable<void> ZipArchive::add_dir(const std::string &path) {
 | 
				
			||||||
 | 
					    const char * const pArchive_name = path.c_str();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const mz_uint64 uncomp_size = 0;
 | 
				
			||||||
 | 
					    const mz_uint32 uncomp_crc32 = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mz_uint ext_attributes = MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG;
 | 
				
			||||||
 | 
					    mz_uint64 local_dir_header_ofs = m_archive_size, comp_size = 0;
 | 
				
			||||||
 | 
					    const size_t archive_name_size = path.size();
 | 
				
			||||||
 | 
					    mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
 | 
				
			||||||
 | 
					    mz_uint32 extra_size = 0;
 | 
				
			||||||
 | 
					    mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE];
 | 
				
			||||||
 | 
					    mz_uint16 bit_flags = MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (local_dir_header_ofs >= MZ_UINT32_MAX) {
 | 
				
			||||||
 | 
					        extra_size = mz_zip_writer_create_zip64_extra_data(
 | 
				
			||||||
 | 
					            extra_data,
 | 
				
			||||||
 | 
					            nullptr,
 | 
				
			||||||
 | 
					            nullptr,
 | 
				
			||||||
 | 
					            (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : nullptr
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mz_zip_writer_create_local_dir_header(local_dir_header, (mz_uint16) archive_name_size,
 | 
				
			||||||
 | 
					                                          (mz_uint16) extra_size,
 | 
				
			||||||
 | 
					                                          bit_flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    co_await write(local_dir_header, sizeof(local_dir_header));
 | 
				
			||||||
 | 
					    m_archive_size += sizeof(local_dir_header);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    co_await write(pArchive_name, archive_name_size);
 | 
				
			||||||
 | 
					    m_archive_size += archive_name_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (extra_size) {
 | 
				
			||||||
 | 
					        co_await write(extra_data, extra_size);
 | 
				
			||||||
 | 
					        m_archive_size += extra_size;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    add_to_central_dir(pArchive_name, (mz_uint16) archive_name_size, (char*)extra_data,
 | 
				
			||||||
 | 
					                                          (mz_uint16) extra_size, uncomp_size, comp_size, uncomp_crc32, bit_flags,
 | 
				
			||||||
 | 
					                                          local_dir_header_ofs, ext_attributes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m_total_files++;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					asio::awaitable<void> ZipArchive::add_file(const std::string &file_path, const std::string &archive_path) {
 | 
				
			||||||
 | 
					    asio::stream_file file{co_await asio::this_coro::executor, file_path, asio::file_base::read_only};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mz_uint64 max_size = file.seek(0, asio::file_base::seek_end);
 | 
				
			||||||
 | 
					    file.seek(0, asio::file_base::seek_set);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const char * const pArchive_name = archive_path.c_str();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mz_uint16 gen_flags;
 | 
				
			||||||
 | 
					    mz_uint uncomp_crc32 = 0;
 | 
				
			||||||
 | 
					    mz_uint16 ext_attributes = 0;
 | 
				
			||||||
 | 
					    mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = m_archive_size, uncomp_size = 0, comp_size = 0;
 | 
				
			||||||
 | 
					    const size_t archive_name_size = archive_path.size();
 | 
				
			||||||
 | 
					    mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
 | 
				
			||||||
 | 
					    mz_uint32 extra_size = 0;
 | 
				
			||||||
 | 
					    mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE];
 | 
				
			||||||
 | 
					    mz_uint64 file_ofs = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    gen_flags = MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local_dir_header_ofs = cur_archive_file_ofs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (max_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) {
 | 
				
			||||||
 | 
					        extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, nullptr, nullptr, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : nullptr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mz_zip_writer_create_local_dir_header(local_dir_header, (mz_uint16) archive_name_size, (mz_uint16) extra_size,
 | 
				
			||||||
 | 
					                                          gen_flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    co_await write(local_dir_header, sizeof(local_dir_header));
 | 
				
			||||||
 | 
					    cur_archive_file_ofs += sizeof(local_dir_header);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    co_await write(pArchive_name, archive_name_size);
 | 
				
			||||||
 | 
					    cur_archive_file_ofs += archive_name_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (extra_size) {
 | 
				
			||||||
 | 
					        co_await write(extra_data, extra_size);
 | 
				
			||||||
 | 
					        cur_archive_file_ofs += extra_size;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (max_size > 0) {
 | 
				
			||||||
 | 
					        std::array<char, 65535> read_buf{};
 | 
				
			||||||
 | 
					        boost::asio::mutable_buffer mut_buf{read_buf.data(), read_buf.size()}; // NOLINT(*-static-accessed-through-instance)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            while (true) {
 | 
				
			||||||
 | 
					                auto n = co_await file.async_read_some(mut_buf, asio::use_awaitable);
 | 
				
			||||||
 | 
					                if (n == 0)
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                co_await write(read_buf.data(), n);
 | 
				
			||||||
 | 
					                file_ofs += n;
 | 
				
			||||||
 | 
					                uncomp_crc32 = (mz_uint32) mz_crc32(uncomp_crc32, (const mz_uint8 *) read_buf.data(), n);
 | 
				
			||||||
 | 
					                cur_archive_file_ofs += n;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (boost::system::system_error &e) {
 | 
				
			||||||
 | 
					            if (e.code() != asio::stream_errc::eof)
 | 
				
			||||||
 | 
					                throw;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        uncomp_size = file_ofs;
 | 
				
			||||||
 | 
					        comp_size = uncomp_size;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64];
 | 
				
			||||||
 | 
					    mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32);
 | 
				
			||||||
 | 
					    if (extra_size == 0) {
 | 
				
			||||||
 | 
					        MZ_WRITE_LE32(local_dir_footer + 8, comp_size);
 | 
				
			||||||
 | 
					        MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        MZ_WRITE_LE64(local_dir_footer + 8, comp_size);
 | 
				
			||||||
 | 
					        MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size);
 | 
				
			||||||
 | 
					        local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    co_await write(local_dir_footer, local_dir_footer_size);
 | 
				
			||||||
 | 
					    cur_archive_file_ofs += local_dir_footer_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (extra_size) {
 | 
				
			||||||
 | 
					        extra_size = mz_zip_writer_create_zip64_extra_data(extra_data,
 | 
				
			||||||
 | 
					                                                           (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : nullptr,
 | 
				
			||||||
 | 
					                                                           (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : nullptr,
 | 
				
			||||||
 | 
					                                                           (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : nullptr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    add_to_central_dir(pArchive_name, archive_name_size, (char*)extra_data, extra_size,
 | 
				
			||||||
 | 
					                                          uncomp_size, comp_size, uncomp_crc32, gen_flags,
 | 
				
			||||||
 | 
					                                          local_dir_header_ofs, ext_attributes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m_total_files++;
 | 
				
			||||||
 | 
					    m_archive_size = cur_archive_file_ofs;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					boost::asio::awaitable<void> ZipArchive::end() {
 | 
				
			||||||
 | 
					    mz_uint64 central_dir_ofs = 0, central_dir_size = 0;
 | 
				
			||||||
 | 
					    mz_uint8 hdr[256];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (m_total_files > 0) {
 | 
				
			||||||
 | 
					        /* Write central directory */
 | 
				
			||||||
 | 
					        central_dir_ofs = m_archive_size;
 | 
				
			||||||
 | 
					        central_dir_size = m_central_dir.size();
 | 
				
			||||||
 | 
					        co_await write(m_central_dir.data(), central_dir_size);
 | 
				
			||||||
 | 
					        m_archive_size += central_dir_size;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mz_uint64 rel_ofs_to_zip64_ecdr = m_archive_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    memset(hdr, 0, sizeof(hdr));
 | 
				
			||||||
 | 
					    MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64));
 | 
				
			||||||
 | 
					    MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, m_total_files);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, m_total_files);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs);
 | 
				
			||||||
 | 
					    co_await write(hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE);
 | 
				
			||||||
 | 
					    m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Write zip64 end of central directory locator */
 | 
				
			||||||
 | 
					    memset(hdr, 0, sizeof(hdr));
 | 
				
			||||||
 | 
					    MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1);
 | 
				
			||||||
 | 
					    co_await write(hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE);
 | 
				
			||||||
 | 
					    m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Write end of central directory record */
 | 
				
			||||||
 | 
					    memset(hdr, 0, sizeof(hdr));
 | 
				
			||||||
 | 
					    MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG);
 | 
				
			||||||
 | 
					    MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, std::min(MZ_UINT16_MAX, m_total_files));
 | 
				
			||||||
 | 
					    MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, std::min(MZ_UINT16_MAX, m_total_files));
 | 
				
			||||||
 | 
					    MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, std::min<mz_uint32>(MZ_UINT32_MAX, central_dir_size));
 | 
				
			||||||
 | 
					    MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, std::min<mz_uint32>(MZ_UINT32_MAX, central_dir_ofs));
 | 
				
			||||||
 | 
					    co_await write(hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE);
 | 
				
			||||||
 | 
					    m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										39
									
								
								src/util/miniz.hxx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/util/miniz.hxx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					#ifndef TEMP_MINIZ_HXX
 | 
				
			||||||
 | 
					#define TEMP_MINIZ_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <boost/asio/awaitable.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef unsigned char mz_uint8;
 | 
				
			||||||
 | 
					typedef signed short mz_int16;
 | 
				
			||||||
 | 
					typedef unsigned short mz_uint16;
 | 
				
			||||||
 | 
					typedef unsigned int mz_uint32;
 | 
				
			||||||
 | 
					typedef unsigned int mz_uint;
 | 
				
			||||||
 | 
					typedef uint64_t mz_uint64;
 | 
				
			||||||
 | 
					typedef bool mz_bool;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ZipArchive {
 | 
				
			||||||
 | 
					    boost::asio::awaitable<void> add_dir(const std::string& path);
 | 
				
			||||||
 | 
					    boost::asio::awaitable<void> add_file(const std::string& file_path, const std::string& archive_path);
 | 
				
			||||||
 | 
					    boost::asio::awaitable<void> end();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					    virtual boost::asio::awaitable<void> write(const void *, size_t n) = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    mz_uint64 m_archive_size = 0;
 | 
				
			||||||
 | 
					    mz_uint32 m_total_files = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<mz_uint8> m_central_dir;
 | 
				
			||||||
 | 
					    std::vector<mz_uint32> m_central_dir_offsets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void add_to_central_dir(const char *pFilename, mz_uint16 filename_size,
 | 
				
			||||||
 | 
					                                                const char *pExtra, mz_uint16 extra_size,
 | 
				
			||||||
 | 
					                                                mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32,
 | 
				
			||||||
 | 
					                                                mz_uint16 bit_flags,
 | 
				
			||||||
 | 
					                                                mz_uint64 local_header_ofs, mz_uint32 ext_attributes);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif //TEMP_MINIZ_HXX
 | 
				
			||||||
@@ -1,30 +0,0 @@
 | 
				
			|||||||
#ifndef FILESERVER_TIMED_MUTEX_HXX
 | 
					 | 
				
			||||||
#define FILESERVER_TIMED_MUTEX_HXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <spdlog/spdlog.h>
 | 
					 | 
				
			||||||
#include <shared_mutex>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct TimedSharedMutex {
 | 
					 | 
				
			||||||
    std::shared_mutex m;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    using clock = std::chrono::high_resolution_clock;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void lock_shared() {
 | 
					 | 
				
			||||||
        auto start = clock::now();
 | 
					 | 
				
			||||||
        m.lock_shared();
 | 
					 | 
				
			||||||
        auto end = clock::now();
 | 
					 | 
				
			||||||
        auto d = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
 | 
					 | 
				
			||||||
        spdlog::info("Lock s took {} ms", d.count());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    void unlock_shared() { m.unlock_shared(); spdlog::info("Unlock s"); }
 | 
					 | 
				
			||||||
    void lock() {
 | 
					 | 
				
			||||||
        auto start = clock::now();
 | 
					 | 
				
			||||||
        m.lock();
 | 
					 | 
				
			||||||
        auto end = clock::now();
 | 
					 | 
				
			||||||
        auto d = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
 | 
					 | 
				
			||||||
        spdlog::info("Lock took {} ms", d.count());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    void unlock() { m.unlock(); spdlog::info("Unlock"); }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif //FILESERVER_TIMED_MUTEX_HXX
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user