Moontalk server and client (provided by many parties)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

271 lines
5.6KB

  1. /* moontalk.c - @BAKE cc -O2 -std=gnu99 -Wall -Wextra -pedantic -Wno-format-truncation $@ -o $* \
  2. -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -lncurses -ltinfo -lreadline $+
  3. * Written by Emil.
  4. * Licensed under the GPLv3 only.
  5. *
  6. * TODO Add proper editing facilities
  7. * TODO Print sent messages
  8. */
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <unistd.h>
  13. #include <signal.h>
  14. #include <time.h>
  15. #include <sys/ioctl.h>
  16. #include <sys/types.h>
  17. #include <sys/socket.h>
  18. #include <netdb.h>
  19. #include <arpa/inet.h>
  20. #include <netinet/in.h>
  21. #include <ncurses.h>
  22. #include <readline/readline.h>
  23. #define SERV "7ks473deh6ggtwqsvbqdurepv5i6iblpbkx33b6cydon3ajph73sssad.onion"
  24. #define PORT "50000"
  25. #define BACKSPACE 263 /* ^G */
  26. #define TIMESTR "<%Y/%m/%d %H:%M:%S "
  27. #define TIMESTRMAX 21
  28. #define SENDMAX (1 << 8) - 1
  29. #define RECVMAX (1 << 11)
  30. #define streq(a,b) (!strcmp(a,b))
  31. #define strneq(a,b,c) (!memcmp(a,b,c))
  32. int g_y, g_x;
  33. int g_sockfd = -1;
  34. void init_screen(int x) {
  35. (void)x;
  36. signal(SIGWINCH, SIG_IGN);
  37. endwin();
  38. struct winsize w;
  39. ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
  40. g_y = w.ws_row;
  41. g_x = w.ws_col;
  42. initscr();
  43. cbreak();
  44. noecho();
  45. keypad(stdscr, ERR);
  46. nodelay(stdscr, TRUE);
  47. clear();
  48. signal(SIGWINCH, init_screen);
  49. }
  50. void free_connection(void) {
  51. close(g_sockfd);
  52. g_sockfd = -1;
  53. }
  54. /* always returns an accessible socket */
  55. int init_connection(char * serv, char * port) {
  56. int status, sockfd;
  57. struct addrinfo hints, * res;
  58. memset(&hints, 0, sizeof(struct addrinfo));
  59. hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
  60. hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
  61. if ((status = getaddrinfo(serv, port, &hints, &res)) != 0) {
  62. fprintf(stderr, "init_connection: %s\n", gai_strerror(status));
  63. exit(1);
  64. }
  65. if ((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1)
  66. { goto error; }
  67. if (connect(sockfd, res->ai_addr, res->ai_addrlen))
  68. { goto error; }
  69. freeaddrinfo(res);
  70. g_sockfd = sockfd;
  71. return sockfd;
  72. error:
  73. perror("init_connection");
  74. exit(1);
  75. __builtin_unreachable();
  76. }
  77. #if 0
  78. int getanonval(char * buf, size_t len) {
  79. return 0;
  80. }
  81. #endif
  82. int countlines(char * buf, size_t len) {
  83. size_t i = 0;
  84. for (buf += len - 1; *buf; --buf)
  85. { i += (*buf == '\n'); }
  86. return i;
  87. }
  88. size_t lastline(char * buf, size_t len) {
  89. size_t ret;
  90. char * start = buf;
  91. for (buf += len - 1; *buf; --buf) {
  92. ret = (*buf == '\n') ? buf - start : 0;
  93. if (ret) { return ret; }
  94. }
  95. return 0;
  96. }
  97. void clearline(int y) {
  98. int x = g_x;
  99. int oldy, oldx;
  100. getyx(stdscr, oldy, oldx);
  101. move(y, x);
  102. clrtoeol();
  103. move(oldy, oldx);
  104. }
  105. #define UPDATE_TIME() do { \
  106. t = time(NULL); \
  107. tm = gmtime(&t); \
  108. strftime(sendbuf, TIMESTRMAX, TIMESTR, tm); \
  109. sendbuf[TIMESTRMAX - 1] = ' '; } while (0)
  110. int main (int argc, char ** argv) {
  111. char * argv0 = argv[0];
  112. char * serv = SERV, * port = PORT, name[32] = "anonymous";
  113. int sockfd;
  114. while (++argv, --argc) {
  115. if (streq(*argv, "-help")) {
  116. printf("%s: HELP\n", argv0);
  117. return 1;
  118. }
  119. if (argc - 1)
  120. { --argc; ++argv; }
  121. else {
  122. printf("%s: %s requires argument\n", argv0, *argv);
  123. return 1;
  124. }
  125. if (streq(*(argv-1), "-serv")) {
  126. printf("serv: %s\n", *argv);
  127. serv = *argv;
  128. }
  129. else if (streq(*(argv-1), "-port")) {
  130. printf("port: %s\n", *argv);
  131. port = *argv;
  132. }
  133. else if (streq(*(argv-1), "-name")) {
  134. printf("name: %s\n", *argv);
  135. memset(name, 0, 31);
  136. strncpy(name, *argv, 31);
  137. }
  138. }
  139. /* atexit(void (*endwin)(void)); */
  140. init_screen(-1);
  141. printw("Connecting to %s:%s as %s\n", serv, port, name);
  142. printw("g_y: %d; g_x: %d\n", g_y, g_x);
  143. refresh();
  144. clear();
  145. sockfd = init_connection(serv, port);
  146. char raw[SENDMAX + RECVMAX];
  147. char * sendbuf = raw, * recvbuf = raw + SENDMAX;
  148. size_t sendminlen;
  149. size_t sendlen = sendminlen, recvlen = 0;
  150. time_t t;
  151. struct tm * tm;
  152. int frame = 15.;
  153. float interval = 1. / frame;
  154. int32_t ct = 0;
  155. int ch;
  156. int ret;
  157. sendminlen = TIMESTRMAX;
  158. UPDATE_TIME();
  159. memcpy(sendbuf + sendminlen, name, strlen(name));
  160. sendminlen += strlen(name);
  161. memcpy(sendbuf + sendminlen, "> ", 2);
  162. sendminlen += 2;
  163. sendlen = sendminlen;
  164. rl_bind_key('\t', rl_insert);
  165. rl_catch_signals = 0;
  166. rl_catch_sigwinch = 0;
  167. rl_prep_term_function = NULL;
  168. rl_deprep_term_function = NULL;
  169. rl_change_environment = 0;
  170. #if 0
  171. rl_getc_function = +[](FILE* ignore){
  172. input_available = false;
  173. return (int)input;
  174. };
  175. rl_input_available_hook = +[]{
  176. return input_available;
  177. };
  178. rl_redisplay_function = +[]{
  179. wmove(myWindow, 1, 1);
  180. wclrtoeol(myWindow);
  181. box(myWindow, 0, 0);
  182. waddstr(myWindow, rl_line_buffer);
  183. wrefresh(myWindow);
  184. return;
  185. };
  186. rl_callback_handler_install("", +[](char *line){
  187. wmove(stdscr, 0, 0);
  188. wclrtoeol(stdscr);
  189. addstr(line);
  190. refresh();
  191. return;
  192. });
  193. #endif
  194. while (1) {
  195. /* update */
  196. if (ct % frame == 0) {
  197. UPDATE_TIME();
  198. }
  199. if (ct % (frame * 2) == 0) {
  200. ret = recv(sockfd, recvbuf + recvlen, RECVMAX - recvlen, MSG_DONTWAIT);
  201. recvlen += (ret > 0) * ret;
  202. mvaddnstr(0, 0, recvbuf, recvlen);
  203. mvprintw(g_y - 2, 0, "recvlen %ld", recvlen);
  204. }
  205. /* send */
  206. ch = getch();
  207. if (ch == -1);
  208. else if (ch == '\n') {
  209. if (sendlen == sendminlen) { continue; }
  210. sendbuf[sendlen++] = '\n'; /* terminator */
  211. send(sockfd, sendbuf, sendlen, 0);
  212. memset(sendbuf + sendminlen, 0, sendlen - sendminlen);
  213. sendlen = sendminlen;
  214. clearline(g_y - 1);
  215. }
  216. else if (ch == BACKSPACE) {
  217. clearline(g_y - 1);
  218. if (sendlen - 1 >= sendminlen)
  219. { --sendlen; }
  220. }
  221. else if (ch > 31 && ch < 127) {
  222. if (sendlen + 1 < SENDMAX)
  223. { sendbuf[sendlen++] = ch; }
  224. }
  225. mvaddnstr(g_y - 1, 0, sendbuf, sendlen);
  226. move(g_y - 1, sendlen);
  227. /* sleep */
  228. ++ct;
  229. sleep(interval);
  230. refresh();
  231. }
  232. }