diff options
Diffstat (limited to 'libs/libtox/src/toxcore/group.c')
-rw-r--r-- | libs/libtox/src/toxcore/group.c | 2544 |
1 files changed, 0 insertions, 2544 deletions
diff --git a/libs/libtox/src/toxcore/group.c b/libs/libtox/src/toxcore/group.c deleted file mode 100644 index d3f068dfce..0000000000 --- a/libs/libtox/src/toxcore/group.c +++ /dev/null @@ -1,2544 +0,0 @@ -/* - * Slightly better groupchats implementation. - */ - -/* - * Copyright © 2016-2017 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/>. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "group.h" - -#include "util.h" - -/* return 1 if the groupnumber is not valid. - * return 0 if the groupnumber is valid. - */ -static uint8_t groupnumber_not_valid(const Group_Chats *g_c, int groupnumber) -{ - if ((unsigned int)groupnumber >= g_c->num_chats) { - return 1; - } - - if (g_c->chats == NULL) { - return 1; - } - - if (g_c->chats[groupnumber].status == GROUPCHAT_STATUS_NONE) { - return 1; - } - - return 0; -} - - -/* Set the size of the groupchat list to num. - * - * return -1 if realloc fails. - * return 0 if it succeeds. - */ -static int realloc_groupchats(Group_Chats *g_c, uint32_t num) -{ - if (num == 0) { - free(g_c->chats); - g_c->chats = NULL; - return 0; - } - - Group_c *newgroup_chats = (Group_c *)realloc(g_c->chats, num * sizeof(Group_c)); - - if (newgroup_chats == NULL) { - return -1; - } - - g_c->chats = newgroup_chats; - return 0; -} - - -/* Create a new empty groupchat connection. - * - * return -1 on failure. - * return groupnumber on success. - */ -static int create_group_chat(Group_Chats *g_c) -{ - uint32_t i; - - for (i = 0; i < g_c->num_chats; ++i) { - if (g_c->chats[i].status == GROUPCHAT_STATUS_NONE) { - return i; - } - } - - int id = -1; - - if (realloc_groupchats(g_c, g_c->num_chats + 1) == 0) { - id = g_c->num_chats; - ++g_c->num_chats; - memset(&(g_c->chats[id]), 0, sizeof(Group_c)); - } - - return id; -} - - -/* Wipe a groupchat. - * - * return -1 on failure. - * return 0 on success. - */ -static int wipe_group_chat(Group_Chats *g_c, int groupnumber) -{ - if (groupnumber_not_valid(g_c, groupnumber)) { - return -1; - } - - uint32_t i; - crypto_memzero(&(g_c->chats[groupnumber]), sizeof(Group_c)); - - for (i = g_c->num_chats; i != 0; --i) { - if (g_c->chats[i - 1].status != GROUPCHAT_STATUS_NONE) { - break; - } - } - - if (g_c->num_chats != i) { - g_c->num_chats = i; - realloc_groupchats(g_c, g_c->num_chats); - } - - return 0; -} - -static Group_c *get_group_c(const Group_Chats *g_c, int groupnumber) -{ - if (groupnumber_not_valid(g_c, groupnumber)) { - return 0; - } - - return &g_c->chats[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. - * - * TODO(irungentoo): make this more efficient. - */ - -static int peer_in_chat(const Group_c *chat, const uint8_t *real_pk) -{ - uint32_t i; - - for (i = 0; i < chat->numpeers; ++i) { - if (id_equal(chat->group[i].real_pk, real_pk)) { - return i; - } - } - - return -1; -} - -/* - * check if group with identifier is in group array. - * - * return group number if peer is in list. - * return -1 if group is not in list. - * - * TODO(irungentoo): make this more efficient and maybe use constant time comparisons? - */ -static int get_group_num(const Group_Chats *g_c, const uint8_t *identifier) -{ - uint32_t i; - - for (i = 0; i < g_c->num_chats; ++i) { - if (crypto_memcmp(g_c->chats[i].identifier, identifier, GROUP_IDENTIFIER_LENGTH) == 0) { - return i; - } - } - - return -1; -} - -/* - * check if peer with peer_number is in peer array. - * - * return peer number if peer is in chat. - * return -1 if peer is not in chat. - * - * TODO(irungentoo): make this more efficient. - */ -static int get_peer_index(Group_c *g, uint16_t peer_number) -{ - uint32_t i; - - for (i = 0; i < g->numpeers; ++i) { - if (g->group[i].peer_number == peer_number) { - return i; - } - } - - return -1; -} - - -static uint64_t calculate_comp_value(const uint8_t *pk1, const uint8_t *pk2) -{ - uint64_t cmp1 = 0, cmp2 = 0; - - unsigned int i; - - for (i = 0; i < sizeof(uint64_t); ++i) { - cmp1 = (cmp1 << 8) + (uint64_t)pk1[i]; - cmp2 = (cmp2 << 8) + (uint64_t)pk2[i]; - } - - return (cmp1 - cmp2); -} - -enum { - GROUPCHAT_CLOSEST_NONE, - GROUPCHAT_CLOSEST_ADDED, - GROUPCHAT_CLOSEST_REMOVED -}; - -static int friend_in_close(Group_c *g, int friendcon_id); -static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, int groupnumber, uint8_t closest, uint8_t lock); - -static int add_to_closest(Group_Chats *g_c, int groupnumber, 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; - } - - unsigned int i; - unsigned int index = DESIRED_CLOSE_CONNECTIONS; - - for (i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) { - if (g->closest_peers[i].entry && public_key_cmp(real_pk, g->closest_peers[i].real_pk) == 0) { - return 0; - } - } - - for (i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) { - if (g->closest_peers[i].entry == 0) { - index = i; - break; - } - } - - if (index == DESIRED_CLOSE_CONNECTIONS) { - 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); - - if (comp > comp_val && comp > comp_d) { - index = i; - comp_d = comp; - } - } - - comp_val = calculate_comp_value(real_pk, g->real_pk); - - for (i = (DESIRED_CLOSE_CONNECTIONS / 2); i < DESIRED_CLOSE_CONNECTIONS; ++i) { - uint64_t comp = calculate_comp_value(g->closest_peers[i].real_pk, g->real_pk); - - if (comp > comp_val && comp > comp_d) { - index = i; - comp_d = comp; - } - } - } - - if (index == DESIRED_CLOSE_CONNECTIONS) { - return -1; - } - - uint8_t old_real_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t old_temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t old = 0; - - if (g->closest_peers[index].entry) { - memcpy(old_real_pk, g->closest_peers[index].real_pk, CRYPTO_PUBLIC_KEY_SIZE); - memcpy(old_temp_pk, g->closest_peers[index].temp_pk, CRYPTO_PUBLIC_KEY_SIZE); - old = 1; - } - - g->closest_peers[index].entry = 1; - memcpy(g->closest_peers[index].real_pk, real_pk, CRYPTO_PUBLIC_KEY_SIZE); - 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); - } - - if (!g->changed) { - g->changed = GROUPCHAT_CLOSEST_ADDED; - } - - return 0; -} - -static unsigned int pk_in_closest_peers(Group_c *g, uint8_t *real_pk) -{ - unsigned int i; - - for (i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) { - if (!g->closest_peers[i].entry) { - continue; - } - - if (public_key_cmp(g->closest_peers[i].real_pk, real_pk) == 0) { - return 1; - } - } - - return 0; -} - -static int send_packet_online(Friend_Connections *fr_c, int friendcon_id, uint16_t group_num, uint8_t *identifier); - -static int connect_to_closest(Group_Chats *g_c, int groupnumber, void *userdata) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - if (!g->changed) { - return 0; - } - - unsigned int i; - - if (g->changed == GROUPCHAT_CLOSEST_REMOVED) { - for (i = 0; i < g->numpeers; ++i) { - add_to_closest(g_c, groupnumber, g->group[i].real_pk, g->group[i].temp_pk); - } - } - - for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type == GROUPCHAT_CLOSE_NONE) { - continue; - } - - if (!g->close[i].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); - - if (!pk_in_closest_peers(g, real_pk)) { - g->close[i].type = GROUPCHAT_CLOSE_NONE; - kill_friend_connection(g_c->fr_c, g->close[i].number); - } - } - - for (i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++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; - - if (friendcon_id == -1) { - friendcon_id = new_friend_connection(g_c->fr_c, g->closest_peers[i].real_pk); - lock = 0; - - if (friendcon_id == -1) { - continue; - } - - 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, 1, lock); - - if (friend_con_connected(g_c->fr_c, friendcon_id) == FRIENDCONN_STATUS_CONNECTED) { - send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->identifier); - } - } - - g->changed = GROUPCHAT_CLOSEST_NONE; - - return 0; -} - -/* Add a peer to the group chat. - * - * do_gc_callback indicates whether we want to trigger callbacks set by the client - * via the public API. This should be set to false if this function is called - * from outside of the tox_iterate() loop. - * - * return peer_index if success or peer already in chat. - * return -1 if error. - */ -static int addpeer(Group_Chats *g_c, int groupnumber, const uint8_t *real_pk, const uint8_t *temp_pk, - uint16_t peer_number, void *userdata, bool do_gc_callback) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - // TODO(irungentoo): - int peer_index = peer_in_chat(g, real_pk); - - if (peer_index != -1) { - id_copy(g->group[peer_index].temp_pk, temp_pk); - - if (g->group[peer_index].peer_number != peer_number) { - return -1; - } - - return peer_index; - } - - peer_index = get_peer_index(g, peer_number); - - if (peer_index != -1) { - return -1; - } - - Group_Peer *temp = (Group_Peer *)realloc(g->group, sizeof(Group_Peer) * (g->numpeers + 1)); - - if (temp == NULL) { - return -1; - } - - 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].peer_number = peer_number; - - g->group[g->numpeers].last_recv = unix_time(); - ++g->numpeers; - - add_to_closest(g_c, groupnumber, real_pk, temp_pk); - - if (do_gc_callback && g_c->group_namelistchange) { - g_c->group_namelistchange(g_c->m, groupnumber, g->numpeers - 1, CHAT_CHANGE_PEER_ADD, userdata); - } - - if (g->peer_on_join) { - g->peer_on_join(g->object, groupnumber, g->numpeers - 1); - } - - return (g->numpeers - 1); -} - -static int remove_close_conn(Group_Chats *g_c, int groupnumber, int friendcon_id) -{ - 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; - } - } - - return -1; -} - - -/* - * Delete a peer from the group chat. - * - * return 0 if success - * return -1 if error. - */ -static int delpeer(Group_Chats *g_c, int groupnumber, int peer_index, void *userdata) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - uint32_t i; - - for (i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) { /* If peer is in closest_peers list, remove it. */ - 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; - break; - } - } - - int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, g->group[peer_index].real_pk); - - if (friendcon_id != -1) { - remove_close_conn(g_c, groupnumber, friendcon_id); - } - - --g->numpeers; - - void *peer_object = g->group[peer_index].object; - - if (g->numpeers == 0) { - free(g->group); - g->group = NULL; - } else { - if (g->numpeers != (uint32_t)peer_index) { - memcpy(&g->group[peer_index], &g->group[g->numpeers], sizeof(Group_Peer)); - } - - Group_Peer *temp = (Group_Peer *)realloc(g->group, sizeof(Group_Peer) * (g->numpeers)); - - if (temp == NULL) { - return -1; - } - - g->group = temp; - } - - if (g_c->group_namelistchange) { - g_c->group_namelistchange(g_c->m, groupnumber, peer_index, CHAT_CHANGE_PEER_DEL, userdata); - } - - if (g->peer_on_leave) { - g->peer_on_leave(g->object, groupnumber, peer_index, peer_object); - } - - return 0; -} - -/* Set the nick for a peer. - * - * do_gc_callback indicates whether we want to trigger callbacks set by the client - * 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. - */ -static int setnick(Group_Chats *g_c, int 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; - } - - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - /* 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 (nick_len) { - memcpy(g->group[peer_index].nick, nick, nick_len); - } - - g->group[peer_index].nick_len = nick_len; - - if (do_gc_callback && g_c->group_namelistchange) { - g_c->group_namelistchange(g_c->m, groupnumber, peer_index, CHAT_CHANGE_PEER_NAME, userdata); - } - - return 0; -} - -static int settitle(Group_Chats *g_c, int 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; - } - - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - /* same as already set? */ - if (g->title_len == title_len && !memcmp(g->title, title, title_len)) { - return 0; - } - - memcpy(g->title, title, title_len); - g->title_len = title_len; - - if (g_c->title_callback) { - g_c->title_callback(g_c->m, groupnumber, peer_index, title, title_len, userdata); - } - - return 0; -} - -static void set_conns_type_close(Group_Chats *g_c, int groupnumber, int friendcon_id, uint8_t type) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return; - } - - 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) { - continue; - } - - if (type == GROUPCHAT_CLOSE_ONLINE) { - send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->identifier); - } else { - g->close[i].type = type; - } - } -} -/* Set the type for all close connections with friendcon_id */ -static void set_conns_status_groups(Group_Chats *g_c, int friendcon_id, uint8_t type) -{ - uint32_t i; - - for (i = 0; i < g_c->num_chats; ++i) { - set_conns_type_close(g_c, i, friendcon_id, type); - } -} - -static int g_handle_status(void *object, int friendcon_id, uint8_t status, void *userdata) -{ - Group_Chats *g_c = (Group_Chats *)object; - - if (status) { /* Went online */ - set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CLOSE_ONLINE); - } else { /* Went offline */ - set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CLOSE_CONNECTION); - // TODO(irungentoo): remove timedout connections? - } - - return 0; -} - -static int g_handle_packet(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata); -static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata); - -/* Add friend to group chat. - * - * return close index on success - * return -1 on failure. - */ -static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, int groupnumber, uint8_t closest, uint8_t lock) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - uint16_t i, ind = MAX_GROUP_CONNECTIONS; - - for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type == GROUPCHAT_CLOSE_NONE) { - ind = i; - continue; - } - - if (g->close[i].number == (uint32_t)friendcon_id) { - g->close[i].closest = closest; - return i; /* Already in list. */ - } - } - - if (ind == MAX_GROUP_CONNECTIONS) { - return -1; - } - - if (lock) { - friend_connection_lock(g_c->fr_c, friendcon_id); - } - - g->close[ind].type = GROUPCHAT_CLOSE_CONNECTION; - g->close[ind].number = friendcon_id; - g->close[ind].closest = closest; - // 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); - - return ind; -} - -/* Creates a new groupchat and puts it in the chats array. - * - * type is one of GROUPCHAT_TYPE_* - * - * return group number on success. - * return -1 on failure. - */ -int add_groupchat(Group_Chats *g_c, uint8_t type) -{ - int groupnumber = create_group_chat(g_c); - - if (groupnumber == -1) { - return -1; - } - - Group_c *g = &g_c->chats[groupnumber]; - - g->status = GROUPCHAT_STATUS_CONNECTED; - g->number_joined = -1; - new_symmetric_key(g->identifier + 1); - g->identifier[0] = type; - g->peer_number = 0; /* Founder is peer 0. */ - memcpy(g->real_pk, g_c->m->net_crypto->self_public_key, CRYPTO_PUBLIC_KEY_SIZE); - int peer_index = addpeer(g_c, groupnumber, g->real_pk, g_c->m->dht->self_public_key, 0, NULL, false); - - if (peer_index == -1) { - return -1; - } - - setnick(g_c, groupnumber, peer_index, g_c->m->name, g_c->m->name_length, NULL, false); - - return groupnumber; -} - -static int group_kill_peer_send(const Group_Chats *g_c, int groupnumber, uint16_t peer_num); -/* Delete a groupchat from the chats array. - * - * return 0 on success. - * return -1 if groupnumber is invalid. - */ -int del_groupchat(Group_Chats *g_c, int groupnumber) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - group_kill_peer_send(g_c, groupnumber, g->peer_number); - - unsigned int i; - - for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type == GROUPCHAT_CLOSE_NONE) { - continue; - } - - g->close[i].type = GROUPCHAT_CLOSE_NONE; - kill_friend_connection(g_c->fr_c, g->close[i].number); - } - - for (i = 0; i < g->numpeers; ++i) { - if (g->peer_on_leave) { - g->peer_on_leave(g->object, groupnumber, i, g->group[i].object); - } - } - - free(g->group); - - if (g->group_on_delete) { - g->group_on_delete(g->object, groupnumber); - } - - return wipe_group_chat(g_c, groupnumber); -} - -/* Copy the public key of peernumber who is in groupnumber to pk. - * pk must be CRYPTO_PUBLIC_KEY_SIZE long. - * - * return 0 on success - * return -1 if groupnumber is invalid. - * return -2 if peernumber is invalid. - */ -int group_peer_pubkey(const Group_Chats *g_c, int groupnumber, int peernumber, uint8_t *pk) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - if ((uint32_t)peernumber >= g->numpeers) { - return -2; - } - - memcpy(pk, g->group[peernumber].real_pk, CRYPTO_PUBLIC_KEY_SIZE); - return 0; -} - -/* - * Return the size of peernumber's name. - * - * return -1 if groupnumber is invalid. - * return -2 if peernumber is invalid. - */ -int group_peername_size(const Group_Chats *g_c, int groupnumber, int peernumber) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - if ((uint32_t)peernumber >= g->numpeers) { - return -2; - } - - if (g->group[peernumber].nick_len == 0) { - return 8; - } - - return g->group[peernumber].nick_len; -} - -/* Copy the name of peernumber who is in groupnumber to name. - * name must be at least MAX_NAME_LENGTH long. - * - * return length of name if success - * return -1 if groupnumber is invalid. - * return -2 if peernumber is invalid. - */ -int group_peername(const Group_Chats *g_c, int groupnumber, int peernumber, uint8_t *name) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - if ((uint32_t)peernumber >= g->numpeers) { - return -2; - } - - if (g->group[peernumber].nick_len == 0) { - memcpy(name, "Tox User", 8); - return 8; - } - - memcpy(name, g->group[peernumber].nick, g->group[peernumber].nick_len); - return g->group[peernumber].nick_len; -} - -/* List all the peers in the group chat. - * - * Copies the names of the peers to the name[length][MAX_NAME_LENGTH] array. - * - * Copies the lengths of the names to lengths[length] - * - * returns the number of peers on success. - * - * return -1 on failure. - */ -int group_names(const Group_Chats *g_c, int groupnumber, uint8_t names[][MAX_NAME_LENGTH], uint16_t lengths[], - uint16_t length) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - unsigned int i; - - for (i = 0; i < g->numpeers && i < length; ++i) { - lengths[i] = group_peername(g_c, groupnumber, i, names[i]); - } - - return i; -} - -/* Return the number of peers in the group chat on success. - * return -1 if groupnumber is invalid. - */ -int group_number_peers(const Group_Chats *g_c, int groupnumber) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - return g->numpeers; -} - -/* return 1 if the peernumber corresponds to ours. - * return 0 if the peernumber is not ours. - * return -1 if groupnumber is invalid. - * 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, int groupnumber, int peernumber) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - if ((uint32_t)peernumber >= g->numpeers) { - return -2; - } - - if (g->status != GROUPCHAT_STATUS_CONNECTED) { - return -3; - } - - return g->peer_number == g->group[peernumber].peer_number; -} - -/* return the type of groupchat (GROUPCHAT_TYPE_) that groupnumber is. - * - * return -1 on failure. - * return type on success. - */ -int group_get_type(const Group_Chats *g_c, int groupnumber) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - return g->identifier[0]; -} - -/* Send a group packet to friendcon_id. - * - * return 1 on success - * return 0 on failure - */ -static unsigned int send_packet_group_peer(Friend_Connections *fr_c, int friendcon_id, uint8_t packet_id, - uint16_t group_num, const uint8_t *data, uint16_t length) -{ - if (1 + sizeof(uint16_t) + length > MAX_CRYPTO_DATA_SIZE) { - return 0; - } - - group_num = net_htons(group_num); - VLA(uint8_t, packet, 1 + sizeof(uint16_t) + length); - packet[0] = packet_id; - memcpy(packet + 1, &group_num, sizeof(uint16_t)); - memcpy(packet + 1 + sizeof(uint16_t), data, length); - return write_cryptpacket(fr_c->net_crypto, friend_connection_crypt_connection_id(fr_c, friendcon_id), packet, - SIZEOF_VLA(packet), 0) != -1; -} - -/* Send a group lossy packet to friendcon_id. - * - * return 1 on success - * return 0 on failure - */ -static unsigned int send_lossy_group_peer(Friend_Connections *fr_c, int friendcon_id, uint8_t packet_id, - uint16_t group_num, const uint8_t *data, uint16_t length) -{ - if (1 + sizeof(uint16_t) + length > MAX_CRYPTO_DATA_SIZE) { - return 0; - } - - group_num = net_htons(group_num); - VLA(uint8_t, packet, 1 + sizeof(uint16_t) + length); - packet[0] = packet_id; - memcpy(packet + 1, &group_num, sizeof(uint16_t)); - memcpy(packet + 1 + sizeof(uint16_t), data, length); - return send_lossy_cryptpacket(fr_c->net_crypto, friend_connection_crypt_connection_id(fr_c, friendcon_id), packet, - SIZEOF_VLA(packet)) != -1; -} - -#define INVITE_PACKET_SIZE (1 + sizeof(uint16_t) + GROUP_IDENTIFIER_LENGTH) -#define INVITE_ID 0 - -#define INVITE_RESPONSE_PACKET_SIZE (1 + sizeof(uint16_t) * 2 + GROUP_IDENTIFIER_LENGTH) -#define INVITE_RESPONSE_ID 1 - -/* invite friendnumber to groupnumber. - * - * return 0 on success. - * return -1 if groupnumber is invalid. - * return -2 if invite packet failed to send. - */ -int invite_friend(Group_Chats *g_c, int32_t friendnumber, int groupnumber) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - uint8_t invite[INVITE_PACKET_SIZE]; - invite[0] = INVITE_ID; - uint16_t groupchat_num = net_htons((uint16_t)groupnumber); - memcpy(invite + 1, &groupchat_num, sizeof(groupchat_num)); - memcpy(invite + 1 + sizeof(groupchat_num), g->identifier, GROUP_IDENTIFIER_LENGTH); - - if (send_conference_invite_packet(g_c->m, friendnumber, invite, sizeof(invite))) { - return 0; - } - - wipe_group_chat(g_c, groupnumber); - return -2; -} - -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.) - * - * expected_type is the groupchat type we expect the chat we are joining is. - * - * return group number on success. - * return -1 if data length is invalid. - * return -2 if group is not the expected type. - * return -3 if friendnumber is invalid. - * return -4 if client is already in this group. - * return -5 if group instance failed to initialize. - * return -6 if join packet fails to send. - */ -int join_groupchat(Group_Chats *g_c, int32_t friendnumber, uint8_t expected_type, const uint8_t *data, uint16_t length) -{ - if (length != sizeof(uint16_t) + GROUP_IDENTIFIER_LENGTH) { - return -1; - } - - if (data[sizeof(uint16_t)] != expected_type) { - return -2; - } - - int friendcon_id = getfriendcon_id(g_c->m, friendnumber); - - if (friendcon_id == -1) { - return -3; - } - - if (get_group_num(g_c, data + sizeof(uint16_t)) != -1) { - return -4; - } - - int groupnumber = create_group_chat(g_c); - - if (groupnumber == -1) { - return -5; - } - - Group_c *g = &g_c->chats[groupnumber]; - - uint16_t group_num = net_htons(groupnumber); - g->status = GROUPCHAT_STATUS_VALID; - g->number_joined = -1; - memcpy(g->real_pk, g_c->m->net_crypto->self_public_key, 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) + GROUP_IDENTIFIER_LENGTH); - - 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); - memcpy(g->identifier, data + sizeof(uint16_t), GROUP_IDENTIFIER_LENGTH); - int close_index = add_conn_to_groupchat(g_c, friendcon_id, groupnumber, 0, 1); - - if (close_index != -1) { - g->close[close_index].group_number = other_groupnum; - g->close[close_index].type = GROUPCHAT_CLOSE_ONLINE; - g->number_joined = friendcon_id; - } - - send_peer_query(g_c, friendcon_id, other_groupnum); - return groupnumber; - } - - g->status = GROUPCHAT_STATUS_NONE; - return -6; -} - -/* Set handlers for custom lossy packets. - * - * NOTE: Handler must return 0 if packet is to be relayed, -1 if the packet should not be relayed. - * - * Function(void *group object (set with group_set_object), int groupnumber, int friendgroupnumber, void *group peer object (set with group_peer_set_object), const uint8_t *packet, uint16_t length) - */ -void group_lossy_packet_registerhandler(Group_Chats *g_c, uint8_t byte, int (*function)(void *, int, int, void *, - const uint8_t *, uint16_t)) -{ - g_c->lossy_packethandlers[byte].function = function; -} - -/* Set the callback for group invites. - * - * Function(Group_Chats *g_c, int32_t friendnumber, uint8_t type, uint8_t *data, uint16_t length, void *userdata) - * - * data of length is what needs to be passed to join_groupchat(). - */ -void g_callback_group_invite(Group_Chats *g_c, void (*function)(Messenger *m, uint32_t, int, const uint8_t *, - size_t, void *)) -{ - g_c->invite_callback = function; -} - -/* Set the callback for group messages. - * - * Function(Group_Chats *g_c, int groupnumber, int friendgroupnumber, uint8_t * message, uint16_t length, void *userdata) - */ -void g_callback_group_message(Group_Chats *g_c, void (*function)(Messenger *m, uint32_t, uint32_t, int, const uint8_t *, - size_t, void *)) -{ - g_c->message_callback = function; -} - -/* Set callback function for peer name list changes. - * - * It gets called every time the name list changes(new peer/name, deleted peer) - * Function(Group_Chats *g_c, int groupnumber, int peernumber, TOX_CHAT_CHANGE change, void *userdata) - */ -void g_callback_group_namelistchange(Group_Chats *g_c, void (*function)(Messenger *m, int, int, uint8_t, void *)) -{ - g_c->group_namelistchange = function; -} - -/* Set callback function for title changes. - * - * Function(Group_Chats *g_c, int groupnumber, int friendgroupnumber, uint8_t * title, uint8_t length, void *userdata) - * if friendgroupnumber == -1, then author is unknown (e.g. initial joining the group) - */ -void g_callback_group_title(Group_Chats *g_c, void (*function)(Messenger *m, uint32_t, uint32_t, const uint8_t *, - size_t, void *)) -{ - g_c->title_callback = function; -} - -/* Set a function to be called when a new peer joins a group chat. - * - * Function(void *group object (set with group_set_object), int groupnumber, int friendgroupnumber) - * - * return 0 on success. - * return -1 on failure. - */ -int callback_groupchat_peer_new(const Group_Chats *g_c, int groupnumber, void (*function)(void *, int, int)) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - g->peer_on_join = function; - return 0; -} - -/* Set a function to be called when a peer leaves a group chat. - * - * Function(void *group object (set with group_set_object), int groupnumber, int friendgroupnumber, void *group peer object (set with group_peer_set_object)) - * - * return 0 on success. - * return -1 on failure. - */ -int callback_groupchat_peer_delete(Group_Chats *g_c, int groupnumber, void (*function)(void *, int, int, void *)) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - g->peer_on_leave = function; - return 0; -} - -/* Set a function to be called when the group chat is deleted. - * - * Function(void *group object (set with group_set_object), int groupnumber) - * - * return 0 on success. - * return -1 on failure. - */ -int callback_groupchat_delete(Group_Chats *g_c, int groupnumber, void (*function)(void *, int)) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - g->group_on_delete = function; - return 0; -} - -static int send_message_group(const Group_Chats *g_c, int groupnumber, uint8_t message_id, const uint8_t *data, - uint16_t len); - -#define GROUP_MESSAGE_PING_ID 0 -static int group_ping_send(const Group_Chats *g_c, int groupnumber) -{ - if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_PING_ID, 0, 0) > 0) { - return 0; - } - - return -1; -} - -#define GROUP_MESSAGE_NEW_PEER_ID 16 -#define GROUP_MESSAGE_NEW_PEER_LENGTH (sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE * 2) -/* send a new_peer message - * return 0 on success - * return -1 on failure - */ -static int group_new_peer_send(const Group_Chats *g_c, int groupnumber, uint16_t peer_num, const uint8_t *real_pk, - uint8_t *temp_pk) -{ - uint8_t packet[GROUP_MESSAGE_NEW_PEER_LENGTH]; - - peer_num = net_htons(peer_num); - memcpy(packet, &peer_num, sizeof(uint16_t)); - memcpy(packet + sizeof(uint16_t), real_pk, CRYPTO_PUBLIC_KEY_SIZE); - 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 -1; -} - -#define GROUP_MESSAGE_KILL_PEER_ID 17 -#define GROUP_MESSAGE_KILL_PEER_LENGTH (sizeof(uint16_t)) - -/* send a kill_peer message - * return 0 on success - * return -1 on failure - */ -static int group_kill_peer_send(const Group_Chats *g_c, int groupnumber, uint16_t peer_num) -{ - uint8_t packet[GROUP_MESSAGE_KILL_PEER_LENGTH]; - - peer_num = net_htons(peer_num); - memcpy(packet, &peer_num, sizeof(uint16_t)); - - if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_KILL_PEER_ID, packet, sizeof(packet)) > 0) { - return 0; - } - - return -1; -} - -#define GROUP_MESSAGE_NAME_ID 48 - -/* send a name message - * return 0 on success - * return -1 on failure - */ -static int group_name_send(const Group_Chats *g_c, int groupnumber, const uint8_t *nick, uint16_t nick_len) -{ - if (nick_len > MAX_NAME_LENGTH) { - return -1; - } - - if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_NAME_ID, nick, nick_len) > 0) { - return 0; - } - - return -1; -} - -#define GROUP_MESSAGE_TITLE_ID 49 - -/* set the group's title, limited to MAX_NAME_LENGTH - * return 0 on success - * return -1 if groupnumber is invalid. - * return -2 if title is too long or empty. - * return -3 if packet fails to send. - */ -int group_title_send(const Group_Chats *g_c, int groupnumber, const uint8_t *title, uint8_t title_len) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - if (title_len > MAX_NAME_LENGTH || title_len == 0) { - return -2; - } - - /* same as already set? */ - if (g->title_len == title_len && !memcmp(g->title, title, title_len)) { - return 0; - } - - memcpy(g->title, title, title_len); - g->title_len = title_len; - - if (g->numpeers == 1) { - return 0; - } - - if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_TITLE_ID, title, title_len) > 0) { - return 0; - } - - return -3; -} - -/* return the group's title size. - * return -1 of groupnumber is invalid. - * return -2 if title is too long or empty. - */ -int group_title_get_size(const Group_Chats *g_c, int groupnumber) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - if (g->title_len == 0 || g->title_len > MAX_NAME_LENGTH) { - return -2; - } - - return g->title_len; -} - -/* Get group title from groupnumber and put it in title. - * Title needs to be a valid memory location with a size of at least MAX_NAME_LENGTH (128) bytes. - * - * return length of copied title if success. - * return -1 if groupnumber is invalid. - * return -2 if title is too long or empty. - */ -int group_title_get(const Group_Chats *g_c, int groupnumber, uint8_t *title) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - if (g->title_len == 0 || g->title_len > MAX_NAME_LENGTH) { - return -2; - } - - memcpy(title, g->title, g->title_len); - return g->title_len; -} - -static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, - void *userdata) -{ - Group_Chats *g_c = (Group_Chats *)m->conferences_object; - - if (length <= 1) { - return; - } - - const uint8_t *invite_data = data + 1; - uint16_t invite_length = length - 1; - - switch (data[0]) { - case INVITE_ID: { - if (length != INVITE_PACKET_SIZE) { - return; - } - - int groupnumber = get_group_num(g_c, data + 1 + sizeof(uint16_t)); - - if (groupnumber == -1) { - if (g_c->invite_callback) { - g_c->invite_callback(m, friendnumber, *(invite_data + sizeof(uint16_t)), invite_data, invite_length, userdata); - } - - return; - } - - break; - } - - case INVITE_RESPONSE_ID: { - if (length != INVITE_RESPONSE_PACKET_SIZE) { - return; - } - - uint16_t other_groupnum, groupnum; - memcpy(&groupnum, data + 1 + sizeof(uint16_t), sizeof(uint16_t)); - groupnum = net_ntohs(groupnum); - - Group_c *g = get_group_c(g_c, groupnum); - - if (!g) { - return; - } - - if (crypto_memcmp(data + 1 + sizeof(uint16_t) * 2, g->identifier, GROUP_IDENTIFIER_LENGTH) != 0) { - 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 = rand(); - - unsigned int tries = 0; - - while (get_peer_index(g, peer_number) != -1) { - peer_number = rand(); - ++tries; - - if (tries > 32) { - return; - } - } - - memcpy(&other_groupnum, data + 1, sizeof(uint16_t)); - other_groupnum = net_ntohs(other_groupnum); - - int friendcon_id = getfriendcon_id(m, friendnumber); - 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); - - addpeer(g_c, groupnum, real_pk, temp_pk, peer_number, userdata, true); - int close_index = add_conn_to_groupchat(g_c, friendcon_id, groupnum, 0, 1); - - if (close_index != -1) { - g->close[close_index].group_number = other_groupnum; - g->close[close_index].type = GROUPCHAT_CLOSE_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; - * - * returns index on success - * returns -1 on failure. - */ -static int friend_in_close(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) { - continue; - } - - if (g->close[i].number != (uint32_t)friendcon_id) { - continue; - } - - return i; - } - - return -1; -} - -/* return number of connected close connections. - */ -static unsigned int count_close_connected(Group_c *g) -{ - unsigned int i, count = 0; - - for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type == GROUPCHAT_CLOSE_ONLINE) { - ++count; - } - } - - return count; -} - -#define ONLINE_PACKET_DATA_SIZE (sizeof(uint16_t) + GROUP_IDENTIFIER_LENGTH) - -static int send_packet_online(Friend_Connections *fr_c, int friendcon_id, uint16_t group_num, uint8_t *identifier) -{ - uint8_t packet[1 + ONLINE_PACKET_DATA_SIZE]; - group_num = net_htons(group_num); - packet[0] = PACKET_ID_ONLINE_PACKET; - memcpy(packet + 1, &group_num, sizeof(uint16_t)); - memcpy(packet + 1 + sizeof(uint16_t), identifier, GROUP_IDENTIFIER_LENGTH); - return write_cryptpacket(fr_c->net_crypto, friend_connection_crypt_connection_id(fr_c, friendcon_id), packet, - sizeof(packet), 0) != -1; -} - -static unsigned int send_peer_kill(Group_Chats *g_c, int friendcon_id, uint16_t group_num); - -static int handle_packet_online(Group_Chats *g_c, int friendcon_id, const uint8_t *data, uint16_t length) -{ - if (length != ONLINE_PACKET_DATA_SIZE) { - return -1; - } - - int groupnumber = get_group_num(g_c, data + sizeof(uint16_t)); - uint16_t other_groupnum; - memcpy(&other_groupnum, data, sizeof(uint16_t)); - other_groupnum = net_ntohs(other_groupnum); - - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - int index = friend_in_close(g, friendcon_id); - - if (index == -1) { - return -1; - } - - if (g->close[index].type == GROUPCHAT_CLOSE_ONLINE) { - return -1; - } - - if (count_close_connected(g) == 0) { - send_peer_query(g_c, friendcon_id, other_groupnum); - } - - g->close[index].group_number = other_groupnum; - g->close[index].type = GROUPCHAT_CLOSE_ONLINE; - send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->identifier); - - if (g->number_joined != -1 && count_close_connected(g) >= DESIRED_CLOSE_CONNECTIONS) { - int fr_close_index = friend_in_close(g, g->number_joined); - - if (fr_close_index == -1) { - return -1; - } - - if (!g->close[fr_close_index].closest) { - g->close[fr_close_index].type = GROUPCHAT_CLOSE_NONE; - send_peer_kill(g_c, g->close[fr_close_index].number, g->close[fr_close_index].group_number); - kill_friend_connection(g_c->fr_c, g->close[fr_close_index].number); - g->number_joined = -1; - } - } - - return 0; -} - -#define PEER_KILL_ID 1 -#define PEER_QUERY_ID 8 -#define PEER_RESPONSE_ID 9 -#define PEER_TITLE_ID 10 -// we could send title with invite, but then if it changes between sending and accepting inv, joinee won't see it - -/* return 1 on success. - * return 0 on failure - */ -static unsigned int send_peer_kill(Group_Chats *g_c, int friendcon_id, uint16_t group_num) -{ - uint8_t packet[1]; - packet[0] = PEER_KILL_ID; - return send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num, packet, sizeof(packet)); -} - - -/* return 1 on success. - * return 0 on failure - */ -static unsigned int send_peer_query(Group_Chats *g_c, int friendcon_id, uint16_t group_num) -{ - uint8_t packet[1]; - packet[0] = PEER_QUERY_ID; - return send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num, packet, sizeof(packet)); -} - -/* return number of peers sent on success. - * return 0 on failure. - */ -static unsigned int send_peers(Group_Chats *g_c, int groupnumber, int friendcon_id, uint16_t group_num) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - uint8_t packet[MAX_CRYPTO_DATA_SIZE - (1 + sizeof(uint16_t))]; - packet[0] = PEER_RESPONSE_ID; - uint8_t *p = packet + 1; - - uint16_t sent = 0; - unsigned int i; - - for (i = 0; i < g->numpeers; ++i) { - if ((p - packet) + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE * 2 + 1 + g->group[i].nick_len > sizeof(packet)) { - if (send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num, packet, (p - packet))) { - sent = i; - } else { - return sent; - } - - p = packet + 1; - } - - uint16_t peer_num = net_htons(g->group[i].peer_number); - memcpy(p, &peer_num, sizeof(peer_num)); - p += sizeof(peer_num); - memcpy(p, g->group[i].real_pk, CRYPTO_PUBLIC_KEY_SIZE); - p += CRYPTO_PUBLIC_KEY_SIZE; - memcpy(p, g->group[i].temp_pk, CRYPTO_PUBLIC_KEY_SIZE); - p += CRYPTO_PUBLIC_KEY_SIZE; - *p = g->group[i].nick_len; - p += 1; - memcpy(p, g->group[i].nick, g->group[i].nick_len); - 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, packet, (p - packet))) { - sent = i; - } - } - - if (g->title_len) { - VLA(uint8_t, Packet, 1 + g->title_len); - Packet[0] = PEER_TITLE_ID; - memcpy(Packet + 1, g->title, g->title_len); - send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num, Packet, SIZEOF_VLA(Packet)); - } - - return sent; -} - -static int handle_send_peers(Group_Chats *g_c, int groupnumber, const uint8_t *data, uint16_t length, void *userdata) -{ - if (length == 0) { - return -1; - } - - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - const uint8_t *d = data; - - while ((unsigned int)(length - (d - data)) >= sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE * 2 + 1) { - uint16_t peer_num; - memcpy(&peer_num, d, sizeof(peer_num)); - peer_num = net_ntohs(peer_num); - d += sizeof(uint16_t); - int peer_index = addpeer(g_c, groupnumber, d, d + CRYPTO_PUBLIC_KEY_SIZE, peer_num, userdata, true); - - if (peer_index == -1) { - return -1; - } - - if (g->status == GROUPCHAT_STATUS_VALID - && public_key_cmp(d, g_c->m->net_crypto->self_public_key) == 0) { - g->peer_number = peer_num; - g->status = GROUPCHAT_STATUS_CONNECTED; - group_name_send(g_c, groupnumber, g_c->m->name, g_c->m->name_length); - } - - d += CRYPTO_PUBLIC_KEY_SIZE * 2; - uint8_t name_length = *d; - d += 1; - - if (name_length > (length - (d - data)) || name_length > MAX_NAME_LENGTH) { - return -1; - } - - setnick(g_c, groupnumber, peer_index, d, name_length, userdata, true); - d += name_length; - } - - return 0; -} - -static void handle_direct_packet(Group_Chats *g_c, int groupnumber, const uint8_t *data, uint16_t length, - int close_index, void *userdata) -{ - if (length == 0) { - return; - } - - switch (data[0]) { - case PEER_KILL_ID: { - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return; - } - - if (!g->close[close_index].closest) { - g->close[close_index].type = GROUPCHAT_CLOSE_NONE; - kill_friend_connection(g_c->fr_c, g->close[close_index].number); - } - } - - break; - - case PEER_QUERY_ID: { - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return; - } - - send_peers(g_c, groupnumber, g->close[close_index].number, g->close[close_index].group_number); - } - - break; - - case PEER_RESPONSE_ID: { - handle_send_peers(g_c, groupnumber, data + 1, length - 1, userdata); - } - - break; - - case PEER_TITLE_ID: { - settitle(g_c, groupnumber, -1, data + 1, length - 1, userdata); - } - - break; - } -} - -#define MIN_MESSAGE_PACKET_LEN (sizeof(uint16_t) * 2 + sizeof(uint32_t) + 1) - -/* Send message to all close 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, int groupnumber, const uint8_t *data, - uint16_t length, int receiver) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return 0; - } - - uint16_t i, sent = 0; - - for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type != GROUPCHAT_CLOSE_ONLINE) { - continue; - } - - if ((int)i == receiver) { - 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)) { - ++sent; - } - } - - return sent; -} - -/* Send lossy message to all close 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, int groupnumber, const uint8_t *data, uint16_t length, - int receiver) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - 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) { - continue; - } - - if ((int)i == receiver) { - continue; - } - - if (g->close[i].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)) { - ++sent; - } - } - - if (!num_connected_closest) { - return sent; - } - - unsigned int to_send = 0; - uint64_t comp_val_old = ~0; - - for (i = 0; i < num_connected_closest; ++i) { - 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[connected_closest[i]].number); - 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; - } - } - - 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]; - uint8_t dht_temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; - get_friendcon_public_keys(real_pk, dht_temp_pk, g_c->fr_c, g->close[connected_closest[i]].number); - 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; - } - } - - 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; - } - - return sent; -} - -#define MAX_GROUP_MESSAGE_DATA_LEN (MAX_CRYPTO_DATA_SIZE - (1 + MIN_MESSAGE_PACKET_LEN)) - -/* Send data of len with message_id to groupnumber. - * - * return number of peers it was sent to on success. - * return -1 if groupnumber is invalid. - * return -2 if message is too long. - * return -3 if we are not connected to the group. - * reutrn -4 if message failed to send. - */ -static int send_message_group(const Group_Chats *g_c, int groupnumber, uint8_t message_id, const uint8_t *data, - uint16_t len) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - if (len > MAX_GROUP_MESSAGE_DATA_LEN) { - return -2; - } - - if (g->status != GROUPCHAT_STATUS_CONNECTED) { - return -3; - } - - VLA(uint8_t, packet, sizeof(uint16_t) + sizeof(uint32_t) + 1 + len); - uint16_t peer_num = net_htons(g->peer_number); - memcpy(packet, &peer_num, sizeof(peer_num)); - - ++g->message_number; - - if (!g->message_number) { - ++g->message_number; - } - - uint32_t message_num = net_htonl(g->message_number); - memcpy(packet + sizeof(uint16_t), &message_num, sizeof(message_num)); - - packet[sizeof(uint16_t) + sizeof(uint32_t)] = message_id; - - if (len) { - 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); - - return (ret == 0) ? -4 : ret; -} - -/* send a group message - * return 0 on success - * see: send_message_group() for error codes. - */ -int group_message_send(const Group_Chats *g_c, int groupnumber, const uint8_t *message, uint16_t length) -{ - int ret = send_message_group(g_c, groupnumber, PACKET_ID_MESSAGE, message, length); - - if (ret > 0) { - return 0; - } - - return ret; -} - -/* send a group action - * return 0 on success - * see: send_message_group() for error codes. - */ -int group_action_send(const Group_Chats *g_c, int groupnumber, const uint8_t *action, uint16_t length) -{ - int ret = send_message_group(g_c, groupnumber, PACKET_ID_ACTION, action, length); - - if (ret > 0) { - return 0; - } - - return ret; -} - -/* High level function to send custom lossy packets. - * - * return -1 on failure. - * return 0 on success. - */ -int send_group_lossy_packet(const Group_Chats *g_c, int groupnumber, const uint8_t *data, uint16_t length) -{ - // TODO(irungentoo): length check here? - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - VLA(uint8_t, packet, sizeof(uint16_t) * 2 + length); - uint16_t peer_number = net_htons(g->peer_number); - memcpy(packet, &peer_number, sizeof(uint16_t)); - uint16_t message_num = net_htons(g->lossy_message_number); - 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) { - return -1; - } - - ++g->lossy_message_number; - return 0; -} - -static void handle_message_packet_group(Group_Chats *g_c, int groupnumber, const uint8_t *data, uint16_t length, - int close_index, void *userdata) -{ - if (length < sizeof(uint16_t) + sizeof(uint32_t) + 1) { - return; - } - - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return; - } - - uint16_t peer_number; - memcpy(&peer_number, data, sizeof(uint16_t)); - peer_number = net_ntohs(peer_number); - - int index = get_peer_index(g, peer_number); - - if (index == -1) { - /* We don't know the peer this packet came from so we query the list of peers from that 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); - return; - } - - uint32_t message_number; - memcpy(&message_number, data + sizeof(uint16_t), sizeof(message_number)); - message_number = net_ntohl(message_number); - - if (g->group[index].last_message_number == 0) { - g->group[index].last_message_number = message_number; - } else if (message_number - g->group[index].last_message_number > 64 || - message_number == g->group[index].last_message_number) { - return; - } - - g->group[index].last_message_number = message_number; - - uint8_t message_id = data[sizeof(uint16_t) + sizeof(message_number)]; - const uint8_t *msg_data = data + sizeof(uint16_t) + sizeof(message_number) + 1; - uint16_t msg_data_len = length - (sizeof(uint16_t) + sizeof(message_number) + 1); - - switch (message_id) { - case GROUP_MESSAGE_PING_ID: { - if (msg_data_len != 0) { - return; - } - - g->group[index].last_recv = unix_time(); - } - break; - - case GROUP_MESSAGE_NEW_PEER_ID: { - if (msg_data_len != GROUP_MESSAGE_NEW_PEER_LENGTH) { - return; - } - - uint16_t new_peer_number; - memcpy(&new_peer_number, msg_data, sizeof(uint16_t)); - new_peer_number = net_ntohs(new_peer_number); - addpeer(g_c, groupnumber, msg_data + sizeof(uint16_t), msg_data + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE, - new_peer_number, userdata, true); - } - break; - - case GROUP_MESSAGE_KILL_PEER_ID: { - if (msg_data_len != GROUP_MESSAGE_KILL_PEER_LENGTH) { - return; - } - - uint16_t kill_peer_number; - memcpy(&kill_peer_number, msg_data, sizeof(uint16_t)); - kill_peer_number = net_ntohs(kill_peer_number); - - if (peer_number == kill_peer_number) { - delpeer(g_c, groupnumber, index, userdata); - } else { - return; - // TODO(irungentoo): - } - } - break; - - case GROUP_MESSAGE_NAME_ID: { - if (setnick(g_c, groupnumber, index, msg_data, msg_data_len, userdata, true) == -1) { - return; - } - } - break; - - case GROUP_MESSAGE_TITLE_ID: { - if (settitle(g_c, groupnumber, index, msg_data, msg_data_len, userdata) == -1) { - return; - } - } - break; - - case PACKET_ID_MESSAGE: { - if (msg_data_len == 0) { - return; - } - - VLA(uint8_t, newmsg, msg_data_len + 1); - memcpy(newmsg, msg_data, msg_data_len); - newmsg[msg_data_len] = 0; - - // TODO(irungentoo): - if (g_c->message_callback) { - g_c->message_callback(g_c->m, groupnumber, index, 0, newmsg, msg_data_len, userdata); - } - - break; - } - - case PACKET_ID_ACTION: { - if (msg_data_len == 0) { - return; - } - - VLA(uint8_t, newmsg, msg_data_len + 1); - memcpy(newmsg, msg_data, msg_data_len); - newmsg[msg_data_len] = 0; - - // TODO(irungentoo): - if (g_c->message_callback) { - g_c->message_callback(g_c->m, groupnumber, index, 1, newmsg, msg_data_len, userdata); - } - - break; - } - - default: - return; - } - - send_message_all_close(g_c, groupnumber, data, length, -1/* TODO(irungentoo) close_index */); -} - -static int g_handle_packet(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata) -{ - Group_Chats *g_c = (Group_Chats *)object; - - if (length < 1 + sizeof(uint16_t) + 1) { - return -1; - } - - if (data[0] == PACKET_ID_ONLINE_PACKET) { - return handle_packet_online(g_c, friendcon_id, data + 1, length - 1); - } - - 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); - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - int index = friend_in_close(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; - } - - default: { - return 0; - } - } - - return 0; -} - -/* Did we already receive the lossy packet or not. - * - * return -1 on failure. - * return 0 if packet was not received. - * return 1 if packet was received. - * - * TODO(irungentoo): test this - */ -static unsigned int lossy_packet_not_received(Group_c *g, int peer_index, uint16_t message_number) -{ - if (peer_index == -1) { - return -1; - } - - if (g->group[peer_index].bottom_lossy_number == g->group[peer_index].top_lossy_number) { - 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; - } - - if ((uint16_t)(message_number - g->group[peer_index].bottom_lossy_number) < MAX_LOSSY_COUNT) { - if (g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT]) { - return 1; - } - - g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT] = 1; - return 0; - } - - if ((uint16_t)(message_number - g->group[peer_index].bottom_lossy_number) > (1 << 15)) { - return -1; - } - - uint16_t top_distance = message_number - g->group[peer_index].top_lossy_number; - - 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; - return 0; - } - - if (top_distance < MAX_LOSSY_COUNT) { - unsigned int i; - - for (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; - return 0; - } - - return -1; -} - -static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata) -{ - Group_Chats *g_c = (Group_Chats *)object; - - if (length < 1 + sizeof(uint16_t) * 3 + 1) { - return -1; - } - - if (data[0] != PACKET_ID_LOSSY_CONFERENCE) { - return -1; - } - - uint16_t groupnumber, peer_number, message_number; - memcpy(&groupnumber, data + 1, sizeof(uint16_t)); - memcpy(&peer_number, data + 1 + sizeof(uint16_t), sizeof(uint16_t)); - memcpy(&message_number, data + 1 + sizeof(uint16_t) * 2, sizeof(uint16_t)); - groupnumber = net_ntohs(groupnumber); - peer_number = net_ntohs(peer_number); - message_number = net_ntohs(message_number); - - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - int index = friend_in_close(g, friendcon_id); - - if (index == -1) { - return -1; - } - - if (peer_number == g->peer_number) { - return -1; - } - - int peer_index = get_peer_index(g, peer_number); - - if (peer_index == -1) { - return -1; - } - - if (lossy_packet_not_received(g, peer_index, message_number)) { - return -1; - } - - const uint8_t *lossy_data = data + 1 + sizeof(uint16_t) * 3; - uint16_t lossy_length = length - (1 + sizeof(uint16_t) * 3); - uint8_t message_id = lossy_data[0]; - ++lossy_data; - --lossy_length; - - 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 { - return -1; - } - - send_lossy_all_close(g_c, groupnumber, data + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), index); - return 0; -} - -/* Set the object that is tied to the group chat. - * - * return 0 on success. - * return -1 on failure - */ -int group_set_object(const Group_Chats *g_c, int groupnumber, void *object) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - g->object = object; - return 0; -} - -/* Set the object that is tied to the group peer. - * - * return 0 on success. - * return -1 on failure - */ -int group_peer_set_object(const Group_Chats *g_c, int groupnumber, int peernumber, void *object) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - if ((uint32_t)peernumber >= g->numpeers) { - return -1; - } - - g->group[peernumber].object = object; - return 0; -} - -/* Return the object tide to the group chat previously set by group_set_object. - * - * return NULL on failure. - * return object on success. - */ -void *group_get_object(const Group_Chats *g_c, int groupnumber) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return NULL; - } - - return g->object; -} - -/* Return the object tide to the group chat peer previously set by group_peer_set_object. - * - * return NULL on failure. - * return object on success. - */ -void *group_peer_get_object(const Group_Chats *g_c, int groupnumber, int peernumber) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return NULL; - } - - if ((uint32_t)peernumber >= g->numpeers) { - return NULL; - } - - return g->group[peernumber].object; -} - -/* Interval in seconds to send ping messages */ -#define GROUP_PING_INTERVAL 20 - -static int ping_groupchat(Group_Chats *g_c, int groupnumber) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - if (is_timeout(g->last_sent_ping, GROUP_PING_INTERVAL)) { - if (group_ping_send(g_c, groupnumber) != -1) { /* Ping */ - g->last_sent_ping = unix_time(); - } - } - - return 0; -} - -static int groupchat_clear_timedout(Group_Chats *g_c, int groupnumber, void *userdata) -{ - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) { - return -1; - } - - uint32_t i; - - for (i = 0; i < g->numpeers; ++i) { - if (g->peer_number != g->group[i].peer_number && is_timeout(g->group[i].last_recv, GROUP_PING_INTERVAL * 3)) { - delpeer(g_c, groupnumber, i, userdata); - } - - if (g->group == NULL || i >= g->numpeers) { - break; - } - } - - return 0; -} - -/* Send current name (set in messenger) to all online groups. - */ -void send_name_all_groups(Group_Chats *g_c) -{ - unsigned int i; - - for (i = 0; i < g_c->num_chats; ++i) { - Group_c *g = get_group_c(g_c, i); - - if (!g) { - continue; - } - - if (g->status == GROUPCHAT_STATUS_CONNECTED) { - group_name_send(g_c, i, g_c->m->name, g_c->m->name_length); - } - } -} - -/* Create new groupchat instance. */ -Group_Chats *new_groupchats(Messenger *m) -{ - if (!m) { - return NULL; - } - - Group_Chats *temp = (Group_Chats *)calloc(1, sizeof(Group_Chats)); - - if (temp == NULL) { - return NULL; - } - - temp->m = m; - temp->fr_c = m->fr_c; - m->conferences_object = temp; - m_callback_conference_invite(m, &handle_friend_invite_packet); - - return temp; -} - -/* main groupchats loop. */ -void do_groupchats(Group_Chats *g_c, void *userdata) -{ - unsigned int i; - - for (i = 0; i < g_c->num_chats; ++i) { - Group_c *g = get_group_c(g_c, i); - - if (!g) { - continue; - } - - if (g->status == GROUPCHAT_STATUS_CONNECTED) { - connect_to_closest(g_c, i, userdata); - ping_groupchat(g_c, i); - groupchat_clear_timedout(g_c, i, userdata); - } - } - - // TODO(irungentoo): -} - -/* Free everything related with group chats. */ -void kill_groupchats(Group_Chats *g_c) -{ - unsigned int i; - - for (i = 0; i < g_c->num_chats; ++i) { - del_groupchat(g_c, i); - } - - m_callback_conference_invite(g_c->m, NULL); - g_c->m->conferences_object = NULL; - free(g_c); -} - -/* Return the number of chats in the instance m. - * You should use this to determine how much memory to allocate - * for copy_chatlist. - */ -uint32_t count_chatlist(Group_Chats *g_c) -{ - uint32_t ret = 0; - uint32_t i; - - for (i = 0; i < g_c->num_chats; i++) { - if (g_c->chats[i].status != GROUPCHAT_STATUS_NONE) { - ret++; - } - } - - return ret; -} - -/* Copy a list of valid chat IDs into the array out_list. - * If out_list is NULL, returns 0. - * Otherwise, returns the number of elements copied. - * If the array was too small, the contents - * of out_list will be truncated to list_size. */ -uint32_t copy_chatlist(Group_Chats *g_c, uint32_t *out_list, uint32_t list_size) -{ - if (!out_list) { - return 0; - } - - if (g_c->num_chats == 0) { - return 0; - } - - uint32_t i, ret = 0; - - for (i = 0; i < g_c->num_chats; ++i) { - if (ret >= list_size) { - break; /* Abandon ship */ - } - - if (g_c->chats[i].status > GROUPCHAT_STATUS_NONE) { - out_list[ret] = i; - ret++; - } - } - - return ret; -} |