diff options
author | George Hazan <ghazan@miranda.im> | 2020-03-26 20:45:32 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2020-03-26 20:45:32 +0300 |
commit | 57bf1c3a69d5b52f0f61172ddff50f24dbe8351e (patch) | |
tree | 9702e1a3e848bffb1ff4d71bbf9096b00d9c3596 /protocols/Tox/libtox/src/toxcore/group.c | |
parent | c89b7af83f86699b0f7b2564d8c301c9f60aaf4e (diff) |
fixes #2286 (Update libtox to 0.2.11)
Diffstat (limited to 'protocols/Tox/libtox/src/toxcore/group.c')
-rw-r--r-- | protocols/Tox/libtox/src/toxcore/group.c | 1181 |
1 files changed, 625 insertions, 556 deletions
diff --git a/protocols/Tox/libtox/src/toxcore/group.c b/protocols/Tox/libtox/src/toxcore/group.c index f6f8c70431..3e04f181c8 100644 --- a/protocols/Tox/libtox/src/toxcore/group.c +++ b/protocols/Tox/libtox/src/toxcore/group.c @@ -1,25 +1,10 @@ -/* - * Slightly better groupchats implementation. +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2016-2018 The TokTok team. + * Copyright © 2014 Tox project. */ /* - * Copyright © 2016-2018 The TokTok team. - * Copyright © 2014 Tox project. - * - * This file is part of Tox, the free peer to peer instant messenger. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see <http://www.gnu.org/licenses/>. + * Slightly better groupchats implementation. */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -54,11 +39,13 @@ typedef enum Group_Message_Id { typedef enum Invite_Id { INVITE_ID = 0, - INVITE_RESPONSE_ID = 1, + INVITE_ACCEPT_ID = 1, + INVITE_MEMBER_ID = 2, } Invite_Id; #define INVITE_PACKET_SIZE (1 + sizeof(uint16_t) + 1 + GROUP_ID_LENGTH) -#define INVITE_RESPONSE_PACKET_SIZE (1 + sizeof(uint16_t) * 2 + 1 + GROUP_ID_LENGTH) +#define INVITE_ACCEPT_PACKET_SIZE (1 + sizeof(uint16_t) * 2 + 1 + GROUP_ID_LENGTH) +#define INVITE_MEMBER_PACKET_SIZE (1 + sizeof(uint16_t) * 2 + 1 + GROUP_ID_LENGTH + sizeof(uint16_t)) #define ONLINE_PACKET_DATA_SIZE (sizeof(uint16_t) + 1 + GROUP_ID_LENGTH) @@ -76,19 +63,9 @@ typedef enum Peer_Id { */ static bool is_groupnumber_valid(const Group_Chats *g_c, uint32_t groupnumber) { - if (groupnumber >= g_c->num_chats) { - return false; - } - - if (g_c->chats == nullptr) { - return false; - } - - if (g_c->chats[groupnumber].status == GROUPCHAT_STATUS_NONE) { - return false; - } - - return true; + return groupnumber < g_c->num_chats + && g_c->chats != nullptr + && g_c->chats[groupnumber].status != GROUPCHAT_STATUS_NONE; } @@ -135,27 +112,25 @@ static int32_t create_group_chat(Group_Chats *g_c) } } - int32_t id = -1; - if (realloc_conferences(g_c, g_c->num_chats + 1)) { - id = g_c->num_chats; + uint16_t id = g_c->num_chats; ++g_c->num_chats; setup_conference(&g_c->chats[id]); + return id; } - return id; + return -1; } /* Wipe a groupchat. * - * return -1 on failure. - * return 0 on success. + * return true on success. */ -static int wipe_group_chat(Group_Chats *g_c, uint32_t groupnumber) +static bool wipe_group_chat(Group_Chats *g_c, uint32_t groupnumber) { if (!is_groupnumber_valid(g_c, groupnumber)) { - return -1; + return false; } uint16_t i; @@ -172,7 +147,7 @@ static int wipe_group_chat(Group_Chats *g_c, uint32_t groupnumber) realloc_conferences(g_c, g_c->num_chats); } - return 0; + return true; } static Group_c *get_group_c(const Group_Chats *g_c, uint32_t groupnumber) @@ -187,16 +162,16 @@ static Group_c *get_group_c(const Group_Chats *g_c, uint32_t groupnumber) /* * check if peer with real_pk is in peer array. * - * return peer index if peer is in chat. - * return -1 if peer is not in chat. + * return peer index if peer is in group. + * return -1 if peer is not in group. * * TODO(irungentoo): make this more efficient. */ -static int peer_in_chat(const Group_c *chat, const uint8_t *real_pk) +static int peer_in_group(const Group_c *g, const uint8_t *real_pk) { - for (uint32_t i = 0; i < chat->numpeers; ++i) { - if (id_equal(chat->group[i].real_pk, real_pk)) { + for (uint32_t i = 0; i < g->numpeers; ++i) { + if (id_equal(g->group[i].real_pk, real_pk)) { return i; } } @@ -204,10 +179,10 @@ static int peer_in_chat(const Group_c *chat, const uint8_t *real_pk) return -1; } -static int frozen_in_chat(const Group_c *chat, const uint8_t *real_pk) +static int frozen_in_group(const Group_c *g, const uint8_t *real_pk) { - for (uint32_t i = 0; i < chat->numfrozen; ++i) { - if (id_equal(chat->frozen[i].real_pk, real_pk)) { + for (uint32_t i = 0; i < g->numfrozen; ++i) { + if (id_equal(g->frozen[i].real_pk, real_pk)) { return i; } } @@ -277,47 +252,39 @@ static uint64_t calculate_comp_value(const uint8_t *pk1, const uint8_t *pk2) return cmp1 - cmp2; } -typedef enum Groupchat_Closest { - GROUPCHAT_CLOSEST_NONE, - GROUPCHAT_CLOSEST_ADDED, - GROUPCHAT_CLOSEST_REMOVED -} Groupchat_Closest; +typedef enum Groupchat_Closest_Change { + GROUPCHAT_CLOSEST_CHANGE_NONE, + GROUPCHAT_CLOSEST_CHANGE_ADDED, + GROUPCHAT_CLOSEST_CHANGE_REMOVED, +} Groupchat_Closest_Change; -static int add_to_closest(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk, const uint8_t *temp_pk) +static bool add_to_closest(Group_c *g, const uint8_t *real_pk, const uint8_t *temp_pk) { - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - if (public_key_cmp(g->real_pk, real_pk) == 0) { - return -1; + return false; } - unsigned int i; - unsigned int index = DESIRED_CLOSE_CONNECTIONS; + unsigned int index = DESIRED_CLOSEST; - for (i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) { + for (unsigned int i = 0; i < DESIRED_CLOSEST; ++i) { if (g->closest_peers[i].entry && public_key_cmp(real_pk, g->closest_peers[i].real_pk) == 0) { - return 0; + return true; } } - for (i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) { + for (unsigned int i = 0; i < DESIRED_CLOSEST; ++i) { if (g->closest_peers[i].entry == 0) { index = i; break; } } - if (index == DESIRED_CLOSE_CONNECTIONS) { + if (index == DESIRED_CLOSEST) { uint64_t comp_val = calculate_comp_value(g->real_pk, real_pk); uint64_t comp_d = 0; - for (i = 0; i < (DESIRED_CLOSE_CONNECTIONS / 2); ++i) { - uint64_t comp; - comp = calculate_comp_value(g->real_pk, g->closest_peers[i].real_pk); + for (unsigned int i = 0; i < (DESIRED_CLOSEST / 2); ++i) { + uint64_t comp = calculate_comp_value(g->real_pk, g->closest_peers[i].real_pk); if (comp > comp_val && comp > comp_d) { index = i; @@ -327,7 +294,7 @@ static int add_to_closest(Group_Chats *g_c, uint32_t groupnumber, const uint8_t comp_val = calculate_comp_value(real_pk, g->real_pk); - for (i = (DESIRED_CLOSE_CONNECTIONS / 2); i < DESIRED_CLOSE_CONNECTIONS; ++i) { + for (unsigned int i = (DESIRED_CLOSEST / 2); i < DESIRED_CLOSEST; ++i) { uint64_t comp = calculate_comp_value(g->closest_peers[i].real_pk, g->real_pk); if (comp > comp_val && comp > comp_d) { @@ -337,8 +304,8 @@ static int add_to_closest(Group_Chats *g_c, uint32_t groupnumber, const uint8_t } } - if (index == DESIRED_CLOSE_CONNECTIONS) { - return -1; + if (index == DESIRED_CLOSEST) { + return false; } uint8_t old_real_pk[CRYPTO_PUBLIC_KEY_SIZE]; @@ -356,89 +323,85 @@ static int add_to_closest(Group_Chats *g_c, uint32_t groupnumber, const uint8_t memcpy(g->closest_peers[index].temp_pk, temp_pk, CRYPTO_PUBLIC_KEY_SIZE); if (old) { - add_to_closest(g_c, groupnumber, old_real_pk, old_temp_pk); + add_to_closest(g, old_real_pk, old_temp_pk); } if (!g->changed) { - g->changed = GROUPCHAT_CLOSEST_ADDED; + g->changed = GROUPCHAT_CLOSEST_CHANGE_ADDED; } - return 0; + return true; } -static unsigned int pk_in_closest_peers(const Group_c *g, uint8_t *real_pk) +static bool pk_in_closest_peers(const Group_c *g, uint8_t *real_pk) { - unsigned int i; - - for (i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) { + for (unsigned int i = 0; i < DESIRED_CLOSEST; ++i) { if (!g->closest_peers[i].entry) { continue; } if (public_key_cmp(g->closest_peers[i].real_pk, real_pk) == 0) { - return 1; + return true; } } - return 0; + return false; } -static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, uint32_t groupnumber, uint8_t reason, - uint8_t lock); - -static void remove_conn_reason(Group_Chats *g_c, uint32_t groupnumber, uint16_t i, uint8_t reason); - -static int send_packet_online(Friend_Connections *fr_c, int friendcon_id, uint16_t group_num, uint8_t type, - const uint8_t *id); +static void remove_connection_reason(Group_Chats *g_c, Group_c *g, uint16_t i, uint8_t reason); -static int connect_to_closest(Group_Chats *g_c, uint32_t groupnumber, void *userdata) +static void purge_closest(Group_Chats *g_c, uint32_t groupnumber) { Group_c *g = get_group_c(g_c, groupnumber); if (!g) { - return -1; - } - - if (!g->changed) { - return 0; - } - - if (g->changed == GROUPCHAT_CLOSEST_REMOVED) { - for (uint32_t i = 0; i < g->numpeers; ++i) { - add_to_closest(g_c, groupnumber, g->group[i].real_pk, g->group[i].temp_pk); - } + return; } for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type == GROUPCHAT_CLOSE_NONE) { + if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) { continue; } - if (!(g->close[i].reasons & GROUPCHAT_CLOSE_REASON_CLOSEST)) { + if (!(g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST)) { continue; } uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t dht_temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; - get_friendcon_public_keys(real_pk, dht_temp_pk, g_c->fr_c, g->close[i].number); + get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->connections[i].number); if (!pk_in_closest_peers(g, real_pk)) { - remove_conn_reason(g_c, groupnumber, i, GROUPCHAT_CLOSE_REASON_CLOSEST); + remove_connection_reason(g_c, g, i, GROUPCHAT_CONNECTION_REASON_CLOSEST); } } +} + +static int send_packet_online(Friend_Connections *fr_c, int friendcon_id, uint16_t group_num, uint8_t type, + const uint8_t *id); + +static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, Group_c *g, uint8_t reason, + uint8_t lock); + +static void add_closest_connections(Group_Chats *g_c, uint32_t groupnumber, void *userdata) +{ + Group_c *g = get_group_c(g_c, groupnumber); + + if (!g) { + return; + } - for (uint32_t i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) { + for (uint32_t i = 0; i < DESIRED_CLOSEST; ++i) { if (!g->closest_peers[i].entry) { continue; } int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, g->closest_peers[i].real_pk); - uint8_t lock = 1; + uint8_t fresh = 0; if (friendcon_id == -1) { friendcon_id = new_friend_connection(g_c->fr_c, g->closest_peers[i].real_pk); - lock = 0; + fresh = 1; if (friendcon_id == -1) { continue; @@ -447,16 +410,49 @@ static int connect_to_closest(Group_Chats *g_c, uint32_t groupnumber, void *user set_dht_temp_pk(g_c->fr_c, friendcon_id, g->closest_peers[i].temp_pk, userdata); } - add_conn_to_groupchat(g_c, friendcon_id, groupnumber, GROUPCHAT_CLOSE_REASON_CLOSEST, lock); + const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g, + GROUPCHAT_CONNECTION_REASON_CLOSEST, !fresh); - if (friend_con_connected(g_c->fr_c, friendcon_id) == FRIENDCONN_STATUS_CONNECTED) { + if (connection_index == -1) { + if (fresh) { + kill_friend_connection(g_c->fr_c, friendcon_id); + } + + continue; + } + + if (friend_con_connected(g_c->fr_c, friendcon_id) == FRIENDCONN_STATUS_CONNECTED + && g->connections[connection_index].type == GROUPCHAT_CONNECTION_CONNECTING) { send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->type, g->id); } } +} - g->changed = GROUPCHAT_CLOSEST_NONE; +static bool connect_to_closest(Group_Chats *g_c, uint32_t groupnumber, void *userdata) +{ + Group_c *g = get_group_c(g_c, groupnumber); - return 0; + if (!g) { + return false; + } + + if (!g->changed) { + return true; + } + + if (g->changed == GROUPCHAT_CLOSEST_CHANGE_REMOVED) { + for (uint32_t i = 0; i < g->numpeers; ++i) { + add_to_closest(g, g->group[i].real_pk, g->group[i].temp_pk); + } + } + + purge_closest(g_c, groupnumber); + + add_closest_connections(g_c, groupnumber, userdata); + + g->changed = GROUPCHAT_CLOSEST_CHANGE_NONE; + + return true; } static int get_frozen_index(const Group_c *g, uint16_t peer_number) @@ -532,12 +528,14 @@ static int note_peer_active(Group_Chats *g_c, uint32_t groupnumber, uint16_t pee return -1; } + const uint32_t thawed_index = g->numpeers; + g->group = temp; - g->group[g->numpeers] = g->frozen[frozen_index]; - g->group[g->numpeers].temp_pk_updated = false; - g->group[g->numpeers].last_active = mono_time_get(g_c->mono_time); + g->group[thawed_index] = g->frozen[frozen_index]; + g->group[thawed_index].temp_pk_updated = false; + g->group[thawed_index].last_active = mono_time_get(g_c->mono_time); - add_to_closest(g_c, groupnumber, g->group[g->numpeers].real_pk, g->group[g->numpeers].temp_pk); + add_to_closest(g, g->group[thawed_index].real_pk, g->group[thawed_index].temp_pk); ++g->numpeers; @@ -548,15 +546,15 @@ static int note_peer_active(Group_Chats *g_c, uint32_t groupnumber, uint16_t pee } if (g->peer_on_join) { - g->peer_on_join(g->object, groupnumber, g->numpeers - 1); + g->peer_on_join(g->object, groupnumber, thawed_index); } g->need_send_name = true; - return g->numpeers - 1; + return thawed_index; } -static int delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata, bool keep_connection); +static bool delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata); static void delete_any_peer_with_pk(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk, void *userdata) { @@ -566,16 +564,16 @@ static void delete_any_peer_with_pk(Group_Chats *g_c, uint32_t groupnumber, cons return; } - const int prev_peer_index = peer_in_chat(g, real_pk); + const int peer_index = peer_in_group(g, real_pk); - if (prev_peer_index >= 0) { - delpeer(g_c, groupnumber, prev_peer_index, userdata, false); + if (peer_index >= 0) { + delpeer(g_c, groupnumber, peer_index, userdata); } - const int prev_frozen_index = frozen_in_chat(g, real_pk); + const int frozen_index = frozen_in_group(g, real_pk); - if (prev_frozen_index >= 0) { - delete_frozen(g, prev_frozen_index); + if (frozen_index >= 0) { + delete_frozen(g, frozen_index); } } @@ -642,59 +640,46 @@ static int addpeer(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_p memset(&temp[g->numpeers], 0, sizeof(Group_Peer)); g->group = temp; - id_copy(g->group[g->numpeers].real_pk, real_pk); - id_copy(g->group[g->numpeers].temp_pk, temp_pk); - g->group[g->numpeers].temp_pk_updated = true; - g->group[g->numpeers].peer_number = peer_number; - g->group[g->numpeers].last_active = mono_time_get(g_c->mono_time); - g->group[g->numpeers].is_friend = (getfriend_id(g_c->m, real_pk) != -1); + const uint32_t new_index = g->numpeers; + + id_copy(g->group[new_index].real_pk, real_pk); + id_copy(g->group[new_index].temp_pk, temp_pk); + g->group[new_index].temp_pk_updated = true; + g->group[new_index].peer_number = peer_number; + g->group[new_index].last_active = mono_time_get(g_c->mono_time); + g->group[new_index].is_friend = (getfriend_id(g_c->m, real_pk) != -1); ++g->numpeers; - add_to_closest(g_c, groupnumber, real_pk, temp_pk); + add_to_closest(g, real_pk, temp_pk); if (do_gc_callback && g_c->peer_list_changed_callback) { g_c->peer_list_changed_callback(g_c->m, groupnumber, userdata); } if (g->peer_on_join) { - g->peer_on_join(g->object, groupnumber, g->numpeers - 1); + g->peer_on_join(g->object, groupnumber, new_index); } - return g->numpeers - 1; + return new_index; } -static int remove_close_conn(Group_Chats *g_c, uint32_t groupnumber, int friendcon_id) +static void remove_connection(Group_Chats *g_c, Group_c *g, uint16_t i) { - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - uint32_t i; - - for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type == GROUPCHAT_CLOSE_NONE) { - continue; - } - - if (g->close[i].number == (unsigned int)friendcon_id) { - g->close[i].type = GROUPCHAT_CLOSE_NONE; - kill_friend_connection(g_c->fr_c, friendcon_id); - return 0; - } + if (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER) { + --g->num_introducer_connections; } - return -1; + kill_friend_connection(g_c->fr_c, g->connections[i].number); + g->connections[i].type = GROUPCHAT_CONNECTION_NONE; } - static void remove_from_closest(Group_c *g, int peer_index) { - for (uint32_t i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) { - if (g->closest_peers[i].entry && id_equal(g->closest_peers[i].real_pk, g->group[peer_index].real_pk)) { + for (uint32_t i = 0; i < DESIRED_CLOSEST; ++i) { + if (g->closest_peers[i].entry + && id_equal(g->closest_peers[i].real_pk, g->group[peer_index].real_pk)) { g->closest_peers[i].entry = 0; - g->changed = GROUPCHAT_CLOSEST_REMOVED; + g->changed = GROUPCHAT_CLOSEST_CHANGE_REMOVED; break; } } @@ -703,23 +688,31 @@ static void remove_from_closest(Group_c *g, int peer_index) /* * Delete a peer from the group chat. * - * return 0 if success - * return -1 if error. + * return true on success */ -static int delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata, bool keep_connection) +static bool delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata) { Group_c *g = get_group_c(g_c, groupnumber); if (!g) { - return -1; + return false; } remove_from_closest(g, peer_index); const int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, g->group[peer_index].real_pk); - if (friendcon_id != -1 && !keep_connection) { - remove_close_conn(g_c, groupnumber, friendcon_id); + if (friendcon_id != -1) { + for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { + if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) { + continue; + } + + if (g->connections[i].number == (unsigned int)friendcon_id) { + remove_connection(g_c, g, i); + break; + } + } } --g->numpeers; @@ -737,7 +730,7 @@ static int delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void Group_Peer *temp = (Group_Peer *)realloc(g->group, sizeof(Group_Peer) * (g->numpeers)); if (temp == nullptr) { - return -1; + return false; } g->group = temp; @@ -751,7 +744,7 @@ static int delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void g->peer_on_leave(g->object, groupnumber, peer_object); } - return 0; + return true; } static int cmp_u64(uint64_t a, uint64_t b) @@ -804,37 +797,37 @@ static bool delete_old_frozen(Group_c *g) return true; } -static bool try_send_rejoin(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk); +static bool try_send_rejoin(Group_Chats *g_c, Group_c *g, const uint8_t *real_pk); -static int freeze_peer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata) +static bool freeze_peer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata) { Group_c *g = get_group_c(g_c, groupnumber); if (!g) { - return -1; + return false; } Group_Peer *temp = (Group_Peer *)realloc(g->frozen, sizeof(Group_Peer) * (g->numfrozen + 1)); if (temp == nullptr) { - return -1; + return false; } g->frozen = temp; g->frozen[g->numfrozen] = g->group[peer_index]; g->frozen[g->numfrozen].object = nullptr; - if (delpeer(g_c, groupnumber, peer_index, userdata, true) != 0) { - return -1; + if (!delpeer(g_c, groupnumber, peer_index, userdata)) { + return false; } - try_send_rejoin(g_c, groupnumber, g->frozen[g->numfrozen].real_pk); + try_send_rejoin(g_c, g, g->frozen[g->numfrozen].real_pk); ++g->numfrozen; delete_old_frozen(g); - return 0; + return true; } @@ -844,29 +837,27 @@ static int freeze_peer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, v * via the public API. This should be set to false if this function is called * from outside of the tox_iterate() loop. * - * return 0 on success. - * return -1 if error. + * return true on success. */ -static int setnick(Group_Chats *g_c, uint32_t groupnumber, int peer_index, const uint8_t *nick, uint16_t nick_len, - void *userdata, bool do_gc_callback) +static bool setnick(Group_Chats *g_c, uint32_t groupnumber, int peer_index, const uint8_t *nick, uint16_t nick_len, + void *userdata, bool do_gc_callback) { if (nick_len > MAX_NAME_LENGTH) { - return -1; + return false; } Group_c *g = get_group_c(g_c, groupnumber); if (!g) { - return -1; + return false; } g->group[peer_index].nick_updated = true; - /* same name as already stored? */ - if (g->group[peer_index].nick_len == nick_len) { - if (nick_len == 0 || !memcmp(g->group[peer_index].nick, nick, nick_len)) { - return 0; - } + if (g->group[peer_index].nick_len == nick_len + && (nick_len == 0 || !memcmp(g->group[peer_index].nick, nick, nick_len))) { + /* same name as already stored */ + return true; } if (nick_len) { @@ -879,25 +870,29 @@ static int setnick(Group_Chats *g_c, uint32_t groupnumber, int peer_index, const g_c->peer_name_callback(g_c->m, groupnumber, peer_index, nick, nick_len, userdata); } - return 0; + return true; } -static int settitle(Group_Chats *g_c, uint32_t groupnumber, int peer_index, const uint8_t *title, uint8_t title_len, - void *userdata) +/* Set the title for a group. + * + * return true on success. + */ +static bool settitle(Group_Chats *g_c, uint32_t groupnumber, int peer_index, const uint8_t *title, uint8_t title_len, + void *userdata) { if (title_len > MAX_NAME_LENGTH || title_len == 0) { - return -1; + return false; } Group_c *g = get_group_c(g_c, groupnumber); if (!g) { - return -1; + return false; } - /* same as already set? */ if (g->title_len == title_len && !memcmp(g->title, title, title_len)) { - return 0; + /* same title as already set */ + return true; } memcpy(g->title, title, title_len); @@ -909,7 +904,7 @@ static int settitle(Group_Chats *g_c, uint32_t groupnumber, int peer_index, cons g_c->title_callback(g_c->m, groupnumber, peer_index, title, title_len, userdata); } - return 0; + return true; } /* Check if the group has no online connection, and freeze all peers if so */ @@ -922,7 +917,7 @@ static void check_disconnected(Group_Chats *g_c, uint32_t groupnumber, void *use } for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type == GROUPCHAT_CLOSE_ONLINE) { + if (g->connections[i].type == GROUPCHAT_CONNECTION_ONLINE) { return; } } @@ -934,7 +929,8 @@ static void check_disconnected(Group_Chats *g_c, uint32_t groupnumber, void *use } } -static void set_conns_type_close(Group_Chats *g_c, uint32_t groupnumber, int friendcon_id, uint8_t type, void *userdata) +static void set_conns_type_connections(Group_Chats *g_c, uint32_t groupnumber, int friendcon_id, uint8_t type, + void *userdata) { Group_c *g = get_group_c(g_c, groupnumber); @@ -942,30 +938,29 @@ static void set_conns_type_close(Group_Chats *g_c, uint32_t groupnumber, int fri return; } - uint32_t i; - - for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type == GROUPCHAT_CLOSE_NONE) { + for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { + if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) { continue; } - if (g->close[i].number != (unsigned int)friendcon_id) { + if (g->connections[i].number != (unsigned int)friendcon_id) { continue; } - if (type == GROUPCHAT_CLOSE_ONLINE) { + if (type == GROUPCHAT_CONNECTION_ONLINE) { send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->type, g->id); } else { - g->close[i].type = type; + g->connections[i].type = type; check_disconnected(g_c, groupnumber, userdata); } } } -/* Set the type for all close connections with friendcon_id */ + +/* Set the type for all connections with friendcon_id */ static void set_conns_status_groups(Group_Chats *g_c, int friendcon_id, uint8_t type, void *userdata) { for (uint16_t i = 0; i < g_c->num_chats; ++i) { - set_conns_type_close(g_c, i, friendcon_id, type, userdata); + set_conns_type_connections(g_c, i, friendcon_id, type, userdata); } } @@ -983,7 +978,7 @@ static void rejoin_frozen_friend(Group_Chats *g_c, int friendcon_id) for (uint32_t j = 0; j < g->numfrozen; ++j) { if (id_equal(g->frozen[j].real_pk, real_pk)) { - try_send_rejoin(g_c, i, real_pk); + try_send_rejoin(g_c, g, real_pk); break; } } @@ -1006,9 +1001,9 @@ static int g_handle_status(void *object, int friendcon_id, uint8_t status, void Group_Chats *g_c = (Group_Chats *)object; if (status) { /* Went online */ - set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CLOSE_ONLINE, userdata); + set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CONNECTION_ONLINE, userdata); } else { /* Went offline */ - set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CLOSE_CONNECTION, userdata); + set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CONNECTION_CONNECTING, userdata); // TODO(irungentoo): remove timedout connections? } @@ -1020,55 +1015,49 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin /* Add friend to group chat. * - * return close index on success + * return connections index on success * return -1 on failure. */ -static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, uint32_t groupnumber, uint8_t reason, +static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, Group_c *g, uint8_t reason, uint8_t lock) { - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - uint16_t empty = MAX_GROUP_CONNECTIONS; uint16_t ind = MAX_GROUP_CONNECTIONS; for (uint16_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type == GROUPCHAT_CLOSE_NONE) { + if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) { empty = i; continue; } - if (g->close[i].number == (uint32_t)friendcon_id) { + if (g->connections[i].number == (uint32_t)friendcon_id) { ind = i; /* Already in list. */ break; } } - if (ind == MAX_GROUP_CONNECTIONS && empty != MAX_GROUP_CONNECTIONS) { + if (ind == MAX_GROUP_CONNECTIONS) { + if (empty == MAX_GROUP_CONNECTIONS) { + return -1; + } + if (lock) { friend_connection_lock(g_c->fr_c, friendcon_id); } - g->close[empty].type = GROUPCHAT_CLOSE_CONNECTION; - g->close[empty].number = friendcon_id; - g->close[empty].reasons = 0; + g->connections[empty].type = GROUPCHAT_CONNECTION_CONNECTING; + g->connections[empty].number = friendcon_id; + g->connections[empty].reasons = 0; // TODO(irungentoo): friend_connection_callbacks(g_c->m->fr_c, friendcon_id, GROUPCHAT_CALLBACK_INDEX, &g_handle_status, &g_handle_packet, &handle_lossy, g_c, friendcon_id); ind = empty; } - if (ind == MAX_GROUP_CONNECTIONS) { - return -1; - } - - if (!(g->close[ind].reasons & reason)) { - g->close[ind].reasons |= reason; + if (!(g->connections[ind].reasons & reason)) { + g->connections[ind].reasons |= reason; - if (reason == GROUPCHAT_CLOSE_REASON_INTRODUCER) { + if (reason == GROUPCHAT_CONNECTION_REASON_INTRODUCER) { ++g->num_introducer_connections; } } @@ -1082,37 +1071,31 @@ static unsigned int send_peer_introduced(Group_Chats *g_c, int friendcon_id, uin * * Kills connection if this was the last reason. */ -static void remove_conn_reason(Group_Chats *g_c, uint32_t groupnumber, uint16_t i, uint8_t reason) +static void remove_connection_reason(Group_Chats *g_c, Group_c *g, uint16_t i, uint8_t reason) { - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { + if (!(g->connections[i].reasons & reason)) { return; } - if (!(g->close[i].reasons & reason)) { - return; - } - - g->close[i].reasons &= ~reason; + g->connections[i].reasons &= ~reason; - if (reason == GROUPCHAT_CLOSE_REASON_INTRODUCER) { + if (reason == GROUPCHAT_CONNECTION_REASON_INTRODUCER) { --g->num_introducer_connections; - if (g->close[i].type == GROUPCHAT_CLOSE_ONLINE) { - send_peer_introduced(g_c, g->close[i].number, g->close[i].group_number); + if (g->connections[i].type == GROUPCHAT_CONNECTION_ONLINE) { + send_peer_introduced(g_c, g->connections[i].number, g->connections[i].group_number); } } - if (g->close[i].reasons == 0) { - kill_friend_connection(g_c->fr_c, g->close[i].number); - g->close[i].type = GROUPCHAT_CLOSE_NONE; + if (g->connections[i].reasons == 0) { + kill_friend_connection(g_c->fr_c, g->connections[i].number); + g->connections[i].type = GROUPCHAT_CONNECTION_NONE; } } /* Creates a new groupchat and puts it in the chats array. * - * type is one of GROUPCHAT_TYPE_* + * type is one of `GROUPCHAT_TYPE_*` * * return group number on success. * return -1 on failure. @@ -1163,12 +1146,12 @@ int del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently group_leave(g_c, groupnumber, leave_permanently); for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type == GROUPCHAT_CLOSE_NONE) { + if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) { continue; } - g->close[i].type = GROUPCHAT_CLOSE_NONE; - kill_friend_connection(g_c->fr_c, g->close[i].number); + g->connections[i].type = GROUPCHAT_CONNECTION_NONE; + kill_friend_connection(g_c->fr_c, g->connections[i].number); } for (uint32_t i = 0; i < g->numpeers; ++i) { @@ -1187,6 +1170,19 @@ int del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently return wipe_group_chat(g_c, groupnumber); } +static const Group_Peer *peer_in_list(const Group_c *g, uint32_t peernumber, bool frozen) +{ + const Group_Peer *list = frozen ? g->frozen : g->group; + const uint32_t num = frozen ? g->numfrozen : g->numpeers; + + if (peernumber >= num) { + return nullptr; + } + + return &list[peernumber]; +} + + /* Copy the public key of (frozen, if frozen is true) peernumber who is in * groupnumber to pk. pk must be CRYPTO_PUBLIC_KEY_SIZE long. * @@ -1194,7 +1190,7 @@ int del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently * return -1 if groupnumber is invalid. * return -2 if peernumber is invalid. */ -int group_peer_pubkey(const Group_Chats *g_c, uint32_t groupnumber, int peernumber, uint8_t *pk, bool frozen) +int group_peer_pubkey(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint8_t *pk, bool frozen) { const Group_c *g = get_group_c(g_c, groupnumber); @@ -1202,14 +1198,13 @@ int group_peer_pubkey(const Group_Chats *g_c, uint32_t groupnumber, int peernumb return -1; } - const Group_Peer *list = frozen ? g->frozen : g->group; - const uint32_t num = frozen ? g->numfrozen : g->numpeers; + const Group_Peer *peer = peer_in_list(g, peernumber, frozen); - if ((uint32_t)peernumber >= num) { + if (peer == nullptr) { return -2; } - memcpy(pk, list[peernumber].real_pk, CRYPTO_PUBLIC_KEY_SIZE); + memcpy(pk, peer->real_pk, CRYPTO_PUBLIC_KEY_SIZE); return 0; } @@ -1219,7 +1214,7 @@ int group_peer_pubkey(const Group_Chats *g_c, uint32_t groupnumber, int peernumb * return -1 if groupnumber is invalid. * return -2 if peernumber is invalid. */ -int group_peername_size(const Group_Chats *g_c, uint32_t groupnumber, int peernumber, bool frozen) +int group_peername_size(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, bool frozen) { const Group_c *g = get_group_c(g_c, groupnumber); @@ -1227,18 +1222,13 @@ int group_peername_size(const Group_Chats *g_c, uint32_t groupnumber, int peernu return -1; } - const Group_Peer *list = frozen ? g->frozen : g->group; - const uint32_t num = frozen ? g->numfrozen : g->numpeers; + const Group_Peer *peer = peer_in_list(g, peernumber, frozen); - if ((uint32_t)peernumber >= num) { + if (peer == nullptr) { return -2; } - if (list[peernumber].nick_len == 0) { - return 0; - } - - return list[peernumber].nick_len; + return peer->nick_len; } /* Copy the name of (frozen, if frozen is true) peernumber who is in @@ -1248,7 +1238,7 @@ int group_peername_size(const Group_Chats *g_c, uint32_t groupnumber, int peernu * return -1 if groupnumber is invalid. * return -2 if peernumber is invalid. */ -int group_peername(const Group_Chats *g_c, uint32_t groupnumber, int peernumber, uint8_t *name, bool frozen) +int group_peername(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint8_t *name, bool frozen) { const Group_c *g = get_group_c(g_c, groupnumber); @@ -1256,19 +1246,17 @@ int group_peername(const Group_Chats *g_c, uint32_t groupnumber, int peernumber, return -1; } - const Group_Peer *list = frozen ? g->frozen : g->group; - const uint32_t num = frozen ? g->numfrozen : g->numpeers; + const Group_Peer *peer = peer_in_list(g, peernumber, frozen); - if ((uint32_t)peernumber >= num) { + if (peer == nullptr) { return -2; } - if (list[peernumber].nick_len == 0) { - return 0; + if (peer->nick_len > 0) { + memcpy(name, peer->nick, peer->nick_len); } - memcpy(name, list[peernumber].nick, list[peernumber].nick_len); - return list[peernumber].nick_len; + return peer->nick_len; } /* Copy last active timestamp of frozennumber who is in groupnumber to @@ -1278,7 +1266,7 @@ int group_peername(const Group_Chats *g_c, uint32_t groupnumber, int peernumber, * return -1 if groupnumber is invalid. * return -2 if frozennumber is invalid. */ -int group_frozen_last_active(const Group_Chats *g_c, uint32_t groupnumber, int peernumber, +int group_frozen_last_active(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint64_t *last_active) { const Group_c *g = get_group_c(g_c, groupnumber); @@ -1287,7 +1275,7 @@ int group_frozen_last_active(const Group_Chats *g_c, uint32_t groupnumber, int p return -1; } - if ((uint32_t)peernumber >= g->numfrozen) { + if (peernumber >= g->numfrozen) { return -2; } @@ -1315,9 +1303,9 @@ int group_set_max_frozen(const Group_Chats *g_c, uint32_t groupnumber, uint32_t /* List all the (frozen, if frozen is true) peers in the group chat. * - * Copies the names of the peers to the name[length][MAX_NAME_LENGTH] array. + * Copies the names of the peers to the `name[length][MAX_NAME_LENGTH]` array. * - * Copies the lengths of the names to lengths[length] + * Copies the lengths of the names to `lengths[length]` * * returns the number of peers on success. * @@ -1364,7 +1352,7 @@ int group_number_peers(const Group_Chats *g_c, uint32_t groupnumber, bool frozen * return -2 if peernumber is invalid. * return -3 if we are not connected to the group chat. */ -int group_peernumber_is_ours(const Group_Chats *g_c, uint32_t groupnumber, int peernumber) +int group_peernumber_is_ours(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber) { const Group_c *g = get_group_c(g_c, groupnumber); @@ -1372,7 +1360,7 @@ int group_peernumber_is_ours(const Group_Chats *g_c, uint32_t groupnumber, int p return -1; } - if ((uint32_t)peernumber >= g->numpeers) { + if (peernumber >= g->numpeers) { return -2; } @@ -1399,11 +1387,11 @@ int group_get_type(const Group_Chats *g_c, uint32_t groupnumber) return g->type; } -/* Copies the unique id of group_chat[groupnumber] into id. -* -* return false on failure. -* return true on success. -*/ +/* Copies the unique id of `group_chat[groupnumber]` into `id`. + * + * return false on failure. + * return true on success. + */ bool conference_get_id(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *id) { const Group_c *g = get_group_c(g_c, groupnumber); @@ -1498,14 +1486,8 @@ int invite_friend(Group_Chats *g_c, uint32_t friendnumber, uint32_t groupnumber) * return true if a packet was sent. * return false otherwise. */ -static bool try_send_rejoin(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk) +static bool try_send_rejoin(Group_Chats *g_c, Group_c *g, const uint8_t *real_pk) { - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return false; - } - const int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, real_pk); if (friendcon_id == -1) { @@ -1522,16 +1504,20 @@ static bool try_send_rejoin(Group_Chats *g_c, uint32_t groupnumber, const uint8_ return false; } - add_conn_to_groupchat(g_c, friendcon_id, groupnumber, GROUPCHAT_CLOSE_REASON_INTRODUCER, 1); + add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, 1); return true; } static unsigned int send_peer_query(Group_Chats *g_c, int friendcon_id, uint16_t group_num); -/* Join a group (you need to have been invited first.) +static bool send_invite_response(Group_Chats *g_c, int groupnumber, uint32_t friendnumber, const uint8_t *data, + uint16_t length); + +/* Join a group (we need to have been invited first.) * - * expected_type is the groupchat type we expect the chat we are joining is. + * expected_type is the groupchat type we expect the chat we are joining to + * have. * * return group number on success. * return -1 if data length is invalid. @@ -1569,34 +1555,65 @@ int join_groupchat(Group_Chats *g_c, uint32_t friendnumber, uint8_t expected_typ Group_c *g = &g_c->chats[groupnumber]; - const uint16_t group_num = net_htons(groupnumber); g->status = GROUPCHAT_STATUS_VALID; memcpy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE); - uint8_t response[INVITE_RESPONSE_PACKET_SIZE]; - response[0] = INVITE_RESPONSE_ID; - memcpy(response + 1, &group_num, sizeof(uint16_t)); - memcpy(response + 1 + sizeof(uint16_t), data, sizeof(uint16_t) + 1 + GROUP_ID_LENGTH); + if (!send_invite_response(g_c, groupnumber, friendnumber, data, length)) { + g->status = GROUPCHAT_STATUS_NONE; + return -6; + } + + return groupnumber; +} + +static bool send_invite_response(Group_Chats *g_c, int groupnumber, uint32_t friendnumber, const uint8_t *data, + uint16_t length) +{ + Group_c *g = get_group_c(g_c, groupnumber); + + const bool member = (g->status == GROUPCHAT_STATUS_CONNECTED); - if (send_conference_invite_packet(g_c->m, friendnumber, response, sizeof(response))) { - uint16_t other_groupnum; - memcpy(&other_groupnum, data, sizeof(other_groupnum)); - other_groupnum = net_ntohs(other_groupnum); + VLA(uint8_t, response, member ? INVITE_MEMBER_PACKET_SIZE : INVITE_ACCEPT_PACKET_SIZE); + response[0] = member ? INVITE_MEMBER_ID : INVITE_ACCEPT_ID; + net_pack_u16(response + 1, groupnumber); + memcpy(response + 1 + sizeof(uint16_t), data, length); + + if (member) { + net_pack_u16(response + 1 + sizeof(uint16_t) + length, g->peer_number); + } + + if (!send_conference_invite_packet(g_c->m, friendnumber, response, SIZEOF_VLA(response))) { + return false; + } + + if (!member) { g->type = data[sizeof(uint16_t)]; memcpy(g->id, data + sizeof(uint16_t) + 1, GROUP_ID_LENGTH); - const int close_index = add_conn_to_groupchat(g_c, friendcon_id, groupnumber, GROUPCHAT_CLOSE_REASON_INTRODUCER, 1); + } - if (close_index != -1) { - g->close[close_index].group_number = other_groupnum; - g->close[close_index].type = GROUPCHAT_CLOSE_ONLINE; - } + uint16_t other_groupnum; + net_unpack_u16(data, &other_groupnum); - send_peer_query(g_c, friendcon_id, other_groupnum); - return groupnumber; + const int friendcon_id = getfriendcon_id(g_c->m, friendnumber); + + if (friendcon_id == -1) { + return false; } - g->status = GROUPCHAT_STATUS_NONE; - return -6; + const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, 1); + + if (member) { + add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCING, 0); + } + + if (connection_index != -1) { + g->connections[connection_index].group_number = other_groupnum; + g->connections[connection_index].type = GROUPCHAT_CONNECTION_ONLINE; + } + + send_peer_query(g_c, friendcon_id, other_groupnum); + + return true; } /* Set handlers for custom lossy packets. */ @@ -1701,21 +1718,23 @@ int callback_groupchat_delete(Group_Chats *g_c, uint32_t groupnumber, group_on_d static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint8_t message_id, const uint8_t *data, uint16_t len); -static int group_ping_send(const Group_Chats *g_c, uint32_t groupnumber) +/* send a ping message + * return true on success + */ +static bool group_ping_send(const Group_Chats *g_c, uint32_t groupnumber) { if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_PING_ID, nullptr, 0) > 0) { - return 0; + return true; } - return -1; + return false; } /* send a new_peer message - * return 0 on success - * return -1 on failure + * return true on success */ -static int group_new_peer_send(const Group_Chats *g_c, uint32_t groupnumber, uint16_t peer_num, const uint8_t *real_pk, - uint8_t *temp_pk) +static bool group_new_peer_send(const Group_Chats *g_c, uint32_t groupnumber, uint16_t peer_num, const uint8_t *real_pk, + uint8_t *temp_pk) { uint8_t packet[GROUP_MESSAGE_NEW_PEER_LENGTH]; @@ -1725,10 +1744,10 @@ static int group_new_peer_send(const Group_Chats *g_c, uint32_t groupnumber, uin memcpy(packet + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE, temp_pk, CRYPTO_PUBLIC_KEY_SIZE); if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_NEW_PEER_ID, packet, sizeof(packet)) > 0) { - return 0; + return true; } - return -1; + return false; } /* send a kill_peer message @@ -1766,20 +1785,19 @@ static bool group_freeze_peer_send(const Group_Chats *g_c, uint32_t groupnumber, } /* send a name message - * return 0 on success - * return -1 on failure + * return true on success */ -static int group_name_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *nick, uint16_t nick_len) +static bool group_name_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *nick, uint16_t nick_len) { if (nick_len > MAX_NAME_LENGTH) { - return -1; + return false; } if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_NAME_ID, nick, nick_len) > 0) { - return 0; + return true; } - return -1; + return false; } /* send message to announce leaving group @@ -1850,7 +1868,7 @@ int group_title_get_size(const Group_Chats *g_c, uint32_t groupnumber) return -1; } - if (g->title_len == 0 || g->title_len > MAX_NAME_LENGTH) { + if (g->title_len > MAX_NAME_LENGTH || g->title_len == 0) { return -2; } @@ -1872,7 +1890,7 @@ int group_title_get(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *title return -1; } - if (g->title_len == 0 || g->title_len > MAX_NAME_LENGTH) { + if (g->title_len > MAX_NAME_LENGTH || g->title_len == 0) { return -2; } @@ -1882,14 +1900,14 @@ int group_title_get(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *title static bool get_peer_number(const Group_c *g, const uint8_t *real_pk, uint16_t *peer_number) { - const int peer_index = peer_in_chat(g, real_pk); + const int peer_index = peer_in_group(g, real_pk); if (peer_index >= 0) { *peer_number = g->group[peer_index].peer_number; return true; } - const int frozen_index = frozen_in_chat(g, real_pk); + const int frozen_index = frozen_in_group(g, real_pk); if (frozen_index >= 0) { *peer_number = g->frozen[frozen_index].peer_number; @@ -1925,19 +1943,28 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con } return; + } else { + Group_c *g = get_group_c(g_c, groupnumber); + + if (g && g->status == GROUPCHAT_STATUS_CONNECTED) { + send_invite_response(g_c, groupnumber, friendnumber, invite_data, invite_length); + } } break; } - case INVITE_RESPONSE_ID: { - if (length != INVITE_RESPONSE_PACKET_SIZE) { + case INVITE_ACCEPT_ID: + case INVITE_MEMBER_ID: { + const bool member = (data[0] == INVITE_MEMBER_ID); + + if (length != (member ? INVITE_MEMBER_PACKET_SIZE : INVITE_ACCEPT_PACKET_SIZE)) { return; } uint16_t other_groupnum, groupnum; - memcpy(&groupnum, data + 1 + sizeof(uint16_t), sizeof(uint16_t)); - groupnum = net_ntohs(groupnum); + net_unpack_u16(data + 1, &other_groupnum); + net_unpack_u16(data + 1 + sizeof(uint16_t), &groupnum); Group_c *g = get_group_c(g_c, groupnum); @@ -1953,24 +1980,28 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con return; } - /* TODO(irungentoo): what if two people enter the group at the same time and - are given the same peer_number by different nodes? */ - uint16_t peer_number = random_u16(); - - unsigned int tries = 0; + uint16_t peer_number; - while (get_peer_index(g, peer_number) != -1 || get_frozen_index(g, peer_number) != -1) { + if (member) { + net_unpack_u16(data + 1 + sizeof(uint16_t) * 2 + 1 + GROUP_ID_LENGTH, &peer_number); + } else { + /* TODO(irungentoo): what if two people enter the group at the + * same time and are given the same peer_number by different + * nodes? */ peer_number = random_u16(); - ++tries; - if (tries > 32) { - return; + unsigned int tries = 0; + + while (get_peer_index(g, peer_number) != -1 || get_frozen_index(g, peer_number) != -1) { + peer_number = random_u16(); + ++tries; + + if (tries > 32) { + return; + } } } - memcpy(&other_groupnum, data + 1, sizeof(uint16_t)); - other_groupnum = net_ntohs(other_groupnum); - const int friendcon_id = getfriendcon_id(m, friendnumber); if (friendcon_id == -1) { @@ -1982,55 +2013,57 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con get_friendcon_public_keys(real_pk, temp_pk, g_c->fr_c, friendcon_id); addpeer(g_c, groupnum, real_pk, temp_pk, peer_number, userdata, true, true); - const int close_index = add_conn_to_groupchat(g_c, friendcon_id, groupnum, GROUPCHAT_CLOSE_REASON_INTRODUCING, 1); + const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g, + GROUPCHAT_CONNECTION_REASON_INTRODUCING, 1); - if (close_index != -1) { - g->close[close_index].group_number = other_groupnum; - g->close[close_index].type = GROUPCHAT_CLOSE_ONLINE; + if (member) { + add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, 0); + send_peer_query(g_c, friendcon_id, other_groupnum); + } + + if (connection_index != -1) { + g->connections[connection_index].group_number = other_groupnum; + g->connections[connection_index].type = GROUPCHAT_CONNECTION_ONLINE; } group_new_peer_send(g_c, groupnum, peer_number, real_pk, temp_pk); + break; } - default: return; } } -/* Find index of friend in the close list; +/* Find index of friend in the connections list. * - * returns index on success - * returns -1 on failure. + * return index on success + * return -1 on failure. */ -static int friend_in_close(const Group_c *g, int friendcon_id) +static int friend_in_connections(const Group_c *g, int friendcon_id) { - unsigned int i; - - for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type == GROUPCHAT_CLOSE_NONE) { + for (unsigned int i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { + if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) { continue; } - if (g->close[i].number != (uint32_t)friendcon_id) { - continue; + if (g->connections[i].number == (uint32_t)friendcon_id) { + return i; } - - return i; } return -1; } -/* return number of connected close connections. +/* return number of connections. */ -static unsigned int count_close_connected(const Group_c *g) +static unsigned int count_connected(const Group_c *g) { - unsigned int i, count = 0; + unsigned int count = 0; - for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type == GROUPCHAT_CLOSE_ONLINE) { + for (unsigned int i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { + if (g->connections[i].type == GROUPCHAT_CONNECTION_ONLINE) { ++count; } } @@ -2051,7 +2084,7 @@ static int send_packet_online(Friend_Connections *fr_c, int friendcon_id, uint16 sizeof(packet), 0) != -1; } -static int ping_groupchat(Group_Chats *g_c, uint32_t groupnumber); +static bool ping_groupchat(Group_Chats *g_c, uint32_t groupnumber); static int handle_packet_online(Group_Chats *g_c, int friendcon_id, const uint8_t *data, uint16_t length) { @@ -2075,29 +2108,29 @@ static int handle_packet_online(Group_Chats *g_c, int friendcon_id, const uint8_ return -1; } - const int index = friend_in_close(g, friendcon_id); + const int index = friend_in_connections(g, friendcon_id); if (index == -1) { return -1; } - if (g->close[index].type == GROUPCHAT_CLOSE_ONLINE) { + if (g->connections[index].type == GROUPCHAT_CONNECTION_ONLINE) { return -1; } - if (count_close_connected(g) == 0 || (g->close[index].reasons & GROUPCHAT_CLOSE_REASON_INTRODUCER)) { + if (count_connected(g) == 0 || (g->connections[index].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER)) { send_peer_query(g_c, friendcon_id, other_groupnum); } - g->close[index].group_number = other_groupnum; - g->close[index].type = GROUPCHAT_CLOSE_ONLINE; + g->connections[index].group_number = other_groupnum; + g->connections[index].type = GROUPCHAT_CONNECTION_ONLINE; send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->type, g->id); - if (g->close[index].reasons & GROUPCHAT_CLOSE_REASON_INTRODUCING) { + if (g->connections[index].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCING) { uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE], temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; get_friendcon_public_keys(real_pk, temp_pk, g_c->fr_c, friendcon_id); - const int peer_index = peer_in_chat(g, real_pk); + const int peer_index = peer_in_group(g, real_pk); if (peer_index != -1) { group_new_peer_send(g_c, groupnumber, g->group[peer_index].peer_number, real_pk, temp_pk); @@ -2120,7 +2153,7 @@ static int handle_packet_rejoin(Group_Chats *g_c, int friendcon_id, const uint8_ const int32_t groupnum = get_group_num(g_c, *data, data + 1); - const Group_c *g = get_group_c(g_c, groupnum); + Group_c *g = get_group_c(g_c, groupnum); if (!g) { return -1; @@ -2136,9 +2169,10 @@ static int handle_packet_rejoin(Group_Chats *g_c, int friendcon_id, const uint8_ } addpeer(g_c, groupnum, real_pk, temp_pk, peer_number, userdata, true, true); - const int close_index = add_conn_to_groupchat(g_c, friendcon_id, groupnum, GROUPCHAT_CLOSE_REASON_INTRODUCING, 1); + const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g, + GROUPCHAT_CONNECTION_REASON_INTRODUCING, 1); - if (close_index != -1) { + if (connection_index != -1) { send_packet_online(g_c->fr_c, friendcon_id, groupnum, g->type, g->id); } @@ -2172,24 +2206,18 @@ static unsigned int send_peer_query(Group_Chats *g_c, int friendcon_id, uint16_t /* return number of peers sent on success. * return 0 on failure. */ -static unsigned int send_peers(Group_Chats *g_c, uint32_t groupnumber, int friendcon_id, uint16_t group_num) +static unsigned int send_peers(Group_Chats *g_c, const Group_c *g, int friendcon_id, uint16_t group_num) { - const Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return 0; - } - uint8_t response_packet[MAX_CRYPTO_DATA_SIZE - (1 + sizeof(uint16_t))]; response_packet[0] = PEER_RESPONSE_ID; uint8_t *p = response_packet + 1; uint16_t sent = 0; - uint32_t i; - for (i = 0; i < g->numpeers; ++i) { - if ((p - response_packet) + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE * 2 + 1 + g->group[i].nick_len > sizeof( - response_packet)) { + for (uint32_t i = 0;; ++i) { + if (i == g->numpeers + || (p - response_packet) + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE * 2 + 1 + g->group[i].nick_len > + sizeof(response_packet)) { if (send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num, response_packet, (p - response_packet))) { sent = i; @@ -2197,6 +2225,10 @@ static unsigned int send_peers(Group_Chats *g_c, uint32_t groupnumber, int frien return sent; } + if (i == g->numpeers) { + break; + } + p = response_packet + 1; } @@ -2213,13 +2245,6 @@ static unsigned int send_peers(Group_Chats *g_c, uint32_t groupnumber, int frien p += g->group[i].nick_len; } - if (sent != i) { - if (send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num, response_packet, - (p - response_packet))) { - sent = i; - } - } - if (g->title_len) { VLA(uint8_t, title_packet, 1 + g->title_len); title_packet[0] = PEER_TITLE_ID; @@ -2289,37 +2314,31 @@ static int handle_send_peers(Group_Chats *g_c, uint32_t groupnumber, const uint8 } static void handle_direct_packet(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length, - int close_index, void *userdata) + int connection_index, void *userdata) { if (length == 0) { return; } - switch (data[0]) { - case PEER_INTRODUCED_ID: { - const Group_c *g = get_group_c(g_c, groupnumber); + Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { - return; - } + if (!g) { + return; + } - remove_conn_reason(g_c, groupnumber, close_index, GROUPCHAT_CLOSE_REASON_INTRODUCING); + switch (data[0]) { + case PEER_INTRODUCED_ID: { + remove_connection_reason(g_c, g, connection_index, GROUPCHAT_CONNECTION_REASON_INTRODUCING); } break; case PEER_QUERY_ID: { - const Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return; - } - - if (g->close[close_index].type != GROUPCHAT_CLOSE_ONLINE) { + if (g->connections[connection_index].type != GROUPCHAT_CONNECTION_ONLINE) { return; } - send_peers(g_c, groupnumber, g->close[close_index].number, g->close[close_index].group_number); + send_peers(g_c, g, g->connections[connection_index].number, g->connections[connection_index].group_number); } break; @@ -2331,12 +2350,6 @@ static void handle_direct_packet(Group_Chats *g_c, uint32_t groupnumber, const u break; case PEER_TITLE_ID: { - const Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - break; - } - if (!g->title_fresh) { settitle(g_c, groupnumber, -1, data + 1, length - 1, userdata); } @@ -2346,24 +2359,18 @@ static void handle_direct_packet(Group_Chats *g_c, uint32_t groupnumber, const u } } -/* Send message to all close except receiver (if receiver isn't -1) +/* Send message to all connections except receiver (if receiver isn't -1) * NOTE: this function appends the group chat number to the data passed to it. * * return number of messages sent. */ -static unsigned int send_message_all_close(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, +static unsigned int send_message_all_connections(const Group_Chats *g_c, const Group_c *g, const uint8_t *data, uint16_t length, int receiver) { - const Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return 0; - } - - uint16_t i, sent = 0; + uint16_t sent = 0; - for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type != GROUPCHAT_CLOSE_ONLINE) { + for (uint16_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { + if (g->connections[i].type != GROUPCHAT_CONNECTION_ONLINE) { continue; } @@ -2371,8 +2378,8 @@ static unsigned int send_message_all_close(const Group_Chats *g_c, uint32_t grou continue; } - if (send_packet_group_peer(g_c->fr_c, g->close[i].number, PACKET_ID_MESSAGE_CONFERENCE, g->close[i].group_number, data, - length)) { + if (send_packet_group_peer(g_c->fr_c, g->connections[i].number, PACKET_ID_MESSAGE_CONFERENCE, + g->connections[i].group_number, data, length)) { ++sent; } } @@ -2380,25 +2387,19 @@ static unsigned int send_message_all_close(const Group_Chats *g_c, uint32_t grou return sent; } -/* Send lossy message to all close except receiver (if receiver isn't -1) +/* Send lossy message to all connections except receiver (if receiver isn't -1) * NOTE: this function appends the group chat number to the data passed to it. * * return number of messages sent. */ -static unsigned int send_lossy_all_close(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, +static unsigned int send_lossy_all_connections(const Group_Chats *g_c, const Group_c *g, const uint8_t *data, uint16_t length, int receiver) { - const Group_c *g = get_group_c(g_c, groupnumber); + unsigned int sent = 0, num_connected_closest = 0, connected_closest[DESIRED_CLOSEST]; - if (!g) { - return 0; - } - - unsigned int i, sent = 0, num_connected_closest = 0, connected_closest[DESIRED_CLOSE_CONNECTIONS]; - - for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type != GROUPCHAT_CLOSE_ONLINE) { + for (unsigned int i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { + if (g->connections[i].type != GROUPCHAT_CONNECTION_ONLINE) { continue; } @@ -2406,14 +2407,14 @@ static unsigned int send_lossy_all_close(const Group_Chats *g_c, uint32_t groupn continue; } - if (g->close[i].reasons & GROUPCHAT_CLOSE_REASON_CLOSEST) { + if (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST) { connected_closest[num_connected_closest] = i; ++num_connected_closest; continue; } - if (send_lossy_group_peer(g_c->fr_c, g->close[i].number, PACKET_ID_LOSSY_CONFERENCE, g->close[i].group_number, data, - length)) { + if (send_lossy_group_peer(g_c->fr_c, g->connections[i].number, PACKET_ID_LOSSY_CONFERENCE, + g->connections[i].group_number, data, length)) { ++sent; } } @@ -2422,48 +2423,31 @@ static unsigned int send_lossy_all_close(const Group_Chats *g_c, uint32_t groupn return sent; } - unsigned int to_send = 0; - uint64_t comp_val_old = ~0; + unsigned int to_send[2] = {0, 0}; + uint64_t comp_val_old[2] = {(uint64_t) -1, (uint64_t) -1}; - for (i = 0; i < num_connected_closest; ++i) { + for (unsigned int i = 0; i < num_connected_closest; ++i) { uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE] = {0}; - uint8_t dht_temp_pk[CRYPTO_PUBLIC_KEY_SIZE] = {0}; - get_friendcon_public_keys(real_pk, dht_temp_pk, g_c->fr_c, g->close[connected_closest[i]].number); + get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->connections[connected_closest[i]].number); const uint64_t comp_val = calculate_comp_value(g->real_pk, real_pk); - if (comp_val < comp_val_old) { - to_send = connected_closest[i]; - comp_val_old = comp_val; + for (uint8_t j = 0; j < 2; ++j) { + if (j ? (comp_val > comp_val_old[j]) : (comp_val < comp_val_old[j])) { + to_send[j] = connected_closest[i]; + comp_val_old[j] = comp_val; + } } } - if (send_lossy_group_peer(g_c->fr_c, g->close[to_send].number, PACKET_ID_LOSSY_CONFERENCE, - g->close[to_send].group_number, data, length)) { - ++sent; - } - - unsigned int to_send_other = 0; - comp_val_old = ~0; - - for (i = 0; i < num_connected_closest; ++i) { - uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE] = {0}; - uint8_t dht_temp_pk[CRYPTO_PUBLIC_KEY_SIZE] = {0}; - get_friendcon_public_keys(real_pk, dht_temp_pk, g_c->fr_c, g->close[connected_closest[i]].number); - const uint64_t comp_val = calculate_comp_value(real_pk, g->real_pk); - - if (comp_val < comp_val_old) { - to_send_other = connected_closest[i]; - comp_val_old = comp_val; + for (uint8_t j = 0; j < 2; ++j) { + if (j && to_send[1] == to_send[0]) { + break; } - } - if (to_send_other == to_send) { - return sent; - } - - if (send_lossy_group_peer(g_c->fr_c, g->close[to_send_other].number, PACKET_ID_LOSSY_CONFERENCE, - g->close[to_send_other].group_number, data, length)) { - ++sent; + if (send_lossy_group_peer(g_c->fr_c, g->connections[to_send[j]].number, PACKET_ID_LOSSY_CONFERENCE, + g->connections[to_send[j]].group_number, data, length)) { + ++sent; + } } return sent; @@ -2490,7 +2474,7 @@ static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint return -2; } - if (g->status != GROUPCHAT_STATUS_CONNECTED || count_close_connected(g) == 0) { + if (g->status != GROUPCHAT_STATUS_CONNECTED || count_connected(g) == 0) { return -3; } @@ -2513,9 +2497,13 @@ static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint memcpy(packet + sizeof(uint16_t) + sizeof(uint32_t) + 1, data, len); } - unsigned int ret = send_message_all_close(g_c, groupnumber, packet, SIZEOF_VLA(packet), -1); + unsigned int ret = send_message_all_connections(g_c, g, packet, SIZEOF_VLA(packet), -1); + + if (ret == 0) { + return -4; + } - return (ret == 0) ? -4 : ret; + return ret; } /* send a group message @@ -2569,7 +2557,7 @@ int send_group_lossy_packet(const Group_Chats *g_c, uint32_t groupnumber, const memcpy(packet + sizeof(uint16_t), &message_num, sizeof(uint16_t)); memcpy(packet + sizeof(uint16_t) * 2, data, length); - if (send_lossy_all_close(g_c, groupnumber, packet, SIZEOF_VLA(packet), -1) == 0) { + if (send_lossy_all_connections(g_c, g, packet, SIZEOF_VLA(packet), -1) == 0) { return -1; } @@ -2584,7 +2572,7 @@ static Message_Info *find_message_slot_or_reject(uint32_t message_number, uint8_ Message_Info *i; for (i = peer->last_message_infos; i < peer->last_message_infos + peer->num_last_message_infos; ++i) { - if (message_number > i->message_number) { + if (message_number - (i->message_number + 1) <= ((uint32_t)1 << 31)) { break; } @@ -2602,7 +2590,7 @@ static Message_Info *find_message_slot_or_reject(uint32_t message_number, uint8_ /* Stores message info in peer->last_message_infos. * - * return true if message should be processed; + * return true if message should be processed. * return false otherwise. */ static bool check_message_info(uint32_t message_number, uint8_t message_id, Group_Peer *peer) @@ -2630,13 +2618,13 @@ static bool check_message_info(uint32_t message_number, uint8_t message_id, Grou } static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length, - int close_index, void *userdata) + int connection_index, void *userdata) { if (length < sizeof(uint16_t) + sizeof(uint32_t) + 1) { return; } - const Group_c *g = get_group_c(g_c, groupnumber); + Group_c *g = get_group_c(g_c, groupnumber); if (!g) { return; @@ -2664,32 +2652,32 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber, return; } - if (g->close[close_index].type != GROUPCHAT_CLOSE_ONLINE) { + if (g->connections[connection_index].type != GROUPCHAT_CONNECTION_ONLINE) { return; } /* If we don't know the peer this packet came from, then we query the * list of peers from the relaying peer. - * (They would not have relayed it if they didn't know the peer.) */ - send_peer_query(g_c, g->close[close_index].number, g->close[close_index].group_number); + * (They wouldn't have relayed it if they didn't know the peer.) */ + send_peer_query(g_c, g->connections[connection_index].number, g->connections[connection_index].group_number); return; } - if (g->num_introducer_connections > 0 && count_close_connected(g) > DESIRED_CLOSE_CONNECTIONS) { + if (g->num_introducer_connections > 0 && count_connected(g) > DESIRED_CLOSEST) { for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type == GROUPCHAT_CLOSE_NONE - || !(g->close[i].reasons & GROUPCHAT_CLOSE_REASON_INTRODUCER) - || i == close_index) { + if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE + || !(g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER) + || i == connection_index) { continue; } uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; - get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->close[i].number); + get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->connections[i].number); if (id_equal(g->group[index].real_pk, real_pk)) { /* Received message from peer relayed via another peer, so * the introduction was successful */ - remove_conn_reason(g_c, groupnumber, i, GROUPCHAT_CLOSE_REASON_INTRODUCER); + remove_connection_reason(g_c, g, i, GROUPCHAT_CONNECTION_REASON_INTRODUCER); } } } @@ -2727,7 +2715,7 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber, if (peer_number == kill_peer_number) { if (message_id == GROUP_MESSAGE_KILL_PEER_ID) { - delpeer(g_c, groupnumber, index, userdata, false); + delpeer(g_c, groupnumber, index, userdata); } else { freeze_peer(g_c, groupnumber, index, userdata); } @@ -2739,14 +2727,14 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber, break; case GROUP_MESSAGE_NAME_ID: { - if (setnick(g_c, groupnumber, index, msg_data, msg_data_len, userdata, true) == -1) { + if (!setnick(g_c, groupnumber, index, msg_data, msg_data_len, userdata, true)) { return; } } break; case GROUP_MESSAGE_TITLE_ID: { - if (settitle(g_c, groupnumber, index, msg_data, msg_data_len, userdata) == -1) { + if (!settitle(g_c, groupnumber, index, msg_data, msg_data_len, userdata)) { return; } } @@ -2790,7 +2778,15 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber, return; } - send_message_all_close(g_c, groupnumber, data, length, -1/* TODO(irungentoo) close_index */); + /* If the packet was received from the peer who sent the message, relay it + * back. When the sender only has one group connection (e.g. because there + * are only two peers in the group), this is the only way for them to + * receive their own message. */ + uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; + get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->connections[connection_index].number); + bool relay_back = id_equal(g->group[index].real_pk, real_pk); + + send_message_all_connections(g_c, g, data, length, relay_back ? -1 : connection_index); } static int g_handle_packet(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata) @@ -2809,10 +2805,6 @@ static int g_handle_packet(void *object, int friendcon_id, const uint8_t *data, return handle_packet_rejoin(g_c, friendcon_id, data + 1, length - 1, userdata); } - if (data[0] != PACKET_ID_DIRECT_CONFERENCE && data[0] != PACKET_ID_MESSAGE_CONFERENCE) { - return -1; - } - uint16_t groupnumber; memcpy(&groupnumber, data + 1, sizeof(uint16_t)); groupnumber = net_ntohs(groupnumber); @@ -2822,30 +2814,25 @@ static int g_handle_packet(void *object, int friendcon_id, const uint8_t *data, return -1; } - const int index = friend_in_close(g, friendcon_id); + const int index = friend_in_connections(g, friendcon_id); if (index == -1) { return -1; } - switch (data[0]) { - case PACKET_ID_DIRECT_CONFERENCE: { - handle_direct_packet(g_c, groupnumber, data + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), index, userdata); - break; - } - - case PACKET_ID_MESSAGE_CONFERENCE: { - handle_message_packet_group(g_c, groupnumber, data + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), index, - userdata); - break; - } + if (data[0] == PACKET_ID_DIRECT_CONFERENCE) { + handle_direct_packet(g_c, groupnumber, data + 1 + sizeof(uint16_t), + length - (1 + sizeof(uint16_t)), index, userdata); + return 0; + } - default: { - return 0; - } + if (data[0] == PACKET_ID_MESSAGE_CONFERENCE) { + handle_message_packet_group(g_c, groupnumber, data + 1 + sizeof(uint16_t), + length - (1 + sizeof(uint16_t)), index, userdata); + return 0; } - return 0; + return -1; } /* Did we already receive the lossy packet or not. @@ -2856,10 +2843,9 @@ static int g_handle_packet(void *object, int friendcon_id, const uint8_t *data, * * TODO(irungentoo): test this */ -static unsigned int lossy_packet_not_received(const Group_c *g, int peer_index, uint16_t message_number) +static int lossy_packet_not_received(const Group_c *g, int peer_index, uint16_t message_number) { if (peer_index == -1) { - // TODO(sudden6): invalid return value return -1; } @@ -2880,7 +2866,6 @@ static unsigned int lossy_packet_not_received(const Group_c *g, int peer_index, } if ((uint16_t)(message_number - g->group[peer_index].bottom_lossy_number) > (1 << 15)) { - // TODO(sudden6): invalid return value return -1; } @@ -2888,21 +2873,18 @@ static unsigned int lossy_packet_not_received(const Group_c *g, int peer_index, if (top_distance >= MAX_LOSSY_COUNT) { crypto_memzero(g->group[peer_index].recv_lossy, sizeof(g->group[peer_index].recv_lossy)); - g->group[peer_index].top_lossy_number = message_number; - g->group[peer_index].bottom_lossy_number = (message_number - MAX_LOSSY_COUNT) + 1; - g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT] = 1; } else { // top_distance < MAX_LOSSY_COUNT for (unsigned int i = g->group[peer_index].bottom_lossy_number; i != g->group[peer_index].bottom_lossy_number + top_distance; ++i) { g->group[peer_index].recv_lossy[i % MAX_LOSSY_COUNT] = 0; } - - g->group[peer_index].top_lossy_number = message_number; - g->group[peer_index].bottom_lossy_number = (message_number - MAX_LOSSY_COUNT) + 1; - g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT] = 1; } + g->group[peer_index].top_lossy_number = message_number; + g->group[peer_index].bottom_lossy_number = (message_number - MAX_LOSSY_COUNT) + 1; + g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT] = 1; + return 0; } @@ -2917,11 +2899,11 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin { Group_Chats *g_c = (Group_Chats *)object; - if (length < 1 + sizeof(uint16_t) * 3 + 1) { + if (data[0] != PACKET_ID_LOSSY_CONFERENCE) { return -1; } - if (data[0] != PACKET_ID_LOSSY_CONFERENCE) { + if (length < 1 + sizeof(uint16_t) * 3 + 1) { return -1; } @@ -2943,7 +2925,7 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin return -1; } - const int index = friend_in_close(g, friendcon_id); + const int index = friend_in_connections(g, friendcon_id); if (index == -1) { return -1; @@ -2959,7 +2941,7 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin return -1; } - if (lossy_packet_not_received(g, peer_index, message_number)) { + if (lossy_packet_not_received(g, peer_index, message_number) != 0) { return -1; } @@ -2969,14 +2951,14 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin ++lossy_data; --lossy_length; - send_lossy_all_close(g_c, groupnumber, data + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), index); + send_lossy_all_connections(g_c, g, data + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), index); - if (g_c->lossy_packethandlers[message_id].function) { - if (g_c->lossy_packethandlers[message_id].function(g->object, groupnumber, peer_index, g->group[peer_index].object, - lossy_data, lossy_length) == -1) { - return -1; - } - } else { + if (!g_c->lossy_packethandlers[message_id].function) { + return -1; + } + + if (g_c->lossy_packethandlers[message_id].function(g->object, groupnumber, peer_index, g->group[peer_index].object, + lossy_data, lossy_length) == -1) { return -1; } @@ -3005,7 +2987,7 @@ int group_set_object(const Group_Chats *g_c, uint32_t groupnumber, void *object) * return 0 on success. * return -1 on failure */ -int group_peer_set_object(const Group_Chats *g_c, uint32_t groupnumber, int peernumber, void *object) +int group_peer_set_object(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, void *object) { const Group_c *g = get_group_c(g_c, groupnumber); @@ -3013,7 +2995,7 @@ int group_peer_set_object(const Group_Chats *g_c, uint32_t groupnumber, int peer return -1; } - if ((uint32_t)peernumber >= g->numpeers) { + if (peernumber >= g->numpeers) { return -1; } @@ -3042,7 +3024,7 @@ void *group_get_object(const Group_Chats *g_c, uint32_t groupnumber) * return NULL on failure. * return object on success. */ -void *group_peer_get_object(const Group_Chats *g_c, uint32_t groupnumber, int peernumber) +void *group_peer_get_object(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber) { const Group_c *g = get_group_c(g_c, groupnumber); @@ -3050,7 +3032,7 @@ void *group_peer_get_object(const Group_Chats *g_c, uint32_t groupnumber, int pe return nullptr; } - if ((uint32_t)peernumber >= g->numpeers) { + if (peernumber >= g->numpeers) { return nullptr; } @@ -3060,29 +3042,32 @@ void *group_peer_get_object(const Group_Chats *g_c, uint32_t groupnumber, int pe /* Interval in seconds to send ping messages */ #define GROUP_PING_INTERVAL 20 -static int ping_groupchat(Group_Chats *g_c, uint32_t groupnumber) +static bool ping_groupchat(Group_Chats *g_c, uint32_t groupnumber) { Group_c *g = get_group_c(g_c, groupnumber); if (!g) { - return -1; + return false; } if (mono_time_is_timeout(g_c->mono_time, g->last_sent_ping, GROUP_PING_INTERVAL)) { - if (group_ping_send(g_c, groupnumber) != -1) { /* Ping */ + if (group_ping_send(g_c, groupnumber)) { g->last_sent_ping = mono_time_get(g_c->mono_time); } } - return 0; + return true; } -static int groupchat_freeze_timedout(Group_Chats *g_c, uint32_t groupnumber, void *userdata) +/* Seconds of inactivity after which to freeze a peer */ +#define FREEZE_TIMEOUT (GROUP_PING_INTERVAL * 3) + +static bool groupchat_freeze_timedout(Group_Chats *g_c, uint32_t groupnumber, void *userdata) { Group_c *g = get_group_c(g_c, groupnumber); if (!g) { - return -1; + return false; } for (uint32_t i = 0; i < g->numpeers; ++i) { @@ -3090,7 +3075,7 @@ static int groupchat_freeze_timedout(Group_Chats *g_c, uint32_t groupnumber, voi continue; } - if (mono_time_is_timeout(g_c->mono_time, g->group[i].last_active, GROUP_PING_INTERVAL * 3)) { + if (mono_time_is_timeout(g_c->mono_time, g->group[i].last_active, FREEZE_TIMEOUT)) { freeze_peer(g_c, groupnumber, i, userdata); } } @@ -3099,7 +3084,74 @@ static int groupchat_freeze_timedout(Group_Chats *g_c, uint32_t groupnumber, voi g->title_fresh = false; } - return 0; + return true; +} + +/* Push non-empty slots to start. */ +static void squash_connections(Group_c *g) +{ + uint16_t i = 0; + + for (uint16_t j = 0; j < MAX_GROUP_CONNECTIONS; ++j) { + if (g->connections[j].type != GROUPCHAT_CONNECTION_NONE) { + g->connections[i] = g->connections[j]; + ++i; + } + } + + for (; i < MAX_GROUP_CONNECTIONS; ++i) { + g->connections[i].type = GROUPCHAT_CONNECTION_NONE; + } +} + +#define MIN_EMPTY_CONNECTIONS (1 + MAX_GROUP_CONNECTIONS / 10) + +/* Remove old connections as necessary to ensure we have space for new + * connections. This invalidates connections array indices (which is + * why we do this periodically rather than on adding a connection). + */ +static void clean_connections(Group_Chats *g_c, Group_c *g) +{ + uint16_t to_clear = MIN_EMPTY_CONNECTIONS; + + for (uint16_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { + if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) { + --to_clear; + + if (to_clear == 0) { + break; + } + } + } + + for (; to_clear > 0; --to_clear) { + // Remove a connection. Prefer non-closest connections, and given + // that prefer non-online connections, and given that prefer earlier + // slots. + uint16_t i = 0; + + while (i < MAX_GROUP_CONNECTIONS + && (g->connections[i].type != GROUPCHAT_CONNECTION_CONNECTING + || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST))) { + ++i; + } + + if (i == MAX_GROUP_CONNECTIONS) { + i = 0; + + while (i < MAX_GROUP_CONNECTIONS - to_clear + && (g->connections[i].type != GROUPCHAT_CONNECTION_ONLINE + || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST))) { + ++i; + } + } + + if (g->connections[i].type != GROUPCHAT_CONNECTION_NONE) { + remove_connection(g_c, g, i); + } + } + + squash_connections(g); } /* Send current name (set in messenger) to all online groups. @@ -3170,6 +3222,11 @@ static uint32_t saved_conf_size(const Group_c *g) return len; } +/* Save a future message number. The save will remain valid until we have sent + * this many more messages. */ +#define SAVE_OFFSET_MESSAGE_NUMBER (1 << 16) +#define SAVE_OFFSET_LOSSY_MESSAGE_NUMBER (1 << 13) + static uint8_t *save_conf(const Group_c *g, uint8_t *data) { *data = g->type; @@ -3178,10 +3235,10 @@ static uint8_t *save_conf(const Group_c *g, uint8_t *data) memcpy(data, g->id, GROUP_ID_LENGTH); data += GROUP_ID_LENGTH; - host_to_lendian_bytes32(data, g->message_number); + host_to_lendian_bytes32(data, g->message_number + SAVE_OFFSET_MESSAGE_NUMBER); data += sizeof(uint32_t); - host_to_lendian_bytes16(data, g->lossy_message_number); + host_to_lendian_bytes16(data, g->lossy_message_number + SAVE_OFFSET_LOSSY_MESSAGE_NUMBER); data += sizeof(uint16_t); host_to_lendian_bytes16(data, g->peer_number); @@ -3294,6 +3351,11 @@ static State_Load_Status load_conferences(Group_Chats *g_c, const uint8_t *data, } g->title_len = *data; + + if (g->title_len > MAX_NAME_LENGTH) { + return STATE_LOAD_STATUS_ERROR; + } + ++data; if (length < (uint32_t)(data - init_data) + g->title_len) { @@ -3323,6 +3385,11 @@ static State_Load_Status load_conferences(Group_Chats *g_c, const uint8_t *data, data += sizeof(uint64_t); peer->nick_len = *data; + + if (peer->nick_len > MAX_NAME_LENGTH) { + return STATE_LOAD_STATUS_ERROR; + } + ++data; if (length < (uint32_t)(data - init_data) + peer->nick_len) { @@ -3405,6 +3472,7 @@ void do_groupchats(Group_Chats *g_c, void *userdata) connect_to_closest(g_c, i, userdata); ping_groupchat(g_c, i); groupchat_freeze_timedout(g_c, i, userdata); + clean_connections(g_c, g); if (g->need_send_name) { group_name_send(g_c, i, g_c->m->name, g_c->m->name_length); @@ -3424,6 +3492,7 @@ void kill_groupchats(Group_Chats *g_c) } m_callback_conference_invite(g_c->m, nullptr); + set_global_status_callback(g_c->m->fr_c, nullptr, nullptr); g_c->m->conferences_object = nullptr; free(g_c); } |