|
|
@@ -0,0 +1,165 @@ |
|
|
|
/* moontalk.c - @BAKE cc -O2 -std=gnu99 -Wall -Wextra -pedantic $@ -o $* -lncurses -ltinfo $+ @STOP */ |
|
|
|
|
|
|
|
#include <stdio.h> |
|
|
|
#include <stdlib.h> |
|
|
|
#include <string.h> |
|
|
|
#include <unistd.h> |
|
|
|
#include <signal.h> |
|
|
|
#include <time.h> |
|
|
|
|
|
|
|
#include <sys/ioctl.h> |
|
|
|
#include <sys/types.h> |
|
|
|
#include <sys/socket.h> |
|
|
|
#include <netdb.h> |
|
|
|
#include <arpa/inet.h> |
|
|
|
#include <netinet/in.h> |
|
|
|
|
|
|
|
#include <ncurses.h> |
|
|
|
|
|
|
|
#define SERV "7ks473deh6ggtwqsvbqdurepv5i6iblpbkx33b6cydon3ajph73sssad.onion" |
|
|
|
#define PORT "50000" |
|
|
|
|
|
|
|
#define streq(a,b) (!strcmp(a,b)) |
|
|
|
|
|
|
|
int g_row, g_col; |
|
|
|
|
|
|
|
void free_screen(void) { |
|
|
|
endwin(); |
|
|
|
} |
|
|
|
|
|
|
|
void init_screen(void) { |
|
|
|
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); |
|
|
|
} |
|
|
|
|
|
|
|
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 */ |
|
|
|
int init_connection(char * serv, char * port) { |
|
|
|
int status, sockfd; |
|
|
|
struct addrinfo hints, * res; |
|
|
|
|
|
|
|
memset(&hints, 0, sizeof(struct addrinfo)); |
|
|
|
hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6 |
|
|
|
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets |
|
|
|
|
|
|
|
if ((status = getaddrinfo(serv, port, &hints, &res)) != 0) { |
|
|
|
fprintf(stderr, "init_connection: %s\n", gai_strerror(status)); |
|
|
|
exit(1); |
|
|
|
} |
|
|
|
|
|
|
|
if ((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) |
|
|
|
{ goto error; } |
|
|
|
if (connect(sockfd, res->ai_addr, res->ai_addrlen)) |
|
|
|
{ goto error; } |
|
|
|
|
|
|
|
freeaddrinfo(res); |
|
|
|
|
|
|
|
g_sockfd = sockfd; |
|
|
|
return sockfd; |
|
|
|
error: |
|
|
|
perror("init_connection"); |
|
|
|
exit(1); |
|
|
|
__builtin_unreachable(); |
|
|
|
} |
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
int sockfd; |
|
|
|
|
|
|
|
while (++argv, --argc) { |
|
|
|
if (streq(*argv, "-help")) { |
|
|
|
printf("%s: HELP\n", argv0); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
if (argc - 1) |
|
|
|
{ --argc; ++argv; } |
|
|
|
else { |
|
|
|
printf("%s: %s requires argument\n", argv0, *argv); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
if (streq(*(argv-1), "-serv")) { |
|
|
|
printf("serv: %s\n", *argv); |
|
|
|
serv = *argv; |
|
|
|
} |
|
|
|
else if (streq(*(argv-1), "-port")) { |
|
|
|
printf("port: %s\n", *argv); |
|
|
|
} |
|
|
|
else if (streq(*(argv-1), "-name")) { |
|
|
|
printf("name: %s\n", *argv); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
atexit(free_screen); |
|
|
|
init_screen(); |
|
|
|
signal(SIGWINCH, handle_winch); |
|
|
|
printw("Connecting to %s:%s as %s\n", serv, port, name); |
|
|
|
refresh(); |
|
|
|
sockfd = init_connection(serv, port); |
|
|
|
float interval = 1 / 30; |
|
|
|
int ch; |
|
|
|
time_t t; |
|
|
|
struct tm * tm; |
|
|
|
while (1) { |
|
|
|
/* 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); |
|
|
|
} |
|
|
|
/* recv */ |
|
|
|
recvlen = recv(sockfd, recvbuf, recvmax, MSG_DONTWAIT); |
|
|
|
/* render */ |
|
|
|
clear(); |
|
|
|
mvaddnstr(0, 0, recvbuf, recvlen); |
|
|
|
mvaddnstr(g_row - 1, 0, sendbuf, sendlen); |
|
|
|
refresh(); |
|
|
|
/* sleep */ |
|
|
|
sleep(interval); |
|
|
|
} |
|
|
|
} |