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