diff --git a/configure.ac b/configure.ac index 712e0aa..86bbf31 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.65]) -AC_INIT([toxic], [0.2.1], [http://tox.im/]) +AC_INIT([toxic], [0.2.2], [http://tox.im/]) AC_CONFIG_AUX_DIR(configure_aux) AC_CONFIG_SRCDIR([src/main.c]) AC_CONFIG_HEADERS([config.h]) diff --git a/src/chat.c b/src/chat.c index 2678a3d..068e4ec 100644 --- a/src/chat.c +++ b/src/chat.c @@ -106,6 +106,75 @@ static void chat_onStatusMessageChange(ToxWindow *self, int num, uint8_t *status memcpy(statusbar->statusmsg, status, len); } +static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t filenum, uint64_t filesize, + uint8_t *filename, uint16_t filename_len) +{ + if (self-> num != num) + return; + + ChatContext *ctx = (ChatContext *) self->chatwin; + + wprintw(ctx->history, "File transfer request for '%s' of size %llu.\n", filename, (long long unsigned int)filesize); + + if (filenum > MAX_FILENUMBER) { + wprintw(ctx->history, "Too many pending file requests; discarding.\n"); + return; + } + + wprintw(ctx->history, "Type '/file %d' to accept the file transfer.\n", filenum); + + pending_file_transfers[filenum] = num; + + self->blink = true; + beep(); + +} + +static void chat_onFileControl(ToxWindow *self, Tox *m, int num, uint8_t receive_send, uint8_t filenum, + uint8_t control_type, uint8_t *data, uint16_t length) +{ + if (self->num != num) + return; + + ChatContext *ctx = (ChatContext *) self->chatwin; + + switch(control_type) { + case 0: + wprintw(ctx->history, "File transfer accepted.\n"); + break; + case 3: + wprintw(ctx->history, "File successfully recieved.\n"); + break; + default: + wprintw(ctx->history, "Control %u receieved.\n", control_type); + break; + } + + self->blink = true; + beep(); +} + +static void chat_onFileData(ToxWindow *self, Tox *m, int num, uint8_t filenum, uint8_t *data, uint16_t length) +{ + if (self->num != num) + return; + + ChatContext *ctx = (ChatContext *) self->chatwin; + + char filename[MAX_STR_SIZE]; + snprintf(filename, sizeof(filename), "%d.%u.bin", num, filenum); + + FILE *file_to_save = fopen(filename, "a"); + + if (fwrite(data, length, 1, file_to_save) != 1) { + wattron(ctx->history, COLOR_PAIR(RED)); + wprintw(ctx->history, "* Error writing to file.\n"); + wattroff(ctx->history, COLOR_PAIR(RED)); + } + + fclose(file_to_save); +} + static void print_chat_help(ChatContext *ctx) { wattron(ctx->history, COLOR_PAIR(CYAN) | A_BOLD); @@ -120,6 +189,8 @@ static void print_chat_help(ChatContext *ctx) wprintw(ctx->history, " /myid : Print your ID\n"); wprintw(ctx->history, " /clear : Clear the screen\n"); wprintw(ctx->history, " /close : Close the current chat window\n"); + wprintw(ctx->history, " /sendfile : Send a file\n"); + wprintw(ctx->history, " /file : Accept a file\n"); wprintw(ctx->history, " /quit or /exit : Exit Toxic\n"); wprintw(ctx->history, " /help : Print this message again\n"); @@ -368,6 +439,9 @@ ToxWindow new_chat(Tox *m, ToxWindow *prompt, int friendnum) ret.onStatusChange = &chat_onStatusChange; ret.onStatusMessageChange = &chat_onStatusMessageChange; ret.onAction = &chat_onAction; + ret.onFileSendRequest = &chat_onFileSendRequest; + ret.onFileControl = &chat_onFileControl; + ret.onFileData = &chat_onFileData; uint8_t name[TOX_MAX_NAME_LENGTH] = {'\0'}; tox_getname(m, friendnum, name); diff --git a/src/commands.c b/src/commands.c index 765ad8d..400d541 100644 --- a/src/commands.c +++ b/src/commands.c @@ -180,6 +180,28 @@ void cmd_connect(WINDOW *window, ToxWindow *prompt, Tox *m, int argc, char **arg free(binary_string); } +void cmd_file(WINDOW *window, ToxWindow *prompt, Tox *m, int argc, char **argv) +{ + if (argc < 1) { + wprintw(window, "Wrong number of arguments.\n"); + return; + } + + uint8_t filenum = atoi(argv[1]); + + if (filenum < 0 || filenum > MAX_FILENUMBER) { + wprintw(window, "File transfer failed.\n"); + return; + } + + int friendnum = pending_file_transfers[filenum]; + + if (tox_file_sendcontrol(m, friendnum, 1, filenum, 0, 0, 0)) + wprintw(window, "Accepted file transfer %u. Saving file as %d.%u.bin.\n", filenum, friendnum, filenum); + else + wprintw(window, "File transfer failed.\n"); +} + void cmd_groupchat(WINDOW *window, ToxWindow *prompt, Tox *m, int argc, char **argv) { int ngc = get_num_groupchats(); @@ -392,6 +414,71 @@ void cmd_quit(WINDOW *window, ToxWindow *prompt, Tox *m, int argc, char **argv) exit_toxic(m); } +void cmd_sendfile(WINDOW *window, ToxWindow *prompt, Tox *m, int argc, char **argv) +{ + if (argc < 1) { + wprintw(window, "Wrong number of arguments.\n"); + return; + } + + uint8_t *friendname = argv[1]; + + int friendnum = get_friendnum(friendname); + + if (friendnum == -1) { + wprintw(window, "Friend '%s' not found.\n", friendname); + return; + } + + if (friendname[0] == '\"') + friendname[strlen(++friendname)-1] = L'\0'; + + uint8_t *filename = argv[2]; + int filename_len = strlen(filename); + + if (filename[0] != '\"') { + wprintw(window, "File name must be enclosed in quotes.\n"); + return; + } + + filename[strlen(++filename)-1] = L'\0'; + + if (filename_len > MAX_STR_SIZE) { + wprintw(window, "File path exceeds character limit.\n"); + return; + } + + FILE *file_to_send = fopen(filename, "r"); + + if (file_to_send == NULL) { + wprintw(window, "File '%s' not found.\n", filename); + return; + } + + fseek(file_to_send, 0, SEEK_END); + uint64_t filesize = ftell(file_to_send); + fseek(file_to_send, 0, SEEK_SET); + + int filenum = tox_new_filesender(m, friendnum, filesize, filename, filename_len + 1); + + if (filenum == -1) { + wprintw(window, "Error sending file\n"); + return; + } + + memcpy(file_senders[num_file_senders].filename, filename, filename_len + 1); + memcpy(file_senders[num_file_senders].friendname, friendname, strlen(friendname) + 1); + file_senders[num_file_senders].file = file_to_send; + file_senders[num_file_senders].filenum = filenum; + file_senders[num_file_senders].friendnum = friendnum; + file_senders[num_file_senders].piecelen = fread(file_senders[num_file_senders].nextpiece, 1, + tox_filedata_size(m, friendnum), file_to_send); + + + wprintw(window, "Sending file '%s' to %s...\n", filename, friendname); + ++num_file_senders; +} + void cmd_status(WINDOW *window, ToxWindow *prompt, Tox *m, int argc, char **argv) { uint8_t *msg = NULL; diff --git a/src/commands.h b/src/commands.h index a6ad45e..3d48cc8 100644 --- a/src/commands.h +++ b/src/commands.h @@ -7,6 +7,7 @@ void cmd_accept(WINDOW *, ToxWindow *, Tox *m, int, char **); void cmd_add(WINDOW *, ToxWindow *, Tox *m, int, char **); void cmd_clear(WINDOW *, ToxWindow *, Tox *m, int, char **); void cmd_connect(WINDOW *, ToxWindow *, Tox *m, int, char **); +void cmd_file(WINDOW *, ToxWindow *, Tox *m, int, char **); void cmd_groupchat(WINDOW *, ToxWindow *, Tox *m, int, char **); void cmd_invite(WINDOW *, ToxWindow *, Tox *m, int, char **); void cmd_join(WINDOW *, ToxWindow *, Tox *m, int, char **); @@ -15,11 +16,12 @@ void cmd_myid(WINDOW *, ToxWindow *, Tox *m, int, char **); void cmd_nick(WINDOW *, ToxWindow *, Tox *m, int, char **); void cmd_note(WINDOW *, ToxWindow *, Tox *m, int, char **); void cmd_quit(WINDOW *, ToxWindow *, Tox *m, int, char **); +void cmd_sendfile(WINDOW *, ToxWindow *, Tox *m, int, char **); void cmd_status(WINDOW *, ToxWindow *, Tox *m, int, char **); void execute(WINDOW *window, ToxWindow *prompt, Tox *m, char *u_cmd, int buf_len); -#define NUM_COMMANDS 15 +#define NUM_COMMANDS 17 static struct { char *name; @@ -30,6 +32,7 @@ static struct { { "/clear", cmd_clear }, { "/connect", cmd_connect }, { "/exit", cmd_quit }, + { "/file", cmd_file }, { "/groupchat", cmd_groupchat }, { "/invite", cmd_invite }, { "/join", cmd_join }, @@ -39,5 +42,6 @@ static struct { { "/note", cmd_note }, { "/q", cmd_quit }, { "/quit", cmd_quit }, + { "/sendfile", cmd_sendfile }, { "/status", cmd_status }, }; diff --git a/src/friendlist.c b/src/friendlist.c index 57bb158..330ca01 100644 --- a/src/friendlist.c +++ b/src/friendlist.c @@ -108,6 +108,16 @@ int friendlist_onFriendAdded(Tox *m, int num) return -1; } +void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t filenum, uint64_t filesize, + uint8_t *filename, uint16_t filename_len) +{ + if (num < 0 || num >= num_friends) + return; + + if (friends[num].chatwin == -1) + friends[num].chatwin = add_window(m, new_chat(m, prompt, friends[num].num)); +} + static void select_friend(Tox *m, wint_t key) { if (num_friends < 1) @@ -286,6 +296,7 @@ ToxWindow new_friendlist() ret.onNickChange = &friendlist_onNickChange; ret.onStatusChange = &friendlist_onStatusChange; ret.onStatusMessageChange = &friendlist_onStatusMessageChange; + ret.onFileSendRequest = &friendlist_onFileSendRequest; strcpy(ret.name, "friends"); return ret; diff --git a/src/main.c b/src/main.c index 7e49a5c..2c0ec8f 100644 --- a/src/main.c +++ b/src/main.c @@ -92,6 +92,7 @@ static Tox *init_tox() { /* Init core */ Tox *m = tox_new(TOX_ENABLE_IPV6_DEFAULT); + if (m == NULL) return NULL; @@ -105,6 +106,10 @@ static Tox *init_tox() tox_callback_action(m, on_action, NULL); tox_callback_group_invite(m, on_groupinvite, NULL); tox_callback_group_message(m, on_groupmessage, NULL); + tox_callback_file_sendrequest(m, on_file_sendrequest, NULL); + tox_callback_file_control(m, on_file_control, NULL); + tox_callback_file_data(m, on_file_data, NULL); + #ifdef __linux__ tox_setname(m, (uint8_t *) "Cool guy", sizeof("Cool guy")); #elif defined(_WIN32) @@ -114,6 +119,7 @@ static Tox *init_tox() #else tox_setname(m, (uint8_t *) "Registered Minix user #4", sizeof("Registered Minix user #4")); #endif + return m; } @@ -341,6 +347,36 @@ static void load_data(Tox *m, char *path) } } +void do_file_senders(Tox *m) +{ + int i; + + for (i = 0; i < NUM_FILE_SENDERS; ++i) { + if (file_senders[i].file == NULL) + continue; + + while (true) { + if (!tox_file_senddata(m, file_senders[i].friendnum, file_senders[i].filenum, + file_senders[i].nextpiece, file_senders[i].piecelen)) + return; + + file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1, tox_filedata_size(m, file_senders[i].friendnum), + file_senders[i].file); + + if (file_senders[i].piecelen == 0) { + fclose(file_senders[i].file); + file_senders[i].file = NULL; + tox_file_sendcontrol(m, file_senders[i].friendnum, 0, file_senders[i].filenum, 3, 0, 0); + + /* TODO: move this alert to chat window */ + wprintw(prompt->window, "File '%s' successfuly sent to %s.\n", file_senders[i].filename, + file_senders[i].friendname); + return; + } + } + } +} + void exit_toxic(Tox *m) { store_data(m, DATA_FILE); @@ -442,10 +478,8 @@ int main(int argc, char *argv[]) prompt_init_statusbar(prompt, m); while (true) { - /* Update tox */ do_tox(m, prompt); - - /* Draw */ + do_file_senders(m); draw_active_window(m); } diff --git a/src/prompt.c b/src/prompt.c index ce41499..4e6e8ab 100644 --- a/src/prompt.c +++ b/src/prompt.c @@ -320,7 +320,7 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m) snprintf(statusbar->nick, sizeof(statusbar->nick), "%s", nick); /* temporary until statusmessage saving works */ - uint8_t *statusmsg = "Toxing on Toxic v.0.2.1"; + uint8_t *statusmsg = "Toxing on Toxic v.0.2.2"; m_set_statusmessage(m, statusmsg, strlen(statusmsg) + 1); snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg); diff --git a/src/toxic_windows.h b/src/toxic_windows.h index 03133e3..37c3e8c 100644 --- a/src/toxic_windows.h +++ b/src/toxic_windows.h @@ -56,6 +56,9 @@ struct ToxWindow_ { void(*onAction)(ToxWindow *, Tox *, int, uint8_t *, uint16_t); void(*onGroupMessage)(ToxWindow *, Tox *, int, int, uint8_t *, uint16_t); void(*onGroupInvite)(ToxWindow *, Tox *, int, uint8_t *); + void(*onFileSendRequest)(ToxWindow *, Tox *, int, uint8_t, uint64_t, uint8_t *, uint16_t); + void(*onFileControl)(ToxWindow *, Tox *, int, uint8_t, uint8_t, uint8_t, uint8_t *, uint16_t); + void(*onFileData)(ToxWindow *, Tox *, int, uint8_t, uint8_t *, uint16_t); char name[TOX_MAX_NAME_LENGTH]; int num; @@ -92,6 +95,29 @@ typedef struct { bool active; } GroupChat; +/* Start file transfer code */ + +#define NUM_FILE_SENDERS 256 +#define MAX_FILENUMBER 100 /* fix */ +#define FILE_PIECE_SIZE 1024 + +typedef struct { + FILE *file; + uint16_t friendnum; + uint8_t filenum; + uint8_t nextpiece[FILE_PIECE_SIZE]; + uint16_t piecelen; + uint8_t friendname[TOXIC_MAX_NAME_LENGTH]; + uint8_t filename[MAX_STR_SIZE]; +} FileSender; + +FileSender file_senders[NUM_FILE_SENDERS]; +uint8_t num_file_senders; + +uint8_t pending_file_transfers[MAX_FILENUMBER]; + +/* End file transfer code */ + void on_request(uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata); void on_connectionchange(Tox *m, int friendnumber, uint8_t status, void *userdata); void on_message(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata); @@ -102,6 +128,10 @@ void on_statusmessagechange(Tox *m, int friendnumber, uint8_t *string, uint16_t void on_friendadded(Tox *m, int friendnumber); void on_groupmessage(Tox *m, int groupnumber, int peernumber, uint8_t *message, uint16_t length, void *userdata); void on_groupinvite(Tox *m, int friendnumber, uint8_t *group_pub_key, void *userdata); +void on_file_sendrequest(Tox *m, int friendnumber, uint8_t filenumber, uint64_t filesize, uint8_t *filename, uint16_t filename_length, void *userdata); +void on_file_control(Tox *m, int friendnumber, uint8_t receive_send, uint8_t filenumber, uint8_t control_type, uint8_t *data, uint16_t length, void *userdata); +void on_file_data(Tox *m, int friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *userdata); + ToxWindow *init_windows(); void draw_active_window(Tox *m); int add_window(Tox *m, ToxWindow w); diff --git a/src/windows.c b/src/windows.c index d06f62e..736dd86 100644 --- a/src/windows.c +++ b/src/windows.c @@ -137,6 +137,41 @@ void on_groupinvite(Tox *m, int friendnumber, uint8_t *group_pub_key, void *user windows[i].onGroupInvite(&windows[i], m, friendnumber, group_pub_key); } } + +void on_file_sendrequest(Tox *m, int friendnumber, uint8_t filenumber, uint64_t filesize, + uint8_t *filename, uint16_t filename_length, void *userdata) +{ + int i; + + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { + if (windows[i].onFileSendRequest != NULL) + windows[i].onFileSendRequest(&windows[i], m, friendnumber, filenumber, filesize, + filename, filename_length); + } +} + +void on_file_control (Tox *m, int friendnumber, uint8_t receive_send, uint8_t filenumber, + uint8_t control_type, uint8_t *data, uint16_t length, void *userdata) +{ + int i; + + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { + if (windows[i].onFileControl != NULL) + windows[i].onFileControl(&windows[i], m, friendnumber, receive_send, filenumber, + control_type, data, length); + } +} + +void on_file_data(Tox *m, int friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *userdata) +{ + int i; + + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { + if (windows[i].onFileData != NULL) + windows[i].onFileData(&windows[i], m, friendnumber, filenumber, data, length); + } +} + /* CALLBACKS END */ int add_window(Tox *m, ToxWindow w)