From 3f4161ef04872ee249909fdb70bf3818338cab70 Mon Sep 17 00:00:00 2001 From: Emil Williams Date: Mon, 5 Feb 2024 21:20:06 +0000 Subject: [PATCH] updated clients --- client/moontalk-cli.c | 222 ++++++++++++++++++++++++++++++++++++-------------- client/moontalk.rb | 84 ++++++++++--------- client/moontalk.sh | 15 ++++ 3 files changed, 222 insertions(+), 99 deletions(-) create mode 100644 client/moontalk.sh diff --git a/client/moontalk-cli.c b/client/moontalk-cli.c index 7cdd855..57c1f94 100644 --- a/client/moontalk-cli.c +++ b/client/moontalk-cli.c @@ -1,7 +1,11 @@ -/* moontalk.c - @BAKE cc -O2 -std=gnu99 -Wall -Wextra -pedantic $@ -o $* -lncurses -ltinfo $+ - * Written by Emil. - * Licensed under the GPLv3 only. - */ +/* moontalk.c - @BAKE cc -O2 -std=gnu99 -Wall -Wextra -pedantic -Wno-format-truncation $@ -o $* \ + -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -lncurses -ltinfo -lreadline $+ + * Written by Emil. + * Licensed under the GPLv3 only. + * + * TODO Add proper editing facilities + * TODO Print sent messages + */ #include #include @@ -18,53 +22,47 @@ #include #include +#include #define SERV "7ks473deh6ggtwqsvbqdurepv5i6iblpbkx33b6cydon3ajph73sssad.onion" #define PORT "50000" +#define BACKSPACE 263 /* ^G */ + +#define TIMESTR "<%Y/%m/%d %H:%M:%S " +#define TIMESTRMAX 21 + +#define SENDMAX (1 << 8) - 1 +#define RECVMAX (1 << 11) + #define streq(a,b) (!strcmp(a,b)) +#define strneq(a,b,c) (!memcmp(a,b,c)) -int g_row, g_col; +int g_y, g_x; +int g_sockfd = -1; + +void init_screen(int x) { + (void)x; + signal(SIGWINCH, SIG_IGN); -void free_screen(void) { endwin(); -} - -void init_screen(void) { + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + g_y = w.ws_row; + g_x = w.ws_col; initscr(); cbreak(); noecho(); keypad(stdscr, ERR); nodelay(stdscr, TRUE); -} - -void handle_winch(int x) { - (void)x; - struct winsize w; - signal(SIGWINCH, SIG_IGN); - - endwin(); - init_screen(); - refresh(); clear(); - ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); - g_row = w.ws_row; - g_col = w.ws_col; - - signal(SIGWINCH, handle_winch); + signal(SIGWINCH, init_screen); } -int g_sockfd = -1; - void free_connection(void) { close(g_sockfd); g_sockfd = -1; - - /* the program should be at an end. If we're reconnecting, then at - the top level (main) we'd free, even though the main's sockfd is now - out-of-date, we can simply ignore that and take the result of socket - init */ } /* always returns an accessible socket */ @@ -96,15 +94,48 @@ error: __builtin_unreachable(); } +#if 0 +int getanonval(char * buf, size_t len) { + return 0; +} +#endif + +int countlines(char * buf, size_t len) { + size_t i = 0; + for (buf += len - 1; *buf; --buf) + { i += (*buf == '\n'); } + return i; +} + +size_t lastline(char * buf, size_t len) { + size_t ret; + char * start = buf; + for (buf += len - 1; *buf; --buf) { + ret = (*buf == '\n') ? buf - start : 0; + if (ret) { return ret; } + } + return 0; +} + +void clearline(int y) { + int x = g_x; + int oldy, oldx; + getyx(stdscr, oldy, oldx); + move(y, x); + clrtoeol(); + move(oldy, oldx); +} + +#define UPDATE_TIME() do { \ + t = time(NULL); \ + tm = gmtime(&t); \ + strftime(sendbuf, TIMESTRMAX, TIMESTR, tm); \ + sendbuf[TIMESTRMAX - 1] = ' '; } while (0) + + int main (int argc, char ** argv) { char * argv0 = argv[0]; - char * serv = SERV, * port = PORT, * name = "anonymous"; - - #define PREFIX_MAX 21 - char raw[(2 << 9) + (2 << 13)]; - char * sendbuf = raw + PREFIX_MAX, * recvbuf = raw + (2 << 9); - size_t sendlen = 0, sendmax = (2 << 9) - PREFIX_MAX, - recvlen = 0, recvmax = 2 << 13; + char * serv = SERV, * port = PORT, name[32] = "anonymous"; int sockfd; @@ -125,44 +156,115 @@ int main (int argc, char ** argv) { } else if (streq(*(argv-1), "-port")) { printf("port: %s\n", *argv); + port = *argv; } else if (streq(*(argv-1), "-name")) { printf("name: %s\n", *argv); + memset(name, 0, 31); + strncpy(name, *argv, 31); } } - atexit(free_screen); - init_screen(); - signal(SIGWINCH, handle_winch); + /* atexit(void (*endwin)(void)); */ + init_screen(-1); printw("Connecting to %s:%s as %s\n", serv, port, name); + printw("g_y: %d; g_x: %d\n", g_y, g_x); refresh(); + clear(); + sockfd = init_connection(serv, port); - float interval = 1 / 30; - int ch; + + char raw[SENDMAX + RECVMAX]; + char * sendbuf = raw, * recvbuf = raw + SENDMAX; + size_t sendminlen; + size_t sendlen = sendminlen, recvlen = 0; + time_t t; struct tm * tm; + int frame = 15.; + float interval = 1. / frame; + int32_t ct = 0; + int ch; + int ret; + + sendminlen = TIMESTRMAX; + UPDATE_TIME(); + + memcpy(sendbuf + sendminlen, name, strlen(name)); + sendminlen += strlen(name); + memcpy(sendbuf + sendminlen, "> ", 2); + sendminlen += 2; + sendlen = sendminlen; + + rl_bind_key('\t', rl_insert); + rl_catch_signals = 0; + rl_catch_sigwinch = 0; + rl_prep_term_function = NULL; + rl_deprep_term_function = NULL; + rl_change_environment = 0; + + #if 0 + rl_getc_function = +[](FILE* ignore){ + input_available = false; + return (int)input; + }; + rl_input_available_hook = +[]{ + return input_available; + }; + rl_redisplay_function = +[]{ + wmove(myWindow, 1, 1); + wclrtoeol(myWindow); + box(myWindow, 0, 0); + waddstr(myWindow, rl_line_buffer); + wrefresh(myWindow); + return; + }; + rl_callback_handler_install("", +[](char *line){ + wmove(stdscr, 0, 0); + wclrtoeol(stdscr); + addstr(line); + refresh(); + return; + }); + #endif + + while (1) { + /* update */ + if (ct % frame == 0) { + UPDATE_TIME(); + } + if (ct % (frame * 2) == 0) { + ret = recv(sockfd, recvbuf + recvlen, RECVMAX - recvlen, MSG_DONTWAIT); + recvlen += (ret > 0) * ret; + mvaddnstr(0, 0, recvbuf, recvlen); + mvprintw(g_y - 2, 0, "recvlen %ld", recvlen); + } /* send */ ch = getch(); - if (ch != -1 && ch != '\n') { - sendbuf[sendlen++] = ch; - } else if (ch == '\n') { - t = time(NULL); - tm = gmtime(&t); - strftime(sendbuf - PREFIX_MAX, PREFIX_MAX, "<%Y/%m/%d %H:%M:%S ", tm); - sendbuf[sendlen++] = '\n'; - send(sockfd, sendbuf - PREFIX_MAX, sendlen + PREFIX_MAX, 0); - sendlen = 0; - memset(sendbuf, 0, sendlen); + if (ch == -1); + else if (ch == '\n') { + if (sendlen == sendminlen) { continue; } + sendbuf[sendlen++] = '\n'; /* terminator */ + send(sockfd, sendbuf, sendlen, 0); + memset(sendbuf + sendminlen, 0, sendlen - sendminlen); + sendlen = sendminlen; + clearline(g_y - 1); } - /* recv */ - recvlen = recv(sockfd, recvbuf, recvmax, MSG_DONTWAIT); - /* render */ - clear(); - mvaddnstr(0, 0, recvbuf, recvlen); - mvaddnstr(g_row - 1, 0, sendbuf, sendlen); - refresh(); + else if (ch == BACKSPACE) { + clearline(g_y - 1); + if (sendlen - 1 >= sendminlen) + { --sendlen; } + } + else if (ch > 31 && ch < 127) { + if (sendlen + 1 < SENDMAX) + { sendbuf[sendlen++] = ch; } + } + mvaddnstr(g_y - 1, 0, sendbuf, sendlen); + move(g_y - 1, sendlen); /* sleep */ + ++ct; sleep(interval); + refresh(); } } diff --git a/client/moontalk.rb b/client/moontalk.rb index c7ad9b7..16c1e8c 100644 --- a/client/moontalk.rb +++ b/client/moontalk.rb @@ -73,49 +73,55 @@ def main end rescue end - socket = connect - begin - delay = 0 + loop do + begin + socket = connect + delay = 0 - h = { "BYE" => 1, "QUIT" => 1, "SAY" => 2, "CLEAR" => 3, "NICK" => 4, "RECONNECT" => 5, "DELAY" => 6 } + h = { "BYE" => 1, "QUIT" => 1, "SAY" => 2, "CLEAR" => 3, "NAME" => 4, "NICK" => 4, + "RC" => 5, "RECONNECT" => 5, "DELAY" => 6 } - update_prefix - while msg = Readline.readline(@prefix, true) - if not msg.empty? - msg.strip! - word = msg.split(/^([\w]*)$|^([\w]*) (.*)$/)[1..] - case h[word[0]] - when h["BYE"] - post(socket, "BYE") - exit 0 - when h["CLEAR"] - puts "\e[1;1H\e[2J" - when h["NICK"] - if not word[1].empty? - @name = word[1] - end - when h["RECONNECT"] - socket.close - socket = connect - when h["DELAY"] - delay = word[1].to_i - when h["SAY"] - if not word.length > 1 - word.push("") - end - post(socket, word[1]) - else - post(socket, msg) - end - end - - read_from(socket) - sleep delay update_prefix + while msg = Readline.readline(@prefix, true) + if not msg.empty? + msg.strip! + word = msg.split(/^\/([\w]*)$|^([\w]*) (.*)$/)[1..] + case h[word[0]] + when h["BYE"] + post(socket, "BYE") + exit 0 + when h["CLEAR"] + puts "\e[1;1H\e[2J" + when h["NICK"] + if not word[1].empty? + @name = word[1] + end + when h["RC"] + puts "reconnecting..." + socket.close + socket = connect + when h["DELAY"] + delay = word[1].to_i + when h["SAY"] + if not word.length > 1 + word.push("") + end + post(socket, word[1]) + else + post(socket, msg) + end + end + + read_from(socket) + sleep delay + update_prefix + end + rescue (Errno::ECONNREFUSED Errno::EPIPE) + puts "\n THE HOUSE IS ON FIRE." + ensure + puts "\nSTOPPING NOW." + exit 1 end - ensure - puts "\nSTOPPING NOW." - socket.close end end diff --git a/client/moontalk.sh b/client/moontalk.sh new file mode 100644 index 0000000..b72f0d7 --- /dev/null +++ b/client/moontalk.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# add this to your .bashrc + +# socat has a more reliable connection to the server than torify, it seems + +# if you want a more reliable client that isn't socat, run: + +# socat TCP-LISTEN:22222 SOCKS4a:localhost:7ks473deh6ggtwqsvbqdurepv5i6iblpbkx33b6cydon3ajph73sssad.onion:50000,socksport=9050 & +# nc localhost 22222 +# ruby moontalk.rb -serv localhost -port 22222 + +# or any other client with the server localhost and port 22222. + +# tail -1 moontalk.sh >> ~/.bashrc +alias socat_moontalk="socat STDIO SOCKS4A:localhost:7ks473deh6ggtwqsvbqdurepv5i6iblpbkx33b6cydon3ajph73sssad.onion:50000,socksport=9150"