From 8b35d194c040270280bf658e514ef61ee2759dfb Mon Sep 17 00:00:00 2001 From: irungentoo Date: Fri, 26 Sep 2014 13:25:52 -0400 Subject: [PATCH] Group chats are starting to work. --- testing/nTox.c | 4 +- toxcore/group.c | 306 ++++++++++++++++++++++++++++++++++++++++++++---- toxcore/group.h | 25 ++-- toxcore/tox.c | 48 ++++++-- toxcore/tox.h | 16 ++- 5 files changed, 349 insertions(+), 50 deletions(-) diff --git a/testing/nTox.c b/testing/nTox.c index edda43b1..b33b1fd3 100644 --- a/testing/nTox.c +++ b/testing/nTox.c @@ -1001,11 +1001,11 @@ void print_help(char *prog_name) puts(" -f keyfile [Optional] Specify a keyfile to read from and write to."); } -void print_invite(Tox *m, int friendnumber, const uint8_t *group_public_key, void *userdata) +void print_invite(Tox *m, int friendnumber, const uint8_t *data, uint16_t length, void *userdata) { char msg[256]; sprintf(msg, "[i] received group chat invite from: %u, auto accepting and joining. group number: %u", friendnumber, - tox_join_groupchat(m, friendnumber, group_public_key)); + tox_join_groupchat(m, friendnumber, data, length)); new_lines(msg); } diff --git a/toxcore/group.c b/toxcore/group.c index d3652e17..602c9124 100644 --- a/toxcore/group.c +++ b/toxcore/group.c @@ -220,7 +220,7 @@ static int wipe_group_chat(Group_Chats *g_c, int groupnumber) return 0; } -static Group_c *get_group_c(Group_Chats *g_c, int groupnumber) +static Group_c *get_group_c(const Group_Chats *g_c, int groupnumber) { if (groupnumber_not_valid(g_c, groupnumber)) return 0; @@ -231,7 +231,7 @@ static Group_c *get_group_c(Group_Chats *g_c, int groupnumber) /* * check if peer with client_id is in peer array. * - * return peer number if peer is in chat. + * return peer index if peer is in chat. * return -1 if peer is not in chat. * * TODO: make this more efficient. @@ -267,18 +267,38 @@ static int get_group_num(const Group_Chats *g_c, const uint8_t *identifier) 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: make this more efficient. + */ +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; +} + /* * Add a peer to the group chat. * - * return peernum if success or peer already in 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) +static int addpeer(Group_c *chat, const uint8_t *client_id, uint16_t peer_number) { - int peernum = peer_in_chat(chat, client_id); + //TODO + //int peer_index = peer_in_chat(chat, client_id); - if (peernum != -1) - return peernum; + //if (peer_index != -1) + // return peer_index; Group_Peer *temp; temp = realloc(chat->group, sizeof(Group_Peer) * (chat->numpeers + 1)); @@ -290,6 +310,8 @@ static int addpeer(Group_c *chat, const uint8_t *client_id) chat->group = temp; id_copy(chat->group[chat->numpeers].client_id, client_id); + chat->group[chat->numpeers].peer_number = peer_number; + chat->group[chat->numpeers].last_recv = unix_time(); chat->group[chat->numpeers].last_recv_msgping = unix_time(); ++chat->numpeers; @@ -300,6 +322,39 @@ static int addpeer(Group_c *chat, const uint8_t *client_id) return (chat->numpeers - 1); } +/* Add friend to group chat. + * + * return 0 on success + * return -1 on failure. + */ +static int add_friend_to_groupchat(Group_Chats *g_c, int32_t friendnumber, int groupnumber, uint16_t other_groupnum) +{ + if (!m_friend_exists(g_c->m, friendnumber)) + return -1; + + Group_c *g = get_group_c(g_c, groupnumber); + + if (!g) + return -1; + + uint16_t i; + + for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { + if (g->close[i].type != GROUPCHAT_CLOSE_NONE) + continue; + + break; + } + + if (i == MAX_GROUP_CONNECTIONS) + return -1; + + g->close[i].type = GROUPCHAT_CLOSE_FRIEND; + g->close[i].number = friendnumber; + g->close[i].group_number = other_groupnum; + + return 0; +} /* Creates a new groupchat and puts it in the chats array. * @@ -310,13 +365,14 @@ int add_groupchat(Group_Chats *g_c) { int groupnumber = create_group_chat(g_c); - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) + if (groupnumber == -1) return -1; + Group_c *g = &g_c->chats[groupnumber]; + g->status = GROUPCHAT_STATUS_VALID; new_symmetric_key(g->identifier); + g->peer_number = 0; /* Founder is peer 0. */ return groupnumber; } @@ -356,18 +412,18 @@ int invite_friend(Group_Chats *g_c, int32_t friendnumber, int groupnumber) * returns group number on success * returns -1 on failure. */ -int join_groupchat(Group_Chats *g_c, int32_t friendnumber, uint8_t *data, uint16_t length) +int join_groupchat(Group_Chats *g_c, int32_t friendnumber, const uint8_t *data, uint16_t length) { if (length != sizeof(uint16_t) + GROUP_IDENTIFIER_LENGTH) return -1; int groupnumber = create_group_chat(g_c); - Group_c *g = get_group_c(g_c, groupnumber); - - if (!g) + if (groupnumber == -1) return -1; + Group_c *g = &g_c->chats[groupnumber]; + uint16_t group_num = htons(groupnumber); g->status = GROUPCHAT_STATUS_VALID; uint8_t response[INVITE_RESPONSE_PACKET_SIZE]; @@ -379,7 +435,9 @@ int join_groupchat(Group_Chats *g_c, int32_t friendnumber, uint8_t *data, uint16 uint16_t other_groupnum; memcpy(&other_groupnum, data, sizeof(other_groupnum)); other_groupnum = htons(other_groupnum); - //TODO add_friend_to_groupchat(g_c, friendnumber, groupnumber, other_groupnum); + memcpy(g->identifier, data + sizeof(uint16_t), GROUP_IDENTIFIER_LENGTH); + add_friend_to_groupchat(g_c, friendnumber, groupnumber, other_groupnum); + g->peer_number = rand(); /* TODO */ return groupnumber; } else { return -1; @@ -428,10 +486,16 @@ static void handle_friend_invite_packet(Messenger *m, int32_t friendnumber, cons int groupnumber = get_group_num(g_c, data + 1 + sizeof(uint16_t)); if (groupnumber == -1) { - g_c->invite_callback(m, friendnumber, invite_data, invite_length, g_c->invite_callback_userdata); + if (g_c->invite_callback) + 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_friend_to_groupchat(g_c, friendnumber, groupnumber, other_groupnum); } break; @@ -441,13 +505,22 @@ static void handle_friend_invite_packet(Messenger *m, int32_t friendnumber, cons if (length != INVITE_RESPONSE_PACKET_SIZE) return; - int groupnumber = get_group_num(g_c, data + 1 + sizeof(uint16_t)); + uint16_t other_groupnum, groupnum; + memcpy(&groupnum, data + 1 + sizeof(uint16_t), sizeof(uint16_t)); + groupnum = ntohs(groupnum); - if (groupnumber == -1) { + Group_c *g = get_group_c(g_c, groupnum); + + if (!g) return; - } else { - //TODO add_friend_to_groupchat(g_c, friendnumber, groupnumber, other_groupnum); - } + + if (memcmp(data + 1 + sizeof(uint16_t) * 2, g->identifier, GROUP_IDENTIFIER_LENGTH) != 0) + return; + + memcpy(&other_groupnum, data + 1, sizeof(uint16_t)); + other_groupnum = ntohs(other_groupnum); + + add_friend_to_groupchat(g_c, friendnumber, groupnum, other_groupnum); break; } @@ -457,9 +530,197 @@ static void handle_friend_invite_packet(Messenger *m, int32_t friendnumber, cons } } -static void handle_friend_message_packet(Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length) +/* Find index of friend in the close list; + * + * returns index on success + * returns -1 on failure. + */ +static int friend_in_close(Group_c *g, int32_t friendnumber) +{ + int i; + + for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { + if (g->close[i].type != GROUPCHAT_CLOSE_FRIEND) + continue; + + if (g->close[i].number != (uint32_t)friendnumber) + continue; + + break; + } + + if (i == MAX_GROUP_CONNECTIONS) + return -1; + + return i; +} + +#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_NONE) + 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))) + ++sent; + } + + return sent; +} + +/* 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) +{ + Group_c *g = get_group_c(g_c, groupnumber); + + if (!g) + 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); +} + +/* send a group message + * return 0 on success + * return -1 on failure + */ +int group_message_send(const Group_Chats *g_c, int groupnumber, const uint8_t *message, uint16_t length) +{ + if (send_message_group(g_c, groupnumber, PACKET_ID_MESSAGE, message, length)) { + return 0; + } else { + return -1; + } +} + +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) + return; + + Group_c *g = get_group_c(g_c, groupnumber); + + if (!g) + return; + + uint16_t peer_number; + memcpy(&peer_number, data + sizeof(uint16_t), 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)); + 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) * 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); + + switch (message_id) { + case PACKET_ID_MESSAGE: { + if (msg_data_len == 0) + return; + + //TODO + if (g_c->message_callback) + g_c->message_callback(g_c->m, groupnumber, index, msg_data, msg_data_len, g_c->message_callback_userdata); + + break; + } + + default: + return; + } + + send_message_all_close(g_c, groupnumber, data + sizeof(uint16_t), length - sizeof(uint16_t), close_index); +} + +static void handle_friend_message_packet(Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length) +{ + Group_Chats *g_c = m->group_chat_object; + + if (length < MIN_MESSAGE_PACKET_LEN) + return; + + uint16_t groupnumber; + memcpy(&groupnumber, data, sizeof(uint16_t)); + groupnumber = ntohs(groupnumber); + Group_c *g = get_group_c(g_c, groupnumber); + + if (!g) + return; + + int index = friend_in_close(g, friendnumber); + + if (index == -1) + return; + + handle_message_packet_group(g_c, groupnumber, data, length, index); } /* Create new groupchat instance. */ @@ -476,6 +737,7 @@ Group_Chats *new_groupchats(Messenger *m) temp->m = m; m->group_chat_object = temp; m_callback_group_invite(m, &handle_friend_invite_packet); + m_callback_group_message(m, &handle_friend_message_packet); return temp; } diff --git a/toxcore/group.h b/toxcore/group.h index 544fbdb7..51152843 100644 --- a/toxcore/group.h +++ b/toxcore/group.h @@ -41,7 +41,6 @@ typedef struct { uint8_t client_id[crypto_box_PUBLICKEYBYTES]; uint64_t pingid; uint64_t last_pinged; - IP_Port ping_via; uint64_t last_recv; uint64_t last_recv_msgping; @@ -52,12 +51,20 @@ typedef struct { uint8_t deleted; uint64_t deleted_time; + + uint16_t peer_number; } Group_Peer; - -#define MAX_GROUP_CONNECTIONS 4 +#define DESIRED_CLOSE_CONNECTIONS 3 +#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_FRIEND, + GROUPCHAT_CLOSE_GROUPCON +}; + typedef struct { uint8_t status; @@ -65,11 +72,15 @@ typedef struct { uint32_t numpeers; struct { - uint8_t type; + uint8_t type; /* GROUPCHAT_CLOSE_* */ uint32_t number; + uint16_t group_number; } close[MAX_GROUP_CONNECTIONS]; uint8_t identifier[GROUP_IDENTIFIER_LENGTH]; + + uint32_t message_number; + uint16_t peer_number; } Group_c; typedef struct { @@ -155,19 +166,19 @@ int invite_friend(Group_Chats *g_c, int32_t friendnumber, int groupnumber); * returns group number on success * returns -1 on failure. */ -int join_groupchat(Group_Chats *g_c, int32_t friendnumber, uint8_t *data, uint16_t length); +int join_groupchat(Group_Chats *g_c, int32_t friendnumber, const uint8_t *data, uint16_t length); /* send a group message * return 0 on success * return -1 on failure */ -int group_message_send(const Group_Chats *g_c, int groupnumber, const uint8_t *message, uint32_t length); +int group_message_send(const Group_Chats *g_c, int groupnumber, const uint8_t *message, uint16_t length); /* send a group action * return 0 on success * return -1 on failure */ -int group_action_send(const Group_Chats *g_c, int groupnumber, const uint8_t *action, uint32_t length); +int group_action_send(const Group_Chats *g_c, int groupnumber, const uint8_t *action, uint16_t length); /* Return the number of peers in the group chat on success. * return -1 on failure diff --git a/toxcore/tox.c b/toxcore/tox.c index 39a63fd9..789ef3c9 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -26,6 +26,7 @@ #endif #include "Messenger.h" +#include "group.h" #include "logger.h" #define __TOX_DEFINED__ @@ -544,13 +545,15 @@ int tox_send_lossless_packet(const Tox *tox, int32_t friendnumber, const uint8_t /* Set the callback for group invites. * - * Function(Tox *tox, int32_t friendnumber, uint8_t *group_public_key, void *userdata) + * Function(Tox *tox, int32_t friendnumber, uint8_t *data, uint16_t length, void *userdata) + * + * data of length is what needs to be passed to join_groupchat(). */ -void tox_callback_group_invite(Tox *tox, void (*function)(Messenger *tox, int32_t, const uint8_t *, void *), +void tox_callback_group_invite(Tox *tox, void (*function)(Messenger *tox, int32_t, const uint8_t *, uint16_t, void *), void *userdata) { Messenger *m = tox; - //m_callback_group_invite(m, function, userdata); + g_callback_group_invite(m->group_chat_object, function, userdata); } /* Set the callback for group messages. @@ -561,7 +564,7 @@ void tox_callback_group_message(Tox *tox, void (*function)(Messenger *tox, int, void *userdata) { Messenger *m = tox; - //m_callback_group_message(m, function, userdata); + g_callback_group_message(m->group_chat_object, function, userdata); } /* Set the callback for group actions. @@ -594,8 +597,9 @@ void tox_callback_group_namelist_change(Tox *tox, void (*function)(Tox *tox, int int tox_add_groupchat(Tox *tox) { Messenger *m = tox; - //return add_groupchat(m); + return add_groupchat(m->group_chat_object); } + /* Delete a groupchat from the chats array. * * return 0 on success. @@ -605,6 +609,7 @@ int tox_del_groupchat(Tox *tox, int groupnumber) { Messenger *m = tox; //return del_groupchat(m, groupnumber); + return -1; } /* Copy the name of peernumber who is in groupnumber to name. @@ -617,7 +622,9 @@ int tox_group_peername(const Tox *tox, int groupnumber, int peernumber, uint8_t { const Messenger *m = tox; //return m_group_peername(m, groupnumber, peernumber, name); + return -1; } + /* invite friendnumber to groupnumber * return 0 on success * return -1 on failure @@ -625,37 +632,40 @@ int tox_group_peername(const Tox *tox, int groupnumber, int peernumber, uint8_t int tox_invite_friend(Tox *tox, int32_t friendnumber, int groupnumber) { Messenger *m = tox; - //return invite_friend(m, friendnumber, groupnumber); + return invite_friend(m->group_chat_object, friendnumber, groupnumber); } -/* Join a group (you need to have been invited first.) + +/* Join a group (you need to have been invited first.) using data of length obtained + * in the group invite callback. * * returns group number on success * returns -1 on failure. */ -int tox_join_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *friend_group_public_key) +int tox_join_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length) { Messenger *m = tox; - //return join_groupchat(m, friendnumber, friend_group_public_key); + return join_groupchat(m->group_chat_object, friendnumber, data, length); } /* send a group message * return 0 on success * return -1 on failure */ -int tox_group_message_send(Tox *tox, int groupnumber, const uint8_t *message, uint32_t length) +int tox_group_message_send(Tox *tox, int groupnumber, const uint8_t *message, uint16_t length) { Messenger *m = tox; - //return group_message_send(m, groupnumber, message, length); + return group_message_send(m->group_chat_object, groupnumber, message, length); } /* send a group action * return 0 on success * return -1 on failure */ -int tox_group_action_send(Tox *tox, int groupnumber, const uint8_t *action, uint32_t length) +int tox_group_action_send(Tox *tox, int groupnumber, const uint8_t *action, uint16_t length) { Messenger *m = tox; //return group_action_send(m, groupnumber, action, length); + return -1; } /* Return the number of peers in the group chat on success. @@ -665,6 +675,7 @@ int tox_group_number_peers(const Tox *tox, int groupnumber) { const Messenger *m = tox; //return group_number_peers(m, groupnumber); + return -1; } /* List all the peers in the group chat. @@ -682,6 +693,7 @@ int tox_group_get_names(const Tox *tox, int groupnumber, uint8_t names[][TOX_MAX { const Messenger *m = tox; //return group_names(m, groupnumber, names, lengths, length); + return -1; } /* Return the number of chats in the instance m. @@ -691,6 +703,7 @@ uint32_t tox_count_chatlist(const Tox *tox) { const Messenger *m = tox; //return count_chatlist(m); + return 0; } /* Copy a list of valid chat IDs into the array out_list. @@ -702,6 +715,7 @@ uint32_t tox_get_chatlist(const Tox *tox, int *out_list, uint32_t list_size) { const Messenger *m = tox; //return copy_chatlist(m, out_list, list_size); + return 0; } @@ -947,7 +961,14 @@ Tox *tox_new(Tox_Options *options) } } - return new_messenger(&m_options); + Messenger *m = new_messenger(&m_options); + + if (!new_groupchats(m)) { + kill_messenger(m); + return NULL; + } + + return m; } /* Run this before closing shop. @@ -956,6 +977,7 @@ Tox *tox_new(Tox_Options *options) void tox_kill(Tox *tox) { Messenger *m = tox; + kill_groupchats(m->group_chat_object); kill_messenger(m); } diff --git a/toxcore/tox.h b/toxcore/tox.h index 61cfdf70..a271bc60 100644 --- a/toxcore/tox.h +++ b/toxcore/tox.h @@ -419,9 +419,12 @@ int tox_send_lossless_packet(const Tox *tox, int32_t friendnumber, const uint8_t /* Set the callback for group invites. * - * Function(Tox *tox, int friendnumber, uint8_t *group_public_key, void *userdata) + * Function(Tox *tox, int32_t friendnumber, uint8_t *data, uint16_t length, void *userdata) + * + * data of length is what needs to be passed to join_groupchat(). */ -void tox_callback_group_invite(Tox *tox, void (*function)(Tox *tox, int32_t, const uint8_t *, void *), void *userdata); +void tox_callback_group_invite(Tox *tox, void (*function)(Tox *tox, int32_t, const uint8_t *, uint16_t, void *), + void *userdata); /* Set the callback for group messages. * @@ -479,24 +482,25 @@ int tox_group_peername(const Tox *tox, int groupnumber, int peernumber, uint8_t */ int tox_invite_friend(Tox *tox, int32_t friendnumber, int groupnumber); -/* Join a group (you need to have been invited first.) +/* Join a group (you need to have been invited first.) using data of length obtained + * in the group invite callback. * * returns group number on success * returns -1 on failure. */ -int tox_join_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *friend_group_public_key); +int tox_join_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length); /* send a group message * return 0 on success * return -1 on failure */ -int tox_group_message_send(Tox *tox, int groupnumber, const uint8_t *message, uint32_t length); +int tox_group_message_send(Tox *tox, int groupnumber, const uint8_t *message, uint16_t length); /* send a group action * return 0 on success * return -1 on failure */ -int tox_group_action_send(Tox *tox, int groupnumber, const uint8_t *action, uint32_t length); +int tox_group_action_send(Tox *tox, int groupnumber, const uint8_t *action, uint16_t length); /* Return the number of peers in the group chat on success. * return -1 on failure