Mercurial > hg > nnchat
comparison nnchat.c @ 28:512775f6b081
A refactored ncurses-based UI.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sat, 02 Aug 2008 11:57:09 +0300 |
parents | da721f94c60f |
children | a27ef0e359b9 |
comparison
equal
deleted
inserted
replaced
27:da721f94c60f | 28:512775f6b081 |
---|---|
1 #ifdef __WIN32 | |
2 #include <winsock2.h> | |
3 #else | |
4 #include <sys/socket.h> | 1 #include <sys/socket.h> |
5 #include <sys/types.h> | 2 #include <sys/types.h> |
6 #include <arpa/inet.h> | 3 #include <arpa/inet.h> |
7 #include <sys/time.h> | 4 #include <sys/time.h> |
8 #include <netdb.h> | 5 #include <netdb.h> |
9 #endif | |
10 | 6 |
11 #include <unistd.h> | 7 #include <unistd.h> |
12 #include <stdlib.h> | 8 #include <stdlib.h> |
13 #include <stdio.h> | 9 #include <stdio.h> |
14 #include "th_args.h" | 10 #include "th_args.h" |
15 #include "th_string.h" | 11 #include "th_string.h" |
16 #include <string.h> | 12 #include <string.h> |
17 #include <errno.h> | 13 #include <errno.h> |
18 #include <time.h> | 14 #include <time.h> |
19 | 15 #include <ncurses.h> |
20 | 16 |
17 | |
18 #define SET_BUFSIZE (4096) | |
21 #define SET_ALLOC_SIZE (128) | 19 #define SET_ALLOC_SIZE (128) |
22 #define SET_SELECT_USEC (100000) | 20 #define SET_DELAY (15) |
23 | 21 #define SET_DELAY_USEC (SET_DELAY * 1000) |
24 #define ANSI_BLACK "\x1b[0;30m" | |
25 #define ANSI_RED "\x1b[0;31m" | |
26 #define ANSI_GREEN "\x1b[0;32m" | |
27 #define ANSI_YELLOW "\x1b[0;33m" | |
28 #define ANSI_BLUE "\x1b[0;34m" | |
29 #define ANSI_MAGENTA "\x1b[0;35m" | |
30 #define ANSI_CYAN "\x1b[0;36m" | |
31 #define ANSI_WHITE "\x1b[0;37m" | |
32 | |
33 #define ANSI_L_BLACK "\x1b[0;1;30m" | |
34 #define ANSI_L_RED "\x1b[0;1;31m" | |
35 #define ANSI_L_GREEN "\x1b[0;1;32m" | |
36 #define ANSI_L_YELLOW "\x1b[0;1;33m" | |
37 #define ANSI_L_BLUE "\x1b[0;1;34m" | |
38 #define ANSI_L_MAGENTA "\x1b[0;1;35m" | |
39 #define ANSI_L_CYAN "\x1b[0;1;36m" | |
40 #define ANSI_L_WHITE "\x1b[0;1;37m" | |
41 | |
42 #define ANSI_END "\x1b[0m" | |
43 | |
44 | 22 |
45 /* Options | 23 /* Options |
46 */ | 24 */ |
47 int optPort = 8005; | 25 int optPort = 8005; |
48 int optUserColor = 0x408060; | 26 int optUserColor = 0x408060; |
52 *optPassword = NULL, | 30 *optPassword = NULL, |
53 *optLogFilename = NULL, | 31 *optLogFilename = NULL, |
54 *setTarget = NULL; | 32 *setTarget = NULL; |
55 BOOL optDaemon = FALSE; | 33 BOOL optDaemon = FALSE; |
56 FILE *optLogFile = NULL; | 34 FILE *optLogFile = NULL; |
57 | 35 WINDOW *mainWin = NULL, |
36 *statusWin = NULL, | |
37 *editWin = NULL; | |
38 | |
39 BOOL setInsertMode = TRUE; | |
58 | 40 |
59 /* Arguments | 41 /* Arguments |
60 */ | 42 */ |
61 optarg_t optList[] = { | 43 optarg_t optList[] = { |
62 { 0, '?', "help", "Show this help", OPT_NONE }, | 44 { 0, '?', "help", "Show this help", OPT_NONE }, |
76 th_args_help(stdout, optList, optListN, th_prog_name, | 58 th_args_help(stdout, optList, optListN, th_prog_name, |
77 "[options] <username> <password>"); | 59 "[options] <username> <password>"); |
78 } | 60 } |
79 | 61 |
80 | 62 |
81 #ifdef __WIN32 | |
82 /* Just a bogus stub | |
83 */ | |
84 const char *hstrerror(int err) | |
85 { | |
86 (void) err; | |
87 | |
88 return "???"; | |
89 } | |
90 #endif | |
91 | |
92 | |
93 int getColor(char *str) | 63 int getColor(char *str) |
94 { | 64 { |
95 char *p = str; | 65 char *p = str; |
96 int len, val = 0; | 66 int len, val = 0; |
97 | 67 |
169 | 139 |
170 return TRUE; | 140 return TRUE; |
171 } | 141 } |
172 | 142 |
173 | 143 |
144 typedef struct { | |
145 ssize_t pos, len; | |
146 char data[SET_BUFSIZE]; | |
147 } editbuf_t; | |
148 | |
149 | |
150 int writeBuf(editbuf_t *buf, ssize_t pos, int ch) | |
151 { | |
152 /* Check arguments */ | |
153 if (buf->len+1 >= SET_BUFSIZE) return -3; | |
154 | |
155 if (pos < 0) | |
156 return -1; | |
157 else if (pos >= buf->len) { | |
158 buf->data[buf->len++] = ch; | |
159 } else { | |
160 buf->data[pos] = ch; | |
161 } | |
162 return 0; | |
163 } | |
164 | |
165 int insertBuf(editbuf_t *buf, ssize_t pos, int ch) | |
166 { | |
167 /* Check arguments */ | |
168 if (buf->len+1 >= SET_BUFSIZE) return -3; | |
169 | |
170 if (pos < 0) | |
171 return -1; | |
172 else if (pos >= buf->len) { | |
173 buf->data[buf->len] = ch; | |
174 } else { | |
175 memmove(&(buf->data[pos+1]), &(buf->data[pos]), buf->len - pos + 1); | |
176 buf->data[pos] = ch; | |
177 } | |
178 buf->len++; | |
179 return 0; | |
180 } | |
181 | |
182 int deleteBuf(editbuf_t *buf, ssize_t pos) | |
183 { | |
184 /* Check arguments */ | |
185 if (pos < 0) | |
186 return -1; | |
187 else if (pos < buf->len) { | |
188 memmove(&(buf->data[pos]), &(buf->data[pos+1]), buf->len - pos); | |
189 buf->len--; | |
190 return 0; | |
191 } else | |
192 return -2; | |
193 } | |
194 | |
195 void clearBuf(editbuf_t *buf) | |
196 { | |
197 buf->len = 0; | |
198 buf->pos = 0; | |
199 } | |
200 | |
201 void setBufPos(editbuf_t *buf, ssize_t pos) | |
202 { | |
203 /* Check arguments */ | |
204 if (pos < 0) | |
205 buf->pos = 0; | |
206 else if (pos >= buf->len) | |
207 buf->pos = buf->len; | |
208 else | |
209 buf->pos = pos; | |
210 } | |
211 | |
212 void updateStatus(void) | |
213 { | |
214 char tmpStr[128] = ""; | |
215 time_t timeStamp; | |
216 struct tm *tmpTime;; | |
217 | |
218 timeStamp = time(NULL); | |
219 if ((tmpTime = localtime(&timeStamp)) != NULL) { | |
220 strftime(tmpStr, sizeof(tmpStr), "%H:%M:%S", tmpTime); | |
221 } | |
222 | |
223 wbkgdset(statusWin, 0x0d00); | |
224 werase(statusWin); | |
225 | |
226 wattrset(statusWin, A_BOLD); | |
227 mvwaddstr(statusWin, 0, 1, tmpStr); | |
228 waddstr(statusWin, " | "); | |
229 wattrset(statusWin, A_BOLD | COLOR_PAIR(11)); | |
230 waddstr(statusWin, setInsertMode ? "INS" : "DEL"); | |
231 | |
232 wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); | |
233 waddstr(statusWin, " | Private target: "); | |
234 | |
235 wattrset(statusWin, A_BOLD | COLOR_PAIR(11)); | |
236 waddstr(statusWin, setTarget != NULL ? setTarget : "--"); | |
237 | |
238 wrefresh(statusWin); | |
239 } | |
240 | |
241 void printEditBuf(editbuf_t *buf) | |
242 { | |
243 buf->data[buf->len] = 0; | |
244 werase(editWin); | |
245 if (buf->pos < buf->len) { | |
246 mvwaddnstr(editWin, 0, 0, buf->data, buf->pos); | |
247 wattrset(editWin, A_REVERSE); | |
248 waddch(editWin, buf->data[buf->pos]); | |
249 wattrset(editWin, A_NORMAL); | |
250 waddnstr(editWin, buf->data + buf->pos + 1, buf->len - buf->pos - 1); | |
251 } else { | |
252 mvwaddnstr(editWin, 0, 0, buf->data, buf->len); | |
253 wattrset(editWin, A_REVERSE); | |
254 waddch(editWin, ' '); | |
255 wattrset(editWin, A_NORMAL); | |
256 } | |
257 wrefresh(editWin); | |
258 } | |
259 | |
174 int openConnection(struct in_addr *addr, int port) | 260 int openConnection(struct in_addr *addr, int port) |
175 { | 261 { |
176 struct sockaddr_in tmpAddr; | 262 struct sockaddr_in tmpAddr; |
177 int sock = -1; | 263 int sock = -1; |
178 | 264 |
200 | 286 |
201 | 287 |
202 void closeConnection(int sock) | 288 void closeConnection(int sock) |
203 { | 289 { |
204 if (sock >= 0) { | 290 if (sock >= 0) { |
205 #ifdef __WIN32 | |
206 closesocket(sock); | |
207 #else | |
208 close(sock); | 291 close(sock); |
209 #endif | |
210 } | 292 } |
211 } | 293 } |
212 | 294 |
213 | 295 |
214 BOOL sendToSocket(int sock, char *buf, const size_t bufLen) | 296 BOOL sendToSocket(int sock, char *buf, const size_t bufLen) |
224 bufPtr += bufSent; | 306 bufPtr += bufSent; |
225 } | 307 } |
226 return TRUE; | 308 return TRUE; |
227 } | 309 } |
228 | 310 |
229 | 311 int printWin(WINDOW *win, const char *fmt) |
230 void printMsg(char *fmt, char *fmt2, ...) | 312 { |
231 { | 313 const char *s = fmt; |
232 char tmpStr[64] = ""; | 314 int col = 0; |
315 | |
316 while (*s) { | |
317 if (*s == '½') { | |
318 int val = 0; | |
319 s++; | |
320 if (*s == '½') { | |
321 waddch(win, *s | col); | |
322 s++; | |
323 } else { | |
324 while (*s && isdigit(*s)) { | |
325 val *= 10; | |
326 val += (*s - '0'); | |
327 s++; | |
328 } | |
329 if (*s != '½') return -1; | |
330 s++; | |
331 | |
332 if (val < 9) { | |
333 col = A_DIM | COLOR_PAIR(val); | |
334 } else if (val < 30) { | |
335 col = A_BOLD | COLOR_PAIR(val - 9); | |
336 } | |
337 } | |
338 } else { | |
339 waddch(win, *s | col); | |
340 s++; | |
341 } | |
342 } | |
343 return 0; | |
344 } | |
345 | |
346 int printFile(FILE *outFile, const char *fmt) | |
347 { | |
348 const char *s = fmt; | |
349 | |
350 while (*s) { | |
351 if (*s == '½') { | |
352 s++; | |
353 if (*s == '½') { | |
354 s++; | |
355 } else { | |
356 while (*s && isdigit(*s)) s++; | |
357 if (*s != '½') return -1; | |
358 s++; | |
359 } | |
360 } else { | |
361 fputc(*s, outFile); | |
362 s++; | |
363 } | |
364 } | |
365 | |
366 return 0; | |
367 } | |
368 | |
369 void printMsg(char *fmt, ...) | |
370 { | |
371 char tmpStr[128] = "", buf[8192]; | |
233 va_list ap; | 372 va_list ap; |
234 time_t timeStamp; | 373 time_t timeStamp; |
235 struct tm *tmpTime;; | 374 struct tm *tmpTime;; |
236 | 375 |
237 timeStamp = time(NULL); | 376 timeStamp = time(NULL); |
238 if ((tmpTime = localtime(&timeStamp)) != NULL) { | 377 if ((tmpTime = localtime(&timeStamp)) != NULL) { |
239 strftime(tmpStr, sizeof(tmpStr), "%H:%M:%S", tmpTime); | 378 strftime(tmpStr, sizeof(tmpStr), "½17½[½11½%H:%M:%S½17½]½0½ ", tmpTime); |
240 } | 379 } |
380 | |
381 va_start(ap, fmt); | |
382 vsnprintf(buf, sizeof(buf), fmt, ap); | |
383 va_end(ap); | |
241 | 384 |
242 if (optLogFile) { | 385 if (optLogFile) { |
243 fprintf(optLogFile, "[%s] ", tmpStr); | 386 printFile(optLogFile, tmpStr); |
244 va_start(ap, fmt2); | 387 printFile(optLogFile, buf); |
245 vfprintf(optLogFile, fmt, ap); | |
246 va_end(ap); | |
247 fflush(optLogFile); | 388 fflush(optLogFile); |
248 } | 389 } |
249 | 390 |
250 if (!optDaemon) { | 391 if (!optDaemon) { |
251 fprintf(stdout, ANSI_L_BLACK "[" ANSI_L_GREEN "%s" ANSI_L_BLACK "]" ANSI_END " ", tmpStr); | 392 printWin(mainWin, tmpStr); |
252 va_start(ap, fmt2); | 393 printWin(mainWin, buf); |
253 vfprintf(stdout, fmt2, ap); | 394 wrefresh(mainWin); |
254 va_end(ap); | |
255 fflush(stdout); | |
256 } | 395 } |
257 } | 396 } |
258 | 397 |
259 | 398 |
260 BOOL bufRealloc(char **buf, size_t *size, size_t add) | 399 BOOL bufRealloc(char **buf, size_t *size, size_t add) |
360 case '+': | 499 case '+': |
361 PUSHCHAR(' '); | 500 PUSHCHAR(' '); |
362 s++; | 501 s++; |
363 break; | 502 break; |
364 | 503 |
504 case '½': | |
505 /* Escape these .. */ | |
506 PUSHCHAR('½'); | |
507 PUSHCHAR('½'); | |
508 s++; | |
509 break; | |
510 | |
365 case '\r': | 511 case '\r': |
366 PUSHCHAR(' '); | 512 PUSHCHAR(' '); |
367 s++; | 513 s++; |
368 break; | 514 break; |
369 | 515 |
548 | 694 |
549 if (*s == '/') { | 695 if (*s == '/') { |
550 if (!strncmp(s, "/BPRV", 5)) { | 696 if (!strncmp(s, "/BPRV", 5)) { |
551 t = stripTags(s + 2); | 697 t = stripTags(s + 2); |
552 h = decodeStr2(t); | 698 h = decodeStr2(t); |
553 printMsg("%s\n", ANSI_YELLOW "%s" ANSI_END "\n", h); | 699 printMsg("%s\n", h); |
554 } else { | 700 } else { |
555 t = stripTags(s + 1); | 701 t = stripTags(s + 1); |
556 h = decodeStr2(t); | 702 h = decodeStr2(t); |
557 printMsg("* %s\n", ANSI_L_YELLOW "* %s" ANSI_END "\n", h); | 703 printMsg("* %s\n", h); |
558 } | 704 } |
559 th_free(h); | 705 th_free(h); |
560 th_free(t); | 706 th_free(t); |
561 } else { | 707 } else { |
562 t = stripTags(s); | 708 t = stripTags(s); |
563 h = decodeStr2(t); | 709 h = decodeStr2(t); |
564 printMsg("<%s> %s\n", ANSI_MAGENTA "<" ANSI_L_CYAN "%s" ANSI_MAGENTA ">" ANSI_END " %s\n", p, h); | 710 printMsg("½5½<½15½%s½5½>½0½ %s\n", p, h); |
565 th_free(h); | 711 th_free(h); |
566 th_free(t); | 712 th_free(t); |
567 } | 713 } |
568 | 714 |
569 th_free(s); | 715 th_free(s); |
582 if ((tmpTime = localtime(&timeStamp)) != NULL) { | 728 if ((tmpTime = localtime(&timeStamp)) != NULL) { |
583 strftime(tmpStr, sizeof(tmpStr), "%c", tmpTime); | 729 strftime(tmpStr, sizeof(tmpStr), "%c", tmpTime); |
584 } | 730 } |
585 | 731 |
586 if (!strncmp(str, "FAILURE", 7)) { | 732 if (!strncmp(str, "FAILURE", 7)) { |
587 printMsg("Login failure - %s\n", "Login failure - %s\n", tmpStr); | 733 printMsg("½1½Login failure½0½ - ½3½%s½0½\n", tmpStr); |
588 return -2; | 734 return -2; |
589 } else if (!strncmp(str, "SUCCESS", 7)) { | 735 } else if (!strncmp(str, "SUCCESS", 7)) { |
590 printMsg("Login success - %s\n", "Login success - %s\n", tmpStr); | 736 printMsg("½2½Login success½0½ - ½3½%s½0½\n", tmpStr); |
591 sendUserMsg(sock, optUserName2, "%%2FRequestUserList"); | 737 sendUserMsg(sock, optUserName2, "%%2FRequestUserList"); |
592 return 0; | 738 return 0; |
593 } else | 739 } else |
594 return 1; | 740 return 1; |
595 } | 741 } |
605 *s = 0; | 751 *s = 0; |
606 | 752 |
607 p = decodeStr1(str); | 753 p = decodeStr1(str); |
608 if (!p) return -1; | 754 if (!p) return -1; |
609 | 755 |
610 printMsg("! %s ADDED.\n", "! " ANSI_GREEN "%s" ANSI_END " ADDED.\n", p); | 756 printMsg("! ½3½%s½0½ ½2½ADDED.½0½\n", p); |
611 th_free(p); | 757 th_free(p); |
612 return 0; | 758 return 0; |
613 } | 759 } |
614 | 760 |
615 | 761 |
623 *s = 0; | 769 *s = 0; |
624 | 770 |
625 p = decodeStr1(str); | 771 p = decodeStr1(str); |
626 if (!p) return -1; | 772 if (!p) return -1; |
627 | 773 |
628 printMsg("! %s DELETED.\n", "! " ANSI_RED "%s" ANSI_END " DELETED.\n", p); | 774 printMsg("! ½3½%s½0½ ½1½DELETED.½0½\n", p); |
629 th_free(p); | 775 th_free(p); |
630 return 0; | 776 return 0; |
631 } | 777 } |
632 | 778 |
633 | 779 |
679 /* Trim right */ | 825 /* Trim right */ |
680 buf[--bufLen] = 0; | 826 buf[--bufLen] = 0; |
681 while (bufLen > 0 && (buf[bufLen] == '\n' || buf[bufLen] == '\r' || th_isspace(buf[bufLen]))) | 827 while (bufLen > 0 && (buf[bufLen] == '\n' || buf[bufLen] == '\r' || th_isspace(buf[bufLen]))) |
682 buf[bufLen--] = 0; | 828 buf[bufLen--] = 0; |
683 | 829 |
684 //fprintf(stderr, "'%s'\n", buf); fflush(stderr); | |
685 | |
686 /* Check command */ | 830 /* Check command */ |
687 if (*buf == 0) { | 831 if (*buf == 0) { |
688 return 1; | 832 return 1; |
689 } else if (!strncmp(buf, "/color ", 7)) { | 833 } else if (!strncmp(buf, "/color ", 7)) { |
690 if ((optUserColor = getColor(buf+7)) < 0) { | 834 if ((optUserColor = getColor(buf+7)) < 0) { |
691 printMsg("Invalid color value '%s'\n", "Invalid color value '%s'\n", buf+7); | 835 printMsg("Invalid color value '%s'\n", buf+7); |
692 return 1; | 836 return 1; |
693 } | 837 } |
694 printMsg("Setting color to #%06x\n", "Setting color to #%06x\n", optUserColor); | 838 printMsg("Setting color to #%06x\n", optUserColor); |
695 sendUserMsg(sock, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); | 839 sendUserMsg(sock, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); |
696 return 0; | 840 return 0; |
697 } else if (!strncmp(buf, "/fake ", 6)) { | 841 } else if (!strncmp(buf, "/flood ", 7)) { |
698 printMsg("Sending /%s\n", "Sending /%s\n", buf+6); | 842 int i; |
843 | |
844 snprintf(tmpBuf, sizeof(tmpBuf), "/prv -to %s -msg . .", | |
845 buf+7); | |
846 | |
699 tmpStr = encodeStr2(tmpBuf); | 847 tmpStr = encodeStr2(tmpBuf); |
700 if (!tmpStr) return -2; | 848 if (!tmpStr) return -2; |
701 tmpStr2 = encodeStr1(tmpStr); | 849 tmpStr2 = encodeStr1(tmpStr); |
702 if (!tmpStr2) { | 850 if (!tmpStr2) { |
703 th_free(tmpStr); | 851 th_free(tmpStr); |
704 return -3; | 852 return -3; |
705 } | 853 } |
706 sendUserMsg(sock ,optUserName2, "%%2F%s", tmpStr2); | |
707 | |
708 th_free(tmpStr); | |
709 th_free(tmpStr2); | |
710 return 0; | |
711 } else if (!strncmp(buf, "/flood ", 7)) { | |
712 int i; | |
713 | |
714 snprintf(tmpBuf, sizeof(tmpBuf), "/prv -to %s -msg . .", | |
715 buf+7); | |
716 | |
717 tmpStr = encodeStr2(tmpBuf); | |
718 if (!tmpStr) return -2; | |
719 tmpStr2 = encodeStr1(tmpStr); | |
720 if (!tmpStr2) { | |
721 th_free(tmpStr); | |
722 return -3; | |
723 } | |
724 | 854 |
725 result = TRUE; | 855 result = TRUE; |
726 for (i = 0; i < 50 && result; i++) { | 856 for (i = 0; i < 50 && result; i++) { |
727 result = sendUserMsg(sock, optUserName2, "%s", tmpStr2); | 857 result = sendUserMsg(sock, optUserName2, "%s", tmpStr2); |
728 usleep(250); | 858 usleep(250); |
730 | 860 |
731 th_free(tmpStr); | 861 th_free(tmpStr); |
732 th_free(tmpStr2); | 862 th_free(tmpStr2); |
733 return 0; | 863 return 0; |
734 } else if (!strncmp(buf, "/msg ", 5)) { | 864 } else if (!strncmp(buf, "/msg ", 5)) { |
735 if (setTarget) { | 865 if (setTarget != NULL) { |
736 snprintf(tmpBuf, sizeof(tmpBuf), "/prv -to %s -msg %s", setTarget, buf+5); | 866 snprintf(tmpBuf, sizeof(tmpBuf), "/prv -to %s -msg %s", setTarget, buf+5); |
737 buf = tmpBuf; | 867 buf = tmpBuf; |
738 } else { | 868 } else { |
739 printMsg("No target set!\n", ANSI_L_RED "No target set!" ANSI_END "\n"); | 869 printMsg("No target set!\n"); |
740 return 1; | 870 return 1; |
741 } | 871 } |
742 } else if (!strncmp(buf, "/to ", 4)) { | 872 } else if (!strncmp(buf, "/to ", 4)) { |
743 buf += 4; | 873 buf += 4; |
744 th_free(setTarget); | 874 th_free(setTarget); |
745 setTarget = th_strdup(buf); | 875 setTarget = th_strdup(buf); |
746 printMsg("Set prv target to '%s'\n", | 876 printMsg("Set prv target to '%s'\n", setTarget); |
747 "Set prv target to '" ANSI_L_GREEN "%s" ANSI_END "'\n", setTarget); | |
748 return 0; | 877 return 0; |
749 } | 878 } |
750 | 879 |
751 { | 880 { |
752 /* Send double-encoded */ | 881 /* Send double-encoded */ |
753 //printf("ENC>%s\n", buf); | |
754 //fflush(stdout); | |
755 | |
756 tmpStr = encodeStr2(buf); | 882 tmpStr = encodeStr2(buf); |
757 if (!tmpStr) return -2; | 883 if (!tmpStr) return -2; |
758 tmpStr2 = encodeStr1(tmpStr); | 884 tmpStr2 = encodeStr1(tmpStr); |
759 if (!tmpStr2) { | 885 if (!tmpStr2) { |
760 th_free(tmpStr); | 886 th_free(tmpStr); |
772 } | 898 } |
773 | 899 |
774 | 900 |
775 int main(int argc, char *argv[]) | 901 int main(int argc, char *argv[]) |
776 { | 902 { |
777 int tmpSocket; | 903 int tmpSocket, curVis, updateCount = 0; |
778 struct hostent *tmpHost; | 904 struct hostent *tmpHost; |
779 BOOL argsOK, exitProg = FALSE, colorSet = FALSE; | 905 BOOL argsOK, isError = FALSE, |
906 exitProg = FALSE, | |
907 colorSet = FALSE, | |
908 cursesInit = FALSE; | |
780 struct timeval tv; | 909 struct timeval tv; |
781 fd_set sockfds; | 910 fd_set sockfds; |
782 fd_set inputfds; | |
783 char *tmpStr; | 911 char *tmpStr; |
912 editbuf_t *editBuf = calloc(1, sizeof(editbuf_t)); | |
784 | 913 |
785 /* Initialize */ | 914 /* Initialize */ |
786 th_init("NNChat", "Newbie Nudes chat client", "0.4", | 915 th_init("NNChat", "Newbie Nudes chat client", "0.4", |
787 "Written and designed by Anonymous Finnish Guy (C) 2008", | 916 "Written and designed by Anonymous Finnish Guy (C) 2008", |
788 "This software is freeware, use and distribute as you wish."); | 917 "This software is freeware, use and distribute as you wish."); |
809 THERR("Could not open logfile for appending!\n"); | 938 THERR("Could not open logfile for appending!\n"); |
810 return -9; | 939 return -9; |
811 } | 940 } |
812 } | 941 } |
813 | 942 |
814 #ifdef __WIN32 | |
815 { | |
816 WSADATA wsaData; | |
817 if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) { | |
818 THERR("WinSock API v2.0 not supported.\n"); | |
819 return -20; | |
820 } | |
821 } | |
822 #endif | |
823 | |
824 /* Okay ... */ | 943 /* Okay ... */ |
825 THMSG(1, "Trying to resolve host '%s' ...\n", optServer); | 944 THMSG(1, "Trying to resolve host '%s' ...\n", optServer); |
826 tmpHost = gethostbyname(optServer); | 945 tmpHost = gethostbyname(optServer); |
827 if (tmpHost == NULL) { | 946 if (tmpHost == NULL) { |
828 THERR("Could not resolve hostname: %s.\n", | 947 THERR("Could not resolve hostname: %s.\n", |
853 /* Okay, now do the proper connection ... */ | 972 /* Okay, now do the proper connection ... */ |
854 if ((tmpSocket = openConnection((struct in_addr *) tmpHost->h_addr, optPort)) < 0) { | 973 if ((tmpSocket = openConnection((struct in_addr *) tmpHost->h_addr, optPort)) < 0) { |
855 THERR("Main connection setup failed!\n"); | 974 THERR("Main connection setup failed!\n"); |
856 goto err_exit; | 975 goto err_exit; |
857 } | 976 } |
858 | 977 |
859 | |
860 THMSG(1, "Connected, logging in as '%s'.\n", optUserName); | 978 THMSG(1, "Connected, logging in as '%s'.\n", optUserName); |
861 optUserName2 = encodeStr1(optUserName); | 979 optUserName2 = encodeStr1(optUserName); |
862 | 980 |
863 sendUserMsg(tmpSocket, optUserName2, "%%2Flogin%%20%%2Dsite%%20NN%%20%%2Dpassword%%20%s", optPassword); | 981 sendUserMsg(tmpSocket, optUserName2, "%%2Flogin%%20%%2Dsite%%20NN%%20%%2Dpassword%%20%s", optPassword); |
864 | 982 |
865 FD_ZERO(&inputfds); | 983 /* Initialize curses */ |
866 FD_SET(0, &inputfds); | 984 |
985 if (!optDaemon) { | |
986 initscr(); | |
987 raw(); | |
988 keypad(stdscr, TRUE); | |
989 noecho(); | |
990 timeout(SET_DELAY); | |
991 curVis = curs_set(0); | |
992 if (has_colors()) { | |
993 start_color(); | |
994 | |
995 init_pair(1, COLOR_RED, COLOR_BLACK); | |
996 init_pair(2, COLOR_GREEN, COLOR_BLACK); | |
997 init_pair(3, COLOR_YELLOW, COLOR_BLACK); | |
998 init_pair(4, COLOR_BLUE, COLOR_BLACK); | |
999 init_pair(5, COLOR_MAGENTA, COLOR_BLACK); | |
1000 init_pair(6, COLOR_CYAN, COLOR_BLACK); | |
1001 init_pair(7, COLOR_WHITE, COLOR_BLACK); | |
1002 init_pair(8, COLOR_BLACK, COLOR_BLACK); | |
1003 | |
1004 init_pair(10, COLOR_BLACK, COLOR_RED); | |
1005 init_pair(11, COLOR_WHITE, COLOR_RED); | |
1006 init_pair(12, COLOR_GREEN, COLOR_RED); | |
1007 init_pair(13, COLOR_YELLOW, COLOR_RED); | |
1008 init_pair(14, COLOR_BLUE, COLOR_RED); | |
1009 init_pair(15, COLOR_MAGENTA, COLOR_RED); | |
1010 init_pair(16, COLOR_CYAN, COLOR_RED); | |
1011 | |
1012 } | |
1013 | |
1014 mainWin = newwin(LINES - 4, COLS, 0, 0); | |
1015 statusWin = newwin(1, COLS, LINES - 4, 0); | |
1016 editWin = newwin(2, COLS, LINES - 3, 0); | |
1017 | |
1018 if (mainWin == NULL || statusWin == NULL || editWin == NULL) { | |
1019 THERR("Could not create ncurses windows!\n"); | |
1020 goto err_exit; | |
1021 } | |
1022 scrollok(mainWin, 1); | |
1023 | |
1024 clearBuf(editBuf); | |
1025 printEditBuf(editBuf); | |
1026 updateStatus(); | |
1027 | |
1028 cursesInit = TRUE; | |
1029 } | |
1030 | |
1031 | |
867 FD_ZERO(&sockfds); | 1032 FD_ZERO(&sockfds); |
868 FD_SET(tmpSocket, &sockfds); | 1033 FD_SET(tmpSocket, &sockfds); |
869 | 1034 |
870 while (!exitProg) { | 1035 while (!isError && !exitProg) { |
871 ssize_t gotBuf; | |
872 int result; | 1036 int result; |
873 char tmpBuf[4096]; | |
874 fd_set tmpfds; | 1037 fd_set tmpfds; |
875 | 1038 |
876 /* Check for incoming data from the server */ | 1039 /* Check for incoming data from the server */ |
877 tv.tv_sec = 0; | 1040 tv.tv_sec = 0; |
878 tv.tv_usec = SET_SELECT_USEC; | 1041 tv.tv_usec = SET_DELAY_USEC; |
879 tmpfds = sockfds; | 1042 tmpfds = sockfds; |
880 if ((result = select(tmpSocket+1, &tmpfds, NULL, NULL, &tv)) == -1) { | 1043 if ((result = select(tmpSocket+1, &tmpfds, NULL, NULL, &tv)) == -1) { |
881 printMsg("Error occured in select(sockfds): %s\n", | 1044 printMsg("Error occured in select(sockfds): %s\n", |
882 "Error occured in select(sockfds): %s\n", | |
883 strerror(errno)); | 1045 strerror(errno)); |
884 exitProg = TRUE; | 1046 isError = TRUE; |
885 } else if (FD_ISSET(tmpSocket, &tmpfds)) { | 1047 } else if (FD_ISSET(tmpSocket, &tmpfds)) { |
1048 ssize_t gotBuf; | |
1049 char tmpBuf[4096]; | |
886 gotBuf = recv(tmpSocket, tmpBuf, sizeof(tmpBuf), 0); | 1050 gotBuf = recv(tmpSocket, tmpBuf, sizeof(tmpBuf), 0); |
887 | 1051 |
888 if (gotBuf < 0) { | 1052 if (gotBuf < 0) { |
889 printMsg("Error in recv: %s\n", strerror(errno)); | 1053 printMsg("Error in recv: %s\n", strerror(errno)); |
890 exitProg = TRUE; | 1054 isError = TRUE; |
891 } else if (gotBuf == 0) { | 1055 } else if (gotBuf == 0) { |
892 printMsg("Server closed connection.\n", "Server closed connection.\n"); | 1056 printMsg("Server closed connection.\n"); |
893 exitProg = TRUE; | 1057 isError = TRUE; |
894 } else { | 1058 } else { |
895 /* Handle protocol data */ | 1059 /* Handle protocol data */ |
896 tmpBuf[gotBuf] = 0; | 1060 tmpBuf[gotBuf] = 0; |
897 result = handleProtocol(tmpSocket, tmpBuf, gotBuf); | 1061 result = handleProtocol(tmpSocket, tmpBuf, gotBuf); |
898 | 1062 |
899 if (result > 0) { | 1063 if (result > 0) { |
900 /* Couldn't handle the message for some reason */ | 1064 /* Couldn't handle the message for some reason */ |
901 printMsg("Could not handle: %s\n", "Could not handle: %s\n", tmpBuf); | 1065 printMsg("Could not handle: %s\n", tmpBuf); |
902 } else if (result < 0) { | 1066 } else if (result < 0) { |
903 /* Fatal error, quit */ | 1067 /* Fatal error, quit */ |
904 printMsg("Fatal error with message: %s\n", tmpBuf); | 1068 printMsg("Fatal error with message: %s\n", tmpBuf); |
905 exitProg = TRUE; | 1069 isError = TRUE; |
906 } | 1070 } |
1071 updateStatus(); | |
907 } | 1072 } |
908 } | 1073 } |
909 | 1074 |
910 /* Check for user input */ | 1075 /* Handle user input */ |
911 if (!optDaemon) { | 1076 if (!optDaemon) { |
912 tv.tv_sec = 0; | 1077 int c, cnt = 0; |
913 tv.tv_usec = SET_SELECT_USEC; | 1078 BOOL update = FALSE; |
914 tmpfds = inputfds; | 1079 |
915 if ((result = select(1, &tmpfds, NULL, NULL, &tv)) == -1) { | 1080 /* Handle several buffered keypresses at once */ |
916 printMsg("Error occured in select(inputfds): %s\n", strerror(errno)); | 1081 do { |
917 exitProg = TRUE; | 1082 c = getch(); |
918 } else if (FD_ISSET(0, &tmpfds)) { | 1083 switch (c) { |
919 gotBuf = read(0, tmpBuf, sizeof(tmpBuf)); | 1084 case KEY_ENTER: |
920 | 1085 case '\n': |
921 if (gotBuf < 0) { | 1086 case '\r': |
922 printMsg("Error in reading stdio.\n", "Error in reading stdio.\n"); | 1087 /* Call the user input handler */ |
1088 if (editBuf->len > 0) { | |
1089 insertBuf(editBuf, editBuf->pos, 0); | |
1090 result = handleUserInput(tmpSocket, editBuf->data, editBuf->len); | |
1091 clearBuf(editBuf); | |
1092 | |
1093 if (result < 0) { | |
1094 printMsg("Fatal error handling user input: %s\n", editBuf->data); | |
1095 isError = TRUE; | |
1096 } | |
1097 | |
1098 update = TRUE; | |
1099 } | |
1100 break; | |
1101 | |
1102 case 0x109: /* F1 */ | |
1103 if (setInsertMode) | |
1104 setInsertMode = FALSE; | |
1105 else | |
1106 setInsertMode = TRUE; | |
1107 update = TRUE; | |
1108 break; | |
1109 | |
1110 case 0x204: /* ctrl+left */ | |
1111 editBuf->pos--; | |
1112 while (editBuf->pos > 0 && !isspace(editBuf->data[editBuf->pos])) | |
1113 editBuf->pos--; | |
1114 if (editBuf->pos < 0) | |
1115 editBuf->pos = 0; | |
1116 update = TRUE; | |
1117 break; | |
1118 | |
1119 case 0x206: /* ctrl+right */ | |
1120 editBuf->pos++; | |
1121 while (editBuf->pos < editBuf->len && !isspace(editBuf->data[editBuf->pos])) | |
1122 editBuf->pos++; | |
1123 if (editBuf->pos > editBuf->len) | |
1124 editBuf->pos = editBuf->len; | |
1125 update = TRUE; | |
1126 break; | |
1127 | |
1128 case 0x111: /* F9 */ | |
1129 printMsg("Quitting per user request."); | |
923 exitProg = TRUE; | 1130 exitProg = TRUE; |
924 } else { | 1131 break; |
925 /* Call the user input handler */ | 1132 |
926 result = handleUserInput(tmpSocket, tmpBuf, gotBuf); | 1133 case 0x10a: /* F2 */ |
927 if (result < 0) { | 1134 clearBuf(editBuf); |
928 printMsg("Fatal error handling user input: %s\n", | 1135 update = TRUE; |
929 tmpBuf); | 1136 break; |
930 exitProg = TRUE; | 1137 |
1138 case KEY_HOME: setBufPos(editBuf, 0); update = TRUE; break; | |
1139 case KEY_END: setBufPos(editBuf, editBuf->len); update = TRUE; break; | |
1140 case KEY_LEFT: setBufPos(editBuf, editBuf->pos - 1); update = TRUE; break; | |
1141 case KEY_RIGHT: setBufPos(editBuf, editBuf->pos + 1); update = TRUE; break; | |
1142 | |
1143 case KEY_BACKSPACE: | |
1144 deleteBuf(editBuf, editBuf->pos - 1); | |
1145 setBufPos(editBuf, editBuf->pos - 1); | |
1146 update = TRUE; | |
1147 break; | |
1148 | |
1149 case 0x14a: | |
1150 /* Delete */ | |
1151 deleteBuf(editBuf, editBuf->pos); | |
1152 update = TRUE; | |
1153 break; | |
1154 | |
1155 case 0x0c: | |
1156 /* ctrl+l */ | |
1157 redrawwin(mainWin); | |
1158 redrawwin(statusWin); | |
1159 redrawwin(editWin); | |
1160 break; | |
1161 | |
1162 case ERR: | |
1163 /* Ignore */ | |
1164 break; | |
1165 | |
1166 default: | |
1167 if (isprint(c)) { | |
1168 if (setInsertMode) | |
1169 insertBuf(editBuf, editBuf->pos, c); | |
1170 else | |
1171 writeBuf(editBuf, editBuf->pos, c); | |
1172 setBufPos(editBuf, editBuf->pos + 1); | |
1173 update = TRUE; | |
1174 } else { | |
1175 printMsg("Unhandled key: %02x\n", c); | |
931 } | 1176 } |
1177 break; | |
932 } | 1178 } |
933 } | 1179 } while (c != ERR && !exitProg && ++cnt < 10); |
1180 | |
1181 if (update) { | |
1182 /* Update edit line */ | |
1183 printEditBuf(editBuf); | |
1184 updateStatus(); | |
1185 } | |
934 } /* !optDaemon */ | 1186 } /* !optDaemon */ |
1187 | |
1188 if (++updateCount > 10) { | |
1189 updateStatus(); | |
1190 updateCount = 0; | |
1191 } | |
935 | 1192 |
936 if (!colorSet) { | 1193 if (!colorSet) { |
937 colorSet = TRUE; | 1194 colorSet = TRUE; |
938 sendUserMsg(tmpSocket, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); | 1195 sendUserMsg(tmpSocket, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); |
939 } | 1196 } |
940 | 1197 } |
941 fflush(stdout); | 1198 |
942 fflush(stderr); | 1199 /* Shutdown */ |
943 } | |
944 | |
945 /* Shotdiwn */ | |
946 err_exit: | 1200 err_exit: |
947 THMSG(1, "Error exit.\n"); | 1201 if (cursesInit) { |
1202 if (curVis != ERR) | |
1203 curs_set(curVis); | |
1204 endwin(); | |
1205 THMSG(1, "NCurses deinitialized.\n"); | |
1206 } | |
1207 | |
1208 if (isError) { | |
1209 THMSG(1, "Error exit.\n"); | |
1210 } | |
1211 | |
948 th_free(optUserName2); | 1212 th_free(optUserName2); |
949 | 1213 |
950 closeConnection(tmpSocket); | 1214 closeConnection(tmpSocket); |
951 | 1215 |
952 #ifdef __WIN32 | |
953 WSACleanup(); | |
954 #endif | |
955 | |
956 THMSG(1, "Connection terminated.\n"); | 1216 THMSG(1, "Connection terminated.\n"); |
957 | 1217 |
958 if (optLogFile) { | 1218 if (optLogFile) { |
959 THMSG(1, "Closing logfile.\n"); | 1219 THMSG(1, "Closing logfile.\n"); |
960 fclose(optLogFile); | 1220 fclose(optLogFile); |
961 } | 1221 } |
962 | 1222 |
963 | |
964 return 0; | 1223 return 0; |
965 } | 1224 } |