diff options
author | zugz <mbays+tox@sdf.org> | 2018-07-25 08:43:48 +0100 |
---|---|---|
committer | zugz (tox) <mbays+tox@sdf.org> | 2018-09-05 20:56:26 +0200 |
commit | 1b2322284f0b688af3a349fe4331be15a565084c (patch) | |
tree | 7651c11b4ec1edd927151e9655727fb0b2365a7c | |
parent | 6872c14e1a02445d945623ee6e85230c5d7ecbce (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.c | 2 | ||||
-rw-r--r-- | auto_tests/conference_peer_nick_test.c | 2 | ||||
-rw-r--r-- | auto_tests/conference_test.c | 237 | ||||
-rw-r--r-- | auto_tests/friend_connection_test.c | 2 | ||||
-rw-r--r-- | auto_tests/lossless_packet_test.c | 2 | ||||
-rw-r--r-- | auto_tests/lossy_packet_test.c | 2 | ||||
-rw-r--r-- | auto_tests/overflow_recvq_test.c | 2 | ||||
-rw-r--r-- | auto_tests/overflow_sendq_test.c | 2 | ||||
-rw-r--r-- | auto_tests/reconnect_test.c | 2 | ||||
-rw-r--r-- | auto_tests/run_auto_test.h | 27 | ||||
-rw-r--r-- | auto_tests/send_message_test.c | 2 | ||||
-rw-r--r-- | docs/minpgc.md | 128 | ||||
-rw-r--r-- | toxcore/group.c | 613 | ||||
-rw-r--r-- | toxcore/group.h | 26 | ||||
-rw-r--r-- | toxcore/net_crypto.h | 1 |
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 | ||
25 | typedef struct State { | 26 | typedef 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 | |||
30 | static void handle_self_connection_status( | 35 | static 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 | ||
73 | static void handle_conference_connected( | 78 | static 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 | ||
105 | static 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 | |||
121 | static 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 | * */ | ||
135 | static 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 | |||
100 | static void run_conference_tests(Tox **toxes, State *state) | 146 | static 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 | ||
161 | static void test_many_group(void) | 243 | static 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 | ||
51 | static void run_auto_test(uint32_t tox_count, void test(Tox **toxes, State *state)) | 51 | static 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 | |||
3 | This document describes the "minpgc" simple persistent conferences | ||
4 | implementation of PR #1069. | ||
5 | |||
6 | Many of the ideas derive from isotoxin's persistent conferences | ||
7 | implementation, PR #826. | ||
8 | |||
9 | ## Specification of changes from pre-existing conference specification | ||
10 | We add one new packet type: | ||
11 | |||
12 | Rejoin Conference packet | ||
13 | |||
14 | | Length | Contents | | ||
15 | |:-------|:--------------------------------| | ||
16 | | `1` | `uint8_t` (0x64) | | ||
17 | | `33` | Group chat identifier | | ||
18 | |||
19 | |||
20 | A peer times out from a group if it has been inactive for 60s. When a peer | ||
21 | times out, we flag it as _frozen_. Frozen peers are disregarded for all | ||
22 | purposes except those discussed below - in particular no packets are sent to | ||
23 | them except as described below, they are omitted from the peer lists sent to | ||
24 | the client or in a Peer Response packet, and they are not considered when | ||
25 | determining closest peers for establishing direct connections. | ||
26 | |||
27 | A peer is considered to be active if we receive a group message or Rejoin | ||
28 | packet from it, or a New Peer message for it. | ||
29 | |||
30 | If a frozen peer is seen to be active, we remove its 'frozen' flag and send a | ||
31 | Name group message. (We can hold off on sending this message until the next | ||
32 | tox\_iterate, and only send one message if many frozen peers become active at | ||
33 | once). | ||
34 | |||
35 | If we receive a New Peer message for a peer, we update its DHT pubkey. | ||
36 | |||
37 | If we receive a group message originating from an unknown peer, we drop the | ||
38 | message but send a Peer Query packet back to the peer who directly sent us the | ||
39 | message. (This is current behaviour; it's mentioned here because it's important | ||
40 | and not currently mentioned in the spec.) | ||
41 | |||
42 | If we receive a Rejoin packet from a peer we update its DHT pubkey, add a | ||
43 | temporary groupchat connection for the peer, and, once the connection is | ||
44 | online, send out a New Peer message announcing the peer, and a Name message. | ||
45 | |||
46 | Whenever we make a new friend connection, we check if the public key is that | ||
47 | of any frozen peer. If so, we send it a Rejoin packet, add a temporary | ||
48 | groupchat connection for it, and, once the connection is online, send the | ||
49 | peer a Peer Query packet. | ||
50 | |||
51 | We do the same with a peer when we are setting it as frozen if we have a | ||
52 | friend connection to it. | ||
53 | |||
54 | The temporary groupchat connections established in sending and handling Rejoin | ||
55 | packets are not immediately operational (because group numbers are not known); | ||
56 | rather, an Online packet is sent when we handle a Rejoin packet. | ||
57 | |||
58 | When a connection is set as online as a result of an Online packet, we ping | ||
59 | the group. | ||
60 | |||
61 | When processing the reply to a Peer Query, we update the DHT pubkey of an | ||
62 | existing peer if and only if it is frozen or has not had its DHT pubkey | ||
63 | updated since it last stopped being frozen. | ||
64 | |||
65 | When we receive a Title Response packet, we set the title if it has never been | ||
66 | set or if at some point since it was last set, there were no unfrozen peers | ||
67 | (except us). | ||
68 | |||
69 | ## Discussion | ||
70 | ### Overview | ||
71 | The intention is to recover seamlessly from splits in the group, the most | ||
72 | common form of which is a single peer temporarily losing all connectivity. | ||
73 | |||
74 | To see how this works, first note that groups (even before the changes | ||
75 | discussed here) have the property that for a group to be connected in the | ||
76 | sense that any peer will receive the messages of any other peer and have them | ||
77 | in their peerlist, it is necessary and sufficient that there is a path of | ||
78 | direct group connections between any two peers. | ||
79 | |||
80 | Now suppose the group is split into two connected components, with each member | ||
81 | of one component frozen according to the members of the other. Suppose there | ||
82 | are two peers, one in each component, which are using the above protocol, and | ||
83 | suppose they establish a friend connection. Then each will rejoin the other, | ||
84 | forming a direct group connection. Hence the whole group will become connected | ||
85 | (even if all other peers are using the unmodified protocol). | ||
86 | |||
87 | The Peer Query packet sent on rejoining hastens this process. | ||
88 | |||
89 | Peers who leave the group during a split will not be deleted by all peers | ||
90 | after the merge - but they will be set as frozen due to ping timeouts, which | ||
91 | is sufficient. | ||
92 | |||
93 | ### Titles | ||
94 | If we have a split into components each containing multiple peers, and the | ||
95 | title is changed in one component, then peers will continue to disagree on the | ||
96 | title after the split. Short of a complicated voting system, this seems the | ||
97 | only reasonable behaviour. | ||
98 | |||
99 | ### Implementation notes | ||
100 | Although I've described the logic in terms of an 'frozen' flag, it might | ||
101 | actually make more sense in the implementation to have a separate list for | ||
102 | frozen peers. | ||
103 | |||
104 | ## Saving | ||
105 | Saving could be implemented by simply saving all live groups with their group | ||
106 | numbers and full peer info for all peers. On reload, all peers would be set as | ||
107 | frozen. | ||
108 | |||
109 | The client would need to support this by understanding that these groups exist | ||
110 | on start-up (e.g. starting windows for them), and by not automatically killing | ||
111 | groups on closing the client. | ||
112 | |||
113 | ## Limitations | ||
114 | If a peer disconnects from the group for a period short enough that group | ||
115 | timeouts do not occur, and a name change occurs during this period, then the | ||
116 | name change will never be propagated. | ||
117 | |||
118 | One way to deal with this would be a general mechanism for storing and | ||
119 | requesting missed group messages. But this is considered out of scope of this | ||
120 | PR. | ||
121 | |||
122 | If a peer changes its DHT pubkey, the change might not be properly propagated | ||
123 | under various circumstances - in particular, if connections do not go down | ||
124 | long enough for the peer to become frozen. | ||
125 | |||
126 | One way to deal with this would be to add a group message announcing the | ||
127 | sending peer's current DHT pubkey, and treat it analogously to the Name | ||
128 | message. | ||
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 | ||
62 | typedef enum Peer_Id { | 62 | typedef 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 | ||
270 | static int friend_in_close(Group_c *g, int friendcon_id); | ||
271 | static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, uint32_t groupnumber, uint8_t closest, | ||
272 | uint8_t lock); | ||
273 | |||
274 | static int add_to_closest(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk, const uint8_t *temp_pk) | 270 | static 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 | ||
370 | static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, uint32_t groupnumber, uint8_t reason, | ||
371 | uint8_t lock); | ||
372 | |||
373 | static void remove_conn_reason(Group_Chats *g_c, uint32_t groupnumber, uint16_t i, uint8_t reason); | ||
374 | |||
374 | static int send_packet_online(Friend_Connections *fr_c, int friendcon_id, uint16_t group_num, uint8_t type, | 375 | static 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. | 446 | static 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 | */ | ||
462 | static 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 | */ |
459 | static int addpeer(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk, const uint8_t *temp_pk, | 544 | static 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 | ||
640 | static 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 | */ |
548 | static int delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata) | 657 | static 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 | ||
705 | static 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 | ||
676 | static 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 */ |
802 | static 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 | |||
823 | static 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 */ |
703 | static void set_conns_status_groups(Group_Chats *g_c, int friendcon_id, uint8_t type) | 851 | static 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 | |||
858 | static bool try_send_rejoin(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk); | ||
859 | |||
860 | static 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 | */ |
732 | static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, uint32_t groupnumber, uint8_t closest, | 904 | static 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 | ||
957 | static 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 | */ | ||
963 | static 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 | */ |
782 | int add_groupchat(Group_Chats *g_c, uint8_t type) | 995 | int 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 | */ | ||
1320 | static 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 | |||
1102 | static unsigned int send_peer_query(Group_Chats *g_c, int friendcon_id, uint16_t group_num); | 1349 | static 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 | ||
1667 | static 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 | |||
1423 | static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, | 1686 | static 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 | ||
1575 | static unsigned int send_peer_kill(Group_Chats *g_c, int friendcon_id, uint16_t group_num); | 1838 | static int ping_groupchat(Group_Chats *g_c, uint32_t groupnumber); |
1576 | 1839 | ||
1577 | static int handle_packet_online(Group_Chats *g_c, int friendcon_id, const uint8_t *data, uint16_t length) | 1840 | static 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 | ||
1898 | static 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 | */ |
1625 | static unsigned int send_peer_kill(Group_Chats *g_c, int friendcon_id, uint16_t group_num) | 1938 | static 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 | */ |
1987 | int group_message_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *message, uint16_t length) | 2305 | int 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 | */ |
2002 | int group_action_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *action, uint16_t length) | 2320 | int 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 | ||
2520 | static int groupchat_clear_timedout(Group_Chats *g_c, uint32_t groupnumber, void *userdata) | 2833 | static 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 { | |||
49 | typedef struct Group_Peer { | 49 | typedef 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 | |||
82 | typedef struct Groupchat_Close { | 92 | typedef 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); | |||
101 | typedef struct Group_c { | 109 | typedef 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. ***/ |