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