diff options
Diffstat (limited to 'toxcore/group_chats.c')
-rw-r--r-- | toxcore/group_chats.c | 837 |
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 | |||
40 | typedef struct { | ||
41 | uint64_t pingid; | ||
42 | //uint8_t client_id[crypto_box_PUBLICKEYBYTES]; | ||
43 | |||
44 | } getnodes_data; | ||
45 | |||
46 | typedef struct { | ||
47 | uint8_t client_id[crypto_box_PUBLICKEYBYTES]; | ||
48 | IP_Port ip_port; | ||
49 | |||
50 | } groupchat_nodes; | ||
51 | |||
52 | typedef 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 | |||
68 | static 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 | */ | ||
85 | static 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 | |||
113 | static 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 | */ | ||
146 | static 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 | |||
178 | static 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 | */ | ||
203 | static 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 | */ | ||
227 | static 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 | */ | ||
260 | static 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 | */ | ||
276 | static 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 | */ | ||
322 | int 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 | |||
337 | static 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 | |||
358 | static 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 | |||
385 | static 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 | |||
407 | static 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 | |||
425 | static 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) | ||
478 | static void send_names_new_peer(Group_Chat *chat); | ||
479 | |||
480 | static 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 | |||
578 | static 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 | |||
607 | int 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 | |||
645 | uint32_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 | |||
650 | uint32_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 | */ | ||
660 | static 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 | |||
668 | int 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 | |||
679 | uint32_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 | |||
685 | void 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 | |||
692 | void 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 | |||
699 | void 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 | |||
706 | uint32_t group_numpeers(const Group_Chat *chat) | ||
707 | { | ||
708 | return chat->numpeers; | ||
709 | } | ||
710 | |||
711 | uint32_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 | |||
723 | Group_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 | |||
742 | static 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 | |||
762 | static 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 | ||
771 | static 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 | ||
791 | static 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 | } | ||
796 | static 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 | |||
810 | void 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 | |||
820 | void 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 | |||
828 | void 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 | |||
833 | void 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 | } | ||