diff --git a/file/include/files/FileServer.h b/file/include/files/FileServer.h index 9302e34..da63a13 100644 --- a/file/include/files/FileServer.h +++ b/file/include/files/FileServer.h @@ -345,8 +345,8 @@ namespace ts::server::file { std::function&)> callback_transfer_registered{}; /* transfer has been registered */ std::function&)> callback_transfer_started{}; /* transfer has been started */ std::function&)> callback_transfer_finished{}; /* transfer has been finished */ - std::function&, const TransferError&)> callback_transfer_aborted{}; /* an error happened while transferring the data */ std::function&, const TransferStatistics&)> callback_transfer_statistics{}; + std::function&, const transfer::TransferStatistics&, const TransferError&)> callback_transfer_aborted{}; /* an error happened while transferring the data */ }; } diff --git a/file/local_server/LocalFileProvider.h b/file/local_server/LocalFileProvider.h index 998ed25..7e3445b 100644 --- a/file/local_server/LocalFileProvider.h +++ b/file/local_server/LocalFileProvider.h @@ -458,7 +458,6 @@ namespace ts::server::file { void enqueue_disk_io(const std::shared_ptr& /* client */); void execute_disk_io(const std::shared_ptr& /* client */); - void report_transfer_statistics(const std::shared_ptr& /* client */); [[nodiscard]] TransferUploadRawResult handle_transfer_upload_raw(const std::shared_ptr& /* client */, const char * /* buffer */, size_t /* length */); [[nodiscard]] TransferUploadHTTPResult handle_transfer_upload_http(const std::shared_ptr& /* client */, const char * /* buffer */, size_t /* length */); @@ -473,6 +472,11 @@ namespace ts::server::file { static void dispatch_loop_network(void*); static void dispatch_loop_disk_io(void*); + void report_transfer_statistics(const std::shared_ptr& /* client */); + [[nodiscard]] TransferStatistics generate_transfer_statistics_report(const std::shared_ptr& /* client */); + void invoke_aborted_callback(const std::shared_ptr& /* client */, const TransferError& /* error */); + void invoke_aborted_callback(const std::shared_ptr& /* pending transfer */, const TransferError& /* error */); + size_t handle_transfer_read(const std::shared_ptr& /* client */, const char* /* buffer */, size_t /* bytes */); size_t handle_transfer_read_raw(const std::shared_ptr& /* client */, const char* /* buffer */, size_t /* bytes */); [[nodiscard]] TransferKeyApplyResult handle_transfer_key_provided(const std::shared_ptr& /* client */, std::string& /* error */); diff --git a/file/local_server/LocalFileTransferClientWorker.cpp b/file/local_server/LocalFileTransferClientWorker.cpp index 1dffb80..41de957 100644 --- a/file/local_server/LocalFileTransferClientWorker.cpp +++ b/file/local_server/LocalFileTransferClientWorker.cpp @@ -88,7 +88,7 @@ void LocalFileTransfer::dispatch_loop_client_worker(void *ptr_transfer) { while(provider->disconnect.active) { { std::unique_lock dlock{provider->disconnect.mutex}; - provider->disconnect.notify_cv.wait_for(dlock, std::chrono::seconds{1}); + provider->disconnect.notify_cv.wait_for(dlock, std::chrono::milliseconds {500}); /* report all 500ms the statistics */ } /* run the disconnect worker at least once before exiting */ @@ -123,17 +123,15 @@ void LocalFileTransfer::dispatch_loop_client_worker(void *ptr_transfer) { auto now = std::chrono::system_clock::now(); std::copy_if(provider->pending_transfers.begin(), provider->pending_transfers.end(), std::back_inserter(timeouted_transfers), [&](const std::shared_ptr& t) { - return t->initialized_timestamp + std::chrono::seconds{100} < now; //FIXME: Decrease to 10 again! + return t->initialized_timestamp + std::chrono::seconds{10} < now; }); provider->pending_transfers.erase(std::remove_if(provider->pending_transfers.begin(), provider->pending_transfers.end(), [&](const auto& t) { return std::find(timeouted_transfers.begin(), timeouted_transfers.end(), t) != timeouted_transfers.end(); }), provider->pending_transfers.end()); } - for(const auto& pt : timeouted_transfers) { - if(auto callback{provider->callback_transfer_aborted}; callback) - callback(pt, { TransferError::TRANSFER_TIMEOUT, "" }); - } + for(const auto& pt : timeouted_transfers) + provider->invoke_aborted_callback(pt, { TransferError::TRANSFER_TIMEOUT, "" }); if(!timeouted_transfers.empty()) logMessage(LOG_FT, "Removed {} pending transfers because no request has been made for them.", timeouted_transfers.size()); @@ -155,7 +153,6 @@ void LocalFileTransfer::dispatch_loop_client_worker(void *ptr_transfer) { } else if(t->state == FileClient::STATE_TRANSFERRING) { assert(t->transfer); if(t->transfer->direction == Transfer::DIRECTION_UPLOAD) { - return false; //FIXME: Due to debugging reasons return t->timings.last_read + std::chrono::seconds{5} < now; } else if(t->transfer->direction == Transfer::DIRECTION_DOWNLOAD) { return t->timings.last_write + std::chrono::seconds{5} < now; @@ -179,8 +176,7 @@ void LocalFileTransfer::dispatch_loop_client_worker(void *ptr_transfer) { break; case FileClient::STATE_TRANSFERRING: logMessage(LOG_FT, "{} Networking timeout. Dropping client", client->log_prefix()); - if(auto callback{provider->callback_transfer_aborted}; callback) - callback(client->transfer, { TransferError::TRANSFER_TIMEOUT, "" }); + provider->invoke_aborted_callback(client, { TransferError::TRANSFER_TIMEOUT, "" }); break; case FileClient::STATE_DISCONNECTING: logMessage(LOG_FT, "{} Failed to flush connection. Dropping client", client->log_prefix()); @@ -207,6 +203,10 @@ void LocalFileTransfer::report_transfer_statistics(const std::shared_ptrcallback_transfer_statistics}; if(!callback) return; + callback(client->transfer, this->generate_transfer_statistics_report(client)); +} + +TransferStatistics LocalFileTransfer::generate_transfer_statistics_report(const std::shared_ptr &client) { TransferStatistics stats{}; stats.network_bytes_send = client->statistics.network_send.total_bytes; @@ -221,6 +221,36 @@ void LocalFileTransfer::report_transfer_statistics(const std::shared_ptrtransfer->file_offset; stats.file_current_offset = client->statistics.file_transferred.total_bytes + client->transfer->file_offset; stats.file_total_size = client->transfer->expected_file_size; + return stats; +} - callback(client->transfer, stats); +void LocalFileTransfer::invoke_aborted_callback(const std::shared_ptr &client, + const ts::server::file::transfer::TransferError &error) { + auto callback{this->callback_transfer_aborted}; + if(!callback) return; + + callback(client->transfer, this->generate_transfer_statistics_report(client), error); +} + +void LocalFileTransfer::invoke_aborted_callback(const std::shared_ptr &transfer, + const ts::server::file::transfer::TransferError &error) { + auto callback{this->callback_transfer_aborted}; + if(!callback) return; + + TransferStatistics stats{}; + + stats.network_bytes_send = 0; + stats.network_bytes_received = 0; + stats.file_bytes_transferred = 0; + + stats.delta_network_bytes_received = 0; + stats.delta_network_bytes_send = 0; + + stats.delta_file_bytes_transferred = 0; + + stats.file_start_offset = transfer->file_offset; + stats.file_current_offset = transfer->file_offset; + stats.file_total_size = transfer->expected_file_size; + + callback(transfer, stats, error); } \ No newline at end of file diff --git a/file/local_server/LocalFileTransferDisk.cpp b/file/local_server/LocalFileTransferDisk.cpp index 9bcc6f1..c97fa44 100644 --- a/file/local_server/LocalFileTransferDisk.cpp +++ b/file/local_server/LocalFileTransferDisk.cpp @@ -336,10 +336,8 @@ void LocalFileTransfer::execute_disk_io(const std::shared_ptr &clien logError(LOG_FT, "{} Received unexpected file write EOF. EOF received at {} but expected {}. Actual file offset: {}. Closing transfer.", client->log_prefix(), offset_written, client->transfer->expected_file_size, aoffset); - this->report_transfer_statistics(client); - if(auto callback{client->handle->callback_transfer_aborted}; callback) - callback(client->transfer, { TransferError::UNEXPECTED_DISK_EOF, strerror(errno) }); + this->invoke_aborted_callback(client, { TransferError::UNEXPECTED_DISK_EOF, strerror(errno) }); { std::unique_lock slock{client->state_mutex}; client->handle->disconnect_client(client, slock, true); @@ -356,10 +354,7 @@ void LocalFileTransfer::execute_disk_io(const std::shared_ptr &clien logError(LOG_FT, "{} Received write to disk IO error. Write pointer is at {} of {}. Actual file offset: {}. Closing transfer.", client->log_prefix(), offset_written, client->transfer->expected_file_size, aoffset); - this->report_transfer_statistics(client); - if(auto callback{client->handle->callback_transfer_aborted}; callback) - callback(client->transfer, { TransferError::DISK_IO_ERROR, strerror(errno) }); - + this->invoke_aborted_callback(client, { TransferError::DISK_IO_ERROR, strerror(errno) }); { std::unique_lock slock{client->state_mutex}; client->handle->disconnect_client(client, slock, true); @@ -429,9 +424,7 @@ void LocalFileTransfer::execute_disk_io(const std::shared_ptr &clien logError(LOG_FT, "{} Received unexpected read EOF. EOF received at {} but expected {}. Actual file offset: {}. Disconnecting client.", client->log_prefix(), offset_send, client->transfer->expected_file_size, aoffset); - this->report_transfer_statistics(client); - if(auto callback{client->handle->callback_transfer_aborted}; callback) - callback(client->transfer, { TransferError::UNEXPECTED_DISK_EOF, strerror(errno) }); + this->invoke_aborted_callback(client, { TransferError::UNEXPECTED_DISK_EOF, "" }); } { @@ -446,10 +439,7 @@ void LocalFileTransfer::execute_disk_io(const std::shared_ptr &clien logWarning(LOG_FT, "{} Failed to read from file {} ({}/{}). Aborting transfer.", client->log_prefix(), client->transfer->absolute_file_path, errno, strerror(errno)); - this->report_transfer_statistics(client); - if(auto callback{client->handle->callback_transfer_aborted}; callback) - callback(client->transfer, { TransferError::DISK_IO_ERROR, strerror(errno) }); - + this->invoke_aborted_callback(client, { TransferError::DISK_IO_ERROR, strerror(errno) }); { std::unique_lock slock{client->state_mutex}; client->handle->disconnect_client(client, slock, true); diff --git a/file/local_server/LocalFileTransferNetwork.cpp b/file/local_server/LocalFileTransferNetwork.cpp index aae2a1a..8f8e9bf 100644 --- a/file/local_server/LocalFileTransferNetwork.cpp +++ b/file/local_server/LocalFileTransferNetwork.cpp @@ -478,9 +478,7 @@ void LocalFileTransfer::callback_transfer_network_read(int fd, short events, voi transfer->log_prefix(), transfer->statistics.file_transferred.total_bytes, transfer->transfer->expected_file_size - transfer->transfer->file_offset); } - transfer->handle->report_transfer_statistics(transfer->shared_from_this()); - if(auto callback{transfer->handle->callback_transfer_aborted}; callback) - callback(transfer->transfer, { TransferError::UNEXPECTED_CLIENT_DISCONNECT, "" }); + transfer->handle->invoke_aborted_callback(transfer->shared_from_this(), { TransferError::UNEXPECTED_CLIENT_DISCONNECT, "" }); break; } case FileClient::STATE_DISCONNECTING: @@ -515,9 +513,8 @@ void LocalFileTransfer::callback_transfer_network_read(int fd, short events, voi transfer->log_prefix(), transfer->statistics.file_transferred.total_bytes, transfer->transfer->expected_file_size - transfer->transfer->file_offset, errno, strerror(errno)); } - transfer->handle->report_transfer_statistics(transfer->shared_from_this()); - if(auto callback{transfer->handle->callback_transfer_aborted}; callback) - callback(transfer->transfer, { TransferError::NETWORK_IO_ERROR, strerror(errno) }); + + transfer->handle->invoke_aborted_callback(transfer->shared_from_this(), { TransferError::NETWORK_IO_ERROR, strerror(errno) }); break; case FileClient::STATE_DISCONNECTING: case FileClient::STATE_DISCONNECTED: @@ -623,10 +620,7 @@ void LocalFileTransfer::callback_transfer_network_write(int fd, short events, vo logError(LOG_FT, "{} Client disconnected unexpectedly on write. Send {} bytes out of {}.", transfer->log_prefix(), transfer->statistics.file_transferred.total_bytes, transfer->transfer->expected_file_size - transfer->transfer->file_offset); - transfer->handle->report_transfer_statistics(transfer->shared_from_this()); - if(auto callback{transfer->handle->callback_transfer_aborted}; callback) - callback(transfer->transfer, { TransferError::UNEXPECTED_CLIENT_DISCONNECT, strerror(errno) }); - + transfer->handle->invoke_aborted_callback(transfer->shared_from_this(), { TransferError::UNEXPECTED_CLIENT_DISCONNECT, "" }); { std::unique_lock slock{transfer->state_mutex}; transfer->handle->disconnect_client(transfer->shared_from_this(), slock, true); @@ -640,10 +634,7 @@ void LocalFileTransfer::callback_transfer_network_write(int fd, short events, vo logError(LOG_FT, "{} Received network write error. Send {} bytes out of {}. Closing transfer.", transfer->log_prefix(), transfer->statistics.file_transferred.total_bytes, transfer->transfer->expected_file_size - transfer->transfer->file_offset); - transfer->handle->report_transfer_statistics(transfer->shared_from_this()); - if(auto callback{transfer->handle->callback_transfer_aborted}; callback) - callback(transfer->transfer, { TransferError::NETWORK_IO_ERROR, strerror(errno) }); - + transfer->handle->invoke_aborted_callback(transfer->shared_from_this(), { TransferError::NETWORK_IO_ERROR, strerror(errno) }); { std::unique_lock slock{transfer->state_mutex}; transfer->handle->disconnect_client(transfer->shared_from_this(), slock, false); @@ -780,20 +771,14 @@ size_t LocalFileTransfer::handle_transfer_read_raw(const std::shared_ptrtransfer); - - this->report_transfer_statistics(client); - if(auto callback{this->callback_transfer_aborted}; callback) - callback(client->transfer, { TransferError::DISK_INITIALIZE_ERROR, error_detail }); + this->invoke_aborted_callback(client, { TransferError::DISK_INITIALIZE_ERROR, error_detail }); logMessage(LOG_FT, "{} Disconnecting client because we failed to open the target file.", client->log_prefix()); break; case TransferKeyApplyResult::INTERNAL_ERROR: default: - this->report_transfer_statistics(client); - if(auto callback{this->callback_transfer_aborted}; client->transfer && callback) - callback(client->transfer, { TransferError::UNKNOWN, error_detail }); - + this->invoke_aborted_callback(client, { TransferError::UNKNOWN, error_detail }); logMessage(LOG_FT, "{} Disconnecting client because of an unknown key initialize error ({}).", client->log_prefix(), (int) key_result); break; } @@ -872,10 +857,7 @@ size_t LocalFileTransfer::handle_transfer_read(const std::shared_ptr case TransferKeyApplyResult::FILE_ERROR: assert(client->transfer); - this->report_transfer_statistics(client); - if(auto callback{this->callback_transfer_aborted}; callback) - callback(client->transfer, { TransferError::DISK_INITIALIZE_ERROR, error_detail }); - + this->invoke_aborted_callback(client, { TransferError::DISK_INITIALIZE_ERROR, error_detail }); logMessage(LOG_FT, "{} Disconnecting client because we failed to open the target file.", client->log_prefix()); response.code = http::code::code(500, "Internal Server Error"); response.setHeader("x-error-message", { error_detail }); @@ -889,10 +871,7 @@ size_t LocalFileTransfer::handle_transfer_read(const std::shared_ptr case TransferKeyApplyResult::INTERNAL_ERROR: default: - this->report_transfer_statistics(client); - if(auto callback{this->callback_transfer_aborted}; client->transfer && callback) - callback(client->transfer, { TransferError::UNKNOWN, error_detail }); - + this->invoke_aborted_callback(client, { TransferError::UNKNOWN, error_detail }); logMessage(LOG_FT, "{} Disconnecting client because of an unknown key initialize error ({}).", client->log_prefix(), (int) key_result); response.code = http::code::code(500, "Internal Server Error"); response.setHeader("x-error-message", { error_detail.empty() ? "failed to initialize transfer" : error_detail }); diff --git a/git-teaspeak b/git-teaspeak index a8a0aab..be826cc 160000 --- a/git-teaspeak +++ b/git-teaspeak @@ -1 +1 @@ -Subproject commit a8a0aabd7fca05b950de9f781a3cd6ba5b799f79 +Subproject commit be826ccb507a84d97c9c07397b05a8fe731f7ce5 diff --git a/server/src/FileServerHandler.cpp b/server/src/FileServerHandler.cpp index 3419d12..d863d31 100644 --- a/server/src/FileServerHandler.cpp +++ b/server/src/FileServerHandler.cpp @@ -4,10 +4,14 @@ #include #include + +#include "./client/ConnectedClient.h" #include "FileServerHandler.h" using namespace ts::server::file; +FileServerHandler::FileServerHandler(ts::server::InstanceHandler *instance) : instance_{instance} {} + bool FileServerHandler::initialize(std::string &error) { /* * FIXME: Ports etc! @@ -15,7 +19,6 @@ bool FileServerHandler::initialize(std::string &error) { auto bindings_string = this->properties()[property::SERVERINSTANCE_FILETRANSFER_HOST].as(); auto port = this->properties()[property::SERVERINSTANCE_FILETRANSFER_PORT].as(); */ - /* TODO: Callback handler */ if(!file::initialize(error)) return false; @@ -27,24 +30,170 @@ bool FileServerHandler::initialize(std::string &error) { transfer.callback_transfer_started = std::bind(&FileServerHandler::callback_transfer_started, this, std::placeholders::_1); transfer.callback_transfer_finished = std::bind(&FileServerHandler::callback_transfer_finished, this, std::placeholders::_1); - transfer.callback_transfer_aborted = std::bind(&FileServerHandler::callback_transfer_aborted, this, std::placeholders::_1, std::placeholders::_2); + transfer.callback_transfer_aborted = std::bind(&FileServerHandler::callback_transfer_aborted, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); transfer.callback_transfer_statistics = std::bind(&FileServerHandler::callback_transfer_statistics, this, std::placeholders::_1, std::placeholders::_2); + return true; +} + +void FileServerHandler::finalize() { + file::finalize(); } void FileServerHandler::callback_transfer_registered(const std::shared_ptr &transfer) { auto server = this->instance_->getVoiceServerManager()->findServerById(transfer->server->server_id()); if(!server) return; /* well that's bad */ + const auto bytes = transfer->expected_file_size - transfer->file_offset; if(transfer->direction == transfer::Transfer::DIRECTION_UPLOAD) { - server->properties()[property::VIRTUALSERVER_TOTAL_BYTES_UPLOADED] += transfer->expected_file_size - transfer->file_offset; - server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED] += transfer->expected_file_size - transfer->file_offset; + server->properties()[property::VIRTUALSERVER_TOTAL_BYTES_UPLOADED] += bytes; + server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED] += bytes; } else { - server->properties()[property::VIRTUALSERVER_TOTAL_BYTES_DOWNLOADED] += transfer->expected_file_size - transfer->file_offset; - server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED] += transfer->expected_file_size - transfer->file_offset; + server->properties()[property::VIRTUALSERVER_TOTAL_BYTES_DOWNLOADED] += bytes; + server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED] += bytes; + } + + auto client = server->find_client_by_id(transfer->client_id); + if(client && client->getUid() == transfer->client_unique_id) { + if(transfer->direction == transfer::Transfer::DIRECTION_UPLOAD) { + client->properties()[property::CLIENT_TOTAL_BYTES_UPLOADED] += bytes; + client->properties()[property::CLIENT_MONTH_BYTES_UPLOADED] += bytes; + } else { + client->properties()[property::CLIENT_MONTH_BYTES_DOWNLOADED] += bytes; + client->properties()[property::CLIENT_MONTH_BYTES_DOWNLOADED] += bytes; + } } } void FileServerHandler::callback_transfer_aborted(const std::shared_ptr &transfer, + const transfer::TransferStatistics &statistics, const ts::server::file::transfer::TransferError &error) { - /* TODO: Remove not used quota from server & client */ + auto server = this->instance_->getVoiceServerManager()->findServerById(transfer->server->server_id()); + if(!server) return; /* well that's bad */ + + if(statistics.file_total_size < statistics.file_current_offset) + return; + + const int64_t bytes_left = statistics.file_total_size - statistics.file_current_offset; + if(transfer->direction == transfer::Transfer::DIRECTION_UPLOAD) { + server->properties()[property::VIRTUALSERVER_TOTAL_BYTES_UPLOADED] += -bytes_left; + server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED] += -bytes_left; + } else { + server->properties()[property::VIRTUALSERVER_TOTAL_BYTES_DOWNLOADED] += -bytes_left; + server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED] += -bytes_left; + } + + auto client = server->find_client_by_id(transfer->client_id); + if(client && client->getUid() == transfer->client_unique_id) { + if(transfer->direction == transfer::Transfer::DIRECTION_UPLOAD) { + client->properties()[property::CLIENT_TOTAL_BYTES_UPLOADED] += -bytes_left; + client->properties()[property::CLIENT_MONTH_BYTES_UPLOADED] += -bytes_left; + } else { + client->properties()[property::CLIENT_MONTH_BYTES_DOWNLOADED] += -bytes_left; + client->properties()[property::CLIENT_MONTH_BYTES_DOWNLOADED] += -bytes_left; + } + + + //notifystatusfiletransfer clientftfid=4096 status=2568 failed_permid=234 msg=insufficient\sclient\spermissions\s(failed\son\si_ft_needed_file_upload_power) size=0 + ts::command_builder notify{"notifystatusfiletransfer"}; + + notify.put_unchecked(0, "clientftfid", transfer->client_transfer_id); + notify.put(0, "size", 0); /* not sure where TeamSpeak counts from */ + + using ErrorType = ts::server::file::transfer::TransferError::Type; + switch (error.error_type) { + case ErrorType::TRANSFER_TIMEOUT: + notify.put_unchecked(0, "status", (int) error::file_transfer_connection_timeout); + notify.put_unchecked(0, "msg", findError(error::file_transfer_connection_timeout).message); + break; + + case ErrorType::DISK_IO_ERROR: + case ErrorType::DISK_TIMEOUT: + case ErrorType::DISK_INITIALIZE_ERROR: + notify.put_unchecked(0, "status", (int) error::file_io_error); + notify.put_unchecked(0, "msg", findError(error::file_io_error).message); + break; + + case ErrorType::UNKNOWN: + case ErrorType::NETWORK_IO_ERROR: + notify.put_unchecked(0, "status", (int) error::file_connection_lost); + notify.put_unchecked(0, "msg", findError(error::file_connection_lost).message); + break; + + case ErrorType::UNEXPECTED_CLIENT_DISCONNECT: + case ErrorType::UNEXPECTED_DISK_EOF: + notify.put_unchecked(0, "status", (int) error::file_transfer_interrupted); + notify.put_unchecked(0, "msg", findError(error::file_transfer_interrupted).message); + break; + } + notify.put_unchecked(0, "extra_msg", error.error_message); + + client->sendCommand(notify); + } +} + +void FileServerHandler::callback_transfer_statistics(const std::shared_ptr &transfer, + const ts::server::file::transfer::TransferStatistics &statistics) { + auto server = this->instance_->getVoiceServerManager()->findServerById(transfer->server->server_id()); + if(!server) return; /* well that's bad */ + + auto client = server->find_client_by_id(transfer->client_id); + if(!client || client->getUid() != transfer->client_unique_id) { + /* client not online anymore, but we could still log this as server traffic */ + if(transfer->direction == transfer::Transfer::DIRECTION_UPLOAD) { + server->getServerStatistics()->logFileTransferIn(statistics.delta_file_bytes_transferred); + } else { + server->getServerStatistics()->logFileTransferOut(statistics.delta_file_bytes_transferred); + } + return; + } + + if(transfer->direction == transfer::Transfer::DIRECTION_UPLOAD) { + client->getConnectionStatistics()->logFileTransferIn(statistics.delta_file_bytes_transferred); + } else { + client->getConnectionStatistics()->logFileTransferOut(statistics.delta_file_bytes_transferred); + } + + ts::command_builder notify{"notifyfiletransferprogress"}; + notify.put_unchecked(0, "clientftid", transfer->client_transfer_id); + + notify.put_unchecked(0, "file_bytes_transferred", statistics.file_bytes_transferred); + notify.put_unchecked(0, "network_bytes_send", statistics.network_bytes_send); + notify.put_unchecked(0, "network_bytes_received", statistics.network_bytes_received); + + notify.put_unchecked(0, "file_start_offset", statistics.file_start_offset); + notify.put_unchecked(0, "file_current_offset", statistics.file_current_offset); + notify.put_unchecked(0, "file_total_size", statistics.file_total_size); + client->sendCommand(notify); +} + +void FileServerHandler::callback_transfer_started(const std::shared_ptr &transfer) { + auto server = this->instance_->getVoiceServerManager()->findServerById(transfer->server->server_id()); + if(!server) return; /* well that's bad */ + + auto client = server->find_client_by_id(transfer->client_id); + if(!client || client->getUid() != transfer->client_unique_id) return; + + + ts::command_builder notify{"notifyfiletransferstarted"}; + notify.put_unchecked(0, "clientftid", transfer->client_transfer_id); + client->sendCommand(notify); +} + +void FileServerHandler::callback_transfer_finished(const std::shared_ptr &transfer) { + auto server = this->instance_->getVoiceServerManager()->findServerById(transfer->server->server_id()); + if(!server) return; /* well that's bad */ + + auto client = server->find_client_by_id(transfer->client_id); + if(!client || client->getUid() != transfer->client_unique_id) return; + + ts::command_builder notify{"notifystatusfiletransfer"}; + + notify.put_unchecked(0, "clientftfid", transfer->client_transfer_id); + notify.put(0, "size", 0); /* not sure where TeamSpeak counts from */ + notify.put_unchecked(0, "status", (int) error::file_transfer_complete); + notify.put_unchecked(0, "msg", findError(error::file_transfer_complete).message); + + /* TODO: Some stats? */ + + client->sendCommand(notify); } \ No newline at end of file diff --git a/server/src/FileServerHandler.h b/server/src/FileServerHandler.h index 36a1ed2..25e7e5c 100644 --- a/server/src/FileServerHandler.h +++ b/server/src/FileServerHandler.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "./InstanceHandler.h" @@ -25,7 +26,7 @@ namespace ts::server::file { void callback_transfer_started(const std::shared_ptr&); void callback_transfer_finished(const std::shared_ptr&); - void callback_transfer_aborted(const std::shared_ptr&, const transfer::TransferError&); + void callback_transfer_aborted(const std::shared_ptr&, const transfer::TransferStatistics&, const transfer::TransferError&); void callback_transfer_statistics(const std::shared_ptr&, const transfer::TransferStatistics&); }; } \ No newline at end of file diff --git a/server/src/InstanceHandler.cpp b/server/src/InstanceHandler.cpp index 3bbc0f8..bf9b8c2 100644 --- a/server/src/InstanceHandler.cpp +++ b/server/src/InstanceHandler.cpp @@ -428,6 +428,11 @@ void InstanceHandler::stopInstance() { this->save_channel_permissions(); this->save_group_permissions(); + if(this->file_server_handler_) { + this->file_server_handler_->finalize(); + delete std::exchange(this->file_server_handler_, nullptr); + } + delete this->sslMgr; this->sslMgr = nullptr; diff --git a/server/src/TS3ServerHeartbeat.cpp b/server/src/TS3ServerHeartbeat.cpp index 56c3c04..193fd6f 100644 --- a/server/src/TS3ServerHeartbeat.cpp +++ b/server/src/TS3ServerHeartbeat.cpp @@ -209,20 +209,6 @@ void VirtualServer::executeServerTick() { BEGIN_TIMINGS(); this->serverStatistics->tick(); - - if(fileStatisticsTimestamp + seconds(5) < system_clock::now()) { - fileStatisticsTimestamp = system_clock::now(); - auto update = this->serverStatistics->mark_file_bytes(); - if(update.first > 0) { - this->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED] += update.first; - this->properties()[property::VIRTUALSERVER_TOTAL_BYTES_DOWNLOADED] += update.first; - } - if(update.second > 0) { - this->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED] += update.second; - this->properties()[property::VIRTUALSERVER_TOTAL_BYTES_UPLOADED] += update.second; - } - } - { lock_guard lock(this->join_attempts_lock); if(system_clock::now() > this->join_last_decrease + seconds(5)) { diff --git a/server/src/client/query/QueryClientCommands.cpp b/server/src/client/query/QueryClientCommands.cpp index ac00a65..97c945b 100644 --- a/server/src/client/query/QueryClientCommands.cpp +++ b/server/src/client/query/QueryClientCommands.cpp @@ -782,8 +782,8 @@ command_result QueryClient::handleCommandHostInfo(Command &) { res["connection_bandwidth_received_last_minute_total"] = std::accumulate(report_minute.connection_bytes_received.begin(), report_minute.connection_bytes_received.end(), 0U); - res["connection_filetransfer_bandwidth_sent"] = report_minute.file_bytes_sent; - res["connection_filetransfer_bandwidth_received"] = report_minute.file_bytes_received; + res["connection_filetransfer_bandwidth_sent"] = report_second.file_bytes_sent; + res["connection_filetransfer_bandwidth_received"] = report_second.file_bytes_received; res["connection_filetransfer_bytes_sent_total"] = total_stats.file_bytes_sent; res["connection_filetransfer_bytes_received_total"] = total_stats.file_bytes_received;