diff --git a/ngc_hs1.cpp b/ngc_hs1.cpp index 61c1abd..8f7a4ff 100644 --- a/ngc_hs1.cpp +++ b/ngc_hs1.cpp @@ -1,16 +1,38 @@ #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]) { + 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; } } @@ -21,9 +43,22 @@ struct _GroupID { 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]) { + 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; } } @@ -31,24 +66,60 @@ struct _PeerID { } }; +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 { - void* temp; - 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; + Tox_Message_Type type{}; + std::string text{}; }; - struct Messages { + 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); @@ -60,7 +131,11 @@ struct NGC_HS1 { } }; - std::map<_GroupID, std::map<_PeerID, Messages>> history; + struct Group { + std::map<_PeerID, Peer> peers; + }; + + std::map<_GroupID, Group> history; }; NGC_HS1* NGC_HS1_new(const struct NGC_HS1_options* options) { @@ -75,7 +150,107 @@ 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( @@ -112,7 +287,7 @@ void NGC_HS1_record_own_message( Tox_Message_Type type, const uint8_t *message, size_t length, uint32_t message_id ) { - printf("record_own_message %u\n", message_id); + fprintf(stderr, "record_own_message %08X\n", message_id); // get group id _GroupID g_id{}; { // TODO: error @@ -125,6 +300,107 @@ void NGC_HS1_record_own_message( tox_group_self_get_public_key(tox, group_number, p_id.data.data(), nullptr); } - ngc_hs1_ctx->history[g_id]; + 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 diff --git a/ngc_hs1.h b/ngc_hs1.h index 29180eb..e17b233 100644 --- a/ngc_hs1.h +++ b/ngc_hs1.h @@ -41,7 +41,7 @@ struct NGC_HS1_options { // 4 no one (above founder) uint8_t default_trust_level /*= 2*/; - //bool test; + bool record_others; }; // ========== init / kill ========== @@ -52,6 +52,10 @@ void NGC_HS1_kill(NGC_HS1* ngc_hs1_ctx); // ========== iterate ========== void NGC_HS1_iterate(Tox *tox, NGC_HS1* ngc_hs1_ctx/*, void *user_data*/); +// ========== peer online/offline ========== + +void NGC_HS1_peer_online(Tox* tox, NGC_HS1* ngc_hs1_ctx, uint32_t group_number, uint32_t peer_number, bool online); + // ========== send ========== // shim (same interface) @@ -81,13 +85,42 @@ void NGC_HS1_record_own_message( // ========== receive message ========== +// !!! no shim, whould require global state !!! // shim (same interface) //typedef void tox_group_message_cb(Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Message_Type type, //const uint8_t *message, size_t length, uint32_t message_id, void *user_data); +//void NGC_HS1_callback_group_message(Tox *tox, NGC_HS1* ngc_hs1_ctx, tox_group_message_cb *callback); +// 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 +); + +// ========== receive custom ========== + +// "callback" +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 +); // ========== receive request ========== +//void NGC_HS1_custom_packet( + // ========== receive answer ========== #ifdef __cplusplus