diff --git a/testing/CMakeLists.txt b/testing/CMakeLists.txt index 12efc2f4..3085b0c5 100644 --- a/testing/CMakeLists.txt +++ b/testing/CMakeLists.txt @@ -10,4 +10,6 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/Lossless_UDP_testserver.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/Messenger_test.cmake) if(NOT WIN32) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/nTox.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/toxic.cmake) + endif() diff --git a/testing/cmake/toxic.cmake b/testing/cmake/toxic.cmake new file mode 100644 index 00000000..494dea90 --- /dev/null +++ b/testing/cmake/toxic.cmake @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.6.0) +project(toxic C) + +set(exe_name toxic/toxic) + +add_executable(${exe_name} + toxic/main.c toxic/prompt.c) + +target_link_libraries(${exe_name} curses) + +linkCoreLibraries(${exe_name}) diff --git a/testing/toxic/main.c b/testing/toxic/main.c new file mode 100644 index 00000000..0aad6777 --- /dev/null +++ b/testing/toxic/main.c @@ -0,0 +1,229 @@ +/* + * Toxic -- Tox Curses Client + */ + +#include +#include +#include +#include +#include + +#include "../../core/Messenger.h" +#include "../../core/network.h" + +#include "windows.h" + +extern ToxWindow new_prompt(); +extern int add_req(uint8_t* public_key); // XXX + +#define TOXWINDOWS_MAX_NUM 32 + +static ToxWindow windows[TOXWINDOWS_MAX_NUM]; +static int w_num; +static int w_active; +static ToxWindow* prompt; + +// CALLBACKS START +void on_request(uint8_t* public_key, uint8_t* data, uint16_t length) { + int n = add_req(public_key); + + wprintw(prompt->window, "\nFriend request.\nUse \"accept %d\" to accept it.\n", n); +} + +void on_message(int friendnumber, uint8_t* string, uint16_t length) { + wprintw(prompt->window, "\n(message) %d: %s!\n", friendnumber, string); +} + +void on_nickchange(int friendnumber, uint8_t* string, uint16_t length) { + wprintw(prompt->window, "\n(nick) %d: %s!\n", friendnumber, string); +} + +void on_statuschange(int friendnumber, uint8_t* string, uint16_t length) { + wprintw(prompt->window, "\n(status) %d: %s!\n", friendnumber, string); +} +// CALLBACKS END + +static void init_term() { + // Setup terminal. + initscr(); + cbreak(); + keypad(stdscr, 1); + noecho(); + timeout(2000); + + if(has_colors()) { + start_color(); + init_pair(1, COLOR_GREEN, COLOR_BLACK); + init_pair(2, COLOR_CYAN, COLOR_BLACK); + init_pair(3, COLOR_RED, COLOR_BLACK); + init_pair(4, COLOR_BLUE, COLOR_BLACK); + } + + refresh(); +} + +static void init_tox() { + // Init core. + initMessenger(); + + // Callbacks. + m_callback_friendrequest(on_request); + m_callback_friendmessage(on_message); + m_callback_namechange(on_nickchange); + m_callback_userstatus(on_statuschange); +} + +static int add_window(ToxWindow w) { + if(w_num == TOXWINDOWS_MAX_NUM) + return -1; + + if(LINES < 2) + return -1; + + w.window = newwin(LINES - 2, COLS, 0, 0); + + if(w.window == NULL) + return -1; + + windows[w_num++] = w; + w.onInit(&w); + + return w_num; +} + +static void init_windows() { + w_num = 0; + w_active = 0; + + if(add_window(new_prompt()) == -1) { + fprintf(stderr, "add_window() failed.\n"); + + endwin(); + exit(1); + } + + prompt = &windows[0]; +} + +static void do_tox() { + static bool dht_on = false; + + if(!dht_on && DHT_isconnected()) { + dht_on = true; + wprintw(prompt->window, "\nDHT connected!\n"); + } + else if(dht_on && !DHT_isconnected()) { + dht_on = false; + wprintw(prompt->window, "\nDHT disconnected!\n"); + } + + doMessenger(); +} + +static void load_data() { + FILE* fd; + size_t len; + uint8_t* buf; + + if((fd = fopen("data", "r")) != NULL) { + fseek(fd, 0, SEEK_END); + len = ftell(fd); + fseek(fd, 0, SEEK_SET); + + buf = malloc(len); + + if(buf == NULL) { + fprintf(stderr, "malloc() failed.\n"); + fclose(fd); + exit(1); + } + + if(fread(buf, len, 1, fd) != 1){ + fprintf(stderr, "fread() failed.\n"); + free(buf); + fclose(fd); + exit(1); + } + + Messenger_load(buf, len); + } + else { + len = Messenger_size(); + buf = malloc(len); + + if(buf == NULL) { + fprintf(stderr, "malloc() failed.\n"); + exit(1); + } + + Messenger_save(buf); + + fd = fopen("data", "w"); + if(fd == NULL) { + fprintf(stderr, "fopen() failed.\n"); + free(buf); + exit(1); + } + + if(fwrite(buf, len, 1, fd) != 1){ + fprintf(stderr, "fwrite() failed.\n"); + free(buf); + fclose(fd); + exit(1); + } + } + + free(buf); + fclose(fd); +} + +static void draw_bar() { + size_t i; + + attron(COLOR_PAIR(4)); + mvhline(LINES - 2, 0, '_', COLS); + attroff(COLOR_PAIR(4)); + + move(LINES - 1, 0); + + for(i=0; ionDraw(a); + draw_bar(); + + ch = getch(); + if(ch != ERR) { + a->onKey(a, ch); + } + + } + + return 0; +} + diff --git a/testing/toxic/prompt.c b/testing/toxic/prompt.c new file mode 100644 index 00000000..4a59cc7b --- /dev/null +++ b/testing/toxic/prompt.c @@ -0,0 +1,292 @@ +/* + * Toxic -- Tox Curses Client + */ + +#include +#include +#include +#include + +#include "../../core/Messenger.h" +#include "../../core/network.h" + +#include "windows.h" + +uint8_t pending_requests[256][CLIENT_ID_SIZE]; // XXX +uint8_t num_requests=0; // XXX + +// XXX: +int add_req(uint8_t* public_key) { + memcpy(pending_requests[num_requests], public_key, CLIENT_ID_SIZE); + ++num_requests; + + return num_requests-1; +} + +// XXX: FIX +unsigned char * hex_string_to_bin(char hex_string[]) +{ + size_t len = strlen(hex_string); + unsigned char *val = malloc(len); + char *pos = hex_string; + int i; + for(i = 0; i < len; ++i, pos+=2) + sscanf(pos,"%2hhx",&val[i]); + return val; +} + +static char prompt_buf[256] = {0}; +static int prompt_buf_pos=0; + +static void execute(ToxWindow* self, char* cmd) { + + // quit/exit: Exit program. + if(!strcmp(cmd, "quit") || !strcmp(cmd, "exit")) { + endwin(); + exit(0); + } + else if(!strncmp(cmd, "connect ", strlen("connect "))) { + char* ip; + char* port; + char* key; + IP_Port dht; + + ip = strchr(cmd, ' '); + if(ip == NULL) { + return; + } + + ip++; + + port = strchr(ip, ' '); + if(port == NULL) { + return; + } + + port[0] = 0; + port++; + + key = strchr(port, ' '); + if(key == NULL) { + return; + } + + key[0] = 0; + key++; + + if(atoi(port) == 0) { + return; + } + + wprintw(self->window, "ip=%s, port=%s, key=%s\n", ip, port, key); + + dht.port = htons(atoi(port)); + + int resolved_address = resolve_addr(ip); + if (resolved_address == -1) { + return; + } + + dht.ip.i = resolved_address; + DHT_bootstrap(dht, hex_string_to_bin(key)); + } + else if(!strncmp(cmd, "add ", strlen("add "))) { + char* id; + char* msg; + int num; + + id = strchr(cmd, ' '); + + if(id == NULL) { + return; + } + + id++; + + msg = strchr(id, ' '); + if(msg == NULL) { + return; + } + + msg[0] = 0; + msg++; + + num = m_addfriend((uint8_t*) id, (uint8_t*) msg, strlen(msg)+1); + wprintw(self->window, "Friend added as %d.\n", num); + } + else if(!strncmp(cmd, "status ", strlen("status "))) { + char* msg; + + msg = strchr(cmd, ' '); + if(msg == NULL) { + return; + } + + msg++; + m_set_userstatus((uint8_t*) msg, strlen(msg)+1); + wprintw(self->window, "Status set to: %s.\n", msg); + } + else if(!strncmp(cmd, "nick ", strlen("nick "))) { + char* nick; + + nick = strchr(cmd, ' '); + if(nick == NULL) { + return; + } + + nick++; + setname((uint8_t*) nick, strlen(nick)+1); + wprintw(self->window, "Nickname set to: %s.\n", nick); + } + else if(!strcmp(cmd, "myid")) { + // XXX: Clean this up + char idstring0[200]; + char idstring1[32][5]; + char idstring2[32][5]; + uint32_t i; + + for(i = 0; i < 32; i++) { + if(self_public_key[i] < 16) + strcpy(idstring1[i], "0"); + else + strcpy(idstring1[i], ""); + + sprintf(idstring2[i], "%hhX", self_public_key[i]); + } + + for (i=0; i<32; i++) { + strcat(idstring0, idstring1[i]); + strcat(idstring0, idstring2[i]); + } + + wprintw(self->window, "%s\n", idstring0); + } + else if(!strncmp(cmd, "accept ", strlen("accept "))) { + char* id; + int num; + + id = strchr(cmd, ' '); + if(id == NULL) { + return; + } + id++; + + num = atoi(id); + if(num >= num_requests) { + return; + } + + num = m_addfriend_norequest(pending_requests[num]); + wprintw(self->window, "Friend accepted as: %d.\n", num); + } + else if(!strncmp(cmd, "msg ", strlen("msg "))) { + char* id; + char* msg; + + id = strchr(cmd, ' '); + + if(id == NULL) { + return; + } + + id++; + + msg = strchr(id, ' '); + if(msg == NULL) { + return; + } + + msg[0] = 0; + msg++; + + if(m_sendmessage(atoi(id), (uint8_t*) msg, strlen(msg)+1) != 1) { + wprintw(self->window, "Error occurred while sending message.\n"); + } + else { + wprintw(self->window, "Message successfully sent.\n"); + } + } +} + +static void prompt_onKey(ToxWindow* self, int key) { + + // PRINTABLE characters: Add to line. + if(isprint(key)) { + + if(prompt_buf_pos == (sizeof(prompt_buf) - 1)) { + return; + } + + prompt_buf[prompt_buf_pos++] = key; + prompt_buf[prompt_buf_pos] = 0; + } + + // RETURN key: execute command. + else if(key == '\n') { + wprintw(self->window, "\n"); + execute(self, prompt_buf); + + prompt_buf_pos = 0; + prompt_buf[0] = 0; + } + + // BACKSPACE key: Remove one character from line. + else if(key == 0x107) { + + if(prompt_buf_pos != 0) { + prompt_buf[--prompt_buf_pos] = 0; + } + } +} + +static void prompt_onDraw(ToxWindow* self) { + int x, y; + + mvwin(self->window,0,0); + wresize(self->window, LINES-2, COLS); + + getyx(self->window, y, x); + (void) x; + + wattron(self->window, COLOR_PAIR(1)); + mvwprintw(self->window, y, 0, "# "); + wattroff(self->window, COLOR_PAIR(1)); + + mvwprintw(self->window, y, 2, "%s", prompt_buf); + wclrtoeol(self->window); + + wrefresh(self->window); +} + +static void print_usage(ToxWindow* self) { + wattron(self->window, COLOR_PAIR(2) | A_BOLD); + wprintw(self->window, "Usage:\n"); + wattroff(self->window, A_BOLD); + + wprintw(self->window, " connect : Connect to DHT server\n"); + wprintw(self->window, " add : Add friend\n"); + wprintw(self->window, " status : Set your status\n"); + wprintw(self->window, " nick : Set your nickname\n"); + wprintw(self->window, " accept : Accept friend request\n"); + wprintw(self->window, " myid : Print your ID\n"); + wprintw(self->window, " quit/exit : Exit program\n"); + wattroff(self->window, COLOR_PAIR(2)); +} + +static void prompt_onInit(ToxWindow* self) { + scrollok(self->window, 1); + + print_usage(self); + wclrtoeol(self->window); +} + +ToxWindow new_prompt() { + ToxWindow ret; + + ret.onKey = &prompt_onKey; + ret.onDraw = &prompt_onDraw; + ret.onInit = &prompt_onInit; + strcpy(ret.title, "[prompt]"); + + return ret; +} diff --git a/testing/toxic/windows.h b/testing/toxic/windows.h new file mode 100644 index 00000000..2e082ca9 --- /dev/null +++ b/testing/toxic/windows.h @@ -0,0 +1,10 @@ +typedef struct ToxWindow_ ToxWindow; + +struct ToxWindow_ { + void(*onKey)(ToxWindow*, int); + void(*onDraw)(ToxWindow*); + void(*onInit)(ToxWindow*); + char title[256]; + + WINDOW* window; +};