/* moontalk.c - @BAKE cc -O2 -std=gnu99 -Wall -Wextra -pedantic $@ -o $* -lncurses -ltinfo $+ * Written by Emil. * Licensed under the GPLv3 only. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #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); } }