1
0
mirror of https://github.com/Tha14/toxic.git synced 2024-11-22 21:13:02 +01:00

Implement file transfer queue for offline friends

File transfers initiated for offline friends are now
added to a queue and initiated all at once when the friend
appears online.
This commit is contained in:
jfreegman 2021-12-22 14:26:07 -05:00
parent 8d58e8f4d6
commit 6f8f6f0ac5
No known key found for this signature in database
GPG Key ID: 3627F3144076AE63
7 changed files with 193 additions and 47 deletions

View File

@ -248,6 +248,7 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, Tox_C
if (prev_status == TOX_CONNECTION_NONE) { if (prev_status == TOX_CONNECTION_NONE) {
chat_resume_file_senders(self, m, num); chat_resume_file_senders(self, m, num);
file_send_queue_check(self, m, self->num);
msg = "has come online"; msg = "has come online";
line_info_add(self, true, nick, NULL, CONNECTION, 0, GREEN, msg); line_info_add(self, true, nick, NULL, CONNECTION, 0, GREEN, msg);

View File

@ -54,7 +54,13 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
return; return;
} }
struct FileTransfer *ft = NULL; // first check transfer queue
if (file_send_queue_remove(self->num, idx) == 0) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Pending file transfer removed from queue");
return;
}
FileTransfer *ft = NULL;
/* cancel an incoming file transfer */ /* cancel an incoming file transfer */
if (strcasecmp(inoutstr, "in") == 0) { if (strcasecmp(inoutstr, "in") == 0) {
@ -239,7 +245,7 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
return; return;
} }
struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV); FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
if (!ft) { if (!ft) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
@ -324,7 +330,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
FILE *file_to_send = fopen(path, "r"); FILE *file_to_send = fopen(path, "r");
if (file_to_send == NULL) { if (file_to_send == NULL) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File not found."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File `%s` not found.", path);
return; return;
} }
@ -347,7 +353,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
goto on_send_error; goto on_send_error;
} }
struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA); FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA);
if (!ft) { if (!ft) {
err = TOX_ERR_FILE_SEND_TOO_MANY; err = TOX_ERR_FILE_SEND_TOO_MANY;
@ -367,29 +373,62 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
on_send_error: on_send_error:
fclose(file_to_send);
switch (err) { switch (err) {
case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND: case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND: {
errmsg = "File transfer failed: Invalid friend."; errmsg = "File transfer failed: Invalid friend.";
break; break;
}
case TOX_ERR_FILE_SEND_FRIEND_NOT_CONNECTED: case TOX_ERR_FILE_SEND_FRIEND_NOT_CONNECTED: {
errmsg = "File transfer failed: Friend is offline."; int queue_idx = file_send_queue_add(self->num, path, path_len);
break;
case TOX_ERR_FILE_SEND_NAME_TOO_LONG: char msg[MAX_STR_SIZE];
switch (queue_idx) {
case -1: {
snprintf(msg, sizeof(msg), "Invalid file name: path is null or length is zero.");
break;
}
case -2: {
snprintf(msg, sizeof(msg), "File name is too long.");
break;
}
case -3: {
snprintf(msg, sizeof(msg), "File send queue is full.");
break;
}
default: {
snprintf(msg, sizeof(msg), "File transfer queued. Type \"/cancel out %d\" to cancel.", queue_idx);
break;
}
}
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", msg);
return;
}
case TOX_ERR_FILE_SEND_NAME_TOO_LONG: {
errmsg = "File transfer failed: Filename is too long."; errmsg = "File transfer failed: Filename is too long.";
break; break;
}
case TOX_ERR_FILE_SEND_TOO_MANY: case TOX_ERR_FILE_SEND_TOO_MANY: {
errmsg = "File transfer failed: Too many concurrent file transfers."; errmsg = "File transfer failed: Too many concurrent file transfers.";
break; break;
}
default: default: {
errmsg = "File transfer failed."; errmsg = "File transfer failed.";
break; break;
}
} }
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg);
tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL); tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
fclose(file_to_send);
} }

View File

@ -25,6 +25,7 @@
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include "execute.h"
#include "file_transfers.h" #include "file_transfers.h"
#include "friendlist.h" #include "friendlist.h"
#include "line_info.h" #include "line_info.h"
@ -98,7 +99,7 @@ void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t l
free(full_line); free(full_line);
} }
static void refresh_progress_helper(ToxWindow *self, struct FileTransfer *ft) static void refresh_progress_helper(ToxWindow *self, FileTransfer *ft)
{ {
if (ft->state == FILE_TRANSFER_INACTIVE) { if (ft->state == FILE_TRANSFER_INACTIVE) {
return; return;
@ -126,8 +127,8 @@ bool refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnumber)
bool active = false; bool active = false;
for (size_t i = 0; i < MAX_FILES; ++i) { for (size_t i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft_r = &Friends.list[friendnumber].file_receiver[i]; FileTransfer *ft_r = &Friends.list[friendnumber].file_receiver[i];
struct FileTransfer *ft_s = &Friends.list[friendnumber].file_sender[i]; FileTransfer *ft_s = &Friends.list[friendnumber].file_sender[i];
refresh_progress_helper(self, ft_r); refresh_progress_helper(self, ft_r);
refresh_progress_helper(self, ft_s); refresh_progress_helper(self, ft_s);
@ -140,9 +141,9 @@ bool refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnumber)
return active; return active;
} }
static void clear_file_transfer(struct FileTransfer *ft) static void clear_file_transfer(FileTransfer *ft)
{ {
*ft = (struct FileTransfer) { *ft = (FileTransfer) {
0 0
}; };
} }
@ -150,16 +151,16 @@ static void clear_file_transfer(struct FileTransfer *ft)
/* Returns a pointer to friendnumber's FileTransfer struct associated with filenumber. /* Returns a pointer to friendnumber's FileTransfer struct associated with filenumber.
* Returns NULL if filenumber is invalid. * Returns NULL if filenumber is invalid.
*/ */
struct FileTransfer *get_file_transfer_struct(uint32_t friendnumber, uint32_t filenumber) FileTransfer *get_file_transfer_struct(uint32_t friendnumber, uint32_t filenumber)
{ {
for (size_t i = 0; i < MAX_FILES; ++i) { for (size_t i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft_send = &Friends.list[friendnumber].file_sender[i]; FileTransfer *ft_send = &Friends.list[friendnumber].file_sender[i];
if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenumber == filenumber) { if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenumber == filenumber) {
return ft_send; return ft_send;
} }
struct FileTransfer *ft_recv = &Friends.list[friendnumber].file_receiver[i]; FileTransfer *ft_recv = &Friends.list[friendnumber].file_receiver[i];
if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenumber == filenumber) { if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenumber == filenumber) {
return ft_recv; return ft_recv;
@ -172,7 +173,7 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnumber, uint32_t fi
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified. /* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
* Returns NULL on failure. * Returns NULL on failure.
*/ */
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnumber, uint32_t index, FileTransfer *get_file_transfer_struct_index(uint32_t friendnumber, uint32_t index,
FILE_TRANSFER_DIRECTION direction) FILE_TRANSFER_DIRECTION direction)
{ {
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) { if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) {
@ -180,9 +181,9 @@ struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnumber, uint3
} }
for (size_t i = 0; i < MAX_FILES; ++i) { for (size_t i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ? FileTransfer *ft = direction == FILE_TRANSFER_SEND ?
&Friends.list[friendnumber].file_sender[i] : &Friends.list[friendnumber].file_sender[i] :
&Friends.list[friendnumber].file_receiver[i]; &Friends.list[friendnumber].file_receiver[i];
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) { if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) {
return ft; return ft;
@ -195,10 +196,10 @@ struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnumber, uint3
/* Returns a pointer to an unused file sender. /* Returns a pointer to an unused file sender.
* Returns NULL if all file senders are in use. * Returns NULL if all file senders are in use.
*/ */
static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber, uint8_t type) static FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber, uint8_t type)
{ {
for (size_t i = 0; i < MAX_FILES; ++i) { for (size_t i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft = &Friends.list[friendnumber].file_sender[i]; FileTransfer *ft = &Friends.list[friendnumber].file_sender[i];
if (ft->state == FILE_TRANSFER_INACTIVE) { if (ft->state == FILE_TRANSFER_INACTIVE) {
clear_file_transfer(ft); clear_file_transfer(ft);
@ -218,11 +219,11 @@ static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnu
/* Returns a pointer to an unused file receiver. /* Returns a pointer to an unused file receiver.
* Returns NULL if all file receivers are in use. * Returns NULL if all file receivers are in use.
*/ */
static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber, static FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber,
uint8_t type) uint8_t type)
{ {
for (size_t i = 0; i < MAX_FILES; ++i) { for (size_t i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft = &Friends.list[friendnumber].file_receiver[i]; FileTransfer *ft = &Friends.list[friendnumber].file_receiver[i];
if (ft->state == FILE_TRANSFER_INACTIVE) { if (ft->state == FILE_TRANSFER_INACTIVE) {
clear_file_transfer(ft); clear_file_transfer(ft);
@ -242,8 +243,8 @@ static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friend
/* Initializes an unused file transfer and returns its pointer. /* Initializes an unused file transfer and returns its pointer.
* Returns NULL on failure. * Returns NULL on failure.
*/ */
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber, FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber,
FILE_TRANSFER_DIRECTION direction, uint8_t type) FILE_TRANSFER_DIRECTION direction, uint8_t type)
{ {
if (direction == FILE_TRANSFER_RECV) { if (direction == FILE_TRANSFER_RECV) {
return new_file_receiver(window, friendnumber, filenumber, type); return new_file_receiver(window, friendnumber, filenumber, type);
@ -256,13 +257,83 @@ struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnumber,
return NULL; return NULL;
} }
int file_send_queue_add(uint32_t friendnumber, const char *file_path, size_t length)
{
if (length == 0 || file_path == NULL) {
return -1;
}
if (length > TOX_MAX_FILENAME_LENGTH) {
return -2;
}
for (size_t i = 0; i < MAX_FILES; ++i) {
PendingFileTransfer *pending_slot = &Friends.list[friendnumber].file_send_queue[i];
if (pending_slot->pending) {
continue;
}
pending_slot->pending = true;
memcpy(pending_slot->file_path, file_path, length);
pending_slot->file_path[length] = 0;
pending_slot->length = length;
return i;
}
return -3;
}
#define FILE_TRANSFER_SEND_CMD "/sendfile "
#define FILE_TRANSFER_SEND_LEN (sizeof(FILE_TRANSFER_SEND_CMD) - 1)
void file_send_queue_check(ToxWindow *self, Tox *m, uint32_t friendnumber)
{
for (size_t i = 0; i < MAX_FILES; ++i) {
PendingFileTransfer *pending_slot = &Friends.list[friendnumber].file_send_queue[i];
if (!pending_slot->pending) {
continue;
}
char command[TOX_MAX_FILENAME_LENGTH + FILE_TRANSFER_SEND_LEN + 1];
snprintf(command, sizeof(command), "%s%s", FILE_TRANSFER_SEND_CMD, pending_slot->file_path);
execute(self->window, self, m, command, CHAT_COMMAND_MODE);
*pending_slot = (PendingFileTransfer) {
0,
};
}
}
int file_send_queue_remove(uint32_t friendnumber, size_t index)
{
if (index >= MAX_FILES) {
return -1;
}
PendingFileTransfer *pending_slot = &Friends.list[friendnumber].file_send_queue[index];
if (!pending_slot->pending) {
return -1;
}
*pending_slot = (PendingFileTransfer) {
0,
};
return 0;
}
/* Closes file transfer ft. /* Closes file transfer ft.
* *
* Set CTRL to -1 if we don't want to send a control signal. * Set CTRL to -1 if we don't want to send a control signal.
* Set message or self to NULL if we don't want to display a message. * Set message or self to NULL if we don't want to display a message.
*/ */
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message, void close_file_transfer(ToxWindow *self, Tox *m, FileTransfer *ft, int CTRL, const char *message,
Notification sound_type) Notification sound_type)
{ {
if (!ft) { if (!ft) {
@ -298,7 +369,7 @@ void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int C
void kill_avatar_file_transfers_friend(Tox *m, uint32_t friendnumber) void kill_avatar_file_transfers_friend(Tox *m, uint32_t friendnumber)
{ {
for (size_t i = 0; i < MAX_FILES; ++i) { for (size_t i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft = &Friends.list[friendnumber].file_sender[i]; FileTransfer *ft = &Friends.list[friendnumber].file_sender[i];
if (ft->file_type == TOX_FILE_KIND_AVATAR) { if (ft->file_type == TOX_FILE_KIND_AVATAR) {
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent); close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
@ -312,6 +383,7 @@ void kill_all_file_transfers_friend(Tox *m, uint32_t friendnumber)
for (size_t i = 0; i < MAX_FILES; ++i) { for (size_t i = 0; i < MAX_FILES; ++i) {
close_file_transfer(NULL, m, &Friends.list[friendnumber].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent); close_file_transfer(NULL, m, &Friends.list[friendnumber].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
close_file_transfer(NULL, m, &Friends.list[friendnumber].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent); close_file_transfer(NULL, m, &Friends.list[friendnumber].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
file_send_queue_remove(friendnumber, i);
} }
} }

View File

@ -29,9 +29,9 @@
#include "toxic.h" #include "toxic.h"
#include "windows.h" #include "windows.h"
#define KiB 1024 #define KiB (uint32_t) 1024
#define MiB 1048576 /* 1024^2 */ #define MiB (uint32_t) (1024 << 10) /* 1024^2 */
#define GiB 1073741824 /* 1024^3 */ #define GiB (uint32_t) (1024 << 20) /* 1024^3 */
#define MAX_FILES 32 #define MAX_FILES 32
@ -47,7 +47,7 @@ typedef enum FILE_TRANSFER_DIRECTION {
FILE_TRANSFER_RECV FILE_TRANSFER_RECV
} FILE_TRANSFER_DIRECTION; } FILE_TRANSFER_DIRECTION;
struct FileTransfer { typedef struct FileTransfer {
ToxWindow *window; ToxWindow *window;
FILE *file; FILE *file;
FILE_TRANSFER_STATE state; FILE_TRANSFER_STATE state;
@ -63,7 +63,15 @@ struct FileTransfer {
time_t last_line_progress; /* The last time we updated the progress bar */ time_t last_line_progress; /* The last time we updated the progress bar */
uint32_t line_id; uint32_t line_id;
uint8_t file_id[TOX_FILE_ID_LENGTH]; uint8_t file_id[TOX_FILE_ID_LENGTH];
}; } FileTransfer;
typedef struct PendingFileTransfer {
char file_path[TOX_MAX_FILENAME_LENGTH + 1];
size_t length;
uint32_t friendnumber;
bool pending;
} PendingFileTransfer;
/* creates initial progress line that will be updated during file transfer. /* creates initial progress line that will be updated during file transfer.
progline must be at lesat MAX_STR_SIZE bytes */ progline must be at lesat MAX_STR_SIZE bytes */
@ -96,6 +104,31 @@ struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnumber, uint3
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber, struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber,
FILE_TRANSFER_DIRECTION direction, uint8_t type); FILE_TRANSFER_DIRECTION direction, uint8_t type);
/* Adds a file designated by `file_path` of length `length` to the file transfer queue.
*
* Items in this queue will be automatically sent to the contact designated by `friendnumber`
* as soon as they appear online. The item will then be removed from the queue whether
* or not the transfer successfully initiates.
*
* If the ToxWindow associated with this friend is closed, all queued items will be
* discarded.
*
* Return the queue index on success.
* Return -1 if the length is invalid.
* Return -2 if the send queue is full.
*/
int file_send_queue_add(uint32_t friendnumber, const char *file_path, size_t length);
/* Initiates all file transfers from the file send queue for friend designated by `friendnumber`. */
void file_send_queue_check(ToxWindow *self, Tox *m, uint32_t friendnumber);
/* Removes the `index`-th item from the file send queue for `friendnumber`.
*
* Return 0 if a pending transfer was successfully removed
* Return -1 if index does not designate a pending file transfer.
*/
int file_send_queue_remove(uint32_t friendnumber, size_t index);
/* Closes file transfer ft. /* Closes file transfer ft.
* *
* Set CTRL to -1 if we don't want to send a control signal. * Set CTRL to -1 if we don't want to send a control signal.

View File

@ -1107,14 +1107,14 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
/* Determine which portion of friendlist to draw based on current position */ /* Determine which portion of friendlist to draw based on current position */
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
int page = Friends.num_selected / (y2 - FLIST_OFST); const int page = Friends.num_selected / (y2 - FLIST_OFST);
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
int start = (y2 - FLIST_OFST) * page; const int start = (y2 - FLIST_OFST) * page;
int end = y2 - FLIST_OFST + start; const int end = y2 - FLIST_OFST + start;
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
size_t num_friends = Friends.num_friends; const size_t num_friends = Friends.num_friends;
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
int i; int i;

View File

@ -79,8 +79,9 @@ typedef struct {
struct GameInvite game_invite; struct GameInvite game_invite;
#endif #endif
struct FileTransfer file_receiver[MAX_FILES]; FileTransfer file_receiver[MAX_FILES];
struct FileTransfer file_sender[MAX_FILES]; FileTransfer file_sender[MAX_FILES];
PendingFileTransfer file_send_queue[MAX_FILES];
} ToxicFriend; } ToxicFriend;
typedef struct { typedef struct {

View File

@ -244,7 +244,7 @@ void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, u
{ {
UNUSED_VAR(userdata); UNUSED_VAR(userdata);
struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber); FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber);
if (!ft) { if (!ft) {
return; return;
@ -267,7 +267,7 @@ void on_file_recv_chunk(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint
{ {
UNUSED_VAR(userdata); UNUSED_VAR(userdata);
struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber); FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber);
if (!ft) { if (!ft) {
return; return;
@ -285,7 +285,7 @@ void on_file_recv_control(Tox *m, uint32_t friendnumber, uint32_t filenumber, To
{ {
UNUSED_VAR(userdata); UNUSED_VAR(userdata);
struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber); FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber);
if (!ft) { if (!ft) {
return; return;