summaryrefslogtreecommitdiff
path: root/toxcore/group.c
diff options
context:
space:
mode:
Diffstat (limited to 'toxcore/group.c')
-rw-r--r--toxcore/group.c2322
1 files changed, 2322 insertions, 0 deletions
diff --git a/toxcore/group.c b/toxcore/group.c
new file mode 100644
index 00000000..c688ccff
--- /dev/null
+++ b/toxcore/group.c
@@ -0,0 +1,2322 @@
1/* group.c
2 *
3 * Slightly better groupchats implementation.
4 *
5 * Copyright (C) 2014 Tox project All Rights Reserved.
6 *
7 * This file is part of Tox.
8 *
9 * Tox is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * Tox is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#include "group.h"
29#include "util.h"
30
31/* return 1 if the groupnumber is not valid.
32 * return 0 if the groupnumber is valid.
33 */
34static uint8_t groupnumber_not_valid(const Group_Chats *g_c, int groupnumber)
35{
36 if ((unsigned int)groupnumber >= g_c->num_chats)
37 return 1;
38
39 if (g_c->chats == NULL)
40 return 1;
41
42 if (g_c->chats[groupnumber].status == GROUPCHAT_STATUS_NONE)
43 return 1;
44
45 return 0;
46}
47
48
49/* Set the size of the groupchat list to num.
50 *
51 * return -1 if realloc fails.
52 * return 0 if it succeeds.
53 */
54static int realloc_groupchats(Group_Chats *g_c, uint32_t num)
55{
56 if (num == 0) {
57 free(g_c->chats);
58 g_c->chats = NULL;
59 return 0;
60 }
61
62 Group_c *newgroup_chats = realloc(g_c->chats, num * sizeof(Group_c));
63
64 if (newgroup_chats == NULL)
65 return -1;
66
67 g_c->chats = newgroup_chats;
68 return 0;
69}
70
71
72/* Create a new empty groupchat connection.
73 *
74 * return -1 on failure.
75 * return groupnumber on success.
76 */
77static int create_group_chat(Group_Chats *g_c)
78{
79 uint32_t i;
80
81 for (i = 0; i < g_c->num_chats; ++i) {
82 if (g_c->chats[i].status == GROUPCHAT_STATUS_NONE)
83 return i;
84 }
85
86 int id = -1;
87
88 if (realloc_groupchats(g_c, g_c->num_chats + 1) == 0) {
89 id = g_c->num_chats;
90 ++g_c->num_chats;
91 memset(&(g_c->chats[id]), 0, sizeof(Group_c));
92 }
93
94 return id;
95}
96
97
98/* Wipe a groupchat.
99 *
100 * return -1 on failure.
101 * return 0 on success.
102 */
103static int wipe_group_chat(Group_Chats *g_c, int groupnumber)
104{
105 if (groupnumber_not_valid(g_c, groupnumber))
106 return -1;
107
108 uint32_t i;
109 memset(&(g_c->chats[groupnumber]), 0 , sizeof(Group_c));
110
111 for (i = g_c->num_chats; i != 0; --i) {
112 if (g_c->chats[i - 1].status != GROUPCHAT_STATUS_NONE)
113 break;
114 }
115
116 if (g_c->num_chats != i) {
117 g_c->num_chats = i;
118 realloc_groupchats(g_c, g_c->num_chats);
119 }
120
121 return 0;
122}
123
124static Group_c *get_group_c(const Group_Chats *g_c, int groupnumber)
125{
126 if (groupnumber_not_valid(g_c, groupnumber))
127 return 0;
128
129 return &g_c->chats[groupnumber];
130}
131
132/*
133 * check if peer with real_pk is in peer array.
134 *
135 * return peer index if peer is in chat.
136 * return -1 if peer is not in chat.
137 *
138 * TODO: make this more efficient.
139 */
140
141static int peer_in_chat(const Group_c *chat, const uint8_t *real_pk)
142{
143 uint32_t i;
144
145 for (i = 0; i < chat->numpeers; ++i)
146 if (id_equal(chat->group[i].real_pk, real_pk))
147 return i;
148
149 return -1;
150}
151
152/*
153 * check if group with identifier is in group array.
154 *
155 * return group number if peer is in list.
156 * return -1 if group is not in list.
157 *
158 * TODO: make this more efficient and maybe use constant time comparisons?
159 */
160static int get_group_num(const Group_Chats *g_c, const uint8_t *identifier)
161{
162 uint32_t i;
163
164 for (i = 0; i < g_c->num_chats; ++i)
165 if (memcmp(g_c->chats[i].identifier, identifier, GROUP_IDENTIFIER_LENGTH) == 0)
166 return i;
167
168 return -1;
169}
170
171/*
172 * check if peer with peer_number is in peer array.
173 *
174 * return peer number if peer is in chat.
175 * return -1 if peer is not in chat.
176 *
177 * TODO: make this more efficient.
178 */
179static int get_peer_index(Group_c *g, uint16_t peer_number)
180{
181 uint32_t i;
182
183 for (i = 0; i < g->numpeers; ++i)
184 if (g->group[i].peer_number == peer_number)
185 return i;
186
187 return -1;
188}
189
190
191static uint64_t calculate_comp_value(const uint8_t *pk1, const uint8_t *pk2)
192{
193 uint64_t cmp1 = 0, cmp2 = 0;
194
195 unsigned int i;
196
197 for (i = 0; i < sizeof(uint64_t); ++i) {
198 cmp1 = (cmp1 << 8) + (uint64_t)pk1[i];
199 cmp2 = (cmp2 << 8) + (uint64_t)pk2[i];
200 }
201
202 return (cmp1 - cmp2);
203}
204
205enum {
206 GROUPCHAT_CLOSEST_NONE,
207 GROUPCHAT_CLOSEST_ADDED,
208 GROUPCHAT_CLOSEST_REMOVED
209};
210
211static int friend_in_close(Group_c *g, int friendcon_id);
212static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, int groupnumber, uint8_t closest, uint8_t lock);
213
214static int add_to_closest(Group_Chats *g_c, int groupnumber, const uint8_t *real_pk, const uint8_t *temp_pk)
215{
216 Group_c *g = get_group_c(g_c, groupnumber);
217
218 if (!g)
219 return -1;
220
221 if (memcmp(g->real_pk, real_pk, crypto_box_PUBLICKEYBYTES) == 0)
222 return -1;
223
224 unsigned int i;
225 unsigned int index = DESIRED_CLOSE_CONNECTIONS;
226
227 for (i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) {
228 if (g->closest_peers[i].entry && memcmp(real_pk, g->closest_peers[i].real_pk, crypto_box_PUBLICKEYBYTES) == 0) {
229 return 0;
230 }
231 }
232
233 for (i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) {
234 if (g->closest_peers[i].entry == 0) {
235 index = i;
236 break;
237 }
238 }
239
240 if (index == DESIRED_CLOSE_CONNECTIONS) {
241 uint64_t comp_val = calculate_comp_value(g->real_pk, real_pk);
242 uint64_t comp_d = 0;
243
244 for (i = 0; i < (DESIRED_CLOSE_CONNECTIONS / 2); ++i) {
245 uint64_t comp;
246 comp = calculate_comp_value(g->real_pk, g->closest_peers[i].real_pk);
247
248 if (comp > comp_val && comp > comp_d) {
249 index = i;
250 comp_d = comp;
251 }
252 }
253
254 comp_val = calculate_comp_value(real_pk, g->real_pk);
255
256 for (i = (DESIRED_CLOSE_CONNECTIONS / 2); i < DESIRED_CLOSE_CONNECTIONS; ++i) {
257 uint64_t comp = calculate_comp_value(g->closest_peers[i].real_pk, g->real_pk);
258
259 if (comp > comp_val && comp > comp_d) {
260 index = i;
261 comp_d = comp;
262 }
263 }
264 }
265
266 if (index == DESIRED_CLOSE_CONNECTIONS) {
267 return -1;
268 }
269
270 uint8_t old_real_pk[crypto_box_PUBLICKEYBYTES];
271 uint8_t old_temp_pk[crypto_box_PUBLICKEYBYTES];
272 uint8_t old = 0;
273
274 if (g->closest_peers[index].entry) {
275 memcpy(old_real_pk, g->closest_peers[index].real_pk, crypto_box_PUBLICKEYBYTES);
276 memcpy(old_temp_pk, g->closest_peers[index].temp_pk, crypto_box_PUBLICKEYBYTES);
277 old = 1;
278 }
279
280 g->closest_peers[index].entry = 1;
281 memcpy(g->closest_peers[index].real_pk, real_pk, crypto_box_PUBLICKEYBYTES);
282 memcpy(g->closest_peers[index].temp_pk, temp_pk, crypto_box_PUBLICKEYBYTES);
283
284 if (old) {
285 add_to_closest(g_c, groupnumber, old_real_pk, old_temp_pk);
286 }
287
288 if (!g->changed)
289 g->changed = GROUPCHAT_CLOSEST_ADDED;
290
291 return 0;
292}
293
294static unsigned int pk_in_closest_peers(Group_c *g, uint8_t *real_pk)
295{
296 unsigned int i;
297
298 for (i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) {
299 if (!g->closest_peers[i].entry)
300 continue;
301
302 if (memcmp(g->closest_peers[i].real_pk, real_pk, crypto_box_PUBLICKEYBYTES) == 0)
303 return 1;
304
305 }
306
307 return 0;
308}
309
310static int send_packet_online(Friend_Connections *fr_c, int friendcon_id, uint16_t group_num, uint8_t *identifier);
311
312static int connect_to_closest(Group_Chats *g_c, int groupnumber)
313{
314 Group_c *g = get_group_c(g_c, groupnumber);
315
316 if (!g)
317 return -1;
318
319 if (!g->changed)
320 return 0;
321
322 unsigned int i;
323
324 if (g->changed == GROUPCHAT_CLOSEST_REMOVED) {
325 for (i = 0; i < g->numpeers; ++i) {
326 add_to_closest(g_c, groupnumber, g->group[i].real_pk, g->group[i].temp_pk);
327 }
328 }
329
330 for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
331 if (g->close[i].type == GROUPCHAT_CLOSE_NONE)
332 continue;
333
334 if (!g->close[i].closest)
335 continue;
336
337 uint8_t real_pk[crypto_box_PUBLICKEYBYTES];
338 uint8_t dht_temp_pk[crypto_box_PUBLICKEYBYTES];
339 get_friendcon_public_keys(real_pk, dht_temp_pk, g_c->fr_c, g->close[i].number);
340
341 if (!pk_in_closest_peers(g, real_pk)) {
342 g->close[i].type = GROUPCHAT_CLOSE_NONE;
343 kill_friend_connection(g_c->fr_c, g->close[i].number);
344 }
345 }
346
347 for (i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) {
348 if (!g->closest_peers[i].entry)
349 continue;
350
351 int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, g->closest_peers[i].real_pk);
352
353 uint8_t lock = 1;
354
355 if (friendcon_id == -1) {
356 friendcon_id = new_friend_connection(g_c->fr_c, g->closest_peers[i].real_pk);
357 lock = 0;
358
359 if (friendcon_id == -1) {
360 continue;
361 }
362
363 set_dht_temp_pk(g_c->fr_c, friendcon_id, g->closest_peers[i].temp_pk);
364 }
365
366 add_conn_to_groupchat(g_c, friendcon_id, groupnumber, 1, lock);
367
368 if (friend_con_connected(g_c->fr_c, friendcon_id) == FRIENDCONN_STATUS_CONNECTED) {
369 send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->identifier);
370 }
371 }
372
373 g->changed = GROUPCHAT_CLOSEST_NONE;
374
375 return 0;
376}
377
378/*
379 * Add a peer to the group chat.
380 *
381 * return peer_index if success or peer already in chat.
382 * return -1 if error.
383 */
384static int addpeer(Group_Chats *g_c, int groupnumber, const uint8_t *real_pk, const uint8_t *temp_pk,
385 uint16_t peer_number)
386{
387 Group_c *g = get_group_c(g_c, groupnumber);
388
389 if (!g)
390 return -1;
391
392 //TODO
393 int peer_index = peer_in_chat(g, real_pk);
394
395 if (peer_index != -1) {
396 id_copy(g->group[peer_index].temp_pk, temp_pk);
397
398 if (g->group[peer_index].peer_number != peer_number)
399 return -1;
400
401 return peer_index;
402 }
403
404 peer_index = get_peer_index(g, peer_number);
405
406 if (peer_index != -1)
407 return -1;
408
409 Group_Peer *temp;
410 temp = realloc(g->group, sizeof(Group_Peer) * (g->numpeers + 1));
411
412 if (temp == NULL)
413 return -1;
414
415 memset(&(temp[g->numpeers]), 0, sizeof(Group_Peer));
416 g->group = temp;
417
418 id_copy(g->group[g->numpeers].real_pk, real_pk);
419 id_copy(g->group[g->numpeers].temp_pk, temp_pk);
420 g->group[g->numpeers].peer_number = peer_number;
421
422 g->group[g->numpeers].last_recv = unix_time();
423 ++g->numpeers;
424
425 add_to_closest(g_c, groupnumber, real_pk, temp_pk);
426
427 if (g_c->peer_namelistchange)
428 g_c->peer_namelistchange(g_c->m, groupnumber, g->numpeers - 1, CHAT_CHANGE_PEER_ADD,
429 g_c->group_namelistchange_userdata);
430
431 if (g->peer_on_join)
432 g->peer_on_join(g->object, groupnumber, g->numpeers - 1);
433
434 return (g->numpeers - 1);
435}
436
437static int remove_close_conn(Group_Chats *g_c, int groupnumber, int friendcon_id)
438{
439 Group_c *g = get_group_c(g_c, groupnumber);
440
441 if (!g)
442 return -1;
443
444 uint32_t i;
445
446 for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
447 if (g->close[i].type == GROUPCHAT_CLOSE_NONE)
448 continue;
449
450 if (g->close[i].number == (unsigned int)friendcon_id) {
451 g->close[i].type = GROUPCHAT_CLOSE_NONE;
452 kill_friend_connection(g_c->fr_c, friendcon_id);
453 return 0;
454 }
455 }
456
457 return -1;
458}
459
460
461/*
462 * Delete a peer from the group chat.
463 *
464 * return 0 if success
465 * return -1 if error.
466 */
467static int delpeer(Group_Chats *g_c, int groupnumber, int peer_index)
468{
469 Group_c *g = get_group_c(g_c, groupnumber);
470
471 if (!g)
472 return -1;
473
474 uint32_t i;
475
476 for (i = 0; i < DESIRED_CLOSE_CONNECTIONS; ++i) { /* If peer is in closest_peers list, remove it. */
477 if (g->closest_peers[i].entry && id_equal(g->closest_peers[i].real_pk, g->group[peer_index].real_pk)) {
478 g->closest_peers[i].entry = 0;
479 g->changed = GROUPCHAT_CLOSEST_REMOVED;
480 break;
481 }
482 }
483
484 int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, g->group[peer_index].real_pk);
485
486 if (friendcon_id != -1) {
487 remove_close_conn(g_c, groupnumber, friendcon_id);
488 }
489
490 Group_Peer *temp;
491 --g->numpeers;
492
493 void *peer_object = g->group[peer_index].object;
494
495 if (g->numpeers == 0) {
496 free(g->group);
497 g->group = NULL;
498 } else {
499 if (g->numpeers != (uint32_t)peer_index)
500 memcpy(&g->group[peer_index], &g->group[g->numpeers], sizeof(Group_Peer));
501
502 temp = realloc(g->group, sizeof(Group_Peer) * (g->numpeers));
503
504 if (temp == NULL)
505 return -1;
506
507 g->group = temp;
508 }
509
510 if (g_c->peer_namelistchange)
511 g_c->peer_namelistchange(g_c->m, groupnumber, peer_index, CHAT_CHANGE_PEER_DEL, g_c->group_namelistchange_userdata);
512
513 if (g->peer_on_leave)
514 g->peer_on_leave(g->object, groupnumber, peer_index, peer_object);
515
516 return 0;
517}
518
519static int setnick(Group_Chats *g_c, int groupnumber, int peer_index, const uint8_t *nick, uint16_t nick_len)
520{
521 if (nick_len > MAX_NAME_LENGTH)
522 return -1;
523
524 Group_c *g = get_group_c(g_c, groupnumber);
525
526 if (!g)
527 return -1;
528
529 /* same name as already stored? */
530 if (g->group[peer_index].nick_len == nick_len)
531 if (nick_len == 0 || !memcmp(g->group[peer_index].nick, nick, nick_len))
532 return 0;
533
534 if (nick_len)
535 memcpy(g->group[peer_index].nick, nick, nick_len);
536
537 g->group[peer_index].nick_len = nick_len;
538
539 if (g_c->peer_namelistchange)
540 g_c->peer_namelistchange(g_c->m, groupnumber, peer_index, CHAT_CHANGE_PEER_NAME, g_c->group_namelistchange_userdata);
541
542 return 0;
543}
544
545static int settitle(Group_Chats *g_c, int groupnumber, int peer_index, const uint8_t *title, uint8_t title_len)
546{
547 if (title_len > MAX_NAME_LENGTH || title_len == 0)
548 return -1;
549
550 Group_c *g = get_group_c(g_c, groupnumber);
551
552 if (!g)
553 return -1;
554
555 /* same as already set? */
556 if (g->title_len == title_len && !memcmp(g->title, title, title_len))
557 return 0;
558
559 memcpy(g->title, title, title_len);
560 g->title_len = title_len;
561
562 if (g_c->title_callback)
563 g_c->title_callback(g_c->m, groupnumber, peer_index, title, title_len, g_c->title_callback_userdata);
564
565 return 0;
566}
567
568static void set_conns_type_close(Group_Chats *g_c, int groupnumber, int friendcon_id, uint8_t type)
569{
570 Group_c *g = get_group_c(g_c, groupnumber);
571
572 if (!g)
573 return;
574
575 uint32_t i;
576
577 for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
578 if (g->close[i].type == GROUPCHAT_CLOSE_NONE)
579 continue;
580
581 if (g->close[i].number != (unsigned int)friendcon_id)
582 continue;
583
584 if (type == GROUPCHAT_CLOSE_ONLINE) {
585 send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->identifier);
586 } else {
587 g->close[i].type = type;
588 }
589 }
590}
591/* Set the type for all close connections with friendcon_id */
592static void set_conns_status_groups(Group_Chats *g_c, int friendcon_id, uint8_t type)
593{
594 uint32_t i;
595
596 for (i = 0; i < g_c->num_chats; ++i) {
597 set_conns_type_close(g_c, i, friendcon_id, type);
598 }
599}
600
601static int handle_status(void *object, int friendcon_id, uint8_t status)
602{
603 Group_Chats *g_c = object;
604
605 if (status) { /* Went online */
606 set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CLOSE_ONLINE);
607 } else { /* Went offline */
608 set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CLOSE_CONNECTION);
609 //TODO remove timedout connections?
610 }
611
612 return 0;
613}
614
615static int handle_packet(void *object, int friendcon_id, uint8_t *data, uint16_t length);
616static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uint16_t length);
617
618/* Add friend to group chat.
619 *
620 * return close index on success
621 * return -1 on failure.
622 */
623static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, int groupnumber, uint8_t closest, uint8_t lock)
624{
625 Group_c *g = get_group_c(g_c, groupnumber);
626
627 if (!g)
628 return -1;
629
630 uint16_t i, ind = MAX_GROUP_CONNECTIONS;
631
632 for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
633 if (g->close[i].type == GROUPCHAT_CLOSE_NONE) {
634 ind = i;
635 continue;
636 }
637
638 if (g->close[i].number == (uint32_t)friendcon_id) {
639 g->close[i].closest = closest;
640 return i; /* Already in list. */
641 }
642 }
643
644 if (ind == MAX_GROUP_CONNECTIONS)
645 return -1;
646
647 if (lock)
648 friend_connection_lock(g_c->fr_c, friendcon_id);
649
650 g->close[ind].type = GROUPCHAT_CLOSE_CONNECTION;
651 g->close[ind].number = friendcon_id;
652 g->close[ind].closest = closest;
653 //TODO
654 friend_connection_callbacks(g_c->m->fr_c, friendcon_id, GROUPCHAT_CALLBACK_INDEX, &handle_status, &handle_packet,
655 &handle_lossy, g_c, friendcon_id);
656
657 return ind;
658}
659
660/* Creates a new groupchat and puts it in the chats array.
661 *
662 * type is one of GROUPCHAT_TYPE_*
663 *
664 * return group number on success.
665 * return -1 on failure.
666 */
667int add_groupchat(Group_Chats *g_c, uint8_t type)
668{
669 int groupnumber = create_group_chat(g_c);
670
671 if (groupnumber == -1)
672 return -1;
673
674 Group_c *g = &g_c->chats[groupnumber];
675
676 g->status = GROUPCHAT_STATUS_CONNECTED;
677 g->number_joined = -1;
678 new_symmetric_key(g->identifier + 1);
679 g->identifier[0] = type;
680 g->peer_number = 0; /* Founder is peer 0. */
681 memcpy(g->real_pk, g_c->m->net_crypto->self_public_key, crypto_box_PUBLICKEYBYTES);
682 int peer_index = addpeer(g_c, groupnumber, g->real_pk, g_c->m->dht->self_public_key, 0);
683
684 if (peer_index == -1) {
685 return -1;
686 }
687
688 setnick(g_c, groupnumber, peer_index, g_c->m->name, g_c->m->name_length);
689
690 return groupnumber;
691}
692
693static int group_kill_peer_send(const Group_Chats *g_c, int groupnumber, uint16_t peer_num);
694/* Delete a groupchat from the chats array.
695 *
696 * return 0 on success.
697 * return -1 if failure.
698 */
699int del_groupchat(Group_Chats *g_c, int groupnumber)
700{
701 Group_c *g = get_group_c(g_c, groupnumber);
702
703 if (!g)
704 return -1;
705
706 group_kill_peer_send(g_c, groupnumber, g->peer_number);
707
708 unsigned int i;
709
710 for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
711 if (g->close[i].type == GROUPCHAT_CLOSE_NONE)
712 continue;
713
714 g->close[i].type = GROUPCHAT_CLOSE_NONE;
715 kill_friend_connection(g_c->fr_c, g->close[i].number);
716 }
717
718 for (i = 0; i < g->numpeers; ++i) {
719 if (g->peer_on_leave)
720 g->peer_on_leave(g->object, groupnumber, i, g->group[i].object);
721 }
722
723 free(g->group);
724
725 if (g->group_on_delete)
726 g->group_on_delete(g->object, groupnumber);
727
728 return wipe_group_chat(g_c, groupnumber);
729}
730
731/* Copy the public key of peernumber who is in groupnumber to pk.
732 * pk must be crypto_box_PUBLICKEYBYTES long.
733 *
734 * returns 0 on success
735 * returns -1 on failure
736 */
737int group_peer_pubkey(const Group_Chats *g_c, int groupnumber, int peernumber, uint8_t *pk)
738{
739 Group_c *g = get_group_c(g_c, groupnumber);
740
741 if (!g)
742 return -1;
743
744 if ((uint32_t)peernumber >= g->numpeers)
745 return -1;
746
747 memcpy(pk, g->group[peernumber].real_pk, crypto_box_PUBLICKEYBYTES);
748 return 0;
749}
750
751/* Copy the name of peernumber who is in groupnumber to name.
752 * name must be at least MAX_NAME_LENGTH long.
753 *
754 * return length of name if success
755 * return -1 if failure
756 */
757int group_peername(const Group_Chats *g_c, int groupnumber, int peernumber, uint8_t *name)
758{
759 Group_c *g = get_group_c(g_c, groupnumber);
760
761 if (!g)
762 return -1;
763
764 if ((uint32_t)peernumber >= g->numpeers)
765 return -1;
766
767 if (g->group[peernumber].nick_len == 0) {
768 memcpy(name, "Tox User", 8);
769 return 8;
770 }
771
772 memcpy(name, g->group[peernumber].nick, g->group[peernumber].nick_len);
773 return g->group[peernumber].nick_len;
774}
775
776/* List all the peers in the group chat.
777 *
778 * Copies the names of the peers to the name[length][MAX_NAME_LENGTH] array.
779 *
780 * Copies the lengths of the names to lengths[length]
781 *
782 * returns the number of peers on success.
783 *
784 * return -1 on failure.
785 */
786int group_names(const Group_Chats *g_c, int groupnumber, uint8_t names[][MAX_NAME_LENGTH], uint16_t lengths[],
787 uint16_t length)
788{
789 Group_c *g = get_group_c(g_c, groupnumber);
790
791 if (!g)
792 return -1;
793
794 unsigned int i;
795
796 for (i = 0; i < g->numpeers && i < length; ++i) {
797 lengths[i] = group_peername(g_c, groupnumber, i, names[i]);
798 }
799
800 return i;
801}
802
803/* Return the number of peers in the group chat on success.
804 * return -1 on failure
805 */
806int group_number_peers(const Group_Chats *g_c, int groupnumber)
807{
808 Group_c *g = get_group_c(g_c, groupnumber);
809
810 if (!g)
811 return -1;
812
813 return g->numpeers;
814}
815
816/* return 1 if the peernumber corresponds to ours.
817 * return 0 on failure.
818 */
819unsigned int group_peernumber_is_ours(const Group_Chats *g_c, int groupnumber, int peernumber)
820{
821 Group_c *g = get_group_c(g_c, groupnumber);
822
823 if (!g)
824 return 0;
825
826 if (g->status != GROUPCHAT_STATUS_CONNECTED)
827 return 0;
828
829 if ((uint32_t)peernumber >= g->numpeers)
830 return 0;
831
832 return g->peer_number == g->group[peernumber].peer_number;
833}
834
835/* return the type of groupchat (GROUPCHAT_TYPE_) that groupnumber is.
836 *
837 * return -1 on failure.
838 * return type on success.
839 */
840int group_get_type(const Group_Chats *g_c, int groupnumber)
841{
842 Group_c *g = get_group_c(g_c, groupnumber);
843
844 if (!g)
845 return -1;
846
847 return g->identifier[0];
848}
849
850/* Send a group packet to friendcon_id.
851 *
852 * return 1 on success
853 * return 0 on failure
854 */
855static unsigned int send_packet_group_peer(Friend_Connections *fr_c, int friendcon_id, uint8_t packet_id,
856 uint16_t group_num, const uint8_t *data, uint16_t length)
857{
858 if (1 + sizeof(uint16_t) + length > MAX_CRYPTO_DATA_SIZE)
859 return 0;
860
861 group_num = htons(group_num);
862 uint8_t packet[1 + sizeof(uint16_t) + length];
863 packet[0] = packet_id;
864 memcpy(packet + 1, &group_num, sizeof(uint16_t));
865 memcpy(packet + 1 + sizeof(uint16_t), data, length);
866 return write_cryptpacket(fr_c->net_crypto, friend_connection_crypt_connection_id(fr_c, friendcon_id), packet,
867 sizeof(packet), 0) != -1;
868}
869
870/* Send a group lossy packet to friendcon_id.
871 *
872 * return 1 on success
873 * return 0 on failure
874 */
875static unsigned int send_lossy_group_peer(Friend_Connections *fr_c, int friendcon_id, uint8_t packet_id,
876 uint16_t group_num, const uint8_t *data, uint16_t length)
877{
878 if (1 + sizeof(uint16_t) + length > MAX_CRYPTO_DATA_SIZE)
879 return 0;
880
881 group_num = htons(group_num);
882 uint8_t packet[1 + sizeof(uint16_t) + length];
883 packet[0] = packet_id;
884 memcpy(packet + 1, &group_num, sizeof(uint16_t));
885 memcpy(packet + 1 + sizeof(uint16_t), data, length);
886 return send_lossy_cryptpacket(fr_c->net_crypto, friend_connection_crypt_connection_id(fr_c, friendcon_id), packet,
887 sizeof(packet)) != -1;
888}
889
890#define INVITE_PACKET_SIZE (1 + sizeof(uint16_t) + GROUP_IDENTIFIER_LENGTH)
891#define INVITE_ID 0
892
893#define INVITE_RESPONSE_PACKET_SIZE (1 + sizeof(uint16_t) * 2 + GROUP_IDENTIFIER_LENGTH)
894#define INVITE_RESPONSE_ID 1
895
896/* invite friendnumber to groupnumber
897 * return 0 on success
898 * return -1 on failure
899 */
900int invite_friend(Group_Chats *g_c, int32_t friendnumber, int groupnumber)
901{
902 Group_c *g = get_group_c(g_c, groupnumber);
903
904 if (!g)
905 return -1;
906
907 uint8_t invite[INVITE_PACKET_SIZE];
908 invite[0] = INVITE_ID;
909 uint16_t groupchat_num = htons((uint16_t)groupnumber);
910 memcpy(invite + 1, &groupchat_num, sizeof(groupchat_num));
911 memcpy(invite + 1 + sizeof(groupchat_num), g->identifier, GROUP_IDENTIFIER_LENGTH);
912
913 if (send_group_invite_packet(g_c->m, friendnumber, invite, sizeof(invite))) {
914 return 0;
915 } else {
916 wipe_group_chat(g_c, groupnumber);
917 return -1;
918 }
919}
920
921static unsigned int send_peer_query(Group_Chats *g_c, int friendcon_id, uint16_t group_num);
922
923/* Join a group (you need to have been invited first.)
924 *
925 * expected_type is the groupchat type we expect the chat we are joining is.
926 *
927 * returns group number on success
928 * returns -1 on failure.
929 */
930int join_groupchat(Group_Chats *g_c, int32_t friendnumber, uint8_t expected_type, const uint8_t *data, uint16_t length)
931{
932 if (length != sizeof(uint16_t) + GROUP_IDENTIFIER_LENGTH)
933 return -1;
934
935 if (data[sizeof(uint16_t)] != expected_type)
936 return -1;
937
938 int friendcon_id = getfriendcon_id(g_c->m, friendnumber);
939
940 if (friendcon_id == -1)
941 return -1;
942
943 if (get_group_num(g_c, data + sizeof(uint16_t)) != -1)
944 return -1;
945
946 int groupnumber = create_group_chat(g_c);
947
948 if (groupnumber == -1)
949 return -1;
950
951 Group_c *g = &g_c->chats[groupnumber];
952
953 uint16_t group_num = htons(groupnumber);
954 g->status = GROUPCHAT_STATUS_VALID;
955 g->number_joined = -1;
956 memcpy(g->real_pk, g_c->m->net_crypto->self_public_key, crypto_box_PUBLICKEYBYTES);
957
958 uint8_t response[INVITE_RESPONSE_PACKET_SIZE];
959 response[0] = INVITE_RESPONSE_ID;
960 memcpy(response + 1, &group_num, sizeof(uint16_t));
961 memcpy(response + 1 + sizeof(uint16_t), data, sizeof(uint16_t) + GROUP_IDENTIFIER_LENGTH);
962
963 if (send_group_invite_packet(g_c->m, friendnumber, response, sizeof(response))) {
964 uint16_t other_groupnum;
965 memcpy(&other_groupnum, data, sizeof(other_groupnum));
966 other_groupnum = ntohs(other_groupnum);
967 memcpy(g->identifier, data + sizeof(uint16_t), GROUP_IDENTIFIER_LENGTH);
968 int close_index = add_conn_to_groupchat(g_c, friendcon_id, groupnumber, 0, 1);
969
970 if (close_index != -1) {
971 g->close[close_index].group_number = other_groupnum;
972 g->close[close_index].type = GROUPCHAT_CLOSE_ONLINE;
973 g->number_joined = friendcon_id;
974 }
975
976 send_peer_query(g_c, friendcon_id, other_groupnum);
977 return groupnumber;
978 } else {
979 g->status = GROUPCHAT_STATUS_NONE;
980 return -1;
981 }
982}
983
984/* Set the callback for group invites.
985 *
986 * Function(Group_Chats *g_c, int32_t friendnumber, uint8_t type, uint8_t *data, uint16_t length, void *userdata)
987 *
988 * data of length is what needs to be passed to join_groupchat().
989 */
990void g_callback_group_invite(Group_Chats *g_c, void (*function)(Messenger *m, int32_t, uint8_t, const uint8_t *,
991 uint16_t, void *), void *userdata)
992{
993 g_c->invite_callback = function;
994 g_c->invite_callback_userdata = userdata;
995}
996
997/* Set the callback for group messages.
998 *
999 * Function(Group_Chats *g_c, int groupnumber, int friendgroupnumber, uint8_t * message, uint16_t length, void *userdata)
1000 */
1001void g_callback_group_message(Group_Chats *g_c, void (*function)(Messenger *m, int, int, const uint8_t *, uint16_t,
1002 void *), void *userdata)
1003{
1004 g_c->message_callback = function;
1005 g_c->message_callback_userdata = userdata;
1006}
1007
1008/* Set the callback for group actions.
1009 *
1010 * Function(Group_Chats *g_c, int groupnumber, int friendgroupnumber, uint8_t * message, uint16_t length, void *userdata)
1011 */
1012void g_callback_group_action(Group_Chats *g_c, void (*function)(Messenger *m, int, int, const uint8_t *, uint16_t,
1013 void *), void *userdata)
1014{
1015 g_c->action_callback = function;
1016 g_c->action_callback_userdata = userdata;
1017}
1018
1019/* Set handlers for custom lossy packets.
1020 *
1021 * NOTE: Handler must return 0 if packet is to be relayed, -1 if the packet should not be relayed.
1022 *
1023 * Function(void *group object (set with group_set_object), int groupnumber, int friendgroupnumber, void *group peer object (set with group_peer_set_object), const uint8_t *packet, uint16_t length)
1024 */
1025void group_lossy_packet_registerhandler(Group_Chats *g_c, uint8_t byte, int (*function)(void *, int, int, void *,
1026 const uint8_t *, uint16_t))
1027{
1028 g_c->lossy_packethandlers[byte].function = function;
1029}
1030
1031/* Set callback function for peer name list changes.
1032 *
1033 * It gets called every time the name list changes(new peer/name, deleted peer)
1034 * Function(Group_Chats *g_c, int groupnumber, int peernumber, TOX_CHAT_CHANGE change, void *userdata)
1035 */
1036void g_callback_group_namelistchange(Group_Chats *g_c, void (*function)(Messenger *m, int, int, uint8_t, void *),
1037 void *userdata)
1038{
1039 g_c->peer_namelistchange = function;
1040 g_c->group_namelistchange_userdata = userdata;
1041}
1042
1043/* Set callback function for title changes.
1044 *
1045 * Function(Group_Chats *g_c, int groupnumber, int friendgroupnumber, uint8_t * title, uint8_t length, void *userdata)
1046 * if friendgroupnumber == -1, then author is unknown (e.g. initial joining the group)
1047 */
1048void g_callback_group_title(Group_Chats *g_c, void (*function)(Messenger *m, int, int, const uint8_t *, uint8_t,
1049 void *), void *userdata)
1050{
1051 g_c->title_callback = function;
1052 g_c->title_callback_userdata = userdata;
1053}
1054
1055/* Set a function to be called when a new peer joins a group chat.
1056 *
1057 * Function(void *group object (set with group_set_object), int groupnumber, int friendgroupnumber)
1058 *
1059 * return 0 on success.
1060 * return -1 on failure.
1061 */
1062int callback_groupchat_peer_new(const Group_Chats *g_c, int groupnumber, void (*function)(void *, int, int))
1063{
1064 Group_c *g = get_group_c(g_c, groupnumber);
1065
1066 if (!g)
1067 return -1;
1068
1069 g->peer_on_join = function;
1070 return 0;
1071}
1072
1073/* Set a function to be called when a peer leaves a group chat.
1074 *
1075 * Function(void *group object (set with group_set_object), int groupnumber, int friendgroupnumber, void *group peer object (set with group_peer_set_object))
1076 *
1077 * return 0 on success.
1078 * return -1 on failure.
1079 */
1080int callback_groupchat_peer_delete(Group_Chats *g_c, int groupnumber, void (*function)(void *, int, int, void *))
1081{
1082 Group_c *g = get_group_c(g_c, groupnumber);
1083
1084 if (!g)
1085 return -1;
1086
1087 g->peer_on_leave = function;
1088 return 0;
1089}
1090
1091/* Set a function to be called when the group chat is deleted.
1092 *
1093 * Function(void *group object (set with group_set_object), int groupnumber)
1094 *
1095 * return 0 on success.
1096 * return -1 on failure.
1097 */
1098int callback_groupchat_delete(Group_Chats *g_c, int groupnumber, void (*function)(void *, int))
1099{
1100 Group_c *g = get_group_c(g_c, groupnumber);
1101
1102 if (!g)
1103 return -1;
1104
1105 g->group_on_delete = function;
1106 return 0;
1107}
1108
1109static unsigned int send_message_group(const Group_Chats *g_c, int groupnumber, uint8_t message_id, const uint8_t *data,
1110 uint16_t len);
1111
1112#define GROUP_MESSAGE_PING_ID 0
1113int group_ping_send(const Group_Chats *g_c, int groupnumber)
1114{
1115 if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_PING_ID, 0, 0)) {
1116 return 0;
1117 } else {
1118 return -1;
1119 }
1120}
1121
1122#define GROUP_MESSAGE_NEW_PEER_ID 16
1123#define GROUP_MESSAGE_NEW_PEER_LENGTH (sizeof(uint16_t) + crypto_box_PUBLICKEYBYTES * 2)
1124/* send a new_peer message
1125 * return 0 on success
1126 * return -1 on failure
1127 */
1128int group_new_peer_send(const Group_Chats *g_c, int groupnumber, uint16_t peer_num, const uint8_t *real_pk,
1129 uint8_t *temp_pk)
1130{
1131 uint8_t packet[GROUP_MESSAGE_NEW_PEER_LENGTH];
1132
1133 peer_num = htons(peer_num);
1134 memcpy(packet, &peer_num, sizeof(uint16_t));
1135 memcpy(packet + sizeof(uint16_t), real_pk, crypto_box_PUBLICKEYBYTES);
1136 memcpy(packet + sizeof(uint16_t) + crypto_box_PUBLICKEYBYTES, temp_pk, crypto_box_PUBLICKEYBYTES);
1137
1138 if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_NEW_PEER_ID, packet, sizeof(packet))) {
1139 return 0;
1140 } else {
1141 return -1;
1142 }
1143}
1144
1145#define GROUP_MESSAGE_KILL_PEER_ID 17
1146#define GROUP_MESSAGE_KILL_PEER_LENGTH (sizeof(uint16_t))
1147
1148/* send a kill_peer message
1149 * return 0 on success
1150 * return -1 on failure
1151 */
1152int group_kill_peer_send(const Group_Chats *g_c, int groupnumber, uint16_t peer_num)
1153{
1154 uint8_t packet[GROUP_MESSAGE_KILL_PEER_LENGTH];
1155
1156 peer_num = htons(peer_num);
1157 memcpy(packet, &peer_num, sizeof(uint16_t));
1158
1159 if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_KILL_PEER_ID, packet, sizeof(packet))) {
1160 return 0;
1161 } else {
1162 return -1;
1163 }
1164}
1165
1166#define GROUP_MESSAGE_NAME_ID 48
1167
1168/* send a name message
1169 * return 0 on success
1170 * return -1 on failure
1171 */
1172static int group_name_send(const Group_Chats *g_c, int groupnumber, const uint8_t *nick, uint16_t nick_len)
1173{
1174 if (nick_len > MAX_NAME_LENGTH)
1175 return -1;
1176
1177 if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_NAME_ID, nick, nick_len)) {
1178 return 0;
1179 } else {
1180 return -1;
1181 }
1182}
1183
1184#define GROUP_MESSAGE_TITLE_ID 49
1185
1186/* set the group's title, limited to MAX_NAME_LENGTH
1187 * return 0 on success
1188 * return -1 on failure
1189 */
1190int group_title_send(const Group_Chats *g_c, int groupnumber, const uint8_t *title, uint8_t title_len)
1191{
1192 if (title_len > MAX_NAME_LENGTH || title_len == 0)
1193 return -1;
1194
1195 Group_c *g = get_group_c(g_c, groupnumber);
1196
1197 if (!g)
1198 return -1;
1199
1200 /* same as already set? */
1201 if (g->title_len == title_len && !memcmp(g->title, title, title_len))
1202 return 0;
1203
1204 memcpy(g->title, title, title_len);
1205 g->title_len = title_len;
1206
1207 if (g->numpeers == 1)
1208 return 0;
1209
1210 if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_TITLE_ID, title, title_len))
1211 return 0;
1212 else
1213 return -1;
1214}
1215
1216/* Get group title from groupnumber and put it in title.
1217 * title needs to be a valid memory location with a max_length size of at least MAX_NAME_LENGTH (128) bytes.
1218 *
1219 * return length of copied title if success.
1220 * return -1 if failure.
1221 */
1222int group_title_get(const Group_Chats *g_c, int groupnumber, uint8_t *title, uint32_t max_length)
1223{
1224 Group_c *g = get_group_c(g_c, groupnumber);
1225
1226 if (!g)
1227 return -1;
1228
1229 if (g->title_len == 0 || g->title_len > MAX_NAME_LENGTH)
1230 return -1;
1231
1232 if (max_length > g->title_len)
1233 max_length = g->title_len;
1234
1235 memcpy(title, g->title, max_length);
1236 return max_length;
1237}
1238
1239static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length)
1240{
1241 Group_Chats *g_c = m->group_chat_object;
1242
1243 if (length <= 1)
1244 return;
1245
1246 const uint8_t *invite_data = data + 1;
1247 uint16_t invite_length = length - 1;
1248
1249 switch (data[0]) {
1250 case INVITE_ID: {
1251 if (length != INVITE_PACKET_SIZE)
1252 return;
1253
1254 int groupnumber = get_group_num(g_c, data + 1 + sizeof(uint16_t));
1255
1256 if (groupnumber == -1) {
1257 if (g_c->invite_callback)
1258 g_c->invite_callback(m, friendnumber, *(invite_data + sizeof(uint16_t)), invite_data, invite_length,
1259 g_c->invite_callback_userdata);
1260
1261 return;
1262 }
1263
1264 break;
1265 }
1266
1267 case INVITE_RESPONSE_ID: {
1268 if (length != INVITE_RESPONSE_PACKET_SIZE)
1269 return;
1270
1271 uint16_t other_groupnum, groupnum;
1272 memcpy(&groupnum, data + 1 + sizeof(uint16_t), sizeof(uint16_t));
1273 groupnum = ntohs(groupnum);
1274
1275 Group_c *g = get_group_c(g_c, groupnum);
1276
1277 if (!g)
1278 return;
1279
1280 if (memcmp(data + 1 + sizeof(uint16_t) * 2, g->identifier, GROUP_IDENTIFIER_LENGTH) != 0)
1281 return;
1282
1283 uint16_t peer_number = rand(); /* TODO: what if two people enter the group at the same time and
1284 are given the same peer_number by different nodes? */
1285
1286 unsigned int tries = 0;
1287
1288 while (get_peer_index(g, peer_number) != -1) {
1289 peer_number = rand();
1290 ++tries;
1291
1292 if (tries > 32)
1293 return;
1294 }
1295
1296 memcpy(&other_groupnum, data + 1, sizeof(uint16_t));
1297 other_groupnum = ntohs(other_groupnum);
1298
1299 int friendcon_id = getfriendcon_id(m, friendnumber);
1300 uint8_t real_pk[crypto_box_PUBLICKEYBYTES], temp_pk[crypto_box_PUBLICKEYBYTES];
1301 get_friendcon_public_keys(real_pk, temp_pk, g_c->fr_c, friendcon_id);
1302
1303 addpeer(g_c, groupnum, real_pk, temp_pk, peer_number);
1304 int close_index = add_conn_to_groupchat(g_c, friendcon_id, groupnum, 0, 1);
1305
1306 if (close_index != -1) {
1307 g->close[close_index].group_number = other_groupnum;
1308 g->close[close_index].type = GROUPCHAT_CLOSE_ONLINE;
1309 }
1310
1311 group_new_peer_send(g_c, groupnum, peer_number, real_pk, temp_pk);
1312 break;
1313 }
1314
1315 default:
1316 return;
1317 }
1318}
1319
1320/* Find index of friend in the close list;
1321 *
1322 * returns index on success
1323 * returns -1 on failure.
1324 */
1325static int friend_in_close(Group_c *g, int friendcon_id)
1326{
1327 unsigned int i;
1328
1329 for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
1330 if (g->close[i].type == GROUPCHAT_CLOSE_NONE)
1331 continue;
1332
1333 if (g->close[i].number != (uint32_t)friendcon_id)
1334 continue;
1335
1336 return i;
1337 }
1338
1339 return -1;
1340}
1341
1342/* return number of connected close connections.
1343 */
1344static unsigned int count_close_connected(Group_c *g)
1345{
1346 unsigned int i, count = 0;
1347
1348 for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
1349 if (g->close[i].type == GROUPCHAT_CLOSE_ONLINE) {
1350 ++count;
1351 }
1352 }
1353
1354 return count;
1355}
1356
1357#define ONLINE_PACKET_DATA_SIZE (sizeof(uint16_t) + GROUP_IDENTIFIER_LENGTH)
1358
1359static int send_packet_online(Friend_Connections *fr_c, int friendcon_id, uint16_t group_num, uint8_t *identifier)
1360{
1361 uint8_t packet[1 + ONLINE_PACKET_DATA_SIZE];
1362 group_num = htons(group_num);
1363 packet[0] = PACKET_ID_ONLINE_PACKET;
1364 memcpy(packet + 1, &group_num, sizeof(uint16_t));
1365 memcpy(packet + 1 + sizeof(uint16_t), identifier, GROUP_IDENTIFIER_LENGTH);
1366 return write_cryptpacket(fr_c->net_crypto, friend_connection_crypt_connection_id(fr_c, friendcon_id), packet,
1367 sizeof(packet), 0) != -1;
1368}
1369
1370static unsigned int send_peer_kill(Group_Chats *g_c, int friendcon_id, uint16_t group_num);
1371
1372static int handle_packet_online(Group_Chats *g_c, int friendcon_id, uint8_t *data, uint16_t length)
1373{
1374 if (length != ONLINE_PACKET_DATA_SIZE)
1375 return -1;
1376
1377 int groupnumber = get_group_num(g_c, data + sizeof(uint16_t));
1378 uint16_t other_groupnum;
1379 memcpy(&other_groupnum, data, sizeof(uint16_t));
1380 other_groupnum = ntohs(other_groupnum);
1381
1382 Group_c *g = get_group_c(g_c, groupnumber);
1383
1384 if (!g)
1385 return -1;
1386
1387 int index = friend_in_close(g, friendcon_id);
1388
1389 if (index == -1)
1390 return -1;
1391
1392 if (g->close[index].type == GROUPCHAT_CLOSE_ONLINE) {
1393 return -1;
1394 }
1395
1396 if (count_close_connected(g) == 0) {
1397 send_peer_query(g_c, friendcon_id, other_groupnum);
1398 }
1399
1400 g->close[index].group_number = other_groupnum;
1401 g->close[index].type = GROUPCHAT_CLOSE_ONLINE;
1402 send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->identifier);
1403
1404 if (g->number_joined != -1 && count_close_connected(g) >= DESIRED_CLOSE_CONNECTIONS) {
1405 int fr_close_index = friend_in_close(g, g->number_joined);
1406
1407 if (fr_close_index == -1)
1408 return -1;
1409
1410 if (!g->close[fr_close_index].closest) {
1411 g->close[fr_close_index].type = GROUPCHAT_CLOSE_NONE;
1412 send_peer_kill(g_c, g->close[fr_close_index].number, g->close[fr_close_index].group_number);
1413 kill_friend_connection(g_c->fr_c, g->close[fr_close_index].number);
1414 g->number_joined = -1;
1415 }
1416 }
1417
1418 return 0;
1419}
1420
1421#define PEER_KILL_ID 1
1422#define PEER_QUERY_ID 8
1423#define PEER_RESPONSE_ID 9
1424#define PEER_TITLE_ID 10
1425// we could send title with invite, but then if it changes between sending and accepting inv, joinee won't see it
1426
1427/* return 1 on success.
1428 * return 0 on failure
1429 */
1430static unsigned int send_peer_kill(Group_Chats *g_c, int friendcon_id, uint16_t group_num)
1431{
1432 uint8_t packet[1];
1433 packet[0] = PEER_KILL_ID;
1434 return send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_GROUPCHAT, group_num, packet, sizeof(packet));
1435}
1436
1437
1438/* return 1 on success.
1439 * return 0 on failure
1440 */
1441static unsigned int send_peer_query(Group_Chats *g_c, int friendcon_id, uint16_t group_num)
1442{
1443 uint8_t packet[1];
1444 packet[0] = PEER_QUERY_ID;
1445 return send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_GROUPCHAT, group_num, packet, sizeof(packet));
1446}
1447
1448/* return number of peers sent on success.
1449 * return 0 on failure.
1450 */
1451static unsigned int send_peers(Group_Chats *g_c, int groupnumber, int friendcon_id, uint16_t group_num)
1452{
1453 Group_c *g = get_group_c(g_c, groupnumber);
1454
1455 if (!g)
1456 return -1;
1457
1458 uint8_t packet[MAX_CRYPTO_DATA_SIZE - (1 + sizeof(uint16_t))];
1459 packet[0] = PEER_RESPONSE_ID;
1460 uint8_t *p = packet + 1;
1461
1462 uint16_t sent = 0;
1463 unsigned int i;
1464
1465 for (i = 0; i < g->numpeers; ++i) {
1466 if ((p - packet) + sizeof(uint16_t) + crypto_box_PUBLICKEYBYTES * 2 + 1 + g->group[i].nick_len > sizeof(packet)) {
1467 if (send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_GROUPCHAT, group_num, packet, (p - packet))) {
1468 sent = i;
1469 } else {
1470 return sent;
1471 }
1472
1473 p = packet + 1;
1474 }
1475
1476 uint16_t peer_num = htons(g->group[i].peer_number);
1477 memcpy(p, &peer_num, sizeof(peer_num));
1478 p += sizeof(peer_num);
1479 memcpy(p, g->group[i].real_pk, crypto_box_PUBLICKEYBYTES);
1480 p += crypto_box_PUBLICKEYBYTES;
1481 memcpy(p, g->group[i].temp_pk, crypto_box_PUBLICKEYBYTES);
1482 p += crypto_box_PUBLICKEYBYTES;
1483 *p = g->group[i].nick_len;
1484 p += 1;
1485 memcpy(p, g->group[i].nick, g->group[i].nick_len);
1486 p += g->group[i].nick_len;
1487 }
1488
1489 if (sent != i) {
1490 if (send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_GROUPCHAT, group_num, packet, (p - packet))) {
1491 sent = i;
1492 }
1493 }
1494
1495 if (g->title_len) {
1496 uint8_t Packet[1 + g->title_len];
1497 Packet[0] = PEER_TITLE_ID;
1498 memcpy(Packet + 1, g->title, g->title_len);
1499 send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_GROUPCHAT, group_num, Packet, sizeof(Packet));
1500 }
1501
1502 return sent;
1503}
1504
1505static int handle_send_peers(Group_Chats *g_c, int groupnumber, const uint8_t *data, uint16_t length)
1506{
1507 if (length == 0)
1508 return -1;
1509
1510 Group_c *g = get_group_c(g_c, groupnumber);
1511
1512 if (!g)
1513 return -1;
1514
1515 const uint8_t *d = data;
1516
1517 while ((length - (d - data)) >= sizeof(uint16_t) + crypto_box_PUBLICKEYBYTES * 2 + 1) {
1518 uint16_t peer_num;
1519 memcpy(&peer_num, d, sizeof(peer_num));
1520 peer_num = ntohs(peer_num);
1521 d += sizeof(uint16_t);
1522 int peer_index = addpeer(g_c, groupnumber, d, d + crypto_box_PUBLICKEYBYTES, peer_num);
1523
1524 if (peer_index == -1)
1525 return -1;
1526
1527 if (g->status == GROUPCHAT_STATUS_VALID
1528 && memcmp(d, g_c->m->net_crypto->self_public_key, crypto_box_PUBLICKEYBYTES) == 0) {
1529 g->peer_number = peer_num;
1530 g->status = GROUPCHAT_STATUS_CONNECTED;
1531 group_name_send(g_c, groupnumber, g_c->m->name, g_c->m->name_length);
1532 }
1533
1534 d += crypto_box_PUBLICKEYBYTES * 2;
1535 uint8_t name_length = *d;
1536 d += 1;
1537
1538 if (name_length > (length - (d - data)) || name_length > MAX_NAME_LENGTH)
1539 return -1;
1540
1541 setnick(g_c, groupnumber, peer_index, d, name_length);
1542 d += name_length;
1543 }
1544
1545 return 0;
1546}
1547
1548static void handle_direct_packet(Group_Chats *g_c, int groupnumber, const uint8_t *data, uint16_t length,
1549 int close_index)
1550{
1551 if (length == 0)
1552 return;
1553
1554 switch (data[0]) {
1555 case PEER_KILL_ID: {
1556 Group_c *g = get_group_c(g_c, groupnumber);
1557
1558 if (!g)
1559 return;
1560
1561 if (!g->close[close_index].closest) {
1562 g->close[close_index].type = GROUPCHAT_CLOSE_NONE;
1563 kill_friend_connection(g_c->fr_c, g->close[close_index].number);
1564 }
1565 }
1566
1567 break;
1568
1569 case PEER_QUERY_ID: {
1570 Group_c *g = get_group_c(g_c, groupnumber);
1571
1572 if (!g)
1573 return;
1574
1575 send_peers(g_c, groupnumber, g->close[close_index].number, g->close[close_index].group_number);
1576 }
1577
1578 break;
1579
1580 case PEER_RESPONSE_ID: {
1581 handle_send_peers(g_c, groupnumber, data + 1, length - 1);
1582 }
1583
1584 break;
1585
1586 case PEER_TITLE_ID: {
1587 settitle(g_c, groupnumber, -1, data + 1, length - 1);
1588 }
1589
1590 break;
1591 }
1592}
1593
1594#define MIN_MESSAGE_PACKET_LEN (sizeof(uint16_t) * 2 + sizeof(uint32_t) + 1)
1595
1596/* Send message to all close except receiver (if receiver isn't -1)
1597 * NOTE: this function appends the group chat number to the data passed to it.
1598 *
1599 * return number of messages sent.
1600 */
1601static unsigned int send_message_all_close(const Group_Chats *g_c, int groupnumber, const uint8_t *data,
1602 uint16_t length, int receiver)
1603{
1604 Group_c *g = get_group_c(g_c, groupnumber);
1605
1606 if (!g)
1607 return 0;
1608
1609 uint16_t i, sent = 0;
1610
1611 for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
1612 if (g->close[i].type != GROUPCHAT_CLOSE_ONLINE)
1613 continue;
1614
1615 if ((int)i == receiver)
1616 continue;
1617
1618 if (send_packet_group_peer(g_c->fr_c, g->close[i].number, PACKET_ID_MESSAGE_GROUPCHAT, g->close[i].group_number, data,
1619 length))
1620 ++sent;
1621 }
1622
1623 return sent;
1624}
1625
1626/* Send lossy message to all close except receiver (if receiver isn't -1)
1627 * NOTE: this function appends the group chat number to the data passed to it.
1628 *
1629 * return number of messages sent.
1630 */
1631static unsigned int send_lossy_all_close(const Group_Chats *g_c, int groupnumber, const uint8_t *data, uint16_t length,
1632 int receiver)
1633{
1634 Group_c *g = get_group_c(g_c, groupnumber);
1635
1636 if (!g)
1637 return 0;
1638
1639 unsigned int i, sent = 0, num_connected_closest = 0, connected_closest[DESIRED_CLOSE_CONNECTIONS];
1640
1641 for (i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
1642 if (g->close[i].type != GROUPCHAT_CLOSE_ONLINE)
1643 continue;
1644
1645 if ((int)i == receiver)
1646 continue;
1647
1648 if (g->close[i].closest) {
1649 connected_closest[num_connected_closest] = i;
1650 ++num_connected_closest;
1651 continue;
1652 }
1653
1654 if (send_lossy_group_peer(g_c->fr_c, g->close[i].number, PACKET_ID_LOSSY_GROUPCHAT, g->close[i].group_number, data,
1655 length))
1656 ++sent;
1657 }
1658
1659 if (!num_connected_closest) {
1660 return sent;
1661 }
1662
1663 unsigned int to_send = 0;
1664 uint64_t comp_val_old = ~0;
1665
1666 for (i = 0; i < num_connected_closest; ++i) {
1667 uint8_t real_pk[crypto_box_PUBLICKEYBYTES];
1668 uint8_t dht_temp_pk[crypto_box_PUBLICKEYBYTES];
1669 get_friendcon_public_keys(real_pk, dht_temp_pk, g_c->fr_c, g->close[connected_closest[i]].number);
1670 uint64_t comp_val = calculate_comp_value(g->real_pk, real_pk);
1671
1672 if (comp_val < comp_val_old) {
1673 to_send = connected_closest[i];
1674 comp_val_old = comp_val;
1675 }
1676 }
1677
1678 if (send_lossy_group_peer(g_c->fr_c, g->close[to_send].number, PACKET_ID_LOSSY_GROUPCHAT,
1679 g->close[to_send].group_number, data, length)) {
1680 ++sent;
1681 }
1682
1683 unsigned int to_send_other = 0;
1684 comp_val_old = ~0;
1685
1686 for (i = 0; i < num_connected_closest; ++i) {
1687 uint8_t real_pk[crypto_box_PUBLICKEYBYTES];
1688 uint8_t dht_temp_pk[crypto_box_PUBLICKEYBYTES];
1689 get_friendcon_public_keys(real_pk, dht_temp_pk, g_c->fr_c, g->close[connected_closest[i]].number);
1690 uint64_t comp_val = calculate_comp_value(real_pk, g->real_pk);
1691
1692 if (comp_val < comp_val_old) {
1693 to_send_other = connected_closest[i];
1694 comp_val_old = comp_val;
1695 }
1696 }
1697
1698 if (to_send_other == to_send) {
1699 return sent;
1700 }
1701
1702 if (send_lossy_group_peer(g_c->fr_c, g->close[to_send_other].number, PACKET_ID_LOSSY_GROUPCHAT,
1703 g->close[to_send_other].group_number, data, length)) {
1704 ++sent;
1705 }
1706
1707 return sent;
1708}
1709
1710#define MAX_GROUP_MESSAGE_DATA_LEN (MAX_CRYPTO_DATA_SIZE - (1 + MIN_MESSAGE_PACKET_LEN))
1711
1712/* Send data of len with message_id to groupnumber.
1713 *
1714 * return number of peers it was sent to on success.
1715 * return 0 on failure.
1716 */
1717static unsigned int send_message_group(const Group_Chats *g_c, int groupnumber, uint8_t message_id, const uint8_t *data,
1718 uint16_t len)
1719{
1720 if (len > MAX_GROUP_MESSAGE_DATA_LEN)
1721 return 0;
1722
1723 Group_c *g = get_group_c(g_c, groupnumber);
1724
1725 if (!g)
1726 return 0;
1727
1728 if (g->status != GROUPCHAT_STATUS_CONNECTED)
1729 return 0;
1730
1731 uint8_t packet[sizeof(uint16_t) + sizeof(uint32_t) + 1 + len];
1732 uint16_t peer_num = htons(g->peer_number);
1733 memcpy(packet, &peer_num, sizeof(peer_num));
1734
1735 ++g->message_number;
1736
1737 if (!g->message_number)
1738 ++g->message_number;
1739
1740 uint32_t message_num = htonl(g->message_number);
1741 memcpy(packet + sizeof(uint16_t), &message_num, sizeof(message_num));
1742
1743 packet[sizeof(uint16_t) + sizeof(uint32_t)] = message_id;
1744
1745 if (len)
1746 memcpy(packet + sizeof(uint16_t) + sizeof(uint32_t) + 1, data, len);
1747
1748 return send_message_all_close(g_c, groupnumber, packet, sizeof(packet), -1);
1749}
1750
1751/* send a group message
1752 * return 0 on success
1753 * return -1 on failure
1754 */
1755int group_message_send(const Group_Chats *g_c, int groupnumber, const uint8_t *message, uint16_t length)
1756{
1757 if (send_message_group(g_c, groupnumber, PACKET_ID_MESSAGE, message, length)) {
1758 return 0;
1759 } else {
1760 return -1;
1761 }
1762}
1763
1764/* send a group action
1765 * return 0 on success
1766 * return -1 on failure
1767 */
1768int group_action_send(const Group_Chats *g_c, int groupnumber, const uint8_t *action, uint16_t length)
1769{
1770 if (send_message_group(g_c, groupnumber, PACKET_ID_ACTION, action, length)) {
1771 return 0;
1772 } else {
1773 return -1;
1774 }
1775}
1776
1777/* High level function to send custom lossy packets.
1778 *
1779 * return -1 on failure.
1780 * return 0 on success.
1781 */
1782int send_group_lossy_packet(const Group_Chats *g_c, int groupnumber, const uint8_t *data, uint16_t length)
1783{
1784 //TODO: length check here?
1785 Group_c *g = get_group_c(g_c, groupnumber);
1786
1787 if (!g)
1788 return -1;
1789
1790 uint8_t packet[sizeof(uint16_t) * 2 + length];
1791 uint16_t peer_number = htons(g->peer_number);
1792 memcpy(packet, &peer_number, sizeof(uint16_t));
1793 uint16_t message_num = htons(g->lossy_message_number);
1794 memcpy(packet + sizeof(uint16_t), &message_num, sizeof(uint16_t));
1795 memcpy(packet + sizeof(uint16_t) * 2, data, length);
1796
1797 if (send_lossy_all_close(g_c, groupnumber, packet, sizeof(packet), -1) == 0) {
1798 return -1;
1799 }
1800
1801 ++g->lossy_message_number;
1802 return 0;
1803}
1804
1805static void handle_message_packet_group(Group_Chats *g_c, int groupnumber, const uint8_t *data, uint16_t length,
1806 int close_index)
1807{
1808 if (length < sizeof(uint16_t) + sizeof(uint32_t) + 1)
1809 return;
1810
1811 Group_c *g = get_group_c(g_c, groupnumber);
1812
1813 if (!g)
1814 return;
1815
1816 uint16_t peer_number;
1817 memcpy(&peer_number, data, sizeof(uint16_t));
1818 peer_number = ntohs(peer_number);
1819
1820 int index = get_peer_index(g, peer_number);
1821
1822 if (index == -1) {
1823 /* We don't know the peer this packet came from so we query the list of peers from that peer.
1824 (They would not have relayed it if they didn't know the peer.) */
1825 send_peer_query(g_c, g->close[close_index].number, g->close[close_index].group_number);
1826 return;
1827 }
1828
1829 uint32_t message_number;
1830 memcpy(&message_number, data + sizeof(uint16_t), sizeof(message_number));
1831 message_number = ntohl(message_number);
1832
1833 if (g->group[index].last_message_number == 0) {
1834 g->group[index].last_message_number = message_number;
1835 } else if (message_number - g->group[index].last_message_number > 64 ||
1836 message_number == g->group[index].last_message_number) {
1837 return;
1838 }
1839
1840 g->group[index].last_message_number = message_number;
1841
1842 uint8_t message_id = data[sizeof(uint16_t) + sizeof(message_number)];
1843 const uint8_t *msg_data = data + sizeof(uint16_t) + sizeof(message_number) + 1;
1844 uint16_t msg_data_len = length - (sizeof(uint16_t) + sizeof(message_number) + 1);
1845
1846 switch (message_id) {
1847 case GROUP_MESSAGE_PING_ID: {
1848 if (msg_data_len != 0)
1849 return;
1850
1851 g->group[index].last_recv = unix_time();
1852 }
1853 break;
1854
1855 case GROUP_MESSAGE_NEW_PEER_ID: {
1856 if (msg_data_len != GROUP_MESSAGE_NEW_PEER_LENGTH)
1857 return;
1858
1859 uint16_t new_peer_number;
1860 memcpy(&new_peer_number, msg_data, sizeof(uint16_t));
1861 new_peer_number = ntohs(new_peer_number);
1862 addpeer(g_c, groupnumber, msg_data + sizeof(uint16_t), msg_data + sizeof(uint16_t) + crypto_box_PUBLICKEYBYTES,
1863 new_peer_number);
1864 }
1865 break;
1866
1867 case GROUP_MESSAGE_KILL_PEER_ID: {
1868 if (msg_data_len != GROUP_MESSAGE_KILL_PEER_LENGTH)
1869 return;
1870
1871 uint16_t kill_peer_number;
1872 memcpy(&kill_peer_number, msg_data, sizeof(uint16_t));
1873 kill_peer_number = ntohs(kill_peer_number);
1874
1875 if (peer_number == kill_peer_number) {
1876 delpeer(g_c, groupnumber, index);
1877 } else {
1878 return;
1879 //TODO
1880 }
1881 }
1882 break;
1883
1884 case GROUP_MESSAGE_NAME_ID: {
1885 if (setnick(g_c, groupnumber, index, msg_data, msg_data_len) == -1)
1886 return;
1887 }
1888 break;
1889
1890 case GROUP_MESSAGE_TITLE_ID: {
1891 if (settitle(g_c, groupnumber, index, msg_data, msg_data_len) == -1)
1892 return;
1893 }
1894 break;
1895
1896 case PACKET_ID_MESSAGE: {
1897 if (msg_data_len == 0)
1898 return;
1899
1900 uint8_t newmsg[msg_data_len + 1];
1901 memcpy(newmsg, msg_data, msg_data_len);
1902 newmsg[msg_data_len] = 0;
1903
1904 //TODO
1905 if (g_c->message_callback)
1906 g_c->message_callback(g_c->m, groupnumber, index, newmsg, msg_data_len, g_c->message_callback_userdata);
1907
1908 break;
1909 }
1910
1911 case PACKET_ID_ACTION: {
1912 if (msg_data_len == 0)
1913 return;
1914
1915 uint8_t newmsg[msg_data_len + 1];
1916 memcpy(newmsg, msg_data, msg_data_len);
1917 newmsg[msg_data_len] = 0;
1918
1919 //TODO
1920 if (g_c->action_callback)
1921 g_c->action_callback(g_c->m, groupnumber, index, newmsg, msg_data_len, g_c->action_callback_userdata);
1922
1923 break;
1924 }
1925
1926 default:
1927 return;
1928 }
1929
1930 send_message_all_close(g_c, groupnumber, data, length, -1/*TODO close_index*/);
1931}
1932
1933static int handle_packet(void *object, int friendcon_id, uint8_t *data, uint16_t length)
1934{
1935 Group_Chats *g_c = object;
1936
1937 if (length < 1 + sizeof(uint16_t) + 1)
1938 return -1;
1939
1940 if (data[0] == PACKET_ID_ONLINE_PACKET) {
1941 return handle_packet_online(g_c, friendcon_id, data + 1, length - 1);
1942 }
1943
1944 if (data[0] != PACKET_ID_DIRECT_GROUPCHAT && data[0] != PACKET_ID_MESSAGE_GROUPCHAT)
1945 return -1;
1946
1947 uint16_t groupnumber;
1948 memcpy(&groupnumber, data + 1, sizeof(uint16_t));
1949 groupnumber = ntohs(groupnumber);
1950 Group_c *g = get_group_c(g_c, groupnumber);
1951
1952 if (!g)
1953 return -1;
1954
1955 int index = friend_in_close(g, friendcon_id);
1956
1957 if (index == -1)
1958 return -1;
1959
1960 switch (data[0]) {
1961 case PACKET_ID_DIRECT_GROUPCHAT: {
1962 handle_direct_packet(g_c, groupnumber, data + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), index);
1963 break;
1964 }
1965
1966 case PACKET_ID_MESSAGE_GROUPCHAT: {
1967 handle_message_packet_group(g_c, groupnumber, data + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), index);
1968 break;
1969 }
1970
1971 default: {
1972 return 0;
1973 }
1974 }
1975
1976 return 0;
1977}
1978
1979/* Did we already receive the lossy packet or not.
1980 *
1981 * return -1 on failure.
1982 * return 0 if packet was not received.
1983 * return 1 if packet was received.
1984 *
1985 * TODO: test this
1986 */
1987static unsigned int lossy_packet_not_received(Group_c *g, int peer_index, uint16_t message_number)
1988{
1989 if (peer_index == -1)
1990 return -1;
1991
1992 if (g->group[peer_index].bottom_lossy_number == g->group[peer_index].top_lossy_number) {
1993 g->group[peer_index].top_lossy_number = message_number;
1994 g->group[peer_index].bottom_lossy_number = (message_number - MAX_LOSSY_COUNT) + 1;
1995 g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT] = 1;
1996 return 0;
1997 }
1998
1999 if ((uint16_t)(message_number - g->group[peer_index].bottom_lossy_number) < MAX_LOSSY_COUNT) {
2000 if (g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT]) {
2001 return 1;
2002 }
2003
2004 g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT] = 1;
2005 return 0;
2006 }
2007
2008 if ((uint16_t)(message_number - g->group[peer_index].bottom_lossy_number) > (1 << 15))
2009 return -1;
2010
2011 uint16_t top_distance = message_number - g->group[peer_index].top_lossy_number;
2012
2013 if (top_distance >= MAX_LOSSY_COUNT) {
2014 memset(g->group[peer_index].recv_lossy, 0, sizeof(g->group[peer_index].recv_lossy));
2015 g->group[peer_index].top_lossy_number = message_number;
2016 g->group[peer_index].bottom_lossy_number = (message_number - MAX_LOSSY_COUNT) + 1;
2017 g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT] = 1;
2018 return 0;
2019 }
2020
2021 if (top_distance < MAX_LOSSY_COUNT) {
2022 unsigned int i;
2023
2024 for (i = g->group[peer_index].bottom_lossy_number; i != (g->group[peer_index].bottom_lossy_number + top_distance);
2025 ++i) {
2026 g->group[peer_index].recv_lossy[i % MAX_LOSSY_COUNT] = 0;
2027 }
2028
2029 g->group[peer_index].top_lossy_number = message_number;
2030 g->group[peer_index].bottom_lossy_number = (message_number - MAX_LOSSY_COUNT) + 1;
2031 g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT] = 1;
2032 return 0;
2033 }
2034
2035 return -1;
2036}
2037
2038static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uint16_t length)
2039{
2040 Group_Chats *g_c = object;
2041
2042 if (length < 1 + sizeof(uint16_t) * 3 + 1)
2043 return -1;
2044
2045 if (data[0] != PACKET_ID_LOSSY_GROUPCHAT)
2046 return -1;
2047
2048 uint16_t groupnumber, peer_number, message_number;
2049 memcpy(&groupnumber, data + 1, sizeof(uint16_t));
2050 memcpy(&peer_number, data + 1 + sizeof(uint16_t), sizeof(uint16_t));
2051 memcpy(&message_number, data + 1 + sizeof(uint16_t) * 2, sizeof(uint16_t));
2052 groupnumber = ntohs(groupnumber);
2053 peer_number = ntohs(peer_number);
2054 message_number = ntohs(message_number);
2055
2056 Group_c *g = get_group_c(g_c, groupnumber);
2057
2058 if (!g)
2059 return -1;
2060
2061 int index = friend_in_close(g, friendcon_id);
2062
2063 if (index == -1)
2064 return -1;
2065
2066 if (peer_number == g->peer_number)
2067 return -1;
2068
2069 int peer_index = get_peer_index(g, peer_number);
2070
2071 if (peer_index == -1)
2072 return -1;
2073
2074 if (lossy_packet_not_received(g, peer_index, message_number))
2075 return -1;
2076
2077 const uint8_t *lossy_data = data + 1 + sizeof(uint16_t) * 3;
2078 uint16_t lossy_length = length - (1 + sizeof(uint16_t) * 3);
2079 uint8_t message_id = lossy_data[0];
2080 ++lossy_data;
2081 --lossy_length;
2082
2083 if (g_c->lossy_packethandlers[message_id].function) {
2084 if (g_c->lossy_packethandlers[message_id].function(g->object, groupnumber, peer_index, g->group[peer_index].object,
2085 lossy_data, lossy_length) == -1) {
2086 return -1;
2087 }
2088 } else {
2089 return -1;
2090 }
2091
2092 send_lossy_all_close(g_c, groupnumber, data + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), index);
2093 return 0;
2094}
2095
2096/* Set the object that is tied to the group chat.
2097 *
2098 * return 0 on success.
2099 * return -1 on failure
2100 */
2101int group_set_object(const Group_Chats *g_c, int groupnumber, void *object)
2102{
2103 Group_c *g = get_group_c(g_c, groupnumber);
2104
2105 if (!g)
2106 return -1;
2107
2108 g->object = object;
2109 return 0;
2110}
2111
2112/* Set the object that is tied to the group peer.
2113 *
2114 * return 0 on success.
2115 * return -1 on failure
2116 */
2117int group_peer_set_object(const Group_Chats *g_c, int groupnumber, int peernumber, void *object)
2118{
2119 Group_c *g = get_group_c(g_c, groupnumber);
2120
2121 if (!g)
2122 return -1;
2123
2124 if ((uint32_t)peernumber >= g->numpeers)
2125 return -1;
2126
2127 g->group[peernumber].object = object;
2128 return 0;
2129}
2130
2131/* Return the object tide to the group chat previously set by group_set_object.
2132 *
2133 * return NULL on failure.
2134 * return object on success.
2135 */
2136void *group_get_object(const Group_Chats *g_c, int groupnumber)
2137{
2138 Group_c *g = get_group_c(g_c, groupnumber);
2139
2140 if (!g)
2141 return NULL;
2142
2143 return g->object;
2144}
2145
2146/* Return the object tide to the group chat peer previously set by group_peer_set_object.
2147 *
2148 * return NULL on failure.
2149 * return object on success.
2150 */
2151void *group_peer_get_object(const Group_Chats *g_c, int groupnumber, int peernumber)
2152{
2153 Group_c *g = get_group_c(g_c, groupnumber);
2154
2155 if (!g)
2156 return NULL;
2157
2158 if ((uint32_t)peernumber >= g->numpeers)
2159 return NULL;
2160
2161 return g->group[peernumber].object;
2162}
2163
2164/* Interval in seconds to send ping messages */
2165#define GROUP_PING_INTERVAL 20
2166
2167static int ping_groupchat(Group_Chats *g_c, int groupnumber)
2168{
2169 Group_c *g = get_group_c(g_c, groupnumber);
2170
2171 if (!g)
2172 return -1;
2173
2174 if (is_timeout(g->last_sent_ping, GROUP_PING_INTERVAL)) {
2175 if (group_ping_send(g_c, groupnumber) != -1) /* Ping */
2176 g->last_sent_ping = unix_time();
2177 }
2178
2179 return 0;
2180}
2181
2182static int groupchat_clear_timedout(Group_Chats *g_c, int groupnumber)
2183{
2184 Group_c *g = get_group_c(g_c, groupnumber);
2185
2186 if (!g)
2187 return -1;
2188
2189 uint32_t i;
2190
2191 for (i = 0; i < g->numpeers; ++i) {
2192 if (g->peer_number != g->group[i].peer_number && is_timeout(g->group[i].last_recv, GROUP_PING_INTERVAL * 3)) {
2193 delpeer(g_c, groupnumber, i);
2194 }
2195
2196 if (g->group == NULL || i >= g->numpeers)
2197 break;
2198 }
2199
2200 return 0;
2201}
2202
2203/* Send current name (set in messenger) to all online groups.
2204 */
2205void send_name_all_groups(Group_Chats *g_c)
2206{
2207 unsigned int i;
2208
2209 for (i = 0; i < g_c->num_chats; ++i) {
2210 Group_c *g = get_group_c(g_c, i);
2211
2212 if (!g)
2213 continue;
2214
2215 if (g->status == GROUPCHAT_STATUS_CONNECTED) {
2216 group_name_send(g_c, i, g_c->m->name, g_c->m->name_length);
2217 }
2218 }
2219}
2220
2221/* Create new groupchat instance. */
2222Group_Chats *new_groupchats(Messenger *m)
2223{
2224 if (!m)
2225 return NULL;
2226
2227 Group_Chats *temp = calloc(1, sizeof(Group_Chats));
2228
2229 if (temp == NULL)
2230 return NULL;
2231
2232 temp->m = m;
2233 temp->fr_c = m->fr_c;
2234 m->group_chat_object = temp;
2235 m_callback_group_invite(m, &handle_friend_invite_packet);
2236
2237 return temp;
2238}
2239
2240/* main groupchats loop. */
2241void do_groupchats(Group_Chats *g_c)
2242{
2243 unsigned int i;
2244
2245 for (i = 0; i < g_c->num_chats; ++i) {
2246 Group_c *g = get_group_c(g_c, i);
2247
2248 if (!g)
2249 continue;
2250
2251 if (g->status == GROUPCHAT_STATUS_CONNECTED) {
2252 connect_to_closest(g_c, i);
2253 ping_groupchat(g_c, i);
2254 groupchat_clear_timedout(g_c, i);
2255 }
2256 }
2257
2258 //TODO
2259}
2260
2261/* Free everything related with group chats. */
2262void kill_groupchats(Group_Chats *g_c)
2263{
2264 unsigned int i;
2265
2266 for (i = 0; i < g_c->num_chats; ++i) {
2267 del_groupchat(g_c, i);
2268 }
2269
2270 m_callback_group_invite(g_c->m, NULL);
2271 g_c->m->group_chat_object = 0;
2272 free(g_c);
2273}
2274
2275/* Return the number of chats in the instance m.
2276 * You should use this to determine how much memory to allocate
2277 * for copy_chatlist.
2278 */
2279uint32_t count_chatlist(Group_Chats *g_c)
2280{
2281 uint32_t ret = 0;
2282 uint32_t i;
2283
2284 for (i = 0; i < g_c->num_chats; i++) {
2285 if (g_c->chats[i].status != GROUPCHAT_STATUS_NONE) {
2286 ret++;
2287 }
2288 }
2289
2290 return ret;
2291}
2292
2293/* Copy a list of valid chat IDs into the array out_list.
2294 * If out_list is NULL, returns 0.
2295 * Otherwise, returns the number of elements copied.
2296 * If the array was too small, the contents
2297 * of out_list will be truncated to list_size. */
2298uint32_t copy_chatlist(Group_Chats *g_c, int32_t *out_list, uint32_t list_size)
2299{
2300 if (!out_list) {
2301 return 0;
2302 }
2303
2304 if (g_c->num_chats == 0) {
2305 return 0;
2306 }
2307
2308 uint32_t i, ret = 0;
2309
2310 for (i = 0; i < g_c->num_chats; ++i) {
2311 if (ret >= list_size) {
2312 break; /* Abandon ship */
2313 }
2314
2315 if (g_c->chats[i].status > GROUPCHAT_STATUS_NONE) {
2316 out_list[ret] = i;
2317 ret++;
2318 }
2319 }
2320
2321 return ret;
2322}