summaryrefslogtreecommitdiff
path: root/toxcore/Messenger.c
diff options
context:
space:
mode:
authorjin-eld <jin at mediatomb dot cc>2013-08-04 15:10:37 +0300
committerjin-eld <jin at mediatomb dot cc>2013-08-24 03:25:07 +0300
commite658892793c42b2d058eed0937025ef2ddaaa372 (patch)
tree2a022cab057f2c16ca95860ed980092880052f6e /toxcore/Messenger.c
parente2aa8161adc85795fe4d63d4642f47e90937ddc2 (diff)
Rename core directory because of autoconf name clash
While doing the checks configure might generate "core" files and will then try to remove them. Having a "core" directory generates an error while runing the configure script. There's no workaround but to rename the core directory.
Diffstat (limited to 'toxcore/Messenger.c')
-rw-r--r--toxcore/Messenger.c1020
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
28static void set_friend_status(Messenger *m, int friendnumber, uint8_t status);
29static 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 */
37int 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 */
56int 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. */
73int 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 */
90static 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 */
108void 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 */
133int 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
202int 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 */
241int 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 */
270int 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 */
281uint32_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
298uint32_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 */
313int 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*/
320static 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 */
331static 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 */
346int 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 */
365uint16_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 */
384int 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
393int 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
409int 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 */
426int 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 */
436int 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
446int 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
453USERSTATUS 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
467USERSTATUS m_get_self_userstatus(Messenger *m)
468{
469 return m->userstatus;
470}
471
472static 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
477static 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
483static 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
489static 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
502static 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. */
508void 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. */
521void 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. */
527void 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
534void 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
540void 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
547void 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
554void 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
560void 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
566void 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
572static 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
587void 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
593int 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*/
615static 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 */
624Messenger *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 */
668void 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.
681void 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
873void 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. */
894void 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) */
906uint32_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() */
920void 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. */
944int 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}