Mercurial > hg > nnchat
comparison main.c @ 413:14b685cdbd2c
Rename files.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Thu, 24 May 2012 06:41:07 +0300 |
parents | nnchat.c@3e64acb433e8 |
children | 8263cb88556a |
comparison
equal
deleted
inserted
replaced
412:3e64acb433e8 | 413:14b685cdbd2c |
---|---|
1 /* | |
2 * NNChat - Custom chat client for NewbieNudes.com chatrooms | |
3 * Written by Matti 'ccr' Hämäläinen | |
4 * (C) Copyright 2008-2011 Tecnic Software productions (TNSP) | |
5 */ | |
6 #include "util.h" | |
7 #include "network.h" | |
8 #include "th_args.h" | |
9 #include "th_config.h" | |
10 #ifdef __WIN32 | |
11 /* Undefine because both windows.h and curses.h #define it */ | |
12 #undef MOUSE_MOVED | |
13 #include <shlwapi.h> | |
14 #else | |
15 #include <sys/wait.h> | |
16 #endif | |
17 #ifdef HAVE_NCURSES_H | |
18 #include <ncurses.h> | |
19 #else | |
20 #include <curses.h> | |
21 #endif | |
22 | |
23 #ifdef __WIN32 | |
24 #define SET_CONFIG_FILE "nnchat.txt" | |
25 #define SET_DIR_SEPARATOR "\\" | |
26 #define SET_DELAY (0) | |
27 #else | |
28 #define SET_CONFIG_FILE ".nnchat" | |
29 #define SET_DIR_SEPARATOR "/" | |
30 #define SET_DELAY (5) | |
31 #endif | |
32 | |
33 #define SET_NICK_SEPARATOR ':' | |
34 | |
35 #define SET_MAX_HISTORY (16) /* Command history length */ | |
36 #define SET_KEEPALIVE (15*60) /* Ping/keepalive period in seconds */ | |
37 #define SET_MAX_WINDOWS (32) | |
38 | |
39 | |
40 /* Options | |
41 */ | |
42 int optPort = 8005, | |
43 optProxyPort = 1080, | |
44 optProxyType = NN_PROXY_NONE; | |
45 int optUserColor = 0x000000; | |
46 char *optServer = "chat.newbienudes.com", | |
47 *optProxyServer = NULL, | |
48 *optUserName = NULL, | |
49 *optUserNameCmd = NULL, | |
50 *optUserNameEnc = NULL, | |
51 *optPassword = NULL, | |
52 *optPasswordCmd = NULL, | |
53 *optLogFilename = NULL, | |
54 *optSite = "NN", | |
55 *optNickSepStr = NULL; | |
56 char optNickSep; | |
57 BOOL optDaemon = FALSE; | |
58 FILE *optLogFile = NULL; | |
59 BOOL setIgnoreMode = FALSE; | |
60 BOOL optDebug = FALSE; | |
61 BOOL optLogEnable = FALSE; | |
62 | |
63 nn_window_t *chatWindows[SET_MAX_WINDOWS], | |
64 *currWin = NULL; | |
65 WINDOW *mainWin = NULL, | |
66 *statusWin = NULL, | |
67 *editWin = NULL; | |
68 | |
69 qlist_t *setIgnoreList = NULL, | |
70 *setIdleMessages = NULL; | |
71 nn_userhash_t *nnUsers = NULL; | |
72 char *setConfigFile = NULL, | |
73 *setBrowser = NULL; | |
74 cfgitem_t *cfg = NULL; | |
75 | |
76 | |
77 /* Logging mode flags | |
78 */ | |
79 enum | |
80 { | |
81 LOG_FILE = 1, | |
82 LOG_WINDOW = 2, | |
83 LOG_STAMP = 4 | |
84 }; | |
85 | |
86 | |
87 /* Arguments | |
88 */ | |
89 optarg_t optList[] = | |
90 { | |
91 { 0, '?', "help", "Show this help", OPT_NONE }, | |
92 { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, | |
93 { 2, 'p', "port", "Connect to port", OPT_ARGREQ }, | |
94 { 3, 's', "server", "Server to connect to", OPT_ARGREQ }, | |
95 { 4, 'C', "color", "Initial color in RGB hex 000000", OPT_ARGREQ }, | |
96 { 5, 'l', "logfile", "Log filename", OPT_ARGREQ }, | |
97 { 6, 'D', "daemon", "A pseudo-daemon mode for logging", OPT_NONE }, | |
98 { 7, 'f', "force-site", "Force site (default: NN)", OPT_ARGREQ }, | |
99 { 8, 'd', "debug", "Enable various debug features", OPT_NONE }, | |
100 | |
101 {10, '4', "socks4", "SOCKS4 proxy server", OPT_ARGREQ }, | |
102 {11, 'A', "socks4a", "SOCKS4A proxy server", OPT_ARGREQ }, | |
103 {12, 'P', "proxy-port", "Proxy port (default: 1080)", OPT_ARGREQ }, | |
104 }; | |
105 | |
106 const int optListN = (sizeof(optList) / sizeof(optList[0])); | |
107 | |
108 | |
109 void argShowHelp(void) | |
110 { | |
111 th_print_banner(stdout, th_prog_name, | |
112 "[options] <username> <password>"); | |
113 | |
114 th_args_help(stdout, optList, optListN); | |
115 } | |
116 | |
117 | |
118 BOOL argHandleOpt(const int optN, char *optArg, char *currArg) | |
119 { | |
120 switch (optN) | |
121 { | |
122 case 0: | |
123 argShowHelp(); | |
124 exit(0); | |
125 break; | |
126 | |
127 case 1: | |
128 th_verbosityLevel++; | |
129 break; | |
130 | |
131 case 2: | |
132 optPort = atoi(optArg); | |
133 break; | |
134 | |
135 case 3: | |
136 optServer = optArg; | |
137 break; | |
138 | |
139 case 4: | |
140 if ((optUserColor = th_get_hex_triplet(optArg)) < 0) | |
141 { | |
142 THERR("Invalid color argument '%s', should be a RGB hex triplet '000000'.\n", | |
143 optArg); | |
144 return FALSE; | |
145 } | |
146 THMSG(1, "Using color #%06x\n", optUserColor); | |
147 break; | |
148 | |
149 case 5: | |
150 optLogFilename = optArg; | |
151 optLogEnable = TRUE; | |
152 break; | |
153 | |
154 case 7: | |
155 optSite = optArg; | |
156 break; | |
157 | |
158 case 6: | |
159 optDaemon = TRUE; | |
160 THMSG(1, "Running in pseudo-daemon mode.\n"); | |
161 break; | |
162 | |
163 case 8: | |
164 optDebug = TRUE; | |
165 THMSG(1, "Debug mode enabled.\n"); | |
166 break; | |
167 | |
168 | |
169 case 10: | |
170 optProxyServer = optArg; | |
171 optProxyType = NN_PROXY_SOCKS4; | |
172 break; | |
173 | |
174 case 11: | |
175 optProxyServer = optArg; | |
176 optProxyType = NN_PROXY_SOCKS4A; | |
177 break; | |
178 | |
179 case 12: | |
180 optPort = atoi(optArg); | |
181 break; | |
182 | |
183 | |
184 default: | |
185 THERR("Unknown option '%s'.\n", currArg); | |
186 return FALSE; | |
187 } | |
188 | |
189 return TRUE; | |
190 } | |
191 | |
192 | |
193 BOOL argHandleFile(char *currArg) | |
194 { | |
195 if (!optUserNameCmd) | |
196 optUserNameCmd = currArg; | |
197 else if (!optPasswordCmd) | |
198 optPasswordCmd = currArg; | |
199 else | |
200 { | |
201 THERR("Username '%s' already specified on commandline!\n", optUserNameCmd); | |
202 return FALSE; | |
203 } | |
204 | |
205 return TRUE; | |
206 } | |
207 | |
208 BOOL str_get_timestamp(char *str, size_t len, const char *fmt) | |
209 { | |
210 time_t stamp = time(NULL); | |
211 struct tm *stamp_tm; | |
212 if ((stamp_tm = localtime(&stamp)) != NULL) | |
213 { | |
214 strftime(str, len, fmt, stamp_tm); | |
215 return TRUE; | |
216 } | |
217 else | |
218 { | |
219 str[0] = 0; | |
220 return FALSE; | |
221 } | |
222 } | |
223 | |
224 | |
225 char * str_trim_left(char *buf) | |
226 { | |
227 while (*buf != 0 && th_isspace(*buf)) buf++; | |
228 return buf; | |
229 } | |
230 | |
231 int compareUsername(const void *s1, const void *s2) | |
232 { | |
233 return th_strcasecmp((char *) s1, (char *) s2); | |
234 } | |
235 | |
236 nn_window_t *findWindow(const char *id) | |
237 { | |
238 int i; | |
239 | |
240 for (i = 0; i < SET_MAX_WINDOWS; i++) | |
241 if (chatWindows[i] != NULL && | |
242 chatWindows[i]->id != NULL && | |
243 th_strcasecmp(id, chatWindows[i]->id) == 0) | |
244 return chatWindows[i]; | |
245 | |
246 return NULL; | |
247 } | |
248 | |
249 | |
250 BOOL openWindow(const char *name, BOOL curwin) | |
251 { | |
252 int i; | |
253 nn_window_t *res; | |
254 if (name == NULL) | |
255 return FALSE; | |
256 | |
257 if ((res = nn_window_new(name)) == NULL) | |
258 return FALSE; | |
259 | |
260 for (i = 1; i < SET_MAX_WINDOWS; i++) | |
261 if (chatWindows[i] == NULL) | |
262 { | |
263 res->num = i; | |
264 chatWindows[i] = res; | |
265 if (curwin) | |
266 currWin = res; | |
267 return TRUE; | |
268 } | |
269 | |
270 return FALSE; | |
271 } | |
272 | |
273 | |
274 void closeWindow(nn_window_t *win) | |
275 { | |
276 int i; | |
277 if (win == NULL) return; | |
278 | |
279 for (i = 1; i < SET_MAX_WINDOWS; i++) | |
280 if (chatWindows[i] == win) | |
281 { | |
282 chatWindows[i] = NULL; | |
283 nn_window_free(win); | |
284 return; | |
285 } | |
286 } | |
287 | |
288 | |
289 void updateStatus(void) | |
290 { | |
291 char tmpStr[128]; | |
292 int i; | |
293 | |
294 if (statusWin == NULL) return; | |
295 | |
296 str_get_timestamp(tmpStr, sizeof(tmpStr), "%H:%M:%S"); | |
297 | |
298 wbkgdset(statusWin, COLOR_PAIR(10)); | |
299 werase(statusWin); | |
300 | |
301 wattrset(statusWin, A_BOLD | COLOR_PAIR(11)); | |
302 mvwaddstr(statusWin, 0, 1, tmpStr); | |
303 | |
304 wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); | |
305 waddstr(statusWin, " | "); | |
306 wattrset(statusWin, A_BOLD | COLOR_PAIR(16)); | |
307 waddstr(statusWin, optUserName); | |
308 wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); | |
309 | |
310 wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); | |
311 waddstr(statusWin, " | "); | |
312 wattrset(statusWin, A_BOLD | COLOR_PAIR(11)); | |
313 snprintf(tmpStr, sizeof(tmpStr), "#%06x", optUserColor); | |
314 waddstr(statusWin, tmpStr); | |
315 | |
316 wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); | |
317 waddstr(statusWin, " | WIN: "); | |
318 snprintf(tmpStr, sizeof(tmpStr), "%d: %s / %d", | |
319 currWin->num + 1, | |
320 currWin->id != NULL ? currWin->id : "MAIN", | |
321 currWin->pos); | |
322 waddstr(statusWin, tmpStr); | |
323 | |
324 wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); | |
325 waddstr(statusWin, " | "); | |
326 wattrset(statusWin, A_BOLD | COLOR_PAIR(11)); | |
327 | |
328 for (i = 0; i < SET_MAX_WINDOWS; i++) | |
329 if (chatWindows[i] != NULL && chatWindows[i]->dirty) | |
330 { | |
331 snprintf(tmpStr, sizeof(tmpStr), "%d ", i + 1); | |
332 waddstr(statusWin, tmpStr); | |
333 } | |
334 | |
335 wrefresh(statusWin); | |
336 } | |
337 | |
338 | |
339 void printEditBuf(nn_editbuf_t *buf) | |
340 { | |
341 char *tmp; | |
342 if (editWin == NULL || buf == NULL) return; | |
343 | |
344 buf->data[buf->len] = 0; | |
345 tmp = nn_username_decode(th_strdup(buf->data)); | |
346 | |
347 werase(editWin); | |
348 | |
349 wattrset(editWin, A_NORMAL); | |
350 | |
351 if (buf->pos < buf->len) | |
352 { | |
353 waddnstr(editWin, tmp, buf->pos); | |
354 wattrset(editWin, A_REVERSE); | |
355 waddch(editWin, tmp[buf->pos]); | |
356 wattrset(editWin, A_NORMAL); | |
357 waddnstr(editWin, tmp + buf->pos + 1, buf->len - buf->pos - 1); | |
358 } | |
359 else | |
360 { | |
361 waddnstr(editWin, tmp, buf->len); | |
362 wattrset(editWin, A_REVERSE); | |
363 waddch(editWin, ' '); | |
364 wattrset(editWin, A_NORMAL); | |
365 } | |
366 wrefresh(editWin); | |
367 th_free(tmp); | |
368 } | |
369 | |
370 | |
371 int printWin(WINDOW *win, const char *fmt) | |
372 { | |
373 const char *s = fmt; | |
374 int col = 0; | |
375 | |
376 while (*s) | |
377 { | |
378 if (*s == '½') | |
379 { | |
380 s++; | |
381 if (*s == '½') | |
382 { | |
383 waddch(win, ((unsigned char) *s) | col); | |
384 s++; | |
385 } | |
386 else | |
387 { | |
388 memcpy(&col, s, sizeof(int)); | |
389 s += sizeof(int); | |
390 } | |
391 } | |
392 else | |
393 { | |
394 waddch(win, ((unsigned char) *s) | col); | |
395 s++; | |
396 } | |
397 } | |
398 return 0; | |
399 } | |
400 | |
401 | |
402 #define QPUTCH(ch) th_vputch(&(win->buf), &(win->bufsize), &(win->len), ch) | |
403 | |
404 int nn_window_print(nn_window_t *win, const char *fmt) | |
405 { | |
406 const char *s = fmt; | |
407 int col = 0; | |
408 while (*s) | |
409 { | |
410 if (*s == '½') | |
411 { | |
412 s++; | |
413 if (*s == '½') | |
414 { | |
415 QPUTCH(*s); | |
416 QPUTCH(*s); | |
417 win->chlen++; | |
418 } | |
419 else | |
420 { | |
421 int val = 0; | |
422 while (*s >= '0' && *s <= '9') | |
423 { | |
424 val *= 10; | |
425 val += (*s - '0'); | |
426 s++; | |
427 } | |
428 if (*s != '½') return -1; | |
429 | |
430 if (val < 9) | |
431 col = A_DIM | COLOR_PAIR(val); | |
432 else if (val < 30) | |
433 col = A_BOLD | COLOR_PAIR(val - 9); | |
434 | |
435 QPUTCH('½'); | |
436 | |
437 if (!th_growbuf(&(win->buf), &(win->bufsize), &(win->len), sizeof(int))) | |
438 return -2; | |
439 | |
440 memcpy(win->buf + win->len, &col, sizeof(int)); | |
441 win->len += sizeof(int); | |
442 } | |
443 } | |
444 else if (*s == '\n') | |
445 { | |
446 QPUTCH('\n'); | |
447 QPUTCH(0); | |
448 th_ringbuf_add(win->data, win->buf); | |
449 win->buf = NULL; | |
450 win->chlen = 0; | |
451 win->dirty = TRUE; | |
452 } | |
453 else if (*s != '\r') | |
454 { | |
455 QPUTCH((unsigned char) *s == 255 ? ' ' : *s); | |
456 win->chlen++; | |
457 } | |
458 | |
459 s++; | |
460 } | |
461 | |
462 return 0; | |
463 } | |
464 | |
465 | |
466 BOOL updateMainWin(BOOL force) | |
467 { | |
468 int h, offs; | |
469 qringbuf_t *buf; | |
470 | |
471 /* Check pointers */ | |
472 if (mainWin == NULL || currWin == NULL) | |
473 return FALSE; | |
474 | |
475 /* Check if update is forced or if the window is dirty */ | |
476 if (!force && !currWin->dirty) | |
477 return FALSE; | |
478 | |
479 /* Compute how many lines from backbuffer fit on the screen */ | |
480 buf = currWin->data; | |
481 h = getmaxy(mainWin); | |
482 | |
483 /* Clear and redraw window */ | |
484 werase(mainWin); | |
485 scrollok(mainWin, 1); | |
486 for (offs = buf->size - h - currWin->pos; offs >= 0 && offs < buf->size - currWin->pos && offs < buf->size; offs++) | |
487 { | |
488 if (buf->data[offs] != NULL) | |
489 printWin(mainWin, buf->data[offs]); | |
490 } | |
491 | |
492 currWin->dirty = FALSE; | |
493 wrefresh(mainWin); | |
494 return TRUE; | |
495 } | |
496 | |
497 | |
498 int printFile(FILE *outFile, const char *fmt) | |
499 { | |
500 const char *s = fmt; | |
501 | |
502 while (*s) | |
503 { | |
504 if (*s == '½') | |
505 { | |
506 s++; | |
507 if (*s == '½') | |
508 { | |
509 fputc((unsigned char) *s, outFile); | |
510 s++; | |
511 } | |
512 else | |
513 { | |
514 while (*s && isdigit((int) *s)) s++; | |
515 if (*s != '½') return -1; | |
516 s++; | |
517 } | |
518 } | |
519 else | |
520 { | |
521 if ((unsigned char) *s == 255) | |
522 fputc(' ', outFile); | |
523 else | |
524 fputc((unsigned char) *s, outFile); | |
525 s++; | |
526 } | |
527 } | |
528 | |
529 return 0; | |
530 } | |
531 | |
532 void printMsgV(nn_window_t *win, int flags, const char *fmt, va_list ap) | |
533 { | |
534 char tmpStr[128], *buf; | |
535 | |
536 str_get_timestamp(tmpStr, sizeof(tmpStr), "½17½[½11½%H:%M:%S½17½]½0½ "); | |
537 | |
538 buf = th_strdup_vprintf(fmt, ap); | |
539 | |
540 if (optLogFile && (flags & LOG_FILE)) | |
541 { | |
542 if (flags & LOG_STAMP) printFile(optLogFile, tmpStr); | |
543 printFile(optLogFile, buf); | |
544 fflush(optLogFile); | |
545 } | |
546 | |
547 if (!optDaemon && (flags & LOG_WINDOW)) | |
548 { | |
549 nn_window_t *tmp = win != NULL ? win : chatWindows[0]; | |
550 if (flags & LOG_STAMP) nn_window_print(tmp, tmpStr); | |
551 nn_window_print(tmp, buf); | |
552 } | |
553 | |
554 th_free(buf); | |
555 } | |
556 | |
557 void printMsg(nn_window_t *win, const char *fmt, ...) | |
558 { | |
559 va_list ap; | |
560 | |
561 va_start(ap, fmt); | |
562 printMsgV(win, LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap); | |
563 va_end(ap); | |
564 } | |
565 | |
566 void printMsgF(nn_window_t *win, int flags, const char *fmt, ...) | |
567 { | |
568 va_list ap; | |
569 | |
570 va_start(ap, fmt); | |
571 printMsgV(win, flags | LOG_STAMP, fmt, ap); | |
572 va_end(ap); | |
573 } | |
574 | |
575 void printMsgQ(nn_window_t *win, const char *fmt, ...) | |
576 { | |
577 va_list ap; | |
578 | |
579 va_start(ap, fmt); | |
580 printMsgV(win, LOG_STAMP | LOG_WINDOW, fmt, ap); | |
581 va_end(ap); | |
582 } | |
583 | |
584 | |
585 char *errorMessages = NULL; | |
586 | |
587 void errorMsgV(const char *fmt, va_list ap) | |
588 { | |
589 char *tmp = th_strdup_vprintf(fmt, ap); | |
590 | |
591 printMsg(NULL, "%s", tmp); | |
592 | |
593 if (errorMessages != NULL) | |
594 { | |
595 char *tmp2 = th_strdup_printf("%s%s", errorMessages, tmp); | |
596 th_free(errorMessages); | |
597 th_free(tmp); | |
598 errorMessages = tmp2; | |
599 } | |
600 else | |
601 errorMessages = tmp; | |
602 } | |
603 | |
604 void errorMsg(const char *fmt, ...) | |
605 { | |
606 va_list ap; | |
607 | |
608 va_start(ap, fmt); | |
609 errorMsgV(fmt, ap); | |
610 va_end(ap); | |
611 } | |
612 | |
613 void errorFunc(struct _nn_conn_t *conn, const char *fmt, va_list ap) | |
614 { | |
615 (void) conn; | |
616 errorMsgV(fmt, ap); | |
617 } | |
618 | |
619 void messageFunc(struct _nn_conn_t *conn, const char *fmt, va_list ap) | |
620 { | |
621 (void) conn; | |
622 printMsgV(NULL, LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap); | |
623 } | |
624 | |
625 | |
626 BOOL checkIgnoreList(const char *name) | |
627 { | |
628 qlist_t *node = setIgnoreList; | |
629 while (node != NULL) | |
630 { | |
631 if (th_strcasecmp(name, (char *) node->data) == 0) | |
632 return TRUE; | |
633 node = node->next; | |
634 } | |
635 return FALSE; | |
636 } | |
637 | |
638 | |
639 int nnproto_handle_user(nn_conn_t *conn) | |
640 { | |
641 static const char *msg = "</USER><MESSAGE>"; | |
642 char *p = conn->ptr; | |
643 BOOL isMine, isIgnored = FALSE; | |
644 char *s, *t, *userName; | |
645 | |
646 /* Find start of the message */ | |
647 s = strstr(p, msg); | |
648 if (!s) return 1; | |
649 *s = 0; | |
650 s += strlen(msg); | |
651 | |
652 /* Find end of the message */ | |
653 t = strstr(s, "</MESSAGE>"); | |
654 if (!t) return 3; | |
655 *t = 0; | |
656 | |
657 /* Decode message string */ | |
658 s = nn_decode_str1(s); | |
659 if (!s) return -1; | |
660 | |
661 /* Decode username */ | |
662 userName = nn_decode_str1(p); | |
663 if (!userName) | |
664 { | |
665 th_free(s); | |
666 return -2; | |
667 } | |
668 | |
669 /* Check if the username is on our ignore list and | |
670 * that it is not our OWN username! | |
671 */ | |
672 isMine = strcmp(userName, optUserName) == 0; | |
673 isIgnored = setIgnoreMode && !isMine && checkIgnoreList(userName); | |
674 | |
675 /* Is it a special control message? */ | |
676 if (*s == '/') | |
677 { | |
678 /* Ignore room join/leave messages */ | |
679 if (!optDebug && (strstr(s, "left the room") || strstr(s, "joined the room from"))) | |
680 goto done; | |
681 | |
682 t = nn_strip_tags(s + 1); | |
683 if (!strncmp(t, "BPRV ", 5)) | |
684 { | |
685 char *name, *tmp, *msg, *h; | |
686 nn_window_t *win; | |
687 h = nn_decode_str2(t + 1); | |
688 | |
689 if (!strncmp(t, "BPRV from ", 10)) | |
690 { | |
691 name = nn_decode_str2(t + 10); | |
692 isMine = FALSE; | |
693 } | |
694 else | |
695 { | |
696 name = nn_decode_str2(t + 8); | |
697 isMine = TRUE; | |
698 } | |
699 | |
700 for (tmp = name; *tmp && *tmp != ':'; tmp++); | |
701 if (tmp[0] != 0 && tmp[1] == ' ') | |
702 msg = tmp + 2; | |
703 else | |
704 msg = ""; | |
705 *tmp = 0; | |
706 | |
707 isIgnored = setIgnoreMode && checkIgnoreList(name); | |
708 win = findWindow(name); | |
709 | |
710 if (win != NULL) | |
711 { | |
712 printMsgF(win, isIgnored ? 0 : LOG_WINDOW, | |
713 "½5½<½%d½%s½5½>½0½ %s\n", | |
714 isMine ? 14 : 15, isMine ? optUserName : name, msg); | |
715 | |
716 printMsgF(NULL, LOG_FILE, "½11½%s½0½\n", h); | |
717 } | |
718 else | |
719 { | |
720 printMsgF(NULL, isIgnored ? LOG_FILE : (LOG_WINDOW | LOG_FILE), | |
721 "½11½%s½0½\n", h); | |
722 } | |
723 th_free(name); | |
724 th_free(h); | |
725 } | |
726 else | |
727 { | |
728 /* It's an action (/me) */ | |
729 char *h = nn_decode_str2(t); | |
730 printMsgF(NULL, isIgnored ? LOG_FILE : (LOG_WINDOW | LOG_FILE), | |
731 "½9½* %s½0½\n", h); | |
732 th_free(h); | |
733 } | |
734 th_free(t); | |
735 } | |
736 else | |
737 { | |
738 /* It's a normal message */ | |
739 char *h; | |
740 t = nn_strip_tags(s); | |
741 h = nn_decode_str2(t); | |
742 printMsgF(NULL, isIgnored ? LOG_FILE : (LOG_WINDOW | LOG_FILE), | |
743 "½5½<½%d½%s½5½>½0½ %s\n", isMine ? 14 : 15, userName, h); | |
744 th_free(h); | |
745 th_free(t); | |
746 } | |
747 | |
748 done: | |
749 th_free(s); | |
750 th_free(userName); | |
751 return 0; | |
752 } | |
753 | |
754 | |
755 int nnproto_handle_login(nn_conn_t *conn) | |
756 { | |
757 char tmpStr[256]; | |
758 str_get_timestamp(tmpStr, sizeof(tmpStr), "%c"); | |
759 | |
760 if (!nn_conn_buf_strcmp(conn, "FAILURE>")) | |
761 { | |
762 printMsg(NULL, "½1½Login failure½0½ - ½3½%s½0½\n", tmpStr); | |
763 return -2; | |
764 } | |
765 else if (!nn_conn_buf_strcmp(conn, "SUCCESS>")) | |
766 { | |
767 printMsg(NULL, "½2½Login success½0½ - ½3½%s½0½\n", tmpStr); | |
768 nn_conn_send_msg(conn, optUserNameEnc, "%%2FRequestUserList"); | |
769 return 0; | |
770 } | |
771 else | |
772 return 1; | |
773 } | |
774 | |
775 | |
776 int nnproto_handle_add_user(nn_conn_t *conn) | |
777 { | |
778 char *p, *s, *str = conn->ptr; | |
779 nn_window_t *win; | |
780 | |
781 s = nn_conn_buf_strstr(conn, "</ADD_USER>"); | |
782 if (!s) return 1; | |
783 *s = 0; | |
784 | |
785 p = nn_dbldecode_str(str); | |
786 if (!p) return -1; | |
787 | |
788 win = findWindow(p); | |
789 nn_userhash_insert(nnUsers, nn_username_encode(p)); | |
790 | |
791 printMsg(NULL, "! ½3½%s½0½ ½2½ADDED.½0½\n", p); | |
792 if (win != NULL) | |
793 printMsg(win, "! ½3½%s½0½ ½2½joined the chat.½0½\n", p); | |
794 | |
795 th_free(p); | |
796 return 0; | |
797 } | |
798 | |
799 | |
800 int nnproto_handle_delete_user(nn_conn_t *conn) | |
801 { | |
802 char *p, *s, *str = conn->ptr; | |
803 nn_window_t *win; | |
804 | |
805 s = nn_conn_buf_strstr(conn, "</DELETE_USER>"); | |
806 if (!s) return 1; | |
807 *s = 0; | |
808 | |
809 p = nn_dbldecode_str(str); | |
810 if (!p) return -1; | |
811 | |
812 win = findWindow(p); | |
813 nn_userhash_delete(nnUsers, nn_username_encode(p)); | |
814 | |
815 printMsg(NULL, "! ½3½%s½0½ ½1½DELETED.½0½\n", p); | |
816 if (win != NULL) | |
817 printMsg(win, "! ½3½%s½0½ ½1½left the chat.½0½\n", p); | |
818 | |
819 th_free(p); | |
820 return 0; | |
821 } | |
822 | |
823 | |
824 int nnproto_handle_num_clients(nn_conn_t *conn) | |
825 { | |
826 nn_conn_buf_strstr(conn, "</NUMCLIENTS>"); | |
827 return 0; | |
828 } | |
829 | |
830 | |
831 int nnproto_handle_boot(nn_conn_t *conn) | |
832 { | |
833 (void) conn; | |
834 errorMsg("Booted by server.\n"); | |
835 return -1; | |
836 } | |
837 | |
838 | |
839 typedef struct | |
840 { | |
841 char *cmd; | |
842 ssize_t len; | |
843 int (*handler)(nn_conn_t *); | |
844 } nn_protocolcmd_t; | |
845 | |
846 | |
847 static nn_protocolcmd_t protoCmds[] = | |
848 { | |
849 { "<USER>", -1, nnproto_handle_user }, | |
850 { "<LOGIN_", -1, nnproto_handle_login }, | |
851 { "<DELETE_USER>", -1, nnproto_handle_delete_user }, | |
852 { "<ADD_USER>", -1, nnproto_handle_add_user }, | |
853 { "<NUMCLIENTS>", -1, nnproto_handle_num_clients }, | |
854 { "<BOOT />", -1, nnproto_handle_boot }, | |
855 }; | |
856 | |
857 static const int nprotoCmds = sizeof(protoCmds) / sizeof(protoCmds[0]); | |
858 | |
859 | |
860 int nn_parse_protocol(nn_conn_t *conn) | |
861 { | |
862 static BOOL protoCmdsInit = FALSE; | |
863 int i; | |
864 | |
865 if (!protoCmdsInit) | |
866 { | |
867 for (i = 0; i < nprotoCmds; i++) | |
868 protoCmds[i].len = strlen(protoCmds[i].cmd); | |
869 | |
870 protoCmdsInit = TRUE; | |
871 } | |
872 | |
873 for (i = 0; i < nprotoCmds; i++) | |
874 { | |
875 if (!nn_conn_buf_strncmp(conn, protoCmds[i].cmd, protoCmds[i].len)) | |
876 return protoCmds[i].handler(conn); | |
877 } | |
878 | |
879 if (optDebug) | |
880 { | |
881 printMsg(NULL, "Unknown protocmd: \"%s\"\n", conn->ptr); | |
882 return 0; | |
883 } | |
884 else | |
885 return 1; | |
886 } | |
887 | |
888 | |
889 int nn_handle_input(nn_conn_t *conn, char *buf, size_t bufLen) | |
890 { | |
891 char *tmpStr, tmpBuf[4096]; | |
892 BOOL result; | |
893 | |
894 /* Trim right */ | |
895 bufLen--; | |
896 buf[bufLen--] = 0; | |
897 while (bufLen > 0 && th_isspace(buf[bufLen])) | |
898 buf[bufLen--] = 0; | |
899 | |
900 /* Decode completed usernames */ | |
901 nn_username_decode(buf); | |
902 | |
903 /* Check for special user commands */ | |
904 if (*buf == 0) | |
905 { | |
906 return 1; | |
907 } | |
908 else if (!th_strncasecmp(buf, "/color ", 7)) | |
909 { | |
910 /* Change color */ | |
911 int tmpInt; | |
912 if ((tmpInt = th_get_hex_triplet(str_trim_left(buf + 7))) < 0) | |
913 { | |
914 printMsgQ(currWin, "Invalid color value '%s'\n", buf+7); | |
915 return 1; | |
916 } | |
917 optUserColor = tmpInt; | |
918 printMsgQ(currWin, "Setting color to #%06x\n", optUserColor); | |
919 nn_conn_send_msg(conn, optUserNameEnc, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); | |
920 return 0; | |
921 } | |
922 else if (!th_strncasecmp(buf, "/ignore", 7)) | |
923 { | |
924 char *name = str_trim_left(buf + 7); | |
925 if (strlen(name) > 0) | |
926 { | |
927 /* Add or remove someone to/from ignore */ | |
928 qlist_t *user = th_llist_find_func(setIgnoreList, name, compareUsername); | |
929 if (user != NULL) | |
930 { | |
931 printMsgQ(currWin, "Removed user '%s' from ignore.\n", name); | |
932 th_llist_delete_node(&setIgnoreList, user); | |
933 } | |
934 else | |
935 { | |
936 printMsgQ(currWin, "Now ignoring '%s'.\n", name); | |
937 th_llist_append(&setIgnoreList, th_strdup(name)); | |
938 } | |
939 } | |
940 else | |
941 { | |
942 /* Just list whomever is in ignore now */ | |
943 qlist_t *user = setIgnoreList; | |
944 ssize_t nuser = th_llist_length(setIgnoreList); | |
945 char *result = th_strdup_printf("Users ignored (%d): ", nuser); | |
946 while (user != NULL) | |
947 { | |
948 if (user->data != NULL) | |
949 { | |
950 th_pstr_printf(&result, "%s'%s'", result, (char *) user->data); | |
951 if (--nuser > 0) | |
952 th_pstr_printf(&result, "%s, ", result); | |
953 } | |
954 user = user->next; | |
955 } | |
956 printMsgQ(currWin, "%s\n", result); | |
957 th_free(result); | |
958 } | |
959 return 0; | |
960 } | |
961 else if (!th_strncasecmp(buf, "/query", 6)) | |
962 { | |
963 char *name = str_trim_left(buf + 6); | |
964 if (strlen(name) > 0) | |
965 { | |
966 nn_user_t *user = nn_user_find(nnUsers, nn_username_encode(name)); | |
967 if (user != NULL) | |
968 { | |
969 name = nn_username_decode(th_strdup(user->name)); | |
970 printMsgQ(currWin, "Opening PRV query for '%s'.\n", name); | |
971 if (openWindow(name, TRUE)) | |
972 printMsgQ(currWin, "In PRV query with '%s'.\n", name); | |
973 th_free(name); | |
974 } | |
975 } | |
976 else | |
977 { | |
978 printMsgQ(currWin, "Usage: /query username\n"); | |
979 printMsgQ(currWin, "To close a PRV query, use /close [username]\n"); | |
980 printMsgQ(currWin, "/close without username will close the current PRV window.\n"); | |
981 } | |
982 return 0; | |
983 } | |
984 else if (!th_strncasecmp(buf, "/win", 4)) | |
985 { | |
986 /* Change color */ | |
987 char *tmp = str_trim_left(buf + 4); | |
988 if (strlen(tmp) > 0) | |
989 { | |
990 int val = atoi(tmp); | |
991 if (val >= 1 && val < SET_MAX_WINDOWS) | |
992 { | |
993 if (chatWindows[val - 1] != NULL) | |
994 currWin = chatWindows[val - 1]; | |
995 } | |
996 else | |
997 { | |
998 printMsgQ(currWin, "Invalid window number '%s'\n", tmp); | |
999 return 1; | |
1000 } | |
1001 } | |
1002 else | |
1003 { | |
1004 printMsgQ(currWin, "Window : #%d\n", currWin->num); | |
1005 printMsgQ(currWin, "ID : %s\n", currWin->id); | |
1006 } | |
1007 return 0; | |
1008 } | |
1009 else if (!th_strncasecmp(buf, "/close", 6)) | |
1010 { | |
1011 char *name = str_trim_left(buf + 6); | |
1012 if (strlen(name) > 0) | |
1013 { | |
1014 nn_window_t *win = findWindow(name); | |
1015 if (win != NULL) | |
1016 { | |
1017 closeWindow(win); | |
1018 printMsgQ(currWin, "Closed PRV query to '%s'.\n", name); | |
1019 } | |
1020 else | |
1021 { | |
1022 printMsgQ(currWin, "No PRV query by name '%s'.\n", name); | |
1023 } | |
1024 } | |
1025 else | |
1026 { | |
1027 if (currWin != chatWindows[0]) | |
1028 { | |
1029 closeWindow(currWin); | |
1030 currWin = chatWindows[0]; | |
1031 } | |
1032 } | |
1033 return 0; | |
1034 } | |
1035 else if (!th_strncasecmp(buf, "/save", 5)) | |
1036 { | |
1037 /* Save configuration */ | |
1038 FILE *cfgfile = fopen(setConfigFile, "w"); | |
1039 if (cfgfile == NULL) | |
1040 { | |
1041 printMsgQ(currWin, "Could not create configuration to file '%s': %s\n", | |
1042 setConfigFile, strerror(errno)); | |
1043 return 0; | |
1044 } | |
1045 printMsgQ(currWin, "Configuration saved in file '%s', res=%d\n", | |
1046 setConfigFile, | |
1047 th_cfg_write(cfgfile, setConfigFile, cfg)); | |
1048 | |
1049 fclose(cfgfile); | |
1050 return 0; | |
1051 } | |
1052 else if (!th_strncasecmp(buf, "/w ", 3)) | |
1053 { | |
1054 /* Open given username's profile via firefox in a new tab */ | |
1055 char *name = str_trim_left(buf + 3); | |
1056 | |
1057 printMsg(currWin, "Opening profile for: '%s'\n", name); | |
1058 | |
1059 tmpStr = nn_encode_str1(name); | |
1060 #ifdef __WIN32 | |
1061 { | |
1062 HINSTANCE status; | |
1063 snprintf(tmpBuf, sizeof(tmpBuf), "http://www.newbienudes.com/profile/%s/", tmpStr); | |
1064 th_free(tmpStr); | |
1065 status = ShellExecute(NULL, "open", tmpBuf, NULL, NULL, SW_SHOWNA); | |
1066 if (status <= (HINSTANCE) 32) | |
1067 printMsgQ(currWin, "Could not launch default web browser: %d\n", status); | |
1068 } | |
1069 #else | |
1070 { | |
1071 int status; | |
1072 int fds[2]; | |
1073 pid_t pid; | |
1074 snprintf(tmpBuf, sizeof(tmpBuf), "openurl(http://www.newbienudes.com/profile/%s/,new-tab)", tmpStr); | |
1075 th_free(tmpStr); | |
1076 | |
1077 if (pipe(fds) == -1) | |
1078 { | |
1079 int ret = errno; | |
1080 printMsgQ(currWin, "Could not open process communication pipe! (%d, %s)\n", ret, strerror(ret)); | |
1081 return 0; | |
1082 } | |
1083 | |
1084 if ((pid = fork()) < 0) | |
1085 { | |
1086 printMsgQ(currWin, "Could not create sub-process!\n"); | |
1087 } | |
1088 else if (pid == 0) | |
1089 { | |
1090 dup2(fds[1], STDOUT_FILENO); | |
1091 dup2(fds[0], STDERR_FILENO); | |
1092 execlp(setBrowser, setBrowser, "-remote", tmpBuf, (void *)NULL); | |
1093 _exit(errno); | |
1094 } | |
1095 | |
1096 wait(&status); | |
1097 } | |
1098 #endif | |
1099 return 0; | |
1100 } | |
1101 else if (!th_strncasecmp(buf, "/who", 4)) | |
1102 { | |
1103 /* Alias /who to /listallusers */ | |
1104 snprintf(tmpBuf, sizeof(tmpBuf), "/listallusers"); | |
1105 buf = tmpBuf; | |
1106 } | |
1107 | |
1108 if (currWin != chatWindows[0]) | |
1109 { | |
1110 if (currWin->id != NULL) | |
1111 { | |
1112 snprintf(tmpBuf, sizeof(tmpBuf), "/prv -to %s -msg %s", currWin->id, buf); | |
1113 buf = tmpBuf; | |
1114 } | |
1115 else | |
1116 { | |
1117 printMsgQ(NULL, "No target set, exiting prv mode.\n"); | |
1118 return 1; | |
1119 } | |
1120 } | |
1121 | |
1122 /* Send double-encoded */ | |
1123 tmpStr = nn_dblencode_str(nn_username_decode(buf)); | |
1124 if (tmpStr == 0) return -2; | |
1125 result = nn_conn_send_msg(conn, optUserNameEnc, "%s", tmpStr); | |
1126 th_free(tmpStr); | |
1127 | |
1128 return result ? 0 : -1; | |
1129 } | |
1130 | |
1131 | |
1132 void closeWindows(void) | |
1133 { | |
1134 if (mainWin) delwin(mainWin); | |
1135 if (statusWin) delwin(statusWin); | |
1136 if (editWin) delwin(editWin); | |
1137 } | |
1138 | |
1139 | |
1140 BOOL initializeWindows(void) | |
1141 { | |
1142 int w, h; | |
1143 | |
1144 getmaxyx(stdscr, h, w); | |
1145 | |
1146 closeWindows(); | |
1147 | |
1148 mainWin = subwin(stdscr, h - 4, w, 0, 0); | |
1149 statusWin = subwin(stdscr, 1, w, h - 4, 0); | |
1150 editWin = subwin(stdscr, 3, w, h - 3, 0); | |
1151 | |
1152 if (mainWin == NULL || statusWin == NULL || editWin == NULL) | |
1153 return FALSE; | |
1154 | |
1155 return TRUE; | |
1156 } | |
1157 | |
1158 | |
1159 void updateWindows(void) | |
1160 { | |
1161 if (mainWin) redrawwin(mainWin); | |
1162 if (statusWin) redrawwin(statusWin); | |
1163 if (editWin) redrawwin(editWin); | |
1164 } | |
1165 | |
1166 | |
1167 BOOL performTabCompletion(nn_editbuf_t *buf) | |
1168 { | |
1169 static char *previous = NULL, *pattern = NULL; | |
1170 BOOL again = FALSE, hasSeparator = FALSE, newPattern = FALSE, hasSpace = FALSE; | |
1171 char *str = buf->data; | |
1172 int mode = 0; | |
1173 ssize_t endPos, startPos = buf->pos; | |
1174 | |
1175 /* previous word */ | |
1176 if (startPos >= 2 && str[startPos - 1] == ' ' && str[startPos - 2] != ' ') | |
1177 { | |
1178 startPos -= 2; | |
1179 endPos = startPos; | |
1180 while (startPos > 0 && str[startPos - 1] != ' ') startPos--; | |
1181 mode = 1; | |
1182 } | |
1183 else | |
1184 /* middle of a word, new pattern */ | |
1185 if (startPos < buf->len && str[startPos] != ' ') | |
1186 { | |
1187 endPos = startPos; | |
1188 while (startPos > 0 && str[startPos - 1] != ' ') startPos--; | |
1189 while (endPos < buf->len - 1 && str[endPos + 1] != ' ') endPos++; | |
1190 newPattern = TRUE; | |
1191 mode = 2; | |
1192 } | |
1193 else | |
1194 /* previous word, new pattern */ | |
1195 if (startPos >= 1 && str[startPos - 1] != ' ') | |
1196 { | |
1197 startPos -= 1; | |
1198 endPos = startPos; | |
1199 while (startPos > 0 && str[startPos - 1] != ' ') startPos--; | |
1200 newPattern = TRUE; | |
1201 mode = 3; | |
1202 } | |
1203 else | |
1204 { | |
1205 if (optDebug) | |
1206 printMsg(currWin, "no mode\n"); | |
1207 return FALSE; | |
1208 } | |
1209 | |
1210 if (str[endPos] == optNickSep) | |
1211 { | |
1212 endPos--; | |
1213 if (startPos > 0) | |
1214 { | |
1215 if (optDebug) | |
1216 printMsg(currWin, "str[endPos] == optNickSep && startPos > 0 (%d)\n", startPos); | |
1217 return FALSE; | |
1218 } | |
1219 hasSeparator = TRUE; | |
1220 } | |
1221 | |
1222 if (buf->pos > 0 && str[buf->pos - 1] == ' ') | |
1223 hasSpace = TRUE; | |
1224 if (buf->pos <= buf->len && str[buf->pos] == ' ') | |
1225 hasSpace = TRUE; | |
1226 | |
1227 if (newPattern) | |
1228 { | |
1229 /* Get pattern, check if it matches previous pattern and set 'again' flag */ | |
1230 char *npattern = nn_editbuf_get_string(buf, startPos, endPos); | |
1231 if (pattern && npattern && th_strcasecmp(npattern, pattern) == 0) | |
1232 again = TRUE; | |
1233 | |
1234 th_free(pattern); | |
1235 pattern = npattern; | |
1236 | |
1237 if (!again) | |
1238 { | |
1239 th_free(previous); | |
1240 previous = NULL; | |
1241 } | |
1242 } | |
1243 | |
1244 if (optDebug) | |
1245 { | |
1246 printMsg(currWin, "sPos=%d, ePos=%d <-> bPos=%d, bufLen=%d : pat='%s' (again=%s, hassep=%s, hasspc=%s, newpat=%s, mode=%d)\n", | |
1247 startPos, endPos, buf->pos, buf->len, pattern, | |
1248 again ? "yes" : "no", | |
1249 hasSeparator ? "yes" : "no", | |
1250 hasSpace ? "yes" : "no", | |
1251 newPattern ? "yes" : "no", mode); | |
1252 } | |
1253 | |
1254 if (pattern) | |
1255 { | |
1256 nn_user_t *user = nn_user_match(nnUsers, pattern, previous, again); | |
1257 | |
1258 if (user) | |
1259 { | |
1260 int i; | |
1261 char *c = user->name; | |
1262 if (optDebug) | |
1263 printMsg(currWin, "match='%s' / prev='%s'\n", user->name, previous); | |
1264 | |
1265 for (i = startPos; i <= endPos; i++) | |
1266 nn_editbuf_delete(buf, startPos); | |
1267 | |
1268 for (i = startPos; *c; i++, c++) | |
1269 nn_editbuf_insert(buf, i, *c); | |
1270 | |
1271 if (!hasSeparator && startPos == 0) | |
1272 { | |
1273 nn_editbuf_insert(buf, i++, optNickSep); | |
1274 startPos++; | |
1275 } | |
1276 if (hasSeparator) | |
1277 startPos++; | |
1278 if (!hasSpace) | |
1279 nn_editbuf_insert(buf, i++, ' '); | |
1280 | |
1281 nn_editbuf_setpos(buf, startPos + 1 + strlen(user->name)); | |
1282 | |
1283 th_free(previous); | |
1284 previous = th_strdup(user->name); | |
1285 | |
1286 return TRUE; | |
1287 } | |
1288 } | |
1289 | |
1290 return FALSE; | |
1291 } | |
1292 | |
1293 | |
1294 #define VPUTCH(CH) th_vputch(&bufData, &bufSize, &bufLen, CH) | |
1295 #define VPUTS(STR) th_vputs(&bufData, &bufSize, &bufLen, STR) | |
1296 | |
1297 char *logParseFilename(const char *fmt, int id) | |
1298 { | |
1299 size_t bufSize = strlen(fmt) + TH_BUFGROW, bufLen = 0; | |
1300 char *bufData = th_malloc(bufSize); | |
1301 char tmpBuf[32]; | |
1302 const char *s = fmt; | |
1303 | |
1304 while (*s) | |
1305 { | |
1306 if (*s == '%') | |
1307 { | |
1308 s++; | |
1309 switch (*s) | |
1310 { | |
1311 case 'i': | |
1312 snprintf(tmpBuf, sizeof(tmpBuf), "%05d", id); | |
1313 VPUTS(tmpBuf); | |
1314 break; | |
1315 | |
1316 case 'd': | |
1317 snprintf(tmpBuf, sizeof(tmpBuf), "%d", id); | |
1318 VPUTS(tmpBuf); | |
1319 break; | |
1320 | |
1321 case '%': | |
1322 VPUTCH('%'); | |
1323 break; | |
1324 } | |
1325 s++; | |
1326 } | |
1327 else | |
1328 { | |
1329 VPUTCH(*s); | |
1330 s++; | |
1331 } | |
1332 } | |
1333 | |
1334 VPUTCH(0); | |
1335 return bufData; | |
1336 } | |
1337 | |
1338 | |
1339 BOOL logFileOpen(void) | |
1340 { | |
1341 char *filename; | |
1342 | |
1343 if (optLogFilename == NULL || !optLogEnable) | |
1344 return FALSE; | |
1345 | |
1346 filename = logParseFilename(optLogFilename, optPort); | |
1347 | |
1348 if ((optLogFile = fopen(filename, "a")) == NULL) | |
1349 { | |
1350 errorMsg("Could not open logfile '%s' for appending!\n", filename); | |
1351 th_free(filename); | |
1352 return FALSE; | |
1353 } | |
1354 | |
1355 th_free(filename); | |
1356 | |
1357 return TRUE; | |
1358 } | |
1359 | |
1360 | |
1361 void logFileClose(void) | |
1362 { | |
1363 if (optLogFile) | |
1364 { | |
1365 fclose(optLogFile); | |
1366 optLogFile = NULL; | |
1367 } | |
1368 } | |
1369 | |
1370 | |
1371 char *promptRequester(WINDOW *win, const char *info, BOOL allowEmpty) | |
1372 { | |
1373 char tmpBuf[512], *ptr; | |
1374 ssize_t pos; | |
1375 int curVis = curs_set(1); | |
1376 | |
1377 echo(); | |
1378 waddstr(win, info); | |
1379 wgetnstr(win, tmpBuf, sizeof(tmpBuf) - 1); | |
1380 noecho(); | |
1381 if (curVis != ERR) | |
1382 curs_set(curVis); | |
1383 | |
1384 for (pos = strlen(tmpBuf) - 1; pos > 0 && th_isspace(tmpBuf[pos]); pos--) | |
1385 tmpBuf[pos] = 0; | |
1386 | |
1387 ptr = str_trim_left(tmpBuf); | |
1388 | |
1389 if (allowEmpty || strlen(ptr) > 0) | |
1390 return th_strdup(ptr); | |
1391 else | |
1392 return NULL; | |
1393 } | |
1394 | |
1395 | |
1396 void printHelp(void) | |
1397 { | |
1398 printMsgQ(currWin, "\n" | |
1399 "NNChat Help\n" | |
1400 "===========\n" | |
1401 "\n" | |
1402 "F1 This help.\n" | |
1403 "F2 \n" | |
1404 ); | |
1405 } | |
1406 | |
1407 | |
1408 int main(int argc, char *argv[]) | |
1409 { | |
1410 nn_conn_t *conn = NULL; | |
1411 int curVis = ERR, updateCount = 0; | |
1412 BOOL argsOK, isError = FALSE, | |
1413 exitProg = FALSE, | |
1414 colorSet = FALSE, | |
1415 cursesInit = FALSE, | |
1416 networkInit = FALSE, | |
1417 insertMode = TRUE, | |
1418 firstUpdate = TRUE; | |
1419 time_t prevTime; | |
1420 char *tmpStr; | |
1421 nn_editbuf_t *editBuf = nn_editbuf_new(NN_TMPBUF_SIZE); | |
1422 nn_editbuf_t *histBuf[SET_MAX_HISTORY+2]; | |
1423 int histPos = 0, histMax = 0; | |
1424 | |
1425 cfgitem_t *tmpcfg; | |
1426 char *homeDir = NULL; | |
1427 | |
1428 memset(histBuf, 0, sizeof(histBuf)); | |
1429 | |
1430 /* Initialize */ | |
1431 th_init("NNChat", "Newbie Nudes chat client", NN_VERSION, | |
1432 "Written and designed by Anonymous Finnish Guy (C) 2008-2012", | |
1433 "This software is freeware, use and distribute as you wish."); | |
1434 th_verbosityLevel = 0; | |
1435 | |
1436 /* Read configuration file */ | |
1437 tmpcfg = NULL; | |
1438 th_cfg_add_comment(&tmpcfg, "General settings"); | |
1439 th_cfg_add_string(&tmpcfg, "username", &optUserName, NULL); | |
1440 th_cfg_add_string(&tmpcfg, "password", &optPassword, NULL); | |
1441 | |
1442 th_cfg_add_comment(&tmpcfg, "Default color as a hex-triplet"); | |
1443 th_cfg_add_hexvalue(&tmpcfg, "color", &optUserColor, optUserColor); | |
1444 | |
1445 th_cfg_add_comment(&tmpcfg, "Default setting of ignore mode"); | |
1446 th_cfg_add_bool(&tmpcfg, "ignore", &setIgnoreMode, setIgnoreMode); | |
1447 th_cfg_add_comment(&tmpcfg, "People to be ignored when ignore mode is enabled"); | |
1448 th_cfg_add_string_list(&tmpcfg, "ignore_list", &setIgnoreList); | |
1449 | |
1450 th_cfg_add_comment(&tmpcfg, "Random messages for idle timeout protection. If none are set, plain '.' is used."); | |
1451 th_cfg_add_string_list(&tmpcfg, "idle_messages", &setIdleMessages); | |
1452 | |
1453 th_cfg_add_comment(&tmpcfg, "Character used as nickname auto-completion separator (default is ':')"); | |
1454 th_cfg_add_string(&tmpcfg, "nick_separator", &optNickSepStr, NULL); | |
1455 | |
1456 th_cfg_add_section(&cfg, "general", tmpcfg); | |
1457 | |
1458 | |
1459 tmpcfg = NULL; | |
1460 th_cfg_add_comment(&tmpcfg, "Chat server hostname or IP address"); | |
1461 th_cfg_add_string(&tmpcfg, "host", &optServer, optServer); | |
1462 th_cfg_add_comment(&tmpcfg, "Default port to connect to (8005 = main room, 8003 = passion pit)"); | |
1463 th_cfg_add_int(&tmpcfg, "port", &optPort, optPort); | |
1464 th_cfg_add_section(&cfg, "server", tmpcfg); | |
1465 | |
1466 tmpcfg = NULL; | |
1467 th_cfg_add_comment(&tmpcfg, "Proxy server type (0 = none, 1 = SOCKS 4, 2 = SOCKS 4a)"); | |
1468 th_cfg_add_int(&tmpcfg, "type", &optProxyType, optProxyType); | |
1469 th_cfg_add_comment(&tmpcfg, "Proxy server host name"); | |
1470 th_cfg_add_string(&tmpcfg, "host", &optProxyServer, optProxyServer); | |
1471 th_cfg_add_comment(&tmpcfg, "Proxy port, 1080 is the standard SOCKS port"); | |
1472 th_cfg_add_int(&tmpcfg, "port", &optProxyPort, optProxyPort); | |
1473 th_cfg_add_section(&cfg, "proxy", tmpcfg); | |
1474 | |
1475 tmpcfg = NULL; | |
1476 th_cfg_add_comment(&tmpcfg, "Enable logging"); | |
1477 th_cfg_add_bool(&tmpcfg, "enable", &optLogEnable, optLogEnable); | |
1478 th_cfg_add_comment(&tmpcfg, "Log filename format"); | |
1479 th_cfg_add_string(&tmpcfg, "filename", &optLogFilename, optLogFilename); | |
1480 th_cfg_add_section(&cfg, "logging", tmpcfg); | |
1481 | |
1482 #ifdef __WIN32 | |
1483 { | |
1484 char tmpPath[MAX_PATH]; | |
1485 if (SHGetFolderPath(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, tmpPath) == S_OK) | |
1486 homeDir = th_strdup(tmpPath); | |
1487 | |
1488 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); | |
1489 } | |
1490 #else | |
1491 homeDir = th_strdup(getenv("HOME")); | |
1492 #endif | |
1493 | |
1494 if (homeDir != NULL) | |
1495 { | |
1496 FILE *cfgfile; | |
1497 setConfigFile = th_strdup_printf("%s" SET_DIR_SEPARATOR "%s", homeDir, SET_CONFIG_FILE); | |
1498 | |
1499 THMSG(0, "Reading configuration from '%s'.\n", setConfigFile); | |
1500 | |
1501 if ((cfgfile = fopen(setConfigFile, "r")) != NULL) | |
1502 { | |
1503 th_cfg_read(cfgfile, setConfigFile, cfg); | |
1504 fclose(cfgfile); | |
1505 } | |
1506 } | |
1507 | |
1508 if (optNickSepStr) | |
1509 optNickSep = optNickSepStr[0]; | |
1510 else | |
1511 optNickSep = SET_NICK_SEPARATOR; | |
1512 | |
1513 | |
1514 setBrowser = getenv("BROWSER"); | |
1515 if (setBrowser == NULL) | |
1516 setBrowser = "firefox"; | |
1517 | |
1518 /* Parse command line arguments */ | |
1519 argsOK = th_args_process(argc, argv, optList, optListN, | |
1520 argHandleOpt, argHandleFile, FALSE); | |
1521 | |
1522 if (optUserNameCmd != NULL) | |
1523 { | |
1524 optUserName = optUserNameCmd; | |
1525 optPassword = optPasswordCmd; | |
1526 } | |
1527 | |
1528 if (!argsOK) | |
1529 return -2; | |
1530 | |
1531 /* Allocate userhash */ | |
1532 if ((nnUsers = nn_userhash_new()) == NULL) | |
1533 { | |
1534 THERR("Could not allocate userhash. Fatal error.\n"); | |
1535 return -105; | |
1536 } | |
1537 | |
1538 /* If no idle messages are set, add default */ | |
1539 if (setIdleMessages == NULL) | |
1540 { | |
1541 th_llist_append(&setIdleMessages, th_strdup(".")); | |
1542 } | |
1543 | |
1544 /* Open logfile */ | |
1545 logFileOpen(); | |
1546 | |
1547 /* Initialize network */ | |
1548 if (!nn_network_init()) | |
1549 { | |
1550 THERR("Could not initialize network subsystem.\n"); | |
1551 goto err_exit; | |
1552 } | |
1553 else | |
1554 networkInit = TRUE; | |
1555 | |
1556 /* Initialize NCurses */ | |
1557 if (!optDaemon) | |
1558 { | |
1559 if (LINES < 0 || LINES > 1000) LINES = 24; | |
1560 if (COLS < 0 || COLS > 1000) COLS = 80; | |
1561 initscr(); | |
1562 raw(); | |
1563 keypad(stdscr, TRUE); | |
1564 noecho(); | |
1565 meta(stdscr, TRUE); | |
1566 timeout(SET_DELAY); | |
1567 curVis = curs_set(0); | |
1568 | |
1569 if (has_colors()) | |
1570 { | |
1571 start_color(); | |
1572 | |
1573 init_pair( 1, COLOR_RED, COLOR_BLACK); | |
1574 init_pair( 2, COLOR_GREEN, COLOR_BLACK); | |
1575 init_pair( 3, COLOR_YELLOW, COLOR_BLACK); | |
1576 init_pair( 4, COLOR_BLUE, COLOR_BLACK); | |
1577 init_pair( 5, COLOR_MAGENTA, COLOR_BLACK); | |
1578 init_pair( 6, COLOR_CYAN, COLOR_BLACK); | |
1579 init_pair( 7, COLOR_WHITE, COLOR_BLACK); | |
1580 init_pair( 8, COLOR_BLACK, COLOR_BLACK); | |
1581 | |
1582 init_pair(10, COLOR_BLACK, COLOR_RED); | |
1583 init_pair(11, COLOR_WHITE, COLOR_RED); | |
1584 init_pair(12, COLOR_GREEN, COLOR_RED); | |
1585 init_pair(13, COLOR_YELLOW, COLOR_RED); | |
1586 init_pair(14, COLOR_BLUE, COLOR_RED); | |
1587 init_pair(15, COLOR_MAGENTA, COLOR_RED); | |
1588 init_pair(16, COLOR_CYAN, COLOR_RED); | |
1589 } | |
1590 | |
1591 cursesInit = TRUE; | |
1592 | |
1593 if (!initializeWindows()) | |
1594 goto err_exit; | |
1595 | |
1596 #ifdef PDCURSES | |
1597 PDC_set_title("NNChat v" NN_VERSION); | |
1598 #endif | |
1599 | |
1600 memset(chatWindows, 0, sizeof(chatWindows)); | |
1601 chatWindows[0] = nn_window_new(NULL); | |
1602 currWin = chatWindows[0]; | |
1603 updateStatus(); | |
1604 } | |
1605 | |
1606 /* Check if we have username and password */ | |
1607 if (cursesInit && (optUserName == NULL || optPassword == NULL)) | |
1608 { | |
1609 printWin(editWin, "You can avoid this prompt by issuing '/save' after logging in.\n"); | |
1610 optUserName = promptRequester(editWin, "NN username: ", FALSE); | |
1611 optPassword = promptRequester(editWin, "NN password: ", TRUE); | |
1612 } | |
1613 | |
1614 if (optUserName == NULL || optPassword == NULL) | |
1615 { | |
1616 errorMsg("Username and/or password not specified.\n"); | |
1617 goto err_exit; | |
1618 } | |
1619 | |
1620 /* Create a connection */ | |
1621 conn = nn_conn_new(errorFunc, messageFunc); | |
1622 if (conn == NULL) | |
1623 { | |
1624 errorMsg("Could not create connection structure.\n"); | |
1625 goto err_exit; | |
1626 } | |
1627 | |
1628 /* Are we using a proxy? */ | |
1629 if (optProxyType != NN_PROXY_NONE && optProxyServer != NULL) | |
1630 { | |
1631 if (nn_conn_set_proxy(conn, optProxyType, optProxyPort, optProxyServer) != 0) | |
1632 { | |
1633 errorMsg("Error setting proxy information.\n"); | |
1634 goto err_exit; | |
1635 } | |
1636 } | |
1637 | |
1638 /* Okay ... */ | |
1639 printMsg(currWin, "Trying to resolve host '%s' ...\n", optServer); | |
1640 conn->host = th_strdup(optServer); | |
1641 conn->hst = nn_resolve_host(conn, optServer); | |
1642 if (conn->hst == NULL) | |
1643 { | |
1644 errorMsg("Could not resolve hostname: %s.\n", strerror(h_errno)); | |
1645 goto err_exit; | |
1646 } | |
1647 | |
1648 #ifdef FINAL_BUILD | |
1649 /* To emulate the official client, we first make a request for | |
1650 * policy file, even though we don't use it for anything... | |
1651 */ | |
1652 if (nn_conn_open(conn, 843, NULL) != 0) | |
1653 { | |
1654 errorMsg("Policy file request connection setup failed!\n"); | |
1655 goto err_exit; | |
1656 } | |
1657 | |
1658 tmpStr = "<policy-file-request/>"; | |
1659 if (nn_conn_send_buf(conn, tmpStr, strlen(tmpStr) + 1) == FALSE) | |
1660 { | |
1661 errorMsg("Failed to send policy file request.\n"); | |
1662 goto err_exit; | |
1663 } | |
1664 else | |
1665 { | |
1666 int cres = nn_conn_pull(conn); | |
1667 if (cres == 0) | |
1668 { | |
1669 printMsg(currWin, "Probe got: %s\n", conn->buf); | |
1670 } | |
1671 else | |
1672 { | |
1673 printMsg(currWin, "Could not get policy probe.\n"); | |
1674 } | |
1675 } | |
1676 nn_conn_close(conn); | |
1677 #endif | |
1678 | |
1679 /* Okay, now do the proper connection ... */ | |
1680 if (nn_conn_open(conn, optPort, NULL) != 0) | |
1681 { | |
1682 errorMsg("Main connection setup failed!\n"); | |
1683 goto err_exit; | |
1684 } | |
1685 | |
1686 /* Send login command */ | |
1687 optUserNameEnc = nn_dblencode_str(optUserName); | |
1688 tmpStr = nn_dblencode_str(optSite); | |
1689 nn_conn_send_msg(conn, optUserNameEnc, "%%2Flogin%%20%%2Dsite%%20%s%%20%%2Dpassword%%20%s", tmpStr, optPassword); | |
1690 th_free(tmpStr); | |
1691 | |
1692 /* Initialize random numbers */ | |
1693 prevTime = time(NULL); | |
1694 srandom((int) prevTime); | |
1695 | |
1696 if (cursesInit) | |
1697 { | |
1698 /* Initialize rest of interactive UI code */ | |
1699 nn_editbuf_clear(editBuf); | |
1700 | |
1701 /* First update of screen */ | |
1702 printEditBuf(editBuf); | |
1703 updateStatus(); | |
1704 | |
1705 printMsg(NULL, "%s v%s - %s\n", th_prog_name, th_prog_version, th_prog_fullname); | |
1706 printMsg(NULL, "%s\n", th_prog_author); | |
1707 printMsg(NULL, "%s\n", th_prog_license); | |
1708 } | |
1709 | |
1710 /* Enter mainloop */ | |
1711 nn_conn_reset(conn); | |
1712 while (!isError && !exitProg) | |
1713 { | |
1714 nn_conn_reset(conn); | |
1715 do { | |
1716 int cres = nn_conn_pull(conn); | |
1717 if (cres == 0 && *(conn->in_ptr - 1) == 0) | |
1718 { | |
1719 int result = nn_parse_protocol(conn); | |
1720 if (result > 0) | |
1721 { | |
1722 /* Couldn't handle the message for some reason */ | |
1723 printMsg(currWin, "Could not handle: %s\n", conn->ptr); | |
1724 } | |
1725 else if (result < 0) | |
1726 { | |
1727 /* Fatal error, quit */ | |
1728 errorMsg("Fatal error with message: %s\n", conn->ptr); | |
1729 isError = TRUE; | |
1730 } | |
1731 } | |
1732 else if (cres < 0) | |
1733 isError = TRUE; | |
1734 else | |
1735 break; | |
1736 } | |
1737 while (conn->total_bytes > 0 && !isError); | |
1738 | |
1739 if (!nn_conn_check(conn)) | |
1740 isError = TRUE; | |
1741 | |
1742 /* Handle user input */ | |
1743 if (cursesInit) | |
1744 { | |
1745 int c, cnt = 0; | |
1746 BOOL update = FALSE, updateMain = FALSE; | |
1747 | |
1748 /* Handle several buffered keypresses at once */ | |
1749 do | |
1750 { | |
1751 c = wgetch(stdscr); | |
1752 | |
1753 /* Handle various problematic cases where terminal | |
1754 * keycodes do not get properly translated by curses | |
1755 */ | |
1756 if (c == 0x1b) | |
1757 { | |
1758 /* ^[O */ | |
1759 c = wgetch(stdscr); | |
1760 if (c == 'O') | |
1761 { | |
1762 c = wgetch(stdscr); | |
1763 switch (c) | |
1764 { | |
1765 case 'd': | |
1766 c = 0x204; | |
1767 break; | |
1768 case 'c': | |
1769 c = 0x206; | |
1770 break; | |
1771 default: | |
1772 if (optDebug) | |
1773 printMsg(currWin, "Unhandled ESC-O key sequence 0x%02x\n", c); | |
1774 break; | |
1775 } | |
1776 } | |
1777 /* ^[[ */ | |
1778 else if (c == '[') | |
1779 { | |
1780 c = wgetch(stdscr); | |
1781 switch (c) | |
1782 { | |
1783 case 0x31: | |
1784 c = wgetch(stdscr); | |
1785 if (c >= 0x31 && c <= 0x39) | |
1786 c = KEY_F(c - 0x30); | |
1787 else | |
1788 c = ERR; | |
1789 break; | |
1790 | |
1791 case 0x32: | |
1792 c = KEY_IC; | |
1793 break; | |
1794 case 0x33: | |
1795 c = KEY_DC; | |
1796 break; | |
1797 | |
1798 case 0x35: | |
1799 c = KEY_PPAGE; | |
1800 break; | |
1801 case 0x36: | |
1802 c = KEY_NPAGE; | |
1803 break; | |
1804 | |
1805 case 0x37: | |
1806 c = KEY_HOME; | |
1807 break; | |
1808 case 0x38: | |
1809 c = KEY_END; | |
1810 break; | |
1811 | |
1812 default: | |
1813 if (optDebug) | |
1814 printMsg(currWin, "Unhandled ESC-[*~ key sequence 0x%02x\n", c); | |
1815 c = ERR; | |
1816 break; | |
1817 } | |
1818 /* Get the trailing ~ */ | |
1819 if (c != ERR) | |
1820 wgetch(stdscr); | |
1821 } | |
1822 if (c >= 0x31 && c <= 0x39) | |
1823 { | |
1824 /* Chat window switching via Meta/Esc-[1..9] */ | |
1825 int win = c - 0x31; | |
1826 if (win < SET_MAX_WINDOWS && chatWindows[win] != NULL) | |
1827 { | |
1828 currWin = chatWindows[win]; | |
1829 update = updateMain = TRUE; | |
1830 } | |
1831 c = ERR; | |
1832 } | |
1833 else | |
1834 { | |
1835 if (optDebug) | |
1836 printMsg(currWin, "Unhandled ESC key sequence 0x%02x\n", c); | |
1837 } | |
1838 } | |
1839 #if defined(__WIN32) && defined(PDCURSES) | |
1840 else if (c >= 0x198 && c <= 0x1a0) | |
1841 { | |
1842 /* Chat window switching via Meta/Esc-[1..9] */ | |
1843 int win = c - 0x198; | |
1844 if (win < SET_MAX_WINDOWS && chatWindows[win] != NULL) | |
1845 { | |
1846 currWin = chatWindows[win]; | |
1847 update = updateMain = TRUE; | |
1848 } | |
1849 c = ERR; | |
1850 } | |
1851 #endif | |
1852 | |
1853 switch (c) | |
1854 { | |
1855 #ifdef KEY_RESIZE | |
1856 case KEY_RESIZE: | |
1857 resize_term(0, 0); | |
1858 erase(); | |
1859 timeout(SET_DELAY); | |
1860 | |
1861 if (!initializeWindows()) | |
1862 { | |
1863 errorMsg("Error resizing curses chatWindows\n"); | |
1864 isError = TRUE; | |
1865 } | |
1866 update = updateMain = TRUE; | |
1867 break; | |
1868 #endif | |
1869 | |
1870 case KEY_ENTER: | |
1871 case '\n': | |
1872 case '\r': | |
1873 /* Call the user input handler */ | |
1874 if (editBuf->len > 0) | |
1875 { | |
1876 int result; | |
1877 | |
1878 if (histMax > 0) | |
1879 { | |
1880 nn_editbuf_free(histBuf[SET_MAX_HISTORY+1]); | |
1881 histBuf[SET_MAX_HISTORY+1] = NULL; | |
1882 memmove(&histBuf[2], &histBuf[1], histMax * sizeof(histBuf[0])); | |
1883 } | |
1884 | |
1885 histPos = 0; | |
1886 histBuf[1] = nn_editbuf_copy(editBuf); | |
1887 if (histMax < SET_MAX_HISTORY) histMax++; | |
1888 | |
1889 nn_editbuf_insert(editBuf, editBuf->len, 0); | |
1890 result = nn_handle_input(conn, editBuf->data, editBuf->len); | |
1891 | |
1892 nn_editbuf_clear(editBuf); | |
1893 | |
1894 if (result < 0) | |
1895 { | |
1896 errorMsg("Fatal error handling user input: %s\n", editBuf->data); | |
1897 isError = TRUE; | |
1898 } | |
1899 else | |
1900 { | |
1901 /* Update time value of last sent message for unidle timeouts */ | |
1902 prevTime = time(NULL); | |
1903 } | |
1904 | |
1905 updateMain = update = TRUE; | |
1906 } | |
1907 break; | |
1908 | |
1909 case KEY_UP: /* Backwards in input history */ | |
1910 if (histPos == 0) | |
1911 { | |
1912 nn_editbuf_free(histBuf[0]); | |
1913 histBuf[0] = nn_editbuf_copy(editBuf); | |
1914 } | |
1915 if (histPos < histMax) | |
1916 { | |
1917 histPos++; | |
1918 nn_editbuf_free(editBuf); | |
1919 editBuf = nn_editbuf_copy(histBuf[histPos]); | |
1920 update = TRUE; | |
1921 } | |
1922 break; | |
1923 | |
1924 case KEY_DOWN: /* Forwards in input history */ | |
1925 if (histPos > 0) | |
1926 { | |
1927 histPos--; | |
1928 nn_editbuf_free(editBuf); | |
1929 editBuf = nn_editbuf_copy(histBuf[histPos]); | |
1930 update = TRUE; | |
1931 } | |
1932 break; | |
1933 | |
1934 case 0x204: /* ctrl+left arrow = Skip words left */ | |
1935 case 0x20b: | |
1936 while (editBuf->pos > 0 && isspace((int) editBuf->data[editBuf->pos - 1])) | |
1937 editBuf->pos--; | |
1938 while (editBuf->pos > 0 && !isspace((int) editBuf->data[editBuf->pos - 1])) | |
1939 editBuf->pos--; | |
1940 update = TRUE; | |
1941 break; | |
1942 | |
1943 case 0x206: /* ctrl+right arrow = Skip words right */ | |
1944 case 0x210: | |
1945 while (editBuf->pos < editBuf->len && isspace((int) editBuf->data[editBuf->pos])) | |
1946 editBuf->pos++; | |
1947 while (editBuf->pos < editBuf->len && !isspace((int) editBuf->data[editBuf->pos])) | |
1948 editBuf->pos++; | |
1949 update = TRUE; | |
1950 break; | |
1951 | |
1952 case KEY_HOME: | |
1953 nn_editbuf_setpos(editBuf, 0); | |
1954 update = TRUE; | |
1955 break; | |
1956 case KEY_END: | |
1957 nn_editbuf_setpos(editBuf, editBuf->len); | |
1958 update = TRUE; | |
1959 break; | |
1960 case KEY_LEFT: | |
1961 nn_editbuf_setpos(editBuf, editBuf->pos - 1); | |
1962 update = TRUE; | |
1963 break; | |
1964 case KEY_RIGHT: | |
1965 nn_editbuf_setpos(editBuf, editBuf->pos + 1); | |
1966 update = TRUE; | |
1967 break; | |
1968 | |
1969 case KEY_BACKSPACE: | |
1970 case 0x08: | |
1971 case 0x7f: | |
1972 nn_editbuf_delete(editBuf, editBuf->pos - 1); | |
1973 nn_editbuf_setpos(editBuf, editBuf->pos - 1); | |
1974 update = TRUE; | |
1975 break; | |
1976 | |
1977 case KEY_DC: /* Delete character */ | |
1978 nn_editbuf_delete(editBuf, editBuf->pos); | |
1979 update = TRUE; | |
1980 break; | |
1981 | |
1982 | |
1983 case KEY_IC: /* Ins = Toggle insert / overwrite mode */ | |
1984 insertMode = !insertMode; | |
1985 update = TRUE; | |
1986 break; | |
1987 | |
1988 case KEY_F(1): /* F1 = Print help */ | |
1989 printHelp(); | |
1990 updateMain = TRUE; | |
1991 break; | |
1992 | |
1993 case KEY_F(2): /* F2 = Clear editbuffer */ | |
1994 nn_editbuf_clear(editBuf); | |
1995 update = TRUE; | |
1996 break; | |
1997 | |
1998 case KEY_F(5): /* F5 = Ignore mode */ | |
1999 setIgnoreMode = !setIgnoreMode; | |
2000 printMsgQ(currWin, "Ignore mode = %s\n", setIgnoreMode ? "ON" : "OFF"); | |
2001 break; | |
2002 | |
2003 #if 0 | |
2004 case KEY_F(8): /* F8 = Debug */ | |
2005 optDebug = !optDebug; | |
2006 update = TRUE; | |
2007 break; | |
2008 #endif | |
2009 | |
2010 case 0x03: /* ^C = quit */ | |
2011 case KEY_F(9): /* F9 = Quit */ | |
2012 printMsg(currWin, "Quitting per user request (%d/0x%x).\n", c, c); | |
2013 exitProg = TRUE; | |
2014 break; | |
2015 | |
2016 case 0x09: /* Tab = complete username */ | |
2017 performTabCompletion(editBuf); | |
2018 update = TRUE; | |
2019 break; | |
2020 | |
2021 case 0x0c: /* Ctrl + L */ | |
2022 updateWindows(); | |
2023 update = updateMain = TRUE; | |
2024 break; | |
2025 | |
2026 case KEY_NPAGE: | |
2027 case KEY_PPAGE: | |
2028 /* Page Up / Page Down */ | |
2029 if (currWin != NULL) | |
2030 { | |
2031 int oldPos = currWin->pos; | |
2032 | |
2033 currWin->pos += (c == KEY_NPAGE) ? -10 : +10; | |
2034 | |
2035 if (currWin->pos < 0) | |
2036 currWin->pos = 0; | |
2037 else if (currWin->pos >= currWin->data->n - 10) | |
2038 currWin->pos = currWin->data->n - 10; | |
2039 | |
2040 if (oldPos != currWin->pos) | |
2041 updateMain = TRUE; | |
2042 } | |
2043 break; | |
2044 | |
2045 case ERR: | |
2046 /* Ignore */ | |
2047 break; | |
2048 | |
2049 default: | |
2050 if (isprint(c) || c == 0xe4 || c == 0xf6 || c == 0xc4 || c == 0xd6) | |
2051 { | |
2052 if (insertMode) | |
2053 nn_editbuf_insert(editBuf, editBuf->pos, c); | |
2054 else | |
2055 nn_editbuf_write(editBuf, editBuf->pos, c); | |
2056 nn_editbuf_setpos(editBuf, editBuf->pos + 1); | |
2057 update = TRUE; | |
2058 } | |
2059 else | |
2060 { | |
2061 if (optDebug) | |
2062 printMsg(currWin, "Unhandled key: 0x%02x\n", c); | |
2063 } | |
2064 break; | |
2065 } | |
2066 } | |
2067 while (c != ERR && !exitProg && ++cnt < 10); | |
2068 | |
2069 updateMainWin(updateMain); | |
2070 | |
2071 if (update || firstUpdate) | |
2072 { | |
2073 /* Update edit line */ | |
2074 updateStatus(); | |
2075 printEditBuf(editBuf); | |
2076 firstUpdate = FALSE; /* a nasty hack ... */ | |
2077 } | |
2078 | |
2079 } /* cursesInit */ | |
2080 | |
2081 if (++updateCount > 10) | |
2082 { | |
2083 time_t tmpTime = time(NULL); | |
2084 if (tmpTime - prevTime > SET_KEEPALIVE) | |
2085 { | |
2086 int n = random() % th_llist_length(setIdleMessages); | |
2087 qlist_t *node = th_llist_get_nth(setIdleMessages, n); | |
2088 nn_conn_send_msg(conn, optUserNameEnc, node->data); | |
2089 prevTime = tmpTime; | |
2090 } | |
2091 | |
2092 if (!colorSet) | |
2093 { | |
2094 colorSet = TRUE; | |
2095 nn_conn_send_msg(conn, optUserNameEnc, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); | |
2096 } | |
2097 | |
2098 updateStatus(); | |
2099 printEditBuf(editBuf); | |
2100 updateCount = 0; | |
2101 } | |
2102 | |
2103 } | |
2104 | |
2105 /* Shutdown */ | |
2106 err_exit: | |
2107 th_cfg_free(cfg); | |
2108 th_free(homeDir); | |
2109 th_llist_free_func(setIdleMessages, th_free); | |
2110 nn_userhash_free(nnUsers); | |
2111 nn_editbuf_free(editBuf); | |
2112 | |
2113 { | |
2114 int i; | |
2115 for (i = 0; i <= SET_MAX_HISTORY; i++) | |
2116 nn_editbuf_free(histBuf[i]); | |
2117 | |
2118 for (i = 0; i < SET_MAX_WINDOWS; i++) | |
2119 nn_window_free(chatWindows[i]); | |
2120 } | |
2121 | |
2122 #ifdef __WIN32 | |
2123 if (errorMessages) | |
2124 { | |
2125 char *tmp; | |
2126 wclear(editWin); | |
2127 tmp = promptRequester(editWin, "Press enter to quit.\n", FALSE); | |
2128 th_free(tmp); | |
2129 } | |
2130 #endif | |
2131 | |
2132 if (cursesInit) | |
2133 { | |
2134 if (curVis != ERR) | |
2135 curs_set(curVis); | |
2136 closeWindows(); | |
2137 endwin(); | |
2138 THMSG(1, "NCurses deinitialized.\n"); | |
2139 } | |
2140 | |
2141 #ifndef __WIN32 | |
2142 if (errorMessages) | |
2143 THERR("%s", errorMessages); | |
2144 #endif | |
2145 | |
2146 th_free(optUserNameEnc); | |
2147 | |
2148 nn_conn_close(conn); | |
2149 | |
2150 if (networkInit) | |
2151 nn_network_close(); | |
2152 | |
2153 THMSG(1, "Connection terminated.\n"); | |
2154 | |
2155 logFileClose(); | |
2156 | |
2157 return 0; | |
2158 } |