summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzugz <mbays+tox@sdf.org>2018-07-25 08:43:48 +0100
committerzugz (tox) <mbays+tox@sdf.org>2018-09-05 20:56:26 +0200
commit1b2322284f0b688af3a349fe4331be15a565084c (patch)
tree7651c11b4ec1edd927151e9655727fb0b2365a7c
parent6872c14e1a02445d945623ee6e85230c5d7ecbce (diff)
Add mechanism for recovering from disconnections in conferences
* add freezing and unfreezing of peers * add rejoin packet * revise handling of temporary invited connections * rename "peer kill" packet to "peer leave" packet * test rejoining in conference test * use custom clock in conference test
-rw-r--r--auto_tests/conference_double_invite_test.c2
-rw-r--r--auto_tests/conference_peer_nick_test.c2
-rw-r--r--auto_tests/conference_test.c237
-rw-r--r--auto_tests/friend_connection_test.c2
-rw-r--r--auto_tests/lossless_packet_test.c2
-rw-r--r--auto_tests/lossy_packet_test.c2
-rw-r--r--auto_tests/overflow_recvq_test.c2
-rw-r--r--auto_tests/overflow_sendq_test.c2
-rw-r--r--auto_tests/reconnect_test.c2
-rw-r--r--auto_tests/run_auto_test.h27
-rw-r--r--auto_tests/send_message_test.c2
-rw-r--r--docs/minpgc.md128
-rw-r--r--toxcore/group.c613
-rw-r--r--toxcore/group.h26
-rw-r--r--toxcore/net_crypto.h1
15 files changed, 773 insertions, 277 deletions
diff --git a/auto_tests/conference_double_invite_test.c b/auto_tests/conference_double_invite_test.c
index 36e93bc0..ba173a90 100644
--- a/auto_tests/conference_double_invite_test.c
+++ b/auto_tests/conference_double_invite_test.c
@@ -81,6 +81,6 @@ int main(void)
81{ 81{
82 setvbuf(stdout, nullptr, _IONBF, 0); 82 setvbuf(stdout, nullptr, _IONBF, 0);
83 83
84 run_auto_test(2, conference_double_invite_test); 84 run_auto_test(2, conference_double_invite_test, false);
85 return 0; 85 return 0;
86} 86}
diff --git a/auto_tests/conference_peer_nick_test.c b/auto_tests/conference_peer_nick_test.c
index eb9bee71..d8d2d64b 100644
--- a/auto_tests/conference_peer_nick_test.c
+++ b/auto_tests/conference_peer_nick_test.c
@@ -129,6 +129,6 @@ int main(void)
129{ 129{
130 setvbuf(stdout, nullptr, _IONBF, 0); 130 setvbuf(stdout, nullptr, _IONBF, 0);
131 131
132 run_auto_test(2, conference_peer_nick_test); 132 run_auto_test(2, conference_peer_nick_test, false);
133 return 0; 133 return 0;
134} 134}
diff --git a/auto_tests/conference_test.c b/auto_tests/conference_test.c
index 235200cd..fb6e7ca8 100644
--- a/auto_tests/conference_test.c
+++ b/auto_tests/conference_test.c
@@ -16,26 +16,31 @@
16#include "check_compat.h" 16#include "check_compat.h"
17 17
18#define NUM_GROUP_TOX 16 18#define NUM_GROUP_TOX 16
19#define NUM_DISCONNECT 8
19#define GROUP_MESSAGE "Install Gentoo" 20#define GROUP_MESSAGE "Install Gentoo"
20 21
21#define NAME_FORMAT_STR "Tox #%4u"
22#define NAMELEN 9 22#define NAMELEN 9
23#define NAME_FORMAT "%9s" 23#define NAME_FORMAT_STR "Tox #%4u"
24#define NEW_NAME_FORMAT_STR "New #%4u"
24 25
25typedef struct State { 26typedef struct State {
26 uint32_t id; 27 uint32_t index;
28 uint64_t clock;
29
27 bool invited_next; 30 bool invited_next;
28} State; 31} State;
29 32
33#include "run_auto_test.h"
34
30static void handle_self_connection_status( 35static void handle_self_connection_status(
31 Tox *tox, TOX_CONNECTION connection_status, void *user_data) 36 Tox *tox, TOX_CONNECTION connection_status, void *user_data)
32{ 37{
33 const State *state = (State *)user_data; 38 const State *state = (State *)user_data;
34 39
35 if (connection_status != TOX_CONNECTION_NONE) { 40 if (connection_status != TOX_CONNECTION_NONE) {
36 printf("tox #%u: is now connected\n", state->id); 41 printf("tox #%u: is now connected\n", state->index);
37 } else { 42 } else {
38 printf("tox #%u: is now disconnected\n", state->id); 43 printf("tox #%u: is now disconnected\n", state->index);
39 } 44 }
40} 45}
41 46
@@ -45,9 +50,9 @@ static void handle_friend_connection_status(
45 const State *state = (State *)user_data; 50 const State *state = (State *)user_data;
46 51
47 if (connection_status != TOX_CONNECTION_NONE) { 52 if (connection_status != TOX_CONNECTION_NONE) {
48 printf("tox #%u: is now connected to friend %u\n", state->id, friendnumber); 53 printf("tox #%u: is now connected to friend %u\n", state->index, friendnumber);
49 } else { 54 } else {
50 printf("tox #%u: is now disconnected from friend %u\n", state->id, friendnumber); 55 printf("tox #%u: is now disconnected from friend %u\n", state->index, friendnumber);
51 } 56 }
52} 57}
53 58
@@ -56,18 +61,18 @@ static void handle_conference_invite(
56 const uint8_t *data, size_t length, void *user_data) 61 const uint8_t *data, size_t length, void *user_data)
57{ 62{
58 const State *state = (State *)user_data; 63 const State *state = (State *)user_data;
59 ck_assert_msg(type == TOX_CONFERENCE_TYPE_TEXT, "tox #%u: wrong conference type: %d", state->id, type); 64 ck_assert_msg(type == TOX_CONFERENCE_TYPE_TEXT, "tox #%u: wrong conference type: %d", state->index, type);
60 65
61 TOX_ERR_CONFERENCE_JOIN err; 66 TOX_ERR_CONFERENCE_JOIN err;
62 uint32_t g_num = tox_conference_join(tox, friendnumber, data, length, &err); 67 uint32_t g_num = tox_conference_join(tox, friendnumber, data, length, &err);
63 68
64 ck_assert_msg(err == TOX_ERR_CONFERENCE_JOIN_OK, "tox #%u: error joining group: %d", state->id, err); 69 ck_assert_msg(err == TOX_ERR_CONFERENCE_JOIN_OK, "tox #%u: error joining group: %d", state->index, err);
65 ck_assert_msg(g_num == 0, "tox #%u: group number was not 0", state->id); 70 ck_assert_msg(g_num == 0, "tox #%u: group number was not 0", state->index);
66 71
67 // Try joining again. We should only be allowed to join once. 72 // Try joining again. We should only be allowed to join once.
68 tox_conference_join(tox, friendnumber, data, length, &err); 73 tox_conference_join(tox, friendnumber, data, length, &err);
69 ck_assert_msg(err != TOX_ERR_CONFERENCE_JOIN_OK, 74 ck_assert_msg(err != TOX_ERR_CONFERENCE_JOIN_OK,
70 "tox #%u: joining groupchat twice should be impossible.", state->id); 75 "tox #%u: joining groupchat twice should be impossible.", state->index);
71} 76}
72 77
73static void handle_conference_connected( 78static void handle_conference_connected(
@@ -81,8 +86,8 @@ static void handle_conference_connected(
81 86
82 TOX_ERR_CONFERENCE_INVITE err; 87 TOX_ERR_CONFERENCE_INVITE err;
83 tox_conference_invite(tox, 1, 0, &err); 88 tox_conference_invite(tox, 1, 0, &err);
84 ck_assert_msg(err == TOX_ERR_CONFERENCE_INVITE_OK, "tox #%u failed to invite next friend: err = %d", state->id, err); 89 ck_assert_msg(err == TOX_ERR_CONFERENCE_INVITE_OK, "tox #%u failed to invite next friend: err = %d", state->index, err);
85 printf("tox #%u: invited next friend\n", state->id); 90 printf("tox #%u: invited next friend\n", state->index);
86 state->invited_next = true; 91 state->invited_next = true;
87} 92}
88 93
@@ -97,8 +102,93 @@ static void handle_conference_message(
97 } 102 }
98} 103}
99 104
105static bool toxes_are_disconnected_from_group(uint32_t tox_count, Tox **toxes, int disconnected_count,
106 bool *disconnected)
107{
108 for (uint32_t i = 0; i < tox_count; i++) {
109 if (disconnected[i]) {
110 continue;
111 }
112
113 if (tox_conference_peer_count(toxes[i], 0, nullptr) > tox_count - NUM_DISCONNECT) {
114 return false;
115 }
116 }
117
118 return true;
119}
120
121static bool all_connected_to_group(uint32_t tox_count, Tox **toxes)
122{
123 for (uint32_t i = 0; i < tox_count; i++) {
124 if (tox_conference_peer_count(toxes[i], 0, nullptr) < tox_count) {
125 return false;
126 }
127 }
128
129 return true;
130}
131
132/* returns a random index at which a list of booleans is false
133 * (some such index is required to exist)
134 * */
135static uint32_t random_false_index(bool *list, const uint32_t length)
136{
137 uint32_t index;
138
139 do {
140 index = random_u32() % length;
141 } while (list[index]);
142
143 return index;
144}
145
100static void run_conference_tests(Tox **toxes, State *state) 146static void run_conference_tests(Tox **toxes, State *state)
101{ 147{
148 /* disabling name propagation check for now, as it occasionally fails due
149 * to disconnections too short to trigger freezing */
150 const bool check_name_propagation = false;
151
152 printf("letting random toxes timeout\n");
153 bool disconnected[NUM_GROUP_TOX] = {0};
154
155 ck_assert(NUM_DISCONNECT < NUM_GROUP_TOX);
156
157 for (uint16_t i = 0; i < NUM_DISCONNECT; ++i) {
158 uint32_t disconnect = random_false_index(disconnected, NUM_GROUP_TOX);
159 disconnected[disconnect] = true;
160 printf("Disconnecting #%u\n", state[disconnect].index);
161 }
162
163 do {
164 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
165 if (!disconnected[i]) {
166 tox_iterate(toxes[i], &state[i]);
167 state[i].clock += 1000;
168 }
169 }
170
171 c_sleep(20);
172 } while (!toxes_are_disconnected_from_group(NUM_GROUP_TOX, toxes, NUM_DISCONNECT, disconnected));
173
174 if (check_name_propagation) {
175 printf("changing names\n");
176
177 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
178 char name[NAMELEN + 1];
179 snprintf(name, NAMELEN + 1, NEW_NAME_FORMAT_STR, state[i].index);
180 tox_self_set_name(toxes[i], (const uint8_t *)name, NAMELEN, nullptr);
181 }
182 }
183
184 printf("reconnecting toxes\n");
185
186 do {
187 iterate_all_wait(NUM_GROUP_TOX, toxes, state, ITERATION_INTERVAL);
188 } while (!all_connected_to_group(NUM_GROUP_TOX, toxes));
189
190 printf("running conference tests\n");
191
102 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { 192 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
103 tox_callback_conference_message(toxes[i], &handle_conference_message); 193 tox_callback_conference_message(toxes[i], &handle_conference_message);
104 } 194 }
@@ -112,41 +202,33 @@ static void run_conference_tests(Tox **toxes, State *state)
112 err == TOX_ERR_CONFERENCE_SEND_MESSAGE_OK, "failed to send group message"); 202 err == TOX_ERR_CONFERENCE_SEND_MESSAGE_OK, "failed to send group message");
113 num_recv = 0; 203 num_recv = 0;
114 204
115 for (uint8_t j = 0; j < 20; ++j) { 205 for (uint8_t j = 0; j < NUM_GROUP_TOX * 2; ++j) {
116 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { 206 iterate_all_wait(NUM_GROUP_TOX, toxes, state, ITERATION_INTERVAL);
117 tox_iterate(toxes[i], &state[i]);
118 }
119
120 c_sleep(25);
121 } 207 }
122 208
123 c_sleep(25);
124 ck_assert_msg(num_recv == NUM_GROUP_TOX, "failed to recv group messages"); 209 ck_assert_msg(num_recv == NUM_GROUP_TOX, "failed to recv group messages");
125 210
126 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { 211 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
127 for (uint16_t j = 0; j < NUM_GROUP_TOX; ++j) { 212 for (uint16_t j = 0; j < NUM_GROUP_TOX; ++j) {
128 const size_t len = tox_conference_peer_get_name_size(toxes[i], 0, j, nullptr); 213 const size_t len = tox_conference_peer_get_name_size(toxes[i], 0, j, nullptr);
129 ck_assert_msg(len == NAMELEN, "name of #%u according to #%u has incorrect length %u", state[j].id, state[i].id, 214 ck_assert_msg(len == NAMELEN, "name of #%u according to #%u has incorrect length %u", state[j].index, state[i].index,
130 (unsigned int)len); 215 (unsigned int)len);
131 uint8_t name[NAMELEN]; 216
132 tox_conference_peer_get_name(toxes[i], 0, j, name, nullptr); 217 if (check_name_propagation) {
133 char expected_name[NAMELEN + 1]; 218 uint8_t name[NAMELEN];
134 snprintf(expected_name, NAMELEN + 1, NAME_FORMAT_STR, state[j].id); 219 tox_conference_peer_get_name(toxes[i], 0, j, name, nullptr);
135 ck_assert_msg(memcmp(name, expected_name, NAMELEN) == 0, 220 /* Note the toxes will have been reordered */
136 "name of #%u according to #%u is \"" NAME_FORMAT "\"; expected \"%s\"", 221 ck_assert_msg(memcmp(name, "New", 3) == 0,
137 state[j].id, state[i].id, name, expected_name); 222 "name of #%u according to #%u not updated", state[j].index, state[i].index);
223 }
138 } 224 }
139 } 225 }
140 226
141 for (uint16_t k = NUM_GROUP_TOX; k != 0 ; --k) { 227 for (uint16_t k = NUM_GROUP_TOX; k != 0 ; --k) {
142 tox_conference_delete(toxes[k - 1], 0, nullptr); 228 tox_conference_delete(toxes[k - 1], 0, nullptr);
143 229
144 for (uint8_t j = 0; j < 10; ++j) { 230 for (uint8_t j = 0; j < 10 || j < NUM_GROUP_TOX; ++j) {
145 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { 231 iterate_all_wait(NUM_GROUP_TOX, toxes, state, ITERATION_INTERVAL);
146 tox_iterate(toxes[i], &state[i]);
147 }
148
149 c_sleep(50);
150 } 232 }
151 233
152 for (uint16_t i = 0; i < k - 1; ++i) { 234 for (uint16_t i = 0; i < k - 1; ++i) {
@@ -158,82 +240,23 @@ static void run_conference_tests(Tox **toxes, State *state)
158 } 240 }
159} 241}
160 242
161static void test_many_group(void) 243static void test_many_group(Tox **toxes, State *state)
162{ 244{
163 const time_t test_start_time = time(nullptr); 245 const time_t test_start_time = time(nullptr);
164 246
165 Tox *toxes[NUM_GROUP_TOX];
166 State state[NUM_GROUP_TOX];
167 memset(state, 0, NUM_GROUP_TOX * sizeof(State));
168 time_t cur_time = time(nullptr);
169 struct Tox_Options *opts = tox_options_new(nullptr);
170 tox_options_set_start_port(opts, 33445);
171 tox_options_set_end_port(opts, 34445);
172
173 printf("creating %d toxes\n", NUM_GROUP_TOX);
174
175 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { 247 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
176 TOX_ERR_NEW err;
177 state[i].id = i + 1;
178 toxes[i] = tox_new_log(opts, &err, &state[i]);
179
180 ck_assert_msg(toxes[i] != nullptr, "failed to create tox instance %u: error %d", i, err);
181 tox_callback_self_connection_status(toxes[i], &handle_self_connection_status); 248 tox_callback_self_connection_status(toxes[i], &handle_self_connection_status);
182 tox_callback_friend_connection_status(toxes[i], &handle_friend_connection_status); 249 tox_callback_friend_connection_status(toxes[i], &handle_friend_connection_status);
183 tox_callback_conference_invite(toxes[i], &handle_conference_invite); 250 tox_callback_conference_invite(toxes[i], &handle_conference_invite);
184 tox_callback_conference_connected(toxes[i], &handle_conference_connected); 251 tox_callback_conference_connected(toxes[i], &handle_conference_connected);
185 252
186 char name[NAMELEN + 1]; 253 char name[NAMELEN + 1];
187 snprintf(name, NAMELEN + 1, NAME_FORMAT_STR, state[i].id); 254 snprintf(name, NAMELEN + 1, NAME_FORMAT_STR, state[i].index);
188 tox_self_set_name(toxes[i], (const uint8_t *)name, NAMELEN, nullptr); 255 tox_self_set_name(toxes[i], (const uint8_t *)name, NAMELEN, nullptr);
189
190 if (i != 0) {
191 uint8_t dht_key[TOX_PUBLIC_KEY_SIZE];
192 tox_self_get_dht_id(toxes[0], dht_key);
193 const uint16_t dht_port = tox_self_get_udp_port(toxes[0], nullptr);
194
195 tox_bootstrap(toxes[i], "localhost", dht_port, dht_key, nullptr);
196 }
197 } 256 }
198 257
199 tox_options_free(opts);
200
201 printf("creating a chain of friends\n");
202
203 for (unsigned i = 1; i < NUM_GROUP_TOX; ++i) {
204 TOX_ERR_FRIEND_ADD err;
205 uint8_t key[TOX_PUBLIC_KEY_SIZE];
206
207 tox_self_get_public_key(toxes[i - 1], key);
208 tox_friend_add_norequest(toxes[i], key, &err);
209 ck_assert_msg(err == TOX_ERR_FRIEND_ADD_OK, "failed to add friend: error %d", err);
210
211 tox_self_get_public_key(toxes[i], key);
212 tox_friend_add_norequest(toxes[i - 1], key, &err);
213 ck_assert_msg(err == TOX_ERR_FRIEND_ADD_OK, "failed to add friend: error %d", err);
214 }
215
216 printf("waiting for everyone to come online\n");
217 unsigned online_count = 0;
218
219 do {
220 online_count = 0;
221
222 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
223 tox_iterate(toxes[i], &state[i]);
224 online_count += tox_friend_get_connection_status(toxes[i], 0, nullptr) != TOX_CONNECTION_NONE;
225 }
226
227 printf("currently %u toxes are online\n", online_count);
228 fflush(stdout);
229
230 c_sleep(1000);
231 } while (online_count != NUM_GROUP_TOX);
232
233 printf("friends connected, took %d seconds\n", (int)(time(nullptr) - cur_time));
234
235 ck_assert_msg(tox_conference_new(toxes[0], nullptr) != UINT32_MAX, "failed to create group"); 258 ck_assert_msg(tox_conference_new(toxes[0], nullptr) != UINT32_MAX, "failed to create group");
236 printf("tox #%u: inviting its first friend\n", state[0].id); 259 printf("tox #%u: inviting its first friend\n", state[0].index);
237 ck_assert_msg(tox_conference_invite(toxes[0], 0, 0, nullptr) != 0, "failed to invite friend"); 260 ck_assert_msg(tox_conference_invite(toxes[0], 0, 0, nullptr) != 0, "failed to invite friend");
238 state[0].invited_next = true; 261 state[0].invited_next = true;
239 ck_assert_msg(tox_conference_set_title(toxes[0], 0, (const uint8_t *)"Gentoo", sizeof("Gentoo") - 1, nullptr) != 0, 262 ck_assert_msg(tox_conference_set_title(toxes[0], 0, (const uint8_t *)"Gentoo", sizeof("Gentoo") - 1, nullptr) != 0,
@@ -244,17 +267,16 @@ static void test_many_group(void)
244 uint16_t invited_count = 0; 267 uint16_t invited_count = 0;
245 268
246 do { 269 do {
270 iterate_all_wait(NUM_GROUP_TOX, toxes, state, ITERATION_INTERVAL);
271
247 invited_count = 0; 272 invited_count = 0;
248 273
249 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { 274 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
250 tox_iterate(toxes[i], &state[i]);
251 invited_count += state[i].invited_next; 275 invited_count += state[i].invited_next;
252 } 276 }
253
254 c_sleep(50);
255 } while (invited_count != NUM_GROUP_TOX - 1); 277 } while (invited_count != NUM_GROUP_TOX - 1);
256 278
257 cur_time = time(nullptr); 279 uint64_t pregroup_clock = state[0].clock;
258 printf("waiting for all toxes to be in the group\n"); 280 printf("waiting for all toxes to be in the group\n");
259 uint16_t fully_connected_count = 0; 281 uint16_t fully_connected_count = 0;
260 282
@@ -262,8 +284,9 @@ static void test_many_group(void)
262 fully_connected_count = 0; 284 fully_connected_count = 0;
263 printf("current peer counts: ["); 285 printf("current peer counts: [");
264 286
287 iterate_all_wait(NUM_GROUP_TOX, toxes, state, ITERATION_INTERVAL);
288
265 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { 289 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
266 tox_iterate(toxes[i], &state[i]);
267 TOX_ERR_CONFERENCE_PEER_QUERY err; 290 TOX_ERR_CONFERENCE_PEER_QUERY err;
268 uint32_t peer_count = tox_conference_peer_count(toxes[i], 0, &err); 291 uint32_t peer_count = tox_conference_peer_count(toxes[i], 0, &err);
269 292
@@ -282,8 +305,6 @@ static void test_many_group(void)
282 305
283 printf("]\n"); 306 printf("]\n");
284 fflush(stdout); 307 fflush(stdout);
285
286 c_sleep(200);
287 } while (fully_connected_count != NUM_GROUP_TOX); 308 } while (fully_connected_count != NUM_GROUP_TOX);
288 309
289 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { 310 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
@@ -300,16 +321,10 @@ static void test_many_group(void)
300 ck_assert_msg(memcmp("Gentoo", title, ret) == 0, "Wrong title"); 321 ck_assert_msg(memcmp("Gentoo", title, ret) == 0, "Wrong title");
301 } 322 }
302 323
303 printf("group connected, took %d seconds\n", (int)(time(nullptr) - cur_time)); 324 printf("group connected, took %d seconds\n", (int)((state[0].clock - pregroup_clock) / 1000));
304 325
305 run_conference_tests(toxes, state); 326 run_conference_tests(toxes, state);
306 327
307 printf("tearing down toxes\n");
308
309 for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
310 tox_kill(toxes[i]);
311 }
312
313 printf("test_many_group succeeded, took %d seconds\n", (int)(time(nullptr) - test_start_time)); 328 printf("test_many_group succeeded, took %d seconds\n", (int)(time(nullptr) - test_start_time));
314} 329}
315 330
@@ -317,6 +332,6 @@ int main(void)
317{ 332{
318 setvbuf(stdout, nullptr, _IONBF, 0); 333 setvbuf(stdout, nullptr, _IONBF, 0);
319 334
320 test_many_group(); 335 run_auto_test(NUM_GROUP_TOX, test_many_group, true);
321 return 0; 336 return 0;
322} 337}
diff --git a/auto_tests/friend_connection_test.c b/auto_tests/friend_connection_test.c
index ab91e0f0..55a930e2 100644
--- a/auto_tests/friend_connection_test.c
+++ b/auto_tests/friend_connection_test.c
@@ -27,6 +27,6 @@ int main(void)
27{ 27{
28 setvbuf(stdout, nullptr, _IONBF, 0); 28 setvbuf(stdout, nullptr, _IONBF, 0);
29 29
30 run_auto_test(2, friend_connection_test); 30 run_auto_test(2, friend_connection_test, false);
31 return 0; 31 return 0;
32} 32}
diff --git a/auto_tests/lossless_packet_test.c b/auto_tests/lossless_packet_test.c
index 5b8ee947..1b6de806 100644
--- a/auto_tests/lossless_packet_test.c
+++ b/auto_tests/lossless_packet_test.c
@@ -61,6 +61,6 @@ int main(void)
61{ 61{
62 setvbuf(stdout, nullptr, _IONBF, 0); 62 setvbuf(stdout, nullptr, _IONBF, 0);
63 63
64 run_auto_test(2, test_lossless_packet); 64 run_auto_test(2, test_lossless_packet, false);
65 return 0; 65 return 0;
66} 66}
diff --git a/auto_tests/lossy_packet_test.c b/auto_tests/lossy_packet_test.c
index 10d4e505..5080b006 100644
--- a/auto_tests/lossy_packet_test.c
+++ b/auto_tests/lossy_packet_test.c
@@ -57,6 +57,6 @@ int main(void)
57{ 57{
58 setvbuf(stdout, nullptr, _IONBF, 0); 58 setvbuf(stdout, nullptr, _IONBF, 0);
59 59
60 run_auto_test(2, test_lossy_packet); 60 run_auto_test(2, test_lossy_packet, false);
61 return 0; 61 return 0;
62} 62}
diff --git a/auto_tests/overflow_recvq_test.c b/auto_tests/overflow_recvq_test.c
index f70618c4..3f7561e1 100644
--- a/auto_tests/overflow_recvq_test.c
+++ b/auto_tests/overflow_recvq_test.c
@@ -62,6 +62,6 @@ int main(void)
62{ 62{
63 setvbuf(stdout, nullptr, _IONBF, 0); 63 setvbuf(stdout, nullptr, _IONBF, 0);
64 64
65 run_auto_test(3, net_crypto_overflow_test); 65 run_auto_test(3, net_crypto_overflow_test, false);
66 return 0; 66 return 0;
67} 67}
diff --git a/auto_tests/overflow_sendq_test.c b/auto_tests/overflow_sendq_test.c
index ce132d63..e67da1cf 100644
--- a/auto_tests/overflow_sendq_test.c
+++ b/auto_tests/overflow_sendq_test.c
@@ -47,6 +47,6 @@ int main(void)
47{ 47{
48 setvbuf(stdout, nullptr, _IONBF, 0); 48 setvbuf(stdout, nullptr, _IONBF, 0);
49 49
50 run_auto_test(2, net_crypto_overflow_test); 50 run_auto_test(2, net_crypto_overflow_test, false);
51 return 0; 51 return 0;
52} 52}
diff --git a/auto_tests/reconnect_test.c b/auto_tests/reconnect_test.c
index e56d818c..9ccceb39 100644
--- a/auto_tests/reconnect_test.c
+++ b/auto_tests/reconnect_test.c
@@ -101,6 +101,6 @@ int main(void)
101{ 101{
102 setvbuf(stdout, nullptr, _IONBF, 0); 102 setvbuf(stdout, nullptr, _IONBF, 0);
103 103
104 run_auto_test(TOX_COUNT, test_reconnect); 104 run_auto_test(TOX_COUNT, test_reconnect, false);
105 return 0; 105 return 0;
106} 106}
diff --git a/auto_tests/run_auto_test.h b/auto_tests/run_auto_test.h
index bcbfed55..4f2dc278 100644
--- a/auto_tests/run_auto_test.h
+++ b/auto_tests/run_auto_test.h
@@ -48,7 +48,7 @@ static uint64_t get_state_clock_callback(Mono_Time *mono_time, void *user_data)
48 return state->clock; 48 return state->clock;
49} 49}
50 50
51static void run_auto_test(uint32_t tox_count, void test(Tox **toxes, State *state)) 51static void run_auto_test(uint32_t tox_count, void test(Tox **toxes, State *state), bool chain)
52{ 52{
53 printf("initialising %u toxes\n", tox_count); 53 printf("initialising %u toxes\n", tox_count);
54 Tox **toxes = (Tox **)calloc(tox_count, sizeof(Tox *)); 54 Tox **toxes = (Tox **)calloc(tox_count, sizeof(Tox *));
@@ -66,19 +66,34 @@ static void run_auto_test(uint32_t tox_count, void test(Tox **toxes, State *stat
66 mono_time_set_current_time_callback(mono_time, get_state_clock_callback, &state[i]); 66 mono_time_set_current_time_callback(mono_time, get_state_clock_callback, &state[i]);
67 } 67 }
68 68
69 printf("toxes all add each other as friends\n"); 69 if (chain) {
70 printf("each tox adds adjacent toxes as friends\n");
71
72 for (uint32_t i = 0; i < tox_count; i++) {
73 for (uint32_t j = i - 1; j != i + 3; j += 2) {
74 if (j >= tox_count) {
75 continue;
76 }
70 77
71 for (uint32_t i = 0; i < tox_count; i++) {
72 for (uint32_t j = 0; j < tox_count; j++) {
73 if (i != j) {
74 uint8_t public_key[TOX_PUBLIC_KEY_SIZE]; 78 uint8_t public_key[TOX_PUBLIC_KEY_SIZE];
75 tox_self_get_public_key(toxes[j], public_key); 79 tox_self_get_public_key(toxes[j], public_key);
76 tox_friend_add_norequest(toxes[i], public_key, nullptr); 80 tox_friend_add_norequest(toxes[i], public_key, nullptr);
77 } 81 }
78 } 82 }
83 } else {
84 printf("toxes all add each other as friends\n");
85
86 for (uint32_t i = 0; i < tox_count; i++) {
87 for (uint32_t j = 0; j < tox_count; j++) {
88 if (i != j) {
89 uint8_t public_key[TOX_PUBLIC_KEY_SIZE];
90 tox_self_get_public_key(toxes[j], public_key);
91 tox_friend_add_norequest(toxes[i], public_key, nullptr);
92 }
93 }
94 }
79 } 95 }
80 96
81
82 printf("bootstrapping all toxes off toxes[0]\n"); 97 printf("bootstrapping all toxes off toxes[0]\n");
83 uint8_t dht_key[TOX_PUBLIC_KEY_SIZE]; 98 uint8_t dht_key[TOX_PUBLIC_KEY_SIZE];
84 tox_self_get_dht_id(toxes[0], dht_key); 99 tox_self_get_dht_id(toxes[0], dht_key);
diff --git a/auto_tests/send_message_test.c b/auto_tests/send_message_test.c
index 81969d59..5346dc9e 100644
--- a/auto_tests/send_message_test.c
+++ b/auto_tests/send_message_test.c
@@ -61,6 +61,6 @@ int main(void)
61{ 61{
62 setvbuf(stdout, nullptr, _IONBF, 0); 62 setvbuf(stdout, nullptr, _IONBF, 0);
63 63
64 run_auto_test(2, send_message_test); 64 run_auto_test(2, send_message_test, false);
65 return 0; 65 return 0;
66} 66}
diff --git a/docs/minpgc.md b/docs/minpgc.md
new file mode 100644
index 00000000..aa2ed1dc
--- /dev/null
+++ b/docs/minpgc.md
@@ -0,0 +1,128 @@
1# Persistent conferences
2
3This document describes the "minpgc" simple persistent conferences
4implementation of PR #1069.
5
6Many of the ideas derive from isotoxin's persistent conferences
7implementation, PR #826.
8
9## Specification of changes from pre-existing conference specification
10We add one new packet type:
11
12Rejoin Conference packet
13
14| Length | Contents |
15|:-------|:--------------------------------|
16| `1` | `uint8_t` (0x64) |
17| `33` | Group chat identifier |
18
19
20A peer times out from a group if it has been inactive for 60s. When a peer
21times out, we flag it as _frozen_. Frozen peers are disregarded for all
22purposes except those discussed below - in particular no packets are sent to
23them except as described below, they are omitted from the peer lists sent to
24the client or in a Peer Response packet, and they are not considered when
25determining closest peers for establishing direct connections.
26
27A peer is considered to be active if we receive a group message or Rejoin
28packet from it, or a New Peer message for it.
29
30If a frozen peer is seen to be active, we remove its 'frozen' flag and send a
31Name group message. (We can hold off on sending this message until the next
32tox\_iterate, and only send one message if many frozen peers become active at
33once).
34
35If we receive a New Peer message for a peer, we update its DHT pubkey.
36
37If we receive a group message originating from an unknown peer, we drop the
38message but send a Peer Query packet back to the peer who directly sent us the
39message. (This is current behaviour; it's mentioned here because it's important
40and not currently mentioned in the spec.)
41
42If we receive a Rejoin packet from a peer we update its DHT pubkey, add a
43temporary groupchat connection for the peer, and, once the connection is
44online, send out a New Peer message announcing the peer, and a Name message.
45
46Whenever we make a new friend connection, we check if the public key is that
47of any frozen peer. If so, we send it a Rejoin packet, add a temporary
48groupchat connection for it, and, once the connection is online, send the
49peer a Peer Query packet.
50
51We do the same with a peer when we are setting it as frozen if we have a
52friend connection to it.
53
54The temporary groupchat connections established in sending and handling Rejoin
55packets are not immediately operational (because group numbers are not known);
56rather, an Online packet is sent when we handle a Rejoin packet.
57
58When a connection is set as online as a result of an Online packet, we ping
59the group.
60
61When processing the reply to a Peer Query, we update the DHT pubkey of an
62existing peer if and only if it is frozen or has not had its DHT pubkey
63updated since it last stopped being frozen.
64
65When we receive a Title Response packet, we set the title if it has never been
66set or if at some point since it was last set, there were no unfrozen peers
67(except us).
68
69## Discussion
70### Overview
71The intention is to recover seamlessly from splits in the group, the most
72common form of which is a single peer temporarily losing all connectivity.
73
74To see how this works, first note that groups (even before the changes
75discussed here) have the property that for a group to be connected in the
76sense that any peer will receive the messages of any other peer and have them
77in their peerlist, it is necessary and sufficient that there is a path of
78direct group connections between any two peers.
79
80Now suppose the group is split into two connected components, with each member
81of one component frozen according to the members of the other. Suppose there
82are two peers, one in each component, which are using the above protocol, and
83suppose they establish a friend connection. Then each will rejoin the other,
84forming a direct group connection. Hence the whole group will become connected
85(even if all other peers are using the unmodified protocol).
86
87The Peer Query packet sent on rejoining hastens this process.
88
89Peers who leave the group during a split will not be deleted by all peers
90after the merge - but they will be set as frozen due to ping timeouts, which
91is sufficient.
92
93### Titles
94If we have a split into components each containing multiple peers, and the
95title is changed in one component, then peers will continue to disagree on the
96title after the split. Short of a complicated voting system, this seems the
97only reasonable behaviour.
98
99### Implementation notes
100Although I've described the logic in terms of an 'frozen' flag, it might
101actually make more sense in the implementation to have a separate list for
102frozen peers.
103
104## Saving
105Saving could be implemented by simply saving all live groups with their group
106numbers and full peer info for all peers. On reload, all peers would be set as
107frozen.
108
109The client would need to support this by understanding that these groups exist
110on start-up (e.g. starting windows for them), and by not automatically killing
111groups on closing the client.
112
113## Limitations
114If a peer disconnects from the group for a period short enough that group
115timeouts do not occur, and a name change occurs during this period, then the
116name change will never be propagated.
117
118One way to deal with this would be a general mechanism for storing and
119requesting missed group messages. But this is considered out of scope of this
120PR.
121
122If a peer changes its DHT pubkey, the change might not be properly propagated
123under various circumstances - in particular, if connections do not go down
124long enough for the peer to become frozen.
125
126One way to deal with this would be to add a group message announcing the
127sending peer's current DHT pubkey, and treat it analogously to the Name
128message.
diff --git a/toxcore/group.c b/toxcore/group.c
index 4c77c0d4..c019c0eb 100644
--- a/toxcore/group.c
+++ b/toxcore/group.c
@@ -60,10 +60,10 @@ typedef enum Invite_Id {
60#define ONLINE_PACKET_DATA_SIZE (sizeof(uint16_t) + 1 + GROUP_ID_LENGTH) 60#define ONLINE_PACKET_DATA_SIZE (sizeof(uint16_t) + 1 + GROUP_ID_LENGTH)
61 61
62typedef enum Peer_Id { 62typedef enum Peer_Id {
63 PEER_KILL_ID = 1, 63 PEER_INTRODUCED_ID = 1,
64 PEER_QUERY_ID = 8, 64 PEER_QUERY_ID = 8,
65 PEER_RESPONSE_ID = 9, 65 PEER_RESPONSE_ID = 9,
66 PEER_TITLE_ID = 10, 66 PEER_TITLE_ID = 10,
67} Peer_Id; 67} Peer_Id;
68 68
69#define MIN_MESSAGE_PACKET_LEN (sizeof(uint16_t) * 2 + sizeof(uint32_t) + 1) 69#define MIN_MESSAGE_PACKET_LEN (sizeof(uint16_t) * 2 + sizeof(uint32_t) + 1)
@@ -232,7 +232,7 @@ int32_t conference_by_id(const Group_Chats *g_c, const uint8_t *id)
232/* 232/*
233 * check if peer with peer_number is in peer array. 233 * check if peer with peer_number is in peer array.
234 * 234 *
235 * return peer number if peer is in chat. 235 * return peer index if peer is in chat.
236 * return -1 if peer is not in chat. 236 * return -1 if peer is not in chat.
237 * 237 *
238 * TODO(irungentoo): make this more efficient. 238 * TODO(irungentoo): make this more efficient.
@@ -267,10 +267,6 @@ typedef enum Groupchat_Closest {
267 GROUPCHAT_CLOSEST_REMOVED 267 GROUPCHAT_CLOSEST_REMOVED
268} Groupchat_Closest; 268} Groupchat_Closest;
269 269
270static int friend_in_close(Group_c *g, int friendcon_id);
271static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, uint32_t groupnumber, uint8_t closest,
272 uint8_t lock);
273
274static int add_to_closest(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk, const uint8_t *temp_pk) 270static int add_to_closest(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk, const uint8_t *temp_pk)
275{ 271{
276 Group_c *g = get_group_c(g_c, groupnumber); 272 Group_c *g = get_group_c(g_c, groupnumber);
@@ -371,6 +367,11 @@ static unsigned int pk_in_closest_peers(Group_c *g, uint8_t *real_pk)
371 return 0; 367 return 0;
372} 368}
373 369
370static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, uint32_t groupnumber, uint8_t reason,
371 uint8_t lock);
372
373static void remove_conn_reason(Group_Chats *g_c, uint32_t groupnumber, uint16_t i, uint8_t reason);
374
374static int send_packet_online(Friend_Connections *fr_c, int friendcon_id, uint16_t group_num, uint8_t type, 375static int send_packet_online(Friend_Connections *fr_c, int friendcon_id, uint16_t group_num, uint8_t type,
375 uint8_t *id); 376 uint8_t *id);
376 377
@@ -397,7 +398,7 @@ static int connect_to_closest(Group_Chats *g_c, uint32_t groupnumber, void *user
397 continue; 398 continue;
398 } 399 }
399 400
400 if (!g->close[i].closest) { 401 if (!(g->close[i].reasons & GROUPCHAT_CLOSE_REASON_CLOSEST)) {
401 continue; 402 continue;
402 } 403 }
403 404
@@ -406,12 +407,7 @@ static int connect_to_closest(Group_Chats *g_c, uint32_t groupnumber, void *user
406 get_friendcon_public_keys(real_pk, dht_temp_pk, g_c->fr_c, g->close[i].number); 407 get_friendcon_public_keys(real_pk, dht_temp_pk, g_c->fr_c, g->close[i].number);
407 408
408 if (!pk_in_closest_peers(g, real_pk)) { 409 if (!pk_in_closest_peers(g, real_pk)) {
409 g->close[i].closest = false; 410 remove_conn_reason(g_c, groupnumber, i, GROUPCHAT_CLOSE_REASON_CLOSEST);
410
411 if (!g->close[i].introducer && !g->close[i].introduced) {
412 g->close[i].type = GROUPCHAT_CLOSE_NONE;
413 kill_friend_connection(g_c->fr_c, g->close[i].number);
414 }
415 } 411 }
416 } 412 }
417 413
@@ -435,7 +431,7 @@ static int connect_to_closest(Group_Chats *g_c, uint32_t groupnumber, void *user
435 set_dht_temp_pk(g_c->fr_c, friendcon_id, g->closest_peers[i].temp_pk, userdata); 431 set_dht_temp_pk(g_c->fr_c, friendcon_id, g->closest_peers[i].temp_pk, userdata);
436 } 432 }
437 433
438 add_conn_to_groupchat(g_c, friendcon_id, groupnumber, 1, lock); 434 add_conn_to_groupchat(g_c, friendcon_id, groupnumber, GROUPCHAT_CLOSE_REASON_CLOSEST, lock);
439 435
440 if (friend_con_connected(g_c->fr_c, friendcon_id) == FRIENDCONN_STATUS_CONNECTED) { 436 if (friend_con_connected(g_c->fr_c, friendcon_id) == FRIENDCONN_STATUS_CONNECTED) {
441 send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->type, g->id); 437 send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->type, g->id);
@@ -447,7 +443,96 @@ static int connect_to_closest(Group_Chats *g_c, uint32_t groupnumber, void *user
447 return 0; 443 return 0;
448} 444}
449 445
450/* Add a peer to the group chat. 446static int get_frozen_index(Group_c *g, uint16_t peer_number)
447{
448 for (uint32_t i = 0; i < g->numfrozen; ++i) {
449 if (g->frozen[i].peer_number == peer_number) {
450 return i;
451 }
452 }
453
454 return -1;
455}
456
457/* Update last_active timestamp on peer, and thaw the peer if it is frozen.
458 *
459 * return peer index if peer is in the conference.
460 * return -1 otherwise, and on error.
461 */
462static int note_peer_active(Group_Chats *g_c, uint32_t groupnumber, uint16_t peer_number, void *userdata)
463{
464 Group_c *g = get_group_c(g_c, groupnumber);
465
466 if (!g) {
467 return -1;
468 }
469
470 const int peer_index = get_peer_index(g, peer_number);
471
472 if (peer_index != -1) {
473 g->group[peer_index].last_active = mono_time_get(g_c->mono_time);
474 return peer_index;
475 }
476
477 const int frozen_index = get_frozen_index(g, peer_number);
478
479 if (frozen_index == -1) {
480 return -1;
481 }
482
483 /* Now thaw the peer */
484
485 Group_Peer *temp = (Group_Peer *)realloc(g->group, sizeof(Group_Peer) * (g->numpeers + 1));
486
487 if (temp == nullptr) {
488 return -1;
489 }
490
491 g->group = temp;
492 g->group[g->numpeers] = g->frozen[frozen_index];
493 g->group[g->numpeers].temp_pk_updated = false;
494 g->group[g->numpeers].last_active = mono_time_get(g_c->mono_time);
495
496 add_to_closest(g_c, groupnumber, g->group[g->numpeers].real_pk, g->group[g->numpeers].temp_pk);
497
498 ++g->numpeers;
499
500 --g->numfrozen;
501
502 if (g->numfrozen == 0) {
503 free(g->frozen);
504 g->frozen = nullptr;
505 } else {
506 if (g->numfrozen != (uint32_t)frozen_index) {
507 g->frozen[frozen_index] = g->frozen[g->numfrozen];
508 }
509
510 Group_Peer *frozen_temp = (Group_Peer *)realloc(g->frozen, sizeof(Group_Peer) * (g->numfrozen));
511
512 if (frozen_temp == nullptr) {
513 return -1;
514 }
515
516 g->frozen = frozen_temp;
517 }
518
519 if (g_c->peer_list_changed_callback) {
520 g_c->peer_list_changed_callback(g_c->m, groupnumber, userdata);
521 }
522
523 if (g->peer_on_join) {
524 g->peer_on_join(g->object, groupnumber, g->numpeers - 1);
525 }
526
527 g->need_send_name = true;
528
529 return g->numpeers - 1;
530}
531
532/* Add a peer to the group chat, or update an existing peer.
533 *
534 * fresh indicates whether we should consider this information on the peer to
535 * be current, and so should update temp_pk and consider the peer active.
451 * 536 *
452 * do_gc_callback indicates whether we want to trigger callbacks set by the client 537 * do_gc_callback indicates whether we want to trigger callbacks set by the client
453 * via the public API. This should be set to false if this function is called 538 * via the public API. This should be set to false if this function is called
@@ -457,7 +542,7 @@ static int connect_to_closest(Group_Chats *g_c, uint32_t groupnumber, void *user
457 * return -1 if error. 542 * return -1 if error.
458 */ 543 */
459static int addpeer(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk, const uint8_t *temp_pk, 544static int addpeer(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk, const uint8_t *temp_pk,
460 uint16_t peer_number, void *userdata, bool do_gc_callback) 545 uint16_t peer_number, void *userdata, bool fresh, bool do_gc_callback)
461{ 546{
462 Group_c *g = get_group_c(g_c, groupnumber); 547 Group_c *g = get_group_c(g_c, groupnumber);
463 548
@@ -465,23 +550,35 @@ static int addpeer(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_p
465 return -1; 550 return -1;
466 } 551 }
467 552
468 // TODO(irungentoo): 553 const int peer_index = fresh ?
469 int peer_index = peer_in_chat(g, real_pk); 554 note_peer_active(g_c, groupnumber, peer_number, userdata) :
555 get_peer_index(g, peer_number);
470 556
471 if (peer_index != -1) { 557 if (peer_index != -1) {
472 id_copy(g->group[peer_index].temp_pk, temp_pk); 558 if (!id_equal(g->group[peer_index].real_pk, real_pk)) {
473
474 if (g->group[peer_index].peer_number != peer_number) {
475 return -1; 559 return -1;
476 } 560 }
477 561
562 if (fresh || !g->group[peer_index].temp_pk_updated) {
563 id_copy(g->group[peer_index].temp_pk, temp_pk);
564 g->group[peer_index].temp_pk_updated = true;
565 }
566
478 return peer_index; 567 return peer_index;
479 } 568 }
480 569
481 peer_index = get_peer_index(g, peer_number); 570 if (!fresh) {
571 const int frozen_index = get_frozen_index(g, peer_number);
482 572
483 if (peer_index != -1) { 573 if (frozen_index != -1) {
484 return -1; 574 if (!id_equal(g->frozen[frozen_index].real_pk, real_pk)) {
575 return -1;
576 }
577
578 id_copy(g->frozen[frozen_index].temp_pk, temp_pk);
579
580 return -1;
581 }
485 } 582 }
486 583
487 Group_Peer *temp = (Group_Peer *)realloc(g->group, sizeof(Group_Peer) * (g->numpeers + 1)); 584 Group_Peer *temp = (Group_Peer *)realloc(g->group, sizeof(Group_Peer) * (g->numpeers + 1));
@@ -495,9 +592,10 @@ static int addpeer(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_p
495 592
496 id_copy(g->group[g->numpeers].real_pk, real_pk); 593 id_copy(g->group[g->numpeers].real_pk, real_pk);
497 id_copy(g->group[g->numpeers].temp_pk, temp_pk); 594 id_copy(g->group[g->numpeers].temp_pk, temp_pk);
595 g->group[g->numpeers].temp_pk_updated = true;
498 g->group[g->numpeers].peer_number = peer_number; 596 g->group[g->numpeers].peer_number = peer_number;
499 597
500 g->group[g->numpeers].last_recv = mono_time_get(g_c->mono_time); 598 g->group[g->numpeers].last_active = mono_time_get(g_c->mono_time);
501 ++g->numpeers; 599 ++g->numpeers;
502 600
503 add_to_closest(g_c, groupnumber, real_pk, temp_pk); 601 add_to_closest(g_c, groupnumber, real_pk, temp_pk);
@@ -539,13 +637,24 @@ static int remove_close_conn(Group_Chats *g_c, uint32_t groupnumber, int friendc
539} 637}
540 638
541 639
640static void remove_from_closest(Group_c *g, int peer_index)
641{
642 for (uint32_t i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) {
643 if (g->closest_peers[i].entry && id_equal(g->closest_peers[i].real_pk, g->group[peer_index].real_pk)) {
644 g->closest_peers[i].entry = 0;
645 g->changed = GROUPCHAT_CLOSEST_REMOVED;
646 break;
647 }
648 }
649}
650
542/* 651/*
543 * Delete a peer from the group chat. 652 * Delete a peer from the group chat.
544 * 653 *
545 * return 0 if success 654 * return 0 if success
546 * return -1 if error. 655 * return -1 if error.
547 */ 656 */
548static int delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata) 657static int delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata, bool keep_connection)
549{ 658{
550 Group_c *g = get_group_c(g_c, groupnumber); 659 Group_c *g = get_group_c(g_c, groupnumber);
551 660
@@ -553,19 +662,11 @@ static int delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void
553 return -1; 662 return -1;
554 } 663 }
555 664
556 uint32_t i; 665 remove_from_closest(g, peer_index);
557 666
558 for (i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) { /* If peer is in closest_peers list, remove it. */ 667 const int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, g->group[peer_index].real_pk);
559 if (g->closest_peers[i].entry && id_equal(g->closest_peers[i].real_pk, g->group[peer_index].real_pk)) {
560 g->closest_peers[i].entry = 0;
561 g->changed = GROUPCHAT_CLOSEST_REMOVED;
562 break;
563 }
564 }
565 668
566 int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, g->group[peer_index].real_pk); 669 if (friendcon_id != -1 && !keep_connection) {
567
568 if (friendcon_id != -1) {
569 remove_close_conn(g_c, groupnumber, friendcon_id); 670 remove_close_conn(g_c, groupnumber, friendcon_id);
570 } 671 }
571 672
@@ -578,7 +679,7 @@ static int delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void
578 g->group = nullptr; 679 g->group = nullptr;
579 } else { 680 } else {
580 if (g->numpeers != (uint32_t)peer_index) { 681 if (g->numpeers != (uint32_t)peer_index) {
581 memcpy(&g->group[peer_index], &g->group[g->numpeers], sizeof(Group_Peer)); 682 g->group[peer_index] = g->group[g->numpeers];
582 } 683 }
583 684
584 Group_Peer *temp = (Group_Peer *)realloc(g->group, sizeof(Group_Peer) * (g->numpeers)); 685 Group_Peer *temp = (Group_Peer *)realloc(g->group, sizeof(Group_Peer) * (g->numpeers));
@@ -601,6 +702,28 @@ static int delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void
601 return 0; 702 return 0;
602} 703}
603 704
705static int freeze_peer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata)
706{
707 Group_c *g = get_group_c(g_c, groupnumber);
708
709 if (!g) {
710 return -1;
711 }
712
713 Group_Peer *temp = (Group_Peer *)realloc(g->frozen, sizeof(Group_Peer) * (g->numfrozen + 1));
714
715 if (temp == nullptr) {
716 return -1;
717 }
718
719 g->frozen = temp;
720 g->frozen[g->numfrozen] = g->group[peer_index];
721 ++g->numfrozen;
722
723 return delpeer(g_c, groupnumber, peer_index, userdata, true);
724}
725
726
604/* Set the nick for a peer. 727/* Set the nick for a peer.
605 * 728 *
606 * do_gc_callback indicates whether we want to trigger callbacks set by the client 729 * do_gc_callback indicates whether we want to trigger callbacks set by the client
@@ -666,6 +789,8 @@ static int settitle(Group_Chats *g_c, uint32_t groupnumber, int peer_index, cons
666 memcpy(g->title, title, title_len); 789 memcpy(g->title, title, title_len);
667 g->title_len = title_len; 790 g->title_len = title_len;
668 791
792 g->title_fresh = true;
793
669 if (g_c->title_callback) { 794 if (g_c->title_callback) {
670 g_c->title_callback(g_c->m, groupnumber, peer_index, title, title_len, userdata); 795 g_c->title_callback(g_c->m, groupnumber, peer_index, title, title_len, userdata);
671 } 796 }
@@ -673,7 +798,29 @@ static int settitle(Group_Chats *g_c, uint32_t groupnumber, int peer_index, cons
673 return 0; 798 return 0;
674} 799}
675 800
676static void set_conns_type_close(Group_Chats *g_c, uint32_t groupnumber, int friendcon_id, uint8_t type) 801/* Check if the group has no online connection, and freeze all peers if so */
802static void check_disconnected(Group_Chats *g_c, uint32_t groupnumber, void *userdata)
803{
804 Group_c *g = get_group_c(g_c, groupnumber);
805
806 if (!g) {
807 return;
808 }
809
810 for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
811 if (g->close[i].type == GROUPCHAT_CLOSE_ONLINE) {
812 return;
813 }
814 }
815
816 for (uint32_t i = 0; i < g->numpeers; ++i) {
817 while (i < g->numpeers && !id_equal(g->group[i].real_pk, g->real_pk)) {
818 freeze_peer(g_c, groupnumber, i, userdata);
819 }
820 }
821}
822
823static void set_conns_type_close(Group_Chats *g_c, uint32_t groupnumber, int friendcon_id, uint8_t type, void *userdata)
677{ 824{
678 Group_c *g = get_group_c(g_c, groupnumber); 825 Group_c *g = get_group_c(g_c, groupnumber);
679 826
@@ -696,14 +843,38 @@ static void set_conns_type_close(Group_Chats *g_c, uint32_t groupnumber, int fri
696 send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->type, g->id); 843 send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->type, g->id);
697 } else { 844 } else {
698 g->close[i].type = type; 845 g->close[i].type = type;
846 check_disconnected(g_c, groupnumber, userdata);
699 } 847 }
700 } 848 }
701} 849}
702/* Set the type for all close connections with friendcon_id */ 850/* Set the type for all close connections with friendcon_id */
703static void set_conns_status_groups(Group_Chats *g_c, int friendcon_id, uint8_t type) 851static void set_conns_status_groups(Group_Chats *g_c, int friendcon_id, uint8_t type, void *userdata)
852{
853 for (uint16_t i = 0; i < g_c->num_chats; ++i) {
854 set_conns_type_close(g_c, i, friendcon_id, type, userdata);
855 }
856}
857
858static bool try_send_rejoin(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk);
859
860static void rejoin_frozen_friend(Group_Chats *g_c, int friendcon_id)
704{ 861{
862 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
863 get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, friendcon_id);
864
705 for (uint16_t i = 0; i < g_c->num_chats; ++i) { 865 for (uint16_t i = 0; i < g_c->num_chats; ++i) {
706 set_conns_type_close(g_c, i, friendcon_id, type); 866 Group_c *g = get_group_c(g_c, i);
867
868 if (!g) {
869 continue;
870 }
871
872 for (uint32_t j = 0; j < g->numfrozen; ++j) {
873 if (id_equal(g->frozen[j].real_pk, real_pk)) {
874 try_send_rejoin(g_c, i, real_pk);
875 break;
876 }
877 }
707 } 878 }
708} 879}
709 880
@@ -712,9 +883,10 @@ static int g_handle_status(void *object, int friendcon_id, uint8_t status, void
712 Group_Chats *g_c = (Group_Chats *)object; 883 Group_Chats *g_c = (Group_Chats *)object;
713 884
714 if (status) { /* Went online */ 885 if (status) { /* Went online */
715 set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CLOSE_ONLINE); 886 set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CLOSE_ONLINE, userdata);
887 rejoin_frozen_friend(g_c, friendcon_id);
716 } else { /* Went offline */ 888 } else { /* Went offline */
717 set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CLOSE_CONNECTION); 889 set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CLOSE_CONNECTION, userdata);
718 // TODO(irungentoo): remove timedout connections? 890 // TODO(irungentoo): remove timedout connections?
719 } 891 }
720 892
@@ -729,7 +901,7 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin
729 * return close index on success 901 * return close index on success
730 * return -1 on failure. 902 * return -1 on failure.
731 */ 903 */
732static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, uint32_t groupnumber, uint8_t closest, 904static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, uint32_t groupnumber, uint8_t reason,
733 uint8_t lock) 905 uint8_t lock)
734{ 906{
735 Group_c *g = get_group_c(g_c, groupnumber); 907 Group_c *g = get_group_c(g_c, groupnumber);
@@ -738,40 +910,81 @@ static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, uint32_t gr
738 return -1; 910 return -1;
739 } 911 }
740 912
741 uint16_t i, ind = MAX_GROUP_CONNECTIONS; 913 uint16_t empty = MAX_GROUP_CONNECTIONS;
914 uint16_t ind = MAX_GROUP_CONNECTIONS;
742 915
743 for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { 916 for (uint16_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
744 if (g->close[i].type == GROUPCHAT_CLOSE_NONE) { 917 if (g->close[i].type == GROUPCHAT_CLOSE_NONE) {
745 ind = i; 918 empty = i;
746 continue; 919 continue;
747 } 920 }
748 921
749 if (g->close[i].number == (uint32_t)friendcon_id) { 922 if (g->close[i].number == (uint32_t)friendcon_id) {
750 g->close[i].closest |= closest; 923 ind = i; /* Already in list. */
751 return i; /* Already in list. */ 924 break;
925 }
926 }
927
928 if (ind == MAX_GROUP_CONNECTIONS && empty != MAX_GROUP_CONNECTIONS) {
929 if (lock) {
930 friend_connection_lock(g_c->fr_c, friendcon_id);
752 } 931 }
932
933 g->close[empty].type = GROUPCHAT_CLOSE_CONNECTION;
934 g->close[empty].number = friendcon_id;
935 g->close[empty].reasons = 0;
936 // TODO(irungentoo):
937 friend_connection_callbacks(g_c->m->fr_c, friendcon_id, GROUPCHAT_CALLBACK_INDEX, &g_handle_status, &g_handle_packet,
938 &handle_lossy, g_c, friendcon_id);
939 ind = empty;
753 } 940 }
754 941
755 if (ind == MAX_GROUP_CONNECTIONS) { 942 if (ind == MAX_GROUP_CONNECTIONS) {
756 return -1; 943 return -1;
757 } 944 }
758 945
759 if (lock) { 946 if (!(g->close[ind].reasons & reason)) {
760 friend_connection_lock(g_c->fr_c, friendcon_id); 947 g->close[ind].reasons |= reason;
761 }
762 948
763 g->close[ind].type = GROUPCHAT_CLOSE_CONNECTION; 949 if (reason == GROUPCHAT_CLOSE_REASON_INTRODUCER) {
764 g->close[ind].number = friendcon_id; 950 ++g->num_introducer_connections;
765 g->close[ind].closest = closest; 951 }
766 g->close[ind].introducer = false; 952 }
767 g->close[ind].introduced = false;
768 // TODO(irungentoo):
769 friend_connection_callbacks(g_c->m->fr_c, friendcon_id, GROUPCHAT_CALLBACK_INDEX, &g_handle_status, &g_handle_packet,
770 &handle_lossy, g_c, friendcon_id);
771 953
772 return ind; 954 return ind;
773} 955}
774 956
957static unsigned int send_peer_introduced(Group_Chats *g_c, int friendcon_id, uint16_t group_num);
958
959/* Removes reason for keeping connection.
960 *
961 * Kills connection if this was the last reason.
962 */
963static void remove_conn_reason(Group_Chats *g_c, uint32_t groupnumber, uint16_t i, uint8_t reason)
964{
965 Group_c *g = get_group_c(g_c, groupnumber);
966
967 if (!g) {
968 return;
969 }
970
971 if (!(g->close[i].reasons & reason)) {
972 return;
973 }
974
975 g->close[i].reasons &= ~reason;
976
977 if (reason == GROUPCHAT_CLOSE_REASON_INTRODUCER) {
978 --g->num_introducer_connections;
979 send_peer_introduced(g_c, g->close[i].number, g->close[i].group_number);
980 }
981
982 if (g->close[i].reasons == 0) {
983 kill_friend_connection(g_c->fr_c, g->close[i].number);
984 g->close[i].type = GROUPCHAT_CLOSE_NONE;
985 }
986}
987
775/* Creates a new groupchat and puts it in the chats array. 988/* Creates a new groupchat and puts it in the chats array.
776 * 989 *
777 * type is one of GROUPCHAT_TYPE_* 990 * type is one of GROUPCHAT_TYPE_*
@@ -781,7 +994,7 @@ static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, uint32_t gr
781 */ 994 */
782int add_groupchat(Group_Chats *g_c, uint8_t type) 995int add_groupchat(Group_Chats *g_c, uint8_t type)
783{ 996{
784 int32_t groupnumber = create_group_chat(g_c); 997 const int32_t groupnumber = create_group_chat(g_c);
785 998
786 if (groupnumber == -1) { 999 if (groupnumber == -1) {
787 return -1; 1000 return -1;
@@ -790,12 +1003,12 @@ int add_groupchat(Group_Chats *g_c, uint8_t type)
790 Group_c *g = &g_c->chats[groupnumber]; 1003 Group_c *g = &g_c->chats[groupnumber];
791 1004
792 g->status = GROUPCHAT_STATUS_CONNECTED; 1005 g->status = GROUPCHAT_STATUS_CONNECTED;
793 g->number_joined = -1;
794 g->type = type; 1006 g->type = type;
795 new_symmetric_key(g->id); 1007 new_symmetric_key(g->id);
796 g->peer_number = 0; /* Founder is peer 0. */ 1008 g->peer_number = 0; /* Founder is peer 0. */
797 memcpy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE); 1009 memcpy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE);
798 int peer_index = addpeer(g_c, groupnumber, g->real_pk, dht_get_self_public_key(g_c->m->dht), 0, nullptr, false); 1010 const int peer_index = addpeer(g_c, groupnumber, g->real_pk, dht_get_self_public_key(g_c->m->dht), 0, nullptr, true,
1011 false);
799 1012
800 if (peer_index == -1) { 1013 if (peer_index == -1) {
801 return -1; 1014 return -1;
@@ -838,6 +1051,7 @@ int del_groupchat(Group_Chats *g_c, uint32_t groupnumber)
838 } 1051 }
839 1052
840 free(g->group); 1053 free(g->group);
1054 free(g->frozen);
841 1055
842 if (g->group_on_delete) { 1056 if (g->group_on_delete) {
843 g->group_on_delete(g->object, groupnumber); 1057 g->group_on_delete(g->object, groupnumber);
@@ -1087,7 +1301,7 @@ int invite_friend(Group_Chats *g_c, uint32_t friendnumber, uint32_t groupnumber)
1087 1301
1088 uint8_t invite[INVITE_PACKET_SIZE]; 1302 uint8_t invite[INVITE_PACKET_SIZE];
1089 invite[0] = INVITE_ID; 1303 invite[0] = INVITE_ID;
1090 uint16_t groupchat_num = net_htons((uint16_t)groupnumber); 1304 const uint16_t groupchat_num = net_htons((uint16_t)groupnumber);
1091 memcpy(invite + 1, &groupchat_num, sizeof(groupchat_num)); 1305 memcpy(invite + 1, &groupchat_num, sizeof(groupchat_num));
1092 invite[1 + sizeof(groupchat_num)] = g->type; 1306 invite[1 + sizeof(groupchat_num)] = g->type;
1093 memcpy(invite + 1 + sizeof(groupchat_num) + 1, g->id, GROUP_ID_LENGTH); 1307 memcpy(invite + 1 + sizeof(groupchat_num) + 1, g->id, GROUP_ID_LENGTH);
@@ -1099,6 +1313,39 @@ int invite_friend(Group_Chats *g_c, uint32_t friendnumber, uint32_t groupnumber)
1099 return -2; 1313 return -2;
1100} 1314}
1101 1315
1316/* Send a rejoin packet to a peer if we have a friend connection to the peer.
1317 * return true if a packet was sent.
1318 * return false otherwise.
1319 */
1320static bool try_send_rejoin(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk)
1321{
1322 Group_c *g = get_group_c(g_c, groupnumber);
1323
1324 if (!g) {
1325 return false;
1326 }
1327
1328 const int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, real_pk);
1329
1330 if (friendcon_id == -1) {
1331 return false;
1332 }
1333
1334 uint8_t packet[1 + 1 + GROUP_ID_LENGTH];
1335 packet[0] = PACKET_ID_REJOIN_CONFERENCE;
1336 packet[1] = g->type;
1337 memcpy(packet + 2, g->id, GROUP_ID_LENGTH);
1338
1339 if (write_cryptpacket(friendconn_net_crypto(g_c->fr_c), friend_connection_crypt_connection_id(g_c->fr_c, friendcon_id),
1340 packet, sizeof(packet), 0) == -1) {
1341 return false;
1342 }
1343
1344 add_conn_to_groupchat(g_c, friendcon_id, groupnumber, GROUPCHAT_CLOSE_REASON_INTRODUCER, 1);
1345
1346 return true;
1347}
1348
1102static unsigned int send_peer_query(Group_Chats *g_c, int friendcon_id, uint16_t group_num); 1349static unsigned int send_peer_query(Group_Chats *g_c, int friendcon_id, uint16_t group_num);
1103 1350
1104/* Join a group (you need to have been invited first.) 1351/* Join a group (you need to have been invited first.)
@@ -1123,7 +1370,7 @@ int join_groupchat(Group_Chats *g_c, uint32_t friendnumber, uint8_t expected_typ
1123 return -2; 1370 return -2;
1124 } 1371 }
1125 1372
1126 int friendcon_id = getfriendcon_id(g_c->m, friendnumber); 1373 const int friendcon_id = getfriendcon_id(g_c->m, friendnumber);
1127 1374
1128 if (friendcon_id == -1) { 1375 if (friendcon_id == -1) {
1129 return -3; 1376 return -3;
@@ -1133,7 +1380,7 @@ int join_groupchat(Group_Chats *g_c, uint32_t friendnumber, uint8_t expected_typ
1133 return -4; 1380 return -4;
1134 } 1381 }
1135 1382
1136 int groupnumber = create_group_chat(g_c); 1383 const int groupnumber = create_group_chat(g_c);
1137 1384
1138 if (groupnumber == -1) { 1385 if (groupnumber == -1) {
1139 return -5; 1386 return -5;
@@ -1141,9 +1388,8 @@ int join_groupchat(Group_Chats *g_c, uint32_t friendnumber, uint8_t expected_typ
1141 1388
1142 Group_c *g = &g_c->chats[groupnumber]; 1389 Group_c *g = &g_c->chats[groupnumber];
1143 1390
1144 uint16_t group_num = net_htons(groupnumber); 1391 const uint16_t group_num = net_htons(groupnumber);
1145 g->status = GROUPCHAT_STATUS_VALID; 1392 g->status = GROUPCHAT_STATUS_VALID;
1146 g->number_joined = -1;
1147 memcpy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE); 1393 memcpy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE);
1148 1394
1149 uint8_t response[INVITE_RESPONSE_PACKET_SIZE]; 1395 uint8_t response[INVITE_RESPONSE_PACKET_SIZE];
@@ -1157,13 +1403,11 @@ int join_groupchat(Group_Chats *g_c, uint32_t friendnumber, uint8_t expected_typ
1157 other_groupnum = net_ntohs(other_groupnum); 1403 other_groupnum = net_ntohs(other_groupnum);
1158 g->type = data[sizeof(uint16_t)]; 1404 g->type = data[sizeof(uint16_t)];
1159 memcpy(g->id, data + sizeof(uint16_t) + 1, GROUP_ID_LENGTH); 1405 memcpy(g->id, data + sizeof(uint16_t) + 1, GROUP_ID_LENGTH);
1160 int close_index = add_conn_to_groupchat(g_c, friendcon_id, groupnumber, 0, 1); 1406 const int close_index = add_conn_to_groupchat(g_c, friendcon_id, groupnumber, GROUPCHAT_CLOSE_REASON_INTRODUCER, 1);
1161 1407
1162 if (close_index != -1) { 1408 if (close_index != -1) {
1163 g->close[close_index].group_number = other_groupnum; 1409 g->close[close_index].group_number = other_groupnum;
1164 g->close[close_index].type = GROUPCHAT_CLOSE_ONLINE; 1410 g->close[close_index].type = GROUPCHAT_CLOSE_ONLINE;
1165 g->number_joined = friendcon_id;
1166 g->close[close_index].introducer = true;
1167 } 1411 }
1168 1412
1169 send_peer_query(g_c, friendcon_id, other_groupnum); 1413 send_peer_query(g_c, friendcon_id, other_groupnum);
@@ -1420,6 +1664,25 @@ int group_title_get(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *title
1420 return g->title_len; 1664 return g->title_len;
1421} 1665}
1422 1666
1667static bool get_peer_number(const Group_c *g, const uint8_t *real_pk, uint16_t *peer_number)
1668{
1669 const int peer_index = peer_in_chat(g, real_pk);
1670
1671 if (peer_index >= 0) {
1672 *peer_number = g->group[peer_index].peer_number;
1673 return true;
1674 }
1675
1676 for (uint32_t i = 0; i < g->numfrozen; ++i) {
1677 if (id_equal(g->frozen[i].real_pk, real_pk)) {
1678 *peer_number = g->frozen[i].peer_number;
1679 return true;
1680 }
1681 }
1682
1683 return false;
1684}
1685
1423static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, 1686static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length,
1424 void *userdata) 1687 void *userdata)
1425{ 1688{
@@ -1430,7 +1693,7 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con
1430 } 1693 }
1431 1694
1432 const uint8_t *invite_data = data + 1; 1695 const uint8_t *invite_data = data + 1;
1433 uint16_t invite_length = length - 1; 1696 const uint16_t invite_length = length - 1;
1434 1697
1435 switch (data[0]) { 1698 switch (data[0]) {
1436 case INVITE_ID: { 1699 case INVITE_ID: {
@@ -1438,7 +1701,7 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con
1438 return; 1701 return;
1439 } 1702 }
1440 1703
1441 int groupnumber = get_group_num(g_c, data[1 + sizeof(uint16_t)], data + 1 + sizeof(uint16_t) + 1); 1704 const int groupnumber = get_group_num(g_c, data[1 + sizeof(uint16_t)], data + 1 + sizeof(uint16_t) + 1);
1442 1705
1443 if (groupnumber == -1) { 1706 if (groupnumber == -1) {
1444 if (g_c->invite_callback) { 1707 if (g_c->invite_callback) {
@@ -1480,7 +1743,7 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con
1480 1743
1481 unsigned int tries = 0; 1744 unsigned int tries = 0;
1482 1745
1483 while (get_peer_index(g, peer_number) != -1) { 1746 while (get_peer_index(g, peer_number) != -1 || get_frozen_index(g, peer_number) != -1) {
1484 peer_number = random_u16(); 1747 peer_number = random_u16();
1485 ++tries; 1748 ++tries;
1486 1749
@@ -1502,19 +1765,19 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con
1502 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE], temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; 1765 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE], temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
1503 get_friendcon_public_keys(real_pk, temp_pk, g_c->fr_c, friendcon_id); 1766 get_friendcon_public_keys(real_pk, temp_pk, g_c->fr_c, friendcon_id);
1504 1767
1505 addpeer(g_c, groupnum, real_pk, temp_pk, peer_number, userdata, true); 1768 addpeer(g_c, groupnum, real_pk, temp_pk, peer_number, userdata, true, true);
1506 int close_index = add_conn_to_groupchat(g_c, friendcon_id, groupnum, 0, 1); 1769 const int close_index = add_conn_to_groupchat(g_c, friendcon_id, groupnum, GROUPCHAT_CLOSE_REASON_INTRODUCING, 1);
1507 1770
1508 if (close_index != -1) { 1771 if (close_index != -1) {
1509 g->close[close_index].group_number = other_groupnum; 1772 g->close[close_index].group_number = other_groupnum;
1510 g->close[close_index].type = GROUPCHAT_CLOSE_ONLINE; 1773 g->close[close_index].type = GROUPCHAT_CLOSE_ONLINE;
1511 g->close[close_index].introduced = true;
1512 } 1774 }
1513 1775
1514 group_new_peer_send(g_c, groupnum, peer_number, real_pk, temp_pk); 1776 group_new_peer_send(g_c, groupnum, peer_number, real_pk, temp_pk);
1515 break; 1777 break;
1516 } 1778 }
1517 1779
1780
1518 default: 1781 default:
1519 return; 1782 return;
1520 } 1783 }
@@ -1572,7 +1835,7 @@ static int send_packet_online(Friend_Connections *fr_c, int friendcon_id, uint16
1572 sizeof(packet), 0) != -1; 1835 sizeof(packet), 0) != -1;
1573} 1836}
1574 1837
1575static unsigned int send_peer_kill(Group_Chats *g_c, int friendcon_id, uint16_t group_num); 1838static int ping_groupchat(Group_Chats *g_c, uint32_t groupnumber);
1576 1839
1577static int handle_packet_online(Group_Chats *g_c, int friendcon_id, const uint8_t *data, uint16_t length) 1840static int handle_packet_online(Group_Chats *g_c, int friendcon_id, const uint8_t *data, uint16_t length)
1578{ 1841{
@@ -1580,7 +1843,7 @@ static int handle_packet_online(Group_Chats *g_c, int friendcon_id, const uint8_
1580 return -1; 1843 return -1;
1581 } 1844 }
1582 1845
1583 int groupnumber = get_group_num(g_c, data[sizeof(uint16_t)], data + sizeof(uint16_t) + 1); 1846 const int groupnumber = get_group_num(g_c, data[sizeof(uint16_t)], data + sizeof(uint16_t) + 1);
1584 1847
1585 if (groupnumber == -1) { 1848 if (groupnumber == -1) {
1586 return -1; 1849 return -1;
@@ -1596,7 +1859,7 @@ static int handle_packet_online(Group_Chats *g_c, int friendcon_id, const uint8_
1596 return -1; 1859 return -1;
1597 } 1860 }
1598 1861
1599 int index = friend_in_close(g, friendcon_id); 1862 const int index = friend_in_close(g, friendcon_id);
1600 1863
1601 if (index == -1) { 1864 if (index == -1) {
1602 return -1; 1865 return -1;
@@ -1606,7 +1869,7 @@ static int handle_packet_online(Group_Chats *g_c, int friendcon_id, const uint8_
1606 return -1; 1869 return -1;
1607 } 1870 }
1608 1871
1609 if (count_close_connected(g) == 0) { 1872 if (count_close_connected(g) == 0 || (g->close[index].reasons & GROUPCHAT_CLOSE_REASON_INTRODUCER)) {
1610 send_peer_query(g_c, friendcon_id, other_groupnum); 1873 send_peer_query(g_c, friendcon_id, other_groupnum);
1611 } 1874 }
1612 1875
@@ -1614,18 +1877,68 @@ static int handle_packet_online(Group_Chats *g_c, int friendcon_id, const uint8_
1614 g->close[index].type = GROUPCHAT_CLOSE_ONLINE; 1877 g->close[index].type = GROUPCHAT_CLOSE_ONLINE;
1615 send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->type, g->id); 1878 send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->type, g->id);
1616 1879
1880 if (g->close[index].reasons & GROUPCHAT_CLOSE_REASON_INTRODUCING) {
1881 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE], temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
1882 get_friendcon_public_keys(real_pk, temp_pk, g_c->fr_c, friendcon_id);
1883
1884 const int peer_index = peer_in_chat(g, real_pk);
1885
1886 if (peer_index != -1) {
1887 group_new_peer_send(g_c, groupnumber, g->group[peer_index].peer_number, real_pk, temp_pk);
1888 }
1889
1890 g->need_send_name = true;
1891 }
1892
1893 ping_groupchat(g_c, groupnumber);
1894
1617 return 0; 1895 return 0;
1618} 1896}
1619 1897
1898static int handle_packet_rejoin(Group_Chats *g_c, int friendcon_id, const uint8_t *data, uint16_t length,
1899 void *userdata)
1900{
1901 if (length < 1 + GROUP_ID_LENGTH) {
1902 return -1;
1903 }
1904
1905 const int32_t groupnum = get_group_num(g_c, *data, data + 1);
1906
1907 Group_c *g = get_group_c(g_c, groupnum);
1908
1909 if (!g) {
1910 return -1;
1911 }
1912
1913 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE], temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
1914 get_friendcon_public_keys(real_pk, temp_pk, g_c->fr_c, friendcon_id);
1915
1916 uint16_t peer_number;
1917
1918 if (!get_peer_number(g, real_pk, &peer_number)) {
1919 return -1;
1920 }
1921
1922 addpeer(g_c, groupnum, real_pk, temp_pk, peer_number, userdata, true, true);
1923 const int close_index = add_conn_to_groupchat(g_c, friendcon_id, groupnum, GROUPCHAT_CLOSE_REASON_INTRODUCING, 1);
1924
1925 if (close_index != -1) {
1926 send_packet_online(g_c->fr_c, friendcon_id, groupnum, g->type, g->id);
1927 }
1928
1929 return 0;
1930}
1931
1932
1620// we could send title with invite, but then if it changes between sending and accepting inv, joinee won't see it 1933// we could send title with invite, but then if it changes between sending and accepting inv, joinee won't see it
1621 1934
1622/* return 1 on success. 1935/* return 1 on success.
1623 * return 0 on failure 1936 * return 0 on failure
1624 */ 1937 */
1625static unsigned int send_peer_kill(Group_Chats *g_c, int friendcon_id, uint16_t group_num) 1938static unsigned int send_peer_introduced(Group_Chats *g_c, int friendcon_id, uint16_t group_num)
1626{ 1939{
1627 uint8_t packet[1]; 1940 uint8_t packet[1];
1628 packet[0] = PEER_KILL_ID; 1941 packet[0] = PEER_INTRODUCED_ID;
1629 return send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num, packet, sizeof(packet)); 1942 return send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num, packet, sizeof(packet));
1630} 1943}
1631 1944
@@ -1671,7 +1984,7 @@ static unsigned int send_peers(Group_Chats *g_c, uint32_t groupnumber, int frien
1671 p = response_packet + 1; 1984 p = response_packet + 1;
1672 } 1985 }
1673 1986
1674 uint16_t peer_num = net_htons(g->group[i].peer_number); 1987 const uint16_t peer_num = net_htons(g->group[i].peer_number);
1675 memcpy(p, &peer_num, sizeof(peer_num)); 1988 memcpy(p, &peer_num, sizeof(peer_num));
1676 p += sizeof(peer_num); 1989 p += sizeof(peer_num);
1677 memcpy(p, g->group[i].real_pk, CRYPTO_PUBLIC_KEY_SIZE); 1990 memcpy(p, g->group[i].real_pk, CRYPTO_PUBLIC_KEY_SIZE);
@@ -1732,17 +2045,17 @@ static int handle_send_peers(Group_Chats *g_c, uint32_t groupnumber, const uint8
1732 g_c->connected_callback(g_c->m, groupnumber, userdata); 2045 g_c->connected_callback(g_c->m, groupnumber, userdata);
1733 } 2046 }
1734 2047
1735 group_name_send(g_c, groupnumber, g_c->m->name, g_c->m->name_length); 2048 g->need_send_name = true;
1736 } 2049 }
1737 2050
1738 int peer_index = addpeer(g_c, groupnumber, d, d + CRYPTO_PUBLIC_KEY_SIZE, peer_num, userdata, true); 2051 const int peer_index = addpeer(g_c, groupnumber, d, d + CRYPTO_PUBLIC_KEY_SIZE, peer_num, userdata, false, true);
1739 2052
1740 if (peer_index == -1) { 2053 if (peer_index == -1) {
1741 return -1; 2054 return -1;
1742 } 2055 }
1743 2056
1744 d += CRYPTO_PUBLIC_KEY_SIZE * 2; 2057 d += CRYPTO_PUBLIC_KEY_SIZE * 2;
1745 uint8_t name_length = *d; 2058 const uint8_t name_length = *d;
1746 d += 1; 2059 d += 1;
1747 2060
1748 if (name_length > (length - (d - data)) || name_length > MAX_NAME_LENGTH) { 2061 if (name_length > (length - (d - data)) || name_length > MAX_NAME_LENGTH) {
@@ -1767,17 +2080,14 @@ static void handle_direct_packet(Group_Chats *g_c, uint32_t groupnumber, const u
1767 } 2080 }
1768 2081
1769 switch (data[0]) { 2082 switch (data[0]) {
1770 case PEER_KILL_ID: { 2083 case PEER_INTRODUCED_ID: {
1771 Group_c *g = get_group_c(g_c, groupnumber); 2084 Group_c *g = get_group_c(g_c, groupnumber);
1772 2085
1773 if (!g) { 2086 if (!g) {
1774 return; 2087 return;
1775 } 2088 }
1776 2089
1777 if (!g->close[close_index].closest) { 2090 remove_conn_reason(g_c, groupnumber, close_index, GROUPCHAT_CLOSE_REASON_INTRODUCING);
1778 g->close[close_index].type = GROUPCHAT_CLOSE_NONE;
1779 kill_friend_connection(g_c->fr_c, g->close[close_index].number);
1780 }
1781 } 2091 }
1782 2092
1783 break; 2093 break;
@@ -1801,7 +2111,15 @@ static void handle_direct_packet(Group_Chats *g_c, uint32_t groupnumber, const u
1801 break; 2111 break;
1802 2112
1803 case PEER_TITLE_ID: { 2113 case PEER_TITLE_ID: {
1804 settitle(g_c, groupnumber, -1, data + 1, length - 1, userdata); 2114 Group_c *g = get_group_c(g_c, groupnumber);
2115
2116 if (!g) {
2117 break;
2118 }
2119
2120 if (!g->title_fresh) {
2121 settitle(g_c, groupnumber, -1, data + 1, length - 1, userdata);
2122 }
1805 } 2123 }
1806 2124
1807 break; 2125 break;
@@ -1868,7 +2186,7 @@ static unsigned int send_lossy_all_close(const Group_Chats *g_c, uint32_t groupn
1868 continue; 2186 continue;
1869 } 2187 }
1870 2188
1871 if (g->close[i].closest) { 2189 if (g->close[i].reasons & GROUPCHAT_CLOSE_REASON_CLOSEST) {
1872 connected_closest[num_connected_closest] = i; 2190 connected_closest[num_connected_closest] = i;
1873 ++num_connected_closest; 2191 ++num_connected_closest;
1874 continue; 2192 continue;
@@ -1891,7 +2209,7 @@ static unsigned int send_lossy_all_close(const Group_Chats *g_c, uint32_t groupn
1891 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE] = {0}; 2209 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE] = {0};
1892 uint8_t dht_temp_pk[CRYPTO_PUBLIC_KEY_SIZE] = {0}; 2210 uint8_t dht_temp_pk[CRYPTO_PUBLIC_KEY_SIZE] = {0};
1893 get_friendcon_public_keys(real_pk, dht_temp_pk, g_c->fr_c, g->close[connected_closest[i]].number); 2211 get_friendcon_public_keys(real_pk, dht_temp_pk, g_c->fr_c, g->close[connected_closest[i]].number);
1894 uint64_t comp_val = calculate_comp_value(g->real_pk, real_pk); 2212 const uint64_t comp_val = calculate_comp_value(g->real_pk, real_pk);
1895 2213
1896 if (comp_val < comp_val_old) { 2214 if (comp_val < comp_val_old) {
1897 to_send = connected_closest[i]; 2215 to_send = connected_closest[i];
@@ -1911,7 +2229,7 @@ static unsigned int send_lossy_all_close(const Group_Chats *g_c, uint32_t groupn
1911 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE] = {0}; 2229 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE] = {0};
1912 uint8_t dht_temp_pk[CRYPTO_PUBLIC_KEY_SIZE] = {0}; 2230 uint8_t dht_temp_pk[CRYPTO_PUBLIC_KEY_SIZE] = {0};
1913 get_friendcon_public_keys(real_pk, dht_temp_pk, g_c->fr_c, g->close[connected_closest[i]].number); 2231 get_friendcon_public_keys(real_pk, dht_temp_pk, g_c->fr_c, g->close[connected_closest[i]].number);
1914 uint64_t comp_val = calculate_comp_value(real_pk, g->real_pk); 2232 const uint64_t comp_val = calculate_comp_value(real_pk, g->real_pk);
1915 2233
1916 if (comp_val < comp_val_old) { 2234 if (comp_val < comp_val_old) {
1917 to_send_other = connected_closest[i]; 2235 to_send_other = connected_closest[i];
@@ -1957,7 +2275,7 @@ static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint
1957 } 2275 }
1958 2276
1959 VLA(uint8_t, packet, sizeof(uint16_t) + sizeof(uint32_t) + 1 + len); 2277 VLA(uint8_t, packet, sizeof(uint16_t) + sizeof(uint32_t) + 1 + len);
1960 uint16_t peer_num = net_htons(g->peer_number); 2278 const uint16_t peer_num = net_htons(g->peer_number);
1961 memcpy(packet, &peer_num, sizeof(peer_num)); 2279 memcpy(packet, &peer_num, sizeof(peer_num));
1962 2280
1963 ++g->message_number; 2281 ++g->message_number;
@@ -1966,7 +2284,7 @@ static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint
1966 ++g->message_number; 2284 ++g->message_number;
1967 } 2285 }
1968 2286
1969 uint32_t message_num = net_htonl(g->message_number); 2287 const uint32_t message_num = net_htonl(g->message_number);
1970 memcpy(packet + sizeof(uint16_t), &message_num, sizeof(message_num)); 2288 memcpy(packet + sizeof(uint16_t), &message_num, sizeof(message_num));
1971 2289
1972 packet[sizeof(uint16_t) + sizeof(uint32_t)] = message_id; 2290 packet[sizeof(uint16_t) + sizeof(uint32_t)] = message_id;
@@ -1986,7 +2304,7 @@ static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint
1986 */ 2304 */
1987int group_message_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *message, uint16_t length) 2305int group_message_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *message, uint16_t length)
1988{ 2306{
1989 int ret = send_message_group(g_c, groupnumber, PACKET_ID_MESSAGE, message, length); 2307 const int ret = send_message_group(g_c, groupnumber, PACKET_ID_MESSAGE, message, length);
1990 2308
1991 if (ret > 0) { 2309 if (ret > 0) {
1992 return 0; 2310 return 0;
@@ -2001,7 +2319,7 @@ int group_message_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8
2001 */ 2319 */
2002int group_action_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *action, uint16_t length) 2320int group_action_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *action, uint16_t length)
2003{ 2321{
2004 int ret = send_message_group(g_c, groupnumber, PACKET_ID_ACTION, action, length); 2322 const int ret = send_message_group(g_c, groupnumber, PACKET_ID_ACTION, action, length);
2005 2323
2006 if (ret > 0) { 2324 if (ret > 0) {
2007 return 0; 2325 return 0;
@@ -2025,9 +2343,9 @@ int send_group_lossy_packet(const Group_Chats *g_c, uint32_t groupnumber, const
2025 } 2343 }
2026 2344
2027 VLA(uint8_t, packet, sizeof(uint16_t) * 2 + length); 2345 VLA(uint8_t, packet, sizeof(uint16_t) * 2 + length);
2028 uint16_t peer_number = net_htons(g->peer_number); 2346 const uint16_t peer_number = net_htons(g->peer_number);
2029 memcpy(packet, &peer_number, sizeof(uint16_t)); 2347 memcpy(packet, &peer_number, sizeof(uint16_t));
2030 uint16_t message_num = net_htons(g->lossy_message_number); 2348 const uint16_t message_num = net_htons(g->lossy_message_number);
2031 memcpy(packet + sizeof(uint16_t), &message_num, sizeof(uint16_t)); 2349 memcpy(packet + sizeof(uint16_t), &message_num, sizeof(uint16_t));
2032 memcpy(packet + sizeof(uint16_t) * 2, data, length); 2350 memcpy(packet + sizeof(uint16_t) * 2, data, length);
2033 2351
@@ -2108,33 +2426,31 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber,
2108 memcpy(&peer_number, data, sizeof(uint16_t)); 2426 memcpy(&peer_number, data, sizeof(uint16_t));
2109 peer_number = net_ntohs(peer_number); 2427 peer_number = net_ntohs(peer_number);
2110 2428
2111 int index = get_peer_index(g, peer_number); 2429 const int index = note_peer_active(g_c, groupnumber, peer_number, userdata);
2112 2430
2113 if (index == -1) { 2431 if (index == -1) {
2114 /* We don't know the peer this packet came from so we query the list of peers from that peer. 2432 /* If we don't know the peer this packet came from, then we query the
2115 (They would not have relayed it if they didn't know the peer.) */ 2433 * list of peers from the relaying peer.
2434 * (They would not have relayed it if they didn't know the peer.) */
2116 send_peer_query(g_c, g->close[close_index].number, g->close[close_index].group_number); 2435 send_peer_query(g_c, g->close[close_index].number, g->close[close_index].group_number);
2117 return; 2436 return;
2118 } 2437 }
2119 2438
2120 if (g->number_joined != -1 && count_close_connected(g) >= DESIRED_CLOSE_CONNECTIONS) { 2439 if (g->num_introducer_connections > 0 && count_close_connected(g) > DESIRED_CLOSE_CONNECTIONS) {
2121 const int fr_close_index = friend_in_close(g, g->number_joined); 2440 for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
2441 if (g->close[i].type == GROUPCHAT_CLOSE_NONE
2442 || !(g->close[i].reasons & GROUPCHAT_CLOSE_REASON_INTRODUCER)
2443 || i == close_index) {
2444 continue;
2445 }
2122 2446
2123 if (fr_close_index >= 0 && fr_close_index != close_index && !g->close[fr_close_index].closest) {
2124 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; 2447 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
2125 get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->close[fr_close_index].number); 2448 get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->close[i].number);
2126 2449
2127 if (id_equal(g->group[index].real_pk, real_pk)) { 2450 if (id_equal(g->group[index].real_pk, real_pk)) {
2128 /* Received message from peer relayed via another peer, so 2451 /* Received message from peer relayed via another peer, so
2129 * the introduction was successful */ 2452 * the introduction was successful */
2130 g->number_joined = -1; 2453 remove_conn_reason(g_c, groupnumber, i, GROUPCHAT_CLOSE_REASON_INTRODUCER);
2131 g->close[fr_close_index].introducer = false;
2132
2133 if (!g->close[fr_close_index].closest && !g->close[fr_close_index].introduced) {
2134 g->close[fr_close_index].type = GROUPCHAT_CLOSE_NONE;
2135 send_peer_kill(g_c, g->close[fr_close_index].number, g->close[fr_close_index].group_number);
2136 kill_friend_connection(g_c->fr_c, g->close[fr_close_index].number);
2137 }
2138 } 2454 }
2139 } 2455 }
2140 } 2456 }
@@ -2143,24 +2459,17 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber,
2143 memcpy(&message_number, data + sizeof(uint16_t), sizeof(message_number)); 2459 memcpy(&message_number, data + sizeof(uint16_t), sizeof(message_number));
2144 message_number = net_ntohl(message_number); 2460 message_number = net_ntohl(message_number);
2145 2461
2146 uint8_t message_id = data[sizeof(uint16_t) + sizeof(message_number)]; 2462 const uint8_t message_id = data[sizeof(uint16_t) + sizeof(message_number)];
2147 const uint8_t *msg_data = data + sizeof(uint16_t) + sizeof(message_number) + 1; 2463 const uint8_t *msg_data = data + sizeof(uint16_t) + sizeof(message_number) + 1;
2148 uint16_t msg_data_len = length - (sizeof(uint16_t) + sizeof(message_number) + 1); 2464 const uint16_t msg_data_len = length - (sizeof(uint16_t) + sizeof(message_number) + 1);
2149 2465
2150 // FIXME(zugz) update discussion of message numbers in the spec
2151 if (!check_message_info(message_number, message_id, &g->group[index])) { 2466 if (!check_message_info(message_number, message_id, &g->group[index])) {
2152 return; 2467 return;
2153 } 2468 }
2154 2469
2155 switch (message_id) { 2470 switch (message_id) {
2156 case GROUP_MESSAGE_PING_ID: { 2471 case GROUP_MESSAGE_PING_ID:
2157 if (msg_data_len != 0) { 2472 break;
2158 return;
2159 }
2160
2161 g->group[index].last_recv = mono_time_get(g_c->mono_time);
2162 }
2163 break;
2164 2473
2165 case GROUP_MESSAGE_NEW_PEER_ID: { 2474 case GROUP_MESSAGE_NEW_PEER_ID: {
2166 if (msg_data_len != GROUP_MESSAGE_NEW_PEER_LENGTH) { 2475 if (msg_data_len != GROUP_MESSAGE_NEW_PEER_LENGTH) {
@@ -2171,7 +2480,7 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber,
2171 memcpy(&new_peer_number, msg_data, sizeof(uint16_t)); 2480 memcpy(&new_peer_number, msg_data, sizeof(uint16_t));
2172 new_peer_number = net_ntohs(new_peer_number); 2481 new_peer_number = net_ntohs(new_peer_number);
2173 addpeer(g_c, groupnumber, msg_data + sizeof(uint16_t), msg_data + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE, 2482 addpeer(g_c, groupnumber, msg_data + sizeof(uint16_t), msg_data + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE,
2174 new_peer_number, userdata, true); 2483 new_peer_number, userdata, true, true);
2175 } 2484 }
2176 break; 2485 break;
2177 2486
@@ -2185,7 +2494,7 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber,
2185 kill_peer_number = net_ntohs(kill_peer_number); 2494 kill_peer_number = net_ntohs(kill_peer_number);
2186 2495
2187 if (peer_number == kill_peer_number) { 2496 if (peer_number == kill_peer_number) {
2188 delpeer(g_c, groupnumber, index, userdata); 2497 delpeer(g_c, groupnumber, index, userdata, false);
2189 } else { 2498 } else {
2190 return; 2499 return;
2191 // TODO(irungentoo): 2500 // TODO(irungentoo):
@@ -2260,6 +2569,10 @@ static int g_handle_packet(void *object, int friendcon_id, const uint8_t *data,
2260 return handle_packet_online(g_c, friendcon_id, data + 1, length - 1); 2569 return handle_packet_online(g_c, friendcon_id, data + 1, length - 1);
2261 } 2570 }
2262 2571
2572 if (data[0] == PACKET_ID_REJOIN_CONFERENCE) {
2573 return handle_packet_rejoin(g_c, friendcon_id, data + 1, length - 1, userdata);
2574 }
2575
2263 if (data[0] != PACKET_ID_DIRECT_CONFERENCE && data[0] != PACKET_ID_MESSAGE_CONFERENCE) { 2576 if (data[0] != PACKET_ID_DIRECT_CONFERENCE && data[0] != PACKET_ID_MESSAGE_CONFERENCE) {
2264 return -1; 2577 return -1;
2265 } 2578 }
@@ -2273,7 +2586,7 @@ static int g_handle_packet(void *object, int friendcon_id, const uint8_t *data,
2273 return -1; 2586 return -1;
2274 } 2587 }
2275 2588
2276 int index = friend_in_close(g, friendcon_id); 2589 const int index = friend_in_close(g, friendcon_id);
2277 2590
2278 if (index == -1) { 2591 if (index == -1) {
2279 return -1; 2592 return -1;
@@ -2335,7 +2648,7 @@ static unsigned int lossy_packet_not_received(Group_c *g, int peer_index, uint16
2335 return -1; 2648 return -1;
2336 } 2649 }
2337 2650
2338 uint16_t top_distance = message_number - g->group[peer_index].top_lossy_number; 2651 const uint16_t top_distance = message_number - g->group[peer_index].top_lossy_number;
2339 2652
2340 if (top_distance >= MAX_LOSSY_COUNT) { 2653 if (top_distance >= MAX_LOSSY_COUNT) {
2341 crypto_memzero(g->group[peer_index].recv_lossy, sizeof(g->group[peer_index].recv_lossy)); 2654 crypto_memzero(g->group[peer_index].recv_lossy, sizeof(g->group[peer_index].recv_lossy));
@@ -2384,7 +2697,7 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin
2384 return -1; 2697 return -1;
2385 } 2698 }
2386 2699
2387 int index = friend_in_close(g, friendcon_id); 2700 const int index = friend_in_close(g, friendcon_id);
2388 2701
2389 if (index == -1) { 2702 if (index == -1) {
2390 return -1; 2703 return -1;
@@ -2394,7 +2707,7 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin
2394 return -1; 2707 return -1;
2395 } 2708 }
2396 2709
2397 int peer_index = get_peer_index(g, peer_number); 2710 const int peer_index = get_peer_index(g, peer_number);
2398 2711
2399 if (peer_index == -1) { 2712 if (peer_index == -1) {
2400 return -1; 2713 return -1;
@@ -2406,7 +2719,7 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin
2406 2719
2407 const uint8_t *lossy_data = data + 1 + sizeof(uint16_t) * 3; 2720 const uint8_t *lossy_data = data + 1 + sizeof(uint16_t) * 3;
2408 uint16_t lossy_length = length - (1 + sizeof(uint16_t) * 3); 2721 uint16_t lossy_length = length - (1 + sizeof(uint16_t) * 3);
2409 uint8_t message_id = lossy_data[0]; 2722 const uint8_t message_id = lossy_data[0];
2410 ++lossy_data; 2723 ++lossy_data;
2411 --lossy_length; 2724 --lossy_length;
2412 2725
@@ -2517,7 +2830,7 @@ static int ping_groupchat(Group_Chats *g_c, uint32_t groupnumber)
2517 return 0; 2830 return 0;
2518} 2831}
2519 2832
2520static int groupchat_clear_timedout(Group_Chats *g_c, uint32_t groupnumber, void *userdata) 2833static int groupchat_freeze_timedout(Group_Chats *g_c, uint32_t groupnumber, void *userdata)
2521{ 2834{
2522 Group_c *g = get_group_c(g_c, groupnumber); 2835 Group_c *g = get_group_c(g_c, groupnumber);
2523 2836
@@ -2526,16 +2839,20 @@ static int groupchat_clear_timedout(Group_Chats *g_c, uint32_t groupnumber, void
2526 } 2839 }
2527 2840
2528 for (uint32_t i = 0; i < g->numpeers; ++i) { 2841 for (uint32_t i = 0; i < g->numpeers; ++i) {
2529 if (g->peer_number != g->group[i].peer_number 2842 if (g->group[i].peer_number == g->peer_number) {
2530 && mono_time_is_timeout(g_c->mono_time, g->group[i].last_recv, GROUP_PING_INTERVAL * 3)) { 2843 continue;
2531 delpeer(g_c, groupnumber, i, userdata);
2532 } 2844 }
2533 2845
2534 if (g->group == nullptr || i >= g->numpeers) { 2846 if (mono_time_is_timeout(g_c->mono_time, g->group[i].last_active, GROUP_PING_INTERVAL * 3)) {
2535 break; 2847 try_send_rejoin(g_c, groupnumber, g->group[i].real_pk);
2848 freeze_peer(g_c, groupnumber, i, userdata);
2536 } 2849 }
2537 } 2850 }
2538 2851
2852 if (g->numpeers <= 1) {
2853 g->title_fresh = false;
2854 }
2855
2539 return 0; 2856 return 0;
2540} 2857}
2541 2858
@@ -2552,6 +2869,7 @@ void send_name_all_groups(Group_Chats *g_c)
2552 2869
2553 if (g->status == GROUPCHAT_STATUS_CONNECTED) { 2870 if (g->status == GROUPCHAT_STATUS_CONNECTED) {
2554 group_name_send(g_c, i, g_c->m->name, g_c->m->name_length); 2871 group_name_send(g_c, i, g_c->m->name, g_c->m->name_length);
2872 g->need_send_name = false;
2555 } 2873 }
2556 } 2874 }
2557} 2875}
@@ -2591,7 +2909,12 @@ void do_groupchats(Group_Chats *g_c, void *userdata)
2591 if (g->status == GROUPCHAT_STATUS_CONNECTED) { 2909 if (g->status == GROUPCHAT_STATUS_CONNECTED) {
2592 connect_to_closest(g_c, i, userdata); 2910 connect_to_closest(g_c, i, userdata);
2593 ping_groupchat(g_c, i); 2911 ping_groupchat(g_c, i);
2594 groupchat_clear_timedout(g_c, i, userdata); 2912 groupchat_freeze_timedout(g_c, i, userdata);
2913
2914 if (g->need_send_name) {
2915 group_name_send(g_c, i, g_c->m->name, g_c->m->name_length);
2916 g->need_send_name = false;
2917 }
2595 } 2918 }
2596 } 2919 }
2597 2920
diff --git a/toxcore/group.h b/toxcore/group.h
index 68488e27..110f5d44 100644
--- a/toxcore/group.h
+++ b/toxcore/group.h
@@ -49,12 +49,13 @@ typedef struct Message_Info {
49typedef struct Group_Peer { 49typedef struct Group_Peer {
50 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; 50 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
51 uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; 51 uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
52 bool temp_pk_updated;
52 53
53 uint64_t last_recv; 54 uint64_t last_active;
54 55
55 Message_Info 56 Message_Info
56 last_message_infos[MAX_LAST_MESSAGE_INFOS]; /* received messages, strictly decreasing in message_number */ 57 last_message_infos[MAX_LAST_MESSAGE_INFOS]; /* received messages, strictly decreasing in message_number */
57 uint8_t num_last_message_infos; 58 uint8_t num_last_message_infos;
58 59
59 uint8_t nick[MAX_NAME_LENGTH]; 60 uint8_t nick[MAX_NAME_LENGTH];
60 uint8_t nick_len; 61 uint8_t nick_len;
@@ -79,11 +80,18 @@ typedef enum Groupchat_Close_Type {
79 GROUPCHAT_CLOSE_ONLINE 80 GROUPCHAT_CLOSE_ONLINE
80} Groupchat_Close_Type; 81} Groupchat_Close_Type;
81 82
83/* Connection is to one of the closest DESIRED_CLOSE_CONNECTIONS peers */
84#define GROUPCHAT_CLOSE_REASON_CLOSEST (1 << 0)
85
86/* Connection is to a peer we are introducing to the conference */
87#define GROUPCHAT_CLOSE_REASON_INTRODUCING (1 << 1)
88
89/* Connection is to a peer who is introducing us to the conference */
90#define GROUPCHAT_CLOSE_REASON_INTRODUCER (1 << 2)
91
82typedef struct Groupchat_Close { 92typedef struct Groupchat_Close {
83 uint8_t type; /* GROUPCHAT_CLOSE_* */ 93 uint8_t type; /* GROUPCHAT_CLOSE_* */
84 bool closest; /* connected to peer because it is one of our closest peers */ 94 uint8_t reasons; /* bit field with flags GROUPCHAT_CLOSE_REASON_* */
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 */
87 uint32_t number; 95 uint32_t number;
88 uint16_t group_number; 96 uint16_t group_number;
89} Groupchat_Close; 97} Groupchat_Close;
@@ -101,9 +109,15 @@ typedef void group_on_delete_cb(void *object, uint32_t conference_number);
101typedef struct Group_c { 109typedef struct Group_c {
102 uint8_t status; 110 uint8_t status;
103 111
112 bool need_send_name;
113 bool title_fresh;
114
104 Group_Peer *group; 115 Group_Peer *group;
105 uint32_t numpeers; 116 uint32_t numpeers;
106 117
118 Group_Peer *frozen;
119 uint32_t numfrozen;
120
107 /* TODO(zugz) rename close to something more accurate - "connected"? */ 121 /* TODO(zugz) rename close to something more accurate - "connected"? */
108 Groupchat_Close close[MAX_GROUP_CONNECTIONS]; 122 Groupchat_Close close[MAX_GROUP_CONNECTIONS];
109 123
@@ -123,7 +137,7 @@ typedef struct Group_c {
123 137
124 uint64_t last_sent_ping; 138 uint64_t last_sent_ping;
125 139
126 int number_joined; /* friendcon_id of person that invited us to the chat. (-1 means none) */ 140 uint32_t num_introducer_connections;
127 141
128 void *object; 142 void *object;
129 143
diff --git a/toxcore/net_crypto.h b/toxcore/net_crypto.h
index 7a2ff1ff..9fd47014 100644
--- a/toxcore/net_crypto.h
+++ b/toxcore/net_crypto.h
@@ -78,6 +78,7 @@
78#define PACKET_ID_ONLINE_PACKET 97 78#define PACKET_ID_ONLINE_PACKET 97
79#define PACKET_ID_DIRECT_CONFERENCE 98 79#define PACKET_ID_DIRECT_CONFERENCE 98
80#define PACKET_ID_MESSAGE_CONFERENCE 99 80#define PACKET_ID_MESSAGE_CONFERENCE 99
81#define PACKET_ID_REJOIN_CONFERENCE 100
81#define PACKET_ID_LOSSY_CONFERENCE 199 82#define PACKET_ID_LOSSY_CONFERENCE 199
82 83
83/*** Crypto connections. ***/ 84/*** Crypto connections. ***/