diff --git a/solanaceae/tox_messages/obj_components.hpp b/solanaceae/tox_messages/obj_components.hpp index f423fe8..f9347f6 100644 --- a/solanaceae/tox_messages/obj_components.hpp +++ b/solanaceae/tox_messages/obj_components.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include // contains the alias #include @@ -36,6 +37,11 @@ namespace ObjectStore::Components { uint32_t transfer_number; }; + // TODO: replace this with something generic + struct ToxContact { + ContactHandle4 c; + }; + // TODO: replace this with something generic struct ToxMessage { // the message, if the ft is visible as a message diff --git a/solanaceae/tox_messages/tox_transfer_manager.cpp b/solanaceae/tox_messages/tox_transfer_manager.cpp index 8cf4d42..1c844d5 100644 --- a/solanaceae/tox_messages/tox_transfer_manager.cpp +++ b/solanaceae/tox_messages/tox_transfer_manager.cpp @@ -25,7 +25,11 @@ namespace Components { struct TFTFile2 { // the cached file2 for receiving/sending only + // should be destroyed when no activity and recreated on demand std::unique_ptr file; + + // set to current time on init, read, write + uint64_t last_activity_ts {}; }; } // Components @@ -78,6 +82,62 @@ ObjectHandle ToxTransferManager::toxFriendLookupReceiving(const uint32_t friend_ } } +File2I* ToxTransferManager::objGetFile2Write(ObjectHandle o) { + auto* file2_comp_ptr = o.try_get(); + if (file2_comp_ptr == nullptr || !file2_comp_ptr->file || !file2_comp_ptr->file->can_write || !file2_comp_ptr->file->isGood()) { + std::cout << "TTM: (re)opening object " << entt::to_integral(entt::to_entity(o.entity())) << " for writing\n"; + // (re)request file2 from backend + auto* file_backend = o.get().ptr; + if (file_backend == nullptr) { + std::cerr << "TTM error: object backend nullptr\n"; + return nullptr; + } + + //auto new_file = _mfb.file2(o, StorageBackendIFile2::FILE2_WRITE); + auto file2 = file_backend->file2(o, StorageBackendIFile2::FILE2_WRITE); + if (!file2 || !file2->isGood() || !file2->can_write) { + std::cerr << "TTM error: creating file2 from object via backendI\n"; + return nullptr; + } + + file2_comp_ptr = &o.emplace_or_replace(std::move(file2), getTimeMS()); + } + assert(file2_comp_ptr != nullptr); + assert(static_cast(file2_comp_ptr->file)); + + file2_comp_ptr->last_activity_ts = getTimeMS(); + + return file2_comp_ptr->file.get(); +} + +File2I* ToxTransferManager::objGetFile2Read(ObjectHandle o) { + auto* file2_comp_ptr = o.try_get(); + if (file2_comp_ptr == nullptr || !file2_comp_ptr->file || !file2_comp_ptr->file->can_read || !file2_comp_ptr->file->isGood()) { + std::cout << "TTM: (re)opening object " << entt::to_integral(entt::to_entity(o.entity())) << " for reading\n"; + // (re)request file2 from backend + auto* file_backend = o.get().ptr; + if (file_backend == nullptr) { + std::cerr << "TTM error: object backend nullptr\n"; + return nullptr; + } + + //auto new_file = _mfb.file2(o, StorageBackendIFile2::FILE2_READ); + auto file2 = file_backend->file2(o, StorageBackendIFile2::FILE2_READ); + if (!file2 || !file2->isGood() || !file2->can_read) { + std::cerr << "TTM error: creating file2 from object via backendI\n"; + return nullptr; + } + + file2_comp_ptr = &o.emplace_or_replace(std::move(file2), getTimeMS()); + } + assert(file2_comp_ptr != nullptr); + assert(static_cast(file2_comp_ptr->file)); + + file2_comp_ptr->last_activity_ts = getTimeMS(); + + return file2_comp_ptr->file.get(); +} + ToxTransferManager::ToxTransferManager( RegistryMessageModelI& rmm, ContactStore4I& cs, @@ -119,11 +179,6 @@ Message3Handle ToxTransferManager::toxSendFilePath(const Contact4 c, uint32_t fi return {}; } - auto* reg_ptr = _rmm.get(c); - if (reg_ptr == nullptr) { - return {}; - } - auto file_impl = std::make_unique(file_path); if (!file_impl->isGood()) { std::cerr << "TTM error: failed opening file '" << file_path << "'!\n"; @@ -149,6 +204,7 @@ Message3Handle ToxTransferManager::toxSendFilePath(const Contact4 c, uint32_t fi } auto o = _ftb.newObject(ByteSpan{file_id}, false); + //auto o = _os.objectHandle(_os.registry().create()); o.emplace(); o.emplace(); @@ -165,6 +221,11 @@ Message3Handle ToxTransferManager::toxSendFilePath(const Contact4 c, uint32_t fi // TODO: replace with better state tracking o.emplace(); + auto* reg_ptr = _rmm.get(c); + if (reg_ptr == nullptr) { + return {}; + } + Message3Handle msg {*reg_ptr, reg_ptr->create()}; msg.emplace(c); msg.emplace(c_self); @@ -355,6 +416,18 @@ bool ToxTransferManager::accept(ObjectHandle transfer, std::string_view file_pat return false; } + if (transfer.any_of()) { + std::cerr << "TTM error: accepted transfer " << entt::to_integral(transfer.entity()) << " already has backend, use obj instead\n"; + return false; + } + + { // HACK: backend has no attach() and we gonna remove this eitherway + const auto& id_data = transfer.get().id.data; + transfer.emplace(std::vector(id_data.cbegin(), id_data.cend())); + transfer.emplace(&_ftb); + transfer.emplace(&_ftb); + } + if (transfer.any_of()) { std::cerr << "TTM warning: overwriting existing file_impl " << entt::to_integral(transfer.entity()) << "\n"; } @@ -383,6 +456,39 @@ bool ToxTransferManager::accept(ObjectHandle transfer, std::string_view file_pat return true; } +bool ToxTransferManager::acceptObj(ObjectHandle transfer) { + if (!static_cast(transfer)) { + std::cerr << "TTM error: accepted transfer " << entt::to_integral(transfer.entity()) << " is not a valid transfer\n"; + return false; + } + + if (!transfer.all_of()) { + std::cerr << "TTM error: accepted transfer " << entt::to_integral(transfer.entity()) << " is not a receiving transfer\n"; + return false; + } + + if (transfer.any_of()) { + std::cerr << "TTM error: existing file_impl " << entt::to_integral(transfer.entity()) << "\n"; + return false; + } + + if (!transfer.all_of()) { + std::cerr << "TTM error: transfer " << entt::to_integral(transfer.entity()) << " missing BackendFile2\n"; + return false; + } + + if (!resume(transfer)) { + std::cerr << "TTM error: accepted transfer " << entt::to_integral(transfer.entity()) << " failed to resume\n"; + return false; + } + + std::cout << "TTM info: accepted " << entt::to_integral(transfer.entity()) << "\n"; + + // resume() throws events (bad lol) + + return true; +} + bool ToxTransferManager::sendFilePath(const Contact4 c, std::string_view file_name, std::string_view file_path) { const auto& cr = _cs.registry(); if ( @@ -487,6 +593,7 @@ bool ToxTransferManager::onToxEvent(const Tox_Event_File_Recv* e) { }); if (static_cast(o)) { std::cerr << "TTM error: existing file transfer frd:" << friend_number << " fnb:" << file_number << "\n"; + // TODO: hard error return false; } assert(!_friend_receiving_lookup.count((uint64_t(friend_number) << 32) | file_number)); @@ -497,7 +604,7 @@ bool ToxTransferManager::onToxEvent(const Tox_Event_File_Recv* e) { //assert(f_id_opt.has_value()); if (!f_id_opt.has_value()) { // very unfortuante, toxcore already forgot about the transfer we are handling - // TODO: make sure we exit cracefully here + // TODO: make sure we exit gracefully here std::cerr << "TTM error: querying for fileid failed, toxcore already forgot. frd:" << friend_number << " fnb:" << file_number << "\n"; return false; } @@ -509,11 +616,14 @@ bool ToxTransferManager::onToxEvent(const Tox_Event_File_Recv* e) { auto self_c = cr.get(c).self; - o = _ftb.newObject(ByteSpan{f_id_opt.value()}, false); + //o = _ftb.newObject(ByteSpan{f_id_opt.value()}, false); + o = _os.objectHandle(_os.registry().create()); + //o.emplace(f_id_opt.value()); // actually no, the meta backend does this, once accepted o.emplace(); o.emplace(); o.emplace(friend_number, file_number); + o.emplace(c); o.emplace(file_kind); o.emplace(f_id_opt.value()); @@ -525,23 +635,35 @@ bool ToxTransferManager::onToxEvent(const Tox_Event_File_Recv* e) { toxFriendLookupAdd(o); - Message3Handle msg = {*reg_ptr, reg_ptr->create()}; + Message3Handle msg; + if (file_kind == 0) { + msg = {*reg_ptr, reg_ptr->create()}; - msg.emplace(self_c); - msg.emplace(c); - msg.emplace(ts); // reactive? - msg.emplace(); - { - auto& rb = msg.emplace().ts; - //rb.try_emplace(self_c, ts); // only on completion - rb.try_emplace(c, ts); + msg.emplace(self_c); + msg.emplace(c); + msg.emplace(ts); // reactive? + msg.emplace(); + { + auto& rb = msg.emplace().ts; + //rb.try_emplace(self_c, ts); // only on completion + rb.try_emplace(c, ts); + } + msg.emplace(o); + + o.emplace(msg); } - msg.emplace(o); - - o.emplace(msg); + // maybe system message otherwise? might get very spammy _os.throwEventConstruct(o); - _rmm.throwEventConstruct(msg); + + // check accepted + if (o.all_of()) { + acceptObj(o); + } + + if (file_kind == 0) { + _rmm.throwEventConstruct(msg); + } return true; } @@ -638,24 +760,26 @@ bool ToxTransferManager::onToxEvent(const Tox_Event_File_Recv_Chunk* e) { _rmm.throwEventUpdate(msg); } - } else if (!o.all_of() || !o.get().file || !o.get().file->isGood()) { - std::cerr << "TTM error: file not good f" << friend_number << " t" << file_number << ", closing\n"; - _t.toxFileControl(friend_number, file_number, Tox_File_Control::TOX_FILE_CONTROL_CANCEL); - - // update lookup table - toxFriendLookupRemove(o); - - o.remove< - ObjComp::Ephemeral::ToxTransferFriend, - Components::TFTFile2 - >(); - - _os.throwEventUpdate(o); - // update messages? } else { - auto& file = o.get().file; - const auto res = file->write({data, data_size}, position); - //o.get().total += data_size; + auto* file_ptr = objGetFile2Write(o); + if (file_ptr == nullptr || !file_ptr->isGood()) { + std::cerr << "TTM error: file not good f" << friend_number << " t" << file_number << ", closing\n"; + _t.toxFileControl(friend_number, file_number, Tox_File_Control::TOX_FILE_CONTROL_CANCEL); + + // update lookup table + toxFriendLookupRemove(o); + + o.remove< + ObjComp::Ephemeral::ToxTransferFriend, + Components::TFTFile2 + >(); + + _os.throwEventUpdate(o); + // update messages? + + return true; + } + const auto res = file_ptr->write({data, data_size}, position); o.get_or_emplace().total_down += data_size; // queue? @@ -691,25 +815,28 @@ bool ToxTransferManager::onToxEvent(const Tox_Event_File_Chunk_Request* e) { >(); // TODO: add tag finished? - //_rmm.throwEventUpdate(o); - _os.throwEventUpdate(o); - } else if (!o.all_of() || !o.get().file || !o.get().file->isGood()) { - std::cerr << "TTM error: file not good f" << friend_number << " t" << file_number << ", closing\n"; - _t.toxFileControl(friend_number, file_number, Tox_File_Control::TOX_FILE_CONTROL_CANCEL); - - // update lookup table - toxFriendLookupRemove(o); - - o.remove< - ObjComp::Ephemeral::ToxTransferFriend, - Components::TFTFile2 - >(); - //_rmm.throwEventUpdate(o); _os.throwEventUpdate(o); } else { - auto& file = o.get().file; - const auto data = file->read(data_size, position); + auto* file_ptr = objGetFile2Read(o); + if (file_ptr == nullptr || !file_ptr->isGood()) { + std::cerr << "TTM error: file not good f" << friend_number << " t" << file_number << ", closing\n"; + _t.toxFileControl(friend_number, file_number, Tox_File_Control::TOX_FILE_CONTROL_CANCEL); + + // update lookup table + toxFriendLookupRemove(o); + + o.remove< + ObjComp::Ephemeral::ToxTransferFriend, + Components::TFTFile2 + >(); + + //_rmm.throwEventUpdate(o); + _os.throwEventUpdate(o); + return true; + } + + const auto data = file_ptr->read(data_size, position); if (data.empty()) { std::cerr << "TMM error: failed to read file!!\n"; return true; diff --git a/solanaceae/tox_messages/tox_transfer_manager.hpp b/solanaceae/tox_messages/tox_transfer_manager.hpp index 68646ec..aada12d 100644 --- a/solanaceae/tox_messages/tox_transfer_manager.hpp +++ b/solanaceae/tox_messages/tox_transfer_manager.hpp @@ -21,7 +21,7 @@ struct ToxI; class ToxTransferManager : public RegistryMessageModelEventI, public ObjectStoreEventI, public ToxEventI { public: - static constexpr const char* version {"3"}; + static constexpr const char* version {"4"}; protected: RegistryMessageModelI& _rmm; @@ -32,6 +32,8 @@ class ToxTransferManager : public RegistryMessageModelEventI, public ObjectStore ToxEventProviderI::SubscriptionReference _tep_sr; ObjectStore2& _os; ObjectStore2::SubscriptionReference _os_sr; + + // TODO: remove Backends::ToxFTFilesystem _ftb; bool _in_obj_update_event {false}; @@ -46,6 +48,9 @@ class ToxTransferManager : public RegistryMessageModelEventI, public ObjectStore ObjectHandle toxFriendLookupSending(const uint32_t friend_number, const uint32_t file_number) const; ObjectHandle toxFriendLookupReceiving(const uint32_t friend_number, const uint32_t file_number) const; + File2I* objGetFile2Write(ObjectHandle o); + File2I* objGetFile2Read(ObjectHandle o); + public: ToxTransferManager( RegistryMessageModelI& rmm, @@ -71,6 +76,7 @@ class ToxTransferManager : public RegistryMessageModelEventI, public ObjectStore // calls setFileI() and resume() bool accept(ObjectHandle transfer, std::string_view file_path, bool path_is_file); + bool acceptObj(ObjectHandle transfer); protected: // (r)mm bool sendFilePath(const Contact4 c, std::string_view file_name, std::string_view file_path) override;