summaryrefslogtreecommitdiff
path: root/toxcore/group_chats.c
diff options
context:
space:
mode:
Diffstat (limited to 'toxcore/group_chats.c')
-rw-r--r--toxcore/group_chats.c837
1 files changed, 0 insertions, 837 deletions
diff --git a/toxcore/group_chats.c b/toxcore/group_chats.c
deleted file mode 100644
index 949ec53a..00000000
--- a/toxcore/group_chats.c
+++ /dev/null
@@ -1,837 +0,0 @@
1/* group_chats.c
2 *
3 * An implementation of massive text only group chats.
4 *
5 *
6 * Copyright (C) 2013 Tox project All Rights Reserved.
7 *
8 * This file is part of Tox.
9 *
10 * Tox is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * Tox is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
22 *
23 */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include "DHT.h"
30#include "assoc.h"
31#include "group_chats.h"
32#include "LAN_discovery.h"
33#include "util.h"
34
35#define GROUPCHAT_MAXDATA_LENGTH (MAX_CRYPTO_REQUEST_SIZE - (1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES))
36#define GROUPCHAT_MAXPLAINDATA_LENGTH (GROUPCHAT_MAXDATA_LENGTH - crypto_box_MACBYTES)
37
38#define GROUP_MAX_SENDNODES (GROUP_CLOSE_CONNECTIONS * 2)
39
40typedef struct {
41 uint64_t pingid;
42 //uint8_t client_id[crypto_box_PUBLICKEYBYTES];
43
44} getnodes_data;
45
46typedef struct {
47 uint8_t client_id[crypto_box_PUBLICKEYBYTES];
48 IP_Port ip_port;
49
50} groupchat_nodes;
51
52typedef struct {
53 uint64_t pingid;
54 groupchat_nodes nodes[GROUP_CLOSE_CONNECTIONS];
55 //uint8_t client_id[crypto_box_PUBLICKEYBYTES];
56
57} sendnodes_data;
58
59/*
60 * check if peer with client_id is in peer array.
61 *
62 * return peer number if peer is in chat.
63 * return -1 if peer is not in chat.
64 *
65 * TODO: make this more efficient.
66 */
67
68static int peer_in_chat(const Group_Chat *chat, const uint8_t *client_id)
69{
70 uint32_t i;
71
72 for (i = 0; i < chat->numpeers; ++i)
73 if (id_equal(chat->group[i].client_id, client_id))
74 return i;
75
76 return -1;
77}
78
79/* Compares client_id1 and client_id2 with client_id.
80 *
81 * return 0 if both are same distance.
82 * return 1 if client_id1 is closer.
83 * return 2 if client_id2 is closer.
84 */
85static int id_closest_groupchats(const uint8_t *id, const uint8_t *id1, const uint8_t *id2)
86{
87 size_t i;
88 uint8_t distance1, distance2;
89
90 for (i = 0; i < CLIENT_ID_SIZE; ++i) {
91
92 distance1 = abs(((int8_t *)id)[i] - ((int8_t *)id1)[i]);
93 distance2 = abs(((int8_t *)id)[i] - ((int8_t *)id2)[i]);
94
95 if (distance1 < distance2)
96 return 1;
97
98 if (distance1 > distance2)
99 return 2;
100 }
101
102 return 0;
103}
104
105#define BAD_GROUPNODE_TIMEOUT 30
106
107/*
108 * Check if peer is closer to us that the other peers in the list and if the peer is in the list.
109 * Return the number of peers it is closer to if it is not in the closelist.
110 * Return -1 if the peer is in the closelist.
111 */
112
113static int peer_okping(const Group_Chat *chat, const uint8_t *client_id)
114{
115 uint32_t i, j = 0;
116
117 if (id_equal(chat->self_public_key, client_id))
118 return -1;
119
120 for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) {
121 if (is_timeout(chat->close[i].last_recv, BAD_GROUPNODE_TIMEOUT)) {
122 ++j;
123 continue;
124 }
125
126 /* Equal */
127 if (id_equal(chat->close[i].client_id, client_id))
128 return -1;
129
130 if (id_closest_groupchats(chat->self_public_key, chat->close[i].client_id, client_id) == 2)
131 ++j;
132 }
133
134 return j;
135}
136
137
138
139/* Attempt to add a peer to the close list.
140 * Update last_recv if it is in list.
141 * Attempt to add it to list if it is not.
142 *
143 * Return 0 if success.
144 * Return -1 if peer was not put in list/updated.
145 */
146static int add_closepeer(Group_Chat *chat, const uint8_t *client_id, IP_Port ip_port)
147{
148 uint32_t i;
149
150 for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* Check if node is already in list, if it is update its last_recv */
151 if (id_equal(chat->close[i].client_id, client_id)) {
152 chat->close[i].last_recv = unix_time();
153 return 0;
154 }
155 }
156
157 for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* Try replacing bad nodes first */
158 if (is_timeout(chat->close[i].last_recv, BAD_GROUPNODE_TIMEOUT)) {
159 id_copy(chat->close[i].client_id, client_id);
160 chat->close[i].ip_port = ip_port;
161 chat->close[i].last_recv = unix_time();
162 return 0;
163 }
164 }
165
166 for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* Replace nodes if given one is closer. */
167 if (id_closest_groupchats(chat->self_public_key, chat->close[i].client_id, client_id) == 2) {
168 id_copy(chat->close[i].client_id, client_id);
169 chat->close[i].ip_port = ip_port;
170 chat->close[i].last_recv = unix_time();
171 return 0;
172 }
173 }
174
175 return -1;
176}
177
178static int send_groupchatpacket(const Group_Chat *chat, IP_Port ip_port, const uint8_t *public_key, const uint8_t *data,
179 uint32_t length, uint8_t request_id)
180{
181 if (id_equal(chat->self_public_key, public_key))
182 return -1;
183
184 uint8_t packet[MAX_CRYPTO_REQUEST_SIZE];
185 int len = create_request(chat->self_public_key, chat->self_secret_key, packet, public_key, data, length, request_id);
186 packet[0] = NET_PACKET_GROUP_CHATS;
187
188 if (len == -1)
189 return -1;
190
191 if (sendpacket(chat->net, ip_port, packet, len) == len)
192 return 0;
193
194 return -1;
195
196}
197
198/*
199 * Send data to all peers in close peer list.
200 *
201 * return the number of peers the packet was sent to.
202 */
203static uint8_t sendto_allpeers(const Group_Chat *chat, const uint8_t *data, uint16_t length, uint8_t request_id)
204{
205 uint16_t sent = 0;
206 uint32_t i;
207
208 for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) {
209 if (ip_isset(&chat->close[i].ip_port.ip) &&
210 !is_timeout(chat->close[i].last_recv, BAD_GROUPNODE_TIMEOUT)) {
211 if (send_groupchatpacket(chat, chat->close[i].ip_port, chat->close[i].client_id,
212 data, length, request_id) == 0)
213 ++sent;
214 }
215 }
216
217 return sent;
218}
219
220
221/*
222 * Add a peer to the group chat.
223 *
224 * return peernum if success or peer already in chat.
225 * return -1 if error.
226 */
227static int addpeer(Group_Chat *chat, const uint8_t *client_id)
228{
229 int peernum = peer_in_chat(chat, client_id);
230
231 if (peernum != -1)
232 return peernum;
233
234 Group_Peer *temp;
235 temp = realloc(chat->group, sizeof(Group_Peer) * (chat->numpeers + 1));
236
237 if (temp == NULL)
238 return -1;
239
240 memset(&(temp[chat->numpeers]), 0, sizeof(Group_Peer));
241 chat->group = temp;
242
243 id_copy(chat->group[chat->numpeers].client_id, client_id);
244 chat->group[chat->numpeers].last_recv = unix_time();
245 chat->group[chat->numpeers].last_recv_msgping = unix_time();
246 ++chat->numpeers;
247
248 if (chat->peer_namelistchange != NULL)
249 (*chat->peer_namelistchange)(chat, chat->numpeers - 1, CHAT_CHANGE_PEER_ADD, chat->group_namelistchange_userdata);
250
251 return (chat->numpeers - 1);
252}
253
254/*
255 * Set a peer from the group chat to deleted.
256 *
257 * return 0 if success
258 * return -1 if error.
259 */
260static int del_peer_set(Group_Chat *chat, int peernum)
261{
262 if ((uint32_t)peernum >= chat->numpeers)
263 return -1;
264
265 chat->group[peernum].deleted = 1;
266 chat->group[peernum].deleted_time = unix_time();
267 return 0;
268}
269
270/*
271 * Delete a peer from the group chat.
272 *
273 * return 0 if success
274 * return -1 if error.
275 */
276static int delpeer(Group_Chat *chat, int peernum)
277{
278 if ((uint32_t)peernum >= chat->numpeers)
279 return -1;
280
281 uint32_t i;
282
283 for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* If peer is in close list, time it out forcefully. */
284 if (id_equal(chat->close[i].client_id, chat->group[peernum].client_id)) {
285 chat->close[i].last_recv = 0;
286 break;
287 }
288 }
289
290 Group_Peer *temp;
291 --chat->numpeers;
292
293 if (chat->numpeers == 0) {
294 free(chat->group);
295 chat->group = NULL;
296 return 0;
297 }
298
299 if (chat->numpeers != (uint32_t)peernum)
300 memcpy(&chat->group[peernum], &chat->group[chat->numpeers], sizeof(Group_Peer));
301
302 temp = realloc(chat->group, sizeof(Group_Peer) * (chat->numpeers));
303
304 if (temp == NULL)
305 return -1;
306
307 chat->group = temp;
308
309 if (chat->peer_namelistchange != NULL) {
310 (*chat->peer_namelistchange)(chat, peernum, CHAT_CHANGE_PEER_DEL, chat->group_namelistchange_userdata);
311 }
312
313 return 0;
314}
315
316/* Copy the name of peernum to name.
317 * name must be at least MAX_NICK_BYTES long.
318 *
319 * return length of name if success
320 * return -1 if failure
321 */
322int group_peername(const Group_Chat *chat, int peernum, uint8_t *name)
323{
324 if ((uint32_t)peernum >= chat->numpeers)
325 return -1;
326
327 if (chat->group[peernum].nick_len == 0) {
328 /* memcpy(name, "NSA agent", 10); */ /* Srsly? */ /* Kindly remind the user that someone with no name might be a moronic NSA agent.*/
329 name[0] = 0;
330 return 0;
331 }
332
333 memcpy(name, chat->group[peernum].nick, chat->group[peernum].nick_len);
334 return chat->group[peernum].nick_len;
335}
336
337static void setnick(Group_Chat *chat, int peernum, const uint8_t *contents, uint16_t contents_len)
338{
339 if (contents_len > MAX_NICK_BYTES || contents_len == 0)
340 return;
341
342 /* same name as already stored? */
343 if (chat->group[peernum].nick_len == contents_len)
344 if (!memcmp(chat->group[peernum].nick, contents, contents_len))
345 return;
346
347 memcpy(chat->group[peernum].nick, contents, contents_len);
348 chat->group[peernum].nick_len = contents_len;
349
350 if (chat->peer_namelistchange != NULL)
351 (*chat->peer_namelistchange)(chat, peernum, CHAT_CHANGE_PEER_NAME, chat->group_namelistchange_userdata);
352}
353
354/* min time between pings sent to one peer in seconds */
355/* TODO: move this to global section */
356#define GROUP_PING_TIMEOUT 5
357
358static int send_getnodes(const Group_Chat *chat, IP_Port ip_port, int peernum)
359{
360 if ((uint32_t)peernum >= chat->numpeers)
361 return -1;
362
363 if (!is_timeout(chat->group[peernum].last_pinged, GROUP_PING_TIMEOUT))
364 return -1;
365
366 getnodes_data contents;
367 contents.pingid = random_64b();
368
369 chat->group[peernum].last_pinged = unix_time();
370 chat->group[peernum].pingid = contents.pingid;
371 chat->group[peernum].ping_via = ip_port;
372
373 if (chat->assoc) {
374 IPPTs ippts;
375 ippts.timestamp = unix_time();
376 ippts.ip_port = ip_port;
377
378 Assoc_add_entry(chat->assoc, chat->group[peernum].client_id, &ippts, NULL, 1);
379 }
380
381 return send_groupchatpacket(chat, ip_port, chat->group[peernum].client_id, (uint8_t *)&contents, sizeof(contents),
382 CRYPTO_PACKET_GROUP_CHAT_GET_NODES);
383}
384
385static int send_sendnodes(const Group_Chat *chat, IP_Port ip_port, int peernum, uint64_t pingid)
386{
387 if ((uint32_t)peernum >= chat->numpeers)
388 return -1;
389
390 sendnodes_data contents;
391 contents.pingid = pingid;
392 uint32_t i, j = 0;
393
394 for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) {
395 if (!is_timeout(chat->close[i].last_recv, BAD_GROUPNODE_TIMEOUT)) {
396 id_copy(contents.nodes[j].client_id, chat->close[i].client_id);
397 contents.nodes[j].ip_port = chat->close[i].ip_port;
398 to_net_family(&contents.nodes[j].ip_port.ip);
399 ++j;
400 }
401 }
402
403 return send_groupchatpacket(chat, ip_port, chat->group[peernum].client_id, (uint8_t *)&contents,
404 sizeof(contents.pingid) + sizeof(groupchat_nodes) * j, CRYPTO_PACKET_GROUP_CHAT_SEND_NODES);
405}
406
407static int handle_getnodes(const Group_Chat *chat, IP_Port source, int peernum, const uint8_t *data, uint32_t len)
408{
409 if (len != sizeof(getnodes_data))
410 return 1;
411
412 if ((uint32_t)peernum >= chat->numpeers)
413 return 1;
414
415 getnodes_data contents;
416 memcpy(&contents, data, sizeof(contents));
417 send_sendnodes(chat, source, peernum, contents.pingid);
418
419 if (peer_okping(chat, chat->group[peernum].client_id) > 0)
420 send_getnodes(chat, source, peernum);
421
422 return 0;
423}
424
425static int handle_sendnodes(Group_Chat *chat, IP_Port source, int peernum, const uint8_t *data, uint32_t len)
426{
427 if ((uint32_t)peernum >= chat->numpeers)
428 return 1;
429
430 if (len > sizeof(sendnodes_data) || len < sizeof(uint64_t))
431 return 1;
432
433 if ((len - sizeof(uint64_t)) % sizeof(groupchat_nodes) != 0)
434 return 1;
435
436 if (is_timeout(chat->group[peernum].last_pinged, GROUP_PING_TIMEOUT))
437 return 1;
438
439 sendnodes_data contents;
440 memcpy(&contents, data, len);
441
442 if (contents.pingid != chat->group[peernum].pingid)
443 return 1;
444
445 uint16_t numnodes = (len - sizeof(contents.pingid)) / sizeof(groupchat_nodes);
446 uint32_t i;
447
448 IPPTs ippts_send;
449 ippts_send.timestamp = unix_time();
450
451 for (i = 0; i < numnodes; ++i) {
452 if (peer_okping(chat, contents.nodes[i].client_id) > 0) {
453 int peern = peer_in_chat(chat, contents.nodes[i].client_id);
454
455 if (peern == -1) { /*NOTE: This is just for testing and will be removed later.*/
456 peern = addpeer(chat, contents.nodes[i].client_id);
457 }
458
459 if (peern == -1)
460 continue;
461
462 to_host_family(&contents.nodes[i].ip_port.ip);
463 send_getnodes(chat, contents.nodes[i].ip_port, peern);
464
465 if (chat->assoc) {
466 ippts_send.ip_port = contents.nodes[i].ip_port;
467 Assoc_add_entry(chat->assoc, contents.nodes[i].client_id, &ippts_send, NULL, 0);
468 }
469 }
470 }
471
472 add_closepeer(chat, chat->group[peernum].client_id, source);
473
474 return 0;
475}
476
477#define GROUP_DATA_MIN_SIZE (crypto_box_PUBLICKEYBYTES + sizeof(uint32_t) + 1)
478static void send_names_new_peer(Group_Chat *chat);
479
480static int handle_data(Group_Chat *chat, const uint8_t *data, uint32_t len)
481{
482 if (len < GROUP_DATA_MIN_SIZE)
483 return 1;
484
485//TODO:
486 int peernum = peer_in_chat(chat, data);
487
488 if (peernum == -1) { /*NOTE: This is just for testing and will be removed later.*/
489 if (data[crypto_box_PUBLICKEYBYTES + sizeof(uint32_t)] != GROUP_CHAT_QUIT)
490 peernum = addpeer(chat, data);
491 }
492
493 if (peernum == -1)
494 return 1;
495
496 if (chat->group[peernum].deleted)
497 return 1;
498
499 /* Spam prevention (1 message per peer per second limit.)
500
501 if (chat->group[peernum].last_recv == temp_time)
502 return 1;
503 */
504 chat->group[peernum].last_recv = unix_time();
505
506 uint32_t message_num;
507 memcpy(&message_num, data + crypto_box_PUBLICKEYBYTES, sizeof(uint32_t));
508 message_num = ntohl(message_num);
509
510 if (chat->group[peernum].last_message_number == 0) {
511 chat->group[peernum].last_message_number = message_num;
512 } else if (message_num - chat->group[peernum].last_message_number > 64 ||
513 message_num == chat->group[peernum].last_message_number)
514 return 1;
515
516 chat->group[peernum].last_message_number = message_num;
517
518 int handled = 1;
519 const uint8_t *contents = data + GROUP_DATA_MIN_SIZE;
520 uint16_t contents_len = len - GROUP_DATA_MIN_SIZE;
521
522 switch (data[crypto_box_PUBLICKEYBYTES + sizeof(message_num)]) {
523 case GROUP_CHAT_PING: /* If message is ping */
524 if (contents_len != 0)
525 return 1;
526
527 chat->group[peernum].last_recv_msgping = unix_time();
528 break;
529
530 case GROUP_CHAT_NEW_PEER: /* If message is new peer */
531 if (contents_len != crypto_box_PUBLICKEYBYTES)
532 return 1;
533
534 addpeer(chat, contents);
535 send_names_new_peer(chat);
536 break;
537
538 case GROUP_CHAT_QUIT: /* If peer tells us he is quitting */
539 if (contents_len != 0)
540 return 1;
541
542 del_peer_set(chat, peernum);
543 break;
544
545 case GROUP_CHAT_PEER_NICK:
546 if (contents_len > MAX_NICK_BYTES || contents_len == 0)
547 return 1;
548
549 setnick(chat, peernum, contents, contents_len);
550 break;
551
552 case GROUP_CHAT_CHAT_MESSAGE: /* If message is chat message */
553 if (chat->group_message != NULL)
554 (*chat->group_message)(chat, peernum, contents, contents_len, chat->group_message_userdata);
555
556 break;
557
558 case GROUP_CHAT_ACTION: /* if message is a peer action */
559 if (chat->group_action != NULL)
560 (*chat->group_action)(chat, peernum, contents, contents_len, chat->group_action_userdata);
561
562 break;
563
564 default:
565 handled = 0;
566 break;
567
568 }
569
570 if (handled == 1) {
571 sendto_allpeers(chat, data, len, CRYPTO_PACKET_GROUP_CHAT_BROADCAST);
572 return 0;
573 }
574
575 return 1;
576}
577
578static uint8_t send_data(Group_Chat *chat, const uint8_t *data, uint32_t len, uint8_t message_id)
579{
580 if (len + GROUP_DATA_MIN_SIZE > MAX_CRYPTO_REQUEST_SIZE) /*NOTE: not the real maximum len.*/
581 return 1;
582
583 uint8_t packet[MAX_CRYPTO_REQUEST_SIZE];
584 ++chat->message_number;
585
586 if (chat->message_number == 0)
587 chat->message_number = 1;
588
589 uint32_t message_num = htonl(chat->message_number);
590//TODO
591 id_copy(packet, chat->self_public_key);
592 memcpy(packet + crypto_box_PUBLICKEYBYTES, &message_num, sizeof(message_num));
593
594 if (len != 0)
595 memcpy(packet + GROUP_DATA_MIN_SIZE, data, len);
596
597 packet[crypto_box_PUBLICKEYBYTES + sizeof(message_num)] = message_id;
598 return sendto_allpeers(chat, packet, len + GROUP_DATA_MIN_SIZE, CRYPTO_PACKET_GROUP_CHAT_BROADCAST);
599}
600/*
601 * Handle get nodes group packet.
602 *
603 * return 0 if handled correctly.
604 * return 1 if error.
605 */
606
607int handle_groupchatpacket(Group_Chat *chat, IP_Port source, const uint8_t *packet, uint32_t length)
608{
609 if (length > MAX_CRYPTO_REQUEST_SIZE)
610 return 1;
611
612 uint8_t public_key[crypto_box_PUBLICKEYBYTES];
613 uint8_t data[MAX_CRYPTO_REQUEST_SIZE];
614 uint8_t number;
615 int len = handle_request(chat->self_public_key, chat->self_secret_key, public_key, data, &number, packet, length);
616
617 if (len <= 0)
618 return 1;
619
620 if (id_equal(chat->self_public_key, public_key))
621 return 1;
622
623 int peernum = peer_in_chat(chat, public_key);
624
625 if (peernum == -1)
626 return 1;
627
628 switch (number) {
629 case CRYPTO_PACKET_GROUP_CHAT_GET_NODES:
630 return handle_getnodes(chat, source, peernum, data, len);
631
632 case CRYPTO_PACKET_GROUP_CHAT_SEND_NODES:
633 return handle_sendnodes(chat, source, peernum, data, len);
634
635 case CRYPTO_PACKET_GROUP_CHAT_BROADCAST:
636 return handle_data(chat, data, len);
637
638 default:
639 return 1;
640 }
641
642 return 1;
643}
644
645uint32_t group_sendmessage(Group_Chat *chat, const uint8_t *message, uint32_t length)
646{
647 return send_data(chat, message, length, GROUP_CHAT_CHAT_MESSAGE); //TODO: better return values?
648}
649
650uint32_t group_sendaction(Group_Chat *chat, const uint8_t *action, uint32_t length)
651{
652 return send_data(chat, action, length, GROUP_CHAT_ACTION);
653}
654
655/*
656 * Send id/nick combo to the group.
657 *
658 * returns the number of peers it has sent it to.
659 */
660static uint32_t group_send_nick(Group_Chat *chat, uint8_t *nick, uint16_t nick_len)
661{
662 if (nick_len > MAX_NICK_BYTES)
663 return 0;
664
665 return send_data(chat, nick, nick_len, GROUP_CHAT_PEER_NICK);
666}
667
668int set_nick(Group_Chat *chat, const uint8_t *nick, uint16_t nick_len)
669{
670 if (nick_len > MAX_NICK_BYTES || nick_len == 0)
671 return -1;
672
673 memcpy(chat->nick, nick, nick_len);
674 chat->nick_len = nick_len;
675 group_send_nick(chat, chat->nick, chat->nick_len);
676 return 0;
677}
678
679uint32_t group_newpeer(Group_Chat *chat, const uint8_t *client_id)
680{
681 addpeer(chat, client_id);
682 return send_data(chat, client_id, crypto_box_PUBLICKEYBYTES, GROUP_CHAT_NEW_PEER); //TODO: better return values?
683}
684
685void callback_groupmessage(Group_Chat *chat, void (*function)(Group_Chat *chat, int, const uint8_t *, uint16_t, void *),
686 void *userdata)
687{
688 chat->group_message = function;
689 chat->group_message_userdata = userdata;
690}
691
692void callback_groupaction(Group_Chat *chat, void (*function)(Group_Chat *chat, int, const uint8_t *, uint16_t, void *),
693 void *userdata)
694{
695 chat->group_action = function;
696 chat->group_action_userdata = userdata;
697}
698
699void callback_namelistchange(Group_Chat *chat, void (*function)(Group_Chat *chat, int peer, uint8_t change, void *),
700 void *userdata)
701{
702 chat->peer_namelistchange = function;
703 chat->group_namelistchange_userdata = userdata;
704}
705
706uint32_t group_numpeers(const Group_Chat *chat)
707{
708 return chat->numpeers;
709}
710
711uint32_t group_client_names(const Group_Chat *chat, uint8_t names[][MAX_NICK_BYTES], uint16_t lengths[],
712 uint16_t length)
713{
714 uint32_t i;
715
716 for (i = 0; i < chat->numpeers && i < length; ++i) {
717 lengths[i] = group_peername(chat, i, names[i]);
718 }
719
720 return i;
721}
722
723Group_Chat *new_groupchat(Networking_Core *net)
724{
725 unix_time_update();
726
727 if (net == 0)
728 return 0;
729
730 Group_Chat *chat = calloc(1, sizeof(Group_Chat));
731 chat->net = net;
732 crypto_box_keypair(chat->self_public_key, chat->self_secret_key);
733
734 /* (2^4) * 5 = 80 entries seems to be a moderate size */
735 chat->assoc = new_Assoc(4, 5, chat->self_public_key);
736
737 return chat;
738}
739
740#define NODE_PING_INTERVAL 10
741
742static void ping_close(Group_Chat *chat)
743{
744 uint32_t i;
745
746 for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) {
747 if (!is_timeout(chat->close[i].last_recv, BAD_GROUPNODE_TIMEOUT)) {
748 int peernum = peer_in_chat(chat, chat->close[i].client_id);
749
750 if (peernum == -1)
751 continue;
752
753 if (is_timeout(chat->group[peernum].last_pinged, NODE_PING_INTERVAL))
754 send_getnodes(chat, chat->close[i].ip_port, peernum);
755 }
756 }
757}
758
759/* Interval in seconds to send ping messages */
760#define GROUP_PING_INTERVAL 30
761
762static void ping_group(Group_Chat *chat)
763{
764 if (is_timeout(chat->last_sent_ping, GROUP_PING_INTERVAL)) {
765 if (send_data(chat, 0, 0, GROUP_CHAT_PING) != 0) /* Ping */
766 chat->last_sent_ping = unix_time();
767 }
768}
769
770#define DEL_PEER_DELAY 3
771static void del_dead_peers(Group_Chat *chat)
772{
773 uint32_t i;
774
775 for (i = 0; i < chat->numpeers; ++i) {
776 if (is_timeout(chat->group[i].last_recv_msgping, GROUP_PING_INTERVAL * 4)) {
777 delpeer(chat, i);
778 }
779
780 if (chat->group == NULL || i >= chat->numpeers)
781 break;
782
783 if (chat->group[i].deleted) {
784 if (is_timeout(chat->group[i].deleted_time, DEL_PEER_DELAY))
785 delpeer(chat, i);
786 }
787 }
788}
789
790#define NICK_SEND_INTERVAL 180
791static void send_names_new_peer(Group_Chat *chat)
792{
793 group_send_nick(chat, chat->nick, chat->nick_len);
794 chat->last_sent_nick = (unix_time() - NICK_SEND_INTERVAL) + 15;
795}
796static void send_names(Group_Chat *chat)
797{
798 /* send own nick from time to time, to let newly added peers be informed
799 * first time only: use a shorter timeframe, because we might not be in our own
800 * peer list yet */
801 if (is_timeout(chat->last_sent_nick, 180))
802 if (group_send_nick(chat, chat->nick, chat->nick_len) > 0) {
803 if (!chat->last_sent_nick)
804 chat->last_sent_nick = (unix_time() - NICK_SEND_INTERVAL) + 10;
805 else
806 chat->last_sent_nick = unix_time();
807 }
808}
809
810void do_groupchat(Group_Chat *chat)
811{
812 unix_time_update();
813 ping_close(chat);
814 ping_group(chat);
815 /* TODO: Maybe run this less? */
816 del_dead_peers(chat);
817 send_names(chat);
818}
819
820void kill_groupchat(Group_Chat *chat)
821{
822 send_data(chat, 0, 0, GROUP_CHAT_QUIT);
823 kill_Assoc(chat->assoc);
824 free(chat->group);
825 free(chat);
826}
827
828void chat_bootstrap(Group_Chat *chat, IP_Port ip_port, const uint8_t *client_id)
829{
830 send_getnodes(chat, ip_port, addpeer(chat, client_id));
831}
832
833void chat_bootstrap_nonlazy(Group_Chat *chat, IP_Port ip_port, const uint8_t *client_id)
834{
835 send_getnodes(chat, ip_port, addpeer(chat, client_id));
836 add_closepeer(chat, client_id, ip_port);
837}