/* Send data of len with message_id to groupnumber. * * return number of peers it was sent to on success. * return 0 on failure. */ static unsigned int send_message_group(const Group_Chats *g_c, int groupnumber, uint8_t message_id, const uint8_t *data, uint16_t len) { if (len > MAX_GROUP_MESSAGE_DATA_LEN) return 0; Group_c *g = get_group_c(g_c, groupnumber); if (!g) return 0; if (g->status != GROUPCHAT_STATUS_CONNECTED) return 0; uint8_t packet[sizeof(uint16_t) + sizeof(uint32_t) + 1 + len]; uint16_t peer_num = 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 = 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); return send_message_all_close(g_c, groupnumber, packet, sizeof(packet), -1); }
static void handle_direct_packet(Group_Chats *g_c, int groupnumber, const uint8_t *data, uint16_t length, int close_index) { if (length == 0) return; switch (data[0]) { 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); } break; } }
/* 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) { 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) { return i; /* Already in list. */ } break; } if (ind == MAX_GROUP_CONNECTIONS) return -1; 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 friend_connection_callbacks(g_c->m->fr_c, friendcon_id, GROUPCHAT_CALLBACK_INDEX, &handle_status, &handle_packet, 0, g_c, friendcon_id); return ind; }
static int handle_send_peers(Group_Chats *g_c, int groupnumber, const uint8_t *data, uint16_t length) { if (length == 0) return -1; if (length % (sizeof(uint16_t) + crypto_box_PUBLICKEYBYTES * 2) != 0) return -1; Group_c *g = get_group_c(g_c, groupnumber); if (!g) return -1; unsigned int i; const uint8_t *d = data; while ((length - (d - data)) >= sizeof(uint16_t) + crypto_box_PUBLICKEYBYTES * 2) { uint16_t peer_num; memcpy(&peer_num, d, sizeof(peer_num)); peer_num = ntohs(peer_num); d += sizeof(uint16_t); addpeer(g_c, groupnumber, d, d + crypto_box_PUBLICKEYBYTES, peer_num); if (g->status == GROUPCHAT_STATUS_VALID && memcmp(d, g_c->m->net_crypto->self_public_key, crypto_box_PUBLICKEYBYTES) == 0) { g->peer_number = peer_num; g->status = GROUPCHAT_STATUS_CONNECTED; } d += crypto_box_PUBLICKEYBYTES * 2; } return 0; }
/* Return the number of peers in the group chat on success. * return -1 on failure */ 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; }
static int connect_to_closest(Group_Chats *g_c, int groupnumber) { Group_c *g = get_group_c(g_c, groupnumber); if (!g) return -1; if (!g->changed) return 0; unsigned int i; 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_box_PUBLICKEYBYTES]; uint8_t dht_temp_pk[crypto_box_PUBLICKEYBYTES]; 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); if (friendcon_id == -1) { friendcon_id = new_friend_connection(g_c->fr_c, g->closest_peers[i].real_pk); if (friendcon_id == -1) { continue; } set_dht_temp_pk(g_c->fr_c, friendcon_id, g->closest_peers[i].temp_pk); } add_conn_to_groupchat(g_c, friendcon_id, groupnumber, 1); 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 = 0; return 0; }
static void handle_direct_packet(Group_Chats *g_c, int groupnumber, const uint8_t *data, uint16_t length, int close_index) { 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); } } 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); } break; } }
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; }
/* * Add a peer to the group chat. * * 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) { Group_c *g = get_group_c(g_c, groupnumber); if (!g) return -1; //TODO 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; temp = 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 (g_c->peer_namelistchange) g_c->peer_namelistchange(g_c->m, groupnumber, g->numpeers - 1, CHAT_CHANGE_PEER_ADD, g_c->group_namelistchange_userdata); return (g->numpeers - 1); }
/* 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); } } }
/* 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; int i; for (i = 0; i < g->numpeers && i < length; ++i) { lengths[i] = group_peername(g_c, groupnumber, i, names[i]); } return i; }
/* main groupchats loop. */ void do_groupchats(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) connect_to_closest(g_c, i); } //TODO }
/* 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_box_PUBLICKEYBYTES * 2 + 1 + g->group[i].nick_len > sizeof(packet)) { if (send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_GROUPCHAT, group_num, packet, (p - packet))) { sent = i; } else { return sent; } p = packet + 1; } uint16_t peer_num = 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_box_PUBLICKEYBYTES); p += crypto_box_PUBLICKEYBYTES; memcpy(p, g->group[i].temp_pk, crypto_box_PUBLICKEYBYTES); p += crypto_box_PUBLICKEYBYTES; *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_GROUPCHAT, group_num, packet, (p - packet))) { sent = i; } } return sent; }
static int handle_packet_online(Group_Chats *g_c, int friendcon_id, 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 = 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; g->close[index].group_number = other_groupnum; g->close[index].type = GROUPCHAT_CLOSE_ONLINE; if (g->number_joined != -1 && g->number_joined != friendcon_id) { int fr_close_index = friend_in_close(g, g->number_joined); uint8_t real_pk[crypto_box_PUBLICKEYBYTES]; uint8_t dht_temp_pk[crypto_box_PUBLICKEYBYTES]; get_friendcon_public_keys(real_pk, dht_temp_pk, g_c->fr_c, g->number_joined); g->number_joined = -1; if (fr_close_index == -1) return -1; if (!g->close[fr_close_index].closest && pk_in_closest_peers(g, real_pk)) { g->close[fr_close_index].closest = 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); } } return 0; }
static int handle_packet(void *object, int friendcon_id, uint8_t *data, uint16_t length) { Group_Chats *g_c = 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_GROUPCHAT && data[0] != PACKET_ID_MESSAGE_GROUPCHAT) return -1; uint16_t groupnumber; memcpy(&groupnumber, data + 1, sizeof(uint16_t)); groupnumber = 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_GROUPCHAT: { handle_direct_packet(g_c, groupnumber, data + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), index); break; } case PACKET_ID_MESSAGE_GROUPCHAT: { handle_message_packet_group(g_c, groupnumber, data + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), index); break; } default: { return 0; } } return 0; }
/* 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 failure */ 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 -1; 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; }
static int handle_send_peers(Group_Chats *g_c, int groupnumber, const uint8_t *data, uint16_t length) { if (length == 0) return -1; Group_c *g = get_group_c(g_c, groupnumber); if (!g) return -1; unsigned int i; const uint8_t *d = data; while ((length - (d - data)) >= sizeof(uint16_t) + crypto_box_PUBLICKEYBYTES * 2 + 1) { uint16_t peer_num; memcpy(&peer_num, d, sizeof(peer_num)); peer_num = ntohs(peer_num); d += sizeof(uint16_t); int peer_index = addpeer(g_c, groupnumber, d, d + crypto_box_PUBLICKEYBYTES, peer_num); if (peer_index == -1) return -1; if (g->status == GROUPCHAT_STATUS_VALID && memcmp(d, g_c->m->net_crypto->self_public_key, crypto_box_PUBLICKEYBYTES) == 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_box_PUBLICKEYBYTES * 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); d += name_length; } return 0; }
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; unsigned int i; unsigned int index = DESIRED_CLOSE_CONNECTIONS; for (i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) { if (g->closest_peers[i].entry == 0) { index = i; break; } } if (index == DESIRED_CLOSE_CONNECTIONS) { uint16_t comp_val = calculate_comp_value(g->real_pk, real_pk); uint16_t comp_d = 0; for (i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) { uint16_t 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; } } } if (index == DESIRED_CLOSE_CONNECTIONS) { return -1; } g->closest_peers[index].entry = 1; memcpy(g->closest_peers[index].real_pk, real_pk, crypto_box_PUBLICKEYBYTES); memcpy(g->closest_peers[index].temp_pk, temp_pk, crypto_box_PUBLICKEYBYTES); g->changed = 1; return 0; }
/* invite friendnumber to groupnumber * return 0 on success * return -1 on failure */ 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 = 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_group_invite_packet(g_c->m, friendnumber, invite, sizeof(invite))) { return 0; } else { wipe_group_chat(g_c, groupnumber); return -1; } }
/* Delete a groupchat from the chats array. * * return 0 on success. * return -1 if failure. */ int del_groupchat(Group_Chats *g_c, int groupnumber) { Group_c *g = get_group_c(g_c, groupnumber); if (!g) return -1; 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); } free(g->group); return wipe_group_chat(g_c, groupnumber); }
static int groupchat_clear_timedout(Group_Chats *g_c, int groupnumber) { 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 * 2)) { delpeer(g_c, groupnumber, i); } if (g->group == NULL || i >= g->numpeers) break; } return 0; }
/* * 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) { 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; } } Group_Peer *temp; --g->numpeers; if (g->numpeers == 0) { free(g->group); g->group = NULL; return 0; } if (g->numpeers != (uint32_t)peer_index) memcpy(&g->group[peer_index], &g->group[g->numpeers], sizeof(Group_Peer)); temp = realloc(g->group, sizeof(Group_Peer) * (g->numpeers)); if (temp == NULL) return -1; g->group = temp; if (g_c->peer_namelistchange) g_c->peer_namelistchange(g_c->m, groupnumber, peer_index, CHAT_CHANGE_PEER_DEL, g_c->group_namelistchange_userdata); return 0; }
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 == friendcon_id) { g->close[i].type = GROUPCHAT_CLOSE_NONE; kill_friend_connection(g_c->fr_c, friendcon_id); return 0; } } return -1; }
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 != 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; } } }
static int setnick(Group_Chats *g_c, int groupnumber, int peer_index, const uint8_t *nick, uint16_t nick_len) { if (nick_len > MAX_NAME_LENGTH || nick_len == 0) 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 (!memcmp(g->group[peer_index].nick, nick, nick_len)) return 0; memcpy(g->group[peer_index].nick, nick, nick_len); g->group[peer_index].nick_len = nick_len; if (g_c->peer_namelistchange) g_c->peer_namelistchange(g_c->m, groupnumber, peer_index, CHAT_CHANGE_PEER_NAME, g_c->group_namelistchange_userdata); return 0; }
static int handle_packet_online(Group_Chats *g_c, int friendcon_id, 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 = 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; g->close[index].group_number = other_groupnum; g->close[index].type = GROUPCHAT_CLOSE_ONLINE; return 0; }
/* 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_GROUPCHAT, g->close[i].group_number, data, length)) ++sent; } return sent; }
static void handle_friend_invite_packet(Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length) { Group_Chats *g_c = m->group_chat_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, invite_length, g_c->invite_callback_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 = ntohs(groupnum); Group_c *g = get_group_c(g_c, groupnum); if (!g) return; if (memcmp(data + 1 + sizeof(uint16_t) * 2, g->identifier, GROUP_IDENTIFIER_LENGTH) != 0) return; uint16_t peer_number = rand(); /* TODO: what if two people enter the group at the same time and are given the same peer_number by different nodes? */ 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 = ntohs(other_groupnum); int friendcon_id = getfriendcon_id(m, friendnumber); uint8_t real_pk[crypto_box_PUBLICKEYBYTES], temp_pk[crypto_box_PUBLICKEYBYTES]; get_friendcon_public_keys(real_pk, temp_pk, g_c->fr_c, friendcon_id); addpeer(g_c, groupnum, real_pk, temp_pk, peer_number); 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; } }
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 (memcmp(g->real_pk, real_pk, crypto_box_PUBLICKEYBYTES) == 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 && memcmp(real_pk, g->closest_peers[i].real_pk, crypto_box_PUBLICKEYBYTES) == 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) { uint16_t comp_val = calculate_comp_value(g->real_pk, real_pk); uint16_t comp_d = 0; for (i = 0; i < (DESIRED_CLOSE_CONNECTIONS / 2); ++i) { uint16_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) { uint16_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_box_PUBLICKEYBYTES]; uint8_t old_temp_pk[crypto_box_PUBLICKEYBYTES]; uint8_t old = 0; if (g->closest_peers[index].entry) { memcpy(old_real_pk, g->closest_peers[index].real_pk, crypto_box_PUBLICKEYBYTES); memcpy(old_temp_pk, g->closest_peers[index].temp_pk, crypto_box_PUBLICKEYBYTES); old = 1; } g->closest_peers[index].entry = 1; memcpy(g->closest_peers[index].real_pk, real_pk, crypto_box_PUBLICKEYBYTES); memcpy(g->closest_peers[index].temp_pk, temp_pk, crypto_box_PUBLICKEYBYTES); 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 void handle_message_packet_group(Group_Chats *g_c, int groupnumber, const uint8_t *data, uint16_t length, int close_index) { 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 = ntohs(peer_number); int index = get_peer_index(g, peer_number); if (index == -1) return; uint32_t message_number; memcpy(&message_number, data + sizeof(uint16_t), sizeof(message_number)); message_number = 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 = ntohs(new_peer_number); addpeer(g_c, groupnumber, msg_data + sizeof(uint16_t), msg_data + sizeof(uint16_t) + crypto_box_PUBLICKEYBYTES, new_peer_number); } 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 = ntohs(kill_peer_number); if (peer_number == kill_peer_number) { delpeer(g_c, groupnumber, index); } else { //TODO } } break; case GROUP_MESSAGE_NAME_ID: { if (setnick(g_c, groupnumber, index, msg_data, msg_data_len) == -1) return; } break; case PACKET_ID_MESSAGE: { if (msg_data_len == 0) return; uint8_t newmsg[msg_data_len + 1]; memcpy(newmsg, msg_data, msg_data_len); newmsg[msg_data_len] = 0; //TODO if (g_c->message_callback) g_c->message_callback(g_c->m, groupnumber, index, newmsg, msg_data_len, g_c->message_callback_userdata); break; } default: return; } send_message_all_close(g_c, groupnumber, data, length, -1/*TODO close_index*/); }