#include "./ngc_hs1.h" #include #include #include #include #include #include #include struct _GroupID { std::array data; _GroupID(void) = default; _GroupID(const _GroupID& other) : data(other.data) { fprintf(stderr, "gcopy\n"); assert(data.data() != other.data.data()); assert(data[0] == other.data[0]); assert(data[1] == other.data[1]); } _GroupID(_GroupID&&) = delete; //_GroupID(_GroupID&& other) : data(std::move(other.data)) {} bool operator<(const _GroupID& rhs) const { for (size_t i = 0; i < data.size(); i++) { if (data[i] >= rhs.data[i]) { return false; } } return true; } bool operator==(const _GroupID& rhs) const { for (size_t i = 0; i < data.size(); i++) { if (data[i] != rhs.data[i]) { return false; } } return true; } }; struct _PeerID { std::array data; _PeerID(void) = default; _PeerID(const _PeerID& other) : data(other.data) {} _PeerID(_PeerID&&) = delete; bool operator<(const _PeerID& rhs) const { for (size_t i = 0; i < data.size(); i++) { if (data[i] >= rhs.data[i]) { return false; } } return true; } bool operator==(const _PeerID& rhs) const { for (size_t i = 0; i < data.size(); i++) { if (data[i] != rhs.data[i]) { return false; } } return true; } }; enum _PacketType : uint8_t { INVALID = 0u, // request last (few) message_ids for a peer // - peer_key bytes (peer key we want to know ids for) // - 1 byte (uint8_t count ids, atleast 1) HS_REQUEST_LAST_IDS, // respond to a request with 0 or more message ids, sorted by newest first // - peer_key bytes (the msg_ids are from) // - 1 byte (uint8_t count ids, can be 0) // - array [ // - msg_id bytes (the message id // - ] HS_RESPONSE_LAST_IDS, //TODO: make it possible to go further back }; const char* _pkgid2str(_PacketType type) { #define _HS1_CASE(x) case (x): return #x; switch (type) { _HS1_CASE(INVALID) _HS1_CASE(HS_REQUEST_LAST_IDS) _HS1_CASE(HS_RESPONSE_LAST_IDS) default: return ""; } #undef _HS1_CASE } struct NGC_HS1 { NGC_HS1_options options; // callbacks //tox_group_message_cb* client_tox_msg_callback; // key - key - key - value store // group pubkey - peer pubkey - msg_id - message(type + text) struct Message { uint32_t msg_id{}; Tox_Message_Type type{}; std::string text{}; }; struct Peer { std::optional id; std::map dict; std::list order; // ordered list of message ids // dont start immediatly float time_since_last_request_sent {0.f}; void append(uint32_t msg_id, Tox_Message_Type type, const std::string& text) { order.push_back(msg_id); // overwrites auto& new_msg = dict[msg_id]; new_msg.msg_id = msg_id; new_msg.type = type; new_msg.text = text; } }; struct Group { std::map<_PeerID, Peer> peers; }; std::map<_GroupID, Group> history; }; NGC_HS1* NGC_HS1_new(const struct NGC_HS1_options* options) { NGC_HS1* context = new NGC_HS1; context->options = *options; return context; } void NGC_HS1_kill(NGC_HS1* ngc_hs1_ctx) { delete ngc_hs1_ctx; } static void _iterate_group(Tox *tox, NGC_HS1* ngc_hs1_ctx, uint32_t group_number, float time_delta) { //fprintf(stderr, "g:%u\n", g_i); _GroupID g_id{}; { // TODO: error tox_group_get_chat_id(tox, group_number, g_id.data.data(), nullptr); } if (ngc_hs1_ctx->history.count(g_id) == 0) { fprintf(stderr, "adding new group: %u %X%X%X%X\n", group_number, g_id.data.data()[0], g_id.data.data()[1], g_id.data.data()[2], g_id.data.data()[3] ); ngc_hs1_ctx->history[g_id]; } else { auto& group = ngc_hs1_ctx->history[g_id]; // for each peer for (auto& [key, peer] : group.peers) { //fprintf(stderr, " p: %X%X%X%X\n", key.data.data()[0], key.data.data()[1], key.data.data()[2], key.data.data()[3]); peer.time_since_last_request_sent += time_delta; if (peer.time_since_last_request_sent > 5.f) { peer.time_since_last_request_sent = 0.f; fprintf(stderr, "requesting ids for %X%X%X%X\n", key.data.data()[0], key.data.data()[1], key.data.data()[2], key.data.data()[3]); // TODO: other way around? // ask everyone if they have newer stuff for this peer // - 1 byte packet id // - peer_key bytes (peer key we want to know ids for) // - 1 byte (uint8_t count ids, atleast 1) std::array pkg; pkg[0] = HS_REQUEST_LAST_IDS; std::copy(key.data.begin(), key.data.end(), pkg.begin()+1); pkg[1+TOX_GROUP_PEER_PUBLIC_KEY_SIZE] = 5; // request last (up to) 5 msg_ids tox_group_send_custom_packet(tox, group_number, true, pkg.data(), pkg.size(), nullptr); } } } assert(ngc_hs1_ctx->history.size() != 0); assert(ngc_hs1_ctx->history.count(g_id)); } void NGC_HS1_iterate(Tox *tox, NGC_HS1* ngc_hs1_ctx/*, void *user_data*/) { assert(ngc_hs1_ctx); //fprintf(stderr, "groups: %u\n", ngc_hs1_ctx->history.size()); uint32_t group_count = tox_group_get_number_groups(tox); // this can loop endless if toxcore misbehaves for (uint32_t g_i = 0, g_c_done = 0; g_c_done < group_count; g_i++) { Tox_Err_Group_Is_Connected g_err; if (tox_group_is_connected(tox, g_i, &g_err)) { // valid and connected here // TODO: delta time, or other timers _iterate_group(tox, ngc_hs1_ctx, g_i, 0.2f); g_c_done++; } else if (g_err != TOX_ERR_GROUP_IS_CONNECTED_GROUP_NOT_FOUND) { g_c_done++; } // else do nothing // safety if (g_i > group_count + 1000) { fprintf(stderr, "WAY PAST GOUPS in iterate\n"); break; } } } void NGC_HS1_peer_online(Tox* tox, NGC_HS1* ngc_hs1_ctx, uint32_t group_number, uint32_t peer_number, bool online) { // get group id _GroupID g_id{}; { // TODO: error tox_group_get_chat_id(tox, group_number, g_id.data.data(), nullptr); } auto& group = ngc_hs1_ctx->history[g_id]; if (online) { // get peer id _PeerID p_id{}; { // TODO: error tox_group_peer_get_public_key(tox, group_number, peer_number, p_id.data.data(), nullptr); } auto& peer = group.peers[p_id]; peer.id = peer_number; } else { // offline // search for (auto& [key, peer] : group.peers) { if (peer.id.has_value() && peer.id.value() == peer_number) { peer.id = {}; // reset break; } } } } bool NGC_HS1_shim_group_send_message( const Tox *tox, NGC_HS1* ngc_hs1_ctx, uint32_t group_number, Tox_Message_Type type, const uint8_t *message, size_t length, uint32_t *message_id, Tox_Err_Group_Send_Message *error ) { uint32_t* msg_id_ptr = message_id; uint32_t msg_id_placeholder = 0; if (msg_id_ptr == nullptr) { msg_id_ptr = &msg_id_placeholder; } bool ret = tox_group_send_message(tox, group_number, type, message, length, msg_id_ptr, error); NGC_HS1_record_own_message(tox, ngc_hs1_ctx, group_number, type, message, length, *msg_id_ptr); return ret; } // record own msg void NGC_HS1_record_own_message( const Tox *tox, NGC_HS1* ngc_hs1_ctx, uint32_t group_number, Tox_Message_Type type, const uint8_t *message, size_t length, uint32_t message_id ) { fprintf(stderr, "record_own_message %08X\n", message_id); // get group id _GroupID g_id{}; { // TODO: error tox_group_get_chat_id(tox, group_number, g_id.data.data(), nullptr); } // get peer id _PeerID p_id{}; { // TODO: error tox_group_self_get_public_key(tox, group_number, p_id.data.data(), nullptr); } ngc_hs1_ctx->history[g_id].peers[p_id].append(message_id, type, std::string{message, message+length}); assert(ngc_hs1_ctx->history.size() != 0); assert(ngc_hs1_ctx->history.count(g_id)); } // record others msg void NGC_HS1_record_message( const Tox *tox, NGC_HS1* ngc_hs1_ctx, uint32_t group_number, uint32_t peer_number, Tox_Message_Type type, const uint8_t *message, size_t length, uint32_t message_id ) { if (!ngc_hs1_ctx->options.record_others) { return; } fprintf(stderr, "record_message %08X\n", message_id); // get group id _GroupID g_id{}; { // TODO: error tox_group_get_chat_id(tox, group_number, g_id.data.data(), nullptr); } // get peer id _PeerID p_id{}; { // TODO: error tox_group_peer_get_public_key(tox, group_number, peer_number, p_id.data.data(), nullptr); } ngc_hs1_ctx->history[g_id].peers[p_id].append(message_id, type, std::string{message, message+length}); } static void _handle_HS_REQUEST_LAST_IDS( Tox* tox, NGC_HS1* ngc_hs1_ctx, uint32_t group_number, uint32_t peer_number, const uint8_t *data, size_t length ); #define _HS1_HAVE(x, error) if ((length - curser) < (x)) { error; } void NGC_HS1_handle_group_custom_packet( Tox* tox, NGC_HS1* ngc_hs1_ctx, uint32_t group_number, uint32_t peer_number, const uint8_t *data, size_t length //void *user_data ) { size_t curser = 0; _HS1_HAVE(1, return) _PacketType pkg_type = static_cast<_PacketType>(*(data + curser)); curser++; fprintf(stderr, "custom_packet [%s] %u\n", _pkgid2str(pkg_type), length); switch (pkg_type) { case INVALID: break; case HS_REQUEST_LAST_IDS: _handle_HS_REQUEST_LAST_IDS(tox, ngc_hs1_ctx, group_number, peer_number, data+curser, length-curser); break; case HS_RESPONSE_LAST_IDS: break; } } static void _handle_HS_REQUEST_LAST_IDS( Tox* tox, NGC_HS1* ngc_hs1_ctx, uint32_t group_number, uint32_t peer_number, const uint8_t *data, size_t length ) { size_t curser = 0; _PeerID p_key; _HS1_HAVE(p_key.data.size(), fprintf(stderr, "packet too small, missing pkey\n"); return) std::copy(data+curser, data+curser+p_key.data.size(), p_key.data.begin()); curser += p_key.data.size(); _HS1_HAVE(1, fprintf(stderr, "packet too small, missing count\n"); return) uint8_t last_msg_id_count = data[curser++]; fprintf(stderr, "got request for last %u ids\n", last_msg_id_count); } #undef _HS1_HAVE