diff --git a/README.md b/README.md index 2afa965..3318693 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,21 @@ -## Toxic - console client for [Tox](http://tox.im) +# Toxic -The client formerly resided in the [Tox core repository](https://github.com/irungentoo/ProjectTox-Core) and is now available as a standalone program. It looks like [this](http://wiki.tox.im/images/b/b6/Ncursesclient1.png). +Toxic is an ncurses based instant messaging client for [Tox](http://tox.im) which formerly resided in the [Tox core repository](https://github.com/irungentoo/ProjectTox-Core) and is now available as a standalone program. It looks like [this](http://i.imgur.com/hL7WhVl.png). +## Installation +* Generate the configure script by running the ```autoreconf -i``` command. -To compile, first generate the configure script by running the ```autoreconf -i``` command. +* Execute the configure script with ```./configure``` (you may need to pass it the location of your dependency libraries, i.e.): +```./configure --prefix=/where/to/install --with-libtoxcore-headers=/path/to/ProjectTox-Core/toxcore --with-libtoxcore-libs=/path/to/ProjectTox-Core/build/toxcore --with-libsodium-headers=/path/to/libsodium/include/ --with-libsodium-libs=/path/to/sodiumtest/lib/ ``` -Then execute the configure script with ./configure (you may need to pass it the location of your dependency libraries, i.e.): -``` -./configure --prefix=/where/to/install --with-libtoxcore-headers=/path/to/ProjectTox-Core/core --with-libtoxcore-libs=/path/to/ProjectTox-Core/build/core --with-libsodium-headers=/path/to/libsodium/include/ --with-libsodium-libs=/path/to/sodiumtest/lib/ +* Compile and install the program with ```make && sudo make install``` -``` -*Note:* If your default prefix is /usr/local and you happen to get an error that says "error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory", then you can try running ```sudo ldconfig```. If that doesn't fix it, run: +#### Notes +If your default prefix is /usr/local and you get the error: "error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory", then you can try running ```sudo ldconfig```. If that doesn't fix it, run: ``` echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf sudo ldconfig ``` +If you dont already have them, you may need to install the ncurses libraries. For Debian based systems: +``` +sudo apt-get install libncurses5-dev libncursesw5-dev +``` diff --git a/build/Makefile.am b/build/Makefile.am index e8e8848..329678d 100644 --- a/build/Makefile.am +++ b/build/Makefile.am @@ -25,7 +25,11 @@ toxic_SOURCES = $(top_srcdir)/src/main.c \ $(top_srcdir)/src/misc_tools.c \ $(top_srcdir)/src/misc_tools.h \ $(top_srcdir)/src/toxic_strings.c \ - $(top_srcdir)/src/toxic_strings.h + $(top_srcdir)/src/toxic_strings.h \ + $(top_srcdir)/src/log.c \ + $(top_srcdir)/src/log.h \ + $(top_srcdir)/src/file_senders.c \ + $(top_srcdir)/src/file_senders.h toxic_CFLAGS = -I$(top_srcdir) \ $(NCURSES_CFLAGS) \ @@ -53,4 +57,4 @@ toxic_CFLAGS += $(LIBTOXAV_CFLAGS) \ toxic_LDADD += $(LIBTOXAV_LIBS) \ $(OPENAL_LIBS) -endif \ No newline at end of file +endif diff --git a/configure.ac b/configure.ac index a58f474..e17e4b4 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.5], [https://tox.im/]) +AC_INIT([toxic], [0.2.7], [https://tox.im/]) AC_CONFIG_AUX_DIR(configure_aux) AC_CONFIG_SRCDIR([src/main.c]) AC_CONFIG_HEADERS([config.h]) diff --git a/misc/DHTservers b/misc/DHTnodes similarity index 100% rename from misc/DHTservers rename to misc/DHTnodes diff --git a/misc/Makefile.am b/misc/Makefile.am index c25600a..0113f9d 100644 --- a/misc/Makefile.am +++ b/misc/Makefile.am @@ -1 +1 @@ -dist_pkgdata_DATA = DHTservers +dist_pkgdata_DATA = DHTnodes diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 53316af..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -cmake_minimum_required(VERSION 2.6.0) -project(toxic C) - -execute_process(COMMAND git rev-list HEAD --count OUTPUT_VARIABLE COMMIT) -SET(GCC_COVERAGE_COMPILE_FLAGS '-DTOXICVER="0.1.1_r${COMMIT}"') -add_definitions(${GCC_COVERAGE_COMPILE_FLAGS}) -set(exe_name toxic) - -add_executable(${exe_name} - main.c - windows.c - prompt.c - friendlist.c - dhtstatus.c - chat.c - configdir.c) - -include_directories(${CURSES_INCLUDE_DIR}) - -target_link_libraries(${exe_name} - ${CURSES_LIBRARIES}) - -linkCoreLibraries(${exe_name}) diff --git a/src/chat.c b/src/chat.c index 492ada1..7e0b29f 100644 --- a/src/chat.c +++ b/src/chat.c @@ -1,5 +1,23 @@ -/* - * Toxic -- Tox Curses Client +/* chat.c + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ #ifdef HAVE_CONFIG_H @@ -15,6 +33,7 @@ #include "misc_tools.h" #include "friendlist.h" #include "toxic_strings.h" +#include "log.h" #ifdef _SUPPORT_AUDIO #include "audio_call.h" @@ -26,7 +45,12 @@ extern int store_data(Tox *m, char *path); extern FileSender file_senders[MAX_FILES]; extern ToxicFriend friends[MAX_FRIENDS_NUM]; -#define AC_NUM_CHAT_COMMANDS 21 + +#ifdef _SUPPORT_AUDIO +#define AC_NUM_CHAT_COMMANDS 22 +#else +#define AC_NUM_CHAT_COMMANDS 18 +#endif /* _SUPPORT_AUDIO */ /* Array of chat command names used for tab completion. */ static const uint8_t chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = { @@ -40,6 +64,7 @@ static const uint8_t chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = { { "/help" }, { "/invite" }, { "/join" }, + { "/log" }, { "/myid" }, { "/nick" }, { "/note" }, @@ -58,6 +83,33 @@ static const uint8_t chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = { #endif /* _SUPPORT_AUDIO */ }; +static void set_typingstatus(ToxWindow *self, Tox *m, bool is_typing) +{ + ChatContext *ctx = self->chatwin; + + tox_set_user_is_typing(m, self->num, is_typing); + ctx->self_is_typing = is_typing; +} + +void kill_chat_window(ToxWindow *self) +{ + set_active_window(0); + ChatContext *ctx = self->chatwin; + StatusBar *statusbar = self->stb; + + log_disable(ctx->log); + + int f_num = self->num; + delwin(ctx->linewin); + delwin(statusbar->topline); + del_window(self); + disable_chatwin(f_num); + + free(ctx->log); + free(ctx); + free(statusbar); +} + static void chat_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *msg, uint16_t len) { if (self->num != num) @@ -81,6 +133,7 @@ static void chat_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *msg, uint1 } else wprintw(ctx->history, "%s\n", msg); + write_to_log(msg, nick, ctx->log, false); alert_window(self, WINDOW_ALERT_1, true); } @@ -90,7 +143,22 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, int num, uint8_t st return; StatusBar *statusbar = self->stb; - statusbar->is_online = status == 1 ? true : false; + + if (status == 1) { + statusbar->is_online = true; + friends[num].is_typing = tox_get_is_typing(m, num); + } else { + statusbar->is_online = false; + friends[num].is_typing = false; + } +} + +static void chat_onTypingChange(ToxWindow *self, Tox *m, int num, int is_typing) +{ + if (self->num != num) + return; + + friends[num].is_typing = is_typing; } static void chat_onAction(ToxWindow *self, Tox *m, int num, uint8_t *action, uint16_t len) @@ -109,6 +177,7 @@ static void chat_onAction(ToxWindow *self, Tox *m, int num, uint8_t *action, uin wprintw(ctx->history, "* %s %s\n", nick, action); wattroff(ctx->history, COLOR_PAIR(YELLOW)); + write_to_log(action, nick, ctx->log, true); alert_window(self, WINDOW_ALERT_1, true); } @@ -172,7 +241,7 @@ static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t fil strcat(filename, d); filename[len + strlen(d)] = '\0'; - if (count > 999999) { + if (count > 999) { wprintw(ctx->history, "Error saving file to disk.\n"); return; } @@ -186,6 +255,15 @@ static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t fil alert_window(self, WINDOW_ALERT_2, true); } +static void chat_close_file_receiver(int num, uint8_t filenum) +{ + friends[num].file_receiver.pending[filenum] = false; + FILE *file = friends[num].file_receiver.files[filenum]; + + if (file != NULL) + fclose(file); +} + 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) { @@ -204,20 +282,20 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, int num, uint8_t receive case TOX_FILECONTROL_ACCEPT: wprintw(ctx->history, "File transfer for '%s' accepted.\n", filename); break; - case TOX_FILECONTROL_PAUSE: - // wprintw(ctx->history, "File transfer for '%s' paused.\n", filename); - break; + // case TOX_FILECONTROL_PAUSE: + // wprintw(ctx->history, "File transfer for '%s' paused.\n", filename); + // break; case TOX_FILECONTROL_KILL: wprintw(ctx->history, "File transfer for '%s' failed.\n", filename); if (receive_send == 0) - friends[num].file_receiver.pending[filenum] = false; + chat_close_file_receiver(num, filenum); else - close_file_sender(filenum); - + chat_close_file_receiver(num, filenum); break; case TOX_FILECONTROL_FINISHED: wprintw(ctx->history, "File transfer for '%s' complete.\n", filename); + chat_close_file_receiver(num, filenum); break; } @@ -232,26 +310,12 @@ static void chat_onFileData(ToxWindow *self, Tox *m, int num, uint8_t filenum, u ChatContext *ctx = self->chatwin; - uint8_t *filename = friends[num].file_receiver.filenames[filenum]; - FILE *file_to_save = fopen(filename, "a"); - - // we have a problem here, but don't let it segfault - if (file_to_save == NULL) { - wattron(ctx->history, COLOR_PAIR(RED)); - wprintw(ctx->history, "* Error writing to file.\n"); - wattroff(ctx->history, COLOR_PAIR(RED)); - tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0); - return; - } - - if (fwrite(data, length, 1, file_to_save) != 1) { + if (fwrite(data, length, 1, friends[num].file_receiver.files[filenum]) != 1) { wattron(ctx->history, COLOR_PAIR(RED)); wprintw(ctx->history, "* Error writing to file.\n"); wattroff(ctx->history, COLOR_PAIR(RED)); tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0); } - - fclose(file_to_save); } static void chat_onGroupInvite(ToxWindow *self, Tox *m, int friendnumber, uint8_t *group_pub_key) @@ -407,6 +471,8 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t *acti wattron(ctx->history, COLOR_PAIR(RED)); wprintw(ctx->history, " * Failed to send action\n"); wattroff(ctx->history, COLOR_PAIR(RED)); + } else { + write_to_log(action, selfname, ctx->log, true); } } @@ -457,14 +523,14 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key) beep(); } - else if (key == KEY_HOME) { /* HOME key: Move cursor to beginning of line */ + else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */ if (ctx->pos > 0) { ctx->pos = 0; wmove(self->window, y2 - CURS_Y_OFFSET, 0); } } - else if (key == KEY_END) { /* END key: move cursor to end of line */ + else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */ if (ctx->pos != ctx->len) { ctx->pos = ctx->len; mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT-1)*x2)), y2, x2); @@ -546,6 +612,9 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key) else wmove(self->window, y, x + MAX(1, wcwidth(key))); } + + if (!ctx->self_is_typing && ctx->line[0] != '/') + set_typingstatus(self, m, true); } /* RETURN key: Execute command or print line */ else if (key == '\n') { @@ -557,22 +626,22 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key) wclear(ctx->linewin); wmove(self->window, y2 - CURS_Y_OFFSET, 0); wclrtobot(self->window); - bool close_win = false; if (!string_is_empty(line)) add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos); if (line[0] == '/') { - if (close_win = !strcmp(line, "/close")) { - int f_num = self->num; - delwin(ctx->linewin); - delwin(statusbar->topline); - del_window(self); - disable_chatwin(f_num); - } else if (strncmp(line, "/me ", strlen("/me ")) == 0) + if (strcmp(line, "/close") == 0) { + if (ctx->self_is_typing) + set_typingstatus(self, m, false); + + kill_chat_window(self); + return; + } else if (strncmp(line, "/me ", strlen("/me ")) == 0) { send_action(self, ctx, m, line + strlen("/me ")); - else + } else { execute(ctx->history, self, m, line, CHAT_COMMAND_MODE); + } } else if (!string_is_empty(line)) { uint8_t selfname[TOX_MAX_NAME_LENGTH]; tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH); @@ -593,16 +662,16 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key) wattron(ctx->history, COLOR_PAIR(RED)); wprintw(ctx->history, " * Failed to send message.\n"); wattroff(ctx->history, COLOR_PAIR(RED)); + } else { + write_to_log(line, selfname, ctx->log, false); } } - if (close_win) { - free(ctx); - free(statusbar); - } else { - reset_buf(ctx->line, &ctx->pos, &ctx->len); - } + reset_buf(ctx->line, &ctx->pos, &ctx->len); } + + if (ctx->len <= 0 && ctx->self_is_typing) + set_typingstatus(self, m, false); } static void chat_onDraw(ToxWindow *self, Tox *m) @@ -653,9 +722,16 @@ static void chat_onDraw(ToxWindow *self, Tox *m) break; } + if (friends[self->num].is_typing) + wattron(statusbar->topline, COLOR_PAIR(YELLOW)); + wattron(statusbar->topline, A_BOLD); wprintw(statusbar->topline, " %s ", self->name); wattroff(statusbar->topline, A_BOLD); + + if (friends[self->num].is_typing) + wattroff(statusbar->topline, COLOR_PAIR(YELLOW)); + wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); wprintw(statusbar->topline, "[%s]", status_text); wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); @@ -724,8 +800,24 @@ static void chat_onInit(ToxWindow *self, Tox *m) ctx->history = subwin(self->window, y2-CHATBOX_HEIGHT+1, x2, 0, 0); scrollok(ctx->history, 1); ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2-CHATBOX_HEIGHT, 0); + + ctx->log = malloc(sizeof(struct chatlog)); + + if (ctx->log == NULL) { + endwin(); + fprintf(stderr, "malloc() failed. Aborting...\n"); + exit(EXIT_FAILURE); + } + + memset(ctx->log, 0, sizeof(struct chatlog)); + + if (friends[self->num].logging_on) + log_enable(self->name, friends[self->num].pub_key, ctx->log); + wprintw(ctx->history, "\n\n"); execute(ctx->history, self, m, "/help", CHAT_COMMAND_MODE); + execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE); + wmove(self->window, y2 - CURS_Y_OFFSET, 0); } @@ -735,12 +827,14 @@ ToxWindow new_chat(Tox *m, int friendnum) memset(&ret, 0, sizeof(ret)); ret.active = true; + ret.is_chat = true; ret.onKey = &chat_onKey; ret.onDraw = &chat_onDraw; ret.onInit = &chat_onInit; ret.onMessage = &chat_onMessage; ret.onConnectionChange = &chat_onConnectionChange; + ret.onTypingChange = & chat_onTypingChange; ret.onGroupInvite = &chat_onGroupInvite; ret.onNickChange = &chat_onNickChange; ret.onStatusChange = &chat_onStatusChange; diff --git a/src/chat.h b/src/chat.h index a7918df..fac132a 100644 --- a/src/chat.h +++ b/src/chat.h @@ -1,8 +1,31 @@ +/* chat.h + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * + */ + #ifndef CHAT_H_6489PZ13 #define CHAT_H_6489PZ13 #include "toxic_windows.h" +void kill_chat_window(ToxWindow *self); ToxWindow new_chat(Tox *m, int friendnum); #endif /* end of include guard: CHAT_H_6489PZ13 */ diff --git a/src/chat_commands.c b/src/chat_commands.c index 600c07a..9bac9a3 100644 --- a/src/chat_commands.c +++ b/src/chat_commands.c @@ -1,5 +1,23 @@ -/* - * Toxic -- Tox Curses Client +/* chat_commands.c + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ #ifdef HAVE_CONFIG_H @@ -12,6 +30,7 @@ #include "toxic_windows.h" #include "misc_tools.h" #include "friendlist.h" +#include "execute.h" extern ToxWindow *prompt; @@ -22,23 +41,17 @@ extern uint8_t max_file_senders_index; void cmd_chat_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { + if (argc == 1) { + if (!strcmp(argv[1], "global")) { + execute(window, self, m, "/help", GLOBAL_COMMAND_MODE); + return; + } + } + wattron(window, COLOR_PAIR(CYAN) | A_BOLD); wprintw(window, "Chat commands:\n"); wattroff(window, COLOR_PAIR(CYAN) | A_BOLD); - wprintw(window, " /status : Set your status with optional note\n"); - wprintw(window, " /note : Set a personal note\n"); - wprintw(window, " /nick : Set your nickname\n"); - wprintw(window, " /invite : Invite friend to a group chat\n"); - wprintw(window, " /me : Do an action\n"); - wprintw(window, " /myid : Print your ID\n"); - wprintw(window, " /join : Join a pending group chat\n"); - wprintw(window, " /clear : Clear the screen\n"); - wprintw(window, " /close : Close the current chat window\n"); - wprintw(window, " /sendfile : Send a file\n"); - wprintw(window, " /savefile : Receive a file\n"); - wprintw(window, " /quit or /exit : Exit Toxic\n"); - #ifdef _SUPPORT_AUDIO wprintw(window, " /call : Audio call\n"); @@ -48,7 +61,14 @@ void cmd_chat_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg #endif /* _SUPPORT_AUDIO */ - wprintw(window, " /help : Print this message again\n"); + wprintw(window, " /invite : Invite friend to a group chat\n"); + wprintw(window, " /join : Join a pending group chat\n"); + wprintw(window, " /log or : Enable/disable logging\n"); + wprintw(window, " /sendfile : Send a file\n"); + wprintw(window, " /savefile : Receive a file\n"); + wprintw(window, " /close : Close the current chat window\n"); + wprintw(window, " /help : Print this message again\n"); + wprintw(window, " /help global : Show a list of global commands\n"); wattron(window, COLOR_PAIR(CYAN) | A_BOLD); wprintw(window, " * Argument messages must be enclosed in quotation marks.\n\n"); @@ -128,10 +148,18 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv uint8_t *filename = friends[self->num].file_receiver.filenames[filenum]; - if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) + if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) { wprintw(window, "Accepted file transfer %u. Saving file as: '%s'\n", filenum, filename); - else + + if ((friends[self->num].file_receiver.files[filenum] = fopen(filename, "a")) == NULL) { + wattron(window, COLOR_PAIR(RED)); + wprintw(window, "* Error writing to file.\n"); + wattroff(window, COLOR_PAIR(RED)); + tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0); + } + } else { wprintw(window, "File transfer failed.\n"); + } friends[self->num].file_receiver.pending[filenum] = false; } diff --git a/src/chat_commands.h b/src/chat_commands.h index d52357b..f5caf1d 100644 --- a/src/chat_commands.h +++ b/src/chat_commands.h @@ -1,5 +1,23 @@ -/* - * Toxic -- Tox Curses Client +/* chat_commands.h + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ void cmd_chat_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); @@ -14,4 +32,4 @@ void cmd_call(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE] void cmd_answer(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_hangup(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); -#endif /* _SUPPORT_AUDIO */ \ No newline at end of file +#endif /* _SUPPORT_AUDIO */ diff --git a/src/configdir.c b/src/configdir.c index 8f43bc9..d43b08c 100644 --- a/src/configdir.c +++ b/src/configdir.c @@ -1,20 +1,22 @@ -/* - * Copyright (C) 2013 Tox project All Rights Reserved. +/* configdir.c * - * This file is part of Tox. * - * Tox is free software: you can redistribute it and/or modify + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * - * Tox is distributed in the hope that it will be useful, + * Toxic is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . + * along with Toxic. If not, see . * */ diff --git a/src/configdir.h b/src/configdir.h index e886e53..6a1a7eb 100644 --- a/src/configdir.h +++ b/src/configdir.h @@ -1,20 +1,22 @@ -/* - * Copyright (C) 2013 Tox project All Rights Reserved. +/* configdir.h * - * This file is part of Tox. * - * Tox is free software: you can redistribute it and/or modify + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * - * Tox is distributed in the hope that it will be useful, + * Toxic is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . + * along with Toxic. If not, see . * */ diff --git a/src/execute.c b/src/execute.c index 104af9a..462749d 100644 --- a/src/execute.c +++ b/src/execute.c @@ -1,5 +1,23 @@ -/* - * Toxic -- Tox Curses Client +/* execute.c + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ #include @@ -23,6 +41,7 @@ static struct cmd_func global_commands[] = { { "/exit", cmd_quit }, { "/groupchat", cmd_groupchat }, { "/help", cmd_prompt_help }, + { "/log", cmd_log }, { "/myid", cmd_myid }, { "/nick", cmd_nick }, { "/note", cmd_note }, diff --git a/src/execute.h b/src/execute.h index 9586410..42b90b9 100644 --- a/src/execute.h +++ b/src/execute.h @@ -1,9 +1,27 @@ -/* - * Toxic -- Tox Curses Client +/* execute.h + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ #define MAX_NUM_ARGS 4 /* Includes command */ -#define GLOBAL_NUM_COMMANDS 13 +#define GLOBAL_NUM_COMMANDS 14 #ifdef _SUPPORT_AUDIO #define CHAT_NUM_COMMANDS 9 diff --git a/src/file_senders.c b/src/file_senders.c new file mode 100644 index 0000000..d643bf4 --- /dev/null +++ b/src/file_senders.c @@ -0,0 +1,109 @@ +/* file_senders.c + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * + */ + +#include +#include +#include + +#include "toxic_windows.h" + +FileSender file_senders[MAX_FILES]; +uint8_t max_file_senders_index; + +static void close_file_sender(int i) +{ + fclose(file_senders[i].file); + memset(&file_senders[i], 0, sizeof(FileSender)); + + int j; + + for (j = max_file_senders_index; j > 0; --j) { + if (file_senders[j-1].active) + break; + } + + max_file_senders_index = j; +} + +/* Should only be called on exit */ +void close_all_file_senders(void) +{ + int i; + + for (i = 0; i < max_file_senders_index; ++i) { + if (file_senders[i].active) + fclose(file_senders[i].file); + } +} + +void do_file_senders(Tox *m) +{ + int i; + + for (i = 0; i < max_file_senders_index; ++i) { + if (!file_senders[i].active) + continue; + + uint8_t *pathname = file_senders[i].pathname; + uint8_t filenum = file_senders[i].filenum; + int friendnum = file_senders[i].friendnum; + FILE *fp = file_senders[i].file; + uint64_t current_time = (uint64_t) time(NULL); + + /* If file transfer has timed out kill transfer and send kill control */ + if (timed_out(file_senders[i].timestamp, current_time, TIMEOUT_FILESENDER)) { + ChatContext *ctx = file_senders[i].toxwin->chatwin; + + if (ctx != NULL) { + wprintw(ctx->history, "File transfer for '%s' timed out.\n", pathname); + alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true); + } + + tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_KILL, 0, 0); + close_file_sender(i); + continue; + } + + while (true) { + if (tox_file_send_data(m, friendnum, filenum, file_senders[i].nextpiece, + file_senders[i].piecelen) == -1) + break; + + file_senders[i].timestamp = current_time; + file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1, + tox_file_data_size(m, friendnum), fp); + + if (file_senders[i].piecelen == 0) { + ChatContext *ctx = file_senders[i].toxwin->chatwin; + + if (ctx != NULL) { + wprintw(ctx->history, "File '%s' successfuly sent.\n", pathname); + alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true); + } + + tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0); + close_file_sender(i); + break; + } + } + } +} diff --git a/src/file_senders.h b/src/file_senders.h new file mode 100644 index 0000000..9ec1cdc --- /dev/null +++ b/src/file_senders.h @@ -0,0 +1,26 @@ +/* file_senders.h + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * + */ + +/* Should only be called on exit */ +void close_all_file_senders(void); + +void do_file_senders(Tox *m); diff --git a/src/friendlist.c b/src/friendlist.c index c6273b5..190de5b 100644 --- a/src/friendlist.c +++ b/src/friendlist.c @@ -1,5 +1,23 @@ -/* - * Toxic -- Tox Curses Client +/* friendlist.c + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ #ifdef HAVE_CONFIG_H @@ -116,7 +134,7 @@ static void friendlist_onStatusMessageChange(ToxWindow *self, int num, uint8_t * friends[num].statusmsg_len = len; } -static void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort) +void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort) { if (max_friends_index < 0 || max_friends_index >= MAX_FRIENDS_NUM) return; diff --git a/src/friendlist.h b/src/friendlist.h index 029c9a6..23a3565 100644 --- a/src/friendlist.h +++ b/src/friendlist.h @@ -1,3 +1,25 @@ +/* friendlist.h + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * + */ + #ifndef FRIENDLIST_H_53I41IM #define FRIENDLIST_H_53I41IM @@ -14,6 +36,8 @@ typedef struct { int chatwin; bool active; bool online; + bool is_typing; + bool logging_on; /* saves preference for friend irrespective of chat windows */ TOX_USERSTATUS status; struct FileReceiver file_receiver; } ToxicFriend; @@ -22,6 +46,8 @@ ToxWindow new_friendlist(void); void disable_chatwin(int f_num); int get_friendnum(uint8_t *name); +void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort); + /* sorts friendlist_index first by connection status then alphabetically */ void sort_friendlist_index(Tox *m); diff --git a/src/global_commands.c b/src/global_commands.c index 29af2d6..80a2978 100644 --- a/src/global_commands.c +++ b/src/global_commands.c @@ -1,5 +1,23 @@ -/* - * Toxic -- Tox Curses Client +/* global_commands.c + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ #ifdef HAVE_CONFIG_H @@ -202,6 +220,73 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg wprintw(window, "Group chat created as %d.\n", groupnum); } +void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) +{ + if (argc == 0) { + bool on; + + if (self->is_chat || self->is_groupchat) + on = self->chatwin->log->log_on; + else if (self->is_prompt) + on = self->promptbuf->log->log_on; + + if (on) { + wprintw(window, "Logging for this window is "); + wattron(window, COLOR_PAIR(GREEN) | A_BOLD); + wprintw(window, "[on]"); + wattroff(window, COLOR_PAIR(GREEN) | A_BOLD); + wprintw(window, ". Type \"/log off\" to disable.\n"); + } else { + wprintw(window, "Logging for this window is "); + wattron(window, COLOR_PAIR(RED) | A_BOLD); + wprintw(window, "[off]"); + wattroff(window, COLOR_PAIR(RED) | A_BOLD); + wprintw(window, ". Type \"/log on\" to enable.\n"); + } + + return; + } + + uint8_t *swch = argv[1]; + + if (!strcmp(swch, "1") || !strcmp(swch, "on")) { + + if (self->is_chat) { + friends[self->num].logging_on = true; + log_enable(self->name, friends[self->num].pub_key, self->chatwin->log); + } else if (self->is_prompt) { + uint8_t myid[TOX_FRIEND_ADDRESS_SIZE]; + tox_get_address(m, myid); + log_enable(self->name, &myid, self->promptbuf->log); + } else if (self->is_groupchat) { + log_enable(self->name, NULL, self->chatwin->log); + } + + wprintw(window, "Logging "); + wattron(window, COLOR_PAIR(GREEN) | A_BOLD); + wprintw(window, "[on]\n"); + wattroff(window, COLOR_PAIR(GREEN) | A_BOLD); + return; + } else if (!strcmp(swch, "0") || !strcmp(swch, "off")) { + if (self->is_chat) { + friends[self->num].logging_on = false; + log_disable(self->chatwin->log); + } else if (self->is_prompt) { + log_disable(self->promptbuf->log); + } else if (self->is_groupchat) { + log_disable(self->chatwin->log); + } + + wprintw(window, "Logging "); + wattron(window, COLOR_PAIR(RED) | A_BOLD); + wprintw(window, "[off]\n"); + wattroff(window, COLOR_PAIR(RED) | A_BOLD); + return; + } + + wprintw(window, "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging.\n"); +} + void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { char id[TOX_FRIEND_ADDRESS_SIZE * 2 + 1] = {0}; @@ -280,17 +365,18 @@ void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a wprintw(window, "\n\nGlobal commands:\n"); wattroff(window, COLOR_PAIR(CYAN) | A_BOLD); - wprintw(window, " /add : Add friend with optional message\n"); - wprintw(window, " /accept : Accept friend request\n"); - wprintw(window, " /connect : Manually connect to a DHT server\n"); - wprintw(window, " /status : Set your status with optional note\n"); - wprintw(window, " /note : Set a personal note\n"); - wprintw(window, " /nick : Set your nickname\n"); - wprintw(window, " /groupchat : Create a group chat\n"); - wprintw(window, " /myid : Print your ID\n"); - wprintw(window, " /quit or /exit : Exit Toxic\n"); - wprintw(window, " /help : Print this message again\n"); - wprintw(window, " /clear : Clear the window\n"); + wprintw(window, " /add : Add friend with optional message\n"); + wprintw(window, " /accept : Accept friend request\n"); + wprintw(window, " /connect : Manually connect to a DHT node\n"); + wprintw(window, " /status : Set status with optional note\n"); + wprintw(window, " /note : Set a personal note\n"); + wprintw(window, " /nick : Set your nickname\n"); + wprintw(window, " /log or : Enable/disable logging\n"); + wprintw(window, " /groupchat : Create a group chat\n"); + wprintw(window, " /myid : Print your ID\n"); + wprintw(window, " /help : Print this message again\n"); + wprintw(window, " /clear : Clear the window\n"); + wprintw(window, " /quit or /exit : Exit Toxic\n"); wattron(window, COLOR_PAIR(CYAN) | A_BOLD); wprintw(window, " * Argument messages must be enclosed in quotation marks.\n"); diff --git a/src/global_commands.h b/src/global_commands.h index 57f16e3..5b0a66b 100644 --- a/src/global_commands.h +++ b/src/global_commands.h @@ -1,5 +1,23 @@ -/* - * Toxic -- Tox Curses Client +/* global_commands.h + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); @@ -7,6 +25,7 @@ void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); +void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); diff --git a/src/groupchat.c b/src/groupchat.c index 5b6d624..2f30ff9 100644 --- a/src/groupchat.c +++ b/src/groupchat.c @@ -1,5 +1,23 @@ -/* - * Toxic -- Tox Curses Client +/* groupchat.c + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ #ifdef HAVE_CONFIG_H @@ -16,6 +34,7 @@ #include "groupchat.h" #include "prompt.h" #include "toxic_strings.h" +#include "log.h" extern char *DATA_FILE; extern int store_data(Tox *m, char *path); @@ -53,8 +72,20 @@ int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum) return -1; } -static void close_groupchatwin(Tox *m, int groupnum) +void kill_groupchat_window(ToxWindow *self) { + ChatContext *ctx = self->chatwin; + + log_disable(ctx->log); + delwin(ctx->linewin); + del_window(self); + free(ctx->log); + free(ctx); +} + +static void close_groupchat(ToxWindow *self, Tox *m, int groupnum) +{ + set_active_window(0); tox_del_groupchat(m, groupnum); free(groupchats[groupnum].peer_names); @@ -69,6 +100,7 @@ static void close_groupchatwin(Tox *m, int groupnum) } max_groupchat_index = i; + kill_groupchat_window(self); } static void print_groupchat_help(ChatContext *ctx) @@ -77,16 +109,15 @@ static void print_groupchat_help(ChatContext *ctx) wprintw(ctx->history, "Group chat commands:\n"); wattroff(ctx->history, COLOR_PAIR(CYAN) | A_BOLD); - wprintw(ctx->history, " /add : Add friend with optional message\n"); - wprintw(ctx->history, " /status : Set your status with optional note\n"); - wprintw(ctx->history, " /note : Set a personal note\n"); - wprintw(ctx->history, " /nick : Set your nickname\n"); - wprintw(ctx->history, " /groupchat : Create a group chat\n"); - wprintw(ctx->history, " /myid : Print your ID\n"); - wprintw(ctx->history, " /clear : Clear the screen\n"); - wprintw(ctx->history, " /close : Close the current group chat\n"); - wprintw(ctx->history, " /quit or /exit : Exit Toxic\n"); - wprintw(ctx->history, " /help : Print this message again\n"); + wprintw(ctx->history, " /add : Add friend with optional message\n"); + wprintw(ctx->history, " /status : Set your status with optional note\n"); + wprintw(ctx->history, " /note : Set a personal note\n"); + wprintw(ctx->history, " /nick : Set your nickname\n"); + wprintw(ctx->history, " /groupchat : Create a group chat\n"); + wprintw(ctx->history, " /log or : Enable/disable logging\n"); + wprintw(ctx->history, " /close : Close the current group chat\n"); + wprintw(ctx->history, " /help : Print this message again\n"); + wprintw(ctx->history, " /help global : Show a list of global commands\n"); wattron(ctx->history, COLOR_PAIR(CYAN) | A_BOLD); wprintw(ctx->history, " * Argument messages must be enclosed in quotation marks.\n"); @@ -136,6 +167,8 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int } else { wprintw(ctx->history, "%s\n", msg); } + + write_to_log(msg, nick, ctx->log, false); } static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t *action, @@ -170,6 +203,8 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p wattron(ctx->history, COLOR_PAIR(YELLOW)); wprintw(ctx->history, "* %s %s\n", nick, action); wattroff(ctx->history, COLOR_PAIR(YELLOW)); + + write_to_log(action, nick, ctx->log, true); } /* Puts two copies of peerlist in chat instance */ @@ -234,25 +269,36 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu ChatContext *ctx = self->chatwin; print_time(ctx->history); + uint8_t *event; + switch (change) { case TOX_CHAT_CHANGE_PEER_ADD: + event = "has joined the room"; + wattron(ctx->history, COLOR_PAIR(GREEN)); wattron(ctx->history, A_BOLD); wprintw(ctx->history, "* %s", peername); wattroff(ctx->history, A_BOLD); - wprintw(ctx->history, " has joined the room\n"); + wprintw(ctx->history, " %s\n", event); wattroff(ctx->history, COLOR_PAIR(GREEN)); + + write_to_log(event, peername, ctx->log, true); break; + case TOX_CHAT_CHANGE_PEER_DEL: + event = "has left the room"; + wattron(ctx->history, A_BOLD); wprintw(ctx->history, "* %s", oldpeername); wattroff(ctx->history, A_BOLD); - wprintw(ctx->history, " has left the room\n"); + wprintw(ctx->history, " %s\n", event); if (groupchats[self->num].side_pos > 0) --groupchats[self->num].side_pos; + write_to_log(event, oldpeername, ctx->log, true); break; + case TOX_CHAT_CHANGE_PEER_NAME: wattron(ctx->history, COLOR_PAIR(MAGENTA)); wattron(ctx->history, A_BOLD); @@ -265,6 +311,10 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu wprintw(ctx->history, "%s\n", peername); wattroff(ctx->history, A_BOLD); wattroff(ctx->history, COLOR_PAIR(MAGENTA)); + + uint8_t tmp_event[TOXIC_MAX_NAME_LENGTH + 32]; + snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", peername); + write_to_log(tmp_event, oldpeername, ctx->log, true); break; } @@ -338,14 +388,14 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key) beep(); } - else if (key == KEY_HOME) { /* HOME key: Move cursor to beginning of line */ + else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */ if (ctx->pos > 0) { ctx->pos = 0; wmove(self->window, y2 - CURS_Y_OFFSET, 0); } } - else if (key == KEY_END) { /* END key: move cursor to end of line */ + else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */ if (ctx->pos != ctx->len) { ctx->pos = ctx->len; mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT-1)*x2)), y2, x2); @@ -458,24 +508,25 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key) wclear(ctx->linewin); wmove(self->window, y2 - CURS_Y_OFFSET, 0); wclrtobot(self->window); - bool close_win = false; if (!string_is_empty(line)) add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos); if (line[0] == '/') { - if (close_win = strcmp(line, "/close") == 0) { - set_active_window(0); - int groupnum = self->num; - delwin(ctx->linewin); - del_window(self); - close_groupchatwin(m, groupnum); - } else if (strcmp(line, "/help") == 0) - print_groupchat_help(ctx); - else if (strncmp(line, "/me ", strlen("/me ")) == 0) + if (strcmp(line, "/close") == 0) { + close_groupchat(self, m, self->num); + return; + } else if (strcmp(line, "/help") == 0) { + if (strcmp(line, "help global") == 0) + execute(ctx->history, self, m, "/help", GLOBAL_COMMAND_MODE); + else + print_groupchat_help(ctx); + + } else if (strncmp(line, "/me ", strlen("/me ")) == 0) { send_group_action(self, ctx, m, line + strlen("/me ")); - else + } else { execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE); + } } else if (!string_is_empty(line)) { if (tox_group_message_send(m, self->num, line, strlen(line) + 1) == -1) { wattron(ctx->history, COLOR_PAIR(RED)); @@ -484,10 +535,7 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key) } } - if (close_win) - free(ctx); - else - reset_buf(ctx->line, &ctx->pos, &ctx->len); + reset_buf(ctx->line, &ctx->pos, &ctx->len); } } @@ -555,7 +603,19 @@ static void groupchat_onInit(ToxWindow *self, Tox *m) ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x, y-CHATBOX_HEIGHT, 0); ctx->sidebar = subwin(self->window, y-CHATBOX_HEIGHT+1, SIDEBAR_WIDTH, 0, x-SIDEBAR_WIDTH); + ctx->log = malloc(sizeof(struct chatlog)); + + if (ctx->log == NULL) { + endwin(); + fprintf(stderr, "malloc() failed. Aborting...\n"); + exit(EXIT_FAILURE); + } + + memset(ctx->log, 0, sizeof(struct chatlog)); + print_groupchat_help(ctx); + execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE); + wmove(self->window, y-CURS_Y_OFFSET, 0); } @@ -565,6 +625,7 @@ ToxWindow new_group_chat(Tox *m, int groupnum) memset(&ret, 0, sizeof(ret)); ret.active = true; + ret.is_groupchat = true; ret.onKey = &groupchat_onKey; ret.onDraw = &groupchat_onDraw; diff --git a/src/groupchat.h b/src/groupchat.h index a3aa0a7..5b079d5 100644 --- a/src/groupchat.h +++ b/src/groupchat.h @@ -1,5 +1,23 @@ -/* - * Toxic -- Tox Curses Client +/* groupchat.h + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ #define SIDEBAR_WIDTH 16 @@ -14,5 +32,6 @@ typedef struct { uint8_t *oldpeer_names; } GroupChat; +void kill_groupchat_window(ToxWindow *self); int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum); ToxWindow new_group_chat(Tox *m, int groupnum); diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..0b41b43 --- /dev/null +++ b/src/log.c @@ -0,0 +1,124 @@ +/* log.c + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * + */ + +#include +#include +#include + +#include "configdir.h" +#include "toxic_windows.h" +#include "misc_tools.h" + +/* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */ +void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log) +{ + if (!log->log_on) + return; + + char *user_config_dir = get_user_config_dir(); + int path_len = strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(name); + + /* use first 4 digits of key as log ident. If no key use a timestamp */ + uint8_t ident[32]; + + if (key != NULL) { + path_len += (KEY_IDENT_DIGITS * 2 + 5); + + sprintf(&ident[0], "%02X", key[0] & 0xff); + sprintf(&ident[2], "%02X", key[2] & 0xff); + ident[KEY_IDENT_DIGITS*2+1] = '\0'; + } else { + struct tm *tminfo = get_time(); + snprintf(ident, sizeof(ident), + "%04d-%02d-%02d[%02d:%02d:%02d]", tminfo->tm_year+1900,tminfo->tm_mon+1, tminfo->tm_mday, + tminfo->tm_hour, tminfo->tm_min, tminfo->tm_sec); + path_len += strlen(ident) + 1; + } + + if (path_len > MAX_STR_SIZE) { + log->log_on = false; + return; + } + + uint8_t log_path[MAX_STR_SIZE]; + + snprintf(log_path, MAX_STR_SIZE, "%s%s%s-%s.log", + user_config_dir, CONFIGDIR, name, ident); + + log->file = fopen(log_path, "a"); + + if (log->file == NULL) { + log->log_on = false; + return; + } + + fprintf(log->file, "\n*** NEW SESSION ***\n\n"); + free(user_config_dir); +} + +void write_to_log(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event) +{ + if (!log->log_on) + return; + + if (log->file == NULL) { + log->log_on = false; + return; + } + + uint8_t name_frmt[TOXIC_MAX_NAME_LENGTH + 3]; + + if (event) + snprintf(name_frmt, sizeof(name_frmt), "* %s", name); + else + snprintf(name_frmt, sizeof(name_frmt), "%s:", name); + + struct tm *tminfo = get_time(); + fprintf(log->file,"%04d/%02d/%02d [%02d:%02d:%02d] %s %s\n", tminfo->tm_year+1900, + tminfo->tm_mon+1, tminfo->tm_mday, tminfo->tm_hour, tminfo->tm_min, + tminfo->tm_sec, name_frmt, msg); + + uint64_t curtime = (uint64_t) time(NULL); + + if (timed_out(log->lastwrite, curtime, LOG_FLUSH_LIMIT)) { + fflush(log->file); + log->lastwrite = curtime; + } +} + +void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log) +{ + log->log_on = true; + + if (log->file == NULL) + init_logging_session(name, key, log); +} + +void log_disable(struct chatlog *log) +{ + log->log_on = false; + + if (log->file != NULL) { + fclose(log->file); + log->file = NULL; + } +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..71c9748 --- /dev/null +++ b/src/log.h @@ -0,0 +1,33 @@ +/* log.h + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * + */ + +/* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */ +void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log); + +/* formats/writes line to log file */ +void write_to_log(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event); + +/* enables logging for specified log and creates/fetches file if necessary */ +void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log); + +/* disables logging for specified log and closes file */ +void log_disable(struct chatlog *log); diff --git a/src/main.c b/src/main.c index cefe2ed..57ae644 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,23 @@ -/* - * Toxic -- Tox Curses Client +/* main.c + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ #ifdef HAVE_CONFIG_H @@ -40,6 +58,7 @@ #include "friendlist.h" #include "prompt.h" #include "misc_tools.h" +#include "file_senders.h" #ifdef _SUPPORT_AUDIO #include "audio_call.h" @@ -51,11 +70,9 @@ /* Export for use in Callbacks */ char *DATA_FILE = NULL; -char *SRVLIST_FILE = NULL; ToxWindow *prompt = NULL; -FileSender file_senders[MAX_FILES]; -uint8_t max_file_senders_index; +static int f_loadfromfile; /* 1 if we want to load from/save the data file, 0 otherwise */ void on_window_resize(int sig) { @@ -103,8 +120,12 @@ static Tox *init_tox(int ipv4) int ipv6 = !ipv4; Tox *m = tox_new(ipv6); - if (TOX_ENABLE_IPV6_DEFAULT && m == NULL) { - fprintf(stderr, "IPv6 didn't initialize, trying IPv4 only\n"); + /* + * TOX_ENABLE_IPV6_DEFAULT is always 1. + * Checking it is redundant, this *should* be doing ipv4 fallback + */ + if (ipv6 && m == NULL) { + fprintf(stderr, "IPv6 didn't initialize, trying IPv4\n"); m = tox_new(0); } @@ -113,6 +134,7 @@ static Tox *init_tox(int ipv4) /* Callbacks */ tox_callback_connection_status(m, on_connectionchange, NULL); + tox_callback_typing_change(m, on_typing_change, NULL); tox_callback_friend_request(m, on_request, NULL); tox_callback_friend_message(m, on_message, NULL); tox_callback_name_change(m, on_nickchange, NULL); @@ -142,25 +164,26 @@ static Tox *init_tox(int ipv4) #define MINLINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */ #define MAXLINE 256 /* Approx max number of chars in a sever line (name + port + key) */ -#define MAXSERVERS 50 -#define SERVERLEN (MAXLINE - TOX_CLIENT_ID_SIZE - 7) +#define MAXNODES 50 +#define NODELEN (MAXLINE - TOX_CLIENT_ID_SIZE - 7) static int linecnt = 0; -static char servers[MAXSERVERS][SERVERLEN]; -static uint16_t ports[MAXSERVERS]; -static uint8_t keys[MAXSERVERS][TOX_CLIENT_ID_SIZE]; +static char nodes[MAXNODES][NODELEN]; +static uint16_t ports[MAXNODES]; +static uint8_t keys[MAXNODES][TOX_CLIENT_ID_SIZE]; -int serverlist_load(void) +static int nodelist_load(char *filename) { - FILE *fp = NULL; + if (!filename) + return 1; - fp = fopen(SRVLIST_FILE, "r"); + FILE *fp = fopen(filename, "r"); if (fp == NULL) return 1; char line[MAXLINE]; - while (fgets(line, sizeof(line), fp) && linecnt < MAXSERVERS) { + while (fgets(line, sizeof(line), fp) && linecnt < MAXNODES) { if (strlen(line) > MINLINE) { char *name = strtok(line, " "); char *port = strtok(NULL, " "); @@ -169,8 +192,8 @@ int serverlist_load(void) if (name == NULL || port == NULL || key_ascii == NULL) continue; - strncpy(servers[linecnt], name, SERVERLEN); - servers[linecnt][SERVERLEN - 1] = 0; + strncpy(nodes[linecnt], name, NODELEN); + nodes[linecnt][NODELEN - 1] = 0; ports[linecnt] = htons(atoi(port)); uint8_t *key_binary = hex_string_to_bin(key_ascii); @@ -190,51 +213,53 @@ int serverlist_load(void) return 0; } -int init_connection_helper(Tox *m, int linenumber) +int init_connection_helper(Tox *m, int line) { - return tox_bootstrap_from_address(m, servers[linenumber], TOX_ENABLE_IPV6_DEFAULT, - ports[linenumber], keys[linenumber]); + return tox_bootstrap_from_address(m, nodes[line], TOX_ENABLE_IPV6_DEFAULT, + ports[line], keys[line]); } -/* Connects to a random DHT server listed in the DHTservers file +/* Connects to a random DHT node listed in the DHTnodes file * * return codes: - * 1: failed to open server file - * 2: no line of sufficient length in server file - * 3: (old, removed) failed to split a selected line in the server file - * 4: failed to resolve name to IP - * 5: serverlist file contains no acceptable line + * 1: failed to open node file + * 2: no line of sufficient length in node file + * 3: failed to resolve name to IP + * 4: nodelist file contains no acceptable line */ -static int init_connection_serverlist_loaded = 0; +static bool srvlist_loaded = false; + +#define NUM_INIT_NODES 5 + int init_connection(Tox *m) { - if (linecnt > 0) /* already loaded serverlist */ - return init_connection_helper(m, rand() % linecnt) ? 0 : 4; + if (linecnt > 0) /* already loaded nodelist */ + return init_connection_helper(m, rand() % linecnt) ? 0 : 3; /* only once: - * - load the serverlist + * - load the nodelist * - connect to "everyone" inside */ - if (!init_connection_serverlist_loaded) { - init_connection_serverlist_loaded = 1; - int res = serverlist_load(); - if (res) + if (!srvlist_loaded) { + srvlist_loaded = true; + int res = nodelist_load(PACKAGE_DATADIR "/DHTnodes"); + + if (linecnt < 1) return res; - if (!linecnt) - return 4; + res = 3; + int i; + int n = MIN(NUM_INIT_NODES, linecnt); - res = 6; - int linenumber; - for(linenumber = 0; linenumber < linecnt; linenumber++) - if (init_connection_helper(m, linenumber)) + for(i = 0; i < n; ++i) + if (init_connection_helper(m, rand() % linecnt)) res = 0; return res; } - /* empty serverlist file */ - return 5; + /* empty nodelist file */ + return 4; } static void do_connection(Tox *m, ToxWindow *prompt) @@ -267,7 +292,14 @@ static void do_connection(Tox *m, ToxWindow *prompt) } } -int f_loadfromfile; +static void load_friendlist(Tox *m) +{ + int i; + uint32_t numfriends = tox_count_friendlist(m); + + for (i = 0; i < numfriends; ++i) + friendlist_onFriendAdded(NULL, m, i, false); +} /* * Store Messenger to given location @@ -347,12 +379,7 @@ static void load_data(Tox *m, char *path) } tox_load(m, buf, len); - - uint32_t i = 0; - uint8_t name[TOX_MAX_NAME_LENGTH]; - - while (tox_get_name(m, i, name) != -1) - on_friendadded(m, i++, false); + load_friendlist(m); free(buf); fclose(fd); @@ -367,88 +394,15 @@ static void load_data(Tox *m, char *path) } } -void close_file_sender(int i) -{ - fclose(file_senders[i].file); - memset(&file_senders[i], 0, sizeof(FileSender)); - - int j; - - for (j = max_file_senders_index; j > 0; --j) { - if (file_senders[j-1].active) - break; - } - - max_file_senders_index = j; -} - -static void do_file_senders(Tox *m) -{ - int i; - - for (i = 0; i < max_file_senders_index; ++i) { - if (!file_senders[i].active) - continue; - - uint8_t *pathname = file_senders[i].pathname; - uint8_t filenum = file_senders[i].filenum; - int friendnum = file_senders[i].friendnum; - FILE *fp = file_senders[i].file; - uint64_t current_time = (uint64_t) time(NULL); - - /* If file transfer has timed out kill transfer and send kill control */ - if (timed_out(file_senders[i].timestamp, current_time, TIMEOUT_FILESENDER)) { - ChatContext *ctx = file_senders[i].toxwin->chatwin; - - if (ctx != NULL) { - wprintw(ctx->history, "File transfer for '%s' timed out.\n", pathname); - alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true); - } - - tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_KILL, 0, 0); - close_file_sender(i); - continue; - } - - while (true) { - if (tox_file_send_data(m, friendnum, filenum, file_senders[i].nextpiece, - file_senders[i].piecelen) == -1) - break; - - file_senders[i].timestamp = current_time; - file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1, - tox_file_data_size(m, friendnum), fp); - - if (file_senders[i].piecelen == 0) { - ChatContext *ctx = file_senders[i].toxwin->chatwin; - - if (ctx != NULL) { - wprintw(ctx->history, "File '%s' successfuly sent.\n", pathname); - alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true); - } - - tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0); - close_file_sender(i); - break; - } - } - } -} - void exit_toxic(Tox *m) { store_data(m, DATA_FILE); - - int i; - - for (i = 0; i < max_file_senders_index; ++i) { - if (file_senders[i].active) - fclose(file_senders[i].file); - } - + close_all_file_senders(); + kill_all_windows(); + log_disable(prompt->promptbuf->log); free(DATA_FILE); - free(SRVLIST_FILE); free(prompt->stb); + free(prompt->promptbuf->log); free(prompt->promptbuf); tox_kill(m); endwin(); @@ -475,7 +429,7 @@ int main(int argc, char *argv[]) int i = 0; int f_use_ipv4 = 0; - // Make sure all written files are read/writeable only by the current user. + /* Make sure all written files are read/writeable only by the current user. */ umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); for (i = 0; i < argc; ++i) { @@ -504,7 +458,7 @@ int main(int argc, char *argv[]) if (DATA_FILE != NULL) { strcpy(DATA_FILE, user_config_dir); strcat(DATA_FILE, CONFIGDIR); - strcat(DATA_FILE, "data"); + strcat(DATA_FILE, "data"); } else { endwin(); fprintf(stderr, "malloc() failed. Aborting...\n"); @@ -513,21 +467,6 @@ int main(int argc, char *argv[]) } } - if (config_err) { - SRVLIST_FILE = strdup(PACKAGE_DATADIR "/DHTservers"); - } else { - SRVLIST_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("DHTservers") + 1); - if (SRVLIST_FILE != NULL) { - strcpy(SRVLIST_FILE, user_config_dir); - strcat(SRVLIST_FILE, CONFIGDIR); - strcat(SRVLIST_FILE, "DHTservers"); - } else { - endwin(); - fprintf(stderr, "malloc() failed. Aborting...\n"); - exit(EXIT_FAILURE); - } - } - free(user_config_dir); init_term(); @@ -577,7 +516,8 @@ int main(int argc, char *argv[]) prompt_init_statusbar(prompt, m); sort_friendlist_index(m); - while (true) do_toxic(m, prompt); + while (true) + do_toxic(m, prompt); #ifdef _SUPPORT_AUDIO terminate_audio(prompt, av); diff --git a/src/misc_tools.c b/src/misc_tools.c index 395a25b..be41bc1 100644 --- a/src/misc_tools.c +++ b/src/misc_tools.c @@ -1,5 +1,23 @@ -/* - * Toxic -- Tox Curses Client +/* misc_tools.c + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ #include @@ -52,8 +70,8 @@ void print_time(WINDOW *window) wattroff(window,COLOR_PAIR(BLUE)); } -/* Returns 1 if the string is empty, 0 otherwise */ -int string_is_empty(char *string) +/* Returns true if the string is empty, false otherwise */ +bool string_is_empty(char *string) { return string[0] == '\0'; } diff --git a/src/misc_tools.h b/src/misc_tools.h index a3f223c..af264b9 100644 --- a/src/misc_tools.h +++ b/src/misc_tools.h @@ -1,9 +1,27 @@ -/* - * Toxic -- Tox Curses Client +/* misc_tools.h + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ -// #define MIN(x, y) (((x) < (y)) ? (x) : (y)) - #define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) /* convert a hex string to binary */ unsigned char *hex_string_to_bin(char hex_string[]); @@ -14,8 +32,8 @@ struct tm *get_time(void); /* Prints the time to given window */ void print_time(WINDOW *window); -/* Returns 1 if the string is empty, 0 otherwise */ -int string_is_empty(char *string); +/* Returns true if the string is empty, false otherwise */ +bool string_is_empty(char *string); /* convert a multibyte string to a wide character string (must provide buffer) */ int char_to_wcs_buf(wchar_t *buf, const uint8_t *string, size_t n); diff --git a/src/prompt.c b/src/prompt.c index 6d0a238..bda06e0 100644 --- a/src/prompt.c +++ b/src/prompt.c @@ -1,5 +1,23 @@ -/* - * Toxic -- Tox Curses Client +/* prompt.c + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ #ifdef HAVE_CONFIG_H @@ -30,6 +48,7 @@ const uint8_t glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = { { "/groupchat" }, { "/help" }, { "/join" }, + { "/log" }, { "/myid" }, { "/nick" }, { "/note" }, @@ -154,12 +173,12 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key) beep(); } - else if (key == KEY_HOME) { /* HOME key: Move cursor to beginning of line */ + else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */ if (prt->pos != 0) prt->pos = 0; } - else if (key == KEY_END) { /* END key: move cursor to end of line */ + else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */ if (prt->pos != prt->len) prt->pos = prt->len; } @@ -341,6 +360,18 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) static void prompt_onInit(ToxWindow *self, Tox *m) { scrollok(self->window, true); + PromptBuf *prt = self->promptbuf; + + prt->log = malloc(sizeof(struct chatlog)); + + if (prt->log == NULL) { + endwin(); + fprintf(stderr, "malloc() failed. Aborting...\n"); + exit(EXIT_FAILURE); + } + + memset(prt->log, 0, sizeof(struct chatlog)); + execute(self->window, self, m, "/help", GLOBAL_COMMAND_MODE); wclrtoeol(self->window); } @@ -350,6 +381,7 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int friendnum , u if (friendnum < 0) return; + PromptBuf *prt = self->promptbuf; prep_prompt_win(); uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; @@ -363,22 +395,29 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int friendnum , u wprintw(self->window, "\n"); print_time(self->window); + uint8_t *msg; + if (status == 1) { + msg = "has come online"; wattron(self->window, COLOR_PAIR(GREEN)); wattron(self->window, A_BOLD); wprintw(self->window, "* %s ", nick); wattroff(self->window, A_BOLD); - wprintw(self->window, "has come online\n"); + wprintw(self->window, "%s\n", msg); wattroff(self->window, COLOR_PAIR(GREEN)); + write_to_log(msg, nick, prt->log, true); alert_window(self, WINDOW_ALERT_2, false); } else { + msg = "has gone offline"; wattron(self->window, COLOR_PAIR(RED)); wattron(self->window, A_BOLD); wprintw(self->window, "* %s ", nick); wattroff(self->window, A_BOLD); - wprintw(self->window, "has gone offline\n"); + wprintw(self->window, "%s\n", msg); wattroff(self->window, COLOR_PAIR(RED)); + + write_to_log(msg, nick, prt->log, true); } } @@ -386,16 +425,23 @@ static void prompt_onFriendRequest(ToxWindow *self, uint8_t *key, uint8_t *data, { // make sure message data is null-terminated data[length - 1] = 0; - + PromptBuf *prt = self->promptbuf; prep_prompt_win(); + wprintw(self->window, "\n"); print_time(self->window); - wprintw(self->window, "Friend request with the message: '%s'\n", data); + + uint8_t msg[MAX_STR_SIZE]; + snprintf(msg, sizeof(msg), "Friend request with the message '%s'\n", data); + wprintw(self->window, "%s", msg); + write_to_log(msg, "", prt->log, true); int n = add_friend_request(key); if (n == -1) { - wprintw(self->window, "Friend request queue is full. Discarding request.\n"); + uint8_t *errmsg = "Friend request queue is full. Discarding request.\n"; + wprintw(self->window, "%s", errmsg); + write_to_log(errmsg, "", prt->log, true); return; } @@ -441,6 +487,7 @@ ToxWindow new_prompt(void) memset(&ret, 0, sizeof(ret)); ret.active = true; + ret.is_prompt = true; ret.onKey = &prompt_onKey; ret.onDraw = &prompt_onDraw; diff --git a/src/prompt.h b/src/prompt.h index 7652503..51acd59 100644 --- a/src/prompt.h +++ b/src/prompt.h @@ -1,5 +1,23 @@ -/* - * Toxic -- Tox Curses Client +/* prompt.h + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ #ifndef PROMPT_H_UZYGWFFL @@ -7,7 +25,7 @@ #define X_OFST 2 /* offset to account for prompt char */ -#define AC_NUM_GLOB_COMMANDS 14 +#define AC_NUM_GLOB_COMMANDS 15 ToxWindow new_prompt(void); void prep_prompt_win(void); diff --git a/src/toxic_strings.c b/src/toxic_strings.c index 15cff71..efebc03 100644 --- a/src/toxic_strings.c +++ b/src/toxic_strings.c @@ -1,5 +1,23 @@ -/* - * Toxic -- Tox Curses Client +/* toxic_strings.c + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ #include @@ -120,19 +138,21 @@ void add_line_to_hist(const wchar_t *buf, size_t len, wchar_t (*hst)[MAX_STR_SIZ } /* copies history item at hst_pos to buf. Sets pos and len to the len of the history item. - hst_pos is decremented or incremented depending on key_dir. */ + hst_pos is decremented or incremented depending on key_dir. + + resets buffer if at end of history */ void fetch_hist_item(wchar_t *buf, size_t *pos, size_t *len, wchar_t (*hst)[MAX_STR_SIZE], int hst_tot, int *hst_pos, int key_dir) { if (key_dir == LN_HIST_MV_UP) { if (--(*hst_pos) < 0) { - ++(*hst_pos); + *hst_pos = 0; beep(); } } else { if (++(*hst_pos) >= hst_tot) { - --(*hst_pos); - beep(); + *hst_pos = hst_tot; + reset_buf(buf, pos, len); return; } } diff --git a/src/toxic_strings.h b/src/toxic_strings.h index f97dd20..3e28c0f 100644 --- a/src/toxic_strings.h +++ b/src/toxic_strings.h @@ -1,5 +1,23 @@ -/* - * Toxic -- Tox Curses Client +/* toxic_strings.h + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ /* Adds char to buffer at pos */ diff --git a/src/toxic_windows.h b/src/toxic_windows.h index 58fac0f..93618c6 100644 --- a/src/toxic_windows.h +++ b/src/toxic_windows.h @@ -1,5 +1,23 @@ -/* - * Toxic -- Tox Curses Client +/* toxic_windows.h + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * */ #ifndef _windows_h @@ -40,6 +58,8 @@ #define T_KEY_DISCARD 0x15 /* ctrl-u */ #define T_KEY_NEXT 0x10 /* ctrl-p */ #define T_KEY_PREV 0x0F /* ctrl-o */ +#define T_KEY_C_E 0x05 /* ctrl-e */ +#define T_KEY_C_A 0x01 /* ctrl-a */ /* Curses foreground colours (background is black) */ enum { @@ -88,6 +108,7 @@ struct ToxWindow { 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); + void(*onTypingChange)(ToxWindow *, Tox *, int, int); #ifdef _SUPPORT_AUDIO @@ -109,6 +130,11 @@ struct ToxWindow { bool active; int x; + /* window type identifiers */ + bool is_chat; + bool is_groupchat; + bool is_prompt; + bool alert0; bool alert1; bool alert2; @@ -131,6 +157,15 @@ struct StatusBar { bool is_online; }; +#define LOG_FLUSH_LIMIT 2 /* limits calls to fflush(logfile) to a max of one per LOG_FLUSH_LIMIT seconds */ + +struct chatlog { + FILE *file; + uint64_t lastwrite; + int pos; + bool log_on; /* specific to current chat window */ +}; + #define MAX_LINE_HIST 128 /* chat and groupchat window/buffer holder */ @@ -143,6 +178,10 @@ struct ChatContext { int hst_pos; int hst_tot; + bool self_is_typing; + + struct chatlog *log; + WINDOW *history; WINDOW *linewin; WINDOW *sidebar; @@ -153,6 +192,7 @@ struct PromptBuf { wchar_t line[MAX_STR_SIZE]; size_t pos; size_t len; + bool at_bottom; /* true if line end is at bottom of window */ int orig_y; /* y axis point of line origin */ bool scroll; /* used for prompt window hack to determine when to scroll down */ @@ -161,6 +201,7 @@ struct PromptBuf { int hst_pos; int hst_tot; + struct chatlog *log; WINDOW *linewin; }; @@ -184,6 +225,7 @@ typedef struct { struct FileReceiver { uint8_t filenames[MAX_FILES][MAX_STR_SIZE]; + FILE *files[MAX_FILES]; bool pending[MAX_FILES]; }; @@ -204,6 +246,7 @@ void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t ch void on_file_sendrequest(Tox *m, int friendnumber, uint8_t filenumber, uint64_t filesize, uint8_t *pathname, uint16_t pathname_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); +void on_typing_change(Tox *m, int friendnumber, int is_typing, void *userdata); ToxWindow *init_windows(Tox *m); void draw_active_window(Tox *m); @@ -211,4 +254,7 @@ int add_window(Tox *m, ToxWindow w); void del_window(ToxWindow *w); void set_active_window(int ch); int num_active_windows(void); + +/* cleans up all chat and groupchat windows (should only be called on shutdown) */ +void kill_all_windows(void); #endif diff --git a/src/windows.c b/src/windows.c index 8e068f5..e5dd40f 100644 --- a/src/windows.c +++ b/src/windows.c @@ -1,3 +1,25 @@ +/* windows.c + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * + */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -8,6 +30,7 @@ #include "friendlist.h" #include "prompt.h" #include "toxic_windows.h" +#include "groupchat.h" extern char *DATA_FILE; @@ -37,6 +60,16 @@ void on_connectionchange(Tox *m, int friendnumber, uint8_t status, void *userdat } } +void on_typing_change(Tox *m, int friendnumber, int is_typing, void *userdata) +{ + int i; + + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { + if (windows[i].onTypingChange != NULL) + windows[i].onTypingChange(&windows[i], m, friendnumber, is_typing); + } +} + void on_message(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata) { int i; @@ -377,3 +410,16 @@ int num_active_windows(void) return count; } + +/* destroys all chat and groupchat windows (should only be called on shutdown) */ +void kill_all_windows(void) +{ + int i; + + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { + if (windows[i].is_chat) + kill_chat_window(&windows[i]); + else if (windows[i].is_groupchat) + kill_groupchat_window(&windows[i]); + } +}