1 #include "tox.h"
2 
3 #include "avatar.h"
4 #include "file_transfers.h"
5 #include "flist.h"
6 #include "friend.h"
7 #include "groups.h"
8 #include "debug.h"
9 #include "macros.h"
10 #include "self.h"
11 #include "settings.h"
12 #include "text.h"
13 #include "tox_bootstrap.h"
14 #include "tox_callbacks.h"
15 #include "utox.h"
16 
17 #include "av/audio.h"
18 #include "av/utox_av.h"
19 #include "av/video.h"
20 
21 #include "ui/edit.h"     // FIXME the toxcore thread shouldn't be interacting directly with the UI
22 #include "ui/switch.h"   // FIXME the toxcore thread shouldn't be interacting directly with the UI
23 #include "ui/dropdown.h"
24 
25 #include "layout/background.h"
26 #include "layout/settings.h"
27 
28 #include "native/thread.h"
29 #include "native/time.h"
30 
31 #include <stdint.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <tox/tox.h>
35 #include <tox/toxencryptsave.h>
36 
37 #include "main.h" // utox_data_save/load, DEFAULT_NAME, DEFAULT_STATUS
38 
39 UTOX_TOX_THREAD_INIT tox_thread_init;
40 
41 TOX_MSG       tox_msg, audio_msg, toxav_msg;
42 volatile bool tox_thread_msg, audio_thread_msg, video_thread_msg;
43 
44 bool tox_connected;
45 char proxy_address[256]; /* Magic Number inside toxcore */
46 
47 static bool save_needed = true;
48 
49 enum {
50     LOG_FILE_MSG_TYPE_TEXT   = 0,
51     LOG_FILE_MSG_TYPE_ACTION = 1,
52 };
53 
54 typedef struct {
55     uint64_t time;
56     uint16_t namelen, length;
57     uint8_t  flags;
58     uint8_t  msg_type;
59     uint8_t  zeroes[2];
60 } LOG_FILE_MSG_HEADER_COMPAT;
61 
62 static void tox_thread_message(Tox *tox, ToxAV *av, uint64_t time, uint8_t msg, uint32_t param1, uint32_t param2,
63                                void *data);
64 
postmessage_toxcore(uint8_t msg,uint32_t param1,uint32_t param2,void * data)65 void postmessage_toxcore(uint8_t msg, uint32_t param1, uint32_t param2, void *data) {
66     while (tox_thread_msg) {
67         yieldcpu(1);
68     }
69 
70     if (!tox_thread_init) {
71         /* Tox is not yet active, drop message (Probably a mistake) */
72         return;
73     }
74 
75     tox_msg.msg    = msg;
76     tox_msg.param1 = param1;
77     tox_msg.param2 = param2;
78     tox_msg.data   = data;
79 
80     tox_thread_msg = 1;
81 }
82 
utox_encrypt_data(void * clear_text,size_t clear_length,uint8_t * cypher_data)83 static int utox_encrypt_data(void *clear_text, size_t clear_length, uint8_t *cypher_data) {
84     size_t passphrase_length = edit_profile_password.length;
85 
86     if (passphrase_length < 4) {
87         return UTOX_ENC_ERR_LENGTH;
88     }
89 
90     uint8_t passphrase[passphrase_length];
91     memcpy(passphrase, edit_profile_password.data, passphrase_length);
92     TOX_ERR_ENCRYPTION err = 0;
93 
94     tox_pass_encrypt((uint8_t *)clear_text, clear_length, (uint8_t *)passphrase, passphrase_length, cypher_data, &err);
95 
96     if (err) {
97         LOG_FATAL_ERR(EXIT_FAILURE, "Toxcore", "Fatal Error; unable to encrypt data!\n");
98     }
99 
100     return err;
101 }
102 
utox_decrypt_data(void * cypher_data,size_t cypher_length,uint8_t * clear_text)103 static int utox_decrypt_data(void *cypher_data, size_t cypher_length, uint8_t *clear_text) {
104     size_t passphrase_length = edit_profile_password.length;
105 
106     if (passphrase_length < 4) {
107         return UTOX_ENC_ERR_LENGTH;
108     }
109 
110     uint8_t passphrase[passphrase_length];
111     memcpy(passphrase, edit_profile_password.data, passphrase_length);
112     TOX_ERR_DECRYPTION err = 0;
113     tox_pass_decrypt((uint8_t *)cypher_data, cypher_length, (uint8_t *)passphrase, passphrase_length, clear_text, &err);
114 
115     switch (err) {
116         case TOX_ERR_DECRYPTION_OK: return 0;
117         case TOX_ERR_DECRYPTION_NULL:
118         case TOX_ERR_DECRYPTION_INVALID_LENGTH: return UTOX_ENC_ERR_LENGTH;
119         case TOX_ERR_DECRYPTION_BAD_FORMAT: return UTOX_ENC_ERR_BAD_DATA;
120         case TOX_ERR_DECRYPTION_KEY_DERIVATION_FAILED: return UTOX_ENC_ERR_UNKNOWN;
121         case TOX_ERR_DECRYPTION_FAILED: return UTOX_ENC_ERR_BAD_PASS;
122     }
123     return -1;
124 }
125 
126 /* bootstrap to dht with bootstrap_nodes */
toxcore_bootstrap(Tox * tox,bool ipv6_enabled)127 static void toxcore_bootstrap(Tox *tox, bool ipv6_enabled) {
128     static unsigned int j = 0;
129 
130     if (j == 0) {
131         j = rand();
132     }
133 
134     int i = 0;
135     while (i < 4) {
136         struct bootstrap_node *d = &bootstrap_nodes[j++ % COUNTOF(bootstrap_nodes)];
137         // do not add IPv6 bootstrap nodes if IPv6 is not enabled
138         if (!ipv6_enabled && d->ipv6) {
139             continue;
140         }
141         LOG_TRACE("Toxcore", "Bootstrapping with node %s udp: %d, tcp: %d", d->address, d->port_udp, d->port_tcp);
142         tox_bootstrap(tox, d->address, d->port_udp, d->key, 0);
143         tox_add_tcp_relay(tox, d->address, d->port_tcp, d->key, 0);
144         i++;
145     }
146 }
147 
set_callbacks(Tox * tox)148 static void set_callbacks(Tox *tox) {
149     utox_set_callbacks_friends(tox);
150     utox_set_callbacks_groups(tox);
151     #ifdef ENABLE_MULTIDEVICE
152     utox_set_callbacks_mdevice(tox);
153     #endif
154     utox_set_callbacks_file_transfer(tox);
155 }
156 
tox_after_load(Tox * tox)157 void tox_after_load(Tox *tox) {
158     utox_friend_list_init(tox);
159     init_groups(tox);
160 
161     #ifdef ENABLE_MULTIDEVICE
162     // self.group_list_count = tox_self_get_(tox);
163     self.device_list_count = tox_self_get_device_count(tox);
164 
165     // devices_update_list();
166     utox_devices_init();
167     devices_update_ui();
168 
169     uint32_t i;
170     for (i = 0; i < self.device_list_count; ++i) {
171         utox_device_init(tox, i);
172     }
173     #endif
174 
175     self.name_length = tox_self_get_name_size(tox);
176     tox_self_get_name(tox, (uint8_t *)self.name);
177     self.statusmsg_length = tox_self_get_status_message_size(tox);
178     tox_self_get_status_message(tox, (uint8_t *)self.statusmsg);
179     self.status = tox_self_get_status(tox);
180 }
181 
load_defaults(Tox * tox)182 static void load_defaults(Tox *tox) {
183     uint8_t *name = (uint8_t *)DEFAULT_NAME, *status = (uint8_t *)DEFAULT_STATUS;
184     uint16_t name_len = sizeof(DEFAULT_NAME) - 1, status_len = sizeof(DEFAULT_STATUS) - 1;
185 
186     tox_self_set_name(tox, name, name_len, 0);
187     tox_self_set_status_message(tox, status, status_len, 0);
188 }
189 
write_save(Tox * tox)190 static void write_save(Tox *tox) {
191     /* Get toxsave info from tox*/
192     size_t clear_length     = tox_get_savedata_size(tox);
193     size_t encrypted_length = clear_length + TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
194 
195     uint8_t *clear_data = calloc(1, clear_length);
196     uint8_t *encrypted_data = calloc(1, encrypted_length);
197     if (!clear_data || !encrypted_data) {
198         LOG_FATAL_ERR(EXIT_FAILURE, "Toxcore", "Could not allocate memory for savedata.\n");
199     }
200 
201     tox_get_savedata(tox, clear_data);
202 
203     if (edit_profile_password.length == 0) {
204         // user doesn't use encryption
205         save_needed = utox_data_save_tox(clear_data, clear_length);
206         LOG_TRACE("Toxcore", "Unencrypted save data written" );
207     } else {
208         UTOX_ENC_ERR enc_err = utox_encrypt_data(clear_data, clear_length, encrypted_data);
209         if (enc_err) {
210             /* encryption failed, write clear text data */
211             save_needed = utox_data_save_tox(clear_data, clear_length);
212             LOG_TRACE("Toxcore", "\n\n\t\tWARNING UTOX WAS UNABLE TO ENCRYPT DATA!\n\t\tDATA WRITTEN IN CLEAR TEXT!\n" );
213         } else {
214             save_needed = utox_data_save_tox(encrypted_data, encrypted_length);
215             LOG_TRACE("Toxcore", "Encrypted save data written" );
216         }
217     }
218 
219     free(encrypted_data);
220     free(clear_data);
221 }
222 
tox_settingschanged(void)223 void tox_settingschanged(void) {
224     // free everything
225     tox_connected = 0;
226 
227 #ifdef ENABLE_MULTIDEVICE
228     utox_devices_decon();
229 #endif
230 
231     flist_freeall();
232 
233     dropdown_list_clear(&dropdown_audio_in);
234     dropdown_list_clear(&dropdown_audio_out);
235     dropdown_list_clear(&dropdown_video);
236 
237     // send the reconfig message!
238     postmessage_toxcore(0, 1, 0, NULL);
239 
240     LOG_NOTE("Toxcore", "Restarting Toxcore");
241     while (!tox_thread_init) {
242         yieldcpu(1);
243     }
244 }
245 
246 /* 6 seconds */
247 #define UTOX_TYPING_NOTIFICATION_TIMEOUT (6ul * 1000 * 1000 * 1000)
248 
249 static struct {
250     Tox *    tox;
251     uint16_t friendnumber;
252     uint64_t time;
253     bool     sent_value;
254     bool     sent;
255 } typing_state = {
256     .tox = NULL, .friendnumber = 0, .time = 0, .sent_value = 0,
257 };
258 
utox_thread_work_for_typing_notifications(Tox * tox,uint64_t time)259 static void utox_thread_work_for_typing_notifications(Tox *tox, uint64_t time) {
260     if (typing_state.tox != tox) {
261         // Guard against Tox engine restarts.
262         return;
263     }
264 
265     bool is_typing = (time < typing_state.time + UTOX_TYPING_NOTIFICATION_TIMEOUT);
266     if (typing_state.sent_value ^ is_typing) {
267         // Need to send an update.
268         if (tox_self_set_typing(tox, typing_state.friendnumber, is_typing, 0)) {
269             // Successfully sent. Mark new state.
270             typing_state.sent_value = is_typing;
271             LOG_TRACE("Toxcore", "Sent typing state to friend (%d): %d" , typing_state.friendnumber, typing_state.sent_value);
272         }
273     }
274 }
275 
load_toxcore_save(struct Tox_Options * options)276 static int load_toxcore_save(struct Tox_Options *options) {
277     settings.save_encryption = 0;
278     size_t   raw_length;
279     uint8_t *raw_data = utox_data_load_tox(&raw_length);
280 
281     /* Check if we're loading a saved profile */
282     if (!raw_data || !raw_length) {
283         // No save file at all, create new profile!
284         return -2;
285     }
286 
287     if (!tox_is_data_encrypted(raw_data)) {
288         LOG_INFO("Toxcore", "Using unencrypted save file");
289         options->savedata_type   = TOX_SAVEDATA_TYPE_TOX_SAVE;
290         options->savedata_data   = raw_data;
291         options->savedata_length = raw_length;
292         return 0;
293     }
294 
295     size_t   cleartext_length = raw_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
296     uint8_t *clear_data       = calloc(1, cleartext_length);
297     settings.save_encryption   = 1;
298     LOG_INFO("Toxcore", "Using encrypted data, trying password: ");
299 
300     UTOX_ENC_ERR decrypt_err = utox_decrypt_data(raw_data, raw_length, clear_data);
301     if (decrypt_err) {
302         if (decrypt_err == UTOX_ENC_ERR_LENGTH) {
303             LOG_WARN("Toxcore", "Password too short!\r");
304         } else if (decrypt_err == UTOX_ENC_ERR_BAD_PASS) {
305             LOG_ERR("Toxcore", "Couldn't decrypt, wrong password?\r");
306         } else {
307             LOG_ERR("Toxcore", "Unknown error, please file a bug report!" );
308         }
309         return -1;
310     }
311 
312     if (!clear_data || !cleartext_length) {
313         return -1;
314     }
315 
316     options->savedata_type   = TOX_SAVEDATA_TYPE_TOX_SAVE;
317     options->savedata_data   = clear_data;
318     options->savedata_length = cleartext_length;
319 
320     return 0;
321 }
322 
log_callback(Tox * UNUSED (tox),TOX_LOG_LEVEL level,const char * file,uint32_t line,const char * func,const char * message,void * UNUSED (user_data))323 static void log_callback(Tox *UNUSED(tox), TOX_LOG_LEVEL level, const char *file, uint32_t line,
324                          const char *func, const char *message, void *UNUSED(user_data)) {
325     if (message && file && line) {
326         LOG_NET_TRACE("Toxcore", "TOXCORE LOGGING ERROR (%u): %s" , level, message);
327         LOG_NET_TRACE("Toxcore", "     in: %s:%u" , file, line);
328     } else if (func) {
329         LOG_NET_TRACE("Toxcore", "TOXCORE LOGGING ERROR: %s" , func);
330     } else {
331         LOG_ERR("Toxcore logging", "TOXCORE LOGGING is broken!!:\tOpen an bug upstream");
332     }
333 }
334 
335 // initialize toxcore based on current settings
336 // returns 0 on success
337 // returns -1 on temporary error (waiting for password encryption)
338 // returns -2 on fatal error
init_toxcore(Tox ** tox)339 static int init_toxcore(Tox **tox) {
340     tox_thread_init = UTOX_TOX_THREAD_INIT_NONE;
341     int save_status = 0;
342 
343     struct Tox_Options topt;
344     tox_options_default(&topt);
345     // tox_options_set_start_port(&topt, 0);
346     // tox_options_set_end_port(&topt, 0);
347 
348     tox_options_set_log_callback(&topt, log_callback);
349 
350     tox_options_set_ipv6_enabled(&topt, settings.enable_ipv6);
351     tox_options_set_udp_enabled(&topt, settings.enable_udp);
352 
353     tox_options_set_proxy_type(&topt, TOX_PROXY_TYPE_NONE);
354     tox_options_set_proxy_host(&topt, proxy_address);
355     tox_options_set_proxy_port(&topt, settings.proxy_port);
356 
357     #ifdef ENABLE_MULTIDEVICE
358     tox_options_set_mdev_mirror_sent(&topt, 1);
359     #endif
360 
361     save_status = load_toxcore_save(&topt);
362 
363     // TODO tox.c shouldn't be interacting with the UI on this level
364     if (save_status == -1) {
365         /* Save file exist, couldn't decrypt, don't start a tox instance
366         TODO: throw an error to the UI! */
367         panel_profile_password.disabled = false;
368         panel_settings_master.disabled  = true;
369         edit_setfocus(&edit_profile_password);
370         postmessage_utox(REDRAW, 0, 0, NULL);
371         return -1;
372     } else if (save_status == -2) {
373         /* New profile! */
374         panel_profile_password.disabled = true;
375         panel_settings_master.disabled  = false;
376     } else {
377         panel_profile_password.disabled = true;
378         if (settings.show_splash) {
379             panel_splash_page.disabled = false;
380         } else {
381             panel_settings_master.disabled = false;
382         }
383         edit_resetfocus();
384     }
385     postmessage_utox(REDRAW, 0, 0, NULL);
386 
387     if (settings.use_proxy) {
388         topt.proxy_type = TOX_PROXY_TYPE_SOCKS5;
389     }
390 
391     // Create main connection
392     LOG_INFO("Toxcore", "Creating New Toxcore instance.\n"
393              "\t\tIPv6 : %u\n"
394              "\t\tUDP  : %u\n"
395              "\t\tProxy: %u %s %u",
396              topt.ipv6_enabled, topt.udp_enabled, topt.proxy_type, topt.proxy_host, topt.proxy_port);
397 
398 
399     TOX_ERR_NEW tox_new_err = 0;
400 
401     *tox = tox_new(&topt, &tox_new_err);
402 
403     if (*tox == NULL) {
404         if (settings.force_proxy) {
405             LOG_ERR("Toxcore", "\t\tError #%u, Not going to try without proxy because of user settings.", tox_new_err);
406             return -2;
407         }
408         LOG_ERR("Toxcore", "\t\tError #%u, Going to try without proxy.", tox_new_err);
409 
410         // reset proxy options as well as GUI and settings
411         topt.proxy_type = TOX_PROXY_TYPE_NONE;
412         settings.use_proxy = settings.force_proxy = 0;
413         switch_proxy.switch_on = 0;
414 
415         *tox = tox_new(&topt, &tox_new_err);
416 
417         if (*tox == NULL) {
418             LOG_ERR("Toxcore", "\t\tError #%u, Going to try without IPv6.", tox_new_err);
419 
420             // reset IPv6 options as well as GUI and settings
421             topt.ipv6_enabled = 0;
422             switch_ipv6.switch_on = settings.enable_ipv6 = 0;
423 
424             *tox = tox_new(&topt, &tox_new_err);
425 
426             if (*tox == NULL) {
427                 LOG_ERR("Toxcore", "\t\tFatal Error creating a Tox instance... Error #%u", tox_new_err);
428                 return -2;
429             }
430         }
431     }
432 
433     free((void *)topt.savedata_data);
434 
435     /* Give toxcore the functions to call */
436     set_callbacks(*tox);
437 
438     /* Connect to bootstrapped nodes in "tox_bootstrap.h" */
439     toxcore_bootstrap(*tox, settings.enable_ipv6);
440 
441     if (save_status == -2) {
442         LOG_NOTE("Toxcore", "No save file, using defaults" );
443         load_defaults(*tox);
444     }
445     tox_after_load(*tox);
446 
447     return 0;
448 }
449 
450 
451 /** void toxcore_thread(void)
452  *
453  * Main tox function, starts a new toxcore for utox to use, and then spawns its
454  * threads.
455  *
456  * Accepts and returns nothing.
457  */
toxcore_thread(void * UNUSED (args))458 void toxcore_thread(void *UNUSED(args)) {
459     ToxAV *av               = NULL;
460     bool   reconfig         = 1;
461     int    toxcore_init_err = 0;
462 
463     while (reconfig) {
464         reconfig = 0;
465 
466         Tox *tox = NULL;
467         toxcore_init_err = init_toxcore(&tox);
468         if (toxcore_init_err == -2) {
469             // fatal failure, unable to create tox instance
470             LOG_ERR("Toxcore", "Unable to create Tox Instance (%d)" , toxcore_init_err);
471             // set init to true because other code is waiting for it.
472             // but indicate error state
473             tox_thread_init = UTOX_TOX_THREAD_INIT_ERROR;
474             while (!reconfig) {
475                 // Waiting for a message triggering the next reconfigure
476                 // avoid trying the creation of thousands of tox instances before user changes the settings
477                 if (tox_thread_msg) {
478                     TOX_MSG *msg = &tox_msg;
479                     // If msg->msg is 0, reconfig
480                     if (!msg->msg) {
481                         reconfig = (bool) msg->param1;
482                         tox_thread_init = UTOX_TOX_THREAD_INIT_NONE;
483                     }
484                     // tox is not configured at this point ignore all other messages
485                     tox_thread_msg = 0;
486                 } else {
487                     yieldcpu(300);
488                 }
489             }
490             continue;
491         } else if (toxcore_init_err) {
492             /* Couldn't init toxcore, probably waiting for user password */
493             yieldcpu(300);
494             tox_thread_init = UTOX_TOX_THREAD_INIT_NONE;
495             // ignore all messages in this stage
496             tox_thread_msg = 0;
497             reconfig = 1;
498             continue;
499         } else {
500             init_self(tox);
501 
502             TOXAV_ERR_NEW toxav_error;
503             av = toxav_new(tox, &toxav_error);
504 
505             if (!av) {
506                 LOG_ERR("Toxcore", "Unable to get ToxAV (%u)" , toxav_error);
507             }
508 
509             tox_thread_init = UTOX_TOX_THREAD_INIT_SUCCESS;
510 
511             /* init the friends list. */
512             flist_start();
513             postmessage_utox(UPDATE_TRAY, 0, 0, NULL);
514             postmessage_utox(PROFILE_DID_LOAD, 0, 0, NULL);
515 
516             postmessage_utoxav(UTOXAV_NEW_TOX_INSTANCE, 0, 0, av);
517         }
518 
519         bool     connected = 0;
520         uint64_t last_save = get_time(), last_connection = get_time(), time;
521 
522         while (1) {
523             // Put toxcore to work
524             tox_iterate(tox, NULL);
525 
526             // Check currents connection
527             if (!!tox_self_get_connection_status(tox) != connected) {
528                 connected = !connected;
529                 postmessage_utox(DHT_CONNECTED, connected, 0, NULL);
530             }
531 
532             /* Wait 10 Billion ticks then verify connection. */
533             time = get_time();
534             if (time - last_connection >= (uint64_t)10 * 1000 * 1000 * 1000) {
535                 last_connection = time;
536                 if (!connected) {
537                     toxcore_bootstrap(tox, settings.enable_ipv6);
538                 }
539 
540                 // save every 1000.
541                 if (save_needed || (time - last_save >= (uint64_t)1000 * 1000 * 1000 * 1000)) {
542                     // Save tox data
543                     write_save(tox);
544                     last_save = time;
545                 }
546             }
547 
548             // If there's a message, load it, and send to the tox message thread
549             if (tox_thread_msg) {
550                 TOX_MSG *msg = &tox_msg;
551                 // If msg->msg is 0, reconfig if needed and break from tox_do
552                 if (!msg->msg) {
553                     reconfig        = msg->param1;
554                     tox_thread_msg  = 0;
555                     tox_thread_init = UTOX_TOX_THREAD_INIT_NONE;
556                     break;
557                 }
558                 tox_thread_message(tox, av, time, msg->msg, msg->param1, msg->param2, msg->data);
559                 tox_thread_msg = 0;
560                 typing_state.sent = (msg->msg == TOX_SEND_MESSAGE || msg->msg == TOX_SEND_ACTION);
561             }
562 
563             if (settings.send_typing_status) {
564                 // Thread active transfers and check if friend is typing
565                 utox_thread_work_for_typing_notifications(tox, time);
566             }
567 
568             /* Ask toxcore how many ms to wait, then wait at the most 20ms */
569             uint32_t interval = tox_iteration_interval(tox);
570             yieldcpu((interval > 20) ? 20 : interval);
571         }
572 
573         /* If for anyreason, we exit, write the save, and clear the password */
574         write_save(tox);
575         edit_setstr(&edit_profile_password, (char *)"", 0);
576 
577         // Stop toxcore.
578         LOG_TRACE("Toxcore", "tox thread ending");
579         tox_kill(tox);
580     }
581 
582     tox_thread_init = UTOX_TOX_THREAD_INIT_NONE;
583     free_friends();
584     raze_groups();
585     LOG_TRACE("Toxcore", "Tox thread:\tClean exit!");
586 }
587 
588 /** General recommendations for working with threads in uTox
589  *
590  * There are two main threads, the tox worker thread, that interacts with Toxcore, and receives the callbacks. The other
591  * is the 'uTox' thread that interacts with the user, (rather sends information to the GUI.) The tox thread and the uTox
592  * thread may interact with each other, as you see fit. However the Toxcore thread has child threads that are a bit
593  * temperamental. The ToxAV thread is a child of the Toxcore thread, and therefore will ideally only be called by the tox
594  * thread. The ToxAV thread also has two children of its own, an audio and a video thread. Both a & v threads should
595  * only be called by the ToxAV thread to avoid deadlocks.
596  */
tox_thread_message(Tox * tox,ToxAV * av,uint64_t time,uint8_t msg,uint32_t param1,uint32_t param2,void * data)597 static void tox_thread_message(Tox *tox, ToxAV *av, uint64_t time, uint8_t msg, uint32_t param1, uint32_t param2,
598                                void *data)
599 {
600     switch (msg) {
601         case TOX_SAVE: {
602             save_needed = 1;
603             break;
604         }
605         /* Change Self in core */
606         case TOX_SELF_SET_NAME: {
607             /* param1: name length
608              * data: name
609              */
610             tox_self_set_name(tox, data, param1, 0);
611             save_needed = 1;
612             break;
613         }
614         case TOX_SELF_SET_STATUS: {
615             /* param1: status length
616              * data: status message
617              */
618             tox_self_set_status_message(tox, data, param1, 0);
619             save_needed = 1;
620             break;
621         }
622         case TOX_SELF_SET_STATE: {
623             /* param1: status
624              */
625             tox_self_set_status(tox, param1);
626             save_needed = 1;
627             break;
628         }
629 
630         case TOX_SELF_CHANGE_NOSPAM: {
631             /* param1: new nospam value
632              */
633             char *old_id = self.id_str;
634 
635             self.nospam = param1;
636 
637             sprintf(self.nospam_str, "%08X", self.nospam);
638             tox_self_set_nospam(tox, self.nospam);
639 
640             /* update tox id */
641             tox_self_get_address(tox, self.id_binary);
642             id_to_string(self.id_str, self.id_binary);
643             LOG_TRACE("Toxcore", "Tox ID: %.*s" , (int)self.id_str_length, self.id_str);
644 
645             /* Update avatar */
646             avatar_move((uint8_t *)old_id, (uint8_t *)self.id_str);
647             edit_setstr(&edit_nospam, self.nospam_str, sizeof(uint32_t) * 2);
648 
649             save_needed = true;
650             break;
651         }
652 
653         case TOX_SELF_NEW_DEVICE: {
654         #ifdef ENABLE_MULTIDEVICE
655 
656             TOX_ERR_DEVICE_ADD error = 0;
657             tox_self_add_device(tox, data + TOX_ADDRESS_SIZE, param1, data, &error);
658 
659             if (error) {
660                 LOG_ERR("Toxcore", "problem with adding device to self %u" , error);
661             } else {
662                 self.device_list_count++;
663             }
664         #endif
665             break;
666         }
667 
668 
669         /* Avatar status */
670         case TOX_AVATAR_SET: {
671             /* param1: avatar format
672              * param2: length of avatar data
673              * data: raw avatar data (PNG)
674              */
675 
676             avatar_set_self(data, param2);
677             save_needed = 1;
678             break;
679         }
680         case TOX_AVATAR_UNSET: {
681             avatar_unset_self();
682             save_needed = 1;
683             break;
684         }
685 
686         /* Interact with contacts */
687         case TOX_FRIEND_NEW: {
688             /* param1: length of message
689              * data: friend id + message
690              */
691             uint32_t           fid;
692             TOX_ERR_FRIEND_ADD f_err;
693 
694             if (!param1) {
695                 STRING *default_add_msg = SPTR(DEFAULT_FRIEND_REQUEST_MESSAGE);
696                 fid = tox_friend_add(tox, data, (const uint8_t *)default_add_msg->str, default_add_msg->length, &f_err);
697             } else {
698                 fid = tox_friend_add(tox, data, (uint8_t *)data + TOX_ADDRESS_SIZE, param1, &f_err);
699             }
700 
701             if (f_err != TOX_ERR_FRIEND_ADD_OK) {
702                 uint8_t addf_error;
703                 switch (f_err) {
704                     case TOX_ERR_FRIEND_ADD_TOO_LONG: addf_error       = ADDF_TOOLONG; break;
705                     case TOX_ERR_FRIEND_ADD_NO_MESSAGE: addf_error     = ADDF_NOMESSAGE; break;
706                     case TOX_ERR_FRIEND_ADD_OWN_KEY: addf_error        = ADDF_OWNKEY; break;
707                     case TOX_ERR_FRIEND_ADD_ALREADY_SENT: addf_error   = ADDF_ALREADYSENT; break;
708                     case TOX_ERR_FRIEND_ADD_BAD_CHECKSUM: addf_error   = ADDF_BADCHECKSUM; break;
709                     case TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM: addf_error = ADDF_SETNEWNOSPAM; break;
710                     case TOX_ERR_FRIEND_ADD_MALLOC: addf_error         = ADDF_NOMEM; break;
711                     default: addf_error                                = ADDF_UNKNOWN; break;
712                 }
713                 postmessage_utox(FRIEND_SEND_REQUEST, 1, addf_error, data);
714             } else {
715                 utox_friend_init(tox, fid);
716                 postmessage_utox(FRIEND_SEND_REQUEST, 0, fid, data);
717             }
718             save_needed = 1;
719             break;
720         }
721 
722         case TOX_FRIEND_NEW_DEVICE: {
723         #ifdef ENABLE_MULTIDEVICE
724             LOG_INFO("Toxcore", "Adding new device to peer %u" , param1);
725             tox_friend_add_device(tox, data, param1, 0);
726             free(data);
727             save_needed = 1;
728         #endif
729             break;
730         }
731 
732         case TOX_FRIEND_NEW_NO_REQ: {
733             /* data: friend's public key
734              */
735             TOX_ERR_FRIEND_ADD f_err;
736             uint32_t fid = tox_friend_add_norequest(tox, data, &f_err);
737 
738             if (!f_err) {
739                 utox_friend_init(tox, fid);
740                 postmessage_utox(FRIEND_ADD_NO_REQ, 0, fid, data);
741             } else {
742                 char hex_id[TOX_ADDRESS_SIZE * 2];
743                 id_to_string(hex_id, data);
744                 LOG_TRACE("Toxcore", "Unable to accept friend %s, error num = %i" , hex_id, fid);
745                 free(data);
746             }
747             save_needed = 1;
748             break;
749         }
750 
751         case TOX_FRIEND_ACCEPT: {
752             /* data: FREQUEST
753              */
754             FREQUEST *req = data;
755             TOX_ERR_FRIEND_ADD f_err;
756             uint32_t fid = tox_friend_add_norequest(tox, req->bin_id, &f_err);
757             if (!f_err) {
758                 utox_friend_init(tox, fid);
759                 postmessage_utox(FRIEND_ACCEPT_REQUEST, fid, 0, req);
760             } else {
761                 char hex_id[TOX_ADDRESS_SIZE * 2];
762                 id_to_string(hex_id, req->bin_id);
763                 LOG_TRACE("Toxcore", "Unable to accept friend %s, error num = %i" , hex_id, fid);
764             }
765             save_needed = 1;
766             break;
767         }
768         case TOX_FRIEND_DELETE: {
769             /* param1: friend #
770              */
771             tox_friend_delete(tox, param1, 0);
772             postmessage_utox(FRIEND_REMOVE, 0, 0, data);
773             save_needed = 1;
774             break;
775         }
776         case TOX_FRIEND_ONLINE: {
777             /* Moved to the call back... */
778             break;
779         }
780 
781         /* Default actions */
782         case TOX_SEND_MESSAGE:
783         case TOX_SEND_ACTION: {
784             /* param1: friend #
785              * param2: message length
786              * data: message
787              */
788             MSG_HEADER *mmsg = (MSG_HEADER *)data;
789 
790             TOX_MESSAGE_TYPE type;
791             if (msg == TOX_SEND_ACTION) {
792                 type = TOX_MESSAGE_TYPE_ACTION;
793             } else {
794                 type = TOX_MESSAGE_TYPE_NORMAL;
795             }
796 
797             uint8_t *next = (uint8_t *)mmsg->via.txt.msg;
798             while (param2 > TOX_MAX_MESSAGE_LENGTH) {
799                 uint16_t len = TOX_MAX_MESSAGE_LENGTH - utf8_unlen((char *)next + TOX_MAX_MESSAGE_LENGTH);
800                 tox_friend_send_message(tox, param1, type, next, len, 0);
801                 param2 -= len;
802                 next += len;
803             }
804 
805             TOX_ERR_FRIEND_SEND_MESSAGE error = 0;
806 
807             // Send last or only message
808             mmsg->receipt      = tox_friend_send_message(tox, param1, type, next, param2, &error);
809             mmsg->receipt_time = 0;
810 
811             LOG_INFO("Toxcore", "Sending message, receipt %u" , mmsg->receipt);
812             if (error) {
813                 LOG_ERR("Toxcore", "Error sending message... %u" , error);
814             }
815 
816             break;
817         }
818         case TOX_SEND_TYPING: {
819             /* param1: friend #
820              */
821 
822             // Check if user has switched to another friend window chat.
823             // Take care not to react on obsolete data from old Tox instance.
824             bool need_resetting = (typing_state.tox == tox)
825                                && (typing_state.friendnumber != param1)
826                                && (typing_state.sent_value);
827 
828             if (need_resetting) {
829                 // Tell previous friend that he's betrayed.
830                 tox_self_set_typing(tox, typing_state.friendnumber, 0, 0);
831                 // Mark that new friend doesn't know that we're typing yet.
832                 typing_state.sent_value = 0;
833             }
834 
835             // Mark us as typing to this friend at the moment.
836             // utox_thread_work_for_typing_notifications() will
837             // send a notification if it deems necessary.
838             typing_state.tox          = tox;
839             typing_state.friendnumber = param1;
840 
841             // UINT64_MAX will set the is_typing in utox_thread_work_for_typing_notifications to 0
842             // and send typing state 0 to friend when message is sent
843             typing_state.time         = (typing_state.sent) ? UINT64_MAX : time;
844 
845             // LOG_TRACE("Toxcore", "Set typing state for friend (%d): %d" , typing_state.friendnumber, typing_state.sent_value);
846             break;
847         }
848 
849         /* File transfers are so in right now. */
850         case TOX_FILE_SEND_NEW:
851         case TOX_FILE_SEND_NEW_SLASH: {
852             /* param1: friend #
853              * param2: offset of first file name in data
854              * data: file names
855              */
856 
857             if (param2 == 0) {
858                 // This is the new default. Where the caller sends an opened file.
859                 UTOX_MSG_FT *msg = data;
860                 ft_send_file(tox, param1, msg->file, msg->name, strlen((char*)msg->name), NULL);
861                 free(msg->name);
862                 free(msg);
863                 break;
864             }
865 
866             break;
867         }
868 
869         case TOX_FILE_SEND_NEW_INLINE: {
870             /* param1: friend id
871                data: pointer to a TOX_SEND_INLINE_MSG struct
872              */
873             LOG_INFO("Toxcore", "Sending picture inline." );
874 
875             struct TOX_SEND_INLINE_MSG *img = data;
876             uint8_t name[] = "utox-inline.png";
877             ft_send_data(tox, param1, img->image, img->image_size, name, strlen((char *)name));
878             free(data);
879             break;
880         }
881 
882         case TOX_FILE_ACCEPT:
883         case TOX_FILE_ACCEPT_AUTO: {
884             /* param1: friend #
885              * param2: file #
886              * data: path to write file */
887             if (utox_file_start_write(param1, param2, (const char *)data)) {
888                 /*  tox, friend#, file#,        START_FILE      */
889                 ft_local_control(tox, param1, param2, TOX_FILE_CONTROL_RESUME);
890             } else {
891                 ft_local_control(tox, param1, param2, TOX_FILE_CONTROL_CANCEL);
892             }
893             free(data);
894             break;
895         }
896 
897         case TOX_FILE_RESUME: {
898             if (data) {
899                 param2 = ((FILE_TRANSFER*)data)->file_number;
900             }
901             ft_local_control(tox, param1, param2, TOX_FILE_CONTROL_RESUME);
902             break;
903         }
904 
905         case TOX_FILE_PAUSE: {
906             if (data) {
907                 param2 = ((FILE_TRANSFER*)data)->file_number;
908             }
909             ft_local_control(tox, param1, param2, TOX_FILE_CONTROL_PAUSE);
910             break;
911         }
912 
913         case TOX_FILE_CANCEL: {
914             if (data) {
915                 param2 = ((FILE_TRANSFER*)data)->file_number;
916             }
917             ft_local_control(tox, param1, param2, TOX_FILE_CONTROL_CANCEL);
918             break;
919         }
920 
921 
922         /* Audio & Video */
923         case TOX_CALL_SEND: {
924             /* param1: friend #
925              */
926             /* Set the video bitrate, if we're starting a video call. */
927             int v_bitrate = 0;
928             if (param2) {
929                 v_bitrate = UTOX_DEFAULT_BITRATE_V;
930                 LOG_TRACE("Toxcore", "Sending video call to friend %u" , param1);
931             } else {
932                 v_bitrate = 0;
933                 LOG_TRACE("Toxcore", "Sending call to friend %u" , param1);
934             }
935             postmessage_utoxav(UTOXAV_OUTGOING_CALL_PENDING, param1, param2, NULL);
936 
937             TOXAV_ERR_CALL error = 0;
938             toxav_call(av, param1, UTOX_DEFAULT_BITRATE_A, v_bitrate, &error);
939             if (error) {
940                 switch (error) {
941                     case TOXAV_ERR_CALL_MALLOC: {
942                         LOG_TRACE("Toxcore", "Error making call to friend %u; Unable to malloc for this call." , param1);
943                         break;
944                     }
945                     case TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL: {
946                         /* This shouldn't happen, but just in case toxav gets a call before uTox gets this message we
947                          * can just pretend like we're answering a call... */
948                         LOG_TRACE("Toxcore", "Error making call to friend %u; Already in call." , param1);
949                         LOG_TRACE("Toxcore", "Forwarding and accepting call!" );
950 
951                         TOXAV_ERR_ANSWER ans_error = 0;
952                         toxav_answer(av, param1, UTOX_DEFAULT_BITRATE_A, v_bitrate, &ans_error);
953                         if (ans_error) {
954                             LOG_TRACE("Toxcore", "Error trying to toxav_answer error (%i)" , ans_error);
955                         } else {
956                             postmessage_utoxav(UTOXAV_OUTGOING_CALL_ACCEPTED, param1, param2, NULL);
957                         }
958                         postmessage_utox(AV_CALL_ACCEPTED, param1, 0, NULL);
959 
960                         break;
961                     }
962                     default: {
963                         /* Un-handled errors
964                         TOXAV_ERR_CALL_SYNC,
965                         TOXAV_ERR_CALL_FRIEND_NOT_FOUND,
966                         TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED,
967                         TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL,
968                         TOXAV_ERR_CALL_INVALID_BIT_RATE,*/
969                         LOG_TRACE("Toxcore", "Error making call to %u, error num is %i." , param1, error);
970                         break;
971                     }
972                 }
973             } else {
974                 postmessage_utox(AV_CALL_RINGING, param1, param2, NULL);
975             }
976             break;
977         }
978         case TOX_CALL_INCOMING: { /* This is a call back, todo remove */ break;
979         }
980         case TOX_CALL_ANSWER: {
981             /* param1: Friend_number #
982              * param2: Accept Video? #
983              */
984             TOXAV_ERR_ANSWER error     = 0;
985             int              v_bitrate = 0;
986 
987             if (param2) {
988                 v_bitrate = UTOX_DEFAULT_BITRATE_V;
989                 LOG_TRACE("Toxcore", "Answering video call." );
990             } else {
991                 v_bitrate = 0;
992                 LOG_TRACE("Toxcore", "Answering audio call." );
993             }
994 
995             toxav_answer(av, param1, UTOX_DEFAULT_BITRATE_A, v_bitrate, &error);
996 
997             if (error) {
998                 LOG_TRACE("Toxcore", "Error trying to toxav_answer error (%i)" , error);
999             } else {
1000                 postmessage_utoxav(UTOXAV_INCOMING_CALL_ANSWER, param1, param2, NULL);
1001             }
1002             postmessage_utox(AV_CALL_ACCEPTED, param1, 0, NULL);
1003             break;
1004         }
1005         case TOX_CALL_PAUSE_AUDIO: {
1006             /* param1: friend # */
1007             LOG_TRACE("Toxcore", "TODO bug, please report 001!!" );
1008             break;
1009         }
1010         case TOX_CALL_PAUSE_VIDEO: {
1011             /* param1: friend # */
1012             LOG_TRACE("Toxcore", "Ending video for active call!" );
1013             utox_av_local_call_control(av, param1, TOXAV_CALL_CONTROL_HIDE_VIDEO);
1014             break;
1015         }
1016         case TOX_CALL_RESUME_AUDIO: {
1017             /* param1: friend # */
1018             LOG_TRACE("Toxcore", "TODO bug, please report 002!!" );
1019             break;
1020         }
1021         case TOX_CALL_RESUME_VIDEO: {
1022             /* param1: friend # */
1023             LOG_TRACE("Toxcore", "Starting video for active call!" );
1024             utox_av_local_call_control(av, param1, TOXAV_CALL_CONTROL_SHOW_VIDEO);
1025             get_friend(param1)->call_state_self |= TOXAV_FRIEND_CALL_STATE_SENDING_V | TOXAV_FRIEND_CALL_STATE_ACCEPTING_V;
1026             break;
1027         }
1028         case TOX_CALL_DISCONNECT: {
1029             /* param1: friend_number
1030              */
1031             utox_av_local_disconnect(av, param1);
1032             break;
1033         }
1034 
1035         /* Groups are broken while we await the new GCs getting merged. */
1036         /*
1037         TOX_GROUP_JOIN,
1038         TOX_GROUP_PART, // 30
1039         TOX_GROUP_INVITE,
1040         TOX_GROUP_SET_TOPIC,
1041         TOX_GROUP_SEND_MESSAGE,
1042         TOX_GROUP_SEND_ACTION,
1043         TOX_GROUP_AUDIO_START, // 35
1044         TOX_GROUP_AUDIO_END,*/
1045 
1046         case TOX_GROUP_CREATE: {
1047             int g_num = -1;
1048 
1049             TOX_ERR_CONFERENCE_NEW error = 0;
1050             if (param2) {
1051                 // TODO FIX THIS AFTER NEW GROUP API
1052                 g_num = toxav_add_av_groupchat(tox, callback_av_group_audio, NULL);
1053             } else {
1054                 g_num = tox_conference_new(tox, &error);
1055             }
1056 
1057             if (g_num != -1) {
1058                 GROUPCHAT *g = get_group(g_num);
1059                 if (!g) {
1060                     if (!group_create(g_num, param2)) {
1061                         LOG_ERR("Toxcore", "Failed creating group %u", g_num);
1062                         break;
1063                     }
1064                 } else {
1065                     group_init(g, g_num, param2);
1066                 }
1067                 postmessage_utox(GROUP_ADD, g_num, param2, NULL);
1068             }
1069 
1070             uint8_t pkey[TOX_PUBLIC_KEY_SIZE];
1071             tox_conference_peer_get_public_key(tox, g_num, 0, pkey, NULL);
1072             uint64_t pkey_to_number = 0;
1073             for (int key_i = 0; key_i < TOX_PUBLIC_KEY_SIZE; ++key_i) {
1074                 pkey_to_number += pkey[key_i];
1075             }
1076             srand(pkey_to_number);
1077             uint32_t name_color = RGB(rand(), rand(), rand());
1078 
1079             group_peer_add(get_group(g_num), 0, 1, name_color);
1080             group_peer_name_change(get_group(g_num), 0, (uint8_t *)self.name, self.name_length);
1081             postmessage_utox(GROUP_PEER_ADD, g_num, 0, NULL);
1082 
1083             save_needed = true;
1084             break;
1085         }
1086         case TOX_GROUP_JOIN: {
1087             break;
1088         }
1089         case TOX_GROUP_PART: {
1090             /* param1: group #
1091              */
1092             postmessage_utoxav(UTOXAV_GROUPCALL_END, param1, param1, NULL);
1093 
1094             TOX_ERR_CONFERENCE_DELETE error = 0;
1095             tox_conference_delete(tox, param1, &error);
1096             save_needed = true;
1097             break;
1098         }
1099         case TOX_GROUP_SEND_INVITE: {
1100             /* param1: group #
1101              * param2: friend #
1102              */
1103             TOX_ERR_CONFERENCE_INVITE error = 0;
1104             tox_conference_invite(tox, param2, param1, &error);
1105             save_needed = true;
1106             break;
1107         }
1108         case TOX_GROUP_SET_TOPIC: {
1109             /* param1: group #
1110              * param2: topic length
1111              * data: topic
1112              */
1113             TOX_ERR_CONFERENCE_TITLE error = 0;
1114 
1115             tox_conference_set_title(tox, param1, data, param2, &error);
1116             postmessage_utox(GROUP_TOPIC, param1, param2, data);
1117             save_needed = true;
1118             break;
1119         }
1120         case TOX_GROUP_SEND_MESSAGE:
1121         case TOX_GROUP_SEND_ACTION: {
1122             /* param1: group #
1123              * param2: message length
1124              * data: message
1125              */
1126             TOX_MESSAGE_TYPE type;
1127             type = (msg == TOX_GROUP_SEND_ACTION ? TOX_MESSAGE_TYPE_ACTION : TOX_MESSAGE_TYPE_NORMAL);
1128 
1129             TOX_ERR_CONFERENCE_SEND_MESSAGE error = 0;
1130             tox_conference_send_message(tox, param1, type, data, param2, &error);
1131             free(data);
1132 
1133             if (error) {
1134                 LOG_ERR("Toxcore", "Error sending groupchat message... %u" , error);
1135             }
1136 
1137             break;
1138         }
1139         case TOX_GROUP_AUDIO_START: {
1140             // We have to take the long way around, because the UI shouldn't depend on AV
1141             LOG_INFO("Toxcore", "Staring call in groupchat %u", param1);
1142             postmessage_utox(GROUP_AUDIO_START, param1, 0, NULL);
1143             break;
1144         }
1145         case TOX_GROUP_AUDIO_END: {
1146             // We have to take the long way around, because the UI shouldn't depend on AV
1147             LOG_INFO("Toxcore", "Ending call in groupchat %u", param1);
1148             postmessage_utox(GROUP_AUDIO_END, param1, 0, NULL);
1149             break;
1150         }
1151     } // End of switch.
1152 }
1153 
id_to_string(char * dest,uint8_t * src)1154 void id_to_string(char *dest, uint8_t *src) {
1155     to_hex(dest, src, TOX_ADDRESS_SIZE);
1156 }
1157