Skip to content

Commit

Permalink
Make conferences persistent across restarts and reconnects.
Browse files Browse the repository at this point in the history
This is not new groupchats. This is just upgrade to old groupchats with
some advantages:
- Groupchats are now saved into tox_save.
- Clients can get groupchat unique id to save message log.
- Auto restore groupchats after restart even your friend uses
  non-upgraded version.
  • Loading branch information
isotoxin authored and iphydf committed May 20, 2018
1 parent be797d4 commit b53e46d
Show file tree
Hide file tree
Showing 15 changed files with 3,013 additions and 1,167 deletions.
108 changes: 59 additions & 49 deletions auto_tests/conference_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@

#include "helpers.h"

#define NUM_GROUP_TOX 5
#define NUM_GROUP_TOX 16
#define GROUP_MESSAGE "Install Gentoo"

static void handle_self_connection_status(Tox *tox, TOX_CONNECTION connection_status, void *user_data)
static void handle_self_connection_status(
Tox *tox, TOX_CONNECTION connection_status, void *user_data)
{
const int id = *(int *)user_data;

Expand All @@ -35,8 +36,8 @@ static void handle_self_connection_status(Tox *tox, TOX_CONNECTION connection_st
}
}

static void handle_friend_connection_status(Tox *tox, uint32_t friendnumber, TOX_CONNECTION connection_status,
void *user_data)
static void handle_friend_connection_status(
Tox *tox, uint32_t friendnumber, TOX_CONNECTION connection_status, void *user_data)
{
const int id = *(int *)user_data;

Expand All @@ -47,8 +48,9 @@ static void handle_friend_connection_status(Tox *tox, uint32_t friendnumber, TOX
}
}

static void handle_conference_invite(Tox *tox, uint32_t friendnumber, TOX_CONFERENCE_TYPE type, const uint8_t *data,
size_t length, void *user_data)
static void handle_conference_invite(
Tox *tox, uint32_t friendnumber, TOX_CONFERENCE_TYPE type,
const uint8_t *data, size_t length, void *user_data)
{
const int id = *(int *)user_data;
ck_assert_msg(type == TOX_CONFERENCE_TYPE_TEXT, "tox #%d: wrong conference type: %d", id, type);
Expand All @@ -74,14 +76,61 @@ static void handle_conference_invite(Tox *tox, uint32_t friendnumber, TOX_CONFER

static unsigned int num_recv;

static void handle_conference_message(Tox *tox, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length, void *user_data)
static void handle_conference_message(
Tox *tox, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length, void *user_data)
{
if (length == (sizeof(GROUP_MESSAGE) - 1) && memcmp(message, GROUP_MESSAGE, sizeof(GROUP_MESSAGE) - 1) == 0) {
++num_recv;
}
}

static void run_conference_tests(Tox **toxes, uint32_t *tox_index)
{
for (unsigned i = 0; i < NUM_GROUP_TOX; ++i) {
tox_callback_conference_message(toxes[i], &handle_conference_message);
}

TOX_ERR_CONFERENCE_SEND_MESSAGE err;
ck_assert_msg(
tox_conference_send_message(
toxes[random_u32() % NUM_GROUP_TOX], 0, TOX_MESSAGE_TYPE_NORMAL, (const uint8_t *)GROUP_MESSAGE,
sizeof(GROUP_MESSAGE) - 1, &err) != 0, "Failed to send group message.");
ck_assert_msg(
err == TOX_ERR_CONFERENCE_SEND_MESSAGE_OK, "Failed to send group message.");
num_recv = 0;

for (unsigned j = 0; j < 20; ++j) {
for (unsigned i = 0; i < NUM_GROUP_TOX; ++i) {
tox_iterate(toxes[i], &tox_index[i]);
}

c_sleep(25);
}

c_sleep(25);
ck_assert_msg(num_recv == NUM_GROUP_TOX, "Failed to recv group messages.");

for (unsigned k = NUM_GROUP_TOX; k != 0 ; --k) {
tox_conference_delete(toxes[k - 1], 0, nullptr);

for (unsigned j = 0; j < 10; ++j) {
for (unsigned i = 0; i < NUM_GROUP_TOX; ++i) {
tox_iterate(toxes[i], &tox_index[i]);
}

c_sleep(50);
}

for (unsigned i = 0; i < k - 1; ++i) {
uint32_t peer_count = tox_conference_peer_count(toxes[i], 0, nullptr);
ck_assert_msg(peer_count == (k - 1), "\n\tBad number of group peers (post check)."
"\n\t\t\tExpected: %u but tox_instance(%u) only has: %" PRIu32 "\n\n",
(k - 1), i, peer_count);
}
}
}

static void test_many_group(void)
{
const time_t test_start_time = time(nullptr);
Expand Down Expand Up @@ -203,48 +252,9 @@ static void test_many_group(void)

printf("group connected, took %d seconds\n", (int)(time(nullptr) - cur_time));

for (unsigned i = 0; i < NUM_GROUP_TOX; ++i) {
tox_callback_conference_message(toxes[i], &handle_conference_message);
}

TOX_ERR_CONFERENCE_SEND_MESSAGE err;
ck_assert_msg(
tox_conference_send_message(
toxes[random_u32() % NUM_GROUP_TOX], 0, TOX_MESSAGE_TYPE_NORMAL, (const uint8_t *)GROUP_MESSAGE,
sizeof(GROUP_MESSAGE) - 1, &err) != 0, "Failed to send group message.");
ck_assert_msg(
err == TOX_ERR_CONFERENCE_SEND_MESSAGE_OK, "Failed to send group message.");
num_recv = 0;

for (unsigned j = 0; j < 20; ++j) {
for (unsigned i = 0; i < NUM_GROUP_TOX; ++i) {
tox_iterate(toxes[i], &tox_index[i]);
}

c_sleep(25);
}
run_conference_tests(toxes, tox_index);

c_sleep(25);
ck_assert_msg(num_recv == NUM_GROUP_TOX, "Failed to recv group messages.");

for (unsigned k = NUM_GROUP_TOX; k != 0 ; --k) {
tox_conference_delete(toxes[k - 1], 0, nullptr);

for (unsigned j = 0; j < 10; ++j) {
for (unsigned i = 0; i < NUM_GROUP_TOX; ++i) {
tox_iterate(toxes[i], &tox_index[i]);
}

c_sleep(50);
}

for (unsigned i = 0; i < k - 1; ++i) {
uint32_t peer_count = tox_conference_peer_count(toxes[i], 0, nullptr);
ck_assert_msg(peer_count == (k - 1), "\n\tBad number of group peers (post check)."
"\n\t\t\tExpected: %u but tox_instance(%u) only has: %" PRIu32 "\n\n",
(k - 1), i, peer_count);
}
}
printf("tearing down toxes\n");

for (unsigned i = 0; i < NUM_GROUP_TOX; ++i) {
tox_kill(toxes[i]);
Expand Down
9 changes: 6 additions & 3 deletions auto_tests/monolith_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,12 @@ int main(int argc, char *argv[])
// toxcore/friend_requests
CHECK_SIZE(Friend_Requests, 1080);
// toxcore/group
CHECK_SIZE(Group_c, 728);
CHECK_SIZE(Group_Chats, 2120);
CHECK_SIZE(Group_Peer, 480);
CHECK_SIZE(Group_c, 312);
CHECK_SIZE(Group_Chats, 80);
CHECK_SIZE(Group_Join_Peer, 48);
CHECK_SIZE(Group_Peer, 144);
CHECK_SIZE(Group_Peer_Lossy, 260);
CHECK_SIZE(jp_iterator, 40);
// toxcore/list
CHECK_SIZE(BS_LIST, 32);
// toxcore/logger
Expand Down
15 changes: 15 additions & 0 deletions conference-stress-test
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/make -f

RESULTS := $(foreach i,$(shell seq 0 10),$(shell printf "results/%02d.log" $i))

.SILENT:
run: $(RESULTS)

results/%.log: auto_conference_test
echo "TEST $< > $@"
mkdir -p $(@D)
./auto_conference_test > $@ 2>&1

.PHONY: auto_conference_test
auto_conference_test:
$(MAKE) $@
68 changes: 40 additions & 28 deletions toxav/groupav.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ typedef struct {

uint16_t audio_sequnum;

void (*audio_data)(Messenger *m, uint32_t groupnumber, uint32_t peernumber, const int16_t *pcm, uint32_t samples,
void (*audio_data)(Messenger *m, uint32_t groupnumber, uint32_t peernumber, const int16_t *pcm, unsigned int samples,
uint8_t channels, unsigned int sample_rate, void *userdata);
void *userdata;
} Group_AV;
Expand Down Expand Up @@ -228,7 +228,7 @@ static int recreate_encoder(Group_AV *group_av)
}

static Group_AV *new_group_av(Logger *log, Group_Chats *g_c, void (*audio_callback)(Messenger *, uint32_t, uint32_t,
const int16_t *, unsigned int, uint8_t, uint32_t, void *), void *userdata)
const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata)
{
if (!g_c) {
return nullptr;
Expand All @@ -249,7 +249,7 @@ static Group_AV *new_group_av(Logger *log, Group_Chats *g_c, void (*audio_callba
return group_av;
}

static void group_av_peer_new(void *object, uint32_t groupnumber, uint32_t friendgroupnumber)
static void group_av_peer_new(void *object, int groupnumber, int friendgroupnumber)
{
Group_AV *group_av = (Group_AV *)object;
Group_Peer_AV *peer_av = (Group_Peer_AV *)calloc(1, sizeof(Group_Peer_AV));
Expand All @@ -262,7 +262,7 @@ static void group_av_peer_new(void *object, uint32_t groupnumber, uint32_t frien
group_peer_set_object(group_av->g_c, groupnumber, friendgroupnumber, peer_av);
}

static void group_av_peer_delete(void *object, uint32_t groupnumber, uint32_t friendgroupnumber, void *peer_object)
static void group_av_peer_delete(void *object, int groupnumber, void *peer_object)
{
Group_Peer_AV *peer_av = (Group_Peer_AV *)peer_object;

Expand All @@ -278,15 +278,14 @@ static void group_av_peer_delete(void *object, uint32_t groupnumber, uint32_t fr
free(peer_object);
}

static void group_av_groupchat_delete(void *object, uint32_t groupnumber)
static void group_av_groupchat_delete(void *object, int groupnumber)
{
if (object) {
kill_group_av((Group_AV *)object);
}
}

static int decode_audio_packet(Group_AV *group_av, Group_Peer_AV *peer_av, uint32_t groupnumber,
uint32_t friendgroupnumber)
static int decode_audio_packet(Group_AV *group_av, Group_Peer_AV *peer_av, int groupnumber, int friendgroupnumber)
{
if (!group_av || !peer_av) {
return -1;
Expand Down Expand Up @@ -391,7 +390,7 @@ static int decode_audio_packet(Group_AV *group_av, Group_Peer_AV *peer_av, uint3
return -1;
}

static int handle_group_audio_packet(void *object, uint32_t groupnumber, uint32_t friendgroupnumber, void *peer_object,
static int handle_group_audio_packet(void *object, int groupnumber, int friendgroupnumber, void *peer_object,
const uint8_t *packet, uint16_t length)
{
if (!peer_object || !object || length <= sizeof(uint16_t)) {
Expand Down Expand Up @@ -429,10 +428,13 @@ static int handle_group_audio_packet(void *object, uint32_t groupnumber, uint32_
* return 0 on success.
* return -1 on failure.
*/
static int groupchat_enable_av(Logger *log, Group_Chats *g_c, uint32_t groupnumber, void (*audio_callback)(Messenger *,
uint32_t,
uint32_t, const int16_t *, unsigned int, uint8_t, uint32_t, void *), void *userdata)
static int groupchat_enable_av(Logger *log, Group_Chats *g_c, int groupnumber, void (*audio_callback)(Messenger *,
uint32_t, uint32_t, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata)
{
if (groupnumber == -1) {
return -1;
}

Group_AV *group_av = new_group_av(log, g_c, audio_callback, userdata);

if (group_av == nullptr) {
Expand All @@ -457,11 +459,10 @@ static int groupchat_enable_av(Logger *log, Group_Chats *g_c, uint32_t groupnumb
* return -1 on failure.
*/
int add_av_groupchat(Logger *log, Group_Chats *g_c, void (*audio_callback)(Messenger *, uint32_t, uint32_t,
const int16_t *,
unsigned int,
uint8_t, uint32_t, void *), void *userdata)
const int16_t *, unsigned int,
uint8_t, unsigned int, void *), void *userdata)
{
int groupnumber = add_groupchat(g_c, GROUPCHAT_TYPE_AV);
int groupnumber = add_groupchat(g_c, GROUPCHAT_TYPE_AV, nullptr);

if (groupnumber == -1) {
return -1;
Expand All @@ -478,21 +479,21 @@ int add_av_groupchat(Logger *log, Group_Chats *g_c, void (*audio_callback)(Messe
/* Join a AV group (you need to have been invited first.)
*
* returns group number on success
* returns -1 on failure.
* returns -1 .. -6 on failure (see join_groupchat)
*/
int join_av_groupchat(Logger *log, Group_Chats *g_c, uint32_t friendnumber, const uint8_t *data, uint16_t length,
void (*audio_callback)(Messenger *, uint32_t, uint32_t, const int16_t *, unsigned int, uint8_t, uint32_t, void *),
void *userdata)
void (*audio_callback)(Messenger *, uint32_t, uint32_t, const int16_t *, unsigned int, uint8_t, unsigned int,
void *), void *userdata)
{
int groupnumber = join_groupchat(g_c, friendnumber, GROUPCHAT_TYPE_AV, data, length);

if (groupnumber == -1) {
return -1;
if (groupnumber < 0) {
return groupnumber;
}

if (groupchat_enable_av(log, g_c, groupnumber, audio_callback, userdata) == -1) {
del_groupchat(g_c, groupnumber);
return -1;
return -5; /* initialization failed */
}

return groupnumber;
Expand All @@ -503,21 +504,32 @@ int join_av_groupchat(Logger *log, Group_Chats *g_c, uint32_t friendnumber, cons
* return 0 on success.
* return -1 on failure.
*/
static int send_audio_packet(Group_Chats *g_c, uint32_t groupnumber, uint8_t *packet, uint16_t length)
static int send_audio_packet(Group_Chats *g_c, int groupnumber, uint8_t *packet, uint16_t length)
{
if (!length) {
return -1;
}

Group_AV *group_av = (Group_AV *)group_get_object(g_c, groupnumber);
VLA(uint8_t, data, 1 + sizeof(uint16_t) + length);
const size_t plen = 1 + sizeof(uint16_t) + length;

if (plen > MAX_CRYPTO_DATA_SIZE) {
return -1;
}

Group_AV *const group_av = (Group_AV *)group_get_object(g_c, groupnumber);

if (!group_av) {
return -1;
}

uint8_t data[MAX_CRYPTO_DATA_SIZE];
data[0] = GROUP_AUDIO_PACKET_ID;

uint16_t sequnum = net_htons(group_av->audio_sequnum);
const uint16_t sequnum = net_htons(group_av->audio_sequnum);
memcpy(data + 1, &sequnum, sizeof(sequnum));
memcpy(data + 1 + sizeof(sequnum), packet, length);

if (send_group_lossy_packet(g_c, groupnumber, data, SIZEOF_VLA(data)) == -1) {
if (send_group_lossy_packet(g_c, groupnumber, data, (uint16_t)plen) == -1) {
return -1;
}

Expand All @@ -530,8 +542,8 @@ static int send_audio_packet(Group_Chats *g_c, uint32_t groupnumber, uint8_t *pa
* return 0 on success.
* return -1 on failure.
*/
int group_send_audio(Group_Chats *g_c, uint32_t groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels,
uint32_t sample_rate)
int group_send_audio(Group_Chats *g_c, uint32_t groupnumber, const int16_t *pcm, unsigned int samples,
uint8_t channels, unsigned int sample_rate)
{
Group_AV *group_av = (Group_AV *)group_get_object(g_c, groupnumber);

Expand Down
4 changes: 2 additions & 2 deletions toxav/groupav.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ int join_av_groupchat(Logger *log, Group_Chats *g_c, uint32_t friendnumber, cons
* return 0 on success.
* return -1 on failure.
*/
int group_send_audio(Group_Chats *g_c, uint32_t groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels,
uint32_t sample_rate);
int group_send_audio(Group_Chats *g_c, uint32_t groupnumber, const int16_t *pcm, unsigned int samples,
uint8_t channels, uint32_t sample_rate);

2 changes: 1 addition & 1 deletion toxav/toxav.api.h
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(void *, uint32_t, ui
/* Join a AV group (you need to have been invited first.)
*
* returns group number on success
* returns -1 on failure.
* returns < 0 on failure.
*
* Audio data callback format (same as the one for toxav_add_av_groupchat()):
* audio_callback(Tox *tox, uint32_t groupnumber, uint32_t peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t sample_rate, void *userdata)
Expand Down
2 changes: 1 addition & 1 deletion toxav/toxav.h
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(void *, uint32_t, ui
/* Join a AV group (you need to have been invited first.)
*
* returns group number on success
* returns -1 on failure.
* returns < 0 on failure.
*
* Audio data callback format (same as the one for toxav_add_av_groupchat()):
* audio_callback(Tox *tox, uint32_t groupnumber, uint32_t peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t sample_rate, void *userdata)
Expand Down
2 changes: 1 addition & 1 deletion toxav/toxav_old.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(void *, uint32_t, ui
/* Join a AV group (you need to have been invited first.)
*
* returns group number on success
* returns -1 on failure.
* returns < 0 on failure.
*
* Audio data callback format (same as the one for toxav_add_av_groupchat()):
* audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
Expand Down
Loading

0 comments on commit b53e46d

Please sign in to comment.