1 #include "tox_callbacks.h"
2 
3 #include "avatar.h"
4 #include "file_transfers.h"
5 #include "friend.h"
6 #include "groups.h"
7 #include "debug.h"
8 #include "macros.h"
9 #include "settings.h"
10 #include "text.h"
11 #include "utox.h"
12 #include "ui.h"
13 
14 #include "av/audio.h"
15 #include "av/utox_av.h"
16 
17 
18 #include <stdint.h>
19 #include <stdlib.h>
20 #include <string.h>
21 
callback_friend_request(Tox * UNUSED (tox),const uint8_t * id,const uint8_t * msg,size_t length,void * UNUSED (userdata))22 static void callback_friend_request(Tox *UNUSED(tox), const uint8_t *id, const uint8_t *msg, size_t length,
23                                     void *UNUSED(userdata)) {
24 
25     if (settings.block_friend_requests) {
26         LOG_WARN("Tox Callbacks", "Friend request ignored."); // TODO move to friend.c
27         return;
28     }
29 
30     length = utf8_validate(msg, length);
31 
32     uint16_t r_number = friend_request_new(id, msg, length);
33 
34     postmessage_utox(FRIEND_INCOMING_REQUEST, r_number, 0, NULL);
35     postmessage_audio(UTOXAUDIO_PLAY_NOTIFICATION, NOTIFY_TONE_FRIEND_REQUEST, 0, NULL);
36 }
37 
callback_friend_message(Tox * UNUSED (tox),uint32_t friend_number,TOX_MESSAGE_TYPE type,const uint8_t * message,size_t length,void * UNUSED (userdata))38 static void callback_friend_message(Tox *UNUSED(tox), uint32_t friend_number, TOX_MESSAGE_TYPE type,
39                                     const uint8_t *message, size_t length, void *UNUSED(userdata)) {
40     /* send message to UI */
41     FRIEND *f = get_friend(friend_number);
42     if (!f) {
43         LOG_ERR("Tox Callbacks", "Could not get friend with number: %u", friend_number);
44         return;
45     }
46 
47     switch (type) {
48         case TOX_MESSAGE_TYPE_NORMAL: {
49             message_add_type_text(&f->msg, 0, (char *)message, length, 1, 0);
50             LOG_INFO("Tox Callbacks", "Friend\t%u\t--\tStandard Message: %.*s" , friend_number, (int)length, message);
51             break;
52         }
53 
54         case TOX_MESSAGE_TYPE_ACTION: {
55             message_add_type_action(&f->msg, 0, (char *)message, length, 1, 0);
56             LOG_INFO("Tox Callbacks", "Friend\t%u\t--\tAction Message: %.*s" , friend_number, (int)length, message);
57             break;
58         }
59 
60         default: {
61             LOG_ERR("Tox Callbacks", "Friend\t%u\t--\tUnsupported message type: %.*s" , friend_number, (int)length, message);
62             break;
63         }
64     }
65     friend_notify_msg(f, (char *)message, length);
66 }
67 
callback_name_change(Tox * UNUSED (tox),uint32_t fid,const uint8_t * newname,size_t length,void * UNUSED (userdata))68 static void callback_name_change(Tox *UNUSED(tox), uint32_t fid, const uint8_t *newname, size_t length,
69                                  void *UNUSED(userdata)) {
70     length     = utf8_validate(newname, length);
71     void *data = malloc(length);
72     if (!data) {
73         LOG_FATAL_ERR(EXIT_MALLOC, "Tox Callbacks",
74                       "Could not alloc for name change callback (%uB)", length);
75     }
76 
77     memcpy(data, newname, length);
78     postmessage_utox(FRIEND_NAME, fid, length, data);
79     LOG_INFO("Tox Callbacks", "Friend\t%u\t--\tName:\t%.*s", fid, (int)length, newname);
80 }
81 
callback_status_message(Tox * UNUSED (tox),uint32_t fid,const uint8_t * newstatus,size_t length,void * UNUSED (userdata))82 static void callback_status_message(Tox *UNUSED(tox), uint32_t fid, const uint8_t *newstatus, size_t length,
83                                     void *UNUSED(userdata)) {
84     length     = utf8_validate(newstatus, length);
85     void *data = malloc(length);
86     if (!data) {
87         LOG_FATAL_ERR(EXIT_MALLOC, "Tox Callbacks",
88                       "Could not alloc for name change callback (%uB)", length);
89     }
90 
91     memcpy(data, newstatus, length);
92     postmessage_utox(FRIEND_STATUS_MESSAGE, fid, length, data);
93     LOG_INFO("Tox Callbacks", "Friend\t%u\t--\tStatus Message:\t%.*s", fid, (int)length, newstatus);
94 }
95 
callback_user_status(Tox * UNUSED (tox),uint32_t fid,TOX_USER_STATUS status,void * UNUSED (userdata))96 static void callback_user_status(Tox *UNUSED(tox), uint32_t fid, TOX_USER_STATUS status, void *UNUSED(userdata)) {
97     postmessage_utox(FRIEND_STATE, fid, status, NULL);
98     LOG_INFO("Tox Callbacks", "Friend\t%u\t--\tState:\t%u", fid, status);
99 }
100 
callback_typing_change(Tox * UNUSED (tox),uint32_t fid,bool is_typing,void * UNUSED (userdata))101 static void callback_typing_change(Tox *UNUSED(tox), uint32_t fid, bool is_typing, void *UNUSED(userdata)) {
102     postmessage_utox(FRIEND_TYPING, fid, is_typing, NULL);
103     LOG_DEBUG("Tox Callbacks", "Friend\t%u\t--\tTyping:\t%u", fid, is_typing);
104 }
105 
callback_read_receipt(Tox * UNUSED (tox),uint32_t fid,uint32_t receipt,void * UNUSED (userdata))106 static void callback_read_receipt(Tox *UNUSED(tox), uint32_t fid, uint32_t receipt, void *UNUSED(userdata)) {
107     FRIEND *f = get_friend(fid);
108     if (!f) {
109         LOG_ERR("Tox Callbacks", "Could not get friend with number: %u", fid);
110         return;
111     }
112 
113     messages_clear_receipt(&f->msg, receipt);
114     LOG_INFO("Tox Callbacks", "Friend\t%u\t--\tReceipt:\t%u", fid, receipt);
115 }
116 
callback_connection_status(Tox * tox,uint32_t fid,TOX_CONNECTION status,void * UNUSED (userdata))117 static void callback_connection_status(Tox *tox, uint32_t fid, TOX_CONNECTION status, void *UNUSED(userdata)) {
118     FRIEND *f = get_friend(fid);
119     if (!f) {
120         LOG_ERR("Tox Callbacks", "Could not get friend with number: %u", fid);
121         return;
122     }
123 
124     if (f->online && !status) {
125         ft_friend_offline(tox, fid);
126         if (f->call_state_self || f->call_state_friend) {
127             utox_av_local_disconnect(NULL, fid); /* TODO HACK, toxav doesn't supply a toxav_get_toxav_from_tox() yet. */
128         }
129     } else if (!f->online && !!status) {
130         ft_friend_online(tox, fid);
131         /* resend avatar info (in case it changed) */
132         /* Avatars must be sent LAST or they will clobber existing file transfers! */
133         avatar_on_friend_online(tox, fid);
134         friend_notify_status(f, (uint8_t *)f->status_message, f->status_length, S(STATUS_ONLINE));
135     }
136     postmessage_utox(FRIEND_ONLINE, fid, !!status, NULL);
137 
138     if (status == TOX_CONNECTION_UDP) {
139         LOG_INFO("Tox Callbacks", "Friend\t%u\t--\tOnline (UDP)", fid);
140     } else if (status == TOX_CONNECTION_TCP) {
141         LOG_INFO("Tox Callbacks", "Friend\t%u\t--\tOnline (TCP)", fid);
142     } else {
143         LOG_INFO("Tox Callbacks", "Friend\t%u\t--\tOffline", fid);
144         friend_notify_status(f, NULL, 0, S(STATUS_OFFLINE));
145     }
146 }
147 
utox_set_callbacks_friends(Tox * tox)148 void utox_set_callbacks_friends(Tox *tox) {
149     tox_callback_friend_request(tox, callback_friend_request);
150     tox_callback_friend_message(tox, callback_friend_message);
151     tox_callback_friend_name(tox, callback_name_change);
152     tox_callback_friend_status_message(tox, callback_status_message);
153     tox_callback_friend_status(tox, callback_user_status);
154     tox_callback_friend_typing(tox, callback_typing_change);
155     tox_callback_friend_read_receipt(tox, callback_read_receipt);
156     tox_callback_friend_connection_status(tox, callback_connection_status);
157 }
158 
159 void callback_av_group_audio(void *tox, uint32_t groupnumber, uint32_t peernumber, const int16_t *pcm, unsigned int samples,
160                              uint8_t channels, unsigned int sample_rate, void *userdata);
161 
callback_group_invite(Tox * tox,uint32_t fid,TOX_CONFERENCE_TYPE type,const uint8_t * data,size_t length,void * UNUSED (userdata))162 static void callback_group_invite(Tox *tox, uint32_t fid, TOX_CONFERENCE_TYPE type, const uint8_t *data, size_t length,
163                                   void *UNUSED(userdata))
164 {
165     LOG_NOTE("Tox Callbacks", "Group Invite (friend %i || type %u)", fid, type);
166 
167     uint32_t gid = UINT32_MAX;
168     if (type == TOX_CONFERENCE_TYPE_TEXT) {
169         gid = tox_conference_join(tox, fid, data, length, NULL);
170     } else if (type == TOX_CONFERENCE_TYPE_AV) {
171         gid = toxav_join_av_groupchat(tox, fid, data, length, callback_av_group_audio, NULL);
172     }
173 
174     if (gid == UINT32_MAX) {
175         LOG_ERR("Tox Callbacks", "Could not join group with type: %u", type);
176         return;
177     }
178 
179     GROUPCHAT *g = get_group(gid);
180     if (!g) {
181         group_create(gid, type == TOX_CONFERENCE_TYPE_AV ? true : false);
182     } else {
183         group_init(g, gid, type == TOX_CONFERENCE_TYPE_AV ? true : false);
184     }
185 
186     LOG_NOTE("Tox Callbacks", "auto join successful group number %u", gid);
187     postmessage_utox(GROUP_ADD, gid, 0, tox);
188 }
189 
callback_group_message(Tox * UNUSED (tox),uint32_t gid,uint32_t pid,TOX_MESSAGE_TYPE type,const uint8_t * message,size_t length,void * UNUSED (userdata))190 static void callback_group_message(Tox *UNUSED(tox), uint32_t gid, uint32_t pid, TOX_MESSAGE_TYPE type,
191                                    const uint8_t *message, size_t length, void *UNUSED(userdata)) {
192     GROUPCHAT *g = get_group(gid);
193 
194     switch (type) {
195         case TOX_MESSAGE_TYPE_ACTION: {
196             LOG_TRACE("Tox Callbacks", "Group Action (%u, %u): %.*s" , gid, pid, (int)length, message);
197             group_add_message(g, pid, message, length, MSG_TYPE_ACTION_TEXT);
198             break;
199         }
200         case TOX_MESSAGE_TYPE_NORMAL: {
201             LOG_INFO("Tox Callbacks", "Group Message (%u, %u): %.*s", gid, pid, (int)length, message);
202             group_add_message(g, pid, message, length, MSG_TYPE_TEXT);
203             break;
204         }
205     }
206     group_notify_msg(g, (const char *)message, length);
207     postmessage_utox(GROUP_MESSAGE, gid, pid, NULL);
208 }
209 
callback_group_peer_name_change(Tox * UNUSED (tox),uint32_t gid,uint32_t pid,const uint8_t * name,size_t length,void * UNUSED (userdata))210 static void callback_group_peer_name_change(Tox *UNUSED(tox), uint32_t gid, uint32_t pid, const uint8_t *name, size_t length, void *UNUSED(userdata)){
211 
212     LOG_DEBUG("Tox Callbacks", "Group:\tPeer name change (%u, %u)" , gid, pid);
213 
214     GROUPCHAT *g = get_group(gid);
215     if (!g) {
216         LOG_ERR("Tox Callbacks", "Could not get groupchat: %u", gid);
217         return;
218     }
219 
220     if (g->peer) {
221         if (!g->peer[pid]) {
222             LOG_ERR("Tox Callbacks", "Tox Group:\tERROR, can't set a name, for non-existent peer!" );
223             return;
224         }
225     } else {
226         // TODO can't happen
227         LOG_ERR("Tox Callbacks", "Tox Group:\tERROR, can't set a name, for non-existent Group!" );
228     }
229 
230     length = utf8_validate(name, length);
231     group_peer_name_change(g, pid, name, length);
232 
233     postmessage_utox(GROUP_PEER_NAME, gid, pid, NULL);
234 }
235 
callback_group_peer_list_changed(Tox * tox,uint32_t gid,void * UNUSED (userdata))236 static void callback_group_peer_list_changed(Tox *tox, uint32_t gid, void *UNUSED(userdata)){
237     GROUPCHAT *g = get_group(gid);
238     if (!g) {
239         LOG_ERR("Tox Callbacks", "Could not get group: %u", gid);
240         return;
241     }
242 
243     pthread_mutex_lock(&messages_lock); /* make sure that messages has posted before we continue */
244 
245     group_reset_peerlist(g);
246 
247     uint32_t number_peers = tox_conference_peer_count(tox, gid, NULL);
248 
249     g->peer = calloc(number_peers, sizeof(void *));
250     if (!g->peer) {
251         LOG_FATAL_ERR(EXIT_MALLOC, "Tox Callbacks", "Group:\tToxcore is very broken, but we couldn't alloc here.");
252     }
253 
254     /* I'm about to break some uTox style here, because I'm expecting
255      * the API to change soon, and I just can't when it's this broken */
256     for (uint32_t i = 0; i < number_peers; ++i) {
257         uint8_t     tmp[TOX_MAX_NAME_LENGTH];
258         size_t      len  = tox_conference_peer_get_name_size(tox, gid, i, NULL);
259         tox_conference_peer_get_name(tox, gid, i, tmp, NULL);
260         GROUP_PEER *peer = calloc(1, sizeof(*peer) + len + 1);
261         if (!peer) {
262             LOG_FATAL_ERR(EXIT_MALLOC, "Group", "Toxcore is very broken, but we couldn't calloc here.");
263         }
264         /* name and id number (it's worthless, but it's needed */
265         memcpy(peer->name, tmp, len);
266         peer->name_length = len;
267         peer->id          = i;
268         /* get static random color */
269         uint8_t pkey[TOX_PUBLIC_KEY_SIZE];
270         tox_conference_peer_get_public_key(tox, gid, i, pkey, NULL);
271         uint64_t pkey_to_number = 0;
272         for (int key_i = 0; key_i < TOX_PUBLIC_KEY_SIZE; ++key_i) {
273             pkey_to_number += pkey[key_i];
274         }
275         /* uTox doesnt' really use this for too much so let's fuck with the random seed.
276          * If you know crypto, and cringe, I know me too... you can blame @irungentoo */
277         srand(pkey_to_number);
278         peer->name_color = RGB(rand(), rand(), rand());
279         g->peer[i]       = peer;
280     }
281     g->peer_count = number_peers;
282 
283     postmessage_utox(GROUP_PEER_CHANGE, gid, 0, NULL);
284     pthread_mutex_unlock(&messages_lock); /* make sure that messages has posted before we continue */
285 }
286 
callback_group_topic(Tox * UNUSED (tox),uint32_t gid,uint32_t pid,const uint8_t * title,size_t length,void * UNUSED (userdata))287 static void callback_group_topic(Tox *UNUSED(tox), uint32_t gid, uint32_t pid, const uint8_t *title, size_t length,
288                                  void *UNUSED(userdata)) {
289     length = utf8_validate(title, length);
290     if (!length)
291         return;
292 
293     uint8_t *copy_title = malloc(length);
294     if (!copy_title)
295         return;
296 
297     memcpy(copy_title, title, length);
298     postmessage_utox(GROUP_TOPIC, gid, length, copy_title);
299 
300     LOG_TRACE("Tox Callbacks", "Group Title (%u, %u): %.*s" , gid, pid, (int)length, title);
301 }
302 
callback_group_connected(Tox * UNUSED (tox),uint32_t gid,void * UNUSED (userdata))303 void callback_group_connected(Tox *UNUSED(tox), uint32_t gid, void *UNUSED(userdata)){
304     GROUPCHAT *g = get_group(gid);
305     if (!g) {
306         LOG_ERR("Tox Callbacks", "Toxcore says we're connected to a non-existent groupchat %u.", gid);
307         return;
308     }
309 
310     g->connected = true;
311 
312     LOG_TRACE("Tox Callbacks", "Connected to groupchat %u.", gid);
313 }
314 
utox_set_callbacks_groups(Tox * tox)315 void utox_set_callbacks_groups(Tox *tox) {
316     tox_callback_conference_invite(tox, callback_group_invite);
317     tox_callback_conference_message(tox, callback_group_message);
318     tox_callback_conference_peer_name(tox, callback_group_peer_name_change);
319     tox_callback_conference_title(tox, callback_group_topic);
320     tox_callback_conference_peer_list_changed(tox, callback_group_peer_list_changed);
321     tox_callback_conference_connected(tox, callback_group_connected);
322 }
323 
324 #ifdef ENABLE_MULTIDEVICE
callback_friend_list_change(Tox * tox,void * user_data)325 static void callback_friend_list_change(Tox *tox, void *user_data) {
326     LOG_ERR("Tox Callbacks", "friend list change, updating roster");
327 
328     flist_dump_contacts();
329     utox_friend_list_init(tox);
330     flist_reload_contacts();
331 }
332 
callback_mdev_self_name(Tox * tox,uint32_t dev_num,const uint8_t * name,size_t length,void * UNUSED (userdata))333 static void callback_mdev_self_name(Tox *tox, uint32_t dev_num, const uint8_t *name, size_t length,
334                                     void *UNUSED(userdata)) {
335 
336     LOG_TRACE("Tox Callbacks", "Name changed on remote device %u", dev_num);
337 
338     memcpy(self.name, name, length);
339     self.name_length = length;
340 
341     edit_setstr(&edit_name, self.name, self.name_length);
342 
343     postmessage_utox(REDRAW, 0, 0, NULL);
344 }
345 
346 typedef void tox_mdev_self_status_message_cb(Tox *tox, uint32_t device_number, const uint8_t *status_message,
347                                              size_t len, void *user_data);
348 
callback_mdev_self_status_msg(Tox * tox,uint32_t dev_num,const uint8_t * smsg,size_t length,void * UNUSED (userdata))349 static void callback_mdev_self_status_msg(Tox *tox, uint32_t dev_num, const uint8_t *smsg, size_t length,
350                                           void *UNUSED(userdata)) {
351 
352     LOG_TRACE("Tox Callbacks", "Status Message changed on remote device %u", dev_num);
353 
354     memcpy(self.statusmsg, smsg, length);
355     self.statusmsg_length = length;
356 
357     edit_setstr(&edit_status, self.statusmsg, self.statusmsg_length);
358 
359     postmessage_utox(REDRAW, 0, 0, NULL);
360 }
361 
callback_mdev_self_state(Tox * tox,uint32_t device_number,TOX_USER_STATUS state,void * user_data)362 static void callback_mdev_self_state(Tox *tox, uint32_t device_number, TOX_USER_STATUS state, void *user_data) {
363     self.status = state;
364 }
365 
callback_device_sent_message(Tox * tox,uint32_t sending_device,uint32_t target_friend,TOX_MESSAGE_TYPE type,uint8_t * msg,size_t msg_length)366 static void callback_device_sent_message(Tox *tox, uint32_t sending_device, uint32_t target_friend,
367                                          TOX_MESSAGE_TYPE type, uint8_t *msg, size_t msg_length) {
368     LOG_TRACE("Tox Callbacks", "Message sent from other device %u\n\t\t%.*s" , sending_device, (uint32_t)msg_length, msg);
369 
370     switch (type) {
371         case TOX_MESSAGE_TYPE_NORMAL: {
372             message_add_type_text(&friend[target_friend].msg, 1, msg, msg_length, 1, 0);
373             break;
374         }
375 
376         case TOX_MESSAGE_TYPE_ACTION: {
377             message_add_type_action(&friend[target_friend].msg, 1, msg, msg_length, 1, 0);
378             break;
379         }
380 
381         default: {
382             LOG_ERR("Tox Callbacks", "Message from Friend\t%u\t--\tof unsupported type: %.*s", target_friend, (uint32_t)msg_length, msg);
383         }
384     }
385     friend_notify_msg(&friend[target_friend], msg, msg_length);
386     postmessage_utox(FRIEND_MESSAGE_UPDATE, target_friend, 0, NULL);
387 }
388 
utox_set_callbacks_mdevice(Tox * tox)389 void utox_set_callbacks_mdevice(Tox *tox) {
390     tox_callback_friend_list_change(tox, callback_friend_list_change, NULL);
391 
392     tox_callback_mdev_self_status_message(tox, callback_mdev_self_status_msg, NULL);
393     tox_callback_mdev_self_name(tox, callback_mdev_self_name, NULL);
394     tox_callback_mdev_self_state(tox, callback_mdev_self_state, NULL);
395 
396     tox_callback_mdev_sent_message(tox, callback_device_sent_message, NULL);
397 }
398 #endif
399