add tox messages
This commit is contained in:
parent
df87cd9c8a
commit
059c0f9ec9
@ -18,3 +18,21 @@ target_link_libraries(solanaceae_tox_contacts PUBLIC
|
|||||||
solanaceae_toxcore
|
solanaceae_toxcore
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_library(solanaceae_tox_messages
|
||||||
|
./solanaceae/tox_messages/components.hpp
|
||||||
|
./solanaceae/tox_messages/components_id.inl
|
||||||
|
|
||||||
|
./solanaceae/tox_messages/tox_message_manager.hpp
|
||||||
|
./solanaceae/tox_messages/tox_message_manager.cpp
|
||||||
|
|
||||||
|
# TODO: seperate tf?
|
||||||
|
./solanaceae/tox_messages/tox_transfer_manager.hpp
|
||||||
|
./solanaceae/tox_messages/tox_transfer_manager.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(solanaceae_tox_messages PUBLIC .)
|
||||||
|
target_compile_features(solanaceae_tox_messages PUBLIC cxx_std_17)
|
||||||
|
target_link_libraries(solanaceae_tox_messages PUBLIC
|
||||||
|
solanaceae_tox_contacts
|
||||||
|
)
|
||||||
|
|
||||||
|
46
solanaceae/tox_messages/components.hpp
Normal file
46
solanaceae/tox_messages/components.hpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <solanaceae/contact/contact_model3.hpp>
|
||||||
|
|
||||||
|
#include <solanaceae/toxcore/tox_key.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
namespace Message::Components {
|
||||||
|
|
||||||
|
struct ToxGroupMessageID {
|
||||||
|
uint32_t id = 0u;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: move all those comps
|
||||||
|
|
||||||
|
struct SyncedBy {
|
||||||
|
std::set<Contact3> list;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Transfer {
|
||||||
|
|
||||||
|
struct ToxTransferFriend {
|
||||||
|
uint32_t friend_number;
|
||||||
|
uint32_t transfer_number;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FileID {
|
||||||
|
// persistent ID
|
||||||
|
// sometimes called file_id or hash
|
||||||
|
ToxKey id;
|
||||||
|
// TODO: variable length
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FileKind {
|
||||||
|
// TODO: use tox file kind
|
||||||
|
uint64_t kind {0};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // Transfer
|
||||||
|
|
||||||
|
} // Message::Components
|
||||||
|
|
||||||
|
#include "./components_id.inl"
|
||||||
|
|
22
solanaceae/tox_messages/components_id.inl
Normal file
22
solanaceae/tox_messages/components_id.inl
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include "./components.hpp"
|
||||||
|
|
||||||
|
#include <entt/core/type_info.hpp>
|
||||||
|
|
||||||
|
// TODO: move more central
|
||||||
|
#define DEFINE_COMP_ID(x) \
|
||||||
|
template<> \
|
||||||
|
constexpr entt::id_type entt::type_hash<x>::value() noexcept { \
|
||||||
|
using namespace entt::literals; \
|
||||||
|
return #x##_hs; \
|
||||||
|
}
|
||||||
|
|
||||||
|
// cross compile(r) stable ids
|
||||||
|
|
||||||
|
DEFINE_COMP_ID(Message::Components::ToxGroupMessageID)
|
||||||
|
DEFINE_COMP_ID(Message::Components::SyncedBy)
|
||||||
|
DEFINE_COMP_ID(Message::Components::Transfer::ToxTransferFriend)
|
||||||
|
DEFINE_COMP_ID(Message::Components::Transfer::FileID)
|
||||||
|
DEFINE_COMP_ID(Message::Components::Transfer::FileKind)
|
||||||
|
|
||||||
|
#undef DEFINE_COMP_ID
|
||||||
|
|
302
solanaceae/tox_messages/tox_message_manager.cpp
Normal file
302
solanaceae/tox_messages/tox_message_manager.cpp
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
#include "./tox_message_manager.hpp"
|
||||||
|
|
||||||
|
#include <solanaceae/toxcore/tox_interface.hpp>
|
||||||
|
|
||||||
|
#include <solanaceae/contact/components.hpp>
|
||||||
|
#include <solanaceae/tox_contacts/components.hpp>
|
||||||
|
#include <solanaceae/message3/components.hpp>
|
||||||
|
#include "./components.hpp"
|
||||||
|
|
||||||
|
#include <sodium.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
ToxMessageManager::ToxMessageManager(RegistryMessageModel& rmm, Contact3Registry& cr, ToxContactModel2& tcm, ToxI& t, ToxEventProviderI& tep) : _rmm(rmm), _cr(cr), _tcm(tcm), _t(t) {
|
||||||
|
// TODO: system messages?
|
||||||
|
//tep.subscribe(this, Tox_Event::TOX_EVENT_FRIEND_CONNECTION_STATUS);
|
||||||
|
//tep.subscribe(this, Tox_Event::TOX_EVENT_FRIEND_STATUS);
|
||||||
|
tep.subscribe(this, Tox_Event::TOX_EVENT_FRIEND_MESSAGE);
|
||||||
|
|
||||||
|
// TODO: conf
|
||||||
|
|
||||||
|
// TODO: system messages?
|
||||||
|
//tep.subscribe(this, Tox_Event::TOX_EVENT_GROUP_PEER_JOIN);
|
||||||
|
//tep.subscribe(this, Tox_Event::TOX_EVENT_GROUP_SELF_JOIN);
|
||||||
|
//tep.subscribe(this, Tox_Event::TOX_EVENT_GROUP_PEER_NAME);
|
||||||
|
tep.subscribe(this, Tox_Event::TOX_EVENT_GROUP_MESSAGE);
|
||||||
|
tep.subscribe(this, Tox_Event::TOX_EVENT_GROUP_PRIVATE_MESSAGE);
|
||||||
|
|
||||||
|
_rmm.subscribe(this, RegistryMessageModel_Event::send_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxMessageManager::~ToxMessageManager(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxMessageManager::sendText(const Contact3 c, std::string_view message, bool action) {
|
||||||
|
if (!_cr.valid(c)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.empty()) {
|
||||||
|
return false; // TODO: empty messages allowed?
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cr.all_of<Contact::Components::TagSelfStrong>(c)) {
|
||||||
|
return false; // message to self? not with tox
|
||||||
|
}
|
||||||
|
|
||||||
|
// testing for persistent is enough
|
||||||
|
if (!_cr.any_of<
|
||||||
|
Contact::Components::ToxFriendPersistent,
|
||||||
|
// TODO: conf
|
||||||
|
Contact::Components::ToxGroupPersistent,
|
||||||
|
Contact::Components::ToxGroupPeerPersistent
|
||||||
|
>(c)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* reg_ptr = _rmm.get(c);
|
||||||
|
if (reg_ptr == nullptr) {
|
||||||
|
return false; // nope
|
||||||
|
}
|
||||||
|
|
||||||
|
Message3Registry& reg = *reg_ptr;
|
||||||
|
|
||||||
|
if (!_cr.all_of<Contact::Components::Self>(c)) {
|
||||||
|
std::cerr << "TMM error: cant get self\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const Contact3 c_self = _cr.get<Contact::Components::Self>(c).self;
|
||||||
|
|
||||||
|
// get current time unix epoch utc
|
||||||
|
uint64_t ts = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
|
|
||||||
|
auto new_msg_e = reg.create();
|
||||||
|
reg.emplace<Message::Components::ContactFrom>(new_msg_e, c_self);
|
||||||
|
reg.emplace<Message::Components::ContactTo>(new_msg_e, c);
|
||||||
|
|
||||||
|
reg.emplace<Message::Components::MessageText>(new_msg_e, message);
|
||||||
|
|
||||||
|
if (action) {
|
||||||
|
reg.emplace<Message::Components::TagMessageIsAction>(new_msg_e);
|
||||||
|
}
|
||||||
|
|
||||||
|
reg.emplace<Message::Components::TimestampWritten>(new_msg_e, ts);
|
||||||
|
reg.emplace<Message::Components::Timestamp>(new_msg_e, ts); // reactive?
|
||||||
|
|
||||||
|
// if sent?
|
||||||
|
reg.emplace<Message::Components::TimestampProcessed>(new_msg_e, ts);
|
||||||
|
|
||||||
|
if (_cr.any_of<Contact::Components::ToxFriendEphemeral>(c)) {
|
||||||
|
// TODO: add friend offline messaging
|
||||||
|
const uint32_t friend_number = _cr.get<Contact::Components::ToxFriendEphemeral>(c).friend_number;
|
||||||
|
|
||||||
|
auto [res, _] = _t.toxFriendSendMessage(
|
||||||
|
friend_number,
|
||||||
|
action ? Tox_Message_Type::TOX_MESSAGE_TYPE_ACTION : Tox_Message_Type::TOX_MESSAGE_TYPE_NORMAL,
|
||||||
|
message
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res.has_value()) {
|
||||||
|
return true; // not online? TODO: check for other errors
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
_cr.any_of<Contact::Components::ToxGroupEphemeral>(c)
|
||||||
|
) {
|
||||||
|
const uint32_t group_number = _cr.get<Contact::Components::ToxGroupEphemeral>(c).group_number;
|
||||||
|
|
||||||
|
auto [message_id_opt, _] = _t.toxGroupSendMessage(
|
||||||
|
group_number,
|
||||||
|
action ? Tox_Message_Type::TOX_MESSAGE_TYPE_ACTION : Tox_Message_Type::TOX_MESSAGE_TYPE_NORMAL,
|
||||||
|
message
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!message_id_opt.has_value()) {
|
||||||
|
// set manually, so it can still be synced
|
||||||
|
const uint32_t msg_id = randombytes_random();
|
||||||
|
reg.emplace<Message::Components::ToxGroupMessageID>(new_msg_e, msg_id);
|
||||||
|
|
||||||
|
std::cerr << "TMM: failed to send group message!\n";
|
||||||
|
} else {
|
||||||
|
// TODO: does group msg without msgid make sense???
|
||||||
|
reg.emplace<Message::Components::ToxGroupMessageID>(new_msg_e, message_id_opt.value());
|
||||||
|
|
||||||
|
// TODO: generalize?
|
||||||
|
auto& synced_by = reg.emplace<Message::Components::SyncedBy>(new_msg_e).list;
|
||||||
|
synced_by.emplace(c_self);
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
// non online group
|
||||||
|
_cr.any_of<Contact::Components::ToxGroupPersistent>(c)
|
||||||
|
) {
|
||||||
|
// create msg_id
|
||||||
|
const uint32_t msg_id = randombytes_random();
|
||||||
|
reg.emplace<Message::Components::ToxGroupMessageID>(new_msg_e, msg_id);
|
||||||
|
|
||||||
|
// TODO: generalize?
|
||||||
|
auto& synced_by = reg.emplace<Message::Components::SyncedBy>(new_msg_e).list;
|
||||||
|
synced_by.emplace(c_self);
|
||||||
|
} else if (
|
||||||
|
_cr.any_of<Contact::Components::ToxGroupPeerEphemeral>(c)
|
||||||
|
) {
|
||||||
|
const auto& numbers = _cr.get<Contact::Components::ToxGroupPeerEphemeral>(c);
|
||||||
|
|
||||||
|
auto res = _t.toxGroupSendPrivateMessage(
|
||||||
|
numbers.group_number,
|
||||||
|
numbers.peer_number,
|
||||||
|
action ? Tox_Message_Type::TOX_MESSAGE_TYPE_ACTION : Tox_Message_Type::TOX_MESSAGE_TYPE_NORMAL,
|
||||||
|
message
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res != TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_OK) {
|
||||||
|
// TODO: add offline messaging
|
||||||
|
//return true; // not online? TODO: check for other errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_rmm.throwEventConstruct(reg, new_msg_e);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxMessageManager::onToxEvent(const Tox_Event_Friend_Message* e) {
|
||||||
|
uint32_t friend_number = tox_event_friend_message_get_friend_number(e);
|
||||||
|
Tox_Message_Type type = tox_event_friend_message_get_type(e);
|
||||||
|
|
||||||
|
// get current time unix epoch utc
|
||||||
|
uint64_t ts = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
|
|
||||||
|
std::string_view message {reinterpret_cast<const char*>(tox_event_friend_message_get_message(e)), tox_event_friend_message_get_message_length(e)};
|
||||||
|
message = message.substr(0, message.find_first_of('\0')); // trim \0 // hi zoff
|
||||||
|
// TODO: low-p, extract ts from zofftrim
|
||||||
|
// TODO: sanitize utf8
|
||||||
|
|
||||||
|
std::cout << "TMM friend message " << message << "\n";
|
||||||
|
|
||||||
|
const auto c = _tcm.getContactFriend(friend_number);
|
||||||
|
const auto self_c = c.get<Contact::Components::Self>().self;
|
||||||
|
|
||||||
|
auto* reg_ptr = _rmm.get(c);
|
||||||
|
if (reg_ptr == nullptr) {
|
||||||
|
std::cerr << "TMM error: cant find reg\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Message3Registry& reg = *reg_ptr;
|
||||||
|
auto new_msg_e = reg.create();
|
||||||
|
|
||||||
|
reg.emplace<Message::Components::ContactFrom>(new_msg_e, c);
|
||||||
|
reg.emplace<Message::Components::ContactTo>(new_msg_e, self_c);
|
||||||
|
|
||||||
|
reg.emplace<Message::Components::MessageText>(new_msg_e, message);
|
||||||
|
|
||||||
|
if (type == Tox_Message_Type::TOX_MESSAGE_TYPE_ACTION) {
|
||||||
|
reg.emplace<Message::Components::TagMessageIsAction>(new_msg_e);
|
||||||
|
}
|
||||||
|
|
||||||
|
reg.emplace<Message::Components::TimestampProcessed>(new_msg_e, ts);
|
||||||
|
//reg.emplace<Components::TimestampWritten>(new_msg_e, 0);
|
||||||
|
reg.emplace<Message::Components::Timestamp>(new_msg_e, ts); // reactive?
|
||||||
|
|
||||||
|
_rmm.throwEventConstruct(reg, new_msg_e);
|
||||||
|
return false; // TODO: return true?
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxMessageManager::onToxEvent(const Tox_Event_Group_Message* e) {
|
||||||
|
const uint32_t group_number = tox_event_group_message_get_group_number(e);
|
||||||
|
const uint32_t peer_number = tox_event_group_message_get_peer_id(e);
|
||||||
|
const uint32_t message_id = tox_event_group_message_get_message_id(e);
|
||||||
|
const Tox_Message_Type type = tox_event_group_message_get_type(e);
|
||||||
|
|
||||||
|
uint64_t ts = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
|
|
||||||
|
auto message = std::string_view{reinterpret_cast<const char*>(tox_event_group_message_get_message(e)), tox_event_group_message_get_message_length(e)};
|
||||||
|
std::cout << "TMM group message: " << message << "\n";
|
||||||
|
|
||||||
|
const auto c = _tcm.getContactGroupPeer(group_number, peer_number);
|
||||||
|
const auto self_c = c.get<Contact::Components::Self>().self;
|
||||||
|
|
||||||
|
auto* reg_ptr = _rmm.get(c);
|
||||||
|
//auto* reg_ptr = _rmm.get({ContactGroupPeerEphemeral{group_number, peer_number}});
|
||||||
|
if (reg_ptr == nullptr) {
|
||||||
|
std::cerr << "TMM error: cant find reg\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Message3Registry& reg = *reg_ptr;
|
||||||
|
// TODO: check for existence, hs or other syncing mechanics might have sent it already (or like, it arrived 2x or whatever)
|
||||||
|
auto new_msg_e = reg.create();
|
||||||
|
|
||||||
|
{ // contact
|
||||||
|
// from
|
||||||
|
reg.emplace<Message::Components::ContactFrom>(new_msg_e, c);
|
||||||
|
|
||||||
|
// to
|
||||||
|
reg.emplace<Message::Components::ContactTo>(new_msg_e, c.get<Contact::Components::Parent>().parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
reg.emplace<Message::Components::ToxGroupMessageID>(new_msg_e, message_id);
|
||||||
|
|
||||||
|
reg.emplace<Message::Components::MessageText>(new_msg_e, message);
|
||||||
|
if (type == Tox_Message_Type::TOX_MESSAGE_TYPE_ACTION) {
|
||||||
|
reg.emplace<Message::Components::TagMessageIsAction>(new_msg_e);
|
||||||
|
}
|
||||||
|
|
||||||
|
reg.emplace<Message::Components::TimestampProcessed>(new_msg_e, ts);
|
||||||
|
//reg.emplace<Components::TimestampWritten>(new_msg_e, 0);
|
||||||
|
reg.emplace<Message::Components::Timestamp>(new_msg_e, ts); // reactive?
|
||||||
|
|
||||||
|
{ // by whom
|
||||||
|
auto& synced_by = reg.get_or_emplace<Message::Components::SyncedBy>(new_msg_e).list;
|
||||||
|
synced_by.emplace(self_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
_rmm.throwEventConstruct(reg, new_msg_e);
|
||||||
|
return false; // TODO: true?
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxMessageManager::onToxEvent(const Tox_Event_Group_Private_Message* e) {
|
||||||
|
const uint32_t group_number = tox_event_group_private_message_get_group_number(e);
|
||||||
|
const uint32_t peer_number = tox_event_group_private_message_get_peer_id(e);
|
||||||
|
const Tox_Message_Type type = tox_event_group_private_message_get_type(e);
|
||||||
|
|
||||||
|
uint64_t ts = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
|
|
||||||
|
auto message = std::string_view{reinterpret_cast<const char*>(tox_event_group_private_message_get_message(e)), tox_event_group_private_message_get_message_length(e)};
|
||||||
|
std::cout << "TMM group private message: " << message << "\n";
|
||||||
|
|
||||||
|
const auto c = _tcm.getContactGroupPeer(group_number, peer_number);
|
||||||
|
const auto self_c = c.get<Contact::Components::Self>().self;
|
||||||
|
|
||||||
|
auto* reg_ptr = _rmm.get(c);
|
||||||
|
if (reg_ptr == nullptr) {
|
||||||
|
std::cerr << "TMM error: cant find reg\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Message3Registry& reg = *reg_ptr;
|
||||||
|
auto new_msg_e = reg.create();
|
||||||
|
|
||||||
|
{ // contact
|
||||||
|
// from
|
||||||
|
reg.emplace<Message::Components::ContactFrom>(new_msg_e, c);
|
||||||
|
|
||||||
|
// to
|
||||||
|
reg.emplace<Message::Components::ContactTo>(new_msg_e, self_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
reg.emplace<Message::Components::MessageText>(new_msg_e, message);
|
||||||
|
if (type == Tox_Message_Type::TOX_MESSAGE_TYPE_ACTION) {
|
||||||
|
reg.emplace<Message::Components::TagMessageIsAction>(new_msg_e);
|
||||||
|
}
|
||||||
|
|
||||||
|
reg.emplace<Message::Components::TimestampProcessed>(new_msg_e, ts);
|
||||||
|
//reg.emplace<Components::TimestampWritten>(new_msg_e, 0);
|
||||||
|
reg.emplace<Message::Components::Timestamp>(new_msg_e, ts); // reactive?
|
||||||
|
|
||||||
|
// private does not track synced by
|
||||||
|
|
||||||
|
_rmm.throwEventConstruct(reg, new_msg_e);
|
||||||
|
return false;
|
||||||
|
}
|
30
solanaceae/tox_messages/tox_message_manager.hpp
Normal file
30
solanaceae/tox_messages/tox_message_manager.hpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <solanaceae/toxcore/tox_event_interface.hpp>
|
||||||
|
#include <solanaceae/message3/registry_message_model.hpp>
|
||||||
|
#include <solanaceae/tox_contacts/tox_contact_model2.hpp>
|
||||||
|
|
||||||
|
// fwd
|
||||||
|
struct ToxI;
|
||||||
|
|
||||||
|
class ToxMessageManager : public RegistryMessageModelEventI, public ToxEventI {
|
||||||
|
protected:
|
||||||
|
RegistryMessageModel& _rmm;
|
||||||
|
Contact3Registry& _cr;
|
||||||
|
ToxContactModel2& _tcm;
|
||||||
|
ToxI& _t;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ToxMessageManager(RegistryMessageModel& rmm, Contact3Registry& cr, ToxContactModel2& tcm, ToxI& t, ToxEventProviderI& tep);
|
||||||
|
virtual ~ToxMessageManager(void);
|
||||||
|
|
||||||
|
public: // mm3
|
||||||
|
bool sendText(const Contact3 c, std::string_view message, bool action = false) override;
|
||||||
|
|
||||||
|
protected: // tox events
|
||||||
|
bool onToxEvent(const Tox_Event_Friend_Message* e) override;
|
||||||
|
|
||||||
|
bool onToxEvent(const Tox_Event_Group_Message* e) override;
|
||||||
|
bool onToxEvent(const Tox_Event_Group_Private_Message* e) override;
|
||||||
|
};
|
||||||
|
|
603
solanaceae/tox_messages/tox_transfer_manager.cpp
Normal file
603
solanaceae/tox_messages/tox_transfer_manager.cpp
Normal file
@ -0,0 +1,603 @@
|
|||||||
|
#include "./tox_transfer_manager.hpp"
|
||||||
|
|
||||||
|
#include <solanaceae/toxcore/tox_interface.hpp>
|
||||||
|
|
||||||
|
#include <solanaceae/message3/file_r_file.hpp>
|
||||||
|
#include <solanaceae/message3/file_w_file.hpp>
|
||||||
|
|
||||||
|
#include <solanaceae/contact/components.hpp>
|
||||||
|
#include <solanaceae/tox_contacts/components.hpp>
|
||||||
|
#include <solanaceae/message3/components.hpp>
|
||||||
|
#include "./components.hpp"
|
||||||
|
|
||||||
|
#include <sodium.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// https://youtu.be/4XsL5iYHS6c
|
||||||
|
|
||||||
|
void ToxTransferManager::toxFriendLookupAdd(Message3Handle h) {
|
||||||
|
const auto& comp = h.get<Message::Components::Transfer::ToxTransferFriend>();
|
||||||
|
const uint64_t key {(uint64_t(comp.friend_number) << 32) | comp.transfer_number};
|
||||||
|
|
||||||
|
if (h.all_of<Message::Components::Transfer::TagSending>()) {
|
||||||
|
assert(!_friend_sending_lookup.count(key));
|
||||||
|
_friend_sending_lookup[key] = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (h.all_of<Message::Components::Transfer::TagReceiving>()) {
|
||||||
|
assert(!_friend_receiving_lookup.count(key));
|
||||||
|
_friend_receiving_lookup[key] = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ToxTransferManager::toxFriendLookupRemove(Message3Handle h) {
|
||||||
|
const auto& comp = h.get<Message::Components::Transfer::ToxTransferFriend>();
|
||||||
|
const uint64_t key {(uint64_t(comp.friend_number) << 32) | comp.transfer_number};
|
||||||
|
|
||||||
|
if (h.all_of<Message::Components::Transfer::TagSending>()) {
|
||||||
|
assert(_friend_sending_lookup.count(key));
|
||||||
|
_friend_sending_lookup.erase(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (h.all_of<Message::Components::Transfer::TagReceiving>()) {
|
||||||
|
assert(_friend_receiving_lookup.count(key));
|
||||||
|
_friend_receiving_lookup.erase(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Message3Handle ToxTransferManager::toxFriendLookupSending(const uint32_t friend_number, const uint32_t file_number) const {
|
||||||
|
const auto lookup_it = _friend_sending_lookup.find((uint64_t(friend_number) << 32) | file_number);
|
||||||
|
if (lookup_it != _friend_sending_lookup.end()) {
|
||||||
|
return lookup_it->second;
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Message3Handle ToxTransferManager::toxFriendLookupReceiving(const uint32_t friend_number, const uint32_t file_number) const {
|
||||||
|
const auto lookup_it = _friend_receiving_lookup.find((uint64_t(friend_number) << 32) | file_number);
|
||||||
|
if (lookup_it != _friend_receiving_lookup.end()) {
|
||||||
|
return lookup_it->second;
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxTransferManager::ToxTransferManager(RegistryMessageModel& rmm, Contact3Registry& cr, ToxContactModel2& tcm, ToxI& t, ToxEventProviderI& tep) : _rmm(rmm), _cr(cr), _tcm(tcm), _t(t) {
|
||||||
|
tep.subscribe(this, Tox_Event::TOX_EVENT_FRIEND_CONNECTION_STATUS);
|
||||||
|
tep.subscribe(this, Tox_Event::TOX_EVENT_FILE_RECV);
|
||||||
|
tep.subscribe(this, Tox_Event::TOX_EVENT_FILE_RECV_CONTROL);
|
||||||
|
tep.subscribe(this, Tox_Event::TOX_EVENT_FILE_RECV_CHUNK);
|
||||||
|
tep.subscribe(this, Tox_Event::TOX_EVENT_FILE_CHUNK_REQUEST);
|
||||||
|
|
||||||
|
_rmm.subscribe(this, RegistryMessageModel_Event::message_construct);
|
||||||
|
_rmm.subscribe(this, RegistryMessageModel_Event::message_updated);
|
||||||
|
_rmm.subscribe(this, RegistryMessageModel_Event::message_destroy);
|
||||||
|
|
||||||
|
_rmm.subscribe(this, RegistryMessageModel_Event::send_file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxTransferManager::~ToxTransferManager(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ToxTransferManager::iterate(void) {
|
||||||
|
// TODO: time out transfers
|
||||||
|
}
|
||||||
|
|
||||||
|
Message3Handle ToxTransferManager::toxSendFilePath(const Contact3 c, uint32_t file_kind, std::string_view file_name, std::string_view file_path) {
|
||||||
|
if (
|
||||||
|
// TODO: add support of offline queuing
|
||||||
|
!_cr.all_of<Contact::Components::ToxFriendEphemeral>(c)
|
||||||
|
) {
|
||||||
|
std::cerr << "TTM error: unsupported contact type\n";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* reg_ptr = _rmm.get(c);
|
||||||
|
if (reg_ptr == nullptr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto file_impl = std::make_unique<FileRFile>(file_path);
|
||||||
|
if (!file_impl->isGood()) {
|
||||||
|
std::cerr << "TTM error: failed opening file '" << file_path << "'!\n";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// get current time unix epoch utc
|
||||||
|
uint64_t ts = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
|
|
||||||
|
// TODO: expose file id
|
||||||
|
std::vector<uint8_t> file_id(32); assert(file_id.size() == 32);
|
||||||
|
randombytes_buf(file_id.data(), file_id.size());
|
||||||
|
|
||||||
|
const auto c_self = _cr.get<Contact::Components::Self>(c).self;
|
||||||
|
if (!_cr.valid(c_self)) {
|
||||||
|
std::cerr << "TTM error: failed to get self!\n";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto e = reg_ptr->create();
|
||||||
|
reg_ptr->emplace<Message::Components::ContactTo>(e, c);
|
||||||
|
reg_ptr->emplace<Message::Components::ContactFrom>(e, c_self);
|
||||||
|
reg_ptr->emplace<Message::Components::Timestamp>(e, ts); // reactive?
|
||||||
|
|
||||||
|
reg_ptr->emplace<Message::Components::Transfer::TagHaveAll>(e);
|
||||||
|
reg_ptr->emplace<Message::Components::Transfer::TagSending>(e);
|
||||||
|
reg_ptr->emplace<Message::Components::Transfer::FileKind>(e, file_kind);
|
||||||
|
reg_ptr->emplace<Message::Components::Transfer::FileID>(e, file_id);
|
||||||
|
|
||||||
|
{ // file info
|
||||||
|
auto& file_info = reg_ptr->emplace<Message::Components::Transfer::FileInfo>(e);
|
||||||
|
file_info.file_list.emplace_back() = {std::string{file_name}, file_impl->_file_size};
|
||||||
|
file_info.total_size = file_impl->_file_size;
|
||||||
|
|
||||||
|
reg_ptr->emplace<Message::Components::Transfer::FileInfoLocal>(e, std::vector{std::string{file_path}});
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_ptr->emplace<Message::Components::Transfer::BytesSent>(e);
|
||||||
|
|
||||||
|
// TODO: determine if this is true
|
||||||
|
reg_ptr->emplace<Message::Components::Transfer::TagPaused>(e);
|
||||||
|
|
||||||
|
const auto friend_number = _cr.get<Contact::Components::ToxFriendEphemeral>(c).friend_number;
|
||||||
|
const auto&& [transfer_id, err] = _t.toxFileSend(friend_number, file_kind, file_impl->_file_size, file_id, file_name);
|
||||||
|
if (err == TOX_ERR_FILE_SEND_OK) {
|
||||||
|
reg_ptr->emplace<Message::Components::Transfer::ToxTransferFriend>(e, friend_number, transfer_id.value());
|
||||||
|
reg_ptr->emplace<Message::Components::Transfer::File>(e, std::move(file_impl));
|
||||||
|
// TODO: add tag signifying init sent status?
|
||||||
|
|
||||||
|
toxFriendLookupAdd({*reg_ptr, e});
|
||||||
|
} // else queue?
|
||||||
|
|
||||||
|
_rmm.throwEventConstruct(*reg_ptr, e);
|
||||||
|
return {*reg_ptr, e};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxTransferManager::resume(Message3Handle transfer) {
|
||||||
|
if (!static_cast<bool>(transfer)) {
|
||||||
|
std::cerr << "TTM error: resume() transfer " << entt::to_integral(transfer.entity()) << " is not a valid transfer\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test for paused?
|
||||||
|
|
||||||
|
if (!transfer.all_of<Message::Components::Transfer::ToxTransferFriend>()) {
|
||||||
|
std::cerr << "TTM error: resume() transfer " << entt::to_integral(transfer.entity()) << " ent does not have toxtransfer info\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto [friend_number, transfer_number] = transfer.get<Message::Components::Transfer::ToxTransferFriend>();
|
||||||
|
|
||||||
|
const auto err = _t.toxFileControl(friend_number, transfer_number, TOX_FILE_CONTROL_RESUME);
|
||||||
|
if (err != TOX_ERR_FILE_CONTROL_OK) {
|
||||||
|
std::cerr << "TTM error: resume() transfer " << entt::to_integral(transfer.entity()) << " tox file control error " << err << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
transfer.remove<Message::Components::Transfer::TagPaused>();
|
||||||
|
|
||||||
|
_rmm.throwEventUpdate(transfer);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxTransferManager::pause(Message3Handle transfer) {
|
||||||
|
if (!static_cast<bool>(transfer)) {
|
||||||
|
std::cerr << "TTM error: pause() transfer " << entt::to_integral(transfer.entity()) << " is not a valid transfer\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test for paused?
|
||||||
|
|
||||||
|
if (!transfer.all_of<Message::Components::Transfer::ToxTransferFriend>()) {
|
||||||
|
std::cerr << "TTM error: pause() transfer " << entt::to_integral(transfer.entity()) << " ent does not have toxtransfer info\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto [friend_number, transfer_number] = transfer.get<Message::Components::Transfer::ToxTransferFriend>();
|
||||||
|
|
||||||
|
const auto err = _t.toxFileControl(friend_number, transfer_number, TOX_FILE_CONTROL_PAUSE);
|
||||||
|
if (err != TOX_ERR_FILE_CONTROL_OK) {
|
||||||
|
std::cerr << "TTM error: pause() transfer " << entt::to_integral(transfer.entity()) << " tox file control error " << err << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
transfer.emplace_or_replace<Message::Components::Transfer::TagPaused>();
|
||||||
|
|
||||||
|
_rmm.throwEventUpdate(transfer);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxTransferManager::setFileI(Message3Handle transfer, std::unique_ptr<FileI>&& new_file) {
|
||||||
|
if (!static_cast<bool>(transfer)) {
|
||||||
|
std::cerr << "TTM error: setFileI() transfer " << entt::to_integral(transfer.entity()) << " is not a valid transfer\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!new_file->isGood()) {
|
||||||
|
std::cerr << "TTM error: failed setting new_file_impl!\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
transfer.emplace_or_replace<Message::Components::Transfer::File>(std::move(new_file));
|
||||||
|
|
||||||
|
_rmm.throwEventUpdate(transfer);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxTransferManager::setFilePathDir(Message3Handle transfer, std::string_view file_path) {
|
||||||
|
if (!static_cast<bool>(transfer)) {
|
||||||
|
std::cerr << "TTM error: setFilePathDir() transfer " << entt::to_integral(transfer.entity()) << " is not a valid transfer\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t file_size {0};
|
||||||
|
std::string full_file_path{file_path};
|
||||||
|
// TODO: replace with filesystem or something
|
||||||
|
// TODO: ensure dir exists
|
||||||
|
if (full_file_path.back() != '/') {
|
||||||
|
full_file_path += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: read file name(s) from comp
|
||||||
|
if (transfer.all_of<Message::Components::Transfer::FileInfo>()) {
|
||||||
|
const auto& file_info = transfer.get<Message::Components::Transfer::FileInfo>();
|
||||||
|
file_size = file_info.total_size; // hack
|
||||||
|
// HACK: use fist enty
|
||||||
|
assert(file_info.file_list.size() == 1);
|
||||||
|
full_file_path += file_info.file_list.front().file_name;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
std::cerr << "TTM warning: no FileInfo on transfer, using default\n";
|
||||||
|
full_file_path += "file_recv.bin";
|
||||||
|
}
|
||||||
|
|
||||||
|
transfer.emplace<Message::Components::Transfer::FileInfoLocal>(std::vector{full_file_path});
|
||||||
|
|
||||||
|
auto file_impl = std::make_unique<FileWFile>(full_file_path, file_size);
|
||||||
|
if (!file_impl->isGood()) {
|
||||||
|
std::cerr << "TTM error: failed opening file '" << file_path << "'!\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
transfer.emplace_or_replace<Message::Components::Transfer::File>(std::move(file_impl));
|
||||||
|
|
||||||
|
// TODO: is this a good idea????
|
||||||
|
_rmm.throwEventUpdate(transfer);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxTransferManager::accept(Message3Handle transfer, std::string_view file_path) {
|
||||||
|
if (!static_cast<bool>(transfer)) {
|
||||||
|
std::cerr << "TTM error: accepted transfer " << entt::to_integral(transfer.entity()) << " is not a valid transfer\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!transfer.all_of<Message::Components::Transfer::TagReceiving, Message::Components::Transfer::ToxTransferFriend>()) {
|
||||||
|
std::cerr << "TTM error: accepted transfer " << entt::to_integral(transfer.entity()) << " is not a receiving transfer\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transfer.any_of<Message::Components::Transfer::File>()) {
|
||||||
|
std::cerr << "TTM warning: overwriting existing file_impl " << entt::to_integral(transfer.entity()) << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!setFilePathDir(transfer, file_path)) {
|
||||||
|
std::cerr << "TTM error: accepted transfer " << entt::to_integral(transfer.entity()) << " failed setting path\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()) << ", saving to " << file_path << "\n";
|
||||||
|
|
||||||
|
// setFilePathDir() and resume() throw events
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxTransferManager::onEvent(const Message::Events::MessageConstruct&) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxTransferManager::onEvent(const Message::Events::MessageUpdated& e) {
|
||||||
|
if (e.e.all_of<Message::Components::Transfer::ActionAccept>()) {
|
||||||
|
accept(e.e, e.e.get<Message::Components::Transfer::ActionAccept>().save_to_path);
|
||||||
|
|
||||||
|
// should?
|
||||||
|
e.e.remove<Message::Components::Transfer::ActionAccept>();
|
||||||
|
|
||||||
|
// TODO: recursion??
|
||||||
|
// oh no, accept calls it 2x
|
||||||
|
//_rmm.throwEventUpdate(
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxTransferManager::onEvent(const Message::Events::MessageDestory& e) {
|
||||||
|
if (e.e.all_of<Message::Components::Transfer::ToxTransferFriend>()) {
|
||||||
|
toxFriendLookupRemove(e.e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxTransferManager::onToxEvent(const Tox_Event_Friend_Connection_Status* e) {
|
||||||
|
const auto friend_number = tox_event_friend_connection_status_get_friend_number(e);
|
||||||
|
const auto connection_status = tox_event_friend_connection_status_get_connection_status(e);
|
||||||
|
|
||||||
|
if (connection_status == TOX_CONNECTION_NONE) {
|
||||||
|
auto c = _tcm.getContactFriend(friend_number);
|
||||||
|
auto* reg_ptr = _rmm.get(c);
|
||||||
|
if (reg_ptr == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Message3> to_destory;
|
||||||
|
reg_ptr->view<Message::Components::Transfer::ToxTransferFriend>().each([&](const Message3 ent, const auto& ttfs) {
|
||||||
|
assert(ttfs.friend_number == friend_number);
|
||||||
|
//if (ttfs.friend_number == friend_number) {
|
||||||
|
to_destory.push_back(ent);
|
||||||
|
std::cerr << "TTM warning: friend disconnected, forcefully removing e:" << entt::to_integral(ent) << " frd:" << friend_number << " fnb:" << ttfs.transfer_number << "\n";
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const auto ent : to_destory) {
|
||||||
|
// update lookup table
|
||||||
|
toxFriendLookupRemove({*reg_ptr, ent});
|
||||||
|
|
||||||
|
// TODO: removing file a good idea?
|
||||||
|
reg_ptr->remove<Message::Components::Transfer::ToxTransferFriend, Message::Components::Transfer::File>(ent);
|
||||||
|
|
||||||
|
reg_ptr->emplace_or_replace<Message::Components::Transfer::TagPaused>(ent);
|
||||||
|
|
||||||
|
_rmm.throwEventUpdate(*reg_ptr, ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // always continue
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxTransferManager::onToxEvent(const Tox_Event_File_Recv* e) {
|
||||||
|
const auto friend_number = tox_event_file_recv_get_friend_number(e);
|
||||||
|
const std::string_view file_name {
|
||||||
|
reinterpret_cast<const char*>(tox_event_file_recv_get_filename(e)),
|
||||||
|
tox_event_file_recv_get_filename_length(e)
|
||||||
|
};
|
||||||
|
const auto file_number = tox_event_file_recv_get_file_number(e);
|
||||||
|
const auto file_size = tox_event_file_recv_get_file_size(e);
|
||||||
|
const auto file_kind = tox_event_file_recv_get_kind(e);
|
||||||
|
|
||||||
|
auto c = _tcm.getContactFriend(friend_number);
|
||||||
|
auto* reg_ptr = _rmm.get(c);
|
||||||
|
if (reg_ptr == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// making sure, we dont have a dup
|
||||||
|
Message3Handle transfer {};
|
||||||
|
reg_ptr->view<Message::Components::Transfer::TagReceiving, Message::Components::Transfer::ToxTransferFriend>().each([&](Message3 ent, const Message::Components::Transfer::ToxTransferFriend& ttf) {
|
||||||
|
if (ttf.friend_number == friend_number && ttf.transfer_number == file_number) {
|
||||||
|
transfer = {*reg_ptr, ent};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (static_cast<bool>(transfer)) {
|
||||||
|
std::cerr << "TTM error: existing file transfer frd:" << friend_number << " fnb:" << file_number << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
assert(!_friend_receiving_lookup.count((uint64_t(friend_number) << 32) | file_number));
|
||||||
|
|
||||||
|
// TODO: also check for file id
|
||||||
|
|
||||||
|
// get current time unix epoch utc
|
||||||
|
uint64_t ts = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
|
|
||||||
|
// create ent
|
||||||
|
transfer = {*reg_ptr, reg_ptr->create()};
|
||||||
|
|
||||||
|
auto self_c = _cr.get<Contact::Components::Self>(c).self;
|
||||||
|
|
||||||
|
transfer.emplace<Message::Components::ContactTo>(self_c);
|
||||||
|
transfer.emplace<Message::Components::ContactFrom>(c);
|
||||||
|
transfer.emplace<Message::Components::Timestamp>(ts); // reactive?
|
||||||
|
|
||||||
|
transfer.emplace<Message::Components::Transfer::TagReceiving>();
|
||||||
|
transfer.emplace<Message::Components::Transfer::TagPaused>();
|
||||||
|
transfer.emplace<Message::Components::Transfer::ToxTransferFriend>(friend_number, file_number);
|
||||||
|
transfer.emplace<Message::Components::Transfer::FileKind>(file_kind);
|
||||||
|
|
||||||
|
auto [f_id_opt, _] = _t.toxFileGetFileID(friend_number, file_number);
|
||||||
|
assert(f_id_opt.has_value());
|
||||||
|
transfer.emplace<Message::Components::Transfer::FileID>(f_id_opt.value());
|
||||||
|
|
||||||
|
{ // file info
|
||||||
|
auto& file_info = transfer.emplace<Message::Components::Transfer::FileInfo>();
|
||||||
|
file_info.file_list.push_back({std::string{file_name}, file_size});
|
||||||
|
file_info.total_size = file_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
transfer.emplace<Message::Components::Transfer::BytesReceived>();
|
||||||
|
|
||||||
|
toxFriendLookupAdd(transfer);
|
||||||
|
|
||||||
|
_rmm.throwEventConstruct(transfer);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxTransferManager::onToxEvent(const Tox_Event_File_Recv_Control* e) {
|
||||||
|
const auto friend_number = tox_event_file_recv_control_get_friend_number(e);
|
||||||
|
const auto file_number = tox_event_file_recv_control_get_file_number(e);
|
||||||
|
const auto control = tox_event_file_recv_control_get_control(e);
|
||||||
|
|
||||||
|
// first try sending
|
||||||
|
Message3Handle transfer = toxFriendLookupSending(friend_number, file_number);
|
||||||
|
if (!static_cast<bool>(transfer)) {
|
||||||
|
// then receiving
|
||||||
|
transfer = toxFriendLookupReceiving(friend_number, file_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!static_cast<bool>(transfer)) {
|
||||||
|
std::cerr << "TMM waring: control for unk ft\n";
|
||||||
|
return false; // shrug, we don't know about it, might be someone else's
|
||||||
|
}
|
||||||
|
|
||||||
|
if (control == TOX_FILE_CONTROL_CANCEL) {
|
||||||
|
std::cerr << "TTM: friend transfer canceled frd:" << friend_number << " fnb:" << file_number << "\n";
|
||||||
|
|
||||||
|
// update lookup table
|
||||||
|
toxFriendLookupRemove(transfer);
|
||||||
|
|
||||||
|
transfer.remove<
|
||||||
|
Message::Components::Transfer::ToxTransferFriend,
|
||||||
|
// TODO: removing file a good idea?
|
||||||
|
Message::Components::Transfer::File
|
||||||
|
>();
|
||||||
|
|
||||||
|
_rmm.throwEventUpdate(transfer);
|
||||||
|
} else if (control == TOX_FILE_CONTROL_PAUSE) {
|
||||||
|
std::cerr << "TTM: friend transfer paused frd:" << friend_number << " fnb:" << file_number << "\n";
|
||||||
|
// TODO: add distinction between local and remote pause
|
||||||
|
transfer.emplace_or_replace<Message::Components::Transfer::TagPaused>();
|
||||||
|
_rmm.throwEventUpdate(transfer);
|
||||||
|
} else if (control == TOX_FILE_CONTROL_RESUME) {
|
||||||
|
std::cerr << "TTM: friend transfer resumed frd:" << friend_number << " fnb:" << file_number << "\n";
|
||||||
|
transfer.remove<Message::Components::Transfer::TagPaused>();
|
||||||
|
_rmm.throwEventUpdate(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxTransferManager::onToxEvent(const Tox_Event_File_Recv_Chunk* e) {
|
||||||
|
const auto friend_number = tox_event_file_recv_chunk_get_friend_number(e);
|
||||||
|
const auto file_number = tox_event_file_recv_chunk_get_file_number(e);
|
||||||
|
const uint8_t* data = tox_event_file_recv_chunk_get_data(e);
|
||||||
|
const auto data_size = tox_event_file_recv_chunk_get_length(e);
|
||||||
|
const auto position = tox_event_file_recv_chunk_get_position(e);
|
||||||
|
|
||||||
|
Message3Handle transfer = toxFriendLookupReceiving(friend_number, file_number);
|
||||||
|
if (!static_cast<bool>(transfer)) {
|
||||||
|
return false; // shrug, we don't know about it, might be someone else's
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_size == 0) {
|
||||||
|
std::cout << "TTM finished friend " << friend_number << " transfer " << file_number << ", closing\n";
|
||||||
|
|
||||||
|
// update lookup table
|
||||||
|
toxFriendLookupRemove(transfer);
|
||||||
|
|
||||||
|
transfer.remove<
|
||||||
|
Message::Components::Transfer::ToxTransferFriend,
|
||||||
|
// TODO: removing file a good idea?
|
||||||
|
Message::Components::Transfer::File
|
||||||
|
>();
|
||||||
|
|
||||||
|
transfer.emplace<Message::Components::Transfer::TagHaveAll>();
|
||||||
|
|
||||||
|
_rmm.throwEventUpdate(transfer);
|
||||||
|
} else if (!transfer.all_of<Message::Components::Transfer::File>() || !transfer.get<Message::Components::Transfer::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(transfer);
|
||||||
|
|
||||||
|
transfer.remove<
|
||||||
|
Message::Components::Transfer::ToxTransferFriend,
|
||||||
|
// TODO: removing file a good idea?
|
||||||
|
Message::Components::Transfer::File
|
||||||
|
>();
|
||||||
|
|
||||||
|
_rmm.throwEventUpdate(transfer);
|
||||||
|
} else {
|
||||||
|
auto* file = transfer.get<Message::Components::Transfer::File>().get();
|
||||||
|
const auto res = file->write(position, std::vector<uint8_t>{data, data+data_size});
|
||||||
|
transfer.get<Message::Components::Transfer::BytesReceived>().total += data_size;
|
||||||
|
|
||||||
|
// queue?
|
||||||
|
_rmm.throwEventUpdate(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxTransferManager::onToxEvent(const Tox_Event_File_Chunk_Request* e) {
|
||||||
|
const auto friend_number = tox_event_file_chunk_request_get_friend_number(e);
|
||||||
|
const auto file_number = tox_event_file_chunk_request_get_file_number(e);
|
||||||
|
const auto position = tox_event_file_chunk_request_get_position(e);
|
||||||
|
const auto data_size = tox_event_file_chunk_request_get_length(e);
|
||||||
|
|
||||||
|
Message3Handle transfer = toxFriendLookupSending(friend_number, file_number);
|
||||||
|
if (!static_cast<bool>(transfer)) {
|
||||||
|
std::cerr << "TTM warning: chunk request for unk ft\n";
|
||||||
|
return false; // shrug, we don't know about it, might be someone else's
|
||||||
|
}
|
||||||
|
|
||||||
|
// tox wants us to end the transmission
|
||||||
|
if (data_size == 0) {
|
||||||
|
std::cout << "TTM finished friend " << friend_number << " transfer " << file_number << ", closing\n";
|
||||||
|
|
||||||
|
// update lookup table
|
||||||
|
toxFriendLookupRemove(transfer);
|
||||||
|
|
||||||
|
transfer.remove<
|
||||||
|
Message::Components::Transfer::ToxTransferFriend,
|
||||||
|
// TODO: removing file a good idea?
|
||||||
|
Message::Components::Transfer::File
|
||||||
|
>();
|
||||||
|
|
||||||
|
// TODO: add tag finished?
|
||||||
|
_rmm.throwEventUpdate(transfer);
|
||||||
|
} else if (!transfer.all_of<Message::Components::Transfer::File>() || !transfer.get<Message::Components::Transfer::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(transfer);
|
||||||
|
|
||||||
|
transfer.remove<
|
||||||
|
Message::Components::Transfer::ToxTransferFriend,
|
||||||
|
// TODO: removing file a good idea?
|
||||||
|
Message::Components::Transfer::File
|
||||||
|
>();
|
||||||
|
|
||||||
|
_rmm.throwEventUpdate(transfer);
|
||||||
|
} else {
|
||||||
|
auto* file = transfer.get<Message::Components::Transfer::File>().get();
|
||||||
|
const auto data = file->read(position, data_size);
|
||||||
|
|
||||||
|
const auto err = _t.toxFileSendChunk(friend_number, file_number, position, data);
|
||||||
|
// TODO: investigate if i need to retry if sendq full
|
||||||
|
if (err == TOX_ERR_FILE_SEND_CHUNK_OK) {
|
||||||
|
transfer.get<Message::Components::Transfer::BytesSent>().total += data.size();
|
||||||
|
_rmm.throwEventUpdate(transfer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxTransferManager::sendFilePath(const Contact3 c, std::string_view file_name, std::string_view file_path) {
|
||||||
|
if (
|
||||||
|
// TODO: add support of offline queuing
|
||||||
|
!_cr.all_of<Contact::Components::ToxFriendEphemeral>(c)
|
||||||
|
) {
|
||||||
|
// TODO: add support for persistant friend filesends
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
toxSendFilePath(c, 0, file_name, file_path);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
65
solanaceae/tox_messages/tox_transfer_manager.hpp
Normal file
65
solanaceae/tox_messages/tox_transfer_manager.hpp
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <solanaceae/toxcore/tox_event_interface.hpp>
|
||||||
|
#include <solanaceae/message3/registry_message_model.hpp>
|
||||||
|
#include <solanaceae/tox_contacts/tox_contact_model2.hpp>
|
||||||
|
|
||||||
|
#include <solanaceae/message3/file.hpp>
|
||||||
|
|
||||||
|
#include <entt/container/dense_map.hpp>
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// fwd
|
||||||
|
struct ToxI;
|
||||||
|
|
||||||
|
class ToxTransferManager : public RegistryMessageModelEventI, public ToxEventI {
|
||||||
|
protected:
|
||||||
|
RegistryMessageModel& _rmm;
|
||||||
|
Contact3Registry& _cr;
|
||||||
|
ToxContactModel2& _tcm;
|
||||||
|
ToxI& _t;
|
||||||
|
|
||||||
|
entt::dense_map<uint64_t, Message3Handle> _friend_sending_lookup;
|
||||||
|
entt::dense_map<uint64_t, Message3Handle> _friend_receiving_lookup;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void toxFriendLookupAdd(Message3Handle h);
|
||||||
|
void toxFriendLookupRemove(Message3Handle h);
|
||||||
|
|
||||||
|
Message3Handle toxFriendLookupSending(const uint32_t friend_number, const uint32_t file_number) const;
|
||||||
|
Message3Handle toxFriendLookupReceiving(const uint32_t friend_number, const uint32_t file_number) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ToxTransferManager(RegistryMessageModel& rmm, Contact3Registry& cr, ToxContactModel2& tcm, ToxI& t, ToxEventProviderI& tep);
|
||||||
|
virtual ~ToxTransferManager(void);
|
||||||
|
|
||||||
|
virtual void iterate(void);
|
||||||
|
|
||||||
|
public: // TODO: private?
|
||||||
|
Message3Handle toxSendFilePath(const Contact3 c, uint32_t file_kind, std::string_view file_name, std::string_view file_path);
|
||||||
|
|
||||||
|
bool resume(Message3Handle transfer);
|
||||||
|
bool pause(Message3Handle transfer);
|
||||||
|
bool setFileI(Message3Handle transfer, std::unique_ptr<FileI>&& new_file); // note, does not emplace FileInfoLocal
|
||||||
|
bool setFilePathDir(Message3Handle transfer, std::string_view file_path);
|
||||||
|
|
||||||
|
// calls setFileI() and resume()
|
||||||
|
bool accept(Message3Handle transfer, std::string_view file_path);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool onEvent(const Message::Events::MessageConstruct&) override;
|
||||||
|
bool onEvent(const Message::Events::MessageUpdated&) override;
|
||||||
|
bool onEvent(const Message::Events::MessageDestory&) override;
|
||||||
|
|
||||||
|
protected: // events
|
||||||
|
virtual bool onToxEvent(const Tox_Event_Friend_Connection_Status* e) override;
|
||||||
|
virtual bool onToxEvent(const Tox_Event_File_Recv* e) override;
|
||||||
|
virtual bool onToxEvent(const Tox_Event_File_Recv_Control* e) override;
|
||||||
|
virtual bool onToxEvent(const Tox_Event_File_Recv_Chunk* e) override;
|
||||||
|
virtual bool onToxEvent(const Tox_Event_File_Chunk_Request* e) override;
|
||||||
|
|
||||||
|
bool sendFilePath(const Contact3 c, std::string_view file_name, std::string_view file_path) override;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user