Moontalk server and client (provided by many parties)
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

357 lignes
8.2KB

  1. /* moontalk.c - @BAKE cc -O2 -std=gnu99 -Wall -Wextra -Wpedantic -Wno-format-truncation $@ -o $* \
  2. -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -lncurses -ltinfo $+
  3. * Written by Emil.
  4. * Licensed under the GPLv3 only.
  5. */
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <unistd.h>
  10. #include <signal.h>
  11. #include <time.h>
  12. #include <errno.h>
  13. #include <ctype.h>
  14. #include <sys/ioctl.h>
  15. #include <sys/types.h>
  16. #include <sys/socket.h>
  17. #include <netdb.h>
  18. #include <arpa/inet.h>
  19. #include <netinet/in.h>
  20. #include <ncurses.h>
  21. #define SERV "7ks473deh6ggtwqsvbqdurepv5i6iblpbkx33b6cydon3ajph73sssad.onion"
  22. #define PORT "50000"
  23. #define NAME "anonymous"
  24. #define streq(a,b) (!strcmp(a,b))
  25. #define strneq(a,b,c) (!memcmp(a,b,c))
  26. int g_sockfd;
  27. #define g_y LINES
  28. #define g_x COLS
  29. #define HELP \
  30. "%s [options ...]\n" \
  31. "\n-serv SERVER Sets the server to connect to [default: " SERV "]" \
  32. "\n-port PORT Sets the port [default: " PORT "]" \
  33. "\n-name NAME Sets your display name [default: " NAME "]\n" \
  34. "\nControls\n" \
  35. "\nC-l Refreshes the screen" \
  36. "\nC-w Delete the previous word" \
  37. "\nC-c Close the client" \
  38. "\nUp/Down Scrolls all the way up/Scrolls down by a line\n"
  39. /* I know, and I don't care */
  40. #define TAB 9 /* */
  41. #define BACKSPACE 263 /* ^G */
  42. #define C_C 4 /* ... */
  43. #define C_D 4 /* quit */
  44. #define C_A 1 /* BOL */
  45. #define C_B 2 /* BOL */
  46. #define C_E 5 /* EOL */
  47. #define C_H 8 /* BACKSPACE */
  48. #define C_L 12 /* REFRESH */
  49. #define C_U 21 /* CLR TO BOL */
  50. #define C_W 23 /* DELETE PREVWORD */
  51. #define C_L 12 /* Signal full refresh */
  52. /****/
  53. void disconnect(void) {
  54. int sockfd = g_sockfd;
  55. if (sockfd > -1) { close(sockfd); }
  56. g_sockfd = -1;
  57. }
  58. /* always returns an accessible socket */
  59. int init_connection(char * serv, char * port) {
  60. int status, sockfd;
  61. struct addrinfo hints, * res;
  62. memset(&hints, 0, sizeof(struct addrinfo));
  63. hints.ai_family = AF_UNSPEC;
  64. hints.ai_socktype = SOCK_STREAM;
  65. if ((status = getaddrinfo(serv, port, &hints, &res)) != 0) {
  66. perror("init_connection");
  67. exit(1);
  68. }
  69. if ((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1)
  70. { goto error; }
  71. if (connect(sockfd, res->ai_addr, res->ai_addrlen))
  72. { goto error; }
  73. freeaddrinfo(res);
  74. g_sockfd = sockfd;
  75. return sockfd;
  76. error:
  77. if (sockfd > -1) { close(sockfd); }
  78. perror("init_connection");
  79. exit(1);
  80. __builtin_unreachable();
  81. }
  82. //
  83. void fillline(WINDOW * w, int y, int xstart, char c) {
  84. int i = xstart, x = g_x;
  85. for (; i < x; ++i)
  86. { mvwaddch(w,y,i,c); }
  87. }
  88. void clearline(WINDOW * w, int y) {
  89. wmove(w, y, 0);
  90. wclrtoeol(w);
  91. }
  92. void sanitize(char * buf, size_t rem) {
  93. char * base = buf;
  94. buf += rem;
  95. while (*buf && buf - base) {
  96. if (*buf < ' ' || *buf > '~') {
  97. if (*buf != '\n')
  98. { *buf = '!'; }
  99. }
  100. --buf;
  101. }
  102. }
  103. int main (int argc, char ** argv) {
  104. char * serv = SERV, * port = PORT, name[32] = NAME;
  105. int sockfd;
  106. {
  107. char * argv0 = argv[0];
  108. while (++argv, --argc) {
  109. if (streq(*argv, "-help")) {
  110. printf(HELP, argv0);
  111. exit(1);
  112. }
  113. if (argc - 1)
  114. { --argc; ++argv; }
  115. else {
  116. printf("%s: %s requires argument\n", argv0, *argv);
  117. return 1;
  118. }
  119. if (streq(*(argv-1), "-serv")) {
  120. serv = *argv;
  121. } else if (streq(*(argv-1), "-port")) {
  122. port = *argv;
  123. } else if (streq(*(argv-1), "-name")) {
  124. memset(name, 0, 31);
  125. strncpy(name, *argv, 31);
  126. }
  127. }
  128. printf("Connecting to %s:%s as %s\n", serv, port, name);
  129. atexit(disconnect);
  130. sockfd = init_connection(serv, port);
  131. }
  132. initscr();
  133. noecho();
  134. keypad(stdscr, ERR);
  135. nodelay(stdscr, TRUE);
  136. ESCDELAY = 0;
  137. curs_set(0);
  138. if (has_colors() && can_change_color()) {
  139. short bg, fg;
  140. /* leaks memory :( */
  141. start_color();
  142. for (bg = 0; bg < 8; ++bg) {
  143. for (fg = 0; fg < 8; ++fg) {
  144. init_pair(16 + fg + (bg * 8), fg, bg);
  145. }
  146. }
  147. }
  148. clear();
  149. #define WINCOUNT 3
  150. WINDOW * w[WINCOUNT];
  151. #define header w[0]
  152. #define body w[1]
  153. #define input w[2]
  154. #define SENDMAX (1 << 8) - 1
  155. #define RECVMAX 17663 /* (1 << 11) */
  156. char raw[SENDMAX + RECVMAX];
  157. char * sendbuf = raw, * recvbuf = raw + SENDMAX, * off = recvbuf;
  158. size_t sendminlen, sendlen, recvlen = 0, offlen = recvlen;
  159. size_t edit;
  160. #define TIMESTR "<%Y/%m/%d %H:%M:%S "
  161. #define TIMESTRMAX 21
  162. #define UPDATE_TIME()\
  163. do { \
  164. t = time(NULL); \
  165. tm = gmtime(&t); \
  166. strftime(sendbuf, TIMESTRMAX, TIMESTR, tm); \
  167. sendbuf[TIMESTRMAX - 1] = ' '; \
  168. } while (0)
  169. time_t t;
  170. struct tm * tm;
  171. useconds_t frame = 30;
  172. useconds_t interval = 1000000. / frame;
  173. int32_t ct;
  174. int ch, ret;
  175. size_t i;
  176. size_t namelen = strlen(name);
  177. edit = sendlen = sendminlen = TIMESTRMAX + namelen + 2;
  178. /* fill in the name */
  179. memcpy(sendbuf + TIMESTRMAX, name, namelen);
  180. memcpy(sendbuf + TIMESTRMAX + namelen, "> ", 2);
  181. int inputrefresh, bodyrefresh;
  182. hardrefresh:
  183. ct = 0;
  184. inputrefresh = bodyrefresh = 1;
  185. header = newwin(1, g_x, 0, 0);
  186. body = newwin(g_y - 4, g_x, 1, 0);
  187. input = newwin(3, g_x, g_y - 3, 0);
  188. fillline(header, 0, 0, '-');
  189. mvwprintw(header, 0, 2, " moontalk ");
  190. fillline(input, 0, 0, '-');
  191. while (1) {
  192. /* input */
  193. while ((ch = getch()) != -1) {
  194. if (ch == KEY_RESIZE || ch == C_L) {
  195. for (i = 0; i < WINCOUNT; ++i)
  196. { delwin(w[i]); }
  197. endwin();
  198. erase();
  199. refresh();
  200. clear();
  201. flushinp();
  202. goto hardrefresh;
  203. }
  204. else if ((ch > 31 && ch < 127)) {
  205. if (sendlen + 1 < SENDMAX)
  206. {
  207. memmove(sendbuf + edit + 1, sendbuf + edit, sendlen - edit);
  208. sendbuf[edit++] = ch; ++sendlen;
  209. }
  210. inputrefresh = 1;
  211. }
  212. else if (ch == '\n') {
  213. if (sendlen == sendminlen)
  214. { continue; }
  215. if (sendlen + 1 < SENDMAX)
  216. { sendbuf[sendlen++] = '\n'; }
  217. if (send(sockfd, sendbuf, sendlen, 0) > 0) {
  218. memcpy(recvbuf + recvlen, sendbuf, (sendlen + recvlen < RECVMAX) * sendlen);
  219. recvlen += sendlen;
  220. offlen += sendlen;
  221. } else {
  222. mvwprintw(input, 1, 0, "message failed: %s", strerror(errno));
  223. }
  224. bodyrefresh = inputrefresh = 1;
  225. edit = sendlen = sendminlen;
  226. }
  227. else if (ch == BACKSPACE || ch == C_H) {
  228. inputrefresh = 1;
  229. if (sendlen - 1 >= sendminlen && edit - 1 >= sendminlen)
  230. {
  231. memmove(sendbuf + edit - 1, sendbuf + edit, sendlen - edit);
  232. --sendlen; --edit;
  233. }
  234. inputrefresh = 1;
  235. }
  236. else if (ch == KEY_LEFT) {
  237. if (edit > sendminlen) { --edit; }
  238. }
  239. else if (ch == KEY_RIGHT) {
  240. if (edit < sendlen) { ++edit; }
  241. }
  242. else if (ch == KEY_DOWN) {
  243. mvwprintw(input, 1, 150, "scroll down %ld", offlen);
  244. while ((size_t)(off - recvbuf) < recvlen && *off != '\n') { ++off; }
  245. if (*off == '\n') { ++off; }
  246. wclear(body);
  247. bodyrefresh = 1;
  248. }
  249. else if (ch == KEY_UP) {
  250. mvwprintw(input, 1, 150, "scroll up %ld", offlen);
  251. if (off - 2 - recvbuf > 0) { off -= 2; }
  252. while (off - recvbuf > 0 && *off != '\n') { --off; }
  253. if (*off == '\n') { ++off; }
  254. bodyrefresh = 1;
  255. }
  256. else if (ch == C_W) {
  257. i = edit;
  258. while (i > sendminlen && isspace(sendbuf[i - 1])) { --i; }
  259. while (i > sendminlen && !isspace(sendbuf[i - 1])) { --i; }
  260. if (i == edit) { continue; }
  261. mvwprintw(input, 1, 200, "diff:%ld", sendlen - edit);
  262. /* memmove(sendbuf + i, sendbuf + edit, sendlen - edit); */
  263. /* sendlen -= edit; */
  264. /* edit = i; */
  265. /* mvwprintw(input, 1, 200, "i:%ld:%ld:sendl:%3ld", */
  266. /* i - sendminlen, (sendbuf + edit) - (sendbuf + i), sendlen - sendminlen); */
  267. inputrefresh = 1;
  268. }
  269. }
  270. /* update and rendering */
  271. if (inputrefresh) {
  272. clearline(input, 2);
  273. mvwaddnstr(input, 2, 0, sendbuf, sendlen);
  274. mvwchgat(input, 2, edit, 1, A_REVERSE, 0, NULL);
  275. }
  276. if (ct % frame == 0) {
  277. UPDATE_TIME();
  278. }
  279. if (ct % frame == 0 || bodyrefresh) {
  280. ret = recv(sockfd, recvbuf + recvlen, RECVMAX - recvlen, MSG_DONTWAIT);
  281. if (errno && errno != EAGAIN)
  282. { mvwaddstr(input, 1, 0, strerror(errno)); }
  283. if (bodyrefresh) {
  284. bodyrefresh = 0;
  285. if (!(ret > 0))
  286. goto _bodyrefresh;
  287. }
  288. if (ret > 0) {
  289. sanitize(recvbuf + recvlen, ret);
  290. if (ret + recvlen < RECVMAX)
  291. {
  292. recvlen += ret;
  293. offlen += ret;
  294. }
  295. _bodyrefresh:
  296. mvwprintw(input, 1, 50, "render ct:%d ret:%d", ct, ret);
  297. mvwaddnstr(body, 0,0, off, offlen);
  298. }
  299. }
  300. refresh();
  301. for (i = 0; i < WINCOUNT; ++i)
  302. { wnoutrefresh(w[i]); }
  303. doupdate();
  304. usleep(interval);
  305. ++ct;
  306. }
  307. endwin();
  308. }