1 #include "file_transfers.h"
2 
3 #include "avatar.h"
4 #include "friend.h"
5 #include "debug.h"
6 #include "macros.h"
7 #include "self.h"
8 #include "settings.h"
9 #include "text.h"
10 #include "tox.h"
11 #include "utox.h"
12 
13 #include "native/filesys.h"
14 #include "native/image.h"
15 #include "native/thread.h"
16 #include "native/time.h"
17 
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/stat.h>
21 
22 #define MAX_INCOMING_COUNT 32
23 
24 #define MAX_INLINE_FILESIZE (1024 * 1024 * 4)
25 
fid_to_string(char * dest,uint8_t * src)26 static void fid_to_string(char *dest, uint8_t *src) {
27     to_hex(dest, src, TOX_FILE_ID_LENGTH);
28 }
29 
30 // Accepts a file number and returns indicating whether it's an incoming one or not.
is_incoming_ft(uint32_t file_number)31 static bool is_incoming_ft(uint32_t file_number) {
32     // This is Toxcore magic.
33     return file_number >= (1 << 16);
34 }
35 
36 // Accepts a Toxcore incoming file number and returns a normal one.
detox_incoming_file_number(uint32_t file_number)37 static uint32_t detox_incoming_file_number(uint32_t file_number) {
38     return (file_number >> 16) - 1;
39 }
40 
get_file_transfer(uint32_t friend_number,uint32_t file_number)41 static FILE_TRANSFER *get_file_transfer(uint32_t friend_number, uint32_t file_number) {
42     FRIEND *f = get_friend(friend_number);
43     if (!f) {
44         return NULL;
45     }
46 
47     if (is_incoming_ft(file_number)) {
48         file_number = detox_incoming_file_number(file_number);
49         if (f->ft_incoming_size && f->ft_incoming_size >= file_number) {
50             return &f->ft_incoming[file_number];
51         }
52     } else if (f->ft_outgoing_size && f->ft_outgoing_size >= file_number) {
53         return &f->ft_outgoing[file_number];
54     }
55 
56     return NULL;
57 }
58 
make_file_transfer(uint32_t friend_number,uint32_t file_number)59 static FILE_TRANSFER *make_file_transfer(uint32_t friend_number, uint32_t file_number) {
60     FRIEND *f = get_friend(friend_number);
61     if (!f) {
62         return NULL;
63     }
64 
65     if (is_incoming_ft(file_number)) {
66         file_number = detox_incoming_file_number(file_number);
67         if (f->ft_incoming_size <= file_number) {
68             LOG_TRACE("FileTransfer", "Realloc incoming %u|%u" , friend_number, file_number + 1);
69 
70             FILE_TRANSFER *new_ftlist = realloc(f->ft_incoming, sizeof(FILE_TRANSFER) * (file_number + 1));
71             if (!new_ftlist) {
72                 LOG_ERR("FileTransfer", "Unable to allocate memory for new incoming file transfer.");
73                 return NULL;
74             }
75 
76             f->ft_incoming = new_ftlist;
77             f->ft_incoming_size = file_number + 1;
78         }
79 
80         return &f->ft_incoming[file_number];
81     }
82 
83     if (f->ft_outgoing_size <= file_number) {
84         LOG_TRACE("FileTransfer", "Realloc outgoing %u|%u" , friend_number, file_number + 1);
85 
86         FILE_TRANSFER *new_ftlist = realloc(f->ft_outgoing, sizeof(FILE_TRANSFER) * (file_number + 1));
87         if (!new_ftlist) {
88             LOG_ERR("FileTransfer", "Unable to allocate memory for new outgoing file transfer.");
89             return NULL;
90         }
91 
92         f->ft_outgoing = new_ftlist;
93         f->ft_outgoing_size = file_number + 1;
94     }
95 
96     return &f->ft_outgoing[file_number];
97 }
98 
99 /* Calculate the transfer speed for the UI. */
calculate_speed(FILE_TRANSFER * file)100 static void calculate_speed(FILE_TRANSFER *file) {
101     if (file->speed > file->num_packets * 20 * 1371) {
102         ++file->num_packets;
103         return;
104     }
105 
106     file->num_packets = 0;
107 
108     uint64_t time = get_time();
109     if (!file->last_check_time) {
110         file->last_check_time = time;
111         return;
112     }
113 
114     // TODO replace magic number with something real. (grayhatter> I think it's cpu clock ticks)
115     if (time - file->last_check_time >= 1000 * 1000 * 100) {
116         file->speed = (((double)(file->current_size - file->last_check_transferred) * 1000.0 * 1000.0 * 1000.0)
117                        / (double)(time - file->last_check_time))
118                       + 0.5;
119         file->last_check_time        = time;
120         file->last_check_transferred = file->current_size;
121     }
122 
123     FILE_TRANSFER *msg = calloc(1, sizeof(FILE_TRANSFER));
124     if (!msg) {
125         LOG_ERR("FileTransfer", "Unable to malloc for internal message. (This is bad!)");
126         return;
127     }
128 
129     *msg = *file;
130     postmessage_utox(FILE_STATUS_UPDATE, file->status, 0, msg);
131 }
132 
ft_decon(uint32_t friend_number,uint32_t file_number)133 static void ft_decon(uint32_t friend_number, uint32_t file_number) {
134     LOG_INFO("FileTransfer", "Cleaning up file transfers! (%u & %u)" , friend_number, file_number);
135     FILE_TRANSFER *ft = get_file_transfer(friend_number, file_number);
136     if (!ft) {
137         LOG_ERR("FileTransfer", "Can't decon a FT that doesn't exist!");
138         return;
139     }
140 
141     if (ft->in_use) {
142         while (ft->decon_wait) {
143             yieldcpu(10);
144         }
145 
146         if (ft->incoming) {
147             get_friend(friend_number)->ft_incoming_active_count--;
148         } else {
149             get_friend(friend_number)->ft_outgoing_active_count--;
150         }
151 
152         if (ft->name) {
153             free(ft->name);
154         }
155 
156         if (ft->in_memory) {
157             // free(ft->via.memory)?
158         } else if (ft->avatar) {
159             // free(ft->via.avatar)?
160         } else if (ft->via.file) {
161             fclose(ft->via.file);
162         }
163     }
164     /* When decon is called we always want to reset the struct. */
165     memset(ft, 0, sizeof(FILE_TRANSFER));
166 }
167 
resumeable_name(FILE_TRANSFER * ft,char * name)168 static bool resumeable_name(FILE_TRANSFER *ft, char *name) {
169     if (ft->incoming) {
170         char hex[TOX_HASH_LENGTH * 2];
171         fid_to_string(hex, ft->data_hash);
172         snprintf(name, UTOX_FILE_NAME_LENGTH, "%.*s.ftinfo", TOX_HASH_LENGTH * 2, hex);
173 
174         uint8_t blank_id[TOX_HASH_LENGTH] = { 0 };
175         if (memcmp(ft->data_hash, blank_id, TOX_HASH_LENGTH) == 0) {
176             LOG_ERR("FileTransfer", "Unable to use current data hash for incoming file.\n"
177                         "\t\tuTox can't resume file %.*s\n"
178                         "\t\tHash is %.*s\n",
179                         (uint32_t)ft->name_length, ft->name,
180                         TOX_HASH_LENGTH * 2, name);
181             return false;
182         }
183     } else {
184         snprintf(name, UTOX_FILE_NAME_LENGTH, "%.*s%02i.ftoutfo",
185                  TOX_PUBLIC_KEY_SIZE * 2, get_friend(ft->friend_number)->id_str,
186                     ft->file_number % 100);
187     }
188 
189     return true;
190 }
191 
ft_update_resumable(FILE_TRANSFER * ft)192 static bool ft_update_resumable(FILE_TRANSFER *ft) {
193     if (!ft->resume_file) {
194         LOG_ERR("FileTransfer", "Unable to save filetransfer info. Got NULL file pointer.");
195         return false;
196     }
197 
198     // This file pointer has a tendency of being both invalid and non-null so we use fstat to check it.
199     struct stat buffer;
200     if (fstat(fileno(ft->resume_file), &buffer) != 0) {
201         LOG_ERR("FileTransfer", "Unable to save file info. Invalid filepointer.");
202         return false;
203     }
204 
205     fseeko(ft->resume_file, SEEK_SET, 0);
206     if (fwrite(ft, sizeof(FILE_TRANSFER), 1, ft->resume_file) != 1) {
207         LOG_ERR("FileTransfer", "Unable to save file info... uTox can't resume file %.*s",
208                 ft->name_length, ft->name);
209         return false;
210     }
211 
212     fflush(ft->resume_file);
213     return true;
214 }
215 
216 /* Create the file transfer resume info file. */
ft_init_resumable(FILE_TRANSFER * ft)217 static bool ft_init_resumable(FILE_TRANSFER *ft) {
218     char name[UTOX_FILE_NAME_LENGTH];
219     if (!resumeable_name(ft, name)) {
220         return false;
221     }
222 
223     ft->resume_file = utox_get_file(name, NULL, UTOX_FILE_OPTS_WRITE | UTOX_FILE_OPTS_MKDIR);
224     if (!ft->resume_file) {
225         return false;
226     }
227 
228     LOG_INFO("FileTransfer", ".ftinfo for file %.*s set; ready to resume!" , (uint32_t)ft->name_length, ft->name);
229     return ft_update_resumable(ft);
230 }
231 
232 /* Free/Remove/Unlink the file transfer resume info file. */
ft_decon_resumable(FILE_TRANSFER * ft)233 static void ft_decon_resumable(FILE_TRANSFER *ft) {
234     char name[UTOX_FILE_NAME_LENGTH];
235     if (!resumeable_name(ft, name)) {
236         return;
237     }
238 
239     LOG_INFO("FileTransfer", "Going to decon file %s." , name);
240     FILE *file = utox_get_file(name, NULL, UTOX_FILE_OPTS_READ | UTOX_FILE_OPTS_WRITE);
241     if (!file) {
242         return;
243     }
244 
245     fclose(file);
246     utox_get_file(name, NULL, UTOX_FILE_OPTS_DELETE);
247 }
248 
ft_find_resumeable(FILE_TRANSFER * ft)249 static bool ft_find_resumeable(FILE_TRANSFER *ft) {
250     char resume_name[UTOX_FILE_NAME_LENGTH];
251     if (!resumeable_name(ft, resume_name)) {
252         return false;
253     }
254 
255     size_t size = 0;
256     FILE *resume_disk = utox_get_file(resume_name, &size, UTOX_FILE_OPTS_READ);
257 
258     if (!resume_disk) {
259         if (ft->incoming) {
260             LOG_INFO("FileTransfer", "Unable to load saved info... uTox can't resume file %.*s",
261                      (uint32_t)ft->name_length, ft->name);
262         }
263         ft->status = 0;
264         return false;
265     }
266 
267     if (size != sizeof(FILE_TRANSFER)) {
268         LOG_ERR("FileTransfer", "Unable to resume this file, size mismatch");
269         fclose(resume_disk);
270         return false;
271     }
272 
273     FILE_TRANSFER resume_file;
274     bool read_resumeable = fread(&resume_file, size, 1, resume_disk);
275     fclose(resume_disk);
276 
277     if (!read_resumeable) {
278         LOG_ERR("FileTransfer", "Failed to read resumeable file.");
279         return false;
280     }
281 
282     if (!resume_file.resumeable
283         || !resume_file.in_use
284         || resume_file.in_memory
285         || resume_file.avatar
286         || resume_file.inline_img) {
287         return false;
288     }
289 
290     memcpy(ft, &resume_file, sizeof(FILE_TRANSFER));
291 
292     ft->name_length = 0;
293     uint8_t *p = ft->path + strlen((char *)ft->path);
294     while (*--p != '/' && *p != '\\') {
295         ++ft->name_length;
296     }
297     ++p;
298     ++ft->name_length;
299 
300     ft->name = calloc(1, ft->name_length + 1);
301     if (!ft->name) {
302         LOG_FATAL_ERR(EXIT_MALLOC, "FileTransfer", "Could not alloc for file name (%uB)",
303                       ft->name_length + 1);
304     }
305     snprintf((char *)ft->name, ft->name_length + 1, "%s", p);
306 
307     ft->via.file = NULL;
308     ft->resume_file = NULL;
309     ft->ui_data = NULL;
310 
311     return true;
312 }
313 
314 /* Cancel active file. */
kill_file(FILE_TRANSFER * file)315 static void kill_file(FILE_TRANSFER *file) {
316     switch (file->status) {
317         case FILE_TRANSFER_STATUS_KILLED: {
318             LOG_WARN("FileTransfer", "File already killed.");
319             return;
320         }
321 
322         case FILE_TRANSFER_STATUS_COMPLETED: {
323             LOG_WARN("FileTransfer", "File already completed.");
324             return;
325         }
326 
327         default: {
328             break;
329         }
330     }
331 
332     file->status = FILE_TRANSFER_STATUS_KILLED;
333     postmessage_utox(FILE_STATUS_DONE, file->status, 0, file->ui_data);
334 
335     if (file->resumeable) {
336         ft_decon_resumable(file);
337     }
338 
339     ft_decon(file->friend_number, file->file_number);
340 }
341 
342 /* Break active file, (when a friend goes offline). */
break_file(FILE_TRANSFER * file)343 static void break_file(FILE_TRANSFER *file) {
344     if (!file) {
345         return;
346     }
347 
348     switch (file->status) {
349         case FILE_TRANSFER_STATUS_NONE: {
350             return kill_file(file);
351         }
352 
353         case FILE_TRANSFER_STATUS_COMPLETED:
354         case FILE_TRANSFER_STATUS_KILLED: {
355             // We don't break files that are already broken.
356             return;
357         }
358 
359         default: {
360             break;
361         }
362     }
363 
364     file->status = FILE_TRANSFER_STATUS_BROKEN;
365     postmessage_utox(FILE_STATUS_DONE, file->status, 0, file->ui_data);
366 
367     if (file->resumeable) {
368         ft_update_resumable(file);
369     }
370 
371     ft_decon(file->friend_number, file->file_number);
372 }
373 
374 /* Pause active file. */
utox_pause_file(FILE_TRANSFER * file,bool us)375 static void utox_pause_file(FILE_TRANSFER *file, bool us) {
376     switch (file->status) {
377         case FILE_TRANSFER_STATUS_NONE: {
378             if (!file->incoming) {
379                 // New transfers start as paused them
380                 file->status = FILE_TRANSFER_STATUS_PAUSED_THEM;
381             } else {
382                 LOG_TRACE("FileTransfer", "We can't pause an unaccepted file!");
383             }
384             break;
385         }
386 
387         case FILE_TRANSFER_STATUS_ACTIVE: {
388             if (us) {
389                 LOG_TRACE("FileTransfer", "File now paused by us.");
390                 file->status = FILE_TRANSFER_STATUS_PAUSED_US;
391             } else {
392                 LOG_TRACE("FileTransfer", "File now paused by them.");
393                 file->status = FILE_TRANSFER_STATUS_PAUSED_THEM;
394             }
395             break;
396         }
397 
398         case FILE_TRANSFER_STATUS_PAUSED_US:
399         case FILE_TRANSFER_STATUS_PAUSED_BOTH:
400         case FILE_TRANSFER_STATUS_PAUSED_THEM: {
401             if (us) {
402                 if (file->status == FILE_TRANSFER_STATUS_PAUSED_US) {
403                     LOG_TRACE("FileTransfer", "File already paused by us!");
404                 } else if (file->status == FILE_TRANSFER_STATUS_PAUSED_THEM) {
405                     file->status = FILE_TRANSFER_STATUS_PAUSED_BOTH;
406                     LOG_TRACE("FileTransfer", "File now paused by both!");
407                 } else if (file->status == FILE_TRANSFER_STATUS_PAUSED_BOTH) {
408                     LOG_TRACE("FileTransfer", "File already paused by both!");
409                 } else {
410                     file->status = FILE_TRANSFER_STATUS_PAUSED_US;
411                 }
412             } else {
413                 if (file->status == FILE_TRANSFER_STATUS_PAUSED_US) {
414                     file->status = FILE_TRANSFER_STATUS_PAUSED_BOTH;
415                     LOG_TRACE("FileTransfer", "File now paused by both!");
416                 } else if (file->status == FILE_TRANSFER_STATUS_PAUSED_THEM) {
417                     LOG_TRACE("FileTransfer", "File was already paused by them!");
418                 } else if (file->status == FILE_TRANSFER_STATUS_PAUSED_BOTH) {
419                     LOG_TRACE("FileTransfer", "File already paused by both!");
420                 } else {
421                     file->status = FILE_TRANSFER_STATUS_PAUSED_THEM;
422                 }
423             }
424             break;
425         }
426 
427         case FILE_TRANSFER_STATUS_BROKEN: {
428             LOG_TRACE("FileTransfer", "Can't pause a broken file;");
429             break;
430         }
431 
432         case FILE_TRANSFER_STATUS_COMPLETED: {
433             LOG_TRACE("FileTransfer", "Can't pause a completed file;");
434             break;
435         }
436 
437         case FILE_TRANSFER_STATUS_KILLED: {
438             LOG_TRACE("FileTransfer", "Can't pause a killed file;");
439             break;
440         }
441     }
442 
443     FILE_TRANSFER *msg = calloc(1, sizeof(FILE_TRANSFER));
444     if (!msg) {
445         LOG_ERR("FileTransfer", "Unable to malloc for internal message. (This is bad!)");
446         return;
447     }
448 
449     *msg = *file;
450     postmessage_utox(FILE_STATUS_UPDATE, file->status, 0, msg);
451     // TODO free not freed data.
452 }
453 
454 /* Start/Resume active file. */
run_file_local(FILE_TRANSFER * file)455 static void run_file_local(FILE_TRANSFER *file) {
456     switch (file->status) {
457         case FILE_TRANSFER_STATUS_NONE: {
458             file->status = FILE_TRANSFER_STATUS_ACTIVE;
459             if (!file->resumeable && file->incoming) {
460                 file->resumeable = ft_init_resumable(file);
461             }
462             break;
463         }
464 
465         case FILE_TRANSFER_STATUS_PAUSED_US: {
466             file->status = FILE_TRANSFER_STATUS_ACTIVE;
467             break;
468         }
469 
470         case FILE_TRANSFER_STATUS_PAUSED_BOTH: {
471             file->status = FILE_TRANSFER_STATUS_PAUSED_THEM;
472             break;
473         }
474 
475         case FILE_TRANSFER_STATUS_ACTIVE:
476         case FILE_TRANSFER_STATUS_PAUSED_THEM: {
477             return;
478         }
479 
480         case FILE_TRANSFER_STATUS_BROKEN:
481         case FILE_TRANSFER_STATUS_COMPLETED:
482         case FILE_TRANSFER_STATUS_KILLED: {
483             LOG_ERR("FileTransfer", "We tried to run file from an unknown state! (%u)" , file->status);
484             return;
485         }
486     }
487 
488     FILE_TRANSFER *msg = calloc(1, sizeof(FILE_TRANSFER));
489     if (!msg) {
490         LOG_ERR("FileTransfer", "Unable to malloc for internal message. (This is bad!)");
491         return;
492     }
493 
494     *msg = *file;
495     postmessage_utox(FILE_STATUS_UPDATE, file->status, 0, msg);
496 }
497 
run_file_remote(FILE_TRANSFER * file)498 static void run_file_remote(FILE_TRANSFER *file) {
499     switch (file->status) {
500         case FILE_TRANSFER_STATUS_PAUSED_US: {
501             break;
502         }
503 
504         case FILE_TRANSFER_STATUS_PAUSED_BOTH: {
505             file->status = FILE_TRANSFER_STATUS_PAUSED_US;
506             break;
507         }
508 
509         case FILE_TRANSFER_STATUS_PAUSED_THEM:
510         case FILE_TRANSFER_STATUS_BROKEN: {
511             file->status = FILE_TRANSFER_STATUS_ACTIVE;
512             break;
513         }
514 
515         default: {
516             LOG_ERR("FileTransfer", "They tried to run file from an unknown state! (%u)" , file->status);
517             break;
518         }
519     }
520 
521     FILE_TRANSFER *msg = calloc(1, sizeof(FILE_TRANSFER));
522     if (!msg) {
523         LOG_ERR("FileTransfer", "Unable to malloc for internal message. (This is bad!)");
524         return;
525     }
526 
527     *msg = *file;
528     postmessage_utox(FILE_STATUS_UPDATE, file->status, 0, msg);
529 }
530 
decode_inline_png(uint32_t friend_id,uint8_t * data,uint64_t size)531 static void decode_inline_png(uint32_t friend_id, uint8_t *data, uint64_t size) {
532     // TODO: start a new thread and decode the png in it.
533     uint16_t width, height;
534     // TODO: move the decode out of file_transfers.c
535     NATIVE_IMAGE *native_image = utox_image_to_native((UTOX_IMAGE)data, size, &width, &height, 0);
536     if (NATIVE_IMAGE_IS_VALID(native_image)) {
537         uint8_t *msg = malloc(sizeof(uint16_t) * 2 + sizeof(NATIVE_IMAGE *));
538         if (!msg) {
539             LOG_ERR("decode_inline_png", "Unable to malloc for inline data.");
540             free(native_image);
541             return;
542         }
543 
544         memcpy(msg, &width, sizeof(uint16_t));
545         memcpy(msg + sizeof(uint16_t), &height, sizeof(uint16_t));
546         memcpy(msg + sizeof(uint16_t) * 2, &native_image, sizeof(NATIVE_IMAGE *));
547 
548         postmessage_utox(FILE_INCOMING_NEW_INLINE, friend_id, 0, msg);
549     }
550 }
551 
552 /* Complete active file, (when the whole file transfer is successful). */
utox_complete_file(FILE_TRANSFER * file)553 static void utox_complete_file(FILE_TRANSFER *file) {
554     FILE_TRANSFER *msg = calloc(1, sizeof(FILE_TRANSFER));
555     if (!msg) {
556         LOG_ERR("FileTransfer", "Unable to malloc for internal message. (This is bad!)");
557         return;
558     }
559 
560     *msg = *file;
561     postmessage_utox(FILE_STATUS_UPDATE, file->status, 0, msg);
562 
563     if (file->status == FILE_TRANSFER_STATUS_ACTIVE) {
564         file->status = FILE_TRANSFER_STATUS_COMPLETED;
565         if (file->incoming) {
566             if (file->inline_img) {
567                 decode_inline_png(file->friend_number, file->via.memory, file->current_size);
568                 postmessage_utox(FILE_INCOMING_NEW_INLINE_DONE, file->friend_number, 0, file);
569             } else if (file->avatar) {
570                 postmessage_utox(FRIEND_AVATAR_SET, file->friend_number, file->current_size, file->via.avatar);
571             }
572         }
573         file->decon_wait = true;
574         postmessage_utox(FILE_STATUS_UPDATE_DATA, file->status, 0, file);
575     } else {
576         LOG_ERR("FileTransfer", "Unable to complete file in non-active state (file:%u)" , file->file_number);
577     }
578     LOG_NOTE("FileTransfer", "File transfer is done (%u & %u)" , file->friend_number, file->file_number);
579     postmessage_utox(FILE_STATUS_DONE, file->status, 0, file->ui_data);
580 
581     if (file->resumeable) {
582         ft_decon_resumable(file);
583     }
584 
585     ft_decon(file->friend_number, file->file_number);
586 }
587 
588 /* Friend has come online, restart our outgoing transfers to this friend. */
ft_friend_online(Tox * tox,uint32_t friend_number)589 void ft_friend_online(Tox *tox, uint32_t friend_number) {
590     for (uint16_t i = 0; i < MAX_FILE_TRANSFERS; i++) {
591         FILE_TRANSFER *file = calloc(1, sizeof(FILE_TRANSFER));
592         if (!file) {
593             LOG_FATAL_ERR(EXIT_MALLOC, "FileTransfer", "Could not alloc file transfer struct");
594         }
595         file->friend_number = friend_number;
596         file->file_number   = i;
597         file->incoming      = false;
598         ft_find_resumeable(file);
599         if (file->path[0]) {
600             /* If we got a path from utox_file_load we should try to resume! */
601             file->via.file = fopen((char *)file->path, "rb+");
602             ft_send_file(tox, friend_number, file->via.file, file->path, strlen((char *)file->path), file->data_hash);
603         }
604 
605         char name[UTOX_FILE_NAME_LENGTH];
606         if (resumeable_name(file, name)) {
607             LOG_INFO("FileTransfer", "Loading outgoing %u, deleting origin file.", i);
608             utox_get_file(name, NULL, UTOX_FILE_OPTS_DELETE);
609         }
610 
611         free(file);
612     }
613 }
614 
615 /* Friend has gone offline, break our outgoing transfers to this friend. */
ft_friend_offline(Tox * UNUSED (tox),uint32_t friend_number)616 void ft_friend_offline(Tox *UNUSED(tox), uint32_t friend_number) {
617     LOG_NOTE("FileTransfer", "Friend %u has gone offline, breaking transfers" , friend_number);
618 
619     FRIEND *f = get_friend(friend_number);
620     if (!f) {
621         return;
622     }
623 
624     for (uint16_t i = 0; i < f->ft_outgoing_size; ++i) {
625         break_file(&f->ft_outgoing[i]);
626     }
627 
628     for (uint16_t i = 0; i < f->ft_incoming_size; ++i) {
629         break_file(&f->ft_incoming[i]);
630     }
631 }
632 
633 /* Local command callback to change a file status. */
ft_local_control(Tox * tox,uint32_t friend_number,uint32_t file_number,TOX_FILE_CONTROL control)634 void ft_local_control(Tox *tox, uint32_t friend_number, uint32_t file_number, TOX_FILE_CONTROL control) {
635     FILE_TRANSFER *info  = get_file_transfer(friend_number, file_number);
636     if (!info) {
637         LOG_ERR("FileTransfer", "We know nothing of this file. This is probably an error. Friend(%u) FileNum(%u)",
638                 friend_number, file_number);
639         return;
640     }
641 
642     TOX_ERR_FILE_CONTROL error = 0;
643     switch (control) {
644         case TOX_FILE_CONTROL_RESUME: {
645             if (info->status != FILE_TRANSFER_STATUS_ACTIVE) {
646                 if (get_friend(friend_number)->ft_outgoing_size < MAX_FILE_TRANSFERS) {
647                     if (tox_file_control(tox, friend_number, file_number, control, &error)) {
648                         LOG_INFO("FileTransfer", "We just resumed file (%u & %u)" , friend_number, file_number);
649                     } else {
650                         LOG_INFO("FileTransfer", "Toxcore doesn't like us! (%u & %u)" , friend_number, file_number);
651                     }
652                 } else {
653                     LOG_INFO("FileTransfer", "Can't start file, max file transfer limit reached! (%u & %u)",
654                           friend_number, file_number);
655                 }
656             } else {
657                 LOG_INFO("FileTransfer", "File already active (%u & %u)" , friend_number, file_number);
658             }
659             run_file_local(info);
660             break;
661         }
662 
663         case TOX_FILE_CONTROL_PAUSE: {
664             if (info->status != FILE_TRANSFER_STATUS_PAUSED_US && info->status != FILE_TRANSFER_STATUS_PAUSED_BOTH) {
665                 if (tox_file_control(tox, friend_number, file_number, control, &error)) {
666                     LOG_INFO("FileTransfer", "We just paused file (%u & %u)" , friend_number, file_number);
667                 } else {
668                     LOG_INFO("FileTransfer", "Toxcore doesn't like us! (%u & %u)" , friend_number, file_number);
669                 }
670             } else {
671                 LOG_INFO("FileTransfer", "File already paused (%u & %u)" , friend_number, file_number);
672             }
673             utox_pause_file(info, true);
674             break;
675         }
676 
677         case TOX_FILE_CONTROL_CANCEL: {
678             if (info->status != FILE_TRANSFER_STATUS_KILLED) {
679                 if (tox_file_control(tox, friend_number, file_number, control, &error)) {
680                     LOG_INFO("FileTransfer", "We just killed file (%u & %u)" , friend_number, file_number);
681                 } else {
682                     LOG_INFO("FileTransfer", "Toxcore doesn't like us! (%u & %u)" , friend_number, file_number);
683                 }
684             } else {
685                 LOG_INFO("FileTransfer", "File already killed (%u & %u)" , friend_number, file_number);
686             }
687             kill_file(info);
688             break;
689         }
690     }
691     /* Do something with the error! */
692     switch (error) {
693         case TOX_ERR_FILE_CONTROL_OK: {
694             // Everything's fine.
695             break;
696         }
697 
698         case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND: {
699             LOG_ERR("FileTransfer", "Unable to send command, Friend (%u) doesn't exist!" , info->friend_number);
700             break;
701         }
702 
703         case TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED: {
704             LOG_ERR("FileTransfer", "Unable to send command, Friend (%u) offline!" , info->friend_number);
705             break;
706         }
707 
708         case TOX_ERR_FILE_CONTROL_NOT_FOUND: {
709             LOG_ERR("FileTransfer", "Unable to send command, ft (%u) doesn't exist!" , info->friend_number);
710             break;
711         }
712 
713         case TOX_ERR_FILE_CONTROL_DENIED: {
714             LOG_ERR("FileTransfer", "Unable to send command, ft (%u) paused by other party." , info->friend_number);
715             break;
716         }
717 
718         default: {
719             LOG_ERR("FileTransfer", "FileTransfer:\tThere was an error(%u) sending the command."
720                         "You probably want to see to that!\n", error);
721             break;
722         }
723     }
724 }
725 
726 /* Remote command callback for friends to change a file status */
file_transfer_callback_control(Tox * UNUSED (tox),uint32_t friend_number,uint32_t file_number,TOX_FILE_CONTROL control,void * UNUSED (userdata))727 static void file_transfer_callback_control(Tox *UNUSED(tox), uint32_t friend_number, uint32_t file_number,
728                                            TOX_FILE_CONTROL control, void *UNUSED(userdata))
729 {
730     FILE_TRANSFER *ft = get_file_transfer(friend_number, file_number);
731     if (!ft || !ft->in_use) {
732         return;
733     }
734 
735     switch (control) {
736         case TOX_FILE_CONTROL_RESUME: {
737             LOG_TRACE("FileTransfer", "Friend (%i) has resumed file (%i)" , friend_number, file_number);
738             run_file_remote(ft);
739             break;
740         }
741 
742         case TOX_FILE_CONTROL_PAUSE: {
743             LOG_TRACE("FileTransfer", "Friend (%i) has paused file (%i)" , friend_number, file_number);
744             utox_pause_file(ft, false);
745             break;
746         }
747 
748         case TOX_FILE_CONTROL_CANCEL: {
749             if (ft->avatar) {
750                 LOG_TRACE("FileTransfer", "Friend (%i) rejected avatar" , friend_number);
751             } else {
752                 LOG_TRACE("FileTransfer", "Friend (%i) has canceled file (%i)" , friend_number, file_number);
753             }
754             kill_file(ft);
755             break;
756         }
757     }
758 }
759 
incoming_avatar(Tox * tox,uint32_t friend_number,uint32_t file_number,uint64_t size)760 static void incoming_avatar(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t size) {
761     LOG_TRACE("FileTransfer", "Incoming avatar from friend %u." , friend_number);
762 
763     FRIEND *f = get_friend(friend_number);
764     if (!f) {
765         LOG_ERR("FileTransfer", "This friend doesn't exist... This is bad!");
766         return;
767     }
768 
769     if (size == 0) {
770         LOG_TRACE("FileTransfer", "Avatar from friend %u deleted." , friend_number);
771         postmessage_utox(FRIEND_AVATAR_UNSET, friend_number, 0, NULL);
772         ft_local_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL);
773         return;
774     } else if (size > UTOX_AVATAR_MAX_DATA_LENGTH) {
775         LOG_TRACE("FileTransfer", "Avatar from friend(%u) rejected. (Too Large %lu)" , friend_number, size);
776         ft_local_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL);
777         return;
778     }
779 
780     uint8_t file_id[TOX_FILE_ID_LENGTH] = { 0 };
781     tox_file_get_file_id(tox, friend_number, file_number, file_id, 0);
782 
783     /* Verify this is a new avatar */
784     if (f->avatar->format && memcmp(f->avatar->hash, file_id, TOX_HASH_LENGTH) == 0) {
785         LOG_TRACE("FileTransfer", "Avatar from friend (%u) rejected: Same as Current" , friend_number);
786         ft_local_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL);
787         return;
788     }
789 
790     FILE_TRANSFER *ft = make_file_transfer(friend_number, file_number);
791     if (!ft) {
792         LOG_ERR("FileTransfer", "Unable to malloc ft to accept incoming avatar!");
793         tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL, NULL);
794         return;
795     }
796 
797     f->ft_incoming_active_count++;
798 
799     memset(ft, 0, sizeof(FILE_TRANSFER));
800     ft->in_use = true;
801 
802     ft->friend_number = friend_number;
803     ft->file_number   = file_number;
804     ft->target_size   = size;
805 
806     tox_file_get_file_id(tox, friend_number, file_number, ft->data_hash, NULL);
807 
808     ft->incoming  = true;
809     ft->avatar    = true;
810 
811     ft->via.avatar = calloc(1, size);
812     if (!ft->via.avatar) {
813         LOG_ERR("FileTransfer", "Unable to malloc for incoming avatar");
814         ft_local_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL);
815         return;
816     }
817 
818     ft->status = FILE_TRANSFER_STATUS_PAUSED_US;
819     ft_local_control(tox, friend_number, file_number, TOX_FILE_CONTROL_RESUME);
820 }
821 
incoming_inline_image(Tox * tox,uint32_t friend_number,uint32_t file_number,size_t size)822 static void incoming_inline_image(Tox *tox, uint32_t friend_number, uint32_t file_number, size_t size) {
823     LOG_INFO("FileTransfer", "Getting an incoming inline image");
824 
825     FRIEND *f = get_friend(friend_number);
826     if (!f) {
827         LOG_ERR("FileTransfer", "This friend doesn't exist... This is bad!");
828         return;
829     }
830 
831     FILE_TRANSFER *ft = make_file_transfer(friend_number, file_number);
832     if (!ft) {
833         LOG_ERR("FileTransfer", "Unable to malloc ft to accept incoming inline image!");
834         tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL, NULL);
835         return;
836     }
837 
838     f->ft_incoming_active_count++;
839 
840     memset(ft, 0, sizeof(FILE_TRANSFER));
841     ft->in_use      = true;
842 
843     ft->incoming    = true;
844     ft->in_memory   = true;
845     ft->inline_img  = true;
846 
847     ft->friend_number = friend_number;
848     ft->file_number = file_number;
849 
850     ft->target_size = size;
851 
852     ft->via.memory = calloc(1, size);
853     if (!ft->via.memory) {
854         LOG_ERR("FileTransfer", "Unable to malloc enough memory for incoming inline image of size %lu" , size);
855         ft_local_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL);
856         return;
857     }
858 
859     LOG_NOTE("FileTransfer", "Starting incoming inline image of size %lu" , size);
860     ft_local_control(tox, friend_number, file_number, TOX_FILE_CONTROL_RESUME);
861 
862     ft->name = (uint8_t *)strdup("utox-inline.png");
863     ft->name_length = strlen("utox-inline.png");
864     if (!ft->name) {
865         LOG_ERR("FileTransfer", "Error, couldn't allocate memory for ft->name.");
866         ft->name_length = 0;
867     }
868 }
869 
870 /* Function called by core with a new file send request from a friend. */
incoming_file_callback_request(Tox * tox,uint32_t friend_number,uint32_t file_number,uint32_t kind,uint64_t size,const uint8_t * name,size_t name_length,void * UNUSED (user_data))871 static void incoming_file_callback_request(Tox *tox, uint32_t friend_number, uint32_t file_number, uint32_t kind,
872                                            uint64_t size, const uint8_t *name, size_t name_length,
873                                            void *UNUSED(user_data))
874 {
875     LOG_NOTE("FileTransfer", "New incoming file transfer request from friend %u" , friend_number);
876 
877     FRIEND *f = get_friend(friend_number);
878     if (f->ft_incoming_active_count >= MAX_INCOMING_COUNT) {
879         LOG_ERR("FileTransfer", "Too many incoming file transfers from friend %u", friend_number);
880         /* ft_local_control is preferred, but in this case it can't access the ft struct. */
881         tox_file_control(tox, friend_number, file_number, TOX_FILE_CANCEL, NULL);
882         return;
883     }
884 
885     if (kind == TOX_FILE_KIND_AVATAR) {
886         return incoming_avatar(tox, friend_number, file_number, size);
887     }
888 
889     if (settings.accept_inline_images
890         && size < MAX_INLINE_FILESIZE
891         && name_length == (sizeof("utox-inline.png") - 1)
892         && memcmp(name, "utox-inline.png", name_length) == 0)
893     {
894         return incoming_inline_image(tox, friend_number, file_number, size);
895     }
896 
897     FILE_TRANSFER *ft = make_file_transfer(friend_number, file_number);
898     if (!ft) {
899         LOG_ERR("FileTransfer", "Unable to get memory handle for transfer, canceling friend/file number (%u/%u)",
900               friend_number, file_number);
901         tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL, 0);
902         return;
903     }
904     f->ft_incoming_active_count++;
905 
906     memset(ft, 0, sizeof(FILE_TRANSFER));
907     ft->in_use = true;
908 
909     // Preload some data needed by ft_find_resumeable
910     ft->friend_number = friend_number;
911     ft->file_number   = file_number;
912     ft->incoming      = true;
913     tox_file_get_file_id(tox, friend_number, file_number, ft->data_hash, NULL);
914     ft->name = calloc(1, name_length + 1);
915     if (!ft->name) {
916         LOG_FATAL_ERR(EXIT_MALLOC, "FileTransfer", "Could not allocate space for file name (%uB)",
917                       name_length + 1);
918     }
919     snprintf((char *)ft->name, name_length + 1, "%.*s", (int)name_length, name);
920     ft->name_length = name_length;
921 
922     /* access the correct memory location for this file */
923     /* Load saved information about this file */
924     if (ft_find_resumeable(ft)) {
925         LOG_NOTE("FileTransfer", "Incoming Existing file from friend (%u) " , friend_number);
926         FILE *file = fopen((const char *)ft->path, "rb+");
927         if (file) {
928             LOG_INFO("FileTransfer", "Cool file exists, let try to restart it.");
929             ft->in_use        = true;
930             ft->in_memory     = false;
931             ft->avatar        = false;
932             ft->friend_number = friend_number;
933             ft->file_number   = file_number;
934             ft->target_size   = size;
935             ft->via.file      = file;
936 
937             FILE_TRANSFER *msg = calloc(1, sizeof(FILE_TRANSFER));
938             if (!msg) {
939                 LOG_ERR("FileTransfer", "Unable to malloc for internal message. (This is bad!)");
940                 return;
941             }
942             *msg = *ft;
943             postmessage_utox(FILE_SEND_NEW, friend_number, file_number, msg);
944             TOX_ERR_FILE_SEEK error = 0;
945             tox_file_seek(tox, friend_number, file_number, ft->current_size, &error);
946             if (error) {
947                 LOG_ERR("FileTransfer", "seek error %i" , error);
948                 // TODO UI error here as well;
949                 ft_local_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL);
950                 return;
951             }
952             LOG_INFO("FileTransfer", "seek & resume");
953             ft->status = FILE_TRANSFER_STATUS_NONE;
954             ft_local_control(tox, friend_number, file_number, TOX_FILE_CONTROL_RESUME);
955             ft->resumeable = ft_init_resumable(ft);
956             return;
957         }
958         LOG_ERR("FileTransfer", "Unable to open file suggested by resume!");
959         // This is fine-ish, we'll just fallback to new incoming file.
960     }
961 
962     ft->friend_number = friend_number;
963     ft->file_number   = file_number;
964 
965     ft->target_size = size;
966 
967     ft->resumeable = ft_init_resumable(ft);
968 
969     FILE_TRANSFER *msg = calloc(1, sizeof(FILE_TRANSFER));
970     if (!msg) {
971         LOG_ERR("FileTransfer", "Unable to malloc for internal message. (This is bad!)");
972         return;
973     }
974 
975     *msg = *ft;
976     postmessage_utox(FILE_INCOMING_NEW, friend_number, detox_incoming_file_number(file_number), msg);
977     /* The file doesn't exist on disk where we expected, let's prompt the user to accept it as a new file */
978     LOG_NOTE("FileTransfer", "New incoming file from friend (%u) file number (%u)\nFileTransfer:\t\tfilename: %s",
979           friend_number, file_number, name);
980     /* Auto accept if it's a utox-inline image, with the correct size */
981 }
982 
983 /* Called by toxcore to deliver the next chunk of incoming data. */
incoming_file_callback_chunk(Tox * tox,uint32_t friend_number,uint32_t file_number,uint64_t position,const uint8_t * data,size_t length,void * UNUSED (user_data))984 static void incoming_file_callback_chunk(Tox *tox, uint32_t friend_number, uint32_t file_number,
985                                          uint64_t position, const uint8_t *data, size_t length, void *UNUSED(user_data))
986 {
987     LOG_INFO("FileTransfer", "Incoming chunk friend(%u), file(%u), start(%lu), end(%lu), \n",
988             friend_number, file_number, position, length);
989 
990     FILE_TRANSFER *ft = get_file_transfer(friend_number, file_number);
991     if (!ft || !ft->in_use) {
992         LOG_ERR("FileTransfer", "ERROR incoming chunk for an out of use file transfer!");
993         return;
994     }
995 
996     if (length == 0) {
997         utox_complete_file(ft);
998         return;
999     }
1000 
1001     if (ft->inline_img && ft->via.memory) {
1002         if (position == 0) {
1003             uint8_t png_header[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
1004             if (memcmp(data, png_header, 8) != 0) {
1005                 // this isn't a png header, just die
1006                 LOG_ERR("FileTransfer", "Friend %u sent an inline image thats' not a PNG" , friend_number);
1007                 ft_local_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL);
1008                 return;
1009             }
1010         }
1011         memcpy(ft->via.memory + position, data, length);
1012     } else if (ft->avatar && ft->via.avatar) {
1013         memcpy(ft->via.avatar + position, data, length);
1014     } else if (ft->via.file) {
1015         uint8_t count = 10;
1016         while (!file_lock(ft->via.file, position, length)) {
1017             LOG_ERR("FileTransfer", "Can't get lock, sleeping...");
1018             yieldcpu(10);
1019             if (count == 0) {
1020                 break;
1021             }
1022             count--;
1023             // If you get a bug report about this hanging utox, just disable it, it's unlikely to be needed!
1024         }
1025         fseeko(ft->via.file, position, SEEK_SET);
1026         size_t write_size = fwrite(data, 1, length, ft->via.file);
1027         fflush(ft->via.file);
1028         file_unlock(ft->via.file, position, length);
1029         if (write_size != length) {
1030             LOG_ERR("FileTransfer", "\n\nFileTransfer:\tERROR WRITING DATA TO FILE! (%u & %u)\n\n", friend_number, file_number);
1031             ft_local_control(tox, friend_number, file_number, TOX_FILE_CANCEL);
1032             return;
1033         }
1034         calculate_speed(ft);
1035     } else {
1036         LOG_TRACE("FileTransfer", "File Handle failed!");
1037         ft_local_control(tox, friend_number, file_number, TOX_FILE_CANCEL);
1038         return;
1039     }
1040 
1041     ft->current_size += length;
1042     if (ft->resume_update) {
1043         --ft->resume_update;
1044     } else {
1045         ft_update_resumable(ft);
1046         ft->resume_update = 20; // every 20 packets we update
1047     }
1048     // TODO dirty hack, this needs to be replaced
1049     // moved it cal_speed() // ft_update_resumable(ft);
1050 }
1051 
ft_send_avatar(Tox * tox,uint32_t friend_number)1052 uint32_t ft_send_avatar(Tox *tox, uint32_t friend_number) {
1053     if (!tox || !self.png_data) {
1054         LOG_ERR("FileTransfer", "Can't send an avatar without data");
1055         return UINT32_MAX;
1056     }
1057     LOG_NOTE("FileTransfer", "Starting avatar to friend %u." , friend_number);
1058 
1059     // TODO send the unset avatar command.
1060 
1061     FRIEND *f = get_friend(friend_number);
1062     if (!f) {
1063         LOG_ERR("FileTransfer", "Unable to get friend %u to send avatar.", friend_number);
1064         return UINT32_MAX;
1065     }
1066 
1067     if (f->ft_outgoing_active_count > MAX_FILE_TRANSFERS) {
1068         LOG_ERR("FileTransfer", "Can't send this avatar too many in progress...");
1069         return UINT32_MAX;
1070     }
1071 
1072     /* While It's not ideal, we don't make sure we can alloc the FILE_TRANSFER until
1073      * we get the file number from toxcore. This could happen, but I assume it'll be
1074      * rare enough. Either way, it'll be noisy if it fails so here's to hoping! */
1075     uint8_t hash[TOX_HASH_LENGTH];
1076     tox_hash(hash, self.png_data, self.png_size);
1077 
1078     TOX_ERR_FILE_SEND error = 0;
1079     uint32_t file_number = tox_file_send(tox, friend_number, TOX_FILE_KIND_AVATAR,
1080                                          self.png_size, hash, NULL, 0, &error);
1081     if (error || file_number == UINT32_MAX) {
1082         LOG_ERR("FileTransfer", "tox_file_send() failed error code %u", error);
1083         return UINT32_MAX;
1084     };
1085 
1086     FILE_TRANSFER *ft = make_file_transfer(friend_number, file_number);
1087     if (!ft) {
1088         // This is the noisy case noted above.
1089         LOG_ERR("FileTransfer", "Unable to malloc to actually send avatar!");
1090         tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL, NULL);
1091         return UINT32_MAX;
1092     }
1093     /* All errors handled */
1094     ++f->ft_outgoing_active_count;
1095 
1096     memset(ft, 0, sizeof(FILE_TRANSFER));
1097     ft->in_use = true;
1098 
1099     ft->incoming = false;
1100     ft->avatar = true;
1101     ft->friend_number = friend_number;
1102     ft->file_number = file_number;
1103     memcpy(ft->data_hash, hash, TOX_HASH_LENGTH);
1104     ft->target_size = self.png_size;
1105     ft->status = FILE_TRANSFER_STATUS_PAUSED_THEM;
1106 
1107     LOG_INFO("FileTransfer", "File transfer #%u sent to friend %u", ft->file_number, ft->friend_number);
1108     return file_number;
1109 }
1110 
ft_send_file(Tox * tox,uint32_t friend_number,FILE * file,uint8_t * path,size_t path_length,uint8_t * hash)1111 uint32_t ft_send_file(Tox *tox, uint32_t friend_number, FILE *file, uint8_t *path, size_t path_length, uint8_t *hash) {
1112     if (!tox || !file) {
1113         LOG_ERR("FileTransfer", "Can't send a file without data");
1114         return UINT32_MAX;
1115     }
1116     LOG_TRACE("FileTransfer", "Starting FILE to friend %u." , friend_number);
1117 
1118     FRIEND *f = get_friend(friend_number);
1119     if (!f) {
1120         LOG_ERR("FileTransfer", "Unable to get friend %u to send file.", friend_number);
1121         return UINT32_MAX;
1122     }
1123 
1124     if (f->ft_outgoing_active_count > MAX_FILE_TRANSFERS) {
1125         LOG_ERR("FileTransfer", "Can't send this file too many in progress...");
1126         return UINT32_MAX;
1127     }
1128 
1129     fseeko(file, 0, SEEK_END);
1130     size_t size = ftello(file);
1131 
1132     const uint8_t *name = path + path_length;
1133     size_t name_length = 0;
1134     while (*--name != '/' && *name != '\\') { // TODO remove widows style path support from uTox.
1135         ++name_length;
1136     }
1137     ++name;
1138 
1139     TOX_ERR_FILE_SEND error = 0;
1140     uint32_t file_number = tox_file_send(tox, friend_number, TOX_FILE_KIND_DATA, size, hash, name, name_length, &error);
1141     if (error || file_number == UINT32_MAX) {
1142         switch (error) {
1143             case TOX_ERR_FILE_SEND_NULL: {
1144                 LOG_ERR("FileTransfer", "Error, Toxcore reports NULL"); break;
1145             }
1146             case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND: {
1147                 LOG_ERR("FileTransfer", "Error, friend Not found."); break;
1148             }
1149             case TOX_ERR_FILE_SEND_FRIEND_NOT_CONNECTED: {
1150                 LOG_ERR("FileTransfer", "Error, friend not connected."); break;
1151             }
1152             case TOX_ERR_FILE_SEND_NAME_TOO_LONG: {
1153                 LOG_ERR("FileTransfer", "Error, name too long '%s'" , name); break;
1154             }
1155             case TOX_ERR_FILE_SEND_TOO_MANY: {
1156                 LOG_ERR("FileTransfer", "Error, too many files in progress"); break;
1157             }
1158             case TOX_ERR_FILE_SEND_OK: { break; }
1159         }
1160         LOG_ERR("FileTransfer", "tox_file_send() failed error code %u", error);
1161         return UINT32_MAX;
1162     }
1163 
1164     FILE_TRANSFER *ft = make_file_transfer(friend_number, file_number);
1165     if (!ft) {
1166         // This is the noisy case noted above.
1167         LOG_ERR("FileTransfer", "Unable to malloc to actually send file!");
1168         tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL, NULL);
1169         return UINT32_MAX;
1170     }
1171     ++f->ft_outgoing_active_count;
1172 
1173     memset(ft, 0, sizeof(FILE_TRANSFER));
1174     ft->in_use = true;
1175 
1176     ft->incoming      = false;
1177     ft->friend_number = friend_number;
1178     ft->file_number   = file_number;
1179 
1180     ft->target_size = size;
1181 
1182     ft->name = calloc(1, name_length + 1);
1183     if (!ft->name) {
1184         LOG_ERR("FileTransfer", "Error, couldn't allocate memory for ft->name.");
1185         --f->ft_outgoing_active_count;
1186         return UINT32_MAX;
1187     }
1188     ft->name_length = name_length;
1189     snprintf((char *)ft->name, name_length + 1, "%.*s", (int)name_length, name);
1190 
1191     snprintf((char *)ft->path, UTOX_FILE_NAME_LENGTH, "%.*s", (int)path_length, path);
1192 
1193     ft->via.file = file;
1194     tox_file_get_file_id(tox, friend_number, file_number, ft->data_hash, NULL);
1195     ft->resumeable = ft_init_resumable(ft);
1196 
1197     ft->status = FILE_TRANSFER_STATUS_PAUSED_THEM;
1198 
1199     FILE_TRANSFER *msg = calloc(1, sizeof(FILE_TRANSFER));
1200     if (!msg) {
1201         LOG_ERR("FileTransfer", "Unable to malloc for internal message. (This is bad!)");
1202         return UINT32_MAX;
1203     }
1204     *msg = *ft;
1205     postmessage_utox(FILE_SEND_NEW, friend_number, file_number, msg);
1206     return file_number;
1207 }
1208 
1209 /* Returns file number on success, UINT32_MAX on failure. */
ft_send_data(Tox * tox,uint32_t friend_number,uint8_t * data,size_t size,uint8_t * name,size_t name_length)1210 uint32_t ft_send_data(Tox *tox, uint32_t friend_number, uint8_t *data, size_t size, uint8_t *name, size_t name_length) {
1211     if (!tox || !data || !name) {
1212         LOG_ERR("FileTransfer", "Can't send data to friend without data");
1213         return UINT32_MAX;
1214     }
1215 
1216     LOG_INFO("FileTransfer", "Starting raw data transfer to friend %u." , friend_number);
1217 
1218     // TODO send the unset avatar command.
1219 
1220     FRIEND *f = get_friend(friend_number);
1221     if (!f) {
1222         LOG_ERR("FileTransfer", "Unable to get friend %u to send raw data.", friend_number);
1223         return UINT32_MAX;
1224     }
1225 
1226     if (f->ft_outgoing_active_count >= MAX_FILE_TRANSFERS) {
1227         LOG_ERR("FileTransfer", "Can't send raw data too many in progress...");
1228         return UINT32_MAX;
1229     }
1230 
1231     /* While It's not ideal, we don't make sure we can alloc the FILE_TRANSFER until
1232      * we get the file number from toxcore. This could happen, but I assume it'll be
1233      * rare enough. Either way, it'll be noisy if it fails so here's to hoping! */
1234     uint8_t hash[TOX_HASH_LENGTH];
1235     tox_hash(hash, data, size); // TODO skip this if the file is HUGE!
1236 
1237     TOX_ERR_FILE_SEND error = 0;
1238     uint32_t file_number = tox_file_send(tox, friend_number, TOX_FILE_KIND_DATA, size, hash, name, name_length, &error);
1239     if (error || file_number == UINT32_MAX) {
1240         LOG_ERR("FileTransfer", "tox_file_send() failed error code %u", error);
1241         return UINT32_MAX;
1242     };
1243 
1244     FILE_TRANSFER *ft = make_file_transfer(friend_number, file_number);
1245     if (!ft) {
1246         // This is the noisy case noted above.
1247         LOG_ERR("FileTransfer", "Unable to malloc to actually send data!");
1248         tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL, NULL);
1249         return UINT32_MAX;
1250     }
1251 
1252     ++f->ft_outgoing_active_count;
1253 
1254     memset(ft, 0, sizeof(FILE_TRANSFER));
1255     ft->in_use     = true;
1256 
1257     ft->incoming   = false;
1258     ft->in_memory  = true;
1259     ft->inline_img = true;
1260 
1261     ft->name = calloc(1, name_length + 1);
1262     if (!ft->name) {
1263         LOG_ERR("FileTransfer", "Error, couldn't allocate memory for ft->name.");
1264         --f->ft_outgoing_active_count;
1265         return UINT32_MAX;
1266     }
1267 
1268     ft->name_length = name_length;
1269     snprintf((char *)ft->name, name_length + 1, "%.*s", (int)name_length, name);
1270 
1271     ft->friend_number = friend_number;
1272     ft->file_number = file_number;
1273 
1274     memcpy(ft->data_hash, hash, TOX_HASH_LENGTH);
1275 
1276     ft->via.memory = data;
1277     ft->target_size = size;
1278     ft->status = FILE_TRANSFER_STATUS_PAUSED_THEM;
1279 
1280 
1281     FILE_TRANSFER *msg = calloc(1, sizeof(FILE_TRANSFER));
1282     if (!msg) {
1283         LOG_ERR("FileTransfer", "Unable to malloc for internal message. (This is bad!)");
1284         return UINT32_MAX;
1285     }
1286 
1287     *msg = *ft;
1288     postmessage_utox(FILE_SEND_NEW, friend_number, file_number, msg);
1289     LOG_INFO("FileTransfer", "Inline image sent to friend. FT %u, Friend %u", ft->file_number, ft->friend_number);
1290 
1291     return file_number;
1292 }
1293 
ft_set_ui_data(uint32_t friend_number,uint32_t file_number,MSG_HEADER * ui_data)1294 bool ft_set_ui_data(uint32_t friend_number, uint32_t file_number, MSG_HEADER *ui_data) {
1295     FILE_TRANSFER *file = get_file_transfer(friend_number, file_number);
1296     if (!file) {
1297         LOG_WARN("FileTransfer", "Unable to set ui_data for unknown file number %u with friend %u",
1298                  file_number, friend_number);
1299         return false;
1300     }
1301 
1302     file->ui_data = ui_data;
1303     return true;
1304 }
1305 
outgoing_file_callback_chunk(Tox * tox,uint32_t friend_number,uint32_t file_number,uint64_t position,size_t length,void * UNUSED (user_data))1306 static void outgoing_file_callback_chunk(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position,
1307                                          size_t length, void *UNUSED(user_data))
1308 {
1309     LOG_INFO("FileTransfer", "Chunk requested for friend_id (%u), and file_id (%u). Start (%lu), End (%zu).\r",
1310             friend_number, file_number, position, length);
1311 
1312     FILE_TRANSFER *ft = get_file_transfer(friend_number, file_number);
1313     if (!ft) {
1314         LOG_ERR("FileTransfer", "Unabele to get file transfer (%u & %u)", friend_number, file_number);
1315         return;
1316     }
1317 
1318     if (length == 0) {
1319         LOG_NOTE("FileTransfer", "Outgoing transfer is done (%u & %u)", friend_number, file_number);
1320         utox_complete_file(ft);
1321         return;
1322     }
1323 
1324     if (position + length > ft->target_size) {
1325         LOG_ERR("FileTransfer", "Outing transfer size mismatch!");
1326         return;
1327     }
1328 
1329     TOX_ERR_FILE_SEND_CHUNK error = 0;
1330     if (ft->in_memory) {
1331         if (!ft->via.memory) {
1332             LOG_ERR("FileTransfer", "ERROR READING FROM MEMORY! (%u & %u)", friend_number, file_number);
1333             return;
1334         }
1335 
1336         tox_file_send_chunk(tox, friend_number, file_number, position, ft->via.memory + position, length, &error);
1337         if (error) {
1338             LOG_ERR("FileTransfer", "Outgoing chunk error on memory (%u)", error);
1339         }
1340 
1341         calculate_speed(ft);
1342     } else if (ft->avatar) {
1343         if (!self.png_data) {
1344             LOG_ERR("FileTransfer", "ERROR READING FROM AVATAR! (%u & %u)", friend_number, file_number);
1345             return;
1346         }
1347 
1348         tox_file_send_chunk(tox, friend_number, file_number, position, self.png_data + position, length, &error);
1349         if (error) {
1350             LOG_ERR("FileTransfer", "Outgoing chunk error on avatar (%u)", error);
1351         }
1352     } else { // File
1353         if (ft->via.file) {
1354             uint8_t buffer[length];
1355             fseeko(ft->via.file, position, SEEK_SET);
1356             if (fread(buffer, length, 1, ft->via.file) != 1) {
1357                 LOG_ERR("FileTransfer", "ERROR READING FILE! (%u & %u)", friend_number, file_number);
1358                 LOG_INFO("FileTransfer", "Size (%lu), Position (%lu), Length(%lu), size_transferred (%lu).",
1359                          ft->target_size, position, length, ft->current_size);
1360                 ft_local_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL);
1361                 return;
1362             }
1363 
1364             tox_file_send_chunk(tox, friend_number, file_number, position, buffer, length, &error);
1365             if (error) {
1366                 LOG_ERR("FileTransfer", "Outgoing chunk error on file (%u)", error);
1367             }
1368         }
1369         calculate_speed(ft);
1370     }
1371 
1372     ft->current_size += length;
1373 }
1374 
utox_file_start_write(uint32_t friend_number,uint32_t file_number,const char * file)1375 bool utox_file_start_write(uint32_t friend_number, uint32_t file_number, const char *file) {
1376     FILE_TRANSFER *ft = get_file_transfer(friend_number, file_number);
1377     if (!ft || !file) {
1378         LOG_ERR("FileTransfer", "FileTransfer:\tUnable to grab a file to start the write friend %u, file %u.",
1379                     friend_number, file_number);
1380         return false;
1381     }
1382 
1383     snprintf((char *)ft->path, UTOX_FILE_NAME_LENGTH, "%s", file);
1384 
1385     ft->via.file = utox_get_file_simple((char *)ft->path, UTOX_FILE_OPTS_WRITE);
1386     if (!ft->via.file) {
1387         LOG_ERR("FileTransfer", "The file we're supposed to write to couldn't be opened\n\t\t\"%s\"", ft->path);
1388         break_file(ft);
1389         return false;
1390     }
1391 
1392     return true;
1393 }
1394 
utox_set_callbacks_file_transfer(Tox * tox)1395 void utox_set_callbacks_file_transfer(Tox *tox) {
1396     /* Incoming files */
1397 
1398     /* This is the callback for a new incoming file. */
1399     tox_callback_file_recv(tox, incoming_file_callback_request);
1400 
1401     /* This is the callback with friend's actions for a file */
1402     tox_callback_file_recv_control(tox, file_transfer_callback_control);
1403 
1404     /* This is the callback with a chunk data for a file. */
1405     tox_callback_file_recv_chunk(tox, incoming_file_callback_chunk);
1406 
1407 
1408     /* Outgoing files */
1409 
1410     /* This is the callback send to request a new file chunk */
1411     tox_callback_file_chunk_request(tox, outgoing_file_callback_chunk);
1412 }
1413