diff --git a/testing/test_avatars.c b/testing/test_avatars.c index 6afbac1d..71a0330e 100644 --- a/testing/test_avatars.c +++ b/testing/test_avatars.c @@ -444,6 +444,7 @@ static void set_avatar(Tox *tox, const char *base_dir) tox_get_address(tox, addr); int i; + for (i = 0; ; i++) { if (avatar_formats[i].format == TOX_AVATAR_FORMAT_NONE) { tox_set_avatar(tox, TOX_AVATAR_FORMAT_NONE, NULL, 0); @@ -452,12 +453,14 @@ static void set_avatar(Tox *tox, const char *base_dir) } else { int ret = make_avatar_file_name(path, sizeof(path), base_dir, avatar_formats[i].format, addr); + if (ret < 0) { printf("Failed to generate avatar file name.\n"); return; } int len = load_avatar_data(path, buf); + if (len < 0) { printf("Failed to load avatar data from file: %s\n", path); continue; @@ -471,10 +474,12 @@ static void set_avatar(Tox *tox, const char *base_dir) ret = tox_set_avatar(tox, avatar_formats[i].format, buf, len); DEBUG("tox_set_avatar returned=%d", ret); + if (ret == 0) printf("Setting avatar from %s (%d bytes).\n", path, len); else printf("Error setting avatar from %s.\n", path); + return; } } diff --git a/toxcore/Messenger.h b/toxcore/Messenger.h index cc09afe3..42919a0d 100644 --- a/toxcore/Messenger.h +++ b/toxcore/Messenger.h @@ -58,7 +58,9 @@ #define PACKET_ID_FILE_CONTROL 81 #define PACKET_ID_FILE_DATA 82 #define PACKET_ID_INVITE_GROUPCHAT 96 -#define PACKET_ID_MESSAGE_GROUPCHAT 98 +#define PACKET_ID_ONLINE_PACKET 97 +#define PACKET_ID_DIRECT_GROUPCHAT 98 +#define PACKET_ID_MESSAGE_GROUPCHAT 99 /* Max number of tcp relays sent to friends */ diff --git a/toxcore/friend_connection.c b/toxcore/friend_connection.c index 1838c62d..7be4251a 100644 --- a/toxcore/friend_connection.c +++ b/toxcore/friend_connection.c @@ -419,7 +419,7 @@ int get_friendcon_public_keys(uint8_t *real_pk, uint8_t *dht_temp_pk, Friend_Con /* Set temp dht key for connection. */ -void set_dht_temp_pk(Friend_Connections *fr_c, int friendcon_id, uint8_t *dht_temp_pk) +void set_dht_temp_pk(Friend_Connections *fr_c, int friendcon_id, const uint8_t *dht_temp_pk) { dht_pk_callback(fr_c, friendcon_id, dht_temp_pk); } diff --git a/toxcore/friend_connection.h b/toxcore/friend_connection.h index 56401547..64280acb 100644 --- a/toxcore/friend_connection.h +++ b/toxcore/friend_connection.h @@ -122,7 +122,7 @@ int get_friendcon_public_keys(uint8_t *real_pk, uint8_t *dht_temp_pk, Friend_Con /* Set temp dht key for connection. */ -void set_dht_temp_pk(Friend_Connections *fr_c, int friendcon_id, uint8_t *dht_temp_pk); +void set_dht_temp_pk(Friend_Connections *fr_c, int friendcon_id, const uint8_t *dht_temp_pk); /* Set the callbacks for the friend connection. * index is the index (0 to (MAX_FRIEND_CONNECTION_CALLBACKS - 1)) we want the callback to set in the array. diff --git a/toxcore/group.c b/toxcore/group.c index ce590015..d5364420 100644 --- a/toxcore/group.c +++ b/toxcore/group.c @@ -130,7 +130,7 @@ static Group_c *get_group_c(const Group_Chats *g_c, int groupnumber) } /* - * check if peer with client_id is in peer array. + * 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. @@ -138,12 +138,12 @@ static Group_c *get_group_c(const Group_Chats *g_c, int groupnumber) * TODO: make this more efficient. */ -static int peer_in_chat(const Group_c *chat, const uint8_t *client_id) +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].client_id, client_id)) + if (id_equal(chat->group[i].real_pk, real_pk)) return i; return -1; @@ -176,7 +176,7 @@ static int get_group_num(const Group_Chats *g_c, const uint8_t *identifier) * * TODO: make this more efficient. */ -int get_peer_index(Group_c *g, uint16_t peer_number) +static int get_peer_index(Group_c *g, uint16_t peer_number) { uint32_t i; @@ -187,59 +187,274 @@ int get_peer_index(Group_c *g, uint16_t peer_number) return -1; } + +static uint16_t calculate_comp_value(const uint8_t *pk1, const uint8_t *pk2) +{ + uint8_t cmp1, cmp2; + + for (cmp1 = crypto_box_PUBLICKEYBYTES; cmp1 != 0; --cmp1) { + uint8_t index = crypto_box_PUBLICKEYBYTES - cmp1; + + if (pk1[index] == pk2[index]) + continue; + + cmp2 = abs((int)pk1[index] - (int)pk2[index]); + break; + } + + return (cmp1 << 8) + cmp2; +} + +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); + +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; +} + +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 (memcmp(g->closest_peers[i].real_pk, real_pk, crypto_box_PUBLICKEYBYTES) == 0) + return 1; + + } + + return 0; +} + +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); + } + + g->changed = 0; + + 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_c *chat, const uint8_t *client_id, uint16_t peer_number) +static int addpeer(Group_Chats *g_c, int groupnumber, const uint8_t *real_pk, const uint8_t *temp_pk, + uint16_t peer_number) { - //TODO - //int peer_index = peer_in_chat(chat, client_id); + Group_c *g = get_group_c(g_c, groupnumber); - //if (peer_index != -1) - // return peer_index; + 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(chat->group, sizeof(Group_Peer) * (chat->numpeers + 1)); + temp = realloc(g->group, sizeof(Group_Peer) * (g->numpeers + 1)); if (temp == NULL) return -1; - memset(&(temp[chat->numpeers]), 0, sizeof(Group_Peer)); - chat->group = temp; + memset(&(temp[g->numpeers]), 0, sizeof(Group_Peer)); + g->group = temp; - id_copy(chat->group[chat->numpeers].client_id, client_id); - chat->group[chat->numpeers].peer_number = peer_number; + 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; - chat->group[chat->numpeers].last_recv = unix_time(); - chat->group[chat->numpeers].last_recv_msgping = unix_time(); - ++chat->numpeers; + g->group[g->numpeers].last_recv = unix_time(); + g->group[g->numpeers].last_recv_msgping = unix_time(); + ++g->numpeers; - //if (chat->peer_namelistchange != NULL) - // (*chat->peer_namelistchange)(chat, chat->numpeers - 1, CHAT_CHANGE_PEER_ADD, chat->group_namelistchange_userdata); + add_to_closest(g_c, groupnumber, real_pk, temp_pk); + //if (g->peer_namelistchange != NULL) + // (*g->peer_namelistchange)(g, g->numpeers - 1, CHAT_CHANGE_PEER_ADD, g->group_namelistchange_userdata); - return (chat->numpeers - 1); + return (g->numpeers - 1); } -static int handle_status(void *object, int number, uint8_t status) +static int remove_close_conn(Group_Chats *g_c, int groupnumber, int friendcon_id) { - if (status) { /* Went online */ + Group_c *g = get_group_c(g_c, groupnumber); - } else { /* Went offline */ + 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 int send_packet_online(Friend_Connections *fr_c, int friendcon_id, uint16_t group_num, uint8_t *identifier); + +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; + } + } +} +/* 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 handle_packet(void *object, int number, uint8_t *data, uint16_t length); +static int handle_status(void *object, int friendcon_id, uint8_t status) +{ + Group_Chats *g_c = 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 remove timedout connections? + } + + return 0; +} + +static int handle_packet(void *object, int friendcon_id, uint8_t *data, uint16_t length); /* Add friend to group chat. * - * return 0 on success + * return close index on success * return -1 on failure. */ -static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, int groupnumber, uint16_t other_groupnum) +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); @@ -254,9 +469,8 @@ static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, int groupnu continue; } - if (g->close[i].type == GROUPCHAT_CLOSE_CONNECTION && g->close[i].number == (uint32_t)friendcon_id) { - g->close[i].group_number = other_groupnum; /* update groupnum. */ - return 0; /* Already in list. */ + if (g->close[i].number == (uint32_t)friendcon_id) { + return i; /* Already in list. */ } break; @@ -265,14 +479,15 @@ static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, int groupnu 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].group_number = other_groupnum; + 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 0; + return ind; } /* Creates a new groupchat and puts it in the chats array. @@ -289,9 +504,11 @@ int add_groupchat(Group_Chats *g_c) Group_c *g = &g_c->chats[groupnumber]; - g->status = GROUPCHAT_STATUS_VALID; + g->status = GROUPCHAT_STATUS_CONNECTED; new_symmetric_key(g->identifier); g->peer_number = 0; /* Founder is peer 0. */ + memcpy(g->real_pk, g_c->m->net_crypto->self_public_key, crypto_box_PUBLICKEYBYTES); + addpeer(g_c, groupnumber, g->real_pk, g_c->m->dht->self_public_key, 0); return groupnumber; } @@ -311,20 +528,23 @@ int del_groupchat(Group_Chats *g_c, int groupnumber) return wipe_group_chat(g_c, groupnumber); } -/* Send a group message packet. +/* Send a group packet to friendcon_id. * * return 1 on success * return 0 on failure */ -int send_group_message_packet(const Messenger *m, int friendcon_id, const uint8_t *data, uint16_t length) +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 (length >= MAX_CRYPTO_DATA_SIZE) + if (1 + sizeof(uint16_t) + length > MAX_CRYPTO_DATA_SIZE) return 0; - uint8_t packet[1 + length]; - packet[0] = PACKET_ID_MESSAGE_GROUPCHAT; - memcpy(packet + 1, data, length); - return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, friendcon_id), packet, + group_num = htons(group_num); + 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(packet), 0) != -1; } @@ -359,6 +579,8 @@ int invite_friend(Group_Chats *g_c, int32_t friendnumber, int groupnumber) } } +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.) * * returns group number on success @@ -369,6 +591,11 @@ int join_groupchat(Group_Chats *g_c, int32_t friendnumber, const uint8_t *data, if (length != sizeof(uint16_t) + GROUP_IDENTIFIER_LENGTH) return -1; + int friendcon_id = getfriendcon_id(g_c->m, friendnumber); + + if (friendcon_id == -1) + return -1; + int groupnumber = create_group_chat(g_c); if (groupnumber == -1) @@ -378,6 +605,8 @@ int join_groupchat(Group_Chats *g_c, int32_t friendnumber, const uint8_t *data, uint16_t group_num = htons(groupnumber); g->status = GROUPCHAT_STATUS_VALID; + memcpy(g->real_pk, g_c->m->net_crypto->self_public_key, crypto_box_PUBLICKEYBYTES); + uint8_t response[INVITE_RESPONSE_PACKET_SIZE]; response[0] = INVITE_RESPONSE_ID; memcpy(response + 1, &group_num, sizeof(uint16_t)); @@ -386,12 +615,19 @@ int join_groupchat(Group_Chats *g_c, int32_t friendnumber, const uint8_t *data, if (send_group_invite_packet(g_c->m, friendnumber, response, sizeof(response))) { uint16_t other_groupnum; memcpy(&other_groupnum, data, sizeof(other_groupnum)); - other_groupnum = htons(other_groupnum); + other_groupnum = ntohs(other_groupnum); memcpy(g->identifier, data + sizeof(uint16_t), GROUP_IDENTIFIER_LENGTH); - add_conn_to_groupchat(g_c, getfriendcon_id(g_c->m, friendnumber), groupnumber, other_groupnum); - g->peer_number = rand(); /* TODO */ + int close_index = add_conn_to_groupchat(g_c, friendcon_id, groupnumber, 0); + + if (close_index != -1) { + g->close[close_index].group_number = other_groupnum; + g->close[close_index].type = GROUPCHAT_CLOSE_ONLINE; + } + + send_peer_query(g_c, friendcon_id, other_groupnum); return groupnumber; } else { + g->status = GROUPCHAT_STATUS_NONE; return -1; } } @@ -420,6 +656,31 @@ void g_callback_group_message(Group_Chats *g_c, void (*function)(Messenger *m, i g_c->message_callback_userdata = userdata; } +static unsigned 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_NEW_PEER_ID 16 +#define GROUP_MESSAGE_NEW_PEER_LENGTH (sizeof(uint16_t) + crypto_box_PUBLICKEYBYTES * 2) +/* send a group message + * return 0 on success + * return -1 on failure + */ +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 = htons(peer_num); + memcpy(packet, &peer_num, sizeof(uint16_t)); + memcpy(packet + sizeof(uint16_t), real_pk, crypto_box_PUBLICKEYBYTES); + memcpy(packet + sizeof(uint16_t) + crypto_box_PUBLICKEYBYTES, temp_pk, crypto_box_PUBLICKEYBYTES); + + if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_NEW_PEER_ID, packet, sizeof(packet))) { + return 0; + } else { + return -1; + } +} + 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; @@ -442,12 +703,6 @@ static void handle_friend_invite_packet(Messenger *m, int32_t friendnumber, cons g_c->invite_callback(m, friendnumber, invite_data, invite_length, g_c->invite_callback_userdata); return; - } else { - //TODO - uint16_t other_groupnum; - memcpy(&other_groupnum, data + 1, sizeof(uint16_t)); - other_groupnum = ntohs(other_groupnum); - add_conn_to_groupchat(g_c, getfriendcon_id(m, friendnumber), groupnumber, other_groupnum); } break; @@ -472,8 +727,20 @@ static void handle_friend_invite_packet(Messenger *m, int32_t friendnumber, cons memcpy(&other_groupnum, data + 1, sizeof(uint16_t)); other_groupnum = ntohs(other_groupnum); - add_conn_to_groupchat(g_c, getfriendcon_id(m, friendnumber), groupnum, 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); + uint16_t peer_number = rand(); /* TODO: make it not random. */ + addpeer(g_c, groupnum, real_pk, temp_pk, peer_number); + int close_index = add_conn_to_groupchat(g_c, friendcon_id, groupnum, 0); + + 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; } @@ -492,7 +759,7 @@ static int friend_in_close(Group_c *g, int friendcon_id) int i; for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type != GROUPCHAT_CLOSE_CONNECTION) + if (g->close[i].type == GROUPCHAT_CLOSE_NONE) continue; if (g->close[i].number != (uint32_t)friendcon_id) @@ -504,6 +771,165 @@ static int friend_in_close(Group_c *g, int friendcon_id) return -1; } +#define ONLINE_PACKET_DATA_SIZE (sizeof(uint16_t) + crypto_box_PUBLICKEYBYTES) + +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 = htons(group_num); + packet[0] = PACKET_ID_ONLINE_PACKET; + memcpy(packet + 1, &group_num, sizeof(uint16_t)); + memcpy(packet + 1 + sizeof(uint16_t), identifier, crypto_box_PUBLICKEYBYTES); + return write_cryptpacket(fr_c->net_crypto, friend_connection_crypt_connection_id(fr_c, friendcon_id), packet, + sizeof(packet), 0) != -1; +} + +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; +} + +#define PEER_QUERY_ID 4 +#define PEER_RESPONSE_ID 8 + +/* 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_GROUPCHAT, 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) { + 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; + + if ((p - packet) + sizeof(uint16_t) + crypto_box_PUBLICKEYBYTES * 2 > 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; + } + } + + 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_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; +} + +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; + + } +} + #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) @@ -514,7 +940,6 @@ static int friend_in_close(Group_c *g, int friendcon_id) 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) @@ -523,18 +948,14 @@ static unsigned int send_message_all_close(const Group_Chats *g_c, int groupnumb uint16_t i, sent = 0; for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->close[i].type == GROUPCHAT_CLOSE_NONE) + if (g->close[i].type != GROUPCHAT_CLOSE_ONLINE) continue; if ((int)i == receiver) continue; - uint16_t other_groupnum = htons(g->close[i].group_number); - uint8_t packet[sizeof(uint16_t) + length]; - memcpy(packet, &other_groupnum, sizeof(uint16_t)); - memcpy(packet + sizeof(uint16_t), data, length); - - if (send_group_message_packet(g_c->m, g->close[i].number, packet, sizeof(packet))) + 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; } @@ -559,6 +980,9 @@ static unsigned int send_message_group(const Group_Chats *g_c, int 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)); @@ -595,7 +1019,7 @@ int group_message_send(const Group_Chats *g_c, int groupnumber, const uint8_t *m static void handle_message_packet_group(Group_Chats *g_c, int groupnumber, const uint8_t *data, uint16_t length, int close_index) { - if (length < MIN_MESSAGE_PACKET_LEN) + if (length < sizeof(uint16_t) + sizeof(uint32_t) + 1) return; Group_c *g = get_group_c(g_c, groupnumber); @@ -604,22 +1028,16 @@ static void handle_message_packet_group(Group_Chats *g_c, int groupnumber, const return; uint16_t peer_number; - memcpy(&peer_number, data + sizeof(uint16_t), sizeof(uint16_t)); + memcpy(&peer_number, data, sizeof(uint16_t)); peer_number = ntohs(peer_number); int index = get_peer_index(g, peer_number); - //TODO remove - if (index == -1) { - uint8_t empty_key[crypto_box_PUBLICKEYBYTES]; - index = addpeer(g, empty_key, peer_number); - } - if (index == -1) return; uint32_t message_number; - memcpy(&message_number, data + sizeof(uint16_t) * 2, sizeof(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) { @@ -631,11 +1049,23 @@ static void handle_message_packet_group(Group_Chats *g_c, int groupnumber, const g->group[index].last_message_number = message_number; - uint8_t message_id = data[sizeof(uint16_t) * 2 + sizeof(message_number)]; - const uint8_t *msg_data = data + sizeof(uint16_t) * 2 + sizeof(message_number) + 1; - uint16_t msg_data_len = length - (sizeof(uint16_t) * 2 + sizeof(message_number) + 1); + 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_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 PACKET_ID_MESSAGE: { if (msg_data_len == 0) return; @@ -651,38 +1081,44 @@ static void handle_message_packet_group(Group_Chats *g_c, int groupnumber, const return; } - send_message_all_close(g_c, groupnumber, data + sizeof(uint16_t), length - sizeof(uint16_t), close_index); + send_message_all_close(g_c, groupnumber, data, length, close_index); } -static void handle_message_packet(Group_Chats *g_c, int friendcon_id, const uint8_t *data, uint16_t length) +static int handle_packet(void *object, int friendcon_id, uint8_t *data, uint16_t length) { - if (length < MIN_MESSAGE_PACKET_LEN) - return; + 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, sizeof(uint16_t)); + memcpy(&groupnumber, data + 1, sizeof(uint16_t)); groupnumber = ntohs(groupnumber); Group_c *g = get_group_c(g_c, groupnumber); if (!g) - return; + return -1; int index = friend_in_close(g, friendcon_id); if (index == -1) - return; - - handle_message_packet_group(g_c, groupnumber, data, length, index); -} - -static int handle_packet(void *object, int number, uint8_t *data, uint16_t length) -{ - if (length <= 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(object, number, data + 1, length - 1); + handle_message_packet_group(g_c, groupnumber, data + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), index); break; } @@ -707,6 +1143,7 @@ Group_Chats *new_groupchats(Messenger *m) return NULL; temp->m = m; + temp->fr_c = m->fr_c; m->group_chat_object = temp; m_callback_group_invite(m, &handle_friend_invite_packet); @@ -716,6 +1153,18 @@ Group_Chats *new_groupchats(Messenger *m) /* 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 } diff --git a/toxcore/group.h b/toxcore/group.h index a5b5723c..94ae28bf 100644 --- a/toxcore/group.h +++ b/toxcore/group.h @@ -29,11 +29,14 @@ enum { GROUPCHAT_STATUS_NONE, - GROUPCHAT_STATUS_VALID + GROUPCHAT_STATUS_VALID, + GROUPCHAT_STATUS_CONNECTED }; typedef struct { - uint8_t client_id[crypto_box_PUBLICKEYBYTES]; + uint8_t real_pk[crypto_box_PUBLICKEYBYTES]; + uint8_t temp_pk[crypto_box_PUBLICKEYBYTES]; + uint64_t pingid; uint64_t last_pinged; @@ -50,13 +53,14 @@ typedef struct { uint16_t peer_number; } Group_Peer; -#define DESIRED_CLOSE_CONNECTIONS 3 +#define DESIRED_CLOSE_CONNECTIONS 4 #define MAX_GROUP_CONNECTIONS 16 #define GROUP_IDENTIFIER_LENGTH crypto_box_KEYBYTES /* So we can use new_symmetric_key(...) to fill it */ enum { GROUPCHAT_CLOSE_NONE, - GROUPCHAT_CLOSE_CONNECTION + GROUPCHAT_CLOSE_CONNECTION, + GROUPCHAT_CLOSE_ONLINE }; typedef struct { @@ -67,10 +71,19 @@ typedef struct { struct { uint8_t type; /* GROUPCHAT_CLOSE_* */ + uint8_t closest; uint32_t number; uint16_t group_number; } close[MAX_GROUP_CONNECTIONS]; + uint8_t real_pk[crypto_box_PUBLICKEYBYTES]; + struct { + uint8_t entry; + uint8_t real_pk[crypto_box_PUBLICKEYBYTES]; + uint8_t temp_pk[crypto_box_PUBLICKEYBYTES]; + } closest_peers[DESIRED_CLOSE_CONNECTIONS]; + uint8_t changed; + uint8_t identifier[GROUP_IDENTIFIER_LENGTH]; uint32_t message_number; @@ -79,6 +92,7 @@ typedef struct { typedef struct { Messenger *m; + Friend_Connections *fr_c; Group_c *chats; uint32_t num_chats; diff --git a/toxcore/tox.c b/toxcore/tox.c index 19e1c0a4..30630cf2 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -991,6 +991,7 @@ void tox_do(Tox *tox) { Messenger *m = tox; do_messenger(m); + do_groupchats(m->group_chat_object); } /* SAVING AND LOADING FUNCTIONS: */