Mercurial > hg > nnchat
comparison nnchat.c @ 96:7c9538e71c89
Add connection keepalive by sending /listallusers every 15 minutes,
due to NN server booting after about 30 minutes of inactivity; Improve
handling of error messages; Add handling for "<BOOT />" protocol token.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sun, 05 Jul 2009 16:55:40 +0300 |
parents | 638f88374e3e |
children | 218efd2f0641 |
comparison
equal
deleted
inserted
replaced
95:69f9c46e4a94 | 96:7c9538e71c89 |
---|---|
1 /* | 1 /* |
2 * NNChat - Custom chat client for NewbieNudes.com chatrooms | 2 * NNChat - Custom chat client for NewbieNudes.com chatrooms |
3 * Written by Matti 'ccr' Hämäläinen | 3 * Written by Matti 'ccr' Hämäläinen |
4 * (C) Copyright 2008 Tecnic Software productions (TNSP) | 4 * (C) Copyright 2008-2009 Tecnic Software productions (TNSP) |
5 */ | 5 */ |
6 #include "libnnchat.h" | 6 #include "libnnchat.h" |
7 #include <stdlib.h> | 7 #include <stdlib.h> |
8 #include "th_args.h" | 8 #include "th_args.h" |
9 #include <string.h> | 9 #include <string.h> |
18 | 18 |
19 #define SET_MAX_BACKBUF (1024) | 19 #define SET_MAX_BACKBUF (1024) |
20 #define SET_MAX_HISTORY (16) | 20 #define SET_MAX_HISTORY (16) |
21 #define SET_DELAY (15) | 21 #define SET_DELAY (15) |
22 #define SET_DELAY_USEC (SET_DELAY * 1000) | 22 #define SET_DELAY_USEC (SET_DELAY * 1000) |
23 #define SET_KEEPALIVE (15*60) /* Ping/keepalive period in seconds */ | |
23 | 24 |
24 | 25 |
25 /* Options | 26 /* Options |
26 */ | 27 */ |
27 int optPort = 8005; | 28 int optPort = 8005; |
28 int optUserColor = 0x006080; | 29 int optUserColor = 0x006080; |
29 char *optServer = "www11.servemedata.com", | 30 char *optServer = "chat.newbienudes.com", |
30 *optUserName = NULL, | 31 *optUserName = NULL, |
31 *optUserName2 = NULL, | 32 *optUserName2 = NULL, |
32 *optPassword = NULL, | 33 *optPassword = NULL, |
33 *optLogFilename = NULL, | 34 *optLogFilename = NULL, |
34 *setTarget = NULL, | 35 *setTarget = NULL, |
309 wrefresh(mainWin); | 310 wrefresh(mainWin); |
310 } | 311 } |
311 } | 312 } |
312 | 313 |
313 | 314 |
315 void errorMsg(char *fmt, ...) | |
316 { | |
317 char buf[8192]; | |
318 va_list ap; | |
319 | |
320 va_start(ap, fmt); | |
321 vsnprintf(buf, sizeof(buf), fmt, ap); | |
322 va_end(ap); | |
323 | |
324 printMsg(buf); | |
325 THERR(buf); | |
326 } | |
327 | |
328 | |
314 int handleUser(int sock, char *str) | 329 int handleUser(int sock, char *str) |
315 { | 330 { |
316 const char *msg = "</USER><MESSAGE>"; | 331 const char *msg = "</USER><MESSAGE>"; |
317 char *p = str, *q, *s, *t, *h; | 332 char *p = str, *q, *s, *t, *h; |
318 | 333 |
428 | 443 |
429 return 0; | 444 return 0; |
430 } | 445 } |
431 | 446 |
432 | 447 |
448 int handleBoot(int sock, char *str) | |
449 { | |
450 (void) sock; (void) str; | |
451 errorMsg("Booted by server.\n"); | |
452 return -1; | |
453 } | |
454 | |
455 | |
433 typedef struct { | 456 typedef struct { |
434 char *cmd; | 457 char *cmd; |
435 int (*handler)(int, char *); | 458 int (*handler)(int, char *); |
436 } protocmd_t; | 459 } protocmd_t; |
437 | 460 |
440 { "<USER>", handleUser }, | 463 { "<USER>", handleUser }, |
441 { "<LOGIN_", handleLogin }, | 464 { "<LOGIN_", handleLogin }, |
442 { "<DELETE_USER>", handleDeleteUser }, | 465 { "<DELETE_USER>", handleDeleteUser }, |
443 { "<ADD_USER>", handleAddUser }, | 466 { "<ADD_USER>", handleAddUser }, |
444 { "<NUMCLIENTS>", handleFoo }, | 467 { "<NUMCLIENTS>", handleFoo }, |
468 { "<BOOT />", handleBoot }, | |
445 }; | 469 }; |
446 | 470 |
447 const int nprotoCmds = (sizeof(protoCmds) / sizeof(protoCmds[0])); | 471 const int nprotoCmds = (sizeof(protoCmds) / sizeof(protoCmds[0])); |
448 | 472 |
449 | 473 |
563 exitProg = FALSE, | 587 exitProg = FALSE, |
564 colorSet = FALSE, | 588 colorSet = FALSE, |
565 cursesInit = FALSE, | 589 cursesInit = FALSE, |
566 networkInit = FALSE, | 590 networkInit = FALSE, |
567 insertMode = TRUE; | 591 insertMode = TRUE; |
568 struct timeval tv; | 592 time_t prevTime; |
593 struct timeval socktv; | |
569 fd_set sockfds; | 594 fd_set sockfds; |
570 char *tmpStr; | 595 char *tmpStr; |
571 nn_editbuf_t *editBuf = newBuf(SET_BUFSIZE); | 596 nn_editbuf_t *editBuf = newBuf(SET_BUFSIZE); |
572 nn_editbuf_t *histBuf[SET_MAX_HISTORY+2]; | 597 nn_editbuf_t *histBuf[SET_MAX_HISTORY+2]; |
573 int histPos = 0, histMax = 0; | 598 int histPos = 0, histMax = 0; |
574 | 599 |
575 memset(histBuf, 0, sizeof(histBuf)); | 600 memset(histBuf, 0, sizeof(histBuf)); |
576 | 601 |
577 /* Initialize */ | 602 /* Initialize */ |
578 th_init("NNChat", "Newbie Nudes chat client", "0.7.1", | 603 th_init("NNChat", "Newbie Nudes chat client", "0.7.2", |
579 "Written and designed by Anonymous Finnish Guy (C) 2008-2009", | 604 "Written and designed by Anonymous Finnish Guy (C) 2008-2009", |
580 "This software is freeware, use and distribute as you wish."); | 605 "This software is freeware, use and distribute as you wish."); |
581 th_verbosityLevel = 0; | 606 th_verbosityLevel = 0; |
582 | 607 |
583 /* Parse arguments */ | 608 /* Parse arguments */ |
619 goto err_exit; | 644 goto err_exit; |
620 } | 645 } |
621 THMSG(2, "True hostname: %s\n", tmpHost->h_name); | 646 THMSG(2, "True hostname: %s\n", tmpHost->h_name); |
622 | 647 |
623 | 648 |
624 #if 0 | 649 #if 1 |
625 /* To emulate the official client, we first make a request for | 650 /* To emulate the official client, we first make a request for |
626 * policy file, even though we don't use it for anything... | 651 * policy file, even though we don't use it for anything... |
627 */ | 652 */ |
628 if ((tmpSocket = openConnection((struct in_addr *) tmpHost->h_addr, optPort)) < 0) { | 653 if ((tmpSocket = openConnection((struct in_addr *) tmpHost->h_addr, 843)) < 0) { |
629 THERR("Policy file request connection setup failed!\n"); | 654 THERR("Policy file request connection setup failed!\n"); |
630 goto err_exit; | 655 goto err_exit; |
631 } | 656 } |
632 | 657 |
633 tmpStr = "<policy-file-request/>"; | 658 tmpStr = "<policy-file-request/>"; |
634 if (sendToSocket(tmpSocket, tmpStr, strlen(tmpStr) + 1) == FALSE) { | 659 if (sendToSocket(tmpSocket, tmpStr, strlen(tmpStr) + 1) == FALSE) { |
635 THERR("Failed to send fakeprobe.\n"); | 660 THERR("Failed to send policy file request.\n"); |
636 goto err_exit; | 661 goto err_exit; |
637 } else { | 662 } else { |
638 ssize_t gotBuf; | 663 ssize_t gotBuf; |
639 char tmpBuf[SET_BUFSIZE]; | 664 char tmpBuf[SET_BUFSIZE]; |
640 gotBuf = recv(tmpSocket, tmpBuf, sizeof(tmpBuf), 0); | 665 gotBuf = recv(tmpSocket, tmpBuf, sizeof(tmpBuf), 0); |
699 printEditBuf("", editBuf); | 724 printEditBuf("", editBuf); |
700 updateStatus(insertMode); | 725 updateStatus(insertMode); |
701 } | 726 } |
702 | 727 |
703 /* Enter mainloop */ | 728 /* Enter mainloop */ |
729 prevTime = time(NULL); | |
704 FD_ZERO(&sockfds); | 730 FD_ZERO(&sockfds); |
705 FD_SET(tmpSocket, &sockfds); | 731 FD_SET(tmpSocket, &sockfds); |
706 | 732 |
707 while (!isError && !exitProg) { | 733 while (!isError && !exitProg) { |
708 int result; | 734 int result; |
709 fd_set tmpfds; | 735 fd_set tmpfds; |
710 | 736 |
711 /* Check for incoming data from the server */ | 737 /* Check for incoming data from the server */ |
712 tv.tv_sec = 0; | 738 socktv.tv_sec = 0; |
713 tv.tv_usec = SET_DELAY_USEC; | 739 socktv.tv_usec = SET_DELAY_USEC; |
714 tmpfds = sockfds; | 740 tmpfds = sockfds; |
715 if ((result = select(tmpSocket+1, &tmpfds, NULL, NULL, &tv)) == -1) { | 741 if ((result = select(tmpSocket+1, &tmpfds, NULL, NULL, &socktv)) == -1) { |
716 int res = getSocketErrno(); | 742 int res = getSocketErrno(); |
717 if (res != EINTR) { | 743 if (res != EINTR) { |
718 printMsg("Error occured in select(sockfds): %d, %s\n", | 744 errorMsg("Error occured in select(sockfds): %d, %s\n", |
719 res, getSocketErrStr(res)); | 745 res, getSocketErrStr(res)); |
720 isError = TRUE; | 746 isError = TRUE; |
721 } | 747 } |
722 } else if (FD_ISSET(tmpSocket, &tmpfds)) { | 748 } else if (FD_ISSET(tmpSocket, &tmpfds)) { |
723 ssize_t gotBuf; | 749 ssize_t gotBuf; |
725 char *bufPtr = tmpBuf; | 751 char *bufPtr = tmpBuf; |
726 gotBuf = recv(tmpSocket, tmpBuf, sizeof(tmpBuf), 0); | 752 gotBuf = recv(tmpSocket, tmpBuf, sizeof(tmpBuf), 0); |
727 | 753 |
728 if (gotBuf < 0) { | 754 if (gotBuf < 0) { |
729 int res = getSocketErrno(); | 755 int res = getSocketErrno(); |
730 printMsg("Error in recv: %d, %s\n", res, getSocketErrStr(res)); | 756 errorMsg("Error in recv: %d, %s\n", res, getSocketErrStr(res)); |
731 isError = TRUE; | 757 isError = TRUE; |
732 } else if (gotBuf == 0) { | 758 } else if (gotBuf == 0) { |
733 printMsg("Server closed connection.\n"); | 759 errorMsg("Server closed connection.\n"); |
734 isError = TRUE; | 760 isError = TRUE; |
735 } else { | 761 } else { |
736 /* Handle protocol data */ | 762 /* Handle protocol data */ |
737 tmpBuf[gotBuf] = 0; | 763 tmpBuf[gotBuf] = 0; |
738 do { | 764 do { |
742 if (result > 0) { | 768 if (result > 0) { |
743 /* Couldn't handle the message for some reason */ | 769 /* Couldn't handle the message for some reason */ |
744 printMsg("Could not handle: %s\n", tmpBuf); | 770 printMsg("Could not handle: %s\n", tmpBuf); |
745 } else if (result < 0) { | 771 } else if (result < 0) { |
746 /* Fatal error, quit */ | 772 /* Fatal error, quit */ |
747 printMsg("Fatal error with message: %s\n", tmpBuf); | 773 errorMsg("Fatal error with message: %s\n", tmpBuf); |
748 isError = TRUE; | 774 isError = TRUE; |
749 } | 775 } |
750 | 776 |
751 gotBuf -= bufLen; | 777 gotBuf -= bufLen; |
752 bufPtr += bufLen; | 778 bufPtr += bufLen; |
783 | 809 |
784 switch (c) { | 810 switch (c) { |
785 #ifdef KEY_RESIZE | 811 #ifdef KEY_RESIZE |
786 case KEY_RESIZE: | 812 case KEY_RESIZE: |
787 if (!initializeWindows()) { | 813 if (!initializeWindows()) { |
788 THERR("Error resizing ncurses windows\n"); | 814 errorMsg("Error resizing ncurses windows\n"); |
789 isError = TRUE; | 815 isError = TRUE; |
790 } | 816 } |
791 break; | 817 break; |
792 #endif | 818 #endif |
793 | 819 |
811 result = handleUserInput(tmpSocket, editBuf->data, editBuf->len); | 837 result = handleUserInput(tmpSocket, editBuf->data, editBuf->len); |
812 | 838 |
813 clearBuf(editBuf); | 839 clearBuf(editBuf); |
814 | 840 |
815 if (result < 0) { | 841 if (result < 0) { |
816 printMsg("Fatal error handling user input: %s\n", editBuf->data); | 842 errorMsg("Fatal error handling user input: %s\n", editBuf->data); |
817 isError = TRUE; | 843 isError = TRUE; |
818 } | 844 } |
819 | 845 |
820 update = TRUE; | 846 update = TRUE; |
821 } | 847 } |
936 updateStatus(insertMode); | 962 updateStatus(insertMode); |
937 } | 963 } |
938 } /* !optDaemon */ | 964 } /* !optDaemon */ |
939 | 965 |
940 if (++updateCount > 10) { | 966 if (++updateCount > 10) { |
967 time_t tmpTime = time(NULL); | |
968 if (tmpTime - prevTime > SET_KEEPALIVE) { | |
969 sendUserMsg(tmpSocket, optUserName2, "/listallusers"); | |
970 prevTime = tmpTime; | |
971 } | |
972 | |
973 if (!colorSet) { | |
974 colorSet = TRUE; | |
975 printMsg("%s v%s - %s\n", th_prog_name, th_prog_version, th_prog_fullname); | |
976 printMsg("%s\n", th_prog_author); | |
977 printMsg("%s\n", th_prog_license); | |
978 sendUserMsg(tmpSocket, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); | |
979 } | |
980 | |
941 updateStatus(insertMode); | 981 updateStatus(insertMode); |
942 updateCount = 0; | 982 updateCount = 0; |
943 } | 983 } |
944 | 984 |
945 if (!colorSet) { | |
946 colorSet = TRUE; | |
947 printMsg("%s v%s - %s\n", th_prog_name, th_prog_version, th_prog_fullname); | |
948 printMsg("%s\n", th_prog_author); | |
949 printMsg("%s\n", th_prog_license); | |
950 sendUserMsg(tmpSocket, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); | |
951 } | |
952 } | 985 } |
953 | 986 |
954 /* Shutdown */ | 987 /* Shutdown */ |
955 err_exit: | 988 err_exit: |
956 freeBuf(editBuf); | 989 freeBuf(editBuf); |