diff --git a/src/chat.c b/src/chat.c index f41958d..89cbe93 100644 --- a/src/chat.c +++ b/src/chat.c @@ -188,8 +188,8 @@ static void chat_onMessage(ToxWindow *self, Tox *m, uint32_t num, TOX_MESSAGE_TY return recv_action_helper(self, m, num, msg, len, nick, timefrmt); } -static void chat_resume_file_transfers(Tox *m, uint32_t fnum); -static void chat_stop_file_senders(Tox *m, uint32_t friendnum); +static void chat_pause_file_transfers(Tox *m, uint32_t friendnum); +static void chat_resume_file_senders(ToxWindow *self, Tox *m, uint32_t fnum); static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_CONNECTION connection_status) { @@ -209,7 +209,7 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C if (connection_status != TOX_CONNECTION_NONE && statusbar->connection == TOX_CONNECTION_NONE) { Friends.list[num].is_typing = user_settings->show_typing_other == SHOW_TYPING_ON ? tox_friend_get_typing(m, num, NULL) : false; - chat_resume_file_transfers(m, num); + chat_resume_file_senders(self, m, num); msg = "has come online"; line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg); @@ -220,7 +220,7 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C if (self->chatwin->self_is_typing) set_self_typingstatus(self, m, 0); - chat_stop_file_senders(m, num); + chat_pause_file_transfers(m, num); msg = "has gone offline"; line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg); @@ -277,17 +277,44 @@ static void chat_onReadReceipt(ToxWindow *self, Tox *m, uint32_t num, uint32_t r cqueue_remove(self, m, receipt); } -/* Stops active file senders for this friend. Call when a friend goes offline */ -static void chat_stop_file_senders(Tox *m, uint32_t friendnum) +/* Stops active file transfers for this friend. Called when a friend goes offline */ +static void chat_pause_file_transfers(Tox *m, uint32_t friendnum) { - // TODO: core purges file transfers when a friend goes offline. Ideally we want to repair/resume - kill_all_file_transfers_friend(m, friendnum); + ToxicFriend *friend = &Friends.list[friendnum]; + + size_t i; + + for (i = 0; i < MAX_FILES; ++i) { + if (friend->file_sender[i].state >= FILE_TRANSFER_STARTED) + friend->file_sender[i].state = FILE_TRANSFER_PAUSED; + + if (friend->file_receiver[i].state >= FILE_TRANSFER_STARTED) + friend->file_receiver[i].state = FILE_TRANSFER_PAUSED; + } } -/* Tries to resume broken file transfers. Call when a friend comes online */ -static void chat_resume_file_transfers(Tox *m, uint32_t fnum) +/* Tries to resume broken file senders. Called when a friend comes online */ +static void chat_resume_file_senders(ToxWindow *self, Tox *m, uint32_t friendnum) { - // TODO + size_t i; + + for (i = 0; i < MAX_FILES; ++i) { + struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i]; + + if (ft->state != FILE_TRANSFER_PAUSED) + continue; + + TOX_ERR_FILE_SEND err; + ft->filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_DATA, ft->file_size, ft->file_id, + (uint8_t *) ft->file_name, strlen(ft->file_name), &err); + + if (err != TOX_ERR_FILE_SEND_OK) { + char msg[MAX_STR_SIZE]; + snprintf(msg, sizeof(msg), "File transfer for '%s' failed.", ft->file_name); + close_file_transfer(self, m, ft, -1, msg, notif_error); + continue; + } + } } static void chat_onFileChunkRequest(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, uint64_t position, @@ -428,12 +455,63 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint } } +/* Attempts to resume a broken inbound file transfer. + * + * Returns true if resume is successful. + */ +static bool chat_resume_broken_ft(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum) +{ + char msg[MAX_STR_SIZE]; + uint8_t file_id[TOX_FILE_ID_LENGTH]; + + if (!tox_file_get_file_id(m, friendnum, filenum, file_id, NULL)) + return false; + + bool resuming = false; + struct FileTransfer *ft = NULL; + size_t i; + + for (i = 0; i < MAX_FILES; ++i) { + ft = &Friends.list[friendnum].file_receiver[i]; + + if (ft->state == FILE_TRANSFER_INACTIVE) + continue; + + if (memcmp(ft->file_id, file_id, TOX_FILE_ID_LENGTH) == 0) { + ft->filenum = filenum; + resuming = true; + break; + } + } + + if (!resuming || !ft) + return false; + + if (!tox_file_seek(m, ft->friendnum, ft->filenum, ft->position, NULL)) + goto on_error; + + if (!tox_file_control(m, friendnum, filenum, TOX_FILE_CONTROL_RESUME, NULL)) + goto on_error; + + ft->state = FILE_TRANSFER_STARTED; + return true; + +on_error: + snprintf(msg, sizeof(msg), "File transfer for '%s' failed.", ft->file_name); + close_file_transfer(self, m, ft, -1, msg, notif_error); + return false; +} + static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, uint64_t file_size, const char *filename, size_t name_length) { if (self->num != friendnum) return; + /* first check if we need to resume a broken transfer */ + if (chat_resume_broken_ft(self, m, friendnum, filenum)) + return; + struct FileTransfer *ft = get_new_file_receiver(friendnum); if (!ft) { @@ -500,6 +578,7 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_ ft->file_type = TOX_FILE_KIND_DATA; snprintf(ft->file_path, sizeof(ft->file_path), "%s", file_path); snprintf(ft->file_name, sizeof(ft->file_name), "%s", filename); + tox_file_get_file_id(m, friendnum, filenum, ft->file_id, NULL); if (self->active_box != -1) box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS, self->active_box, diff --git a/src/chat_commands.c b/src/chat_commands.c index 3352f47..ef9dbe0 100644 --- a/src/chat_commands.c +++ b/src/chat_commands.c @@ -278,6 +278,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv ft->friendnum = self->num; ft->direction = FILE_TRANSFER_SEND; ft->file_type = TOX_FILE_KIND_DATA; + tox_file_get_file_id(m, self->num, filenum, ft->file_id, NULL); char sizestr[32]; bytes_convert_str(sizestr, sizeof(sizestr), filesize); diff --git a/src/file_transfers.c b/src/file_transfers.c index 771f958..f4ce0e5 100644 --- a/src/file_transfers.c +++ b/src/file_transfers.c @@ -209,8 +209,6 @@ void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int C if (ft->file) fclose(ft->file); - memset(ft, 0, sizeof(struct FileTransfer)); - if (CTRL >= 0) tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL); @@ -222,6 +220,8 @@ void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int C line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", message); } + + memset(ft, 0, sizeof(struct FileTransfer)); } /* Kills all active file transfers for friendnum */ diff --git a/src/file_transfers.h b/src/file_transfers.h index 52b19e1..39b2c30 100644 --- a/src/file_transfers.h +++ b/src/file_transfers.h @@ -30,8 +30,8 @@ #include "notify.h" #define KiB 1024 -#define MiB 1048576 /* 1024 ^ 2 */ -#define GiB 1073741824 /* 1024 ^ 3 */ +#define MiB 1048576 /* 1024^2 */ +#define GiB 1073741824 /* 1024^3 */ #define MAX_FILES 32 #define TIMEOUT_FILESENDER 120 @@ -40,7 +40,7 @@ typedef enum FILE_TRANSFER_STATE { FILE_TRANSFER_INACTIVE, FILE_TRANSFER_PENDING, FILE_TRANSFER_STARTED, - FILE_TRANSFER_PAUSED + FILE_TRANSFER_PAUSED, } FILE_TRANSFER_STATE; typedef enum FILE_TRANSFER_DIRECTION { @@ -63,6 +63,7 @@ struct FileTransfer { uint64_t position; uint64_t last_progress; uint32_t line_id; + uint8_t file_id[TOX_FILE_ID_LENGTH]; }; /* creates initial progress line that will be updated during file transfer.