1 /* SPDX-License-Identifier: GPL-3.0-or-later
2 * Copyright © 2016-2018 The TokTok team.
3 * Copyright © 2014 Tox project.
4 */
5
6 /*
7 * Slightly better groupchats implementation.
8 */
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #include "group.h"
14
15 #include <assert.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 #include "mono_time.h"
20 #include "state.h"
21 #include "util.h"
22
23 /**
24 * Packet type IDs as per the protocol specification.
25 */
26 typedef enum Group_Message_Id {
27 GROUP_MESSAGE_PING_ID = 0,
28 GROUP_MESSAGE_NEW_PEER_ID = 16,
29 GROUP_MESSAGE_KILL_PEER_ID = 17,
30 GROUP_MESSAGE_FREEZE_PEER_ID = 18,
31 GROUP_MESSAGE_NAME_ID = 48,
32 GROUP_MESSAGE_TITLE_ID = 49,
33 } Group_Message_Id;
34
35 #define GROUP_MESSAGE_NEW_PEER_LENGTH (sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE * 2)
36 #define GROUP_MESSAGE_KILL_PEER_LENGTH (sizeof(uint16_t))
37
38 #define MAX_GROUP_MESSAGE_DATA_LEN (MAX_CRYPTO_DATA_SIZE - (1 + MIN_MESSAGE_PACKET_LEN))
39
40 typedef enum Invite_Id {
41 INVITE_ID = 0,
42 INVITE_ACCEPT_ID = 1,
43 INVITE_MEMBER_ID = 2,
44 } Invite_Id;
45
46 #define INVITE_PACKET_SIZE (1 + sizeof(uint16_t) + 1 + GROUP_ID_LENGTH)
47 #define INVITE_ACCEPT_PACKET_SIZE (1 + sizeof(uint16_t) * 2 + 1 + GROUP_ID_LENGTH)
48 #define INVITE_MEMBER_PACKET_SIZE (1 + sizeof(uint16_t) * 2 + 1 + GROUP_ID_LENGTH + sizeof(uint16_t))
49
50 #define ONLINE_PACKET_DATA_SIZE (sizeof(uint16_t) + 1 + GROUP_ID_LENGTH)
51
52 typedef enum Peer_Id {
53 PEER_INTRODUCED_ID = 1,
54 PEER_QUERY_ID = 8,
55 PEER_RESPONSE_ID = 9,
56 PEER_TITLE_ID = 10,
57 } Peer_Id;
58
59 #define MIN_MESSAGE_PACKET_LEN (sizeof(uint16_t) * 2 + sizeof(uint32_t) + 1)
60
61 /* return false if the groupnumber is not valid.
62 * return true if the groupnumber is valid.
63 */
is_groupnumber_valid(const Group_Chats * g_c,uint32_t groupnumber)64 static bool is_groupnumber_valid(const Group_Chats *g_c, uint32_t groupnumber)
65 {
66 return groupnumber < g_c->num_chats
67 && g_c->chats != nullptr
68 && g_c->chats[groupnumber].status != GROUPCHAT_STATUS_NONE;
69 }
70
71
72 /* Set the size of the groupchat list to num.
73 *
74 * return false if realloc fails.
75 * return true if it succeeds.
76 */
realloc_conferences(Group_Chats * g_c,uint16_t num)77 static bool realloc_conferences(Group_Chats *g_c, uint16_t num)
78 {
79 if (num == 0) {
80 free(g_c->chats);
81 g_c->chats = nullptr;
82 return true;
83 }
84
85 Group_c *newgroup_chats = (Group_c *)realloc(g_c->chats, num * sizeof(Group_c));
86
87 if (newgroup_chats == nullptr) {
88 return false;
89 }
90
91 g_c->chats = newgroup_chats;
92 return true;
93 }
94
setup_conference(Group_c * g)95 static void setup_conference(Group_c *g)
96 {
97 memset(g, 0, sizeof(Group_c));
98
99 g->maxfrozen = MAX_FROZEN_DEFAULT;
100 }
101
102 /* Create a new empty groupchat connection.
103 *
104 * return -1 on failure.
105 * return groupnumber on success.
106 */
create_group_chat(Group_Chats * g_c)107 static int32_t create_group_chat(Group_Chats *g_c)
108 {
109 for (uint16_t i = 0; i < g_c->num_chats; ++i) {
110 if (g_c->chats[i].status == GROUPCHAT_STATUS_NONE) {
111 return i;
112 }
113 }
114
115 if (realloc_conferences(g_c, g_c->num_chats + 1)) {
116 uint16_t id = g_c->num_chats;
117 ++g_c->num_chats;
118 setup_conference(&g_c->chats[id]);
119 return id;
120 }
121
122 return -1;
123 }
124
125
126 /* Wipe a groupchat.
127 *
128 * return true on success.
129 */
wipe_group_chat(Group_Chats * g_c,uint32_t groupnumber)130 static bool wipe_group_chat(Group_Chats *g_c, uint32_t groupnumber)
131 {
132 if (!is_groupnumber_valid(g_c, groupnumber)) {
133 return false;
134 }
135
136 uint16_t i;
137 crypto_memzero(&g_c->chats[groupnumber], sizeof(Group_c));
138
139 for (i = g_c->num_chats; i != 0; --i) {
140 if (g_c->chats[i - 1].status != GROUPCHAT_STATUS_NONE) {
141 break;
142 }
143 }
144
145 if (g_c->num_chats != i) {
146 g_c->num_chats = i;
147 realloc_conferences(g_c, g_c->num_chats);
148 }
149
150 return true;
151 }
152
get_group_c(const Group_Chats * g_c,uint32_t groupnumber)153 static Group_c *get_group_c(const Group_Chats *g_c, uint32_t groupnumber)
154 {
155 if (!is_groupnumber_valid(g_c, groupnumber)) {
156 return nullptr;
157 }
158
159 return &g_c->chats[groupnumber];
160 }
161
162 /*
163 * check if peer with real_pk is in peer array.
164 *
165 * return peer index if peer is in group.
166 * return -1 if peer is not in group.
167 *
168 * TODO(irungentoo): make this more efficient.
169 */
170
peer_in_group(const Group_c * g,const uint8_t * real_pk)171 static int peer_in_group(const Group_c *g, const uint8_t *real_pk)
172 {
173 for (uint32_t i = 0; i < g->numpeers; ++i) {
174 if (id_equal(g->group[i].real_pk, real_pk)) {
175 return i;
176 }
177 }
178
179 return -1;
180 }
181
frozen_in_group(const Group_c * g,const uint8_t * real_pk)182 static int frozen_in_group(const Group_c *g, const uint8_t *real_pk)
183 {
184 for (uint32_t i = 0; i < g->numfrozen; ++i) {
185 if (id_equal(g->frozen[i].real_pk, real_pk)) {
186 return i;
187 }
188 }
189
190 return -1;
191 }
192
193 /*
194 * check if group with the given type and id is in group array.
195 *
196 * return group number if peer is in list.
197 * return -1 if group is not in list.
198 *
199 * TODO(irungentoo): make this more efficient and maybe use constant time comparisons?
200 */
get_group_num(const Group_Chats * g_c,const uint8_t type,const uint8_t * id)201 static int32_t get_group_num(const Group_Chats *g_c, const uint8_t type, const uint8_t *id)
202 {
203 for (uint16_t i = 0; i < g_c->num_chats; ++i) {
204 if (g_c->chats[i].type == type && crypto_memcmp(g_c->chats[i].id, id, GROUP_ID_LENGTH) == 0) {
205 return i;
206 }
207 }
208
209 return -1;
210 }
211
conference_by_id(const Group_Chats * g_c,const uint8_t * id)212 int32_t conference_by_id(const Group_Chats *g_c, const uint8_t *id)
213 {
214 for (uint16_t i = 0; i < g_c->num_chats; ++i) {
215 if (crypto_memcmp(g_c->chats[i].id, id, GROUP_ID_LENGTH) == 0) {
216 return i;
217 }
218 }
219
220 return -1;
221 }
222
223 /*
224 * check if peer with peer_number is in peer array.
225 *
226 * return peer index if peer is in chat.
227 * return -1 if peer is not in chat.
228 *
229 * TODO(irungentoo): make this more efficient.
230 */
get_peer_index(const Group_c * g,uint16_t peer_number)231 static int get_peer_index(const Group_c *g, uint16_t peer_number)
232 {
233 for (uint32_t i = 0; i < g->numpeers; ++i) {
234 if (g->group[i].peer_number == peer_number) {
235 return i;
236 }
237 }
238
239 return -1;
240 }
241
242
calculate_comp_value(const uint8_t * pk1,const uint8_t * pk2)243 static uint64_t calculate_comp_value(const uint8_t *pk1, const uint8_t *pk2)
244 {
245 uint64_t cmp1 = 0;
246 uint64_t cmp2 = 0;
247
248 for (size_t i = 0; i < sizeof(uint64_t); ++i) {
249 cmp1 = (cmp1 << 8) + (uint64_t)pk1[i];
250 cmp2 = (cmp2 << 8) + (uint64_t)pk2[i];
251 }
252
253 return cmp1 - cmp2;
254 }
255
256 typedef enum Groupchat_Closest_Change {
257 GROUPCHAT_CLOSEST_CHANGE_NONE,
258 GROUPCHAT_CLOSEST_CHANGE_ADDED,
259 GROUPCHAT_CLOSEST_CHANGE_REMOVED,
260 } Groupchat_Closest_Change;
261
add_to_closest(Group_c * g,const uint8_t * real_pk,const uint8_t * temp_pk)262 static bool add_to_closest(Group_c *g, const uint8_t *real_pk, const uint8_t *temp_pk)
263 {
264 if (public_key_cmp(g->real_pk, real_pk) == 0) {
265 return false;
266 }
267
268 unsigned int index = DESIRED_CLOSEST;
269
270 for (unsigned int i = 0; i < DESIRED_CLOSEST; ++i) {
271 if (g->closest_peers[i].entry && public_key_cmp(real_pk, g->closest_peers[i].real_pk) == 0) {
272 return true;
273 }
274 }
275
276 for (unsigned int i = 0; i < DESIRED_CLOSEST; ++i) {
277 if (g->closest_peers[i].entry == 0) {
278 index = i;
279 break;
280 }
281 }
282
283 if (index == DESIRED_CLOSEST) {
284 uint64_t comp_val = calculate_comp_value(g->real_pk, real_pk);
285 uint64_t comp_d = 0;
286
287 for (unsigned int i = 0; i < (DESIRED_CLOSEST / 2); ++i) {
288 uint64_t comp = calculate_comp_value(g->real_pk, g->closest_peers[i].real_pk);
289
290 if (comp > comp_val && comp > comp_d) {
291 index = i;
292 comp_d = comp;
293 }
294 }
295
296 comp_val = calculate_comp_value(real_pk, g->real_pk);
297
298 for (unsigned int i = (DESIRED_CLOSEST / 2); i < DESIRED_CLOSEST; ++i) {
299 uint64_t comp = calculate_comp_value(g->closest_peers[i].real_pk, g->real_pk);
300
301 if (comp > comp_val && comp > comp_d) {
302 index = i;
303 comp_d = comp;
304 }
305 }
306 }
307
308 if (index == DESIRED_CLOSEST) {
309 return false;
310 }
311
312 uint8_t old_real_pk[CRYPTO_PUBLIC_KEY_SIZE];
313 uint8_t old_temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
314 uint8_t old = 0;
315
316 if (g->closest_peers[index].entry) {
317 memcpy(old_real_pk, g->closest_peers[index].real_pk, CRYPTO_PUBLIC_KEY_SIZE);
318 memcpy(old_temp_pk, g->closest_peers[index].temp_pk, CRYPTO_PUBLIC_KEY_SIZE);
319 old = 1;
320 }
321
322 g->closest_peers[index].entry = 1;
323 memcpy(g->closest_peers[index].real_pk, real_pk, CRYPTO_PUBLIC_KEY_SIZE);
324 memcpy(g->closest_peers[index].temp_pk, temp_pk, CRYPTO_PUBLIC_KEY_SIZE);
325
326 if (old) {
327 add_to_closest(g, old_real_pk, old_temp_pk);
328 }
329
330 if (!g->changed) {
331 g->changed = GROUPCHAT_CLOSEST_CHANGE_ADDED;
332 }
333
334 return true;
335 }
336
pk_in_closest_peers(const Group_c * g,uint8_t * real_pk)337 static bool pk_in_closest_peers(const Group_c *g, uint8_t *real_pk)
338 {
339 for (unsigned int i = 0; i < DESIRED_CLOSEST; ++i) {
340 if (!g->closest_peers[i].entry) {
341 continue;
342 }
343
344 if (public_key_cmp(g->closest_peers[i].real_pk, real_pk) == 0) {
345 return true;
346 }
347 }
348
349 return false;
350 }
351
352 static void remove_connection_reason(Group_Chats *g_c, Group_c *g, uint16_t i, uint8_t reason);
353
purge_closest(Group_Chats * g_c,uint32_t groupnumber)354 static void purge_closest(Group_Chats *g_c, uint32_t groupnumber)
355 {
356 Group_c *g = get_group_c(g_c, groupnumber);
357
358 if (!g) {
359 return;
360 }
361
362 for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
363 if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) {
364 continue;
365 }
366
367 if (!(g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST)) {
368 continue;
369 }
370
371 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
372 get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->connections[i].number);
373
374 if (!pk_in_closest_peers(g, real_pk)) {
375 remove_connection_reason(g_c, g, i, GROUPCHAT_CONNECTION_REASON_CLOSEST);
376 }
377 }
378 }
379
380 static int send_packet_online(Friend_Connections *fr_c, int friendcon_id, uint16_t group_num, uint8_t type,
381 const uint8_t *id);
382
383 static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, Group_c *g, uint8_t reason,
384 uint8_t lock);
385
add_closest_connections(Group_Chats * g_c,uint32_t groupnumber,void * userdata)386 static void add_closest_connections(Group_Chats *g_c, uint32_t groupnumber, void *userdata)
387 {
388 Group_c *g = get_group_c(g_c, groupnumber);
389
390 if (!g) {
391 return;
392 }
393
394 for (uint32_t i = 0; i < DESIRED_CLOSEST; ++i) {
395 if (!g->closest_peers[i].entry) {
396 continue;
397 }
398
399 int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, g->closest_peers[i].real_pk);
400
401 uint8_t fresh = 0;
402
403 if (friendcon_id == -1) {
404 friendcon_id = new_friend_connection(g_c->fr_c, g->closest_peers[i].real_pk);
405 fresh = 1;
406
407 if (friendcon_id == -1) {
408 continue;
409 }
410
411 set_dht_temp_pk(g_c->fr_c, friendcon_id, g->closest_peers[i].temp_pk, userdata);
412 }
413
414 const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g,
415 GROUPCHAT_CONNECTION_REASON_CLOSEST, !fresh);
416
417 if (connection_index == -1) {
418 if (fresh) {
419 kill_friend_connection(g_c->fr_c, friendcon_id);
420 }
421
422 continue;
423 }
424
425 if (friend_con_connected(g_c->fr_c, friendcon_id) == FRIENDCONN_STATUS_CONNECTED
426 && g->connections[connection_index].type == GROUPCHAT_CONNECTION_CONNECTING) {
427 send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->type, g->id);
428 }
429 }
430 }
431
connect_to_closest(Group_Chats * g_c,uint32_t groupnumber,void * userdata)432 static bool connect_to_closest(Group_Chats *g_c, uint32_t groupnumber, void *userdata)
433 {
434 Group_c *g = get_group_c(g_c, groupnumber);
435
436 if (!g) {
437 return false;
438 }
439
440 if (!g->changed) {
441 return true;
442 }
443
444 if (g->changed == GROUPCHAT_CLOSEST_CHANGE_REMOVED) {
445 for (uint32_t i = 0; i < g->numpeers; ++i) {
446 add_to_closest(g, g->group[i].real_pk, g->group[i].temp_pk);
447 }
448 }
449
450 purge_closest(g_c, groupnumber);
451
452 add_closest_connections(g_c, groupnumber, userdata);
453
454 g->changed = GROUPCHAT_CLOSEST_CHANGE_NONE;
455
456 return true;
457 }
458
get_frozen_index(const Group_c * g,uint16_t peer_number)459 static int get_frozen_index(const Group_c *g, uint16_t peer_number)
460 {
461 for (uint32_t i = 0; i < g->numfrozen; ++i) {
462 if (g->frozen[i].peer_number == peer_number) {
463 return i;
464 }
465 }
466
467 return -1;
468 }
469
delete_frozen(Group_c * g,uint32_t frozen_index)470 static bool delete_frozen(Group_c *g, uint32_t frozen_index)
471 {
472 if (frozen_index >= g->numfrozen) {
473 return false;
474 }
475
476 --g->numfrozen;
477
478 if (g->numfrozen == 0) {
479 free(g->frozen);
480 g->frozen = nullptr;
481 } else {
482 if (g->numfrozen != frozen_index) {
483 g->frozen[frozen_index] = g->frozen[g->numfrozen];
484 }
485
486 Group_Peer *const frozen_temp = (Group_Peer *)realloc(g->frozen, sizeof(Group_Peer) * (g->numfrozen));
487
488 if (frozen_temp == nullptr) {
489 return false;
490 }
491
492 g->frozen = frozen_temp;
493 }
494
495 return true;
496 }
497
498 /* Update last_active timestamp on peer, and thaw the peer if it is frozen.
499 *
500 * return peer index if peer is in the conference.
501 * return -1 otherwise, and on error.
502 */
note_peer_active(Group_Chats * g_c,uint32_t groupnumber,uint16_t peer_number,void * userdata)503 static int note_peer_active(Group_Chats *g_c, uint32_t groupnumber, uint16_t peer_number, void *userdata)
504 {
505 Group_c *g = get_group_c(g_c, groupnumber);
506
507 if (!g) {
508 return -1;
509 }
510
511 const int peer_index = get_peer_index(g, peer_number);
512
513 if (peer_index != -1) {
514 g->group[peer_index].last_active = mono_time_get(g_c->mono_time);
515 return peer_index;
516 }
517
518 const int frozen_index = get_frozen_index(g, peer_number);
519
520 if (frozen_index == -1) {
521 return -1;
522 }
523
524 /* Now thaw the peer */
525
526 Group_Peer *temp = (Group_Peer *)realloc(g->group, sizeof(Group_Peer) * (g->numpeers + 1));
527
528 if (temp == nullptr) {
529 return -1;
530 }
531
532 const uint32_t thawed_index = g->numpeers;
533
534 g->group = temp;
535 g->group[thawed_index] = g->frozen[frozen_index];
536 g->group[thawed_index].temp_pk_updated = false;
537 g->group[thawed_index].last_active = mono_time_get(g_c->mono_time);
538
539 add_to_closest(g, g->group[thawed_index].real_pk, g->group[thawed_index].temp_pk);
540
541 ++g->numpeers;
542
543 delete_frozen(g, frozen_index);
544
545 if (g_c->peer_list_changed_callback) {
546 g_c->peer_list_changed_callback(g_c->m, groupnumber, userdata);
547 }
548
549 if (g->peer_on_join) {
550 g->peer_on_join(g->object, groupnumber, thawed_index);
551 }
552
553 g->need_send_name = true;
554
555 return thawed_index;
556 }
557
558 static bool delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata);
559
delete_any_peer_with_pk(Group_Chats * g_c,uint32_t groupnumber,const uint8_t * real_pk,void * userdata)560 static void delete_any_peer_with_pk(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk, void *userdata)
561 {
562 Group_c *g = get_group_c(g_c, groupnumber);
563
564 if (!g) {
565 return;
566 }
567
568 const int peer_index = peer_in_group(g, real_pk);
569
570 if (peer_index >= 0) {
571 delpeer(g_c, groupnumber, peer_index, userdata);
572 }
573
574 const int frozen_index = frozen_in_group(g, real_pk);
575
576 if (frozen_index >= 0) {
577 delete_frozen(g, frozen_index);
578 }
579 }
580
581 /* Add a peer to the group chat, or update an existing peer.
582 *
583 * fresh indicates whether we should consider this information on the peer to
584 * be current, and so should update temp_pk and consider the peer active.
585 *
586 * do_gc_callback indicates whether we want to trigger callbacks set by the client
587 * via the public API. This should be set to false if this function is called
588 * from outside of the tox_iterate() loop.
589 *
590 * return peer_index if success or peer already in chat.
591 * return -1 if error.
592 */
addpeer(Group_Chats * g_c,uint32_t groupnumber,const uint8_t * real_pk,const uint8_t * temp_pk,uint16_t peer_number,void * userdata,bool fresh,bool do_gc_callback)593 static int addpeer(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk, const uint8_t *temp_pk,
594 uint16_t peer_number, void *userdata, bool fresh, bool do_gc_callback)
595 {
596 Group_c *g = get_group_c(g_c, groupnumber);
597
598 if (!g) {
599 return -1;
600 }
601
602 const int peer_index = fresh ?
603 note_peer_active(g_c, groupnumber, peer_number, userdata) :
604 get_peer_index(g, peer_number);
605
606 if (peer_index != -1) {
607 if (!id_equal(g->group[peer_index].real_pk, real_pk)) {
608 return -1;
609 }
610
611 if (fresh || !g->group[peer_index].temp_pk_updated) {
612 id_copy(g->group[peer_index].temp_pk, temp_pk);
613 g->group[peer_index].temp_pk_updated = true;
614 }
615
616 return peer_index;
617 }
618
619 if (!fresh) {
620 const int frozen_index = get_frozen_index(g, peer_number);
621
622 if (frozen_index != -1) {
623 if (!id_equal(g->frozen[frozen_index].real_pk, real_pk)) {
624 return -1;
625 }
626
627 id_copy(g->frozen[frozen_index].temp_pk, temp_pk);
628
629 return -1;
630 }
631 }
632
633 delete_any_peer_with_pk(g_c, groupnumber, real_pk, userdata);
634
635 Group_Peer *temp = (Group_Peer *)realloc(g->group, sizeof(Group_Peer) * (g->numpeers + 1));
636
637 if (temp == nullptr) {
638 return -1;
639 }
640
641 memset(&temp[g->numpeers], 0, sizeof(Group_Peer));
642 g->group = temp;
643
644 const uint32_t new_index = g->numpeers;
645
646 id_copy(g->group[new_index].real_pk, real_pk);
647 id_copy(g->group[new_index].temp_pk, temp_pk);
648 g->group[new_index].temp_pk_updated = true;
649 g->group[new_index].peer_number = peer_number;
650 g->group[new_index].last_active = mono_time_get(g_c->mono_time);
651 g->group[new_index].is_friend = (getfriend_id(g_c->m, real_pk) != -1);
652 ++g->numpeers;
653
654 add_to_closest(g, real_pk, temp_pk);
655
656 if (do_gc_callback && g_c->peer_list_changed_callback) {
657 g_c->peer_list_changed_callback(g_c->m, groupnumber, userdata);
658 }
659
660 if (g->peer_on_join) {
661 g->peer_on_join(g->object, groupnumber, new_index);
662 }
663
664 return new_index;
665 }
666
remove_connection(Group_Chats * g_c,Group_c * g,uint16_t i)667 static void remove_connection(Group_Chats *g_c, Group_c *g, uint16_t i)
668 {
669 if (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER) {
670 --g->num_introducer_connections;
671 }
672
673 kill_friend_connection(g_c->fr_c, g->connections[i].number);
674 g->connections[i].type = GROUPCHAT_CONNECTION_NONE;
675 }
676
remove_from_closest(Group_c * g,int peer_index)677 static void remove_from_closest(Group_c *g, int peer_index)
678 {
679 for (uint32_t i = 0; i < DESIRED_CLOSEST; ++i) {
680 if (g->closest_peers[i].entry
681 && id_equal(g->closest_peers[i].real_pk, g->group[peer_index].real_pk)) {
682 g->closest_peers[i].entry = 0;
683 g->changed = GROUPCHAT_CLOSEST_CHANGE_REMOVED;
684 break;
685 }
686 }
687 }
688
689 /*
690 * Delete a peer from the group chat.
691 *
692 * return true on success
693 */
delpeer(Group_Chats * g_c,uint32_t groupnumber,int peer_index,void * userdata)694 static bool delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata)
695 {
696 Group_c *g = get_group_c(g_c, groupnumber);
697
698 if (!g) {
699 return false;
700 }
701
702 remove_from_closest(g, peer_index);
703
704 const int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, g->group[peer_index].real_pk);
705
706 if (friendcon_id != -1) {
707 for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
708 if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) {
709 continue;
710 }
711
712 if (g->connections[i].number == (unsigned int)friendcon_id) {
713 remove_connection(g_c, g, i);
714 break;
715 }
716 }
717 }
718
719 --g->numpeers;
720
721 void *peer_object = g->group[peer_index].object;
722
723 if (g->numpeers == 0) {
724 free(g->group);
725 g->group = nullptr;
726 } else {
727 if (g->numpeers != (uint32_t)peer_index) {
728 g->group[peer_index] = g->group[g->numpeers];
729 }
730
731 Group_Peer *temp = (Group_Peer *)realloc(g->group, sizeof(Group_Peer) * (g->numpeers));
732
733 if (temp == nullptr) {
734 return false;
735 }
736
737 g->group = temp;
738 }
739
740 if (g_c->peer_list_changed_callback) {
741 g_c->peer_list_changed_callback(g_c->m, groupnumber, userdata);
742 }
743
744 if (g->peer_on_leave) {
745 g->peer_on_leave(g->object, groupnumber, peer_object);
746 }
747
748 return true;
749 }
750
cmp_u64(uint64_t a,uint64_t b)751 static int cmp_u64(uint64_t a, uint64_t b)
752 {
753 return (a > b) - (a < b);
754 }
755
756 /* Order peers with friends first and with more recently active earlier */
cmp_frozen(const void * a,const void * b)757 static int cmp_frozen(const void *a, const void *b)
758 {
759 const Group_Peer *pa = (const Group_Peer *) a;
760 const Group_Peer *pb = (const Group_Peer *) b;
761
762 if (pa->is_friend ^ pb->is_friend) {
763 return pa->is_friend ? -1 : 1;
764 }
765
766 return cmp_u64(pb->last_active, pa->last_active);
767 }
768
769 /* Delete frozen peers as necessary to ensure at most g->maxfrozen remain.
770 *
771 * return true if any frozen peers are removed.
772 */
delete_old_frozen(Group_c * g)773 static bool delete_old_frozen(Group_c *g)
774 {
775 if (g->numfrozen <= g->maxfrozen) {
776 return false;
777 }
778
779 if (g->maxfrozen == 0) {
780 free(g->frozen);
781 g->frozen = nullptr;
782 g->numfrozen = 0;
783 return true;
784 }
785
786 qsort(g->frozen, g->numfrozen, sizeof(Group_Peer), cmp_frozen);
787
788 Group_Peer *temp = (Group_Peer *)realloc(g->frozen, sizeof(Group_Peer) * g->maxfrozen);
789
790 if (temp == nullptr) {
791 return false;
792 }
793
794 g->frozen = temp;
795
796 g->numfrozen = g->maxfrozen;
797
798 return true;
799 }
800
801 static bool try_send_rejoin(Group_Chats *g_c, Group_c *g, const uint8_t *real_pk);
802
freeze_peer(Group_Chats * g_c,uint32_t groupnumber,int peer_index,void * userdata)803 static bool freeze_peer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata)
804 {
805 Group_c *g = get_group_c(g_c, groupnumber);
806
807 if (!g) {
808 return false;
809 }
810
811 Group_Peer *temp = (Group_Peer *)realloc(g->frozen, sizeof(Group_Peer) * (g->numfrozen + 1));
812
813 if (temp == nullptr) {
814 return false;
815 }
816
817 g->frozen = temp;
818 g->frozen[g->numfrozen] = g->group[peer_index];
819 g->frozen[g->numfrozen].object = nullptr;
820
821 if (!delpeer(g_c, groupnumber, peer_index, userdata)) {
822 return false;
823 }
824
825 try_send_rejoin(g_c, g, g->frozen[g->numfrozen].real_pk);
826
827 ++g->numfrozen;
828
829 delete_old_frozen(g);
830
831 return true;
832 }
833
834
835 /* Set the nick for a peer.
836 *
837 * do_gc_callback indicates whether we want to trigger callbacks set by the client
838 * via the public API. This should be set to false if this function is called
839 * from outside of the tox_iterate() loop.
840 *
841 * return true on success.
842 */
setnick(Group_Chats * g_c,uint32_t groupnumber,int peer_index,const uint8_t * nick,uint16_t nick_len,void * userdata,bool do_gc_callback)843 static bool setnick(Group_Chats *g_c, uint32_t groupnumber, int peer_index, const uint8_t *nick, uint16_t nick_len,
844 void *userdata, bool do_gc_callback)
845 {
846 if (nick_len > MAX_NAME_LENGTH) {
847 return false;
848 }
849
850 Group_c *g = get_group_c(g_c, groupnumber);
851
852 if (!g) {
853 return false;
854 }
855
856 g->group[peer_index].nick_updated = true;
857
858 if (g->group[peer_index].nick_len == nick_len
859 && (nick_len == 0 || !memcmp(g->group[peer_index].nick, nick, nick_len))) {
860 /* same name as already stored */
861 return true;
862 }
863
864 if (nick_len) {
865 memcpy(g->group[peer_index].nick, nick, nick_len);
866 }
867
868 g->group[peer_index].nick_len = nick_len;
869
870 if (do_gc_callback && g_c->peer_name_callback) {
871 g_c->peer_name_callback(g_c->m, groupnumber, peer_index, nick, nick_len, userdata);
872 }
873
874 return true;
875 }
876
877 /* Set the title for a group.
878 *
879 * return true on success.
880 */
settitle(Group_Chats * g_c,uint32_t groupnumber,int peer_index,const uint8_t * title,uint8_t title_len,void * userdata)881 static bool settitle(Group_Chats *g_c, uint32_t groupnumber, int peer_index, const uint8_t *title, uint8_t title_len,
882 void *userdata)
883 {
884 if (title_len > MAX_NAME_LENGTH || title_len == 0) {
885 return false;
886 }
887
888 Group_c *g = get_group_c(g_c, groupnumber);
889
890 if (!g) {
891 return false;
892 }
893
894 if (g->title_len == title_len && !memcmp(g->title, title, title_len)) {
895 /* same title as already set */
896 return true;
897 }
898
899 memcpy(g->title, title, title_len);
900 g->title_len = title_len;
901
902 g->title_fresh = true;
903
904 if (g_c->title_callback) {
905 g_c->title_callback(g_c->m, groupnumber, peer_index, title, title_len, userdata);
906 }
907
908 return true;
909 }
910
911 /* Check if the group has no online connection, and freeze all peers if so */
check_disconnected(Group_Chats * g_c,uint32_t groupnumber,void * userdata)912 static void check_disconnected(Group_Chats *g_c, uint32_t groupnumber, void *userdata)
913 {
914 Group_c *g = get_group_c(g_c, groupnumber);
915
916 if (!g) {
917 return;
918 }
919
920 for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
921 if (g->connections[i].type == GROUPCHAT_CONNECTION_ONLINE) {
922 return;
923 }
924 }
925
926 for (uint32_t i = 0; i < g->numpeers; ++i) {
927 while (i < g->numpeers && !id_equal(g->group[i].real_pk, g->real_pk)) {
928 freeze_peer(g_c, groupnumber, i, userdata);
929 }
930 }
931 }
932
set_conns_type_connections(Group_Chats * g_c,uint32_t groupnumber,int friendcon_id,uint8_t type,void * userdata)933 static void set_conns_type_connections(Group_Chats *g_c, uint32_t groupnumber, int friendcon_id, uint8_t type,
934 void *userdata)
935 {
936 Group_c *g = get_group_c(g_c, groupnumber);
937
938 if (!g) {
939 return;
940 }
941
942 for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
943 if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) {
944 continue;
945 }
946
947 if (g->connections[i].number != (unsigned int)friendcon_id) {
948 continue;
949 }
950
951 if (type == GROUPCHAT_CONNECTION_ONLINE) {
952 send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->type, g->id);
953 } else {
954 g->connections[i].type = type;
955 check_disconnected(g_c, groupnumber, userdata);
956 }
957 }
958 }
959
960 /* Set the type for all connections with friendcon_id */
set_conns_status_groups(Group_Chats * g_c,int friendcon_id,uint8_t type,void * userdata)961 static void set_conns_status_groups(Group_Chats *g_c, int friendcon_id, uint8_t type, void *userdata)
962 {
963 for (uint16_t i = 0; i < g_c->num_chats; ++i) {
964 set_conns_type_connections(g_c, i, friendcon_id, type, userdata);
965 }
966 }
967
rejoin_frozen_friend(Group_Chats * g_c,int friendcon_id)968 static void rejoin_frozen_friend(Group_Chats *g_c, int friendcon_id)
969 {
970 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
971 get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, friendcon_id);
972
973 for (uint16_t i = 0; i < g_c->num_chats; ++i) {
974 Group_c *g = get_group_c(g_c, i);
975
976 if (!g) {
977 continue;
978 }
979
980 for (uint32_t j = 0; j < g->numfrozen; ++j) {
981 if (id_equal(g->frozen[j].real_pk, real_pk)) {
982 try_send_rejoin(g_c, g, real_pk);
983 break;
984 }
985 }
986 }
987 }
988
g_handle_any_status(void * object,int friendcon_id,uint8_t status,void * userdata)989 static int g_handle_any_status(void *object, int friendcon_id, uint8_t status, void *userdata)
990 {
991 Group_Chats *g_c = (Group_Chats *)object;
992
993 if (status) {
994 rejoin_frozen_friend(g_c, friendcon_id);
995 }
996
997 return 0;
998 }
999
g_handle_status(void * object,int friendcon_id,uint8_t status,void * userdata)1000 static int g_handle_status(void *object, int friendcon_id, uint8_t status, void *userdata)
1001 {
1002 Group_Chats *g_c = (Group_Chats *)object;
1003
1004 if (status) { /* Went online */
1005 set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CONNECTION_ONLINE, userdata);
1006 } else { /* Went offline */
1007 set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CONNECTION_CONNECTING, userdata);
1008 // TODO(irungentoo): remove timedout connections?
1009 }
1010
1011 return 0;
1012 }
1013
1014 static int g_handle_packet(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata);
1015 static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata);
1016
1017 /* Add friend to group chat.
1018 *
1019 * return connections index on success
1020 * return -1 on failure.
1021 */
add_conn_to_groupchat(Group_Chats * g_c,int friendcon_id,Group_c * g,uint8_t reason,uint8_t lock)1022 static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, Group_c *g, uint8_t reason,
1023 uint8_t lock)
1024 {
1025 uint16_t empty = MAX_GROUP_CONNECTIONS;
1026 uint16_t ind = MAX_GROUP_CONNECTIONS;
1027
1028 for (uint16_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
1029 if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) {
1030 empty = i;
1031 continue;
1032 }
1033
1034 if (g->connections[i].number == (uint32_t)friendcon_id) {
1035 ind = i; /* Already in list. */
1036 break;
1037 }
1038 }
1039
1040 if (ind == MAX_GROUP_CONNECTIONS) {
1041 if (empty == MAX_GROUP_CONNECTIONS) {
1042 return -1;
1043 }
1044
1045 if (lock) {
1046 friend_connection_lock(g_c->fr_c, friendcon_id);
1047 }
1048
1049 g->connections[empty].type = GROUPCHAT_CONNECTION_CONNECTING;
1050 g->connections[empty].number = friendcon_id;
1051 g->connections[empty].reasons = 0;
1052 // TODO(irungentoo):
1053 friend_connection_callbacks(g_c->m->fr_c, friendcon_id, GROUPCHAT_CALLBACK_INDEX, &g_handle_status, &g_handle_packet,
1054 &handle_lossy, g_c, friendcon_id);
1055 ind = empty;
1056 }
1057
1058 if (!(g->connections[ind].reasons & reason)) {
1059 g->connections[ind].reasons |= reason;
1060
1061 if (reason == GROUPCHAT_CONNECTION_REASON_INTRODUCER) {
1062 ++g->num_introducer_connections;
1063 }
1064 }
1065
1066 return ind;
1067 }
1068
1069 static unsigned int send_peer_introduced(Group_Chats *g_c, int friendcon_id, uint16_t group_num);
1070
1071 /* Removes reason for keeping connection.
1072 *
1073 * Kills connection if this was the last reason.
1074 */
remove_connection_reason(Group_Chats * g_c,Group_c * g,uint16_t i,uint8_t reason)1075 static void remove_connection_reason(Group_Chats *g_c, Group_c *g, uint16_t i, uint8_t reason)
1076 {
1077 if (!(g->connections[i].reasons & reason)) {
1078 return;
1079 }
1080
1081 g->connections[i].reasons &= ~reason;
1082
1083 if (reason == GROUPCHAT_CONNECTION_REASON_INTRODUCER) {
1084 --g->num_introducer_connections;
1085
1086 if (g->connections[i].type == GROUPCHAT_CONNECTION_ONLINE) {
1087 send_peer_introduced(g_c, g->connections[i].number, g->connections[i].group_number);
1088 }
1089 }
1090
1091 if (g->connections[i].reasons == 0) {
1092 kill_friend_connection(g_c->fr_c, g->connections[i].number);
1093 g->connections[i].type = GROUPCHAT_CONNECTION_NONE;
1094 }
1095 }
1096
1097 /* Creates a new groupchat and puts it in the chats array.
1098 *
1099 * type is one of `GROUPCHAT_TYPE_*`
1100 *
1101 * return group number on success.
1102 * return -1 on failure.
1103 */
add_groupchat(Group_Chats * g_c,uint8_t type)1104 int add_groupchat(Group_Chats *g_c, uint8_t type)
1105 {
1106 const int32_t groupnumber = create_group_chat(g_c);
1107
1108 if (groupnumber == -1) {
1109 return -1;
1110 }
1111
1112 Group_c *g = &g_c->chats[groupnumber];
1113
1114 g->status = GROUPCHAT_STATUS_CONNECTED;
1115 g->type = type;
1116 new_symmetric_key(g->id);
1117 g->peer_number = 0; /* Founder is peer 0. */
1118 memcpy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE);
1119 const int peer_index = addpeer(g_c, groupnumber, g->real_pk, dht_get_self_public_key(g_c->m->dht), 0, nullptr, true,
1120 false);
1121
1122 if (peer_index == -1) {
1123 return -1;
1124 }
1125
1126 setnick(g_c, groupnumber, peer_index, g_c->m->name, g_c->m->name_length, nullptr, false);
1127
1128 return groupnumber;
1129 }
1130
1131 static bool group_leave(const Group_Chats *g_c, uint32_t groupnumber, bool permanent);
1132
1133 /* Delete a groupchat from the chats array, informing the group first as
1134 * appropriate.
1135 *
1136 * return 0 on success.
1137 * return -1 if groupnumber is invalid.
1138 */
del_groupchat(Group_Chats * g_c,uint32_t groupnumber,bool leave_permanently)1139 int del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently)
1140 {
1141 Group_c *g = get_group_c(g_c, groupnumber);
1142
1143 if (!g) {
1144 return -1;
1145 }
1146
1147 group_leave(g_c, groupnumber, leave_permanently);
1148
1149 for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
1150 if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) {
1151 continue;
1152 }
1153
1154 g->connections[i].type = GROUPCHAT_CONNECTION_NONE;
1155 kill_friend_connection(g_c->fr_c, g->connections[i].number);
1156 }
1157
1158 for (uint32_t i = 0; i < g->numpeers; ++i) {
1159 if (g->peer_on_leave) {
1160 g->peer_on_leave(g->object, groupnumber, g->group[i].object);
1161 }
1162 }
1163
1164 free(g->group);
1165 free(g->frozen);
1166
1167 if (g->group_on_delete) {
1168 g->group_on_delete(g->object, groupnumber);
1169 }
1170
1171 return wipe_group_chat(g_c, groupnumber);
1172 }
1173
peer_in_list(const Group_c * g,uint32_t peernumber,bool frozen)1174 static const Group_Peer *peer_in_list(const Group_c *g, uint32_t peernumber, bool frozen)
1175 {
1176 const Group_Peer *list = frozen ? g->frozen : g->group;
1177 const uint32_t num = frozen ? g->numfrozen : g->numpeers;
1178
1179 if (peernumber >= num) {
1180 return nullptr;
1181 }
1182
1183 return &list[peernumber];
1184 }
1185
1186
1187 /* Copy the public key of (frozen, if frozen is true) peernumber who is in
1188 * groupnumber to pk. pk must be CRYPTO_PUBLIC_KEY_SIZE long.
1189 *
1190 * return 0 on success
1191 * return -1 if groupnumber is invalid.
1192 * return -2 if peernumber is invalid.
1193 */
group_peer_pubkey(const Group_Chats * g_c,uint32_t groupnumber,uint32_t peernumber,uint8_t * pk,bool frozen)1194 int group_peer_pubkey(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint8_t *pk, bool frozen)
1195 {
1196 const Group_c *g = get_group_c(g_c, groupnumber);
1197
1198 if (!g) {
1199 return -1;
1200 }
1201
1202 const Group_Peer *peer = peer_in_list(g, peernumber, frozen);
1203
1204 if (peer == nullptr) {
1205 return -2;
1206 }
1207
1208 memcpy(pk, peer->real_pk, CRYPTO_PUBLIC_KEY_SIZE);
1209 return 0;
1210 }
1211
1212 /*
1213 * Return the size of (frozen, if frozen is true) peernumber's name.
1214 *
1215 * return -1 if groupnumber is invalid.
1216 * return -2 if peernumber is invalid.
1217 */
group_peername_size(const Group_Chats * g_c,uint32_t groupnumber,uint32_t peernumber,bool frozen)1218 int group_peername_size(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, bool frozen)
1219 {
1220 const Group_c *g = get_group_c(g_c, groupnumber);
1221
1222 if (!g) {
1223 return -1;
1224 }
1225
1226 const Group_Peer *peer = peer_in_list(g, peernumber, frozen);
1227
1228 if (peer == nullptr) {
1229 return -2;
1230 }
1231
1232 return peer->nick_len;
1233 }
1234
1235 /* Copy the name of (frozen, if frozen is true) peernumber who is in
1236 * groupnumber to name. name must be at least MAX_NAME_LENGTH long.
1237 *
1238 * return length of name if success
1239 * return -1 if groupnumber is invalid.
1240 * return -2 if peernumber is invalid.
1241 */
group_peername(const Group_Chats * g_c,uint32_t groupnumber,uint32_t peernumber,uint8_t * name,bool frozen)1242 int group_peername(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint8_t *name, bool frozen)
1243 {
1244 const Group_c *g = get_group_c(g_c, groupnumber);
1245
1246 if (!g) {
1247 return -1;
1248 }
1249
1250 const Group_Peer *peer = peer_in_list(g, peernumber, frozen);
1251
1252 if (peer == nullptr) {
1253 return -2;
1254 }
1255
1256 if (peer->nick_len > 0) {
1257 memcpy(name, peer->nick, peer->nick_len);
1258 }
1259
1260 return peer->nick_len;
1261 }
1262
1263 /* Copy last active timestamp of frozennumber who is in groupnumber to
1264 * last_active.
1265 *
1266 * return 0 on success.
1267 * return -1 if groupnumber is invalid.
1268 * return -2 if frozennumber is invalid.
1269 */
group_frozen_last_active(const Group_Chats * g_c,uint32_t groupnumber,uint32_t peernumber,uint64_t * last_active)1270 int group_frozen_last_active(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber,
1271 uint64_t *last_active)
1272 {
1273 const Group_c *g = get_group_c(g_c, groupnumber);
1274
1275 if (!g) {
1276 return -1;
1277 }
1278
1279 if (peernumber >= g->numfrozen) {
1280 return -2;
1281 }
1282
1283 *last_active = g->frozen[peernumber].last_active;
1284 return 0;
1285 }
1286
1287 /* Set maximum number of frozen peers.
1288 *
1289 * return 0 on success.
1290 * return -1 if groupnumber is invalid.
1291 */
group_set_max_frozen(const Group_Chats * g_c,uint32_t groupnumber,uint32_t maxfrozen)1292 int group_set_max_frozen(const Group_Chats *g_c, uint32_t groupnumber, uint32_t maxfrozen)
1293 {
1294 Group_c *g = get_group_c(g_c, groupnumber);
1295
1296 if (!g) {
1297 return -1;
1298 }
1299
1300 g->maxfrozen = maxfrozen;
1301 delete_old_frozen(g);
1302 return 0;
1303 }
1304
1305 /* Return the number of (frozen, if frozen is true) peers in the group chat on
1306 * success.
1307 * return -1 if groupnumber is invalid.
1308 */
group_number_peers(const Group_Chats * g_c,uint32_t groupnumber,bool frozen)1309 int group_number_peers(const Group_Chats *g_c, uint32_t groupnumber, bool frozen)
1310 {
1311 const Group_c *g = get_group_c(g_c, groupnumber);
1312
1313 if (!g) {
1314 return -1;
1315 }
1316
1317 return frozen ? g->numfrozen : g->numpeers;
1318 }
1319
1320 /* return 1 if the peernumber corresponds to ours.
1321 * return 0 if the peernumber is not ours.
1322 * return -1 if groupnumber is invalid.
1323 * return -2 if peernumber is invalid.
1324 * return -3 if we are not connected to the group chat.
1325 */
group_peernumber_is_ours(const Group_Chats * g_c,uint32_t groupnumber,uint32_t peernumber)1326 int group_peernumber_is_ours(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber)
1327 {
1328 const Group_c *g = get_group_c(g_c, groupnumber);
1329
1330 if (!g) {
1331 return -1;
1332 }
1333
1334 if (peernumber >= g->numpeers) {
1335 return -2;
1336 }
1337
1338 if (g->status != GROUPCHAT_STATUS_CONNECTED) {
1339 return -3;
1340 }
1341
1342 return g->peer_number == g->group[peernumber].peer_number;
1343 }
1344
1345 /* return the type of groupchat (GROUPCHAT_TYPE_) that groupnumber is.
1346 *
1347 * return -1 on failure.
1348 * return type on success.
1349 */
group_get_type(const Group_Chats * g_c,uint32_t groupnumber)1350 int group_get_type(const Group_Chats *g_c, uint32_t groupnumber)
1351 {
1352 const Group_c *g = get_group_c(g_c, groupnumber);
1353
1354 if (!g) {
1355 return -1;
1356 }
1357
1358 return g->type;
1359 }
1360
1361 /* Copies the unique id of `group_chat[groupnumber]` into `id`.
1362 *
1363 * return false on failure.
1364 * return true on success.
1365 */
conference_get_id(const Group_Chats * g_c,uint32_t groupnumber,uint8_t * id)1366 bool conference_get_id(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *id)
1367 {
1368 const Group_c *g = get_group_c(g_c, groupnumber);
1369
1370 if (!g) {
1371 return false;
1372 }
1373
1374 if (id != nullptr) {
1375 memcpy(id, g->id, sizeof(g->id));
1376 }
1377
1378 return true;
1379 }
1380
1381 /* Send a group packet to friendcon_id.
1382 *
1383 * return 1 on success
1384 * return 0 on failure
1385 */
send_packet_group_peer(Friend_Connections * fr_c,int friendcon_id,uint8_t packet_id,uint16_t group_num,const uint8_t * data,uint16_t length)1386 static unsigned int send_packet_group_peer(Friend_Connections *fr_c, int friendcon_id, uint8_t packet_id,
1387 uint16_t group_num, const uint8_t *data, uint16_t length)
1388 {
1389 if (1 + sizeof(uint16_t) + length > MAX_CRYPTO_DATA_SIZE) {
1390 return 0;
1391 }
1392
1393 group_num = net_htons(group_num);
1394 VLA(uint8_t, packet, 1 + sizeof(uint16_t) + length);
1395 packet[0] = packet_id;
1396 memcpy(packet + 1, &group_num, sizeof(uint16_t));
1397 memcpy(packet + 1 + sizeof(uint16_t), data, length);
1398 return write_cryptpacket(friendconn_net_crypto(fr_c), friend_connection_crypt_connection_id(fr_c, friendcon_id), packet,
1399 SIZEOF_VLA(packet), 0) != -1;
1400 }
1401
1402 /* Send a group lossy packet to friendcon_id.
1403 *
1404 * return 1 on success
1405 * return 0 on failure
1406 */
send_lossy_group_peer(Friend_Connections * fr_c,int friendcon_id,uint8_t packet_id,uint16_t group_num,const uint8_t * data,uint16_t length)1407 static unsigned int send_lossy_group_peer(Friend_Connections *fr_c, int friendcon_id, uint8_t packet_id,
1408 uint16_t group_num, const uint8_t *data, uint16_t length)
1409 {
1410 if (1 + sizeof(uint16_t) + length > MAX_CRYPTO_DATA_SIZE) {
1411 return 0;
1412 }
1413
1414 group_num = net_htons(group_num);
1415 VLA(uint8_t, packet, 1 + sizeof(uint16_t) + length);
1416 packet[0] = packet_id;
1417 memcpy(packet + 1, &group_num, sizeof(uint16_t));
1418 memcpy(packet + 1 + sizeof(uint16_t), data, length);
1419 return send_lossy_cryptpacket(friendconn_net_crypto(fr_c), friend_connection_crypt_connection_id(fr_c, friendcon_id),
1420 packet, SIZEOF_VLA(packet)) != -1;
1421 }
1422
1423 /* invite friendnumber to groupnumber.
1424 *
1425 * return 0 on success.
1426 * return -1 if groupnumber is invalid.
1427 * return -2 if invite packet failed to send.
1428 * return -3 if we are not connected to the group chat.
1429 */
invite_friend(Group_Chats * g_c,uint32_t friendnumber,uint32_t groupnumber)1430 int invite_friend(Group_Chats *g_c, uint32_t friendnumber, uint32_t groupnumber)
1431 {
1432 Group_c *g = get_group_c(g_c, groupnumber);
1433
1434 if (!g) {
1435 return -1;
1436 }
1437
1438 if (g->status != GROUPCHAT_STATUS_CONNECTED) {
1439 return -3;
1440 }
1441
1442 uint8_t invite[INVITE_PACKET_SIZE];
1443 invite[0] = INVITE_ID;
1444 const uint16_t groupchat_num = net_htons((uint16_t)groupnumber);
1445 memcpy(invite + 1, &groupchat_num, sizeof(groupchat_num));
1446 invite[1 + sizeof(groupchat_num)] = g->type;
1447 memcpy(invite + 1 + sizeof(groupchat_num) + 1, g->id, GROUP_ID_LENGTH);
1448
1449 if (send_conference_invite_packet(g_c->m, friendnumber, invite, sizeof(invite))) {
1450 return 0;
1451 }
1452
1453 return -2;
1454 }
1455
1456 /* Send a rejoin packet to a peer if we have a friend connection to the peer.
1457 * return true if a packet was sent.
1458 * return false otherwise.
1459 */
try_send_rejoin(Group_Chats * g_c,Group_c * g,const uint8_t * real_pk)1460 static bool try_send_rejoin(Group_Chats *g_c, Group_c *g, const uint8_t *real_pk)
1461 {
1462 const int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, real_pk);
1463
1464 if (friendcon_id == -1) {
1465 return false;
1466 }
1467
1468 uint8_t packet[1 + 1 + GROUP_ID_LENGTH];
1469 packet[0] = PACKET_ID_REJOIN_CONFERENCE;
1470 packet[1] = g->type;
1471 memcpy(packet + 2, g->id, GROUP_ID_LENGTH);
1472
1473 if (write_cryptpacket(friendconn_net_crypto(g_c->fr_c), friend_connection_crypt_connection_id(g_c->fr_c, friendcon_id),
1474 packet, sizeof(packet), 0) == -1) {
1475 return false;
1476 }
1477
1478 add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, 1);
1479
1480 return true;
1481 }
1482
1483 static unsigned int send_peer_query(Group_Chats *g_c, int friendcon_id, uint16_t group_num);
1484
1485 static bool send_invite_response(Group_Chats *g_c, int groupnumber, uint32_t friendnumber, const uint8_t *data,
1486 uint16_t length);
1487
1488 /* Join a group (we need to have been invited first.)
1489 *
1490 * expected_type is the groupchat type we expect the chat we are joining to
1491 * have.
1492 *
1493 * return group number on success.
1494 * return -1 if data length is invalid.
1495 * return -2 if group is not the expected type.
1496 * return -3 if friendnumber is invalid.
1497 * return -4 if client is already in this group.
1498 * return -5 if group instance failed to initialize.
1499 * return -6 if join packet fails to send.
1500 */
join_groupchat(Group_Chats * g_c,uint32_t friendnumber,uint8_t expected_type,const uint8_t * data,uint16_t length)1501 int join_groupchat(Group_Chats *g_c, uint32_t friendnumber, uint8_t expected_type, const uint8_t *data, uint16_t length)
1502 {
1503 if (length != sizeof(uint16_t) + 1 + GROUP_ID_LENGTH) {
1504 return -1;
1505 }
1506
1507 if (data[sizeof(uint16_t)] != expected_type) {
1508 return -2;
1509 }
1510
1511 const int friendcon_id = getfriendcon_id(g_c->m, friendnumber);
1512
1513 if (friendcon_id == -1) {
1514 return -3;
1515 }
1516
1517 if (get_group_num(g_c, data[sizeof(uint16_t)], data + sizeof(uint16_t) + 1) != -1) {
1518 return -4;
1519 }
1520
1521 const int groupnumber = create_group_chat(g_c);
1522
1523 if (groupnumber == -1) {
1524 return -5;
1525 }
1526
1527 Group_c *g = &g_c->chats[groupnumber];
1528
1529 g->status = GROUPCHAT_STATUS_VALID;
1530 memcpy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE);
1531
1532 if (!send_invite_response(g_c, groupnumber, friendnumber, data, length)) {
1533 g->status = GROUPCHAT_STATUS_NONE;
1534 return -6;
1535 }
1536
1537 return groupnumber;
1538 }
1539
send_invite_response(Group_Chats * g_c,int groupnumber,uint32_t friendnumber,const uint8_t * data,uint16_t length)1540 static bool send_invite_response(Group_Chats *g_c, int groupnumber, uint32_t friendnumber, const uint8_t *data,
1541 uint16_t length)
1542 {
1543 Group_c *g = get_group_c(g_c, groupnumber);
1544
1545 if (g == nullptr) {
1546 return false;
1547 }
1548
1549 const bool member = (g->status == GROUPCHAT_STATUS_CONNECTED);
1550
1551 VLA(uint8_t, response, member ? INVITE_MEMBER_PACKET_SIZE : INVITE_ACCEPT_PACKET_SIZE);
1552 response[0] = member ? INVITE_MEMBER_ID : INVITE_ACCEPT_ID;
1553 net_pack_u16(response + 1, groupnumber);
1554 memcpy(response + 1 + sizeof(uint16_t), data, length);
1555
1556 if (member) {
1557 net_pack_u16(response + 1 + sizeof(uint16_t) + length, g->peer_number);
1558 }
1559
1560 if (!send_conference_invite_packet(g_c->m, friendnumber, response, SIZEOF_VLA(response))) {
1561 return false;
1562 }
1563
1564 if (!member) {
1565 g->type = data[sizeof(uint16_t)];
1566 memcpy(g->id, data + sizeof(uint16_t) + 1, GROUP_ID_LENGTH);
1567 }
1568
1569 uint16_t other_groupnum;
1570 net_unpack_u16(data, &other_groupnum);
1571
1572 const int friendcon_id = getfriendcon_id(g_c->m, friendnumber);
1573
1574 if (friendcon_id == -1) {
1575 return false;
1576 }
1577
1578 const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, 1);
1579
1580 if (member) {
1581 add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCING, 0);
1582 }
1583
1584 if (connection_index != -1) {
1585 g->connections[connection_index].group_number = other_groupnum;
1586 g->connections[connection_index].type = GROUPCHAT_CONNECTION_ONLINE;
1587 }
1588
1589 send_peer_query(g_c, friendcon_id, other_groupnum);
1590
1591 return true;
1592 }
1593
1594 /* Set handlers for custom lossy packets. */
group_lossy_packet_registerhandler(Group_Chats * g_c,uint8_t byte,lossy_packet_cb * function)1595 void group_lossy_packet_registerhandler(Group_Chats *g_c, uint8_t byte, lossy_packet_cb *function)
1596 {
1597 g_c->lossy_packethandlers[byte].function = function;
1598 }
1599
1600 /* Set the callback for group invites. */
g_callback_group_invite(Group_Chats * g_c,g_conference_invite_cb * function)1601 void g_callback_group_invite(Group_Chats *g_c, g_conference_invite_cb *function)
1602 {
1603 g_c->invite_callback = function;
1604 }
1605
1606 /* Set the callback for group connection. */
g_callback_group_connected(Group_Chats * g_c,g_conference_connected_cb * function)1607 void g_callback_group_connected(Group_Chats *g_c, g_conference_connected_cb *function)
1608 {
1609 g_c->connected_callback = function;
1610 }
1611
1612 /* Set the callback for group messages. */
g_callback_group_message(Group_Chats * g_c,g_conference_message_cb * function)1613 void g_callback_group_message(Group_Chats *g_c, g_conference_message_cb *function)
1614 {
1615 g_c->message_callback = function;
1616 }
1617
1618 /* Set callback function for peer nickname changes.
1619 *
1620 * It gets called every time a peer changes their nickname.
1621 */
g_callback_peer_name(Group_Chats * g_c,peer_name_cb * function)1622 void g_callback_peer_name(Group_Chats *g_c, peer_name_cb *function)
1623 {
1624 g_c->peer_name_callback = function;
1625 }
1626
1627 /* Set callback function for peer list changes.
1628 *
1629 * It gets called every time the name list changes(new peer, deleted peer)
1630 */
g_callback_peer_list_changed(Group_Chats * g_c,peer_list_changed_cb * function)1631 void g_callback_peer_list_changed(Group_Chats *g_c, peer_list_changed_cb *function)
1632 {
1633 g_c->peer_list_changed_callback = function;
1634 }
1635
1636 /* Set callback function for title changes. */
g_callback_group_title(Group_Chats * g_c,title_cb * function)1637 void g_callback_group_title(Group_Chats *g_c, title_cb *function)
1638 {
1639 g_c->title_callback = function;
1640 }
1641
1642 /* Set a function to be called when a new peer joins a group chat.
1643 *
1644 * return 0 on success.
1645 * return -1 on failure.
1646 */
callback_groupchat_peer_new(const Group_Chats * g_c,uint32_t groupnumber,peer_on_join_cb * function)1647 int callback_groupchat_peer_new(const Group_Chats *g_c, uint32_t groupnumber, peer_on_join_cb *function)
1648 {
1649 Group_c *g = get_group_c(g_c, groupnumber);
1650
1651 if (!g) {
1652 return -1;
1653 }
1654
1655 g->peer_on_join = function;
1656 return 0;
1657 }
1658
1659 /* Set a function to be called when a peer leaves a group chat.
1660 *
1661 * return 0 on success.
1662 * return -1 on failure.
1663 */
callback_groupchat_peer_delete(Group_Chats * g_c,uint32_t groupnumber,peer_on_leave_cb * function)1664 int callback_groupchat_peer_delete(Group_Chats *g_c, uint32_t groupnumber, peer_on_leave_cb *function)
1665 {
1666 Group_c *g = get_group_c(g_c, groupnumber);
1667
1668 if (!g) {
1669 return -1;
1670 }
1671
1672 g->peer_on_leave = function;
1673 return 0;
1674 }
1675
1676 /* Set a function to be called when the group chat is deleted.
1677 *
1678 * return 0 on success.
1679 * return -1 on failure.
1680 */
callback_groupchat_delete(Group_Chats * g_c,uint32_t groupnumber,group_on_delete_cb * function)1681 int callback_groupchat_delete(Group_Chats *g_c, uint32_t groupnumber, group_on_delete_cb *function)
1682 {
1683 Group_c *g = get_group_c(g_c, groupnumber);
1684
1685 if (!g) {
1686 return -1;
1687 }
1688
1689 g->group_on_delete = function;
1690 return 0;
1691 }
1692
1693 static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint8_t message_id, const uint8_t *data,
1694 uint16_t len);
1695
1696 /* send a ping message
1697 * return true on success
1698 */
group_ping_send(const Group_Chats * g_c,uint32_t groupnumber)1699 static bool group_ping_send(const Group_Chats *g_c, uint32_t groupnumber)
1700 {
1701 if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_PING_ID, nullptr, 0) > 0) {
1702 return true;
1703 }
1704
1705 return false;
1706 }
1707
1708 /* send a new_peer message
1709 * return true on success
1710 */
group_new_peer_send(const Group_Chats * g_c,uint32_t groupnumber,uint16_t peer_num,const uint8_t * real_pk,uint8_t * temp_pk)1711 static bool group_new_peer_send(const Group_Chats *g_c, uint32_t groupnumber, uint16_t peer_num, const uint8_t *real_pk,
1712 uint8_t *temp_pk)
1713 {
1714 uint8_t packet[GROUP_MESSAGE_NEW_PEER_LENGTH];
1715
1716 peer_num = net_htons(peer_num);
1717 memcpy(packet, &peer_num, sizeof(uint16_t));
1718 memcpy(packet + sizeof(uint16_t), real_pk, CRYPTO_PUBLIC_KEY_SIZE);
1719 memcpy(packet + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE, temp_pk, CRYPTO_PUBLIC_KEY_SIZE);
1720
1721 if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_NEW_PEER_ID, packet, sizeof(packet)) > 0) {
1722 return true;
1723 }
1724
1725 return false;
1726 }
1727
1728 /* send a kill_peer message
1729 * return true on success
1730 */
group_kill_peer_send(const Group_Chats * g_c,uint32_t groupnumber,uint16_t peer_num)1731 static bool group_kill_peer_send(const Group_Chats *g_c, uint32_t groupnumber, uint16_t peer_num)
1732 {
1733 uint8_t packet[GROUP_MESSAGE_KILL_PEER_LENGTH];
1734
1735 peer_num = net_htons(peer_num);
1736 memcpy(packet, &peer_num, sizeof(uint16_t));
1737
1738 if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_KILL_PEER_ID, packet, sizeof(packet)) > 0) {
1739 return true;
1740 }
1741
1742 return false;
1743 }
1744
1745 /* send a freeze_peer message
1746 * return true on success
1747 */
group_freeze_peer_send(const Group_Chats * g_c,uint32_t groupnumber,uint16_t peer_num)1748 static bool group_freeze_peer_send(const Group_Chats *g_c, uint32_t groupnumber, uint16_t peer_num)
1749 {
1750 uint8_t packet[GROUP_MESSAGE_KILL_PEER_LENGTH];
1751
1752 peer_num = net_htons(peer_num);
1753 memcpy(packet, &peer_num, sizeof(uint16_t));
1754
1755 if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_FREEZE_PEER_ID, packet, sizeof(packet)) > 0) {
1756 return true;
1757 }
1758
1759 return false;
1760 }
1761
1762 /* send a name message
1763 * return true on success
1764 */
group_name_send(const Group_Chats * g_c,uint32_t groupnumber,const uint8_t * nick,uint16_t nick_len)1765 static bool group_name_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *nick, uint16_t nick_len)
1766 {
1767 if (nick_len > MAX_NAME_LENGTH) {
1768 return false;
1769 }
1770
1771 if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_NAME_ID, nick, nick_len) > 0) {
1772 return true;
1773 }
1774
1775 return false;
1776 }
1777
1778 /* send message to announce leaving group
1779 * return true on success
1780 */
group_leave(const Group_Chats * g_c,uint32_t groupnumber,bool permanent)1781 static bool group_leave(const Group_Chats *g_c, uint32_t groupnumber, bool permanent)
1782 {
1783 const Group_c *g = get_group_c(g_c, groupnumber);
1784
1785 if (!g) {
1786 return false;
1787 }
1788
1789 if (permanent) {
1790 return group_kill_peer_send(g_c, groupnumber, g->peer_number);
1791 } else {
1792 return group_freeze_peer_send(g_c, groupnumber, g->peer_number);
1793 }
1794 }
1795
1796
1797 /* set the group's title, limited to MAX_NAME_LENGTH
1798 * return 0 on success
1799 * return -1 if groupnumber is invalid.
1800 * return -2 if title is too long or empty.
1801 * return -3 if packet fails to send.
1802 */
group_title_send(const Group_Chats * g_c,uint32_t groupnumber,const uint8_t * title,uint8_t title_len)1803 int group_title_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *title, uint8_t title_len)
1804 {
1805 Group_c *g = get_group_c(g_c, groupnumber);
1806
1807 if (!g) {
1808 return -1;
1809 }
1810
1811 if (title_len > MAX_NAME_LENGTH || title_len == 0) {
1812 return -2;
1813 }
1814
1815 /* same as already set? */
1816 if (g->title_len == title_len && !memcmp(g->title, title, title_len)) {
1817 return 0;
1818 }
1819
1820 memcpy(g->title, title, title_len);
1821 g->title_len = title_len;
1822
1823 if (g->numpeers == 1) {
1824 return 0;
1825 }
1826
1827 if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_TITLE_ID, title, title_len) > 0) {
1828 return 0;
1829 }
1830
1831 return -3;
1832 }
1833
1834 /* return the group's title size.
1835 * return -1 of groupnumber is invalid.
1836 * return -2 if title is too long or empty.
1837 */
group_title_get_size(const Group_Chats * g_c,uint32_t groupnumber)1838 int group_title_get_size(const Group_Chats *g_c, uint32_t groupnumber)
1839 {
1840 const Group_c *g = get_group_c(g_c, groupnumber);
1841
1842 if (!g) {
1843 return -1;
1844 }
1845
1846 if (g->title_len > MAX_NAME_LENGTH || g->title_len == 0) {
1847 return -2;
1848 }
1849
1850 return g->title_len;
1851 }
1852
1853 /* Get group title from groupnumber and put it in title.
1854 * Title needs to be a valid memory location with a size of at least MAX_NAME_LENGTH (128) bytes.
1855 *
1856 * return length of copied title if success.
1857 * return -1 if groupnumber is invalid.
1858 * return -2 if title is too long or empty.
1859 */
group_title_get(const Group_Chats * g_c,uint32_t groupnumber,uint8_t * title)1860 int group_title_get(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *title)
1861 {
1862 const Group_c *g = get_group_c(g_c, groupnumber);
1863
1864 if (!g) {
1865 return -1;
1866 }
1867
1868 if (g->title_len > MAX_NAME_LENGTH || g->title_len == 0) {
1869 return -2;
1870 }
1871
1872 memcpy(title, g->title, g->title_len);
1873 return g->title_len;
1874 }
1875
get_peer_number(const Group_c * g,const uint8_t * real_pk,uint16_t * peer_number)1876 static bool get_peer_number(const Group_c *g, const uint8_t *real_pk, uint16_t *peer_number)
1877 {
1878 const int peer_index = peer_in_group(g, real_pk);
1879
1880 if (peer_index >= 0) {
1881 *peer_number = g->group[peer_index].peer_number;
1882 return true;
1883 }
1884
1885 const int frozen_index = frozen_in_group(g, real_pk);
1886
1887 if (frozen_index >= 0) {
1888 *peer_number = g->frozen[frozen_index].peer_number;
1889 return true;
1890 }
1891
1892 return false;
1893 }
1894
handle_friend_invite_packet(Messenger * m,uint32_t friendnumber,const uint8_t * data,uint16_t length,void * userdata)1895 static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length,
1896 void *userdata)
1897 {
1898 Group_Chats *g_c = m->conferences_object;
1899
1900 if (length <= 1) {
1901 return;
1902 }
1903
1904 const uint8_t *invite_data = data + 1;
1905 const uint16_t invite_length = length - 1;
1906
1907 switch (data[0]) {
1908 case INVITE_ID: {
1909 if (length != INVITE_PACKET_SIZE) {
1910 return;
1911 }
1912
1913 const int groupnumber = get_group_num(g_c, data[1 + sizeof(uint16_t)], data + 1 + sizeof(uint16_t) + 1);
1914
1915 if (groupnumber == -1) {
1916 if (g_c->invite_callback) {
1917 g_c->invite_callback(m, friendnumber, invite_data[sizeof(uint16_t)], invite_data, invite_length, userdata);
1918 }
1919
1920 return;
1921 } else {
1922 Group_c *g = get_group_c(g_c, groupnumber);
1923
1924 if (g && g->status == GROUPCHAT_STATUS_CONNECTED) {
1925 send_invite_response(g_c, groupnumber, friendnumber, invite_data, invite_length);
1926 }
1927 }
1928
1929 break;
1930 }
1931
1932 case INVITE_ACCEPT_ID:
1933 case INVITE_MEMBER_ID: {
1934 const bool member = (data[0] == INVITE_MEMBER_ID);
1935
1936 if (length != (member ? INVITE_MEMBER_PACKET_SIZE : INVITE_ACCEPT_PACKET_SIZE)) {
1937 return;
1938 }
1939
1940 uint16_t other_groupnum;
1941 uint16_t groupnum;
1942 net_unpack_u16(data + 1, &other_groupnum);
1943 net_unpack_u16(data + 1 + sizeof(uint16_t), &groupnum);
1944
1945 Group_c *g = get_group_c(g_c, groupnum);
1946
1947 if (!g) {
1948 return;
1949 }
1950
1951 if (data[1 + sizeof(uint16_t) * 2] != g->type) {
1952 return;
1953 }
1954
1955 if (crypto_memcmp(data + 1 + sizeof(uint16_t) * 2 + 1, g->id, GROUP_ID_LENGTH) != 0) {
1956 return;
1957 }
1958
1959 uint16_t peer_number;
1960
1961 if (member) {
1962 net_unpack_u16(data + 1 + sizeof(uint16_t) * 2 + 1 + GROUP_ID_LENGTH, &peer_number);
1963 } else {
1964 /* TODO(irungentoo): what if two people enter the group at the
1965 * same time and are given the same peer_number by different
1966 * nodes? */
1967 peer_number = random_u16();
1968
1969 unsigned int tries = 0;
1970
1971 while (get_peer_index(g, peer_number) != -1 || get_frozen_index(g, peer_number) != -1) {
1972 peer_number = random_u16();
1973 ++tries;
1974
1975 if (tries > 32) {
1976 return;
1977 }
1978 }
1979 }
1980
1981 const int friendcon_id = getfriendcon_id(m, friendnumber);
1982
1983 if (friendcon_id == -1) {
1984 // TODO(iphydf): Log something?
1985 return;
1986 }
1987
1988 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
1989 uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
1990 get_friendcon_public_keys(real_pk, temp_pk, g_c->fr_c, friendcon_id);
1991
1992 addpeer(g_c, groupnum, real_pk, temp_pk, peer_number, userdata, true, true);
1993 const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g,
1994 GROUPCHAT_CONNECTION_REASON_INTRODUCING, 1);
1995
1996 if (member) {
1997 add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, 0);
1998 send_peer_query(g_c, friendcon_id, other_groupnum);
1999 }
2000
2001 if (connection_index != -1) {
2002 g->connections[connection_index].group_number = other_groupnum;
2003 g->connections[connection_index].type = GROUPCHAT_CONNECTION_ONLINE;
2004 }
2005
2006 group_new_peer_send(g_c, groupnum, peer_number, real_pk, temp_pk);
2007
2008 break;
2009 }
2010
2011 default:
2012 return;
2013 }
2014 }
2015
2016 /* Find index of friend in the connections list.
2017 *
2018 * return index on success
2019 * return -1 on failure.
2020 */
friend_in_connections(const Group_c * g,int friendcon_id)2021 static int friend_in_connections(const Group_c *g, int friendcon_id)
2022 {
2023 for (unsigned int i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
2024 if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) {
2025 continue;
2026 }
2027
2028 if (g->connections[i].number == (uint32_t)friendcon_id) {
2029 return i;
2030 }
2031 }
2032
2033 return -1;
2034 }
2035
2036 /* return number of connections.
2037 */
count_connected(const Group_c * g)2038 static unsigned int count_connected(const Group_c *g)
2039 {
2040 unsigned int count = 0;
2041
2042 for (unsigned int i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
2043 if (g->connections[i].type == GROUPCHAT_CONNECTION_ONLINE) {
2044 ++count;
2045 }
2046 }
2047
2048 return count;
2049 }
2050
send_packet_online(Friend_Connections * fr_c,int friendcon_id,uint16_t group_num,uint8_t type,const uint8_t * id)2051 static int send_packet_online(Friend_Connections *fr_c, int friendcon_id, uint16_t group_num, uint8_t type,
2052 const uint8_t *id)
2053 {
2054 uint8_t packet[1 + ONLINE_PACKET_DATA_SIZE];
2055 group_num = net_htons(group_num);
2056 packet[0] = PACKET_ID_ONLINE_PACKET;
2057 memcpy(packet + 1, &group_num, sizeof(uint16_t));
2058 packet[1 + sizeof(uint16_t)] = type;
2059 memcpy(packet + 1 + sizeof(uint16_t) + 1, id, GROUP_ID_LENGTH);
2060 return write_cryptpacket(friendconn_net_crypto(fr_c), friend_connection_crypt_connection_id(fr_c, friendcon_id), packet,
2061 sizeof(packet), 0) != -1;
2062 }
2063
2064 static bool ping_groupchat(Group_Chats *g_c, uint32_t groupnumber);
2065
handle_packet_online(Group_Chats * g_c,int friendcon_id,const uint8_t * data,uint16_t length)2066 static int handle_packet_online(Group_Chats *g_c, int friendcon_id, const uint8_t *data, uint16_t length)
2067 {
2068 if (length != ONLINE_PACKET_DATA_SIZE) {
2069 return -1;
2070 }
2071
2072 const int groupnumber = get_group_num(g_c, data[sizeof(uint16_t)], data + sizeof(uint16_t) + 1);
2073
2074 if (groupnumber == -1) {
2075 return -1;
2076 }
2077
2078 uint16_t other_groupnum;
2079 memcpy(&other_groupnum, data, sizeof(uint16_t));
2080 other_groupnum = net_ntohs(other_groupnum);
2081
2082 Group_c *g = get_group_c(g_c, groupnumber);
2083
2084 if (!g) {
2085 return -1;
2086 }
2087
2088 const int index = friend_in_connections(g, friendcon_id);
2089
2090 if (index == -1) {
2091 return -1;
2092 }
2093
2094 if (g->connections[index].type == GROUPCHAT_CONNECTION_ONLINE) {
2095 return -1;
2096 }
2097
2098 if (count_connected(g) == 0 || (g->connections[index].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER)) {
2099 send_peer_query(g_c, friendcon_id, other_groupnum);
2100 }
2101
2102 g->connections[index].group_number = other_groupnum;
2103 g->connections[index].type = GROUPCHAT_CONNECTION_ONLINE;
2104 send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->type, g->id);
2105
2106 if (g->connections[index].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCING) {
2107 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
2108 uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
2109 get_friendcon_public_keys(real_pk, temp_pk, g_c->fr_c, friendcon_id);
2110
2111 const int peer_index = peer_in_group(g, real_pk);
2112
2113 if (peer_index != -1) {
2114 group_new_peer_send(g_c, groupnumber, g->group[peer_index].peer_number, real_pk, temp_pk);
2115 }
2116
2117 g->need_send_name = true;
2118 }
2119
2120 ping_groupchat(g_c, groupnumber);
2121
2122 return 0;
2123 }
2124
handle_packet_rejoin(Group_Chats * g_c,int friendcon_id,const uint8_t * data,uint16_t length,void * userdata)2125 static int handle_packet_rejoin(Group_Chats *g_c, int friendcon_id, const uint8_t *data, uint16_t length,
2126 void *userdata)
2127 {
2128 if (length < 1 + GROUP_ID_LENGTH) {
2129 return -1;
2130 }
2131
2132 const int32_t groupnum = get_group_num(g_c, *data, data + 1);
2133
2134 Group_c *g = get_group_c(g_c, groupnum);
2135
2136 if (!g) {
2137 return -1;
2138 }
2139
2140 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
2141 uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
2142 get_friendcon_public_keys(real_pk, temp_pk, g_c->fr_c, friendcon_id);
2143
2144 uint16_t peer_number;
2145
2146 if (!get_peer_number(g, real_pk, &peer_number)) {
2147 return -1;
2148 }
2149
2150 addpeer(g_c, groupnum, real_pk, temp_pk, peer_number, userdata, true, true);
2151 const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g,
2152 GROUPCHAT_CONNECTION_REASON_INTRODUCING, 1);
2153
2154 if (connection_index != -1) {
2155 send_packet_online(g_c->fr_c, friendcon_id, groupnum, g->type, g->id);
2156 }
2157
2158 return 0;
2159 }
2160
2161
2162 // we could send title with invite, but then if it changes between sending and accepting inv, joinee won't see it
2163
2164 /* return 1 on success.
2165 * return 0 on failure
2166 */
send_peer_introduced(Group_Chats * g_c,int friendcon_id,uint16_t group_num)2167 static unsigned int send_peer_introduced(Group_Chats *g_c, int friendcon_id, uint16_t group_num)
2168 {
2169 uint8_t packet[1];
2170 packet[0] = PEER_INTRODUCED_ID;
2171 return send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num, packet, sizeof(packet));
2172 }
2173
2174
2175 /* return 1 on success.
2176 * return 0 on failure
2177 */
send_peer_query(Group_Chats * g_c,int friendcon_id,uint16_t group_num)2178 static unsigned int send_peer_query(Group_Chats *g_c, int friendcon_id, uint16_t group_num)
2179 {
2180 uint8_t packet[1];
2181 packet[0] = PEER_QUERY_ID;
2182 return send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num, packet, sizeof(packet));
2183 }
2184
2185 /* return number of peers sent on success.
2186 * return 0 on failure.
2187 */
send_peers(Group_Chats * g_c,const Group_c * g,int friendcon_id,uint16_t group_num)2188 static unsigned int send_peers(Group_Chats *g_c, const Group_c *g, int friendcon_id, uint16_t group_num)
2189 {
2190 uint8_t response_packet[MAX_CRYPTO_DATA_SIZE - (1 + sizeof(uint16_t))];
2191 response_packet[0] = PEER_RESPONSE_ID;
2192 uint8_t *p = response_packet + 1;
2193
2194 uint16_t sent = 0;
2195
2196 for (uint32_t i = 0;; ++i) {
2197 if (i == g->numpeers
2198 || (p - response_packet) + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE * 2 + 1 + g->group[i].nick_len >
2199 sizeof(response_packet)) {
2200 if (send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num, response_packet,
2201 (p - response_packet))) {
2202 sent = i;
2203 } else {
2204 return sent;
2205 }
2206
2207 if (i == g->numpeers) {
2208 break;
2209 }
2210
2211 p = response_packet + 1;
2212 }
2213
2214 const uint16_t peer_num = net_htons(g->group[i].peer_number);
2215 memcpy(p, &peer_num, sizeof(peer_num));
2216 p += sizeof(peer_num);
2217 memcpy(p, g->group[i].real_pk, CRYPTO_PUBLIC_KEY_SIZE);
2218 p += CRYPTO_PUBLIC_KEY_SIZE;
2219 memcpy(p, g->group[i].temp_pk, CRYPTO_PUBLIC_KEY_SIZE);
2220 p += CRYPTO_PUBLIC_KEY_SIZE;
2221 *p = g->group[i].nick_len;
2222 p += 1;
2223 memcpy(p, g->group[i].nick, g->group[i].nick_len);
2224 p += g->group[i].nick_len;
2225 }
2226
2227 if (g->title_len) {
2228 VLA(uint8_t, title_packet, 1 + g->title_len);
2229 title_packet[0] = PEER_TITLE_ID;
2230 memcpy(title_packet + 1, g->title, g->title_len);
2231 send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num, title_packet,
2232 SIZEOF_VLA(title_packet));
2233 }
2234
2235 return sent;
2236 }
2237
handle_send_peers(Group_Chats * g_c,uint32_t groupnumber,const uint8_t * data,uint16_t length,void * userdata)2238 static int handle_send_peers(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length,
2239 void *userdata)
2240 {
2241 if (length == 0) {
2242 return -1;
2243 }
2244
2245 Group_c *g = get_group_c(g_c, groupnumber);
2246
2247 if (!g) {
2248 return -1;
2249 }
2250
2251 const uint8_t *d = data;
2252
2253 while ((unsigned int)(length - (d - data)) >= sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE * 2 + 1) {
2254 uint16_t peer_num;
2255 memcpy(&peer_num, d, sizeof(peer_num));
2256 peer_num = net_ntohs(peer_num);
2257 d += sizeof(uint16_t);
2258
2259 if (g->status == GROUPCHAT_STATUS_VALID
2260 && public_key_cmp(d, nc_get_self_public_key(g_c->m->net_crypto)) == 0) {
2261 g->peer_number = peer_num;
2262 g->status = GROUPCHAT_STATUS_CONNECTED;
2263
2264 if (g_c->connected_callback) {
2265 g_c->connected_callback(g_c->m, groupnumber, userdata);
2266 }
2267
2268 g->need_send_name = true;
2269 }
2270
2271 const int peer_index = addpeer(g_c, groupnumber, d, d + CRYPTO_PUBLIC_KEY_SIZE, peer_num, userdata, false, true);
2272
2273 if (peer_index == -1) {
2274 return -1;
2275 }
2276
2277 d += CRYPTO_PUBLIC_KEY_SIZE * 2;
2278 const uint8_t name_length = *d;
2279 d += 1;
2280
2281 if (name_length > (length - (d - data)) || name_length > MAX_NAME_LENGTH) {
2282 return -1;
2283 }
2284
2285 if (!g->group[peer_index].nick_updated) {
2286 setnick(g_c, groupnumber, peer_index, d, name_length, userdata, true);
2287 }
2288
2289 d += name_length;
2290 }
2291
2292 return 0;
2293 }
2294
handle_direct_packet(Group_Chats * g_c,uint32_t groupnumber,const uint8_t * data,uint16_t length,int connection_index,void * userdata)2295 static void handle_direct_packet(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length,
2296 int connection_index, void *userdata)
2297 {
2298 if (length == 0) {
2299 return;
2300 }
2301
2302 Group_c *g = get_group_c(g_c, groupnumber);
2303
2304 if (!g) {
2305 return;
2306 }
2307
2308 switch (data[0]) {
2309 case PEER_INTRODUCED_ID: {
2310 remove_connection_reason(g_c, g, connection_index, GROUPCHAT_CONNECTION_REASON_INTRODUCING);
2311 }
2312
2313 break;
2314
2315 case PEER_QUERY_ID: {
2316 if (g->connections[connection_index].type != GROUPCHAT_CONNECTION_ONLINE) {
2317 return;
2318 }
2319
2320 send_peers(g_c, g, g->connections[connection_index].number, g->connections[connection_index].group_number);
2321 }
2322
2323 break;
2324
2325 case PEER_RESPONSE_ID: {
2326 handle_send_peers(g_c, groupnumber, data + 1, length - 1, userdata);
2327 }
2328
2329 break;
2330
2331 case PEER_TITLE_ID: {
2332 if (!g->title_fresh) {
2333 settitle(g_c, groupnumber, -1, data + 1, length - 1, userdata);
2334 }
2335 }
2336
2337 break;
2338 }
2339 }
2340
2341 /* Send message to all connections except receiver (if receiver isn't -1)
2342 * NOTE: this function appends the group chat number to the data passed to it.
2343 *
2344 * return number of messages sent.
2345 */
send_message_all_connections(const Group_Chats * g_c,const Group_c * g,const uint8_t * data,uint16_t length,int receiver)2346 static unsigned int send_message_all_connections(const Group_Chats *g_c, const Group_c *g, const uint8_t *data,
2347 uint16_t length, int receiver)
2348 {
2349 uint16_t sent = 0;
2350
2351 for (uint16_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
2352 if (g->connections[i].type != GROUPCHAT_CONNECTION_ONLINE) {
2353 continue;
2354 }
2355
2356 if ((int)i == receiver) {
2357 continue;
2358 }
2359
2360 if (send_packet_group_peer(g_c->fr_c, g->connections[i].number, PACKET_ID_MESSAGE_CONFERENCE,
2361 g->connections[i].group_number, data, length)) {
2362 ++sent;
2363 }
2364 }
2365
2366 return sent;
2367 }
2368
2369 /* Send lossy message to all connections except receiver (if receiver isn't -1)
2370 * NOTE: this function appends the group chat number to the data passed to it.
2371 *
2372 * return number of messages sent.
2373 */
send_lossy_all_connections(const Group_Chats * g_c,const Group_c * g,const uint8_t * data,uint16_t length,int receiver)2374 static unsigned int send_lossy_all_connections(const Group_Chats *g_c, const Group_c *g, const uint8_t *data,
2375 uint16_t length,
2376 int receiver)
2377 {
2378 unsigned int sent = 0;
2379 unsigned int num_connected_closest = 0;
2380 unsigned int connected_closest[DESIRED_CLOSEST];
2381
2382 for (unsigned int i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
2383 if (g->connections[i].type != GROUPCHAT_CONNECTION_ONLINE) {
2384 continue;
2385 }
2386
2387 if ((int)i == receiver) {
2388 continue;
2389 }
2390
2391 if (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST) {
2392 connected_closest[num_connected_closest] = i;
2393 ++num_connected_closest;
2394 continue;
2395 }
2396
2397 if (send_lossy_group_peer(g_c->fr_c, g->connections[i].number, PACKET_ID_LOSSY_CONFERENCE,
2398 g->connections[i].group_number, data, length)) {
2399 ++sent;
2400 }
2401 }
2402
2403 if (!num_connected_closest) {
2404 return sent;
2405 }
2406
2407 unsigned int to_send[2] = {0, 0};
2408 uint64_t comp_val_old[2] = {(uint64_t) -1, (uint64_t) -1};
2409
2410 for (unsigned int i = 0; i < num_connected_closest; ++i) {
2411 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE] = {0};
2412 get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->connections[connected_closest[i]].number);
2413 const uint64_t comp_val = calculate_comp_value(g->real_pk, real_pk);
2414
2415 for (uint8_t j = 0; j < 2; ++j) {
2416 if (j ? (comp_val > comp_val_old[j]) : (comp_val < comp_val_old[j])) {
2417 to_send[j] = connected_closest[i];
2418 comp_val_old[j] = comp_val;
2419 }
2420 }
2421 }
2422
2423 for (uint8_t j = 0; j < 2; ++j) {
2424 if (j && to_send[1] == to_send[0]) {
2425 break;
2426 }
2427
2428 if (send_lossy_group_peer(g_c->fr_c, g->connections[to_send[j]].number, PACKET_ID_LOSSY_CONFERENCE,
2429 g->connections[to_send[j]].group_number, data, length)) {
2430 ++sent;
2431 }
2432 }
2433
2434 return sent;
2435 }
2436
2437 /* Send data of len with message_id to groupnumber.
2438 *
2439 * return number of peers it was sent to on success.
2440 * return -1 if groupnumber is invalid.
2441 * return -2 if message is too long.
2442 * return -3 if we are not connected to the group.
2443 * return -4 if message failed to send.
2444 */
send_message_group(const Group_Chats * g_c,uint32_t groupnumber,uint8_t message_id,const uint8_t * data,uint16_t len)2445 static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint8_t message_id, const uint8_t *data,
2446 uint16_t len)
2447 {
2448 Group_c *g = get_group_c(g_c, groupnumber);
2449
2450 if (!g) {
2451 return -1;
2452 }
2453
2454 if (len > MAX_GROUP_MESSAGE_DATA_LEN) {
2455 return -2;
2456 }
2457
2458 if (g->status != GROUPCHAT_STATUS_CONNECTED || count_connected(g) == 0) {
2459 return -3;
2460 }
2461
2462 VLA(uint8_t, packet, sizeof(uint16_t) + sizeof(uint32_t) + 1 + len);
2463 const uint16_t peer_num = net_htons(g->peer_number);
2464 memcpy(packet, &peer_num, sizeof(peer_num));
2465
2466 ++g->message_number;
2467
2468 if (!g->message_number) {
2469 ++g->message_number;
2470 }
2471
2472 const uint32_t message_num = net_htonl(g->message_number);
2473 memcpy(packet + sizeof(uint16_t), &message_num, sizeof(message_num));
2474
2475 packet[sizeof(uint16_t) + sizeof(uint32_t)] = message_id;
2476
2477 if (len) {
2478 memcpy(packet + sizeof(uint16_t) + sizeof(uint32_t) + 1, data, len);
2479 }
2480
2481 unsigned int ret = send_message_all_connections(g_c, g, packet, SIZEOF_VLA(packet), -1);
2482
2483 if (ret == 0) {
2484 return -4;
2485 }
2486
2487 return ret;
2488 }
2489
2490 /* send a group message
2491 * return 0 on success
2492 * see: send_message_group() for error codes.
2493 */
group_message_send(const Group_Chats * g_c,uint32_t groupnumber,const uint8_t * message,uint16_t length)2494 int group_message_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *message, uint16_t length)
2495 {
2496 const int ret = send_message_group(g_c, groupnumber, PACKET_ID_MESSAGE, message, length);
2497
2498 if (ret > 0) {
2499 return 0;
2500 }
2501
2502 return ret;
2503 }
2504
2505 /* send a group action
2506 * return 0 on success
2507 * see: send_message_group() for error codes.
2508 */
group_action_send(const Group_Chats * g_c,uint32_t groupnumber,const uint8_t * action,uint16_t length)2509 int group_action_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *action, uint16_t length)
2510 {
2511 const int ret = send_message_group(g_c, groupnumber, PACKET_ID_ACTION, action, length);
2512
2513 if (ret > 0) {
2514 return 0;
2515 }
2516
2517 return ret;
2518 }
2519
2520 /* High level function to send custom lossy packets.
2521 *
2522 * return -1 on failure.
2523 * return 0 on success.
2524 */
send_group_lossy_packet(const Group_Chats * g_c,uint32_t groupnumber,const uint8_t * data,uint16_t length)2525 int send_group_lossy_packet(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length)
2526 {
2527 // TODO(irungentoo): length check here?
2528 Group_c *g = get_group_c(g_c, groupnumber);
2529
2530 if (!g) {
2531 return -1;
2532 }
2533
2534 VLA(uint8_t, packet, sizeof(uint16_t) * 2 + length);
2535 const uint16_t peer_number = net_htons(g->peer_number);
2536 memcpy(packet, &peer_number, sizeof(uint16_t));
2537 const uint16_t message_num = net_htons(g->lossy_message_number);
2538 memcpy(packet + sizeof(uint16_t), &message_num, sizeof(uint16_t));
2539 memcpy(packet + sizeof(uint16_t) * 2, data, length);
2540
2541 if (send_lossy_all_connections(g_c, g, packet, SIZEOF_VLA(packet), -1) == 0) {
2542 return -1;
2543 }
2544
2545 ++g->lossy_message_number;
2546 return 0;
2547 }
2548
find_message_slot_or_reject(uint32_t message_number,uint8_t message_id,Group_Peer * peer)2549 static Message_Info *find_message_slot_or_reject(uint32_t message_number, uint8_t message_id, Group_Peer *peer)
2550 {
2551 const bool ignore_older = (message_id == GROUP_MESSAGE_NAME_ID || message_id == GROUP_MESSAGE_TITLE_ID);
2552
2553 Message_Info *i;
2554
2555 for (i = peer->last_message_infos; i < peer->last_message_infos + peer->num_last_message_infos; ++i) {
2556 if (message_number - (i->message_number + 1) <= ((uint32_t)1 << 31)) {
2557 break;
2558 }
2559
2560 if (message_number == i->message_number) {
2561 return nullptr;
2562 }
2563
2564 if (ignore_older && message_id == i->message_id) {
2565 return nullptr;
2566 }
2567 }
2568
2569 return i;
2570 }
2571
2572 /* Stores message info in peer->last_message_infos.
2573 *
2574 * return true if message should be processed.
2575 * return false otherwise.
2576 */
check_message_info(uint32_t message_number,uint8_t message_id,Group_Peer * peer)2577 static bool check_message_info(uint32_t message_number, uint8_t message_id, Group_Peer *peer)
2578 {
2579 Message_Info *const i = find_message_slot_or_reject(message_number, message_id, peer);
2580
2581 if (i == nullptr) {
2582 return false;
2583 }
2584
2585 if (i == peer->last_message_infos + MAX_LAST_MESSAGE_INFOS) {
2586 return false;
2587 }
2588
2589 if (peer->num_last_message_infos < MAX_LAST_MESSAGE_INFOS) {
2590 ++peer->num_last_message_infos;
2591 }
2592
2593 memmove(i + 1, i, ((peer->last_message_infos + peer->num_last_message_infos - 1) - i) * sizeof(Message_Info));
2594
2595 i->message_number = message_number;
2596 i->message_id = message_id;
2597
2598 return true;
2599 }
2600
handle_message_packet_group(Group_Chats * g_c,uint32_t groupnumber,const uint8_t * data,uint16_t length,int connection_index,void * userdata)2601 static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length,
2602 int connection_index, void *userdata)
2603 {
2604 if (length < sizeof(uint16_t) + sizeof(uint32_t) + 1) {
2605 return;
2606 }
2607
2608 Group_c *g = get_group_c(g_c, groupnumber);
2609
2610 if (!g) {
2611 return;
2612 }
2613
2614 uint16_t peer_number;
2615 memcpy(&peer_number, data, sizeof(uint16_t));
2616 peer_number = net_ntohs(peer_number);
2617
2618 uint32_t message_number;
2619 memcpy(&message_number, data + sizeof(uint16_t), sizeof(message_number));
2620 message_number = net_ntohl(message_number);
2621
2622 const uint8_t message_id = data[sizeof(uint16_t) + sizeof(message_number)];
2623 const uint8_t *msg_data = data + sizeof(uint16_t) + sizeof(message_number) + 1;
2624 const uint16_t msg_data_len = length - (sizeof(uint16_t) + sizeof(message_number) + 1);
2625
2626 const bool ignore_frozen = message_id == GROUP_MESSAGE_FREEZE_PEER_ID;
2627
2628 const int index = ignore_frozen ? get_peer_index(g, peer_number)
2629 : note_peer_active(g_c, groupnumber, peer_number, userdata);
2630
2631 if (index == -1) {
2632 if (ignore_frozen) {
2633 return;
2634 }
2635
2636 if (g->connections[connection_index].type != GROUPCHAT_CONNECTION_ONLINE) {
2637 return;
2638 }
2639
2640 /* If we don't know the peer this packet came from, then we query the
2641 * list of peers from the relaying peer.
2642 * (They wouldn't have relayed it if they didn't know the peer.) */
2643 send_peer_query(g_c, g->connections[connection_index].number, g->connections[connection_index].group_number);
2644 return;
2645 }
2646
2647 if (g->num_introducer_connections > 0 && count_connected(g) > DESIRED_CLOSEST) {
2648 for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
2649 if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE
2650 || !(g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER)
2651 || i == connection_index) {
2652 continue;
2653 }
2654
2655 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
2656 get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->connections[i].number);
2657
2658 if (id_equal(g->group[index].real_pk, real_pk)) {
2659 /* Received message from peer relayed via another peer, so
2660 * the introduction was successful */
2661 remove_connection_reason(g_c, g, i, GROUPCHAT_CONNECTION_REASON_INTRODUCER);
2662 }
2663 }
2664 }
2665
2666 if (!check_message_info(message_number, message_id, &g->group[index])) {
2667 return;
2668 }
2669
2670 uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
2671 get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->connections[connection_index].number);
2672 const bool direct_from_sender = id_equal(g->group[index].real_pk, real_pk);
2673
2674 switch (message_id) {
2675 case GROUP_MESSAGE_PING_ID:
2676 break;
2677
2678 case GROUP_MESSAGE_NEW_PEER_ID: {
2679 if (msg_data_len != GROUP_MESSAGE_NEW_PEER_LENGTH) {
2680 return;
2681 }
2682
2683 uint16_t new_peer_number;
2684 memcpy(&new_peer_number, msg_data, sizeof(uint16_t));
2685 new_peer_number = net_ntohs(new_peer_number);
2686 addpeer(g_c, groupnumber, msg_data + sizeof(uint16_t), msg_data + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE,
2687 new_peer_number, userdata, true, true);
2688 }
2689 break;
2690
2691 case GROUP_MESSAGE_KILL_PEER_ID:
2692 case GROUP_MESSAGE_FREEZE_PEER_ID: {
2693 if (msg_data_len != GROUP_MESSAGE_KILL_PEER_LENGTH) {
2694 return;
2695 }
2696
2697 uint16_t kill_peer_number;
2698 memcpy(&kill_peer_number, msg_data, sizeof(uint16_t));
2699 kill_peer_number = net_ntohs(kill_peer_number);
2700
2701 if (peer_number == kill_peer_number) {
2702 if (message_id == GROUP_MESSAGE_KILL_PEER_ID) {
2703 delpeer(g_c, groupnumber, index, userdata);
2704 } else {
2705 freeze_peer(g_c, groupnumber, index, userdata);
2706 }
2707 } else {
2708 return;
2709 // TODO(irungentoo):
2710 }
2711 }
2712 break;
2713
2714 case GROUP_MESSAGE_NAME_ID: {
2715 if (!setnick(g_c, groupnumber, index, msg_data, msg_data_len, userdata, true)) {
2716 return;
2717 }
2718 }
2719 break;
2720
2721 case GROUP_MESSAGE_TITLE_ID: {
2722 if (!settitle(g_c, groupnumber, index, msg_data, msg_data_len, userdata)) {
2723 return;
2724 }
2725 }
2726 break;
2727
2728 case PACKET_ID_MESSAGE: {
2729 if (msg_data_len == 0) {
2730 return;
2731 }
2732
2733 VLA(uint8_t, newmsg, msg_data_len + 1);
2734 memcpy(newmsg, msg_data, msg_data_len);
2735 newmsg[msg_data_len] = 0;
2736
2737 // TODO(irungentoo):
2738 if (g_c->message_callback) {
2739 g_c->message_callback(g_c->m, groupnumber, index, 0, newmsg, msg_data_len, userdata);
2740 }
2741
2742 break;
2743 }
2744
2745 case PACKET_ID_ACTION: {
2746 if (msg_data_len == 0) {
2747 return;
2748 }
2749
2750 VLA(uint8_t, newmsg, msg_data_len + 1);
2751 memcpy(newmsg, msg_data, msg_data_len);
2752 newmsg[msg_data_len] = 0;
2753
2754 // TODO(irungentoo):
2755 if (g_c->message_callback) {
2756 g_c->message_callback(g_c->m, groupnumber, index, 1, newmsg, msg_data_len, userdata);
2757 }
2758
2759 break;
2760 }
2761
2762 default:
2763 return;
2764 }
2765
2766 /* If the packet was received from the peer who sent the message, relay it
2767 * back. When the sender only has one group connection (e.g. because there
2768 * are only two peers in the group), this is the only way for them to
2769 * receive their own message. */
2770 send_message_all_connections(g_c, g, data, length, direct_from_sender ? -1 : connection_index);
2771 }
2772
g_handle_packet(void * object,int friendcon_id,const uint8_t * data,uint16_t length,void * userdata)2773 static int g_handle_packet(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata)
2774 {
2775 Group_Chats *g_c = (Group_Chats *)object;
2776
2777 if (length < 1 + sizeof(uint16_t) + 1) {
2778 return -1;
2779 }
2780
2781 if (data[0] == PACKET_ID_ONLINE_PACKET) {
2782 return handle_packet_online(g_c, friendcon_id, data + 1, length - 1);
2783 }
2784
2785 if (data[0] == PACKET_ID_REJOIN_CONFERENCE) {
2786 return handle_packet_rejoin(g_c, friendcon_id, data + 1, length - 1, userdata);
2787 }
2788
2789 uint16_t groupnumber;
2790 memcpy(&groupnumber, data + 1, sizeof(uint16_t));
2791 groupnumber = net_ntohs(groupnumber);
2792 const Group_c *g = get_group_c(g_c, groupnumber);
2793
2794 if (!g) {
2795 return -1;
2796 }
2797
2798 const int index = friend_in_connections(g, friendcon_id);
2799
2800 if (index == -1) {
2801 return -1;
2802 }
2803
2804 if (data[0] == PACKET_ID_DIRECT_CONFERENCE) {
2805 handle_direct_packet(g_c, groupnumber, data + 1 + sizeof(uint16_t),
2806 length - (1 + sizeof(uint16_t)), index, userdata);
2807 return 0;
2808 }
2809
2810 if (data[0] == PACKET_ID_MESSAGE_CONFERENCE) {
2811 handle_message_packet_group(g_c, groupnumber, data + 1 + sizeof(uint16_t),
2812 length - (1 + sizeof(uint16_t)), index, userdata);
2813 return 0;
2814 }
2815
2816 return -1;
2817 }
2818
2819 /* Did we already receive the lossy packet or not.
2820 *
2821 * return -1 on failure.
2822 * return 0 if packet was not received.
2823 * return 1 if packet was received.
2824 *
2825 * TODO(irungentoo): test this
2826 */
lossy_packet_not_received(const Group_c * g,int peer_index,uint16_t message_number)2827 static int lossy_packet_not_received(const Group_c *g, int peer_index, uint16_t message_number)
2828 {
2829 if (peer_index == -1) {
2830 return -1;
2831 }
2832
2833 if (g->group[peer_index].bottom_lossy_number == g->group[peer_index].top_lossy_number) {
2834 g->group[peer_index].top_lossy_number = message_number;
2835 g->group[peer_index].bottom_lossy_number = (message_number - MAX_LOSSY_COUNT) + 1;
2836 g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT] = 1;
2837 return 0;
2838 }
2839
2840 if ((uint16_t)(message_number - g->group[peer_index].bottom_lossy_number) < MAX_LOSSY_COUNT) {
2841 if (g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT]) {
2842 return 1;
2843 }
2844
2845 g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT] = 1;
2846 return 0;
2847 }
2848
2849 if ((uint16_t)(message_number - g->group[peer_index].bottom_lossy_number) > (1 << 15)) {
2850 return -1;
2851 }
2852
2853 const uint16_t top_distance = message_number - g->group[peer_index].top_lossy_number;
2854
2855 if (top_distance >= MAX_LOSSY_COUNT) {
2856 crypto_memzero(g->group[peer_index].recv_lossy, sizeof(g->group[peer_index].recv_lossy));
2857 } else { // top_distance < MAX_LOSSY_COUNT
2858 for (unsigned int i = g->group[peer_index].bottom_lossy_number;
2859 i != g->group[peer_index].bottom_lossy_number + top_distance;
2860 ++i) {
2861 g->group[peer_index].recv_lossy[i % MAX_LOSSY_COUNT] = 0;
2862 }
2863 }
2864
2865 g->group[peer_index].top_lossy_number = message_number;
2866 g->group[peer_index].bottom_lossy_number = (message_number - MAX_LOSSY_COUNT) + 1;
2867 g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT] = 1;
2868
2869 return 0;
2870
2871 }
2872
2873 /* Does this group type make use of lossy packets? */
type_uses_lossy(uint8_t type)2874 static bool type_uses_lossy(uint8_t type)
2875 {
2876 return (type == GROUPCHAT_TYPE_AV);
2877 }
2878
handle_lossy(void * object,int friendcon_id,const uint8_t * data,uint16_t length,void * userdata)2879 static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata)
2880 {
2881 Group_Chats *g_c = (Group_Chats *)object;
2882
2883 if (data[0] != PACKET_ID_LOSSY_CONFERENCE) {
2884 return -1;
2885 }
2886
2887 if (length < 1 + sizeof(uint16_t) * 3 + 1) {
2888 return -1;
2889 }
2890
2891 uint16_t groupnumber;
2892 uint16_t peer_number;
2893 uint16_t message_number;
2894 memcpy(&groupnumber, data + 1, sizeof(uint16_t));
2895 memcpy(&peer_number, data + 1 + sizeof(uint16_t), sizeof(uint16_t));
2896 memcpy(&message_number, data + 1 + sizeof(uint16_t) * 2, sizeof(uint16_t));
2897 groupnumber = net_ntohs(groupnumber);
2898 peer_number = net_ntohs(peer_number);
2899 message_number = net_ntohs(message_number);
2900
2901 const Group_c *g = get_group_c(g_c, groupnumber);
2902
2903 if (!g) {
2904 return -1;
2905 }
2906
2907 if (!type_uses_lossy(g->type)) {
2908 return -1;
2909 }
2910
2911 const int index = friend_in_connections(g, friendcon_id);
2912
2913 if (index == -1) {
2914 return -1;
2915 }
2916
2917 if (peer_number == g->peer_number) {
2918 return -1;
2919 }
2920
2921 const int peer_index = get_peer_index(g, peer_number);
2922
2923 if (peer_index == -1) {
2924 return -1;
2925 }
2926
2927 if (lossy_packet_not_received(g, peer_index, message_number) != 0) {
2928 return -1;
2929 }
2930
2931 const uint8_t *lossy_data = data + 1 + sizeof(uint16_t) * 3;
2932 uint16_t lossy_length = length - (1 + sizeof(uint16_t) * 3);
2933 const uint8_t message_id = lossy_data[0];
2934 ++lossy_data;
2935 --lossy_length;
2936
2937 send_lossy_all_connections(g_c, g, data + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), index);
2938
2939 if (!g_c->lossy_packethandlers[message_id].function) {
2940 return -1;
2941 }
2942
2943 if (g_c->lossy_packethandlers[message_id].function(g->object, groupnumber, peer_index, g->group[peer_index].object,
2944 lossy_data, lossy_length) == -1) {
2945 return -1;
2946 }
2947
2948 return 0;
2949 }
2950
2951 /* Set the object that is tied to the group chat.
2952 *
2953 * return 0 on success.
2954 * return -1 on failure
2955 */
group_set_object(const Group_Chats * g_c,uint32_t groupnumber,void * object)2956 int group_set_object(const Group_Chats *g_c, uint32_t groupnumber, void *object)
2957 {
2958 Group_c *g = get_group_c(g_c, groupnumber);
2959
2960 if (!g) {
2961 return -1;
2962 }
2963
2964 g->object = object;
2965 return 0;
2966 }
2967
2968 /* Set the object that is tied to the group peer.
2969 *
2970 * return 0 on success.
2971 * return -1 on failure
2972 */
group_peer_set_object(const Group_Chats * g_c,uint32_t groupnumber,uint32_t peernumber,void * object)2973 int group_peer_set_object(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, void *object)
2974 {
2975 const Group_c *g = get_group_c(g_c, groupnumber);
2976
2977 if (!g) {
2978 return -1;
2979 }
2980
2981 if (peernumber >= g->numpeers) {
2982 return -1;
2983 }
2984
2985 g->group[peernumber].object = object;
2986 return 0;
2987 }
2988
2989 /* Return the object tied to the group chat previously set by group_set_object.
2990 *
2991 * return NULL on failure.
2992 * return object on success.
2993 */
group_get_object(const Group_Chats * g_c,uint32_t groupnumber)2994 void *group_get_object(const Group_Chats *g_c, uint32_t groupnumber)
2995 {
2996 const Group_c *g = get_group_c(g_c, groupnumber);
2997
2998 if (!g) {
2999 return nullptr;
3000 }
3001
3002 return g->object;
3003 }
3004
3005 /* Return the object tied to the group chat peer previously set by group_peer_set_object.
3006 *
3007 * return NULL on failure.
3008 * return object on success.
3009 */
group_peer_get_object(const Group_Chats * g_c,uint32_t groupnumber,uint32_t peernumber)3010 void *group_peer_get_object(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber)
3011 {
3012 const Group_c *g = get_group_c(g_c, groupnumber);
3013
3014 if (!g) {
3015 return nullptr;
3016 }
3017
3018 if (peernumber >= g->numpeers) {
3019 return nullptr;
3020 }
3021
3022 return g->group[peernumber].object;
3023 }
3024
3025 /* Interval in seconds to send ping messages */
3026 #define GROUP_PING_INTERVAL 20
3027
ping_groupchat(Group_Chats * g_c,uint32_t groupnumber)3028 static bool ping_groupchat(Group_Chats *g_c, uint32_t groupnumber)
3029 {
3030 Group_c *g = get_group_c(g_c, groupnumber);
3031
3032 if (!g) {
3033 return false;
3034 }
3035
3036 if (mono_time_is_timeout(g_c->mono_time, g->last_sent_ping, GROUP_PING_INTERVAL)) {
3037 if (group_ping_send(g_c, groupnumber)) {
3038 g->last_sent_ping = mono_time_get(g_c->mono_time);
3039 }
3040 }
3041
3042 return true;
3043 }
3044
3045 /* Seconds of inactivity after which to freeze a peer */
3046 #define FREEZE_TIMEOUT (GROUP_PING_INTERVAL * 3)
3047
groupchat_freeze_timedout(Group_Chats * g_c,uint32_t groupnumber,void * userdata)3048 static bool groupchat_freeze_timedout(Group_Chats *g_c, uint32_t groupnumber, void *userdata)
3049 {
3050 Group_c *g = get_group_c(g_c, groupnumber);
3051
3052 if (!g) {
3053 return false;
3054 }
3055
3056 for (uint32_t i = 0; i < g->numpeers; ++i) {
3057 if (g->group[i].peer_number == g->peer_number) {
3058 continue;
3059 }
3060
3061 if (mono_time_is_timeout(g_c->mono_time, g->group[i].last_active, FREEZE_TIMEOUT)) {
3062 freeze_peer(g_c, groupnumber, i, userdata);
3063 }
3064 }
3065
3066 if (g->numpeers <= 1) {
3067 g->title_fresh = false;
3068 }
3069
3070 return true;
3071 }
3072
3073 /* Push non-empty slots to start. */
squash_connections(Group_c * g)3074 static void squash_connections(Group_c *g)
3075 {
3076 uint16_t i = 0;
3077
3078 for (uint16_t j = 0; j < MAX_GROUP_CONNECTIONS; ++j) {
3079 if (g->connections[j].type != GROUPCHAT_CONNECTION_NONE) {
3080 g->connections[i] = g->connections[j];
3081 ++i;
3082 }
3083 }
3084
3085 for (; i < MAX_GROUP_CONNECTIONS; ++i) {
3086 g->connections[i].type = GROUPCHAT_CONNECTION_NONE;
3087 }
3088 }
3089
3090 #define MIN_EMPTY_CONNECTIONS (1 + MAX_GROUP_CONNECTIONS / 10)
3091
3092 /* Remove old connections as necessary to ensure we have space for new
3093 * connections. This invalidates connections array indices (which is
3094 * why we do this periodically rather than on adding a connection).
3095 */
clean_connections(Group_Chats * g_c,Group_c * g)3096 static void clean_connections(Group_Chats *g_c, Group_c *g)
3097 {
3098 uint16_t to_clear = MIN_EMPTY_CONNECTIONS;
3099
3100 for (uint16_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
3101 if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) {
3102 --to_clear;
3103
3104 if (to_clear == 0) {
3105 break;
3106 }
3107 }
3108 }
3109
3110 for (; to_clear > 0; --to_clear) {
3111 // Remove a connection. Prefer non-closest connections, and given
3112 // that prefer non-online connections, and given that prefer earlier
3113 // slots.
3114 uint16_t i = 0;
3115
3116 while (i < MAX_GROUP_CONNECTIONS
3117 && (g->connections[i].type != GROUPCHAT_CONNECTION_CONNECTING
3118 || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST))) {
3119 ++i;
3120 }
3121
3122 if (i == MAX_GROUP_CONNECTIONS) {
3123 i = 0;
3124
3125 while (i < MAX_GROUP_CONNECTIONS - to_clear
3126 && (g->connections[i].type != GROUPCHAT_CONNECTION_ONLINE
3127 || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST))) {
3128 ++i;
3129 }
3130 }
3131
3132 if (g->connections[i].type != GROUPCHAT_CONNECTION_NONE) {
3133 remove_connection(g_c, g, i);
3134 }
3135 }
3136
3137 squash_connections(g);
3138 }
3139
3140 /* Send current name (set in messenger) to all online groups.
3141 */
send_name_all_groups(Group_Chats * g_c)3142 void send_name_all_groups(Group_Chats *g_c)
3143 {
3144 for (uint16_t i = 0; i < g_c->num_chats; ++i) {
3145 Group_c *g = get_group_c(g_c, i);
3146
3147 if (!g) {
3148 continue;
3149 }
3150
3151 if (g->status == GROUPCHAT_STATUS_CONNECTED) {
3152 group_name_send(g_c, i, g_c->m->name, g_c->m->name_length);
3153 g->need_send_name = false;
3154 }
3155 }
3156 }
3157
3158 #define SAVED_PEER_SIZE_CONSTANT (2 * CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint16_t) + sizeof(uint64_t) + 1)
3159
saved_peer_size(const Group_Peer * peer)3160 static uint32_t saved_peer_size(const Group_Peer *peer)
3161 {
3162 return SAVED_PEER_SIZE_CONSTANT + peer->nick_len;
3163 }
3164
save_peer(const Group_Peer * peer,uint8_t * data)3165 static uint8_t *save_peer(const Group_Peer *peer, uint8_t *data)
3166 {
3167 memcpy(data, peer->real_pk, CRYPTO_PUBLIC_KEY_SIZE);
3168 data += CRYPTO_PUBLIC_KEY_SIZE;
3169
3170 memcpy(data, peer->temp_pk, CRYPTO_PUBLIC_KEY_SIZE);
3171 data += CRYPTO_PUBLIC_KEY_SIZE;
3172
3173 host_to_lendian_bytes16(data, peer->peer_number);
3174 data += sizeof(uint16_t);
3175
3176 host_to_lendian_bytes64(data, peer->last_active);
3177 data += sizeof(uint64_t);
3178
3179 *data = peer->nick_len;
3180 ++data;
3181
3182 memcpy(data, peer->nick, peer->nick_len);
3183 data += peer->nick_len;
3184
3185 return data;
3186 }
3187
3188 #define SAVED_CONF_SIZE_CONSTANT (1 + GROUP_ID_LENGTH + sizeof(uint32_t) \
3189 + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + 1)
3190
saved_conf_size(const Group_c * g)3191 static uint32_t saved_conf_size(const Group_c *g)
3192 {
3193 uint32_t len = SAVED_CONF_SIZE_CONSTANT + g->title_len;
3194
3195 for (uint32_t j = 0; j < g->numpeers + g->numfrozen; ++j) {
3196 const Group_Peer *peer = (j < g->numpeers) ? &g->group[j] : &g->frozen[j - g->numpeers];
3197
3198 if (id_equal(peer->real_pk, g->real_pk)) {
3199 continue;
3200 }
3201
3202 len += saved_peer_size(peer);
3203 }
3204
3205 return len;
3206 }
3207
3208 /* Save a future message number. The save will remain valid until we have sent
3209 * this many more messages. */
3210 #define SAVE_OFFSET_MESSAGE_NUMBER (1 << 16)
3211 #define SAVE_OFFSET_LOSSY_MESSAGE_NUMBER (1 << 13)
3212
save_conf(const Group_c * g,uint8_t * data)3213 static uint8_t *save_conf(const Group_c *g, uint8_t *data)
3214 {
3215 *data = g->type;
3216 ++data;
3217
3218 memcpy(data, g->id, GROUP_ID_LENGTH);
3219 data += GROUP_ID_LENGTH;
3220
3221 host_to_lendian_bytes32(data, g->message_number + SAVE_OFFSET_MESSAGE_NUMBER);
3222 data += sizeof(uint32_t);
3223
3224 host_to_lendian_bytes16(data, g->lossy_message_number + SAVE_OFFSET_LOSSY_MESSAGE_NUMBER);
3225 data += sizeof(uint16_t);
3226
3227 host_to_lendian_bytes16(data, g->peer_number);
3228 data += sizeof(uint16_t);
3229
3230 uint8_t *const numsaved_location = data;
3231 data += sizeof(uint32_t);
3232
3233 *data = g->title_len;
3234 ++data;
3235
3236 memcpy(data, g->title, g->title_len);
3237 data += g->title_len;
3238
3239 uint32_t numsaved = 0;
3240
3241 for (uint32_t j = 0; j < g->numpeers + g->numfrozen; ++j) {
3242 const Group_Peer *peer = (j < g->numpeers) ? &g->group[j] : &g->frozen[j - g->numpeers];
3243
3244 if (id_equal(peer->real_pk, g->real_pk)) {
3245 continue;
3246 }
3247
3248 data = save_peer(peer, data);
3249 ++numsaved;
3250 }
3251
3252 host_to_lendian_bytes32(numsaved_location, numsaved);
3253
3254 return data;
3255 }
3256
conferences_section_size(const Group_Chats * g_c)3257 static uint32_t conferences_section_size(const Group_Chats *g_c)
3258 {
3259 uint32_t len = 0;
3260
3261 for (uint16_t i = 0; i < g_c->num_chats; ++i) {
3262 const Group_c *g = get_group_c(g_c, i);
3263
3264 if (!g || g->status != GROUPCHAT_STATUS_CONNECTED) {
3265 continue;
3266 }
3267
3268 len += saved_conf_size(g);
3269 }
3270
3271 return len;
3272 }
3273
conferences_size(const Group_Chats * g_c)3274 uint32_t conferences_size(const Group_Chats *g_c)
3275 {
3276 return 2 * sizeof(uint32_t) + conferences_section_size(g_c);
3277 }
3278
conferences_save(const Group_Chats * g_c,uint8_t * data)3279 uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data)
3280 {
3281 const uint32_t len = conferences_section_size(g_c);
3282 data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_CONFERENCES);
3283
3284 for (uint16_t i = 0; i < g_c->num_chats; ++i) {
3285 const Group_c *g = get_group_c(g_c, i);
3286
3287 if (!g || g->status != GROUPCHAT_STATUS_CONNECTED) {
3288 continue;
3289 }
3290
3291 data = save_conf(g, data);
3292 }
3293
3294 return data;
3295 }
3296
load_conferences(Group_Chats * g_c,const uint8_t * data,uint32_t length)3297 static State_Load_Status load_conferences(Group_Chats *g_c, const uint8_t *data, uint32_t length)
3298 {
3299 const uint8_t *init_data = data;
3300
3301 while (length >= (uint32_t)(data - init_data) + SAVED_CONF_SIZE_CONSTANT) {
3302 const int groupnumber = create_group_chat(g_c);
3303
3304 if (groupnumber == -1) {
3305 return STATE_LOAD_STATUS_ERROR;
3306 }
3307
3308 Group_c *g = &g_c->chats[groupnumber];
3309
3310 g->type = *data;
3311 ++data;
3312
3313 memcpy(g->id, data, GROUP_ID_LENGTH);
3314 data += GROUP_ID_LENGTH;
3315
3316 lendian_bytes_to_host32(&g->message_number, data);
3317 data += sizeof(uint32_t);
3318
3319 lendian_bytes_to_host16(&g->lossy_message_number, data);
3320 data += sizeof(uint16_t);
3321
3322 lendian_bytes_to_host16(&g->peer_number, data);
3323 data += sizeof(uint16_t);
3324
3325 lendian_bytes_to_host32(&g->numfrozen, data);
3326 data += sizeof(uint32_t);
3327
3328 if (g->numfrozen > 0) {
3329 g->frozen = (Group_Peer *)malloc(sizeof(Group_Peer) * g->numfrozen);
3330
3331 if (g->frozen == nullptr) {
3332 return STATE_LOAD_STATUS_ERROR;
3333 }
3334 }
3335
3336 g->title_len = *data;
3337
3338 if (g->title_len > MAX_NAME_LENGTH) {
3339 return STATE_LOAD_STATUS_ERROR;
3340 }
3341
3342 ++data;
3343
3344 if (length < (uint32_t)(data - init_data) + g->title_len) {
3345 return STATE_LOAD_STATUS_ERROR;
3346 }
3347
3348 memcpy(g->title, data, g->title_len);
3349 data += g->title_len;
3350
3351 for (uint32_t j = 0; j < g->numfrozen; ++j) {
3352 if (length < (uint32_t)(data - init_data) + SAVED_PEER_SIZE_CONSTANT) {
3353 return STATE_LOAD_STATUS_ERROR;
3354 }
3355
3356 Group_Peer *peer = &g->frozen[j];
3357 memset(peer, 0, sizeof(Group_Peer));
3358
3359 id_copy(peer->real_pk, data);
3360 data += CRYPTO_PUBLIC_KEY_SIZE;
3361 id_copy(peer->temp_pk, data);
3362 data += CRYPTO_PUBLIC_KEY_SIZE;
3363
3364 lendian_bytes_to_host16(&peer->peer_number, data);
3365 data += sizeof(uint16_t);
3366
3367 lendian_bytes_to_host64(&peer->last_active, data);
3368 data += sizeof(uint64_t);
3369
3370 peer->nick_len = *data;
3371
3372 if (peer->nick_len > MAX_NAME_LENGTH) {
3373 return STATE_LOAD_STATUS_ERROR;
3374 }
3375
3376 ++data;
3377
3378 if (length < (uint32_t)(data - init_data) + peer->nick_len) {
3379 return STATE_LOAD_STATUS_ERROR;
3380 }
3381
3382 memcpy(peer->nick, data, peer->nick_len);
3383 data += peer->nick_len;
3384
3385 // NOTE: this relies on friends being loaded before conferences.
3386 peer->is_friend = (getfriend_id(g_c->m, peer->real_pk) != -1);
3387 }
3388
3389 if (g->numfrozen > g->maxfrozen) {
3390 g->maxfrozen = g->numfrozen;
3391 }
3392
3393 g->status = GROUPCHAT_STATUS_CONNECTED;
3394 memcpy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE);
3395 const int peer_index = addpeer(g_c, groupnumber, g->real_pk, dht_get_self_public_key(g_c->m->dht), g->peer_number,
3396 nullptr, true, false);
3397
3398 if (peer_index == -1) {
3399 return STATE_LOAD_STATUS_ERROR;
3400 }
3401
3402 setnick(g_c, groupnumber, peer_index, g_c->m->name, g_c->m->name_length, nullptr, false);
3403 }
3404
3405 return STATE_LOAD_STATUS_CONTINUE;
3406 }
3407
conferences_load_state_section(Group_Chats * g_c,const uint8_t * data,uint32_t length,uint16_t type,State_Load_Status * status)3408 bool conferences_load_state_section(Group_Chats *g_c, const uint8_t *data, uint32_t length, uint16_t type,
3409 State_Load_Status *status)
3410 {
3411 if (type != STATE_TYPE_CONFERENCES) {
3412 return false;
3413 }
3414
3415 *status = load_conferences(g_c, data, length);
3416 return true;
3417 }
3418
3419
3420 /* Create new groupchat instance. */
new_groupchats(Mono_Time * mono_time,Messenger * m)3421 Group_Chats *new_groupchats(Mono_Time *mono_time, Messenger *m)
3422 {
3423 if (!m) {
3424 return nullptr;
3425 }
3426
3427 Group_Chats *temp = (Group_Chats *)calloc(1, sizeof(Group_Chats));
3428
3429 if (temp == nullptr) {
3430 return nullptr;
3431 }
3432
3433 temp->mono_time = mono_time;
3434 temp->m = m;
3435 temp->fr_c = m->fr_c;
3436 m->conferences_object = temp;
3437 m_callback_conference_invite(m, &handle_friend_invite_packet);
3438
3439 set_global_status_callback(m->fr_c, &g_handle_any_status, temp);
3440
3441 return temp;
3442 }
3443
3444 /* main groupchats loop. */
do_groupchats(Group_Chats * g_c,void * userdata)3445 void do_groupchats(Group_Chats *g_c, void *userdata)
3446 {
3447 for (uint16_t i = 0; i < g_c->num_chats; ++i) {
3448 Group_c *g = get_group_c(g_c, i);
3449
3450 if (!g) {
3451 continue;
3452 }
3453
3454 if (g->status == GROUPCHAT_STATUS_CONNECTED) {
3455 connect_to_closest(g_c, i, userdata);
3456 ping_groupchat(g_c, i);
3457 groupchat_freeze_timedout(g_c, i, userdata);
3458 clean_connections(g_c, g);
3459
3460 if (g->need_send_name) {
3461 group_name_send(g_c, i, g_c->m->name, g_c->m->name_length);
3462 g->need_send_name = false;
3463 }
3464 }
3465 }
3466
3467 // TODO(irungentoo):
3468 }
3469
3470 /* Free everything related with group chats. */
kill_groupchats(Group_Chats * g_c)3471 void kill_groupchats(Group_Chats *g_c)
3472 {
3473 for (uint16_t i = 0; i < g_c->num_chats; ++i) {
3474 del_groupchat(g_c, i, false);
3475 }
3476
3477 m_callback_conference_invite(g_c->m, nullptr);
3478 set_global_status_callback(g_c->m->fr_c, nullptr, nullptr);
3479 g_c->m->conferences_object = nullptr;
3480 free(g_c);
3481 }
3482
3483 /* Return the number of chats in the instance m.
3484 * You should use this to determine how much memory to allocate
3485 * for copy_chatlist.
3486 */
count_chatlist(const Group_Chats * g_c)3487 uint32_t count_chatlist(const Group_Chats *g_c)
3488 {
3489 uint32_t ret = 0;
3490
3491 for (uint16_t i = 0; i < g_c->num_chats; ++i) {
3492 if (g_c->chats[i].status != GROUPCHAT_STATUS_NONE) {
3493 ++ret;
3494 }
3495 }
3496
3497 return ret;
3498 }
3499
3500 /* Copy a list of valid chat IDs into the array out_list.
3501 * If out_list is NULL, returns 0.
3502 * Otherwise, returns the number of elements copied.
3503 * If the array was too small, the contents
3504 * of out_list will be truncated to list_size. */
copy_chatlist(const Group_Chats * g_c,uint32_t * out_list,uint32_t list_size)3505 uint32_t copy_chatlist(const Group_Chats *g_c, uint32_t *out_list, uint32_t list_size)
3506 {
3507 if (!out_list) {
3508 return 0;
3509 }
3510
3511 if (g_c->num_chats == 0) {
3512 return 0;
3513 }
3514
3515 uint32_t ret = 0;
3516
3517 for (uint16_t i = 0; i < g_c->num_chats; ++i) {
3518 if (ret >= list_size) {
3519 break; /* Abandon ship */
3520 }
3521
3522 if (g_c->chats[i].status > GROUPCHAT_STATUS_NONE) {
3523 out_list[ret] = i;
3524 ++ret;
3525 }
3526 }
3527
3528 return ret;
3529 }
3530