mirror of
https://github.com/irungentoo/toxcore.git
synced 2024-03-22 13:30:51 +08:00
Group chats now actually work somewhat.
This commit is contained in:
parent
9dd62023f5
commit
e304fa847e
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
621
toxcore/group.c
621
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)
|
||||
{
|
||||
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 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_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?
|
||||
}
|
||||
|
||||
static int handle_packet(void *object, int number, uint8_t *data, uint16_t length);
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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: */
|
||||
|
|
Loading…
Reference in New Issue
Block a user