summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--auto_tests/conference_simple_test.c34
-rw-r--r--auto_tests/conference_test.c141
-rw-r--r--toxcore/group.c141
-rw-r--r--toxcore/group.h18
-rw-r--r--toxcore/tox.api.h4
-rw-r--r--toxcore/tox.c4
-rw-r--r--toxcore/tox.h5
7 files changed, 260 insertions, 87 deletions
diff --git a/auto_tests/conference_simple_test.c b/auto_tests/conference_simple_test.c
index 8d95bba8..f2bede30 100644
--- a/auto_tests/conference_simple_test.c
+++ b/auto_tests/conference_simple_test.c
@@ -13,6 +13,7 @@ typedef struct State {
13 uint32_t id; 13 uint32_t id;
14 bool self_online; 14 bool self_online;
15 bool friend_online; 15 bool friend_online;
16 bool invited_next;
16 17
17 bool joined; 18 bool joined;
18 uint32_t conference; 19 uint32_t conference;
@@ -55,19 +56,6 @@ static void handle_conference_invite(Tox *tox, uint32_t friend_number, TOX_CONFE
55 fprintf(stderr, "tox%d Joined conference %d\n", state->id, state->conference); 56 fprintf(stderr, "tox%d Joined conference %d\n", state->id, state->conference);
56 state->joined = true; 57 state->joined = true;
57 } 58 }
58
59 // We're tox2, so now we invite tox3.
60 if (state->id == 2) {
61 TOX_ERR_CONFERENCE_INVITE err;
62 tox_conference_invite(tox, 1, state->conference, &err);
63
64 if (err != TOX_ERR_CONFERENCE_INVITE_OK) {
65 fprintf(stderr, "ERROR: %d\n", err);
66 exit(EXIT_FAILURE);
67 }
68
69 fprintf(stderr, "tox2 invited tox3\n");
70 }
71} 59}
72 60
73static void handle_conference_message(Tox *tox, uint32_t conference_number, uint32_t peer_number, 61static void handle_conference_message(Tox *tox, uint32_t conference_number, uint32_t peer_number,
@@ -99,6 +87,25 @@ static void handle_conference_peer_list_changed(Tox *tox, uint32_t conference_nu
99 87
100 fprintf(stderr, "tox%d has %d peers online\n", state->id, count); 88 fprintf(stderr, "tox%d has %d peers online\n", state->id, count);
101 state->peers = count; 89 state->peers = count;
90
91 // We're tox2, so now we invite tox3.
92 if (state->id == 2 && !state->invited_next) {
93 // TODO(zugz): neater way to determine whether we are connected, and when
94 // we become so
95 TOX_ERR_CONFERENCE_PEER_QUERY peer_err;
96 tox_conference_peer_number_is_ours(tox, 0, 0, &peer_err);
97
98 if (peer_err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
99 return;
100 }
101
102 TOX_ERR_CONFERENCE_INVITE err;
103 tox_conference_invite(tox, 1, state->conference, &err);
104 ck_assert_msg(err == TOX_ERR_CONFERENCE_INVITE_OK, "tox2 failed to invite tox3: err = %d", err);
105
106 state->invited_next = true;
107 fprintf(stderr, "tox2 invited tox3\n");
108 }
102} 109}
103 110
104int main(void) 111int main(void)
@@ -195,6 +202,7 @@ int main(void)
195 TOX_ERR_CONFERENCE_INVITE err; 202 TOX_ERR_CONFERENCE_INVITE err;
196 tox_conference_invite(tox1, 0, state1.conference, &err); 203 tox_conference_invite(tox1, 0, state1.conference, &err);
197 ck_assert_msg(err == TOX_ERR_CONFERENCE_INVITE_OK, "failed to invite a friend: err = %d", err); 204 ck_assert_msg(err == TOX_ERR_CONFERENCE_INVITE_OK, "failed to invite a friend: err = %d", err);
205 state1.invited_next = true;
198 fprintf(stderr, "tox1 invited tox2\n"); 206 fprintf(stderr, "tox1 invited tox2\n");
199 } 207 }
200 208
diff --git a/auto_tests/conference_test.c b/auto_tests/conference_test.c
index 2bdee78f..fd4f41b4 100644
--- a/auto_tests/conference_test.c
+++ b/auto_tests/conference_test.c
@@ -15,30 +15,39 @@
15#include "../toxcore/util.h" 15#include "../toxcore/util.h"
16#include "check_compat.h" 16#include "check_compat.h"
17 17
18#define NUM_GROUP_TOX 5 18#define NUM_GROUP_TOX 16
19#define GROUP_MESSAGE "Install Gentoo" 19#define GROUP_MESSAGE "Install Gentoo"
20 20
21#define NAME_FORMAT_STR "Tox #%4u"
22#define NAMELEN 9
23#define NAME_FORMAT "%9s"
24
25typedef struct State {
26 uint32_t id;
27 bool invited_next;
28} State;
29
21static void handle_self_connection_status( 30static void handle_self_connection_status(
22 Tox *tox, TOX_CONNECTION connection_status, void *user_data) 31 Tox *tox, TOX_CONNECTION connection_status, void *user_data)
23{ 32{
24 const uint16_t id = *(uint16_t *)user_data; 33 const State *state = (State *)user_data;
25 34
26 if (connection_status != TOX_CONNECTION_NONE) { 35 if (connection_status != TOX_CONNECTION_NONE) {
27 printf("tox #%d: is now connected\n", id); 36 printf("tox #%u: is now connected\n", state->id);
28 } else { 37 } else {
29 printf("tox #%d: is now disconnected\n", id); 38 printf("tox #%u: is now disconnected\n", state->id);
30 } 39 }
31} 40}
32 41
33static void handle_friend_connection_status( 42static void handle_friend_connection_status(
34 Tox *tox, uint32_t friendnumber, TOX_CONNECTION connection_status, void *user_data) 43 Tox *tox, uint32_t friendnumber, TOX_CONNECTION connection_status, void *user_data)
35{ 44{
36 const uint16_t id = *(uint16_t *)user_data; 45 const State *state = (State *)user_data;
37 46
38 if (connection_status != TOX_CONNECTION_NONE) { 47 if (connection_status != TOX_CONNECTION_NONE) {
39 printf("tox #%d: is now connected to friend %d\n", id, friendnumber); 48 printf("tox #%u: is now connected to friend %u\n", state->id, friendnumber);
40 } else { 49 } else {
41 printf("tox #%d: is now disconnected from friend %d\n", id, friendnumber); 50 printf("tox #%u: is now disconnected from friend %u\n", state->id, friendnumber);
42 } 51 }
43} 52}
44 53
@@ -46,26 +55,44 @@ static void handle_conference_invite(
46 Tox *tox, uint32_t friendnumber, TOX_CONFERENCE_TYPE type, 55 Tox *tox, uint32_t friendnumber, TOX_CONFERENCE_TYPE type,
47 const uint8_t *data, size_t length, void *user_data) 56 const uint8_t *data, size_t length, void *user_data)
48{ 57{
49 const uint16_t id = *(uint16_t *)user_data; 58 const State *state = (State *)user_data;
50 ck_assert_msg(type == TOX_CONFERENCE_TYPE_TEXT, "tox #%d: wrong conference type: %d", id, type); 59 ck_assert_msg(type == TOX_CONFERENCE_TYPE_TEXT, "tox #%u: wrong conference type: %d", state->id, type);
51 60
52 TOX_ERR_CONFERENCE_JOIN err; 61 TOX_ERR_CONFERENCE_JOIN err;
53 uint32_t g_num = tox_conference_join(tox, friendnumber, data, length, &err); 62 uint32_t g_num = tox_conference_join(tox, friendnumber, data, length, &err);
54 63
55 ck_assert_msg(err == TOX_ERR_CONFERENCE_JOIN_OK, "tox #%d: error joining group: %d", id, err); 64 ck_assert_msg(err == TOX_ERR_CONFERENCE_JOIN_OK, "tox #%u: error joining group: %d", state->id, err);
56 ck_assert_msg(g_num == 0, "tox #%d: group number was not 0", id); 65 ck_assert_msg(g_num == 0, "tox #%u: group number was not 0", state->id);
57 66
58 // Try joining again. We should only be allowed to join once. 67 // Try joining again. We should only be allowed to join once.
59 tox_conference_join(tox, friendnumber, data, length, &err); 68 tox_conference_join(tox, friendnumber, data, length, &err);
60 ck_assert_msg(err != TOX_ERR_CONFERENCE_JOIN_OK, 69 ck_assert_msg(err != TOX_ERR_CONFERENCE_JOIN_OK,
61 "tox #%d: joining groupchat twice should be impossible.", id); 70 "tox #%u: joining groupchat twice should be impossible.", state->id);
71}
62 72
63 if (tox_self_get_friend_list_size(tox) > 1) { 73static void handle_conference_peer_list_changed(
64 printf("tox #%d: inviting next friend\n", id); 74 Tox *tox, uint32_t conference_number, void *user_data)
65 ck_assert_msg(tox_conference_invite(tox, 1, g_num, nullptr) != 0, "failed to invite friend"); 75{
66 } else { 76 State *state = (State *)user_data;
67 printf("tox #%d was the last tox, no further invites happening\n", id); 77
78 if (state->invited_next || tox_self_get_friend_list_size(tox) <= 1) {
79 return;
68 } 80 }
81
82 // TODO(zugz): neater way to determine whether we are connected, and when
83 // we become so
84 TOX_ERR_CONFERENCE_PEER_QUERY peer_err;
85 tox_conference_peer_number_is_ours(tox, 0, 0, &peer_err);
86
87 if (peer_err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
88 return;
89 }
90
91 TOX_ERR_CONFERENCE_INVITE err;
92 tox_conference_invite(tox, 1, 0, &err);
93 ck_assert_msg(err == TOX_ERR_CONFERENCE_INVITE_OK, "tox #%u failed to invite next friend: err = %d", state->id, err);
94 printf("tox #%u: invited next friend\n", state->id);
95 state->invited_next = true;
69} 96}
70 97
71static uint16_t num_recv; 98static uint16_t num_recv;
@@ -79,7 +106,7 @@ static void handle_conference_message(
79 } 106 }
80} 107}
81 108
82static void run_conference_tests(Tox **toxes, uint32_t *tox_index) 109static void run_conference_tests(Tox **toxes, State *state)
83{ 110{
84 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { 111 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
85 tox_callback_conference_message(toxes[i], &handle_conference_message); 112 tox_callback_conference_message(toxes[i], &handle_conference_message);
@@ -96,7 +123,7 @@ static void run_conference_tests(Tox **toxes, uint32_t *tox_index)
96 123
97 for (uint8_t j = 0; j < 20; ++j) { 124 for (uint8_t j = 0; j < 20; ++j) {
98 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { 125 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
99 tox_iterate(toxes[i], &tox_index[i]); 126 tox_iterate(toxes[i], &state[i]);
100 } 127 }
101 128
102 c_sleep(25); 129 c_sleep(25);
@@ -105,12 +132,27 @@ static void run_conference_tests(Tox **toxes, uint32_t *tox_index)
105 c_sleep(25); 132 c_sleep(25);
106 ck_assert_msg(num_recv == NUM_GROUP_TOX, "failed to recv group messages"); 133 ck_assert_msg(num_recv == NUM_GROUP_TOX, "failed to recv group messages");
107 134
135 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
136 for (uint16_t j = 0; j < NUM_GROUP_TOX; ++j) {
137 const size_t len = tox_conference_peer_get_name_size(toxes[i], 0, j, nullptr);
138 ck_assert_msg(len == NAMELEN, "name of #%u according to #%u has incorrect length %u", state[j].id, state[i].id,
139 (unsigned int)len);
140 uint8_t name[NAMELEN];
141 tox_conference_peer_get_name(toxes[i], 0, j, name, nullptr);
142 char expected_name[NAMELEN + 1];
143 snprintf(expected_name, NAMELEN + 1, NAME_FORMAT_STR, state[j].id);
144 ck_assert_msg(memcmp(name, expected_name, NAMELEN) == 0,
145 "name of #%u according to #%u is \"" NAME_FORMAT "\"; expected \"%s\"",
146 state[j].id, state[i].id, name, expected_name);
147 }
148 }
149
108 for (uint16_t k = NUM_GROUP_TOX; k != 0 ; --k) { 150 for (uint16_t k = NUM_GROUP_TOX; k != 0 ; --k) {
109 tox_conference_delete(toxes[k - 1], 0, nullptr); 151 tox_conference_delete(toxes[k - 1], 0, nullptr);
110 152
111 for (uint8_t j = 0; j < 10; ++j) { 153 for (uint8_t j = 0; j < 10; ++j) {
112 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { 154 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
113 tox_iterate(toxes[i], &tox_index[i]); 155 tox_iterate(toxes[i], &state[i]);
114 } 156 }
115 157
116 c_sleep(50); 158 c_sleep(50);
@@ -130,7 +172,8 @@ static void test_many_group(void)
130 const time_t test_start_time = time(nullptr); 172 const time_t test_start_time = time(nullptr);
131 173
132 Tox *toxes[NUM_GROUP_TOX]; 174 Tox *toxes[NUM_GROUP_TOX];
133 uint32_t tox_index[NUM_GROUP_TOX]; 175 State state[NUM_GROUP_TOX];
176 memset(state, 0, NUM_GROUP_TOX * sizeof(State));
134 time_t cur_time = time(nullptr); 177 time_t cur_time = time(nullptr);
135 struct Tox_Options *opts = tox_options_new(nullptr); 178 struct Tox_Options *opts = tox_options_new(nullptr);
136 tox_options_set_start_port(opts, 33445); 179 tox_options_set_start_port(opts, 33445);
@@ -140,13 +183,18 @@ static void test_many_group(void)
140 183
141 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { 184 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
142 TOX_ERR_NEW err; 185 TOX_ERR_NEW err;
143 tox_index[i] = i + 1; 186 state[i].id = i + 1;
144 toxes[i] = tox_new_log(opts, &err, &tox_index[i]); 187 toxes[i] = tox_new_log(opts, &err, &state[i]);
145 188
146 ck_assert_msg(toxes[i] != nullptr, "failed to create tox instance %u: error %d", i, err); 189 ck_assert_msg(toxes[i] != nullptr, "failed to create tox instance %u: error %d", i, err);
147 tox_callback_self_connection_status(toxes[i], &handle_self_connection_status); 190 tox_callback_self_connection_status(toxes[i], &handle_self_connection_status);
148 tox_callback_friend_connection_status(toxes[i], &handle_friend_connection_status); 191 tox_callback_friend_connection_status(toxes[i], &handle_friend_connection_status);
149 tox_callback_conference_invite(toxes[i], &handle_conference_invite); 192 tox_callback_conference_invite(toxes[i], &handle_conference_invite);
193 tox_callback_conference_peer_list_changed(toxes[i], &handle_conference_peer_list_changed);
194
195 char name[NAMELEN + 1];
196 snprintf(name, NAMELEN + 1, NAME_FORMAT_STR, state[i].id);
197 tox_self_set_name(toxes[i], (const uint8_t *)name, NAMELEN, nullptr);
150 198
151 if (i != 0) { 199 if (i != 0) {
152 uint8_t dht_key[TOX_PUBLIC_KEY_SIZE]; 200 uint8_t dht_key[TOX_PUBLIC_KEY_SIZE];
@@ -181,11 +229,11 @@ static void test_many_group(void)
181 online_count = 0; 229 online_count = 0;
182 230
183 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { 231 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
184 tox_iterate(toxes[i], &tox_index[i]); 232 tox_iterate(toxes[i], &state[i]);
185 online_count += tox_friend_get_connection_status(toxes[i], 0, nullptr) != TOX_CONNECTION_NONE; 233 online_count += tox_friend_get_connection_status(toxes[i], 0, nullptr) != TOX_CONNECTION_NONE;
186 } 234 }
187 235
188 printf("currently %d toxes are online\n", online_count); 236 printf("currently %u toxes are online\n", online_count);
189 fflush(stdout); 237 fflush(stdout);
190 238
191 c_sleep(1000); 239 c_sleep(1000);
@@ -194,40 +242,57 @@ static void test_many_group(void)
194 printf("friends connected, took %d seconds\n", (int)(time(nullptr) - cur_time)); 242 printf("friends connected, took %d seconds\n", (int)(time(nullptr) - cur_time));
195 243
196 ck_assert_msg(tox_conference_new(toxes[0], nullptr) != UINT32_MAX, "failed to create group"); 244 ck_assert_msg(tox_conference_new(toxes[0], nullptr) != UINT32_MAX, "failed to create group");
197 printf("tox #%d: inviting its first friend\n", tox_index[0]); 245 printf("tox #%u: inviting its first friend\n", state[0].id);
198 ck_assert_msg(tox_conference_invite(toxes[0], 0, 0, nullptr) != 0, "failed to invite friend"); 246 ck_assert_msg(tox_conference_invite(toxes[0], 0, 0, nullptr) != 0, "failed to invite friend");
247 state[0].invited_next = true;
199 ck_assert_msg(tox_conference_set_title(toxes[0], 0, (const uint8_t *)"Gentoo", sizeof("Gentoo") - 1, nullptr) != 0, 248 ck_assert_msg(tox_conference_set_title(toxes[0], 0, (const uint8_t *)"Gentoo", sizeof("Gentoo") - 1, nullptr) != 0,
200 "failed to set group title"); 249 "failed to set group title");
201 250
202 // One iteration for all the invitations to happen. 251
203 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { 252 printf("waiting for invitations to be made\n");
204 tox_iterate(toxes[i], &tox_index[i]); 253 uint16_t invited_count = 0;
254
255 while (invited_count != NUM_GROUP_TOX - 1) {
256 invited_count = 0;
257
258 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
259 tox_iterate(toxes[i], &state[i]);
260 invited_count += state[i].invited_next;
261 }
262
263 c_sleep(50);
205 } 264 }
206 265
207 cur_time = time(nullptr); 266 cur_time = time(nullptr);
208 printf("waiting for all toxes to be in the group\n"); 267 printf("waiting for all toxes to be in the group\n");
209 unsigned invited_count = 0; 268 uint16_t fully_connected_count = 0;
210 269
211 while (invited_count != NUM_GROUP_TOX) { 270 while (fully_connected_count != NUM_GROUP_TOX) {
212 invited_count = 0; 271 fully_connected_count = 0;
213 printf("current peer counts: ["); 272 printf("current peer counts: [");
214 273
215 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { 274 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
216 tox_iterate(toxes[i], &tox_index[i]); 275 tox_iterate(toxes[i], &state[i]);
217 uint32_t peer_count = tox_conference_peer_count(toxes[i], 0, nullptr); 276 TOX_ERR_CONFERENCE_PEER_QUERY err;
218 invited_count += peer_count == NUM_GROUP_TOX; 277 uint32_t peer_count = tox_conference_peer_count(toxes[i], 0, &err);
278
279 if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
280 peer_count = 0;
281 }
282
283 fully_connected_count += peer_count == NUM_GROUP_TOX;
219 284
220 if (i != 0) { 285 if (i != 0) {
221 printf(", "); 286 printf(", ");
222 } 287 }
223 288
224 printf("%d", peer_count); 289 printf("%u", peer_count);
225 } 290 }
226 291
227 printf("]\n"); 292 printf("]\n");
228 fflush(stdout); 293 fflush(stdout);
229 294
230 c_sleep(1000); 295 c_sleep(200);
231 } 296 }
232 297
233 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { 298 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
@@ -246,7 +311,7 @@ static void test_many_group(void)
246 311
247 printf("group connected, took %d seconds\n", (int)(time(nullptr) - cur_time)); 312 printf("group connected, took %d seconds\n", (int)(time(nullptr) - cur_time));
248 313
249 run_conference_tests(toxes, tox_index); 314 run_conference_tests(toxes, state);
250 315
251 printf("tearing down toxes\n"); 316 printf("tearing down toxes\n");
252 317
diff --git a/toxcore/group.c b/toxcore/group.c
index 826ea239..317b885f 100644
--- a/toxcore/group.c
+++ b/toxcore/group.c
@@ -405,8 +405,12 @@ static int connect_to_closest(Group_Chats *g_c, uint32_t groupnumber, void *user
405 get_friendcon_public_keys(real_pk, dht_temp_pk, g_c->fr_c, g->close[i].number); 405 get_friendcon_public_keys(real_pk, dht_temp_pk, g_c->fr_c, g->close[i].number);
406 406
407 if (!pk_in_closest_peers(g, real_pk)) { 407 if (!pk_in_closest_peers(g, real_pk)) {
408 g->close[i].type = GROUPCHAT_CLOSE_NONE; 408 g->close[i].closest = false;
409 kill_friend_connection(g_c->fr_c, g->close[i].number); 409
410 if (!g->close[i].introducer && !g->close[i].introduced) {
411 g->close[i].type = GROUPCHAT_CLOSE_NONE;
412 kill_friend_connection(g_c->fr_c, g->close[i].number);
413 }
410 } 414 }
411 } 415 }
412 416
@@ -618,6 +622,8 @@ static int setnick(Group_Chats *g_c, uint32_t groupnumber, int peer_index, const
618 return -1; 622 return -1;
619 } 623 }
620 624
625 g->group[peer_index].nick_updated = true;
626
621 /* same name as already stored? */ 627 /* same name as already stored? */
622 if (g->group[peer_index].nick_len == nick_len) { 628 if (g->group[peer_index].nick_len == nick_len) {
623 if (nick_len == 0 || !memcmp(g->group[peer_index].nick, nick, nick_len)) { 629 if (nick_len == 0 || !memcmp(g->group[peer_index].nick, nick, nick_len)) {
@@ -740,7 +746,7 @@ static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, uint32_t gr
740 } 746 }
741 747
742 if (g->close[i].number == (uint32_t)friendcon_id) { 748 if (g->close[i].number == (uint32_t)friendcon_id) {
743 g->close[i].closest = closest; 749 g->close[i].closest |= closest;
744 return i; /* Already in list. */ 750 return i; /* Already in list. */
745 } 751 }
746 } 752 }
@@ -756,6 +762,8 @@ static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, uint32_t gr
756 g->close[ind].type = GROUPCHAT_CLOSE_CONNECTION; 762 g->close[ind].type = GROUPCHAT_CLOSE_CONNECTION;
757 g->close[ind].number = friendcon_id; 763 g->close[ind].number = friendcon_id;
758 g->close[ind].closest = closest; 764 g->close[ind].closest = closest;
765 g->close[ind].introducer = false;
766 g->close[ind].introduced = false;
759 // TODO(irungentoo): 767 // TODO(irungentoo):
760 friend_connection_callbacks(g_c->m->fr_c, friendcon_id, GROUPCHAT_CALLBACK_INDEX, &g_handle_status, &g_handle_packet, 768 friend_connection_callbacks(g_c->m->fr_c, friendcon_id, GROUPCHAT_CALLBACK_INDEX, &g_handle_status, &g_handle_packet,
761 &handle_lossy, g_c, friendcon_id); 769 &handle_lossy, g_c, friendcon_id);
@@ -1062,6 +1070,7 @@ static unsigned int send_lossy_group_peer(Friend_Connections *fr_c, int friendco
1062 * return 0 on success. 1070 * return 0 on success.
1063 * return -1 if groupnumber is invalid. 1071 * return -1 if groupnumber is invalid.
1064 * return -2 if invite packet failed to send. 1072 * return -2 if invite packet failed to send.
1073 * return -3 if we are not connected to the group chat.
1065 */ 1074 */
1066int invite_friend(Group_Chats *g_c, uint32_t friendnumber, uint32_t groupnumber) 1075int invite_friend(Group_Chats *g_c, uint32_t friendnumber, uint32_t groupnumber)
1067{ 1076{
@@ -1071,6 +1080,10 @@ int invite_friend(Group_Chats *g_c, uint32_t friendnumber, uint32_t groupnumber)
1071 return -1; 1080 return -1;
1072 } 1081 }
1073 1082
1083 if (g->status != GROUPCHAT_STATUS_CONNECTED) {
1084 return -3;
1085 }
1086
1074 uint8_t invite[INVITE_PACKET_SIZE]; 1087 uint8_t invite[INVITE_PACKET_SIZE];
1075 invite[0] = INVITE_ID; 1088 invite[0] = INVITE_ID;
1076 uint16_t groupchat_num = net_htons((uint16_t)groupnumber); 1089 uint16_t groupchat_num = net_htons((uint16_t)groupnumber);
@@ -1081,7 +1094,6 @@ int invite_friend(Group_Chats *g_c, uint32_t friendnumber, uint32_t groupnumber)
1081 return 0; 1094 return 0;
1082 } 1095 }
1083 1096
1084 wipe_group_chat(g_c, groupnumber);
1085 return -2; 1097 return -2;
1086} 1098}
1087 1099
@@ -1148,6 +1160,7 @@ int join_groupchat(Group_Chats *g_c, uint32_t friendnumber, uint8_t expected_typ
1148 g->close[close_index].group_number = other_groupnum; 1160 g->close[close_index].group_number = other_groupnum;
1149 g->close[close_index].type = GROUPCHAT_CLOSE_ONLINE; 1161 g->close[close_index].type = GROUPCHAT_CLOSE_ONLINE;
1150 g->number_joined = friendcon_id; 1162 g->number_joined = friendcon_id;
1163 g->close[close_index].introducer = true;
1151 } 1164 }
1152 1165
1153 send_peer_query(g_c, friendcon_id, other_groupnum); 1166 send_peer_query(g_c, friendcon_id, other_groupnum);
@@ -1503,6 +1516,7 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con
1503 if (close_index != -1) { 1516 if (close_index != -1) {
1504 g->close[close_index].group_number = other_groupnum; 1517 g->close[close_index].group_number = other_groupnum;
1505 g->close[close_index].type = GROUPCHAT_CLOSE_ONLINE; 1518 g->close[close_index].type = GROUPCHAT_CLOSE_ONLINE;
1519 g->close[close_index].introduced = true;
1506 } 1520 }
1507 1521
1508 group_new_peer_send(g_c, groupnum, peer_number, real_pk, temp_pk); 1522 group_new_peer_send(g_c, groupnum, peer_number, real_pk, temp_pk);
@@ -1606,21 +1620,6 @@ static int handle_packet_online(Group_Chats *g_c, int friendcon_id, const uint8_
1606 g->close[index].type = GROUPCHAT_CLOSE_ONLINE; 1620 g->close[index].type = GROUPCHAT_CLOSE_ONLINE;
1607 send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->identifier); 1621 send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->identifier);
1608 1622
1609 if (g->number_joined != -1 && count_close_connected(g) >= DESIRED_CLOSE_CONNECTIONS) {
1610 int fr_close_index = friend_in_close(g, g->number_joined);
1611
1612 if (fr_close_index == -1) {
1613 return -1;
1614 }
1615
1616 if (!g->close[fr_close_index].closest) {
1617 g->close[fr_close_index].type = GROUPCHAT_CLOSE_NONE;
1618 send_peer_kill(g_c, g->close[fr_close_index].number, g->close[fr_close_index].group_number);
1619 kill_friend_connection(g_c->fr_c, g->close[fr_close_index].number);
1620 g->number_joined = -1;
1621 }
1622 }
1623
1624 return 0; 1623 return 0;
1625} 1624}
1626 1625
@@ -1729,11 +1728,6 @@ static int handle_send_peers(Group_Chats *g_c, uint32_t groupnumber, const uint8
1729 memcpy(&peer_num, d, sizeof(peer_num)); 1728 memcpy(&peer_num, d, sizeof(peer_num));
1730 peer_num = net_ntohs(peer_num); 1729 peer_num = net_ntohs(peer_num);
1731 d += sizeof(uint16_t); 1730 d += sizeof(uint16_t);
1732 int peer_index = addpeer(g_c, groupnumber, d, d + CRYPTO_PUBLIC_KEY_SIZE, peer_num, userdata, true);
1733
1734 if (peer_index == -1) {
1735 return -1;
1736 }
1737 1731
1738 if (g->status == GROUPCHAT_STATUS_VALID 1732 if (g->status == GROUPCHAT_STATUS_VALID
1739 && public_key_cmp(d, nc_get_self_public_key(g_c->m->net_crypto)) == 0) { 1733 && public_key_cmp(d, nc_get_self_public_key(g_c->m->net_crypto)) == 0) {
@@ -1742,6 +1736,12 @@ static int handle_send_peers(Group_Chats *g_c, uint32_t groupnumber, const uint8
1742 group_name_send(g_c, groupnumber, g_c->m->name, g_c->m->name_length); 1736 group_name_send(g_c, groupnumber, g_c->m->name, g_c->m->name_length);
1743 } 1737 }
1744 1738
1739 int peer_index = addpeer(g_c, groupnumber, d, d + CRYPTO_PUBLIC_KEY_SIZE, peer_num, userdata, true);
1740
1741 if (peer_index == -1) {
1742 return -1;
1743 }
1744
1745 d += CRYPTO_PUBLIC_KEY_SIZE * 2; 1745 d += CRYPTO_PUBLIC_KEY_SIZE * 2;
1746 uint8_t name_length = *d; 1746 uint8_t name_length = *d;
1747 d += 1; 1747 d += 1;
@@ -1750,7 +1750,10 @@ static int handle_send_peers(Group_Chats *g_c, uint32_t groupnumber, const uint8
1750 return -1; 1750 return -1;
1751 } 1751 }
1752 1752
1753 setnick(g_c, groupnumber, peer_index, d, name_length, userdata, true); 1753 if (!g->group[peer_index].nick_updated) {
1754 setnick(g_c, groupnumber, peer_index, d, name_length, userdata, true);
1755 }
1756
1754 d += name_length; 1757 d += name_length;
1755 } 1758 }
1756 1759
@@ -2037,6 +2040,58 @@ int send_group_lossy_packet(const Group_Chats *g_c, uint32_t groupnumber, const
2037 return 0; 2040 return 0;
2038} 2041}
2039 2042
2043static Message_Info *find_message_slot_or_reject(uint32_t message_number, uint8_t message_id, Group_Peer *peer)
2044{
2045 const bool ignore_older = (message_id == GROUP_MESSAGE_NAME_ID || message_id == GROUP_MESSAGE_TITLE_ID);
2046
2047 Message_Info *i;
2048
2049 for (i = peer->last_message_infos; i < peer->last_message_infos + peer->num_last_message_infos; ++i) {
2050 if (message_number > i->message_number) {
2051 break;
2052 }
2053
2054 if (message_number == i->message_number) {
2055 return nullptr;
2056 }
2057
2058 if (ignore_older && message_id == i->message_id) {
2059 return nullptr;
2060 }
2061 }
2062
2063 return i;
2064}
2065
2066/* Stores message info in peer->last_message_infos.
2067 *
2068 * return true if message should be processed;
2069 * return false otherwise.
2070 */
2071static bool check_message_info(uint32_t message_number, uint8_t message_id, Group_Peer *peer)
2072{
2073 Message_Info *const i = find_message_slot_or_reject(message_number, message_id, peer);
2074
2075 if (i == nullptr) {
2076 return false;
2077 }
2078
2079 if (i == peer->last_message_infos + MAX_LAST_MESSAGE_INFOS) {
2080 return false;
2081 }
2082
2083 if (peer->num_last_message_infos < MAX_LAST_MESSAGE_INFOS) {
2084 ++peer->num_last_message_infos;
2085 }
2086
2087 memmove(i + 1, i, ((peer->last_message_infos + peer->num_last_message_infos - 1) - i) * sizeof(Message_Info));
2088
2089 i->message_number = message_number;
2090 i->message_id = message_id;
2091
2092 return true;
2093}
2094
2040static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length, 2095static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length,
2041 int close_index, void *userdata) 2096 int close_index, void *userdata)
2042{ 2097{
@@ -2063,23 +2118,41 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber,
2063 return; 2118 return;
2064 } 2119 }
2065 2120
2121 if (g->number_joined != -1 && count_close_connected(g) >= DESIRED_CLOSE_CONNECTIONS) {
2122 const int fr_close_index = friend_in_close(g, g->number_joined);
2123
2124 if (fr_close_index >= 0 && fr_close_index != close_index && !g->close[fr_close_index].closest) {
2125 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
2126 get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->close[fr_close_index].number);
2127
2128 if (id_equal(g->group[index].real_pk, real_pk)) {
2129 /* Received message from peer relayed via another peer, so
2130 * the introduction was successful */
2131 g->number_joined = -1;
2132 g->close[fr_close_index].introducer = false;
2133
2134 if (!g->close[fr_close_index].closest && !g->close[fr_close_index].introduced) {
2135 g->close[fr_close_index].type = GROUPCHAT_CLOSE_NONE;
2136 send_peer_kill(g_c, g->close[fr_close_index].number, g->close[fr_close_index].group_number);
2137 kill_friend_connection(g_c->fr_c, g->close[fr_close_index].number);
2138 }
2139 }
2140 }
2141 }
2142
2066 uint32_t message_number; 2143 uint32_t message_number;
2067 memcpy(&message_number, data + sizeof(uint16_t), sizeof(message_number)); 2144 memcpy(&message_number, data + sizeof(uint16_t), sizeof(message_number));
2068 message_number = net_ntohl(message_number); 2145 message_number = net_ntohl(message_number);
2069 2146
2070 if (g->group[index].last_message_number == 0) {
2071 g->group[index].last_message_number = message_number;
2072 } else if (message_number - g->group[index].last_message_number > 64 ||
2073 message_number == g->group[index].last_message_number) {
2074 return;
2075 }
2076
2077 g->group[index].last_message_number = message_number;
2078
2079 uint8_t message_id = data[sizeof(uint16_t) + sizeof(message_number)]; 2147 uint8_t message_id = data[sizeof(uint16_t) + sizeof(message_number)];
2080 const uint8_t *msg_data = data + sizeof(uint16_t) + sizeof(message_number) + 1; 2148 const uint8_t *msg_data = data + sizeof(uint16_t) + sizeof(message_number) + 1;
2081 uint16_t msg_data_len = length - (sizeof(uint16_t) + sizeof(message_number) + 1); 2149 uint16_t msg_data_len = length - (sizeof(uint16_t) + sizeof(message_number) + 1);
2082 2150
2151 // FIXME(zugz) update discussion of message numbers in the spec
2152 if (!check_message_info(message_number, message_id, &g->group[index])) {
2153 return;
2154 }
2155
2083 switch (message_id) { 2156 switch (message_id) {
2084 case GROUP_MESSAGE_PING_ID: { 2157 case GROUP_MESSAGE_PING_ID: {
2085 if (msg_data_len != 0) { 2158 if (msg_data_len != 0) {
diff --git a/toxcore/group.h b/toxcore/group.h
index 9b4541c4..970cf7fb 100644
--- a/toxcore/group.h
+++ b/toxcore/group.h
@@ -39,15 +39,26 @@ typedef enum Groupchat_Type {
39 39
40#define MAX_LOSSY_COUNT 256 40#define MAX_LOSSY_COUNT 256
41 41
42typedef struct Message_Info {
43 uint32_t message_number;
44 uint8_t message_id;
45} Message_Info;
46
47#define MAX_LAST_MESSAGE_INFOS 8
48
42typedef struct Group_Peer { 49typedef struct Group_Peer {
43 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; 50 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
44 uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; 51 uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
45 52
46 uint64_t last_recv; 53 uint64_t last_recv;
47 uint32_t last_message_number; 54
55 Message_Info
56 last_message_infos[MAX_LAST_MESSAGE_INFOS]; /* received messages, strictly decreasing in message_number */
57 uint8_t num_last_message_infos;
48 58
49 uint8_t nick[MAX_NAME_LENGTH]; 59 uint8_t nick[MAX_NAME_LENGTH];
50 uint8_t nick_len; 60 uint8_t nick_len;
61 bool nick_updated;
51 62
52 uint16_t peer_number; 63 uint16_t peer_number;
53 64
@@ -70,7 +81,9 @@ typedef enum Groupchat_Close_Type {
70 81
71typedef struct Groupchat_Close { 82typedef struct Groupchat_Close {
72 uint8_t type; /* GROUPCHAT_CLOSE_* */ 83 uint8_t type; /* GROUPCHAT_CLOSE_* */
73 uint8_t closest; 84 bool closest; /* connected to peer because it is one of our closest peers */
85 bool introducer; /* connected to peer because it introduced us to the group */
86 bool introduced; /* connected to peer because we introduced it to the group */
74 uint32_t number; 87 uint32_t number;
75 uint16_t group_number; 88 uint16_t group_number;
76} Groupchat_Close; 89} Groupchat_Close;
@@ -91,6 +104,7 @@ typedef struct Group_c {
91 Group_Peer *group; 104 Group_Peer *group;
92 uint32_t numpeers; 105 uint32_t numpeers;
93 106
107 /* TODO(zugz) rename close to something more accurate - "connected"? */
94 Groupchat_Close close[MAX_GROUP_CONNECTIONS]; 108 Groupchat_Close close[MAX_GROUP_CONNECTIONS];
95 109
96 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; 110 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
diff --git a/toxcore/tox.api.h b/toxcore/tox.api.h
index 6cb3145b..7cc58e3b 100644
--- a/toxcore/tox.api.h
+++ b/toxcore/tox.api.h
@@ -2306,6 +2306,10 @@ namespace conference {
2306 * The invite packet failed to send. 2306 * The invite packet failed to send.
2307 */ 2307 */
2308 FAIL_SEND, 2308 FAIL_SEND,
2309 /**
2310 * The client is not connected to the conference.
2311 */
2312 NO_CONNECTION,
2309 } 2313 }
2310 2314
2311 2315
diff --git a/toxcore/tox.c b/toxcore/tox.c
index 101494cc..750a52fa 100644
--- a/toxcore/tox.c
+++ b/toxcore/tox.c
@@ -1290,6 +1290,10 @@ bool tox_conference_invite(Tox *tox, uint32_t friend_number, uint32_t conference
1290 case -2: 1290 case -2:
1291 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_INVITE_FAIL_SEND); 1291 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_INVITE_FAIL_SEND);
1292 return false; 1292 return false;
1293
1294 case -3:
1295 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_INVITE_NO_CONNECTION);
1296 return false;
1293 } 1297 }
1294 1298
1295 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_INVITE_OK); 1299 SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_INVITE_OK);
diff --git a/toxcore/tox.h b/toxcore/tox.h
index 4c83ca23..35ff19e7 100644
--- a/toxcore/tox.h
+++ b/toxcore/tox.h
@@ -2613,6 +2613,11 @@ typedef enum TOX_ERR_CONFERENCE_INVITE {
2613 */ 2613 */
2614 TOX_ERR_CONFERENCE_INVITE_FAIL_SEND, 2614 TOX_ERR_CONFERENCE_INVITE_FAIL_SEND,
2615 2615
2616 /**
2617 * The client is not connected to the conference.
2618 */
2619 TOX_ERR_CONFERENCE_INVITE_NO_CONNECTION,
2620
2616} TOX_ERR_CONFERENCE_INVITE; 2621} TOX_ERR_CONFERENCE_INVITE;
2617 2622
2618 2623