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

Refactor and clean up file transfers

(No longer rely on undefined core filenumber property for indexing)
This commit is contained in:
Jfreegman 2015-03-29 18:33:51 -04:00
parent bf09b3b6c4
commit 522aabd4e4
No known key found for this signature in database
GPG Key ID: 3627F3144076AE63
12 changed files with 322 additions and 266 deletions

View File

@ -189,7 +189,7 @@ static void chat_onMessage(ToxWindow *self, Tox *m, uint32_t num, TOX_MESSAGE_TY
} }
static void chat_resume_file_transfers(Tox *m, uint32_t fnum); static void chat_resume_file_transfers(Tox *m, uint32_t fnum);
static void chat_stop_file_senders(uint32_t friendnum); static void chat_stop_file_senders(Tox *m, uint32_t friendnum);
static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_CONNECTION connection_status) static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_CONNECTION connection_status)
{ {
@ -222,7 +222,7 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C
if (self->chatwin->self_is_typing) if (self->chatwin->self_is_typing)
set_self_typingstatus(self, m, 0); set_self_typingstatus(self, m, 0);
chat_stop_file_senders(num); chat_stop_file_senders(m, num);
msg = "has gone offline"; msg = "has gone offline";
line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg); line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg);
@ -278,164 +278,146 @@ static void chat_onReadReceipt(ToxWindow *self, Tox *m, uint32_t num, uint32_t r
} }
/* Stops active file senders for this friend. Call when a friend goes offline */ /* Stops active file senders for this friend. Call when a friend goes offline */
static void chat_stop_file_senders(uint32_t friendnum) static void chat_stop_file_senders(Tox *m, uint32_t friendnum)
{ {
// size_t i; // TODO: core purges file transfers when a friend goes offline. Ideally we want to repair/resume
kill_all_file_transfers_friend(m, friendnum);
// for (i = 0; i < MAX_FILES; ++i) {
// if (Friends.list[friendnum].file_sender[i].active)
// Friends.list[friendnum].file_sender[i].noconnection = true;
// }
} }
/* Tries to resume broken file transfers. Call when a friend comes online */ /* Tries to resume broken file transfers. Call when a friend comes online */
static void chat_resume_file_transfers(Tox *m, uint32_t fnum) static void chat_resume_file_transfers(Tox *m, uint32_t fnum)
{ {
// size_t i; // TODO
// for (i = 0; i < MAX_FILES; ++i) {
// if (Friends.list[fnum].file_receiver[i].active) {
// uint8_t bytes_recv[sizeof(uint64_t)];
// memcpy(bytes_recv, &Friends.list[fnum].file_receiver[i].bytes_recv, sizeof(uint64_t));
// net_to_host(bytes_recv, sizeof(uint64_t));
// uint32_t filenum = Friends.list[fnum].file_receiver[i].filenum;
// tox_file_send_control(m, fnum, 1, filenum, TOX_FILECONTROL_RESUME_BROKEN, bytes_recv, sizeof(uint64_t));
// }
// }
} }
static void chat_onFileChunkRequest(ToxWindow *self, Tox *m, uint32_t num, uint32_t filenum, uint64_t position, static void chat_onFileChunkRequest(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, uint64_t position,
size_t length) size_t length)
{ {
if (num != self->num) if (friendnum != self->num)
return; return;
uint32_t idx = get_file_transfer_index(filenum); struct FileTransfer *ft = get_file_transfer_struct(friendnum, filenum);
if (idx >= MAX_FILES) if (ft == NULL)
return;
if (ft->state != FILE_TRANSFER_STARTED)
return; return;
char msg[MAX_STR_SIZE]; char msg[MAX_STR_SIZE];
const char *file_name = Friends.list[num].file_sender[idx].file_name;
FILE *fp = Friends.list[num].file_sender[idx].file;
if (fp == NULL) {
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Null file pointer.", file_name);
close_file_transfer(self, m, filenum, num, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
return;
}
if (length == 0) { if (length == 0) {
snprintf(msg, sizeof(msg), "File '%s' successfully sent.", file_name); snprintf(msg, sizeof(msg), "File '%s' successfully sent.", ft->file_name);
print_progress_bar(self, Friends.list[num].file_sender[idx].bps, 100.0, Friends.list[num].file_sender[idx].line_id); print_progress_bar(self, ft->bps, 100.0, ft->line_id);
close_file_transfer(self, m, filenum, num, -1, msg, transfer_completed); close_file_transfer(self, m, ft, -1, msg, transfer_completed);
return; return;
} }
if (Friends.list[num].file_sender[idx].position != position) { if (ft->file == NULL) {
if (fseek(fp, position, SEEK_SET) == -1) { snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Null file pointer.", ft->file_name);
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Seek fail.", file_name); close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
close_file_transfer(self, m, filenum, num, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
return; return;
} }
Friends.list[num].file_sender[idx].position = position; if (ft->position != position) {
if (fseek(ft->file, position, SEEK_SET) == -1) {
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Seek fail.", ft->file_name);
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
return;
}
ft->position = position;
} }
uint8_t send_data[length]; uint8_t send_data[length];
size_t send_length = fread(send_data, 1, sizeof(send_data), fp); size_t send_length = fread(send_data, 1, sizeof(send_data), ft->file);
if (send_length != length) { if (send_length != length) {
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Read fail.", file_name); snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Read fail.", ft->file_name);
close_file_transfer(self, m, filenum, num, TOX_FILE_CONTROL_CANCEL, msg, notif_error); close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
return; return;
} }
TOX_ERR_FILE_SEND_CHUNK err; TOX_ERR_FILE_SEND_CHUNK err;
tox_file_send_chunk(m, num, filenum, position, send_data, send_length, &err); tox_file_send_chunk(m, friendnum, filenum, position, send_data, send_length, &err);
if (err != TOX_ERR_FILE_SEND_CHUNK_OK) if (err != TOX_ERR_FILE_SEND_CHUNK_OK)
fprintf(stderr, "tox_file_send_chunk failed (error %d)\n", err); fprintf(stderr, "tox_file_send_chunk failed (error %d)\n", err);
Friends.list[num].file_sender[idx].position += send_length; ft->position += send_length;
Friends.list[num].file_sender[idx].bps += send_length; ft->bps += send_length;
} }
static void chat_onFileRecvChunk(ToxWindow *self, Tox *m, uint32_t num, uint32_t filenum, uint64_t position, static void chat_onFileRecvChunk(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, uint64_t position,
const char *data, size_t length) const char *data, size_t length)
{ {
if (num != self->num) if (friendnum != self->num)
return; return;
uint32_t idx = get_file_transfer_index(filenum); struct FileTransfer *ft = get_file_transfer_struct(friendnum, filenum);
if (idx >= MAX_FILES) if (ft == NULL)
return;
if (ft->state != FILE_TRANSFER_STARTED)
return; return;
char msg[MAX_STR_SIZE]; char msg[MAX_STR_SIZE];
char file_name[MAX_STR_SIZE];
get_file_name(file_name, sizeof(file_name), Friends.list[num].file_receiver[idx].file_path);
if (length == 0) { if (length == 0) {
snprintf(msg, sizeof(msg), "File '%s' successfully received.", file_name); snprintf(msg, sizeof(msg), "File '%s' successfully received.", ft->file_name);
print_progress_bar(self, Friends.list[num].file_receiver[idx].bps, 100.0, Friends.list[num].file_receiver[idx].line_id); print_progress_bar(self, ft->bps, 100.0, ft->line_id);
close_file_transfer(self, m, filenum, num, -1, msg, transfer_completed); close_file_transfer(self, m, ft, -1, msg, transfer_completed);
return; return;
} }
FILE *fp = Friends.list[num].file_receiver[idx].file; if (ft->file == NULL) {
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Invalid file pointer.", ft->file_name);
if (fp == NULL) { close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Invalid file pointer.", file_name);
close_file_transfer(self, m, filenum, num, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
return; return;
} }
if (fwrite(data, length, 1, fp) != 1) { if (fwrite(data, length, 1, ft->file) != 1) {
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Write fail.", file_name); snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Write fail.", ft->file_name);
close_file_transfer(self, m, filenum, num, TOX_FILE_CONTROL_CANCEL, msg, notif_error); close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
return; return;
} }
Friends.list[num].file_receiver[idx].bps += length; ft->bps += length;
Friends.list[num].file_receiver[idx].position += length; ft->position += length;
} }
static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t num, uint32_t filenum, TOX_FILE_CONTROL control) static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, TOX_FILE_CONTROL control)
{ {
if (self->num != num) if (self->num != friendnum)
return; return;
uint32_t idx = get_file_transfer_index(filenum); struct FileTransfer *ft = get_file_transfer_struct(friendnum, filenum);
bool sending = filenum_is_sending(filenum);
if (idx >= MAX_FILES) if (ft == NULL)
return; return;
char file_name[MAX_STR_SIZE];
char msg[MAX_STR_SIZE]; char msg[MAX_STR_SIZE];
if (sending && Friends.list[num].file_sender[idx].active) {
snprintf(file_name, sizeof(file_name), "%s", Friends.list[num].file_sender[idx].file_name);
} else if (!sending && Friends.list[num].file_receiver[idx].active) {
get_file_name(file_name, sizeof(file_name), Friends.list[num].file_receiver[idx].file_path);
} else {
return;
}
switch (control) { switch (control) {
case TOX_FILE_CONTROL_RESUME: case TOX_FILE_CONTROL_RESUME:
/* transfer is accepted */ /* transfer is accepted */
if (sending && !Friends.list[num].file_sender[idx].started) { if (ft->state == FILE_TRANSFER_PENDING) {
Friends.list[num].file_sender[idx].started = true; ft->state = FILE_TRANSFER_STARTED;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer [%d] for '%s' accepted.", line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer [%d] for '%s' accepted.",
idx, file_name); ft->index, ft->file_name);
char progline[MAX_STR_SIZE]; char progline[MAX_STR_SIZE];
prep_prog_line(progline); prep_prog_line(progline);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
Friends.list[num].file_sender[idx].line_id = self->chatwin->hst->line_end->id + 2;
sound_notify(self, silent, NT_NOFOCUS | NT_BEEP | NT_WNDALERT_2, NULL); sound_notify(self, silent, NT_NOFOCUS | NT_BEEP | NT_WNDALERT_2, NULL);
ft->line_id = self->chatwin->hst->line_end->id + 2;
break;
}
/* transfer is resumed */
if (ft->state == FILE_TRANSFER_PAUSED) {
ft->state = FILE_TRANSFER_STARTED;
} }
break; break;
@ -444,29 +426,29 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t num, uint32_t f
break; break;
case TOX_FILE_CONTROL_CANCEL: case TOX_FILE_CONTROL_CANCEL:
snprintf(msg, sizeof(msg), "File transfer for '%s' was aborted.", file_name); snprintf(msg, sizeof(msg), "File transfer for '%s' was aborted.", ft->file_name);
close_file_transfer(self, m, filenum, num, -1, msg, notif_error); close_file_transfer(self, m, ft, -1, msg, notif_error);
break; break;
} }
} }
static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_t filenum, uint32_t kind, static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, uint64_t file_size,
uint64_t file_size, const char *filename, size_t name_length) const char *filename, size_t name_length)
{ {
if (self->num != num) if (self->num != friendnum)
return; return;
uint32_t idx = get_file_transfer_index(filenum); struct FileTransfer *ft = get_new_file_receiver(friendnum);
if (idx >= MAX_FILES) { if (!ft) {
tox_file_control(m, friendnum, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Too many concurrent file transfers."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Too many concurrent file transfers.");
return; return;
} }
char sizestr[32]; char sizestr[32];
bytes_convert_str(sizestr, sizeof(sizestr), file_size); bytes_convert_str(sizestr, sizeof(sizestr), file_size);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer request for '%s' (%s)", line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer request for '%s' (%s)", filename, sizestr);
filename, sizestr);
char file_path[MAX_STR_SIZE]; char file_path[MAX_STR_SIZE];
size_t path_len = name_length; size_t path_len = name_length;
@ -479,8 +461,9 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_t file
snprintf(file_path, sizeof(file_path), "%s", filename); snprintf(file_path, sizeof(file_path), "%s", filename);
} }
if (path_len >= sizeof(Friends.list[num].file_receiver[idx].file_path)) { if (path_len >= sizeof(ft->file_path) || name_length >= sizeof(ft->file_name)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer faield: File name too long."); tox_file_control(m, friendnum, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer faield: File path too long.");
return; return;
} }
@ -505,16 +488,20 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_t file
file_path[path_len + d_len] = '\0'; file_path[path_len + d_len] = '\0';
if (count > 999) { if (count > 999) {
tox_file_control(m, friendnum, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: invalid file path."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: invalid file path.");
return; return;
} }
} }
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type '/savefile %d' to accept the file transfer.", idx); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type '/savefile %d' to accept the file transfer.", ft->index);
Friends.list[num].file_receiver[idx].pending = true; ft->state = FILE_TRANSFER_PENDING;
Friends.list[num].file_receiver[idx].file_size = file_size; ft->direction = FILE_TRANSFER_RECV;
strcpy(Friends.list[num].file_receiver[idx].file_path, file_path); ft->file_size = file_size;
ft->filenum = filenum;
snprintf(ft->file_path, sizeof(ft->file_path), "%s", file_path);
snprintf(ft->file_name, sizeof(ft->file_name), "%s", filename);
if (self->active_box != -1) if (self->active_box != -1)
box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS, self->active_box, box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS, self->active_box,

View File

@ -52,31 +52,30 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
return; return;
} }
if (strcasecmp(inoutstr, "in") == 0) { /* cancel an incoming file transfer */ struct FileTransfer *ft = NULL;
if (!Friends.list[self->num].file_receiver[idx].active) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
return;
}
const char *file_path = Friends.list[self->num].file_receiver[idx].file_path; /* cancel an incoming file transfer */
char file_name[MAX_STR_SIZE]; if (strcasecmp(inoutstr, "in") == 0) {
get_file_name(file_name, sizeof(file_name), file_path); ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
snprintf(msg, sizeof(msg), "File transfer for '%s' canceled.", file_name); } else if (strcasecmp(inoutstr, "out") == 0) {
close_file_transfer(self, m, get_file_receiver_filenum(idx), self->num, TOX_FILE_CONTROL_CANCEL, msg, silent); ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND);
return;
} else if (strcasecmp(inoutstr, "out") == 0) { /* cancel an outgoing file transfer */
if (!Friends.list[self->num].file_sender[idx].active) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
return;
}
snprintf(msg, sizeof(msg), "File transfer for '%s' canceled.", Friends.list[self->num].file_sender[idx].file_name);
close_file_transfer(self, m, idx, self->num, TOX_FILE_CONTROL_CANCEL, msg, silent);
return;
} else { } else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'.");
return; return;
} }
if (!ft) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
return;
}
if (ft->state == FILE_TRANSFER_INACTIVE) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
return;
}
snprintf(msg, sizeof(msg), "File transfer for '%s' aborted.", ft->file_name);
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent);
} }
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -154,36 +153,39 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
return; return;
} }
uint32_t filenum = get_file_receiver_filenum(idx); struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
if (!Friends.list[self->num].file_receiver[idx].pending) { if (!ft) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
return; return;
} }
const char *file_path = Friends.list[self->num].file_receiver[idx].file_path; if (ft->state != FILE_TRANSFER_PENDING) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
return;
}
if ((ft->file = fopen(ft->file_path, "a")) == NULL) {
const char *msg = "File transfer failed: Invalid file path.";
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
return;
}
TOX_ERR_FILE_CONTROL err; TOX_ERR_FILE_CONTROL err;
tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_RESUME, &err); tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &err);
if (err != TOX_ERR_FILE_CONTROL_OK) if (err != TOX_ERR_FILE_CONTROL_OK)
goto on_recv_error; goto on_recv_error;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, file_path); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, ft->file_path);
/* prep progress bar line */ /* prep progress bar line */
char progline[MAX_STR_SIZE]; char progline[MAX_STR_SIZE];
prep_prog_line(progline); prep_prog_line(progline);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
Friends.list[self->num].file_receiver[idx].line_id = self->chatwin->hst->line_end->id + 2;
Friends.list[self->num].file_receiver[idx].pending = false;
if ((Friends.list[self->num].file_receiver[idx].file = fopen(file_path, "a")) == NULL) { ft->line_id = self->chatwin->hst->line_end->id + 2;
tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL); ft->state = FILE_TRANSFER_STARTED;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid file path.");
} else {
Friends.list[self->num].file_receiver[idx].active = true;
}
return; return;
@ -252,33 +254,32 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
} }
char file_name[TOX_MAX_FILENAME_LENGTH]; char file_name[TOX_MAX_FILENAME_LENGTH];
get_file_name(file_name, sizeof(file_name), path); size_t namelen = get_file_name(file_name, sizeof(file_name), path);
size_t namelen = strlen(file_name);
TOX_ERR_FILE_SEND err; TOX_ERR_FILE_SEND err;
uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, NULL,
NULL, (uint8_t *) file_name, namelen, &err); (uint8_t *) file_name, namelen, &err);
if (err != TOX_ERR_FILE_SEND_OK) if (err != TOX_ERR_FILE_SEND_OK)
goto on_send_error; goto on_send_error;
uint32_t idx = get_file_transfer_index(filenum); struct FileTransfer *ft = get_new_file_sender(self->num);
if (idx >= MAX_FILES) { if (!ft) {
errmsg = "File transfer failed: Too many concurrent file transfers"; err = TOX_ERR_FILE_SEND_TOO_MANY;
goto on_send_error; goto on_send_error;
} }
memcpy(Friends.list[self->num].file_sender[idx].file_name, file_name, namelen + 1); memcpy(ft->file_name, file_name, namelen + 1);
Friends.list[self->num].file_sender[idx].active = true; ft->state = FILE_TRANSFER_PENDING;
Friends.list[self->num].file_sender[idx].started = false; ft->file = file_to_send;
Friends.list[self->num].file_sender[idx].file = file_to_send; ft->file_size = filesize;
Friends.list[self->num].file_sender[idx].timestamp = get_unix_time(); ft->filenum = filenum;
Friends.list[self->num].file_sender[idx].file_size = filesize; ft->direction = FILE_TRANSFER_SEND;
char sizestr[32]; char sizestr[32];
bytes_convert_str(sizestr, sizeof(sizestr), filesize); bytes_convert_str(sizestr, sizeof(sizestr), filesize);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", idx, file_name, sizestr); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", filenum, file_name, sizestr);
return; return;
@ -301,7 +302,7 @@ on_send_error:
break; break;
default: default:
errmsg = "File transfer failed"; errmsg = "File transfer failed.";
break; break;
} }

View File

@ -54,6 +54,9 @@ void prep_prog_line(char *progline)
if friendnum is -1 we're sending the file, otherwise we're receiving. */ if friendnum is -1 we're sending the file, otherwise we're receiving. */
void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t line_id) void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t line_id)
{ {
if (bps < 0 || pct_done < 0 || pct_done > 100)
return;
char msg[MAX_STR_SIZE]; char msg[MAX_STR_SIZE];
bytes_convert_str(msg, sizeof(msg), bps); bytes_convert_str(msg, sizeof(msg), bps);
strcat(msg, "/s ["); strcat(msg, "/s [");
@ -77,27 +80,21 @@ void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t l
line_info_set(self, line_id, msg); line_info_set(self, line_id, msg);
} }
/* Filenumbers >= this number are receiving, otherwise sending. static void refresh_progress_helper(ToxWindow *self, struct FileTransfer *ft, uint64_t curtime)
* Warning: This behaviour is not defined by the Tox API and is subject to change at any time.
*/
#define FILE_NUMBER_MAGIC_NUM (1 << 16)
/* Returns filenum's file transfer array index */
uint32_t get_file_transfer_index(uint32_t filenum)
{ {
return filenum >= FILE_NUMBER_MAGIC_NUM ? (filenum >> 16) - 1 : filenum; if (ft->state == FILE_TRANSFER_INACTIVE)
} return;
/* Returns the filenumber of a file receiver's index */ /* Timeout must be set to 1 second to show correct bytes per second */
uint32_t get_file_receiver_filenum(uint32_t idx) if (!timed_out(ft->last_progress, curtime, 1))
{ return;
return (idx + 1) << 16;
}
/* Return true if filenum is associated with a file receiver, false if file sender */ double remain = ft->file_size - ft->position;
bool filenum_is_sending(uint32_t filenum) double pct_done = remain > 0 ? (1 - (remain / ft->file_size)) * 100 : 100;
{ print_progress_bar(self, ft->bps, pct_done, ft->line_id);
return filenum < FILE_NUMBER_MAGIC_NUM;
ft->bps = 0;
ft->last_progress = curtime;
} }
/* refreshes active file receiver status bars for friendnum */ /* refreshes active file receiver status bars for friendnum */
@ -107,67 +104,114 @@ void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum)
size_t i; size_t i;
for (i = 0; i < MAX_FILES; ++i) { for (i = 0; i < MAX_FILES; ++i) {
if (Friends.list[friendnum].file_receiver[i].active) { refresh_progress_helper(self, &Friends.list[friendnum].file_receiver[i], curtime);
if (timed_out(Friends.list[friendnum].file_receiver[i].last_progress, curtime, 1)) { refresh_progress_helper(self, &Friends.list[friendnum].file_sender[i], curtime);
uint64_t size = Friends.list[friendnum].file_receiver[i].file_size;
double remain = size - Friends.list[friendnum].file_receiver[i].position;
double pct_done = remain > 0 ? (1 - (remain / size)) * 100 : 100;
print_progress_bar(self, Friends.list[friendnum].file_receiver[i].bps, pct_done,
Friends.list[friendnum].file_receiver[i].line_id);
Friends.list[friendnum].file_receiver[i].bps = 0;
Friends.list[friendnum].file_receiver[i].last_progress = curtime;
} }
} }
if (Friends.list[friendnum].file_sender[i].active) { /* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
if (timed_out(Friends.list[friendnum].file_sender[i].last_progress, curtime, 1)) { * Returns NULL if filenum is invalid.
uint64_t size = Friends.list[friendnum].file_sender[i].file_size; */
double remain = size - Friends.list[friendnum].file_sender[i].position; struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum)
double pct_done = remain > 0 ? (1 - (remain / size)) * 100 : 100; {
size_t i;
print_progress_bar(self, Friends.list[friendnum].file_sender[i].bps, pct_done, for (i = 0; i < MAX_FILES; ++i) {
Friends.list[friendnum].file_sender[i].line_id); struct FileTransfer *ft_send = &Friends.list[friendnum].file_sender[i];
Friends.list[friendnum].file_sender[i].bps = 0; if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum)
Friends.list[friendnum].file_sender[i].last_progress = curtime; return ft_send;
struct FileTransfer *ft_recv = &Friends.list[friendnum].file_receiver[i];
if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum)
return ft_recv;
} }
return NULL;
} }
/* Returns a pointer to friendnum's file receiver associated with index with the direction specified.
* Returns NULL on failure.
*/
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
FILE_TRANSFER_DIRECTION direction)
{
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND)
return NULL;
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ?
&Friends.list[friendnum].file_sender[i] :
&Friends.list[friendnum].file_receiver[i];
if (ft->index == index)
return ft;
}
return NULL;
}
/* Returns a pointer to an unused file sender.
* Returns NULL if all file senders are in use.
*/
struct FileTransfer *get_new_file_sender(uint32_t friendnum)
{
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i];
if (ft->state == FILE_TRANSFER_INACTIVE) {
ft->index = i;
return ft;
} }
} }
/* Closes file transfer with filenum. return NULL;
}
/* Returns a pointer to an unused file receiver.
* Returns NULL if all file receivers are in use.
*/
struct FileTransfer *get_new_file_receiver(uint32_t friendnum)
{
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft = &Friends.list[friendnum].file_receiver[i];
if (ft->state == FILE_TRANSFER_INACTIVE) {
ft->index = i;
return ft;
}
}
return NULL;
}
/* 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, uint32_t filenum, uint32_t friendnum, int CTRL, void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
const char *message, Notification sound_type) Notification sound_type)
{ {
uint32_t idx = get_file_transfer_index(filenum); if (!ft)
bool sending = filenum_is_sending(filenum);
if (sending && Friends.list[friendnum].file_sender[idx].active) {
FILE *fp = Friends.list[friendnum].file_sender[idx].file;
if (fp)
fclose(fp);
memset(&Friends.list[friendnum].file_sender[idx], 0, sizeof(struct FileSender));
}
else if (!sending && Friends.list[friendnum].file_receiver[idx].active) {
FILE *fp = Friends.list[friendnum].file_receiver[idx].file;
if (fp)
fclose(fp);
memset(&Friends.list[friendnum].file_receiver[idx], 0, sizeof(struct FileReceiver));
}
else
return; return;
if (ft->state == FILE_TRANSFER_INACTIVE)
return;
if (ft->file)
fclose(ft->file);
memset(ft, 0, sizeof(struct FileTransfer));
if (CTRL >= 0) if (CTRL >= 0)
tox_file_control(m, friendnum, filenum, CTRL, NULL); tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL);
if (message && self) { if (message && self) {
if (self->active_box != -1) if (self->active_box != -1)
@ -185,10 +229,7 @@ void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum)
size_t i; size_t i;
for (i = 0; i < MAX_FILES; ++i) { for (i = 0; i < MAX_FILES; ++i) {
fprintf(stderr, "%lu\n", i); close_file_transfer(NULL, m, &Friends.list[friendnum].file_sender[i], -1, NULL, silent);
if (Friends.list[friendnum].file_sender[i].active) close_file_transfer(NULL, m, &Friends.list[friendnum].file_receiver[i], -1, NULL, silent);
close_file_transfer(NULL, m, i, friendnum, -1, NULL, silent);
if (Friends.list[friendnum].file_receiver[i].active)
close_file_transfer(NULL, m, get_file_receiver_filenum(i), friendnum, -1, NULL, silent);
} }
} }

View File

@ -33,31 +33,31 @@
#define MiB 1048576 /* 1024 ^ 2 */ #define MiB 1048576 /* 1024 ^ 2 */
#define GiB 1073741824 /* 1024 ^ 3 */ #define GiB 1073741824 /* 1024 ^ 3 */
#define FILE_PIECE_SIZE 2048
#define MAX_FILES 32 #define MAX_FILES 32
#define TIMEOUT_FILESENDER 120 #define TIMEOUT_FILESENDER 120
struct FileSender { typedef enum FILE_TRANSFER_STATE {
FILE *file; FILE_TRANSFER_INACTIVE,
char file_name[TOX_MAX_FILENAME_LENGTH]; FILE_TRANSFER_PENDING,
bool active; FILE_TRANSFER_STARTED,
bool noconnection; /* set when the connection has been interrupted */ FILE_TRANSFER_PAUSED
bool paused; /* set when transfer has been explicitly paused */ } FILE_TRANSFER_STATE;
bool started; /* set after TOX_FILECONTROL_ACCEPT received */
uint64_t timestamp; /* marks the last time data was successfully transfered */
double bps;
uint64_t file_size;
uint64_t last_progress; /* marks the last time the progress bar was refreshed */
uint64_t position;
uint32_t line_id;
};
struct FileReceiver { typedef enum FILE_TRANSFER_DIRECTION {
FILE_TRANSFER_SEND,
FILE_TRANSFER_RECV
} FILE_TRANSFER_DIRECTION;
struct FileTransfer {
FILE *file; FILE *file;
char file_path[PATH_MAX + 1]; FILE_TRANSFER_STATE state;
bool pending; FILE_TRANSFER_DIRECTION direction;
bool active; char file_name[TOX_MAX_FILENAME_LENGTH + 1];
char file_path[PATH_MAX + 1]; /* Not used by senders */
double bps; double bps;
uint32_t filenum;
uint32_t friendnum;
size_t index;
uint64_t file_size; uint64_t file_size;
uint64_t last_progress; uint64_t last_progress;
uint64_t position; uint64_t position;
@ -74,21 +74,35 @@ void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t l
/* refreshes active file receiver status bars for friendnum */ /* refreshes active file receiver status bars for friendnum */
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum); void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum);
/* Returns filenum's file transfer array index */ /* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
uint32_t get_file_transfer_index(uint32_t filenum); * Returns NULL if filenum is invalid.
*/
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum);
/* Returns the filenumber of a file receiver's index */
uint32_t get_file_receiver_filenum(uint32_t idx);
/* Return true if filenum is associated with a file receiver, false if file sender */ /* Returns a pointer to friendnum's file receiver associated with index with the direction specified.
bool filenum_is_sending(uint32_t filenum); * Returns NULL on failure.
*/
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
FILE_TRANSFER_DIRECTION direction);
/* Closes file transfer with filenum. /* Returns a pointer to an unused file sender.
* Returns NULL if all file senders are in use.
*/
struct FileTransfer *get_new_file_sender(uint32_t friendnum);
/* Returns a pointer to an unused file receiver.
* Returns NULL if all file receivers are in use.
*/
struct FileTransfer *get_new_file_receiver(uint32_t friendnum);
/* 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, uint32_t filenum, uint32_t friendnum, int CTRL, void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
const char *message, Notification sound_type); Notification sound_type);
/* Kills all active file transfers for friendnum */ /* Kills all active file transfers for friendnum */
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum); void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum);

View File

@ -460,7 +460,7 @@ static void friendlist_add_blocked(Tox *m, uint32_t fnum, uint32_t bnum)
} }
} }
static void friendlist_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_t filenum, uint32_t kind, static void friendlist_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_t filenum,
uint64_t file_size, const char *filename, size_t name_length) uint64_t file_size, const char *filename, size_t name_length)
{ {
if (num >= Friends.max_idx) if (num >= Friends.max_idx)

View File

@ -59,8 +59,8 @@ typedef struct {
struct LastOnline last_online; struct LastOnline last_online;
struct GroupChatInvite group_invite; struct GroupChatInvite group_invite;
struct FileReceiver file_receiver[MAX_FILES]; struct FileTransfer file_receiver[MAX_FILES];
struct FileSender file_sender[MAX_FILES]; struct FileTransfer file_sender[MAX_FILES];
} ToxicFriend; } ToxicFriend;
typedef struct { typedef struct {

View File

@ -155,11 +155,13 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
/* for type-specific formatting in print function */ /* for type-specific formatting in print function */
switch (type) { switch (type) {
case IN_ACTION: case IN_ACTION:
/* fallthrough */
case OUT_ACTION: case OUT_ACTION:
len += strlen(user_settings->line_normal) + 2; len += strlen(user_settings->line_normal) + 2;
break; break;
case IN_MSG: case IN_MSG:
/* fallthrough */
case OUT_MSG: case OUT_MSG:
len += strlen(user_settings->line_normal) + 3; len += strlen(user_settings->line_normal) + 3;
break; break;
@ -299,7 +301,9 @@ void line_info_print(ToxWindow *self)
switch (type) { switch (type) {
case OUT_MSG: case OUT_MSG:
/* fallthrough */
case OUT_MSG_READ: case OUT_MSG_READ:
/* fallthrough */
case IN_MSG: case IN_MSG:
wattron(win, COLOR_PAIR(BLUE)); wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s ", line->timestr); wprintw(win, "%s ", line->timestr);
@ -339,7 +343,9 @@ void line_info_print(ToxWindow *self)
break; break;
case OUT_ACTION_READ: case OUT_ACTION_READ:
/* fallthrough */
case OUT_ACTION: case OUT_ACTION:
/* fallthrough */
case IN_ACTION: case IN_ACTION:
wattron(win, COLOR_PAIR(BLUE)); wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s ", line->timestr); wprintw(win, "%s ", line->timestr);

View File

@ -222,17 +222,19 @@ void filter_str(char *str, size_t len)
} }
} }
/* gets base file name from path or original file name if no path is supplied */ /* gets base file name from path or original file name if no path is supplied.
void get_file_name(char *namebuf, int bufsize, const char *pathname) * Returns the file name length
*/
size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname)
{ {
int idx = strlen(pathname) - 1; int len = strlen(pathname) - 1;
char *path = strdup(pathname); char *path = strdup(pathname);
if (path == NULL) if (path == NULL)
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY); exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
while (idx >= 0 && pathname[idx] == '/') while (len >= 0 && pathname[len] == '/')
path[idx--] = '\0'; path[len--] = '\0';
char *finalname = strdup(path); char *finalname = strdup(path);
@ -249,6 +251,8 @@ void get_file_name(char *namebuf, int bufsize, const char *pathname)
snprintf(namebuf, bufsize, "%s", finalname); snprintf(namebuf, bufsize, "%s", finalname);
free(finalname); free(finalname);
free(path); free(path);
return strlen(namebuf);
} }
/* converts str to all lowercase */ /* converts str to all lowercase */
@ -363,7 +367,7 @@ bool file_exists(const char *path)
return stat(path, &s) == 0; return stat(path, &s) == 0;
} }
/* returns file size or 0 on error */ /* returns file size. If file doesn't exist returns 0. */
off_t file_size(const char *path) off_t file_size(const char *path)
{ {
struct stat st; struct stat st;

View File

@ -92,10 +92,10 @@ int qsort_strcasecmp_hlpr(const void *str1, const void *str2);
int valid_nick(const char *nick); int valid_nick(const char *nick);
/* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */ /* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */
void filter_str(char *str, size_t len);; void filter_str(char *str, size_t len);
/* gets base file name from path or original file name if no path is supplied */ /* gets base file name from path or original file name if no path is supplied */
void get_file_name(char *namebuf, int bufsize, const char *pathname); size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname);
/* converts str to all lowercase */ /* converts str to all lowercase */
void str_to_lower(char *str); void str_to_lower(char *str);
@ -125,7 +125,7 @@ void bytes_convert_str(char *buf, int size, uint64_t bytes);
/* checks if a file exists. Returns true or false */ /* checks if a file exists. Returns true or false */
bool file_exists(const char *path); bool file_exists(const char *path);
/* returns file size or 0 on error */ /* returns file size. If file doesn't exist returns 0. */
off_t file_size(const char *path); off_t file_size(const char *path);
/* compares the first size bytes of fp and signature. /* compares the first size bytes of fp and signature.

View File

@ -334,9 +334,6 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum , TOX_CONNECTION connection_status) static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum , TOX_CONNECTION connection_status)
{ {
if (friendnum < 0)
return;
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
char nick[TOX_MAX_NAME_LENGTH] = {0}; /* stop removing this initiation */ char nick[TOX_MAX_NAME_LENGTH] = {0}; /* stop removing this initiation */

View File

@ -248,11 +248,17 @@ void on_file_control(Tox *m, uint32_t friendnumber, uint32_t filenumber, TOX_FIL
void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t kind, uint64_t file_size, void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t kind, uint64_t file_size,
const uint8_t *filename, size_t filename_length, void *userdata) const uint8_t *filename, size_t filename_length, void *userdata)
{ {
/* We don't care about receiving avatars */
if (kind != TOX_FILE_KIND_DATA) {
tox_file_control(m, friendnumber, filenumber, TOX_FILE_CONTROL_CANCEL, NULL);
return;
}
size_t i; size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFileRecv != NULL) if (windows[i].onFileRecv != NULL)
windows[i].onFileRecv(&windows[i], m, friendnumber, filenumber, kind, file_size, (char *) filename, windows[i].onFileRecv(&windows[i], m, friendnumber, filenumber, file_size, (char *) filename,
filename_length); filename_length);
} }
} }

View File

@ -127,7 +127,7 @@ struct ToxWindow {
void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t); void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t);
void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t); void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t);
void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL); void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL);
void(*onFileRecv)(ToxWindow *, Tox *, uint32_t, uint32_t, uint32_t, uint64_t, const char *, size_t); void(*onFileRecv)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t);
void(*onTypingChange)(ToxWindow *, Tox *, uint32_t, bool); void(*onTypingChange)(ToxWindow *, Tox *, uint32_t, bool);
void(*onReadReceipt)(ToxWindow *, Tox *, uint32_t, uint32_t); void(*onReadReceipt)(ToxWindow *, Tox *, uint32_t, uint32_t);