diff options
Diffstat (limited to 'toxcore/Messenger.c')
-rw-r--r-- | toxcore/Messenger.c | 1020 |
1 files changed, 1020 insertions, 0 deletions
diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c new file mode 100644 index 00000000..c5dd8a40 --- /dev/null +++ b/toxcore/Messenger.c | |||
@@ -0,0 +1,1020 @@ | |||
1 | /* Messenger.c | ||
2 | * | ||
3 | * An implementation of a simple text chat only messenger on the tox network core. | ||
4 | * | ||
5 | * Copyright (C) 2013 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 | #include "Messenger.h" | ||
25 | |||
26 | #define MIN(a,b) (((a)<(b))?(a):(b)) | ||
27 | |||
28 | static void set_friend_status(Messenger *m, int friendnumber, uint8_t status); | ||
29 | static int write_cryptpacket_id(Messenger *m, int friendnumber, uint8_t packet_id, uint8_t *data, uint32_t length); | ||
30 | |||
31 | /* 1 if we are online | ||
32 | 0 if we are offline | ||
33 | static uint8_t online; */ | ||
34 | |||
35 | /* set the size of the friend list to numfriends | ||
36 | return -1 if realloc fails */ | ||
37 | int realloc_friendlist(Messenger *m, uint32_t num) | ||
38 | { | ||
39 | if (num == 0) { | ||
40 | free(m->friendlist); | ||
41 | m->friendlist = NULL; | ||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | Friend *newfriendlist = realloc(m->friendlist, num * sizeof(Friend)); | ||
46 | |||
47 | if (newfriendlist == NULL) | ||
48 | return -1; | ||
49 | |||
50 | m->friendlist = newfriendlist; | ||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | /* return the friend id associated to that public key. | ||
55 | return -1 if no such friend */ | ||
56 | int getfriend_id(Messenger *m, uint8_t *client_id) | ||
57 | { | ||
58 | uint32_t i; | ||
59 | |||
60 | for (i = 0; i < m->numfriends; ++i) { | ||
61 | if (m->friendlist[i].status > 0) | ||
62 | if (memcmp(client_id, m->friendlist[i].client_id, crypto_box_PUBLICKEYBYTES) == 0) | ||
63 | return i; | ||
64 | } | ||
65 | |||
66 | return -1; | ||
67 | } | ||
68 | |||
69 | /* copies the public key associated to that friend id into client_id buffer. | ||
70 | make sure that client_id is of size CLIENT_ID_SIZE. | ||
71 | return 0 if success | ||
72 | return -1 if failure. */ | ||
73 | int getclient_id(Messenger *m, int friend_id, uint8_t *client_id) | ||
74 | { | ||
75 | if (friend_id >= m->numfriends || friend_id < 0) | ||
76 | return -1; | ||
77 | |||
78 | if (m->friendlist[friend_id].status > 0) { | ||
79 | memcpy(client_id, m->friendlist[friend_id].client_id, CLIENT_ID_SIZE); | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | return -1; | ||
84 | } | ||
85 | /* | ||
86 | * returns a uint16_t that represents the checksum of address of length len | ||
87 | * | ||
88 | * TODO: Another checksum algorithm might be better. | ||
89 | */ | ||
90 | static uint16_t address_checksum(uint8_t *address, uint32_t len) | ||
91 | { | ||
92 | uint8_t checksum[2] = {0}; | ||
93 | uint16_t check; | ||
94 | uint32_t i; | ||
95 | |||
96 | for (i = 0; i < len; ++i) | ||
97 | checksum[i % 2] ^= address[i]; | ||
98 | |||
99 | memcpy(&check, checksum, sizeof(check)); | ||
100 | return check; | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | * returns a FRIEND_ADDRESS_SIZE byte address to give to others. | ||
105 | * format: [client_id (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)] | ||
106 | * | ||
107 | */ | ||
108 | void getaddress(Messenger *m, uint8_t *address) | ||
109 | { | ||
110 | memcpy(address, m->net_crypto->self_public_key, crypto_box_PUBLICKEYBYTES); | ||
111 | uint32_t nospam = get_nospam(&(m->fr)); | ||
112 | memcpy(address + crypto_box_PUBLICKEYBYTES, &nospam, sizeof(nospam)); | ||
113 | uint16_t checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum)); | ||
114 | memcpy(address + crypto_box_PUBLICKEYBYTES + sizeof(nospam), &checksum, sizeof(checksum)); | ||
115 | } | ||
116 | |||
117 | /* | ||
118 | * add a friend | ||
119 | * set the data that will be sent along with friend request | ||
120 | * address is the address of the friend (returned by getaddress of the friend you wish to add) it must be FRIEND_ADDRESS_SIZE bytes. TODO: add checksum. | ||
121 | * data is the data and length is the length | ||
122 | * returns the friend number if success | ||
123 | * return FA_TOOLONG if message length is too long | ||
124 | * return FAERR_NOMESSAGE if no message (message length must be >= 1 byte) | ||
125 | * return FAERR_OWNKEY if user's own key | ||
126 | * return FAERR_ALREADYSENT if friend request already sent or already a friend | ||
127 | * return FAERR_UNKNOWN for unknown error | ||
128 | * return FAERR_BADCHECKSUM if bad checksum in address | ||
129 | * return FAERR_SETNEWNOSPAM if the friend was already there but the nospam was different | ||
130 | * (the nospam for that friend was set to the new one) | ||
131 | * return FAERR_NOMEM if increasing the friend list size fails | ||
132 | */ | ||
133 | int m_addfriend(Messenger *m, uint8_t *address, uint8_t *data, uint16_t length) | ||
134 | { | ||
135 | if (length >= (MAX_DATA_SIZE - crypto_box_PUBLICKEYBYTES | ||
136 | - crypto_box_NONCEBYTES - crypto_box_BOXZEROBYTES | ||
137 | + crypto_box_ZEROBYTES)) | ||
138 | return FAERR_TOOLONG; | ||
139 | |||
140 | uint8_t client_id[crypto_box_PUBLICKEYBYTES]; | ||
141 | memcpy(client_id, address, crypto_box_PUBLICKEYBYTES); | ||
142 | uint16_t check, checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum)); | ||
143 | memcpy(&check, address + crypto_box_PUBLICKEYBYTES + sizeof(uint32_t), sizeof(check)); | ||
144 | |||
145 | if (check != checksum) | ||
146 | return FAERR_BADCHECKSUM; | ||
147 | |||
148 | if (length < 1) | ||
149 | return FAERR_NOMESSAGE; | ||
150 | |||
151 | if (memcmp(client_id, m->net_crypto->self_public_key, crypto_box_PUBLICKEYBYTES) == 0) | ||
152 | return FAERR_OWNKEY; | ||
153 | |||
154 | int friend_id = getfriend_id(m, client_id); | ||
155 | |||
156 | if (friend_id != -1) { | ||
157 | uint32_t nospam; | ||
158 | memcpy(&nospam, address + crypto_box_PUBLICKEYBYTES, sizeof(nospam)); | ||
159 | |||
160 | if (m->friendlist[friend_id].friendrequest_nospam == nospam) | ||
161 | return FAERR_ALREADYSENT; | ||
162 | |||
163 | m->friendlist[friend_id].friendrequest_nospam = nospam; | ||
164 | return FAERR_SETNEWNOSPAM; | ||
165 | } | ||
166 | |||
167 | /* resize the friend list if necessary */ | ||
168 | if (realloc_friendlist(m, m->numfriends + 1) != 0) | ||
169 | return FAERR_NOMEM; | ||
170 | |||
171 | memset(&(m->friendlist[m->numfriends]), 0, sizeof(Friend)); | ||
172 | |||
173 | uint32_t i; | ||
174 | |||
175 | for (i = 0; i <= m->numfriends; ++i) { | ||
176 | if (m->friendlist[i].status == NOFRIEND) { | ||
177 | DHT_addfriend(m->dht, client_id); | ||
178 | m->friendlist[i].status = FRIEND_ADDED; | ||
179 | m->friendlist[i].crypt_connection_id = -1; | ||
180 | m->friendlist[i].friendrequest_lastsent = 0; | ||
181 | m->friendlist[i].friendrequest_timeout = FRIENDREQUEST_TIMEOUT; | ||
182 | memcpy(m->friendlist[i].client_id, client_id, CLIENT_ID_SIZE); | ||
183 | m->friendlist[i].statusmessage = calloc(1, 1); | ||
184 | m->friendlist[i].statusmessage_length = 1; | ||
185 | m->friendlist[i].userstatus = USERSTATUS_NONE; | ||
186 | memcpy(m->friendlist[i].info, data, length); | ||
187 | m->friendlist[i].info_size = length; | ||
188 | m->friendlist[i].message_id = 0; | ||
189 | m->friendlist[i].receives_read_receipts = 1; /* default: YES */ | ||
190 | memcpy(&(m->friendlist[i].friendrequest_nospam), address + crypto_box_PUBLICKEYBYTES, sizeof(uint32_t)); | ||
191 | |||
192 | if (m->numfriends == i) | ||
193 | ++ m->numfriends; | ||
194 | |||
195 | return i; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | return FAERR_UNKNOWN; | ||
200 | } | ||
201 | |||
202 | int m_addfriend_norequest(Messenger *m, uint8_t *client_id) | ||
203 | { | ||
204 | if (getfriend_id(m, client_id) != -1) | ||
205 | return -1; | ||
206 | |||
207 | /* resize the friend list if necessary */ | ||
208 | if (realloc_friendlist(m, m->numfriends + 1) != 0) | ||
209 | return FAERR_NOMEM; | ||
210 | |||
211 | memset(&(m->friendlist[m->numfriends]), 0, sizeof(Friend)); | ||
212 | |||
213 | uint32_t i; | ||
214 | |||
215 | for (i = 0; i <= m->numfriends; ++i) { | ||
216 | if (m->friendlist[i].status == NOFRIEND) { | ||
217 | DHT_addfriend(m->dht, client_id); | ||
218 | m->friendlist[i].status = FRIEND_CONFIRMED; | ||
219 | m->friendlist[i].crypt_connection_id = -1; | ||
220 | m->friendlist[i].friendrequest_lastsent = 0; | ||
221 | memcpy(m->friendlist[i].client_id, client_id, CLIENT_ID_SIZE); | ||
222 | m->friendlist[i].statusmessage = calloc(1, 1); | ||
223 | m->friendlist[i].statusmessage_length = 1; | ||
224 | m->friendlist[i].userstatus = USERSTATUS_NONE; | ||
225 | m->friendlist[i].message_id = 0; | ||
226 | m->friendlist[i].receives_read_receipts = 1; /* default: YES */ | ||
227 | |||
228 | if (m->numfriends == i) | ||
229 | ++ m->numfriends; | ||
230 | |||
231 | return i; | ||
232 | } | ||
233 | } | ||
234 | |||
235 | return -1; | ||
236 | } | ||
237 | |||
238 | /* remove a friend | ||
239 | return 0 if success | ||
240 | return -1 if failure */ | ||
241 | int m_delfriend(Messenger *m, int friendnumber) | ||
242 | { | ||
243 | if (friendnumber >= m->numfriends || friendnumber < 0) | ||
244 | return -1; | ||
245 | |||
246 | DHT_delfriend(m->dht, m->friendlist[friendnumber].client_id); | ||
247 | crypto_kill(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id); | ||
248 | free(m->friendlist[friendnumber].statusmessage); | ||
249 | memset(&(m->friendlist[friendnumber]), 0, sizeof(Friend)); | ||
250 | uint32_t i; | ||
251 | |||
252 | for (i = m->numfriends; i != 0; --i) { | ||
253 | if (m->friendlist[i - 1].status != NOFRIEND) | ||
254 | break; | ||
255 | } | ||
256 | |||
257 | m->numfriends = i; | ||
258 | |||
259 | if (realloc_friendlist(m, m->numfriends) != 0) | ||
260 | return FAERR_NOMEM; | ||
261 | |||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | /* return FRIEND_ONLINE if friend is online | ||
266 | return FRIEND_CONFIRMED if friend is confirmed | ||
267 | return FRIEND_REQUESTED if the friend request was sent | ||
268 | return FRIEND_ADDED if the friend was added | ||
269 | return NOFRIEND if there is no friend with that number */ | ||
270 | int m_friendstatus(Messenger *m, int friendnumber) | ||
271 | { | ||
272 | if (friendnumber < 0 || friendnumber >= m->numfriends) | ||
273 | return NOFRIEND; | ||
274 | |||
275 | return m->friendlist[friendnumber].status; | ||
276 | } | ||
277 | |||
278 | /* send a text chat message to an online friend | ||
279 | return the message id if packet was successfully put into the send queue | ||
280 | return 0 if it was not */ | ||
281 | uint32_t m_sendmessage(Messenger *m, int friendnumber, uint8_t *message, uint32_t length) | ||
282 | { | ||
283 | if (friendnumber < 0 || friendnumber >= m->numfriends) | ||
284 | return 0; | ||
285 | |||
286 | uint32_t msgid = ++m->friendlist[friendnumber].message_id; | ||
287 | |||
288 | if (msgid == 0) | ||
289 | msgid = 1; /* otherwise, false error */ | ||
290 | |||
291 | if (m_sendmessage_withid(m, friendnumber, msgid, message, length)) { | ||
292 | return msgid; | ||
293 | } | ||
294 | |||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | uint32_t m_sendmessage_withid(Messenger *m, int friendnumber, uint32_t theid, uint8_t *message, uint32_t length) | ||
299 | { | ||
300 | if (length >= (MAX_DATA_SIZE - sizeof(theid))) | ||
301 | return 0; | ||
302 | |||
303 | uint8_t temp[MAX_DATA_SIZE]; | ||
304 | theid = htonl(theid); | ||
305 | memcpy(temp, &theid, sizeof(theid)); | ||
306 | memcpy(temp + sizeof(theid), message, length); | ||
307 | return write_cryptpacket_id(m, friendnumber, PACKET_ID_MESSAGE, temp, length + sizeof(theid)); | ||
308 | } | ||
309 | |||
310 | /* send an action to an online friend | ||
311 | return 1 if packet was successfully put into the send queue | ||
312 | return 0 if it was not */ | ||
313 | int m_sendaction(Messenger *m, int friendnumber, uint8_t *action, uint32_t length) | ||
314 | { | ||
315 | return write_cryptpacket_id(m, friendnumber, PACKET_ID_ACTION, action, length); | ||
316 | } | ||
317 | |||
318 | /* send a name packet to friendnumber | ||
319 | length is the length with the NULL terminator*/ | ||
320 | static int m_sendname(Messenger *m, int friendnumber, uint8_t *name, uint16_t length) | ||
321 | { | ||
322 | if (length > MAX_NAME_LENGTH || length == 0) | ||
323 | return 0; | ||
324 | |||
325 | return write_cryptpacket_id(m, friendnumber, PACKET_ID_NICKNAME, name, length); | ||
326 | } | ||
327 | |||
328 | /* set the name of a friend | ||
329 | return 0 if success | ||
330 | return -1 if failure */ | ||
331 | static int setfriendname(Messenger *m, int friendnumber, uint8_t *name) | ||
332 | { | ||
333 | if (friendnumber >= m->numfriends || friendnumber < 0) | ||
334 | return -1; | ||
335 | |||
336 | memcpy(m->friendlist[friendnumber].name, name, MAX_NAME_LENGTH); | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | /* Set our nickname | ||
341 | name must be a string of maximum MAX_NAME_LENGTH length. | ||
342 | length must be at least 1 byte | ||
343 | length is the length of name with the NULL terminator | ||
344 | return 0 if success | ||
345 | return -1 if failure */ | ||
346 | int setname(Messenger *m, uint8_t *name, uint16_t length) | ||
347 | { | ||
348 | if (length > MAX_NAME_LENGTH || length == 0) | ||
349 | return -1; | ||
350 | |||
351 | memcpy(m->name, name, length); | ||
352 | m->name_length = length; | ||
353 | uint32_t i; | ||
354 | |||
355 | for (i = 0; i < m->numfriends; ++i) | ||
356 | m->friendlist[i].name_sent = 0; | ||
357 | |||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | /* get our nickname | ||
362 | put it in name | ||
363 | name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH bytes. | ||
364 | return the length of the name */ | ||
365 | uint16_t getself_name(Messenger *m, uint8_t *name, uint16_t nlen) | ||
366 | { | ||
367 | uint16_t len; | ||
368 | |||
369 | if (name == NULL || nlen == 0) { | ||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | len = MIN(nlen, m->name_length); | ||
374 | memcpy(name, m->name, m->name_length); | ||
375 | |||
376 | return len; | ||
377 | } | ||
378 | |||
379 | /* get name of friendnumber | ||
380 | put it in name | ||
381 | name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH bytes. | ||
382 | return 0 if success | ||
383 | return -1 if failure */ | ||
384 | int getname(Messenger *m, int friendnumber, uint8_t *name) | ||
385 | { | ||
386 | if (friendnumber >= m->numfriends || friendnumber < 0) | ||
387 | return -1; | ||
388 | |||
389 | memcpy(name, m->friendlist[friendnumber].name, MAX_NAME_LENGTH); | ||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | int m_set_statusmessage(Messenger *m, uint8_t *status, uint16_t length) | ||
394 | { | ||
395 | if (length > MAX_STATUSMESSAGE_LENGTH) | ||
396 | return -1; | ||
397 | |||
398 | memcpy(m->statusmessage, status, length); | ||
399 | m->statusmessage_length = length; | ||
400 | |||
401 | uint32_t i; | ||
402 | |||
403 | for (i = 0; i < m->numfriends; ++i) | ||
404 | m->friendlist[i].statusmessage_sent = 0; | ||
405 | |||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | int m_set_userstatus(Messenger *m, USERSTATUS status) | ||
410 | { | ||
411 | if (status >= USERSTATUS_INVALID) { | ||
412 | return -1; | ||
413 | } | ||
414 | |||
415 | m->userstatus = status; | ||
416 | uint32_t i; | ||
417 | |||
418 | for (i = 0; i < m->numfriends; ++i) | ||
419 | m->friendlist[i].userstatus_sent = 0; | ||
420 | |||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | /* return the size of friendnumber's user status | ||
425 | guaranteed to be at most MAX_STATUSMESSAGE_LENGTH */ | ||
426 | int m_get_statusmessage_size(Messenger *m, int friendnumber) | ||
427 | { | ||
428 | if (friendnumber >= m->numfriends || friendnumber < 0) | ||
429 | return -1; | ||
430 | |||
431 | return m->friendlist[friendnumber].statusmessage_length; | ||
432 | } | ||
433 | |||
434 | /* copy the user status of friendnumber into buf, truncating if needed to maxlen | ||
435 | bytes, use m_get_statusmessage_size to find out how much you need to allocate */ | ||
436 | int m_copy_statusmessage(Messenger *m, int friendnumber, uint8_t *buf, uint32_t maxlen) | ||
437 | { | ||
438 | if (friendnumber >= m->numfriends || friendnumber < 0) | ||
439 | return -1; | ||
440 | |||
441 | memset(buf, 0, maxlen); | ||
442 | memcpy(buf, m->friendlist[friendnumber].statusmessage, MIN(maxlen, MAX_STATUSMESSAGE_LENGTH) - 1); | ||
443 | return 0; | ||
444 | } | ||
445 | |||
446 | int m_copy_self_statusmessage(Messenger *m, uint8_t *buf, uint32_t maxlen) | ||
447 | { | ||
448 | memset(buf, 0, maxlen); | ||
449 | memcpy(buf, m->statusmessage, MIN(maxlen, MAX_STATUSMESSAGE_LENGTH) - 1); | ||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | USERSTATUS m_get_userstatus(Messenger *m, int friendnumber) | ||
454 | { | ||
455 | if (friendnumber >= m->numfriends || friendnumber < 0) | ||
456 | return USERSTATUS_INVALID; | ||
457 | |||
458 | USERSTATUS status = m->friendlist[friendnumber].userstatus; | ||
459 | |||
460 | if (status >= USERSTATUS_INVALID) { | ||
461 | status = USERSTATUS_NONE; | ||
462 | } | ||
463 | |||
464 | return status; | ||
465 | } | ||
466 | |||
467 | USERSTATUS m_get_self_userstatus(Messenger *m) | ||
468 | { | ||
469 | return m->userstatus; | ||
470 | } | ||
471 | |||
472 | static int send_statusmessage(Messenger *m, int friendnumber, uint8_t *status, uint16_t length) | ||
473 | { | ||
474 | return write_cryptpacket_id(m, friendnumber, PACKET_ID_STATUSMESSAGE, status, length); | ||
475 | } | ||
476 | |||
477 | static int send_userstatus(Messenger *m, int friendnumber, USERSTATUS status) | ||
478 | { | ||
479 | uint8_t stat = status; | ||
480 | return write_cryptpacket_id(m, friendnumber, PACKET_ID_USERSTATUS, &stat, sizeof(stat)); | ||
481 | } | ||
482 | |||
483 | static int send_ping(Messenger *m, int friendnumber) | ||
484 | { | ||
485 | m->friendlist[friendnumber].ping_lastsent = unix_time(); | ||
486 | return write_cryptpacket_id(m, friendnumber, PACKET_ID_PING, 0, 0); | ||
487 | } | ||
488 | |||
489 | static int set_friend_statusmessage(Messenger *m, int friendnumber, uint8_t *status, uint16_t length) | ||
490 | { | ||
491 | if (friendnumber >= m->numfriends || friendnumber < 0) | ||
492 | return -1; | ||
493 | |||
494 | uint8_t *newstatus = calloc(length, 1); | ||
495 | memcpy(newstatus, status, length); | ||
496 | free(m->friendlist[friendnumber].statusmessage); | ||
497 | m->friendlist[friendnumber].statusmessage = newstatus; | ||
498 | m->friendlist[friendnumber].statusmessage_length = length; | ||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | static void set_friend_userstatus(Messenger *m, int friendnumber, USERSTATUS status) | ||
503 | { | ||
504 | m->friendlist[friendnumber].userstatus = status; | ||
505 | } | ||
506 | |||
507 | /* Sets whether we send read receipts for friendnumber. */ | ||
508 | void m_set_sends_receipts(Messenger *m, int friendnumber, int yesno) | ||
509 | { | ||
510 | if (yesno != 0 || yesno != 1) | ||
511 | return; | ||
512 | |||
513 | if (friendnumber >= m->numfriends || friendnumber < 0) | ||
514 | return; | ||
515 | |||
516 | m->friendlist[friendnumber].receives_read_receipts = yesno; | ||
517 | } | ||
518 | |||
519 | /* static void (*friend_request)(uint8_t *, uint8_t *, uint16_t); */ | ||
520 | /* set the function that will be executed when a friend request is received. */ | ||
521 | void m_callback_friendrequest(Messenger *m, void (*function)(uint8_t *, uint8_t *, uint16_t, void *), void *userdata) | ||
522 | { | ||
523 | callback_friendrequest(&(m->fr), function, userdata); | ||
524 | } | ||
525 | |||
526 | /* set the function that will be executed when a message from a friend is received. */ | ||
527 | void m_callback_friendmessage(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *), | ||
528 | void *userdata) | ||
529 | { | ||
530 | m->friend_message = function; | ||
531 | m->friend_message_userdata = userdata; | ||
532 | } | ||
533 | |||
534 | void m_callback_action(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *), void *userdata) | ||
535 | { | ||
536 | m->friend_action = function; | ||
537 | m->friend_action_userdata = userdata; | ||
538 | } | ||
539 | |||
540 | void m_callback_namechange(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *), | ||
541 | void *userdata) | ||
542 | { | ||
543 | m->friend_namechange = function; | ||
544 | m->friend_namechange_userdata = userdata; | ||
545 | } | ||
546 | |||
547 | void m_callback_statusmessage(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *), | ||
548 | void *userdata) | ||
549 | { | ||
550 | m->friend_statusmessagechange = function; | ||
551 | m->friend_statuschange_userdata = userdata; | ||
552 | } | ||
553 | |||
554 | void m_callback_userstatus(Messenger *m, void (*function)(Messenger *m, int, USERSTATUS, void *), void *userdata) | ||
555 | { | ||
556 | m->friend_userstatuschange = function; | ||
557 | m->friend_userstatuschange_userdata = userdata; | ||
558 | } | ||
559 | |||
560 | void m_callback_read_receipt(Messenger *m, void (*function)(Messenger *m, int, uint32_t, void *), void *userdata) | ||
561 | { | ||
562 | m->read_receipt = function; | ||
563 | m->read_receipt_userdata = userdata; | ||
564 | } | ||
565 | |||
566 | void m_callback_connectionstatus(Messenger *m, void (*function)(Messenger *m, int, uint8_t, void *), void *userdata) | ||
567 | { | ||
568 | m->friend_connectionstatuschange = function; | ||
569 | m->friend_connectionstatuschange_userdata = userdata; | ||
570 | } | ||
571 | |||
572 | static void check_friend_connectionstatus(Messenger *m, int friendnumber, uint8_t status) | ||
573 | { | ||
574 | if (!m->friend_connectionstatuschange) | ||
575 | return; | ||
576 | |||
577 | if (status == NOFRIEND) | ||
578 | return; | ||
579 | |||
580 | const uint8_t was_connected = m->friendlist[friendnumber].status == FRIEND_ONLINE; | ||
581 | const uint8_t is_connected = status == FRIEND_ONLINE; | ||
582 | |||
583 | if (is_connected != was_connected) | ||
584 | m->friend_connectionstatuschange(m, friendnumber, is_connected, m->friend_connectionstatuschange_userdata); | ||
585 | } | ||
586 | |||
587 | void set_friend_status(Messenger *m, int friendnumber, uint8_t status) | ||
588 | { | ||
589 | check_friend_connectionstatus(m, friendnumber, status); | ||
590 | m->friendlist[friendnumber].status = status; | ||
591 | } | ||
592 | |||
593 | int write_cryptpacket_id(Messenger *m, int friendnumber, uint8_t packet_id, uint8_t *data, uint32_t length) | ||
594 | { | ||
595 | if (friendnumber < 0 || friendnumber >= m->numfriends) | ||
596 | return 0; | ||
597 | |||
598 | if (length >= MAX_DATA_SIZE || m->friendlist[friendnumber].status != FRIEND_ONLINE) | ||
599 | return 0; | ||
600 | |||
601 | uint8_t packet[length + 1]; | ||
602 | packet[0] = packet_id; | ||
603 | |||
604 | if (length != 0) | ||
605 | memcpy(packet + 1, data, length); | ||
606 | |||
607 | return write_cryptpacket(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id, packet, length + 1); | ||
608 | } | ||
609 | |||
610 | /*Interval in seconds between LAN discovery packet sending*/ | ||
611 | #define LAN_DISCOVERY_INTERVAL 60 | ||
612 | #define PORT 33445 | ||
613 | |||
614 | /*Send a LAN discovery packet every LAN_DISCOVERY_INTERVAL seconds*/ | ||
615 | static void LANdiscovery(Messenger *m) | ||
616 | { | ||
617 | if (m->last_LANdiscovery + LAN_DISCOVERY_INTERVAL < unix_time()) { | ||
618 | send_LANdiscovery(htons(PORT), m->net_crypto); | ||
619 | m->last_LANdiscovery = unix_time(); | ||
620 | } | ||
621 | } | ||
622 | |||
623 | /* run this at startup */ | ||
624 | Messenger *initMessenger(void) | ||
625 | { | ||
626 | Messenger *m = calloc(1, sizeof(Messenger)); | ||
627 | |||
628 | if ( ! m ) | ||
629 | return NULL; | ||
630 | |||
631 | IP ip; | ||
632 | ip.i = 0; | ||
633 | m->net = new_networking(ip, PORT); | ||
634 | |||
635 | if (m->net == NULL) { | ||
636 | free(m); | ||
637 | return NULL; | ||
638 | } | ||
639 | |||
640 | m->net_crypto = new_net_crypto(m->net); | ||
641 | |||
642 | if (m->net_crypto == NULL) { | ||
643 | kill_networking(m->net); | ||
644 | free(m); | ||
645 | return NULL; | ||
646 | } | ||
647 | |||
648 | m->dht = new_DHT(m->net_crypto); | ||
649 | |||
650 | if (m->dht == NULL) { | ||
651 | kill_net_crypto(m->net_crypto); | ||
652 | kill_networking(m->net); | ||
653 | free(m); | ||
654 | return NULL; | ||
655 | } | ||
656 | |||
657 | new_keys(m->net_crypto); | ||
658 | m_set_statusmessage(m, (uint8_t *)"Online", sizeof("Online")); | ||
659 | |||
660 | friendreq_init(&(m->fr), m->net_crypto); | ||
661 | LANdiscovery_init(m->dht); | ||
662 | set_nospam(&(m->fr), random_int()); | ||
663 | |||
664 | return m; | ||
665 | } | ||
666 | |||
667 | /* run this before closing shop */ | ||
668 | void cleanupMessenger(Messenger *m) | ||
669 | { | ||
670 | /* FIXME TODO ideally cleanupMessenger will mirror initMessenger | ||
671 | * this requires the other modules to expose cleanup functions | ||
672 | */ | ||
673 | kill_DHT(m->dht); | ||
674 | kill_net_crypto(m->net_crypto); | ||
675 | kill_networking(m->net); | ||
676 | free(m->friendlist); | ||
677 | free(m); | ||
678 | } | ||
679 | |||
680 | //TODO: make this function not suck. | ||
681 | void doFriends(Messenger *m) | ||
682 | { | ||
683 | /* TODO: add incoming connections and some other stuff. */ | ||
684 | uint32_t i; | ||
685 | int len; | ||
686 | uint8_t temp[MAX_DATA_SIZE]; | ||
687 | uint64_t temp_time = unix_time(); | ||
688 | |||
689 | for (i = 0; i < m->numfriends; ++i) { | ||
690 | if (m->friendlist[i].status == FRIEND_ADDED) { | ||
691 | int fr = send_friendrequest(m->dht, m->friendlist[i].client_id, m->friendlist[i].friendrequest_nospam, | ||
692 | m->friendlist[i].info, | ||
693 | m->friendlist[i].info_size); | ||
694 | |||
695 | if (fr >= 0) { | ||
696 | set_friend_status(m, i, FRIEND_REQUESTED); | ||
697 | m->friendlist[i].friendrequest_lastsent = temp_time; | ||
698 | } | ||
699 | } | ||
700 | |||
701 | if (m->friendlist[i].status == FRIEND_REQUESTED | ||
702 | || m->friendlist[i].status == FRIEND_CONFIRMED) { /* friend is not online */ | ||
703 | if (m->friendlist[i].status == FRIEND_REQUESTED) { | ||
704 | /* If we didn't connect to friend after successfully sending him a friend request the request is deemed | ||
705 | unsuccessful so we set the status back to FRIEND_ADDED and try again.*/ | ||
706 | if (m->friendlist[i].friendrequest_lastsent + m->friendlist[i].friendrequest_timeout < temp_time) { | ||
707 | set_friend_status(m, i, FRIEND_ADDED); | ||
708 | /* Double the default timeout everytime if friendrequest is assumed to have been | ||
709 | sent unsuccessfully. */ | ||
710 | m->friendlist[i].friendrequest_timeout *= 2; | ||
711 | } | ||
712 | } | ||
713 | |||
714 | IP_Port friendip = DHT_getfriendip(m->dht, m->friendlist[i].client_id); | ||
715 | |||
716 | switch (is_cryptoconnected(m->net_crypto, m->friendlist[i].crypt_connection_id)) { | ||
717 | case 0: | ||
718 | if (friendip.ip.i > 1) | ||
719 | m->friendlist[i].crypt_connection_id = crypto_connect(m->net_crypto, m->friendlist[i].client_id, friendip); | ||
720 | |||
721 | break; | ||
722 | |||
723 | case 3: /* Connection is established */ | ||
724 | set_friend_status(m, i, FRIEND_ONLINE); | ||
725 | m->friendlist[i].name_sent = 0; | ||
726 | m->friendlist[i].userstatus_sent = 0; | ||
727 | m->friendlist[i].statusmessage_sent = 0; | ||
728 | m->friendlist[i].ping_lastrecv = temp_time; | ||
729 | break; | ||
730 | |||
731 | case 4: | ||
732 | crypto_kill(m->net_crypto, m->friendlist[i].crypt_connection_id); | ||
733 | m->friendlist[i].crypt_connection_id = -1; | ||
734 | break; | ||
735 | |||
736 | default: | ||
737 | break; | ||
738 | } | ||
739 | } | ||
740 | |||
741 | while (m->friendlist[i].status == FRIEND_ONLINE) { /* friend is online */ | ||
742 | if (m->friendlist[i].name_sent == 0) { | ||
743 | if (m_sendname(m, i, m->name, m->name_length)) | ||
744 | m->friendlist[i].name_sent = 1; | ||
745 | } | ||
746 | |||
747 | if (m->friendlist[i].statusmessage_sent == 0) { | ||
748 | if (send_statusmessage(m, i, m->statusmessage, m->statusmessage_length)) | ||
749 | m->friendlist[i].statusmessage_sent = 1; | ||
750 | } | ||
751 | |||
752 | if (m->friendlist[i].userstatus_sent == 0) { | ||
753 | if (send_userstatus(m, i, m->userstatus)) | ||
754 | m->friendlist[i].userstatus_sent = 1; | ||
755 | } | ||
756 | |||
757 | if (m->friendlist[i].ping_lastsent + FRIEND_PING_INTERVAL < temp_time) { | ||
758 | send_ping(m, i); | ||
759 | } | ||
760 | |||
761 | len = read_cryptpacket(m->net_crypto, m->friendlist[i].crypt_connection_id, temp); | ||
762 | uint8_t packet_id = temp[0]; | ||
763 | uint8_t *data = temp + 1; | ||
764 | int data_length = len - 1; | ||
765 | |||
766 | if (len > 0) { | ||
767 | switch (packet_id) { | ||
768 | case PACKET_ID_PING: { | ||
769 | m->friendlist[i].ping_lastrecv = temp_time; | ||
770 | break; | ||
771 | } | ||
772 | |||
773 | case PACKET_ID_NICKNAME: { | ||
774 | if (data_length >= MAX_NAME_LENGTH || data_length == 0) | ||
775 | break; | ||
776 | |||
777 | if (m->friend_namechange) | ||
778 | m->friend_namechange(m, i, data, data_length, m->friend_namechange_userdata); | ||
779 | |||
780 | memcpy(m->friendlist[i].name, data, data_length); | ||
781 | m->friendlist[i].name[data_length - 1] = 0; /* make sure the NULL terminator is present. */ | ||
782 | break; | ||
783 | } | ||
784 | |||
785 | case PACKET_ID_STATUSMESSAGE: { | ||
786 | if (data_length == 0) | ||
787 | break; | ||
788 | |||
789 | uint8_t *status = calloc(MIN(data_length, MAX_STATUSMESSAGE_LENGTH), 1); | ||
790 | memcpy(status, data, MIN(data_length, MAX_STATUSMESSAGE_LENGTH)); | ||
791 | |||
792 | if (m->friend_statusmessagechange) | ||
793 | m->friend_statusmessagechange(m, i, status, MIN(data_length, MAX_STATUSMESSAGE_LENGTH), | ||
794 | m->friend_statuschange_userdata); | ||
795 | |||
796 | set_friend_statusmessage(m, i, status, MIN(data_length, MAX_STATUSMESSAGE_LENGTH)); | ||
797 | free(status); | ||
798 | break; | ||
799 | } | ||
800 | |||
801 | case PACKET_ID_USERSTATUS: { | ||
802 | if (data_length != 1) | ||
803 | break; | ||
804 | |||
805 | USERSTATUS status = data[0]; | ||
806 | |||
807 | if (m->friend_userstatuschange) | ||
808 | m->friend_userstatuschange(m, i, status, m->friend_userstatuschange_userdata); | ||
809 | |||
810 | set_friend_userstatus(m, i, status); | ||
811 | break; | ||
812 | } | ||
813 | |||
814 | case PACKET_ID_MESSAGE: { | ||
815 | uint8_t *message_id = data; | ||
816 | uint8_t message_id_length = 4; | ||
817 | uint8_t *message = data + message_id_length; | ||
818 | uint16_t message_length = data_length - message_id_length; | ||
819 | |||
820 | if (m->friendlist[i].receives_read_receipts) { | ||
821 | write_cryptpacket_id(m, i, PACKET_ID_RECEIPT, message_id, message_id_length); | ||
822 | } | ||
823 | |||
824 | if (m->friend_message) | ||
825 | (*m->friend_message)(m, i, message, message_length, m->friend_message_userdata); | ||
826 | |||
827 | break; | ||
828 | } | ||
829 | |||
830 | case PACKET_ID_ACTION: { | ||
831 | if (m->friend_action) | ||
832 | (*m->friend_action)(m, i, data, data_length, m->friend_action_userdata); | ||
833 | |||
834 | break; | ||
835 | } | ||
836 | |||
837 | case PACKET_ID_RECEIPT: { | ||
838 | uint32_t msgid; | ||
839 | |||
840 | if (data_length < sizeof(msgid)) | ||
841 | break; | ||
842 | |||
843 | memcpy(&msgid, data, sizeof(msgid)); | ||
844 | msgid = ntohl(msgid); | ||
845 | |||
846 | if (m->read_receipt) | ||
847 | (*m->read_receipt)(m, i, msgid, m->read_receipt_userdata); | ||
848 | |||
849 | break; | ||
850 | } | ||
851 | } | ||
852 | } else { | ||
853 | if (is_cryptoconnected(m->net_crypto, | ||
854 | m->friendlist[i].crypt_connection_id) == 4) { /* if the connection timed out, kill it */ | ||
855 | crypto_kill(m->net_crypto, m->friendlist[i].crypt_connection_id); | ||
856 | m->friendlist[i].crypt_connection_id = -1; | ||
857 | set_friend_status(m, i, FRIEND_CONFIRMED); | ||
858 | } | ||
859 | |||
860 | break; | ||
861 | } | ||
862 | |||
863 | if (m->friendlist[i].ping_lastrecv + FRIEND_CONNECTION_TIMEOUT < temp_time) { | ||
864 | /* if we stopped recieving ping packets kill it */ | ||
865 | crypto_kill(m->net_crypto, m->friendlist[i].crypt_connection_id); | ||
866 | m->friendlist[i].crypt_connection_id = -1; | ||
867 | set_friend_status(m, i, FRIEND_CONFIRMED); | ||
868 | } | ||
869 | } | ||
870 | } | ||
871 | } | ||
872 | |||
873 | void doInbound(Messenger *m) | ||
874 | { | ||
875 | uint8_t secret_nonce[crypto_box_NONCEBYTES]; | ||
876 | uint8_t public_key[crypto_box_PUBLICKEYBYTES]; | ||
877 | uint8_t session_key[crypto_box_PUBLICKEYBYTES]; | ||
878 | int inconnection = crypto_inbound(m->net_crypto, public_key, secret_nonce, session_key); | ||
879 | |||
880 | if (inconnection != -1) { | ||
881 | int friend_id = getfriend_id(m, public_key); | ||
882 | |||
883 | if (friend_id != -1) { | ||
884 | crypto_kill(m->net_crypto, m->friendlist[friend_id].crypt_connection_id); | ||
885 | m->friendlist[friend_id].crypt_connection_id = | ||
886 | accept_crypto_inbound(m->net_crypto, inconnection, public_key, secret_nonce, session_key); | ||
887 | |||
888 | set_friend_status(m, friend_id, FRIEND_CONFIRMED); | ||
889 | } | ||
890 | } | ||
891 | } | ||
892 | |||
893 | /* the main loop that needs to be run at least 20 times per second. */ | ||
894 | void doMessenger(Messenger *m) | ||
895 | { | ||
896 | networking_poll(m->net); | ||
897 | |||
898 | do_DHT(m->dht); | ||
899 | do_net_crypto(m->net_crypto); | ||
900 | doInbound(m); | ||
901 | doFriends(m); | ||
902 | LANdiscovery(m); | ||
903 | } | ||
904 | |||
905 | /* returns the size of the messenger data (for saving) */ | ||
906 | uint32_t Messenger_size(Messenger *m) | ||
907 | { | ||
908 | return crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES | ||
909 | + sizeof(uint32_t) // nospam | ||
910 | + sizeof(uint32_t) // DHT size | ||
911 | + DHT_size(m->dht) // DHT itself | ||
912 | + sizeof(uint32_t) // Friendlist size | ||
913 | + sizeof(Friend) * m->numfriends // Friendlist itself | ||
914 | + sizeof(uint16_t) // Own nickname length | ||
915 | + m->name_length // Own nickname | ||
916 | ; | ||
917 | } | ||
918 | |||
919 | /* save the messenger in data of size Messenger_size() */ | ||
920 | void Messenger_save(Messenger *m, uint8_t *data) | ||
921 | { | ||
922 | save_keys(m->net_crypto, data); | ||
923 | data += crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES; | ||
924 | uint32_t nospam = get_nospam(&(m->fr)); | ||
925 | memcpy(data, &nospam, sizeof(nospam)); | ||
926 | data += sizeof(nospam); | ||
927 | uint32_t size = DHT_size(m->dht); | ||
928 | memcpy(data, &size, sizeof(size)); | ||
929 | data += sizeof(size); | ||
930 | DHT_save(m->dht, data); | ||
931 | data += size; | ||
932 | size = sizeof(Friend) * m->numfriends; | ||
933 | memcpy(data, &size, sizeof(size)); | ||
934 | data += sizeof(size); | ||
935 | memcpy(data, m->friendlist, sizeof(Friend) * m->numfriends); | ||
936 | data += size; | ||
937 | uint16_t small_size = m->name_length; | ||
938 | memcpy(data, &small_size, sizeof(small_size)); | ||
939 | data += sizeof(small_size); | ||
940 | memcpy(data, m->name, small_size); | ||
941 | } | ||
942 | |||
943 | /* load the messenger from data of size length. */ | ||
944 | int Messenger_load(Messenger *m, uint8_t *data, uint32_t length) | ||
945 | { | ||
946 | if (length == ~0) | ||
947 | return -1; | ||
948 | |||
949 | if (length < crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES + sizeof(uint32_t) * 3) | ||
950 | return -1; | ||
951 | |||
952 | length -= crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES + sizeof(uint32_t) * 3; | ||
953 | load_keys(m->net_crypto, data); | ||
954 | data += crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES; | ||
955 | uint32_t nospam; | ||
956 | memcpy(&nospam, data, sizeof(nospam)); | ||
957 | set_nospam(&(m->fr), nospam); | ||
958 | data += sizeof(nospam); | ||
959 | uint32_t size; | ||
960 | memcpy(&size, data, sizeof(size)); | ||
961 | data += sizeof(size); | ||
962 | |||
963 | if (length < size) | ||
964 | return -1; | ||
965 | |||
966 | length -= size; | ||
967 | |||
968 | if (DHT_load(m->dht, data, size) == -1) | ||
969 | return -1; | ||
970 | |||
971 | data += size; | ||
972 | memcpy(&size, data, sizeof(size)); | ||
973 | data += sizeof(size); | ||
974 | |||
975 | if (length < size || size % sizeof(Friend) != 0) | ||
976 | return -1; | ||
977 | |||
978 | Friend *temp = malloc(size); | ||
979 | memcpy(temp, data, size); | ||
980 | |||
981 | uint16_t num = size / sizeof(Friend); | ||
982 | |||
983 | uint32_t i; | ||
984 | |||
985 | for (i = 0; i < num; ++i) { | ||
986 | if (temp[i].status >= 3) { | ||
987 | int fnum = m_addfriend_norequest(m, temp[i].client_id); | ||
988 | setfriendname(m, fnum, temp[i].name); | ||
989 | /* set_friend_statusmessage(fnum, temp[i].statusmessage, temp[i].statusmessage_length); */ | ||
990 | } else if (temp[i].status != 0) { | ||
991 | /* TODO: this is not a good way to do this. */ | ||
992 | uint8_t address[FRIEND_ADDRESS_SIZE]; | ||
993 | memcpy(address, temp[i].client_id, crypto_box_PUBLICKEYBYTES); | ||
994 | memcpy(address + crypto_box_PUBLICKEYBYTES, &(temp[i].friendrequest_nospam), sizeof(uint32_t)); | ||
995 | uint16_t checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum)); | ||
996 | memcpy(address + crypto_box_PUBLICKEYBYTES + sizeof(uint32_t), &checksum, sizeof(checksum)); | ||
997 | m_addfriend(m, address, temp[i].info, temp[i].info_size); | ||
998 | } | ||
999 | } | ||
1000 | |||
1001 | free(temp); | ||
1002 | data += size; | ||
1003 | length -= size; | ||
1004 | |||
1005 | uint16_t small_size; | ||
1006 | |||
1007 | if (length < sizeof(small_size)) | ||
1008 | return -1; | ||
1009 | |||
1010 | memcpy(&small_size, data, sizeof(small_size)); | ||
1011 | data += sizeof(small_size); | ||
1012 | length -= sizeof(small_size); | ||
1013 | |||
1014 | if (length != small_size) | ||
1015 | return -1; | ||
1016 | |||
1017 | setname(m, data, small_size); | ||
1018 | |||
1019 | return 0; | ||
1020 | } | ||