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